Skip to content

Commit

Permalink
lib: Make sure that PWM continues to work even if it misses an IRQ.
Browse files Browse the repository at this point in the history
If the soft PWM driver misses an IRQ then the next CC value (computed as
CC[x] += delay) could already be behind the timer counter value, and hence
the next compare IRQ won't fire until the timer counter value wraps around.

Example code which demonstrates the bug (by having a long neopixel write
that stops IRQs):

    import microbit
    import music
    import neopixel

    np = neopixel.NeoPixel(microbit.pin0, 64)

    while True:
        music.play(music.BA_DING, wait=False)
        microbit.sleep(1000)
        np.clear()
        microbit.sleep(2000)

Prior to this commit the music would only play every 16 seconds or so,
which is the time taken for the PWM fast ticker to wrap around.

Fixes issue #600.

Signed-off-by: Damien George <damien@micropython.org>
  • Loading branch information
dpgeorge committed Nov 29, 2021
1 parent 3148254 commit 3b55d0d
Showing 1 changed file with 25 additions and 4 deletions.
29 changes: 25 additions & 4 deletions source/lib/ticker.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,23 @@
#define LowPriority_IRQn SWI4_IRQn
#define LowPriority_IRQHandler SWI4_IRQHandler

// Ticker is configured to TIMER_BITMODE_BITMODE_24Bit.
#define TICKER_MASK 0xffffff

// Schedule the next CC value, taking care of the case where an IRQ could have been
// missed, so CC[x]+dt is already behind the timer counter value. In such a case
// schedule it as COUNTER[x]+dt.
#define TICKER_SCHEDULE_NEXT_CC(reg_id, dt) \
do { \
uint32_t next_cc = ticker->CC[reg_id] + dt; \
ticker->TASKS_CAPTURE[reg_id] = 1; \
uint32_t cc = ticker->CC[reg_id]; \
if (((next_cc - cc) & TICKER_MASK) > (TICKER_MASK >> 1)) { \
next_cc = cc + dt; \
} \
ticker->CC[reg_id] = next_cc; \
} while (0)

// The number of milliseconds that have passed since the ticker was initialised
volatile uint32_t ticker_ticks_ms;

Expand Down Expand Up @@ -90,19 +107,23 @@ void FastTicker_IRQHandler(void) {
ticker_callback_ptr *call = callbacks;
if (ticker->EVENTS_COMPARE[0]) {
ticker->EVENTS_COMPARE[0] = 0;
ticker->CC[0] += call[0]()*MICROSECONDS_PER_TICK;
uint32_t dt = call[0]() * MICROSECONDS_PER_TICK;
TICKER_SCHEDULE_NEXT_CC(0, dt);
}
if (ticker->EVENTS_COMPARE[1]) {
ticker->EVENTS_COMPARE[1] = 0;
ticker->CC[1] += call[1]()*MICROSECONDS_PER_TICK;
uint32_t dt = call[1]() * MICROSECONDS_PER_TICK;
TICKER_SCHEDULE_NEXT_CC(1, dt);
}
if (ticker->EVENTS_COMPARE[2]) {
ticker->EVENTS_COMPARE[2] = 0;
ticker->CC[2] += call[2]()*MICROSECONDS_PER_TICK;
uint32_t dt = call[2]() * MICROSECONDS_PER_TICK;
TICKER_SCHEDULE_NEXT_CC(2, dt);
}
if (ticker->EVENTS_COMPARE[3]) {
ticker->EVENTS_COMPARE[3] = 0;
ticker->CC[3] += MICROSECONDS_PER_MACRO_TICK;
uint32_t dt = MICROSECONDS_PER_MACRO_TICK;
TICKER_SCHEDULE_NEXT_CC(3, dt);
ticker_ticks_ms += MILLISECONDS_PER_MACRO_TICK;
NVIC_SetPendingIRQ(SlowTicker_IRQn);
}
Expand Down

0 comments on commit 3b55d0d

Please sign in to comment.