It is not freezing, it is sleeping for an long time - precisely the uptime subtracted from 2^16 seconds, and it is caused by the timer architecture involving two timers which don’t latch correctly. But there is a fix, and here it is.
- Always set the duty cycle to a multiple of 2 seconds. Any randomisation also a multiple of 2s.
- Wait for LoRaMacState != LORAMAC_IDLE before before LoRaWAN.sleep();
The bug is that Asr_Timer_Disable does not disarm the ISR. CySysTimerDisable stops the counters. CySysTimerDisableIsr would remove Timer1
from the interrupt controller’s active vector table — but it is not called.
Timer1’s ISR vector remains armed.
Because Timer1 has ClearOnMatch, when it reaches its match value it does not
stop — it resets its counter to zero and immediately begins counting toward the
next match.
The race condition that follows when g_timeout != 0:
- Timer1 reaches its match. Timer1IsrCallback fires.
- g_timeout != 0, so it calls Asr_SetTimeout(g_timeout).
- Stage 2 calls Asr_Timer_Disable() — Timer1 counters reset, ISR still armed.
- Stage 2 configures Timer0 for the short remainder and enables Timer0 only.
- Timer1, now reset and counting again with ISR still armed, reaches its
match value a second time.
- Timer1IsrCallback fires again. This time g_timeout is 0. The callback sees zero and calls
timerAlarmCallback() — before Timer0 has fired.
- The wakeup event is consumed and discarded. No new alarm is queued.
The device calls CySysPmDeepSleep() with no valid wakeup alarm armed.
I found out about this because there was a relationship between the deep sleep time and the elapsed time since the device was reset.