Write TCC CC while counter is running not always succeed - Arduino Zero

Hi,
I'm trying to generate the attached waveforms repeatedly.
GCLK5 is 2MHz and CC0 = 9 for period of 10usec in NFRQ.
To disable CC2 from altering I write it with value higher than CC1 but write not alwas succeed and the CC2 waveform is inverted every few seconds randomaly.
The write to CC2 is triggered by MC0 interrupt at CC0 = 5 and should complete before double buffer update at CC0 = 9.

I tried without success:

  1. Lower frequency using higher GCLK divider values or TCC period.
  2. Similar approach in NPWM and DSTOP.
  3. Disable double buffering in TCC0 initialization:
    REG_TCC0_CTRLBSET = TCC_CTRLBSET_LUPD;
    while (TCC0->SYNCBUSY.bit.CTRLB);

Thank you in advance for helping.

This is GCLK5:

REG_GCLK_GENDIV = GCLK_GENDIV_DIV(24) | // Set the clock divisor
GCLK_GENDIV_ID(5); // Set the clock generator ID to 5
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for the bus to synchronise

// Connect the 48MHz clock source to generic clock generator 5 and enable its outputs
REG_GCLK_GENCTRL = GCLK_GENCTRL_OE | // Enable the generic clock 5's outputs
GCLK_GENCTRL_IDC | // Ensure the duty cycle is about 50-50 high-low
GCLK_GENCTRL_GENEN | // Enable the clock generator 5
GCLK_GENCTRL_SRC_DFLL48M | // Set source to be 48MHz clock Note: for 8MHz source use GCLK_GENCTRL_SRC_OSC8M
GCLK_GENCTRL_ID(5); // Set the clock generator ID to 5
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for the bus to synchronise

This is TCC0:

// Feed GCLK5 to TCC0 and TCC1
REG_GCLK_CLKCTRL = GCLK_CLKCTRL_CLKEN | // Enable the generic clock
GCLK_CLKCTRL_GEN_GCLK5 | // on GCLK5
GCLK_CLKCTRL_ID_TCC0_TCC1; // Feed the GCLK5 to TCC0 and TCC1
while (GCLK->STATUS.bit.SYNCBUSY); // Wait for synchronization

REG_TCC0_WAVE |= TCC_WAVE_WAVEGEN_NFRQ | // Setup normal frequency - toggle between 0 and top
TCC_WAVE_POL2; // invert cc2

while (TCC0->SYNCBUSY.bit.WAVE); // Wait for synchronization

REG_TCC0_PER = 9; // TCC0 period
while (TCC0->SYNCBUSY.bit.PER); // Wait for synchronization
REG_TCC0_CC0 = 5; // TCC0 CC0 - D23/PB10/WO[4]
while (TCC0->SYNCBUSY.bit.CC0); // Wait for synchronization
REG_TCC0_CC2 = 10; // TCC0 CC2 - D23/PA12/WO[6]
while (TCC0->SYNCBUSY.bit.CC2); // Wait for synchronization

// map tcc0 to port PB10
PORT->Group[g_APinDescription[23].ulPort].PINCFG[g_APinDescription[23].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[23].ulPort].PMUX[g_APinDescription[23].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F; // even

// map tcc0 to port PA12
PORT->Group[g_APinDescription[22].ulPort].PINCFG[g_APinDescription[22].ulPin].bit.PMUXEN = 1;
PORT->Group[g_APinDescription[22].ulPort].PMUX[g_APinDescription[22].ulPin >> 1].reg |= PORT_PMUX_PMUXE_F; // even

// enable tcc0
REG_TCC0_CTRLA |= TCC_CTRLA_PRESCALER_DIV1 | // Set prescaler to 1
TCC_CTRLA_ENABLE; // Enable TCC0
while (TCC0->SYNCBUSY.bit.ENABLE); // Wait for synchronization

REG_TCC0_INTENSET = TCC_INTENSET_MC0; // Enable compare channel 0 (CC0) interrupt

NVIC_SetPriority(TCC0_IRQn, 0); // Set the Nested Vector Interrupt Controller (NVIC) priority for TCC0 to 0 (highest)
NVIC_EnableIRQ(TCC0_IRQn); // Connect the TCC0 timer to the Nested Vector Interrupt Controller (NVIC)

This is the interrupt handler:

void TCC0_Handler() {
volatile static int cnt = 0;

REG_TCC0_INTFLAG = TCC_INTENSET_MC0; // clear interrupt

switch(cnt) {
case 0: REG_TCC0_CC2 = 4;
break;

case 1: REG_TCC0_CC2 = 10;
break;

case 2: REG_TCC0_CC2 = 4;
break;

case 3: REG_TCC0_CC2 = 10;
break;
}

if(cnt++>20)
cnt = 0;
}

Thank you.

t.png

Hi foont,

You're clocking the timer by dividing GCLK5 down to 2MHz, however this will cause the whole timer peripheral to be clocked a 2MHz, with the corresponding synchronization delay. To clock the timer a 2MHz it might be better, (although not necessary), to divide GCLK5 by 3 to get 16MHz, then use the timer's prescaler in the CTRLA register to divide this clock signal by 8.

Normal Frequency (NFRQ) mode just toggles the output. To have full control over the output waveform you need to set the timer to Normal PWM (NPWM) mode.

Double buffering means just using the CCxB or PERB registers. These buffered registers only take effect at the beginning of the timer cycle (after an overflow) and prevent changes to the registers from causing glitches on your outputs. Changes to the CCx and PER registers by contrast take effect on the output (almost) immediately.

Writing to the CCx registers in your case statement requires register synchronization.

The PER or period register defines the frequency of the timer cycle. Setting the value of the CCx register above PER register's value will have no effect, as the timer will reach the PER value overflow. In other words it will never reach the CCx value.

Thank you MartinL.

I apologies not to mention that GCLK5 2MHz is needed on another IO.

// Enable the port multiplexer (mux) on digital pin clk_pin
PORT->Group[g_APinDescription[24].ulPort].PINCFG[g_APinDescription[24].ulPin].bit.PMUXEN = 1;
// Set the port mux mask for odd processor pin numbers (D24/PB11: PMUXO = PMUX Odd)
PORT->Group[g_APinDescription[24].ulPort].PMUX[g_APinDescription[24].ulPin >> 1].reg |= PORT_PMUX_PMUXO_H;

I think that to replace GCLK5 output by CC/WO another timer would be needed.
This second timer could harm the synchronizations of the 3 signals (2MHz, CC0, CC2) wouldn't it?

So maybe as you said the too slow timer peripheral clock is the issue but I would need to think of a way how to take care for the 2MHz clock output.

Thanks.

Hi foont,

If the 2MHz GCLK works for your project, then that's fine.

I just mentioned it because the GCLK drives the whole timer peripheral. A while back I tested clocking the timer using an external GCLK input. The idea was that the timer would count the number of incoming GCLK pulses, however in the absence of a GCLK signal the whole timer, including its registers and everything ground to a halt. Using 2MHz will work OK, it's just that reading and writing to the timer's registers will end being a little slow.

One way around this, while still synchronizing your signals to the 2MHz signal would be to clock the timer at 48MHz and multiply up (by 24) the values of the CC0, CC2 and PER registers. The output waveforms remain the same, but this speeds up you timer register access.

In your original post you mentioned about switching off or disabling one of the channels. You should be able to turn off the waveform output by setting the value in the CCx register to 0, or if you're using reversed polarity set CCx to the same value in the PER register.

Thank you MartinL.
Do you mean to clock the timer from GCLKx = 48MHz and output GCLKy = 2MHz to the IO?
Thank you.

Do you mean to clock the timer from GCLKx = 48MHz and output GCLKy = 2MHz to the IO?

On second thoughts, it's probably easiest to just clock the timer at 2MHz.

Thank you.
I will investigate it further.