samd21 TCC0/TC3 - what'd I do? :)

Go To Last Post
5 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In my primary project, I recently did a major refactoring which mostly involved shuffling a bunch of files around, but in there was also an update of the ASF code from 3.30 to 3.34.1.  For reasons which are too complex to get into, I didn't notice until recently that something in that shuffling of files caused TC3 (and possibly TCC0, performing a different function) to stop triggering its callback every ~60 seconds.  The maddening thing is that the enabling code where I configure TC3 is exactly the same, and I've verified (using the debugger) that it IS actually being called (for setting up TC3) in both cases.  _Something_ else I've done has clearly had an unfortunate side effect, but I'm having a heckuva time spotting it - best guess is I'm changing something else somewhere later in the code which breaks it.  I _also_ have a problem with PWM output which is controlled by TCC0 - prior to the reorg, it worked, but now the LED which is supposed to be PWM-controlled is arbitrarily just on or off, not changing, which leads me to believe TCC0 isn't working either.

Things I've checked:  clocks are still set up the same (conf_clocks.h hasn't changed and is definitely being compiled in - if I insert a compilation error, it breaks the compile as expected), the TC enabling code is called.  It seems to be that the callbacks are just not getting called.  Looking for creative troubleshooting suggestions at this point because I'm a little baffled.  :)

If it matters, I see this on a few different boards (supporting different boards was part of the refactor), but I can duplicate both the working and non-working state on a SAMW25 Xplained Pro (SAMD21G18A core).

Thanks!

This topic has a solution.
Last Edited: Fri. Aug 11, 2017 - 08:20 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Posting while I try to figure this out, in case it helps any future people:  I've figured out that TC3 is, in fact, enabled in the problem case.   GDB shows me:

 

(gdb) print/x *(0x42002C00U)
$5 = 0x202

 

0x42002C00 is the address of TC3 CTRLA register (from the datasheet). It's in DIV4 prescaler mode (0x200) and enabled (0x002).  In the _working_ example, I see the same;  then, when the timer expires, TC3 gets disabled and shows 0x200.

 

The next thing I'll pursue:  is something resetting the count, or something like that?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, part of the mystery solved.  :)  The problems with TC3 were, in fact, because I was resetting it periodically, and apparently not directly related to the TCC0 issue, which I haven't yet solved.

 

So - TCC0 is set up as single-slope PWM, with a wave out pin.  It's running (the stop bit is cleared, and the count increases), and the LED which it's supposed to be controlling comes on, but the dutycycle callback stops getting called very early in the process.  (I think it's called twice)

 

Continuing to scratch my head around this one.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OK, after the callback stops working, the corresponding bit in TCC0->INTENSET is cleared - so now I know why the interrupt isn't getting called.  Now I get to track down what's clearing it.  :)  (GDB-based breakpoint suggestions welcome, but I bet i'll figure it out eventually)

 

Working, getting callbacks:

(gdb) print/x TCC0->INTENSET
$1 = {bit = {OVF = 0x0, TRG = 0x0, CNT = 0x0, ERR = 0x0, UFS = 0x0, DFS = 0x0,
    FAULTA = 0x0, FAULTB = 0x0, FAULT0 = 0x0, FAULT1 = 0x0, MC0 = 0x0, MC1 = 0x1,
    MC2 = 0x0, MC3 = 0x0}, vec = {MC = 0x2}, reg = 0x20000}
(gdb)

 

Not working, no callbacks:

(gdb) print/x TCC0->INTENSET
$2 = {bit = {OVF = 0x0, TRG = 0x0, CNT = 0x0, ERR = 0x0, UFS = 0x0, DFS = 0x0,
    FAULTA = 0x0, FAULTB = 0x0, FAULT0 = 0x0, FAULT1 = 0x0, MC0 = 0x0, MC1 = 0x0,
    MC2 = 0x0, MC3 = 0x0}, vec = {MC = 0x0}, reg = 0x0}

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

SOLVED!

It's Atmel's fault.  :)

 

Sometime between ASF 3.30 and 3.34, they changed the code for tcc_disable():

 

static inline void tcc_disable(
                const struct tcc_module *const module_inst)
{
        /* Sanity check arguments */
        Assert(module_inst);
        Assert(module_inst->hw);

        /* Get a pointer to the module's hardware instance */
        Tcc *const tcc_module = module_inst->hw;

        while (tcc_module->SYNCBUSY.reg & TCC_SYNCBUSY_ENABLE) {
                /* Wait for sync */
        }

#if 0
        /* Disbale interrupt */
        tcc_module->INTENCLR.reg = TCC_INTENCLR_MASK;
        /* Clear interrupt flag */
        tcc_module->INTFLAG.reg = TCC_INTFLAG_MASK;
#endif

        /* Disable the TCC module */
        tcc_module->CTRLA.reg  &= ~TCC_CTRLA_ENABLE;
}

... adding the bit that I enclosed in #if 0, above.  This has the effect of DISABLING THE INTERRUPTS I'M WAITING FOR.  They also don't re-enable them in tcc_enable().

 

With the #if 0 in place, my code works again.  Yeesh.