Author Topic: AVR UC3 Timer/Counter Issue - AT32UC3C2512C  (Read 2712 times)

0 Members and 1 Guest are viewing this topic.

Offline selkathguyTopic starter

  • Supporter
  • ****
  • Posts: 88
  • Country: us
AVR UC3 Timer/Counter Issue - AT32UC3C2512C
« on: October 05, 2015, 06:26:32 pm »
I have an AVR32 UC3 for which have the Timer/Counter TC0 configured to generate an interrupt.  The interrupt is generated, routine called, and returns.  Everything seemed good.  The interrupt continues to fire but not at the rate which has been configured by the timer/counter.

It is one of the most basic timer setups.  I am only using TC0, and of that only channel 0. It is set to trigger on a match with register C (RC Compare).  This sets (in hardware) the CPCS bit in the Channel Status Register (SR).  This bit is supposedly cleared when read, thus clearing the interrupt line.  The issue is that it does not matter what value RC is.  It always triggers at the same rate, more frequently than I want.

Datasheet: http://www.atmel.com/images/doc32117.pdf

Specifics:  The interrupt is supposed to trigger once per second.  Please help me if any of the following seems incorrect.  See below to confirm my math.   I am using the internal 8Mhz RC oscillator, and am feeding that into PLL0 with a multiplier of 6x (48M/8M) to get ~48 Mhz.  That is used for the CPU, HSB and PBB.  PBA and PBC both go through a divider by 8 (23) to get ~6 Mhz.  TC0 is clocked from Peripheral Bus C.  TC0 channel 0 is configured to use the highest possible divider (128) as its clock.  That makes 48M -> 6M -> ~46,875 Hz for the counter clock.  I have set Register C for the comparison to exactly 46,875, which is just below the limit of a 16-bit counter and should match and reset once every second.  This does not happen.  The interrupt is occuring about 8-12 times per second and the status register doesn't ever seem to be clearing.

Relevant configuration: Clocks (conf_clock.h)
Code: [Select]
#define CONFIG_SYSCLK_SOURCE        SYSCLK_SRC_PLL0

/* Fbus = Fsys / (2 ^ BUS_div) */
#define CONFIG_SYSCLK_CPU_DIV       0
#define CONFIG_SYSCLK_PBA_DIV       3
#define CONFIG_SYSCLK_PBB_DIV       0
#define CONFIG_SYSCLK_PBC_DIV       3

/* Fpll0 = (Fclk * PLL_mul) / PLL_div */
#define CONFIG_PLL0_SOURCE          PLL_SRC_RC8M
#define CONFIG_PLL0_MUL             (48000000UL / 8000000UL)
#define CONFIG_PLL0_DIV             1

Relevant configuration: TC0
Code: [Select]
void tc_setup()
{
tc_waveform_opt_t waveformOpts = {
waveformOpts.acpa = TC_EVT_EFFECT_NOOP,
waveformOpts.acpc = TC_EVT_EFFECT_NOOP,
waveformOpts.aeevt = TC_EVT_EFFECT_NOOP,
waveformOpts.aswtrg = TC_EVT_EFFECT_NOOP,
waveformOpts.bcpb = TC_EVT_EFFECT_NOOP,
waveformOpts.bcpc = TC_EVT_EFFECT_NOOP,
waveformOpts.beevt = TC_EVT_EFFECT_NOOP,
waveformOpts.bswtrg = TC_EVT_EFFECT_NOOP,
waveformOpts.burst = TC_BURST_NOT_GATED,
waveformOpts.channel = 0,
waveformOpts.clki = TC_CLOCK_RISING_EDGE,
waveformOpts.cpcdis = false,
waveformOpts.cpcstop = false,
waveformOpts.eevt = TC_EXT_EVENT_SEL_TIOB_INPUT,
waveformOpts.eevtedg = TC_SEL_NO_EDGE,
waveformOpts.enetrg = false,
waveformOpts.tcclks = TC_CLOCK_SOURCE_TC5, // Frequency of PBC divided by 128
waveformOpts.wavsel = TC_WAVEFORM_SEL_UP_MODE_RC_TRIGGER
};
tc_init_waveform(&AVR32_TC0, &waveformOpts);
tc_write_rc(&AVR32_TC0, 0, sysclk_get_pbc_hz()/128);  //even if this is defined explicitly as 46,875.  Changing this value is not affecting anything and it should.
}

The ISR.   Note that I have attempted to even disable the channel interrupts for the read/clearing period.  Shouldnt be necessary:

Code: [Select]
int a=48;
ISR(onTimer, 33, AVR32_INTC_INT2)
{
    //just prints 0-9 in a USB CDC Virtual COM terminal, one digit per interrupt
udi_cdc_putc(a++);
if (a==58)
a=48;

AVR32_TC0.channel[0].idr = AVR32_TC_IER0_CPCS_MASK;
delay_ms(1);
tc_read_sr(&AVR32_TC0, 0); //this is the ASF function that reads the register, clearing bits like CPCS
delay_ms(1);
        //BREAKING HERE SHOWS CPCS BIT STILL SET
AVR32_TC0.channel[0].ier = AVR32_TC_IER0_CPCS_MASK;
}

 
Running code in main:
Code: [Select]
//[...]
tc_setup();
tc_interrupt_t intmask = {0};
intmask.cpcs=1;
tc_configure_interrupts(&AVR32_TC0, 0, &intmask);

INTC_register_interrupt(&onTimer, AVR32_TC0_IRQ0, AVR32_INTC_INT2);
cpu_irq_enable();
tc_start(&AVR32_TC0, 0);
//[...]

Things I have tried:

I figured since the CPU clock is so incredibly fast compared to the TC channel clock, I suspected it is entirely possible that the interrupt is being fired, Interrupt controller raising that line, CPU processing the ISR and "clearing" the CPCS bit by reading that bit in the channel register, and returning from the interrupt all within the same clock cycle of that 47Khz channel clock.  Meaning when the interrupt returns, that request is still pinned active as it is still hardware-connected to the RC Compare state (which would still be high/active).  I inserted some CPU delays inside the Interrupt Service Routine to ensure that the TC channel clock would have advanced.  This doesn't seem to have helped.

Optimization is OFF -O0

I have looked at the disassembly and can confirm there is a load occuring from the status register.  It even breaks on memory access exactly as expected.  It has not been optimized away as a useless read.

What am I doing wrong?  Thank you for your time.
« Last Edit: October 05, 2015, 09:05:40 pm by selkathguy »
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf