diff options
author | ademuri <adam.demuri@gmail.com> | 2019-04-06 05:16:05 +0300 |
---|---|---|
committer | Daniel Garcia <danielgarcia@gmail.com> | 2019-04-06 05:16:05 +0300 |
commit | 403464a499a8feffa48f6f85d205550b9bc9c89b (patch) | |
tree | f04d42f56a83800d71f4c444d6d3944f221f09d2 | |
parent | 55359644ef0d6d13fefc23a6e23d6b0df7d68b57 (diff) |
Re-enable interrupts in between writing LED data for ARM M0. (#751)
* Fix breakage in NRF51 support introduced by #dba8825
* Re-enable interrupts in between writing LED data for ARM M0.
This adds support for FASTLED_ALLOW_INTERRUPTS to ARM M0-based platforms
(e.g. SAMD21).
This fixes the clock getting off when using more than ~30 LEDs, since
Arduino uses an interrupt to increment the millis clock.
This uses SysTick->VAL to determine whether more than 45uSecs have
elapsed while interrupts were enabled. This isn't as correct as using a
dedicated timer, but it does work on all ARM M0 platforms.
-rw-r--r-- | platforms/arm/common/m0clockless.h | 149 | ||||
-rw-r--r-- | platforms/arm/d21/led_sysdefs_arm_d21.h | 2 |
2 files changed, 111 insertions, 40 deletions
diff --git a/platforms/arm/common/m0clockless.h b/platforms/arm/common/m0clockless.h index 4798c6fa..d5a0cf6f 100644 --- a/platforms/arm/common/m0clockless.h +++ b/platforms/arm/common/m0clockless.h @@ -225,16 +225,17 @@ showLedData(volatile uint32_t *_port, uint32_t _bitmask, const uint8_t *_leds, u #define CMPLOOP5 " cmploop5 %[counter], loop_%=;" #define NOTHING "" -#if !(defined(SEI_CHK) && (FASTLED_ALLOW_INTERRUPTS == 1)) - // We're not allowing interrupts - run the entire loop in asm to keep things - // as tight as possible. In an ideal world, we should be pushing out ws281x - // leds (or other 3-wire leds) with zero gaps between pixels. +#if (defined(SEI_CHK) && (FASTLED_ALLOW_INTERRUPTS == 1)) + // We're allowing interrupts and have hardware timer support defined - + // track the loop outside the asm code, to allow inserting the interrupt + // overrun checks. asm __volatile__ ( // pre-load byte 0 - LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1 + LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1 + M0_ASM_ARGS); - // loop over writing out the data - LOOP + do { + asm __volatile__ ( // Write out byte 0, prepping byte 1 HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0) @@ -252,24 +253,26 @@ showLedData(volatile uint32_t *_port, uint32_t _bitmask, const uint8_t *_leds, u HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0) HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0) - HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0) // Write out byte 2, prepping byte 0 - HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0) HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0) HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0) HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0) HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0) HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) - HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5) CMPLOOP5 + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5) M0_ASM_ARGS - ); -#else - // We're allowing interrupts - track the loop outside the asm code, to allow - // inserting the interrupt overrun checks. + ); + SEI_CHK; INNER_SEI; --counter; CLI_CHK; + } while(counter); +#elif (FASTLED_ALLOW_INTERRUPTS == 1) + // We're allowing interrupts - track the loop outside the asm code, and + // re-enable interrupts in between each iteration. asm __volatile__ ( // pre-load byte 0 LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1 @@ -278,39 +281,107 @@ showLedData(volatile uint32_t *_port, uint32_t _bitmask, const uint8_t *_leds, u do { asm __volatile__ ( // Write out byte 0, prepping byte 1 - HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0) - HI2 D1 QLO4 LOADLEDS3(1) D2(3) LO2 D3(0) - HI2 D1 QLO4 LOADDITHER7(1) D2(7) LO2 D3(0) - HI2 D1 QLO4 DITHER5 D2(5) LO2 D3(0) - HI2 D1 QLO4 SCALE4(1) D2(4) LO2 D3(0) - HI2 D1 QLO4 ADJDITHER7(1) D2(7) LO2 D3(0) - HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0) - HI2 D1 QLO4 SWAPBBN1 D2(1) LO2 D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0) // Write out byte 1, prepping byte 2 - HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0) - HI2 D1 QLO4 LOADLEDS3(2) D2(3) LO2 D3(0) - HI2 D1 QLO4 LOADDITHER7(2) D2(7) LO2 D3(0) - HI2 D1 QLO4 DITHER5 D2(5) LO2 D3(0) - HI2 D1 QLO4 SCALE4(2) D2(4) LO2 D3(0) - HI2 D1 QLO4 ADJDITHER7(2) D2(7) LO2 D3(0) - HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0) - HI2 D1 QLO4 SWAPBBN1 D2(1) LO2 D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0) // Write out byte 2, prepping byte 0 - HI2 D1 QLO4 INCLEDS3 D2(3) LO2 D3(0) - HI2 D1 QLO4 LOADLEDS3(0) D2(3) LO2 D3(0) - HI2 D1 QLO4 LOADDITHER7(0) D2(7) LO2 D3(0) - HI2 D1 QLO4 DITHER5 D2(5) LO2 D3(0) - HI2 D1 QLO4 SCALE4(0) D2(4) LO2 D3(0) - HI2 D1 QLO4 ADJDITHER7(0) D2(7) LO2 D3(0) - HI2 D1 QLO4 NOTHING D2(0) LO2 D3(0) - HI2 D1 QLO4 SWAPBBN1 D2(1) LO2 D3(5) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5) M0_ASM_ARGS ); - SEI_CHK; INNER_SEI; --counter; CLI_CHK; + + uint32_t ticksBeforeInterrupts = SysTick->VAL; + sei(); + --counter; + cli(); + + // If more than 45 uSecs have elapsed, give up on this frame and start over. + // Note: this isn't completely correct. It's possible that more than one + // millisecond will elapse, and so SysTick->VAL will lap + // ticksBeforeInterrupts. + // Note: ticksBeforeInterrupts DECREASES + const uint32_t kTicksPerMs = VARIANT_MCK / 1000; + const uint32_t kTicksPerUs = kTicksPerMs / 1000; + const uint32_t kTicksIn45us = kTicksPerUs * 45; + + const uint32_t currentTicks = SysTick->VAL; + + if (ticksBeforeInterrupts < currentTicks) { + // Timer started over + if ((ticksBeforeInterrupts + (kTicksPerMs - currentTicks)) > kTicksIn45us) { + return 0; + } + } else { + if ((ticksBeforeInterrupts - currentTicks) > kTicksIn45us) { + return 0; + } + } } while(counter); +#else + // We're not allowing interrupts - run the entire loop in asm to keep things + // as tight as possible. In an ideal world, we should be pushing out ws281x + // leds (or other 3-wire leds) with zero gaps between pixels. + asm __volatile__ ( + // pre-load byte 0 + LOADLEDS3(0) LOADDITHER7(0) DITHER5 SCALE4(0) ADJDITHER7(0) SWAPBBN1 + + // loop over writing out the data + LOOP + // Write out byte 0, prepping byte 1 + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(1) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(1) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(1) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(1) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0) + + // Write out byte 1, prepping byte 2 + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(2) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(2) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(2) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(2) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 INCLEDS3 _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(0) + + // Write out byte 2, prepping byte 0 + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 LOADLEDS3(0) _D2(3) LO2 _D3(0) + HI2 _D1 QLO4 LOADDITHER7(0) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 DITHER5 _D2(5) LO2 _D3(0) + HI2 _D1 QLO4 SCALE4(0) _D2(4) LO2 _D3(0) + HI2 _D1 QLO4 ADJDITHER7(0) _D2(7) LO2 _D3(0) + HI2 _D1 QLO4 NOTHING _D2(0) LO2 _D3(0) + HI2 _D1 QLO4 SWAPBBN1 _D2(1) LO2 _D3(5) CMPLOOP5 + + M0_ASM_ARGS + ); #endif return num_leds; } diff --git a/platforms/arm/d21/led_sysdefs_arm_d21.h b/platforms/arm/d21/led_sysdefs_arm_d21.h index b343d3f2..a48db10a 100644 --- a/platforms/arm/d21/led_sysdefs_arm_d21.h +++ b/platforms/arm/d21/led_sysdefs_arm_d21.h @@ -11,7 +11,7 @@ // Default to allowing interrupts #ifndef FASTLED_ALLOW_INTERRUPTS -#define FASTLED_ALLOW_INTERRUPTS 0 +#define FASTLED_ALLOW_INTERRUPTS 1 #endif #if FASTLED_ALLOW_INTERRUPTS == 1 |