Hello,
I am having an issue where it appears that my hardware timer interrupts can occasionally be delayed by upwards of 20 ms or even be missed entirely. My code is based on Martin BL's example as seen below.
Original thread: https://devzone.nordicsemi.com/f/nordic-q-a/25074/why-is-not-my-timer-triggered-when-configured-through-ppi-and-spi
Direct link to MartinBL's code in this thread: https://devzone.nordicsemi.com/cfs-file/__key/communityserver-discussions-components-files/4/5037.main.c
Hardware: nRF52840 SoC
SDK: 15.0.0
Connected to PC with nRF52840 USB Dongle and nRF Connect
In short, I have two hardware timers as observed in the example source above (Timer CC values are different however):
TIMER_TIMER (Timer mode): Fires every 32 usec that triggers the SPI_START_TASK using PPI. This iterates through an ArrayList. This timer's interrupt callback id disabled.
TIMER_COUNTER (Counter mode): Counts the SPI_END_EVENT occurrences. This timer interrupt is enabled.
I have a SPI TX buffer of size 625 bytes. So I expect the TIMER_COUNTER interrupt to fire once for every 625 bytes sent.
It is imperative that my SPI writes occur every 32 usec since I am writing to a DAC. So to achieve this, I have a total buffer of length 1250 bytes that makes up 40 msec worth of DAC values where the last 20 msec worth of buffer space is deemed an overflow region. So the first 625 bytes are the intended transmit buffer region, and the remaining 625 bytes are the overflow region. This is done since I expect the SoftDevice to delay my TIMER_COUNTER interrupt callback to some degree. When the TIMER_COUNTER interrupt fires, I read the spi3.p_reg->TXD.PTR to see how far into the overflow region of the buffer it has gone, and then move the pointer back to the start of the buffer (plus an offset of however many bytes the TX.PTR went over 625) and update the TIMER_COUNTER CC value accordingly. This way I am not changing the interval of my 32 usec SPI writes.
Here is an example log snippet of everything working properly:
(10:58:57.251) bufferOver 1
(10:58:57.251) TXD.PTR = 0x20017828 and COUNTER CC0 = 624
(10:59:00.431) bufferOver 3
(10:59:00.431) TXD.PTR = 0x2001782c and COUNTER CC0 = 622
(10:59:06.450) bufferOver 3
(10:59:06.450) TXD.PTR = 0x2001782c and COUNTER CC0 = 622
(10:59:12.469) bufferOver 4
(10:59:12.469) TXD.PTR = 0x2001782c and COUNTER CC0 = 621
(10:59:17.931) bufferOver 3
(10:59:17.931) TXD.PTR = 0x2001782c and COUNTER CC0 = 622
The log statements above are printed from the TIMER_COUNTER interrupt handler every ~6 msec as to not flood the UART. The code will run fine continuously for 10 min to an hour (varies). The failure occurs when the following happens:
(11:54:45.606) TXD.PTR = 0x20017828 and COUNTER CC0 = 624
(11:54:51.588) bufferOver 1
(11:54:51.588) TXD.PTR = 0x20017828 and COUNTER CC0 = 624
(11:54:51.656) bufferOver 625
(11:54:51.656) TXD.PTR = 0x20017d10 and COUNTER CC0 = 0
In the log above, it appears hat for some reason the TIMER_COUNTER interrupt never fired when it counted to 624. It instead waited an additional 625 counts + 1 for interrupt latency, hence the bufferOver value of 625. This is causing the CC register to be set to a value of 0 effectively disabling the TIMER_COUNTER.
For context:
bufferOver = (spi3.p_reg->TXD.PTR) - (uint32_t) p_waveformData_End; //p_waveformData_End points to the halfway point of the TX buffer (625 bytes). It never changes
timer_counter.p_reg->CC[0] = number_of_transfers - bufferOver; // number_of_transfers == 625 (Always)
So essentially, TXD.PTR made it all the way to the end of the entire buffer before the interrupt fired, indicating that either the SoftDevice blocked for ~20 msec (625 bytes of SPI data) or that the timer interrupt never fired once it counted to the CC value of 624.
So, is it possible for the SoftDevice (or some other driver) to disable interrupts at any point that could cause a missed timer interrupt? The only operations that are running during this time are the two hardware timers mentioned above, the two associated PPI channels, BLE with no traffic (0 PDU size), and an app_timer() that is blinking a LED.
Thanks for any help or insight! I have been trying to resolve this issue for days.