Thanks for responding so quickly, MartinL!
I implemented the switch to the TC3 timer as you suggested this morning. The motor ran fine but, unfortunately, the intermittent speed spikes did not go away.
Two issues come to mind...
First, I don't understand the interaction between TC3 changes & synchronization-waits when called from inside interrupts and the program flow...
For example, on a zero-crossing (inside an interrupt), I run this code:
// retrigger the TC3 timer
TC3->COUNT16.CTRLBSET.reg = TC_CTRLBSET_CMD_RETRIGGER;
// wait for synchronization
while (TC3->COUNT16.STATUS.bit.SYNCBUSY);
But I also make changes (to the pulse delays) in the non-interrupt program flow in my motorSetDrive() calls:
// set the TC3 CC1 register to the pulse delay time
TC3->COUNT16.CC[1].reg = delay >> 4;
// wait for syncrhonization
while (TC3->COUNT16.STATUS.bit.SYNCBUSY);
// Set the TC3 CC0 register to the pulse delay + pulse width time
TC3->COUNT16.CC[0].reg = (delay >> 4) + (pulseWidth >> 4);
// wait for syncrhonization
while (TC3->COUNT16.STATUS.bit.SYNCBUSY);
What happens when motorSetDrive() is making changes to the delay and waiting for TC3 synchronization and the zero-crossing interrupt tries to schedule a new pulse by making a change to the same register?
Second, in my logs, I'm noticing that the spikes occur more often when the pulse delay is long (i.e. the pulse starts further from the triggering zero crossing and close to the subsequent one). In this case, the window to set the triac pulse back low is smaller... and I wonder if some intermittent slowness in an interrupt occasionally causes the triac pulse not to return to low before the next zero crossing.
I'm triggering on zero crossings at 60Hz so my pulse window is 8.3ms. In software, I'm limiting my pulse delay to a max of 6.8ms after the zero crossing (1.5ms of headroom) and a pulse width of about 80ms. Is it possible that either latency (e.g. waiting for register synchronization or something) on the zero-crossing interrupt could push back scheduling the pulse delay or, alternatively, latency around the TC3_Handler() could cause the pulse not to clear before the subsequent zero crossing? I know this is pretty far fetched (since the code in my ZC & TC3_Handler interrupt is minimal and a millisecond+ of headroom feels like an eternity)... but I thought I'd throw this out there. (Your missed TC4 interrupt theory was promising since a missed trailing-edge bit bang could also have been the cause.)
Unless one of these issues seems like a possible culprit,
I'm seriously considering changing my control board to be compatible with the SAMD21 event system (which you recommended in Sept). At the time, I resisted your suggestion since my already-fabricated boards used A3/PA4 (for the zero crossing interrupt) and A5/PB02 (for my triac pulse). I have since moved the triac pulse to A2/PB09, but I think PB09 is also not connected to a timer output.
It wouldn't take much work to switch the triac pulse output to A4/PA05 which I believe connects to TCC0.
Do you know of a good code reference for using the SAMD21 event system as follows:
- Zero crossing read on A3/PA4 --> schedule pulse delay on A4/PA05
- Generate pulse leading edge on A4/PA05 using TCC0 timer
- Generate pulse trailing edge on A4/PA05 using TCC0 timer
Thanks again for your insights.
Randy