diff options
| author | Scott Shawcroft <scott.shawcroft@gmail.com> | 2017-10-31 22:23:45 -0700 |
|---|---|---|
| committer | Scott Shawcroft <scott.shawcroft@gmail.com> | 2017-10-31 22:38:09 -0700 |
| commit | 3177e10e9e7b6ce720ca4cd0a952c884c876bcd6 (patch) | |
| tree | ed5376099ee8b7b178494e6a6ed79309177d9adb /ports/atmel-samd | |
| parent | d023879bea7c711e2a65c11bbe82f840afe9a664 (diff) | |
atmel-samd: Add samd21 neopixel support.
Also, fix and enable the status neopixel.
Fixes #264
Diffstat (limited to 'ports/atmel-samd')
| -rw-r--r-- | ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h | 2 | ||||
| -rw-r--r-- | ports/atmel-samd/common-hal/neopixel_write/__init__.c | 74 | ||||
| -rw-r--r-- | ports/atmel-samd/tick.c | 14 | ||||
| -rw-r--r-- | ports/atmel-samd/tick.h | 5 |
4 files changed, 89 insertions, 6 deletions
diff --git a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h index 27426940d..34a681607 100644 --- a/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h +++ b/ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h @@ -6,7 +6,7 @@ #define MICROPY_HW_LED_TX PIN_PA27 #define MICROPY_HW_LED_RX PIN_PB06 -// #define MICROPY_HW_NEOPIXEL (&pin_PB17) +#define MICROPY_HW_NEOPIXEL (&pin_PB17) #define SPI_FLASH_BAUDRATE (1000000) diff --git a/ports/atmel-samd/common-hal/neopixel_write/__init__.c b/ports/atmel-samd/common-hal/neopixel_write/__init__.c index d0ad67171..fbe7a2ff6 100644 --- a/ports/atmel-samd/common-hal/neopixel_write/__init__.c +++ b/ports/atmel-samd/common-hal/neopixel_write/__init__.c @@ -29,6 +29,21 @@ #include "shared-bindings/neopixel_write/__init__.h" +#include "tick.h" + +#ifdef SAMD51 +static inline void delay_cycles(uint8_t cycles) { + uint32_t start = SysTick->VAL; + uint32_t stop = start - cycles; + if (start < cycles) { + stop = 0xffffff + start - cycles; + } + while (SysTick->VAL > stop) {} +} +#endif + +uint64_t next_start_tick_ms = 0; +uint32_t next_start_tick_us = 1000; void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout, uint8_t *pixels, uint32_t numBytes) { // This is adapted directly from the Adafruit NeoPixel library SAMD21G18A code: @@ -37,11 +52,18 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout, uint32_t pinMask; PortGroup* port; + // This must be called while interrupts are on in case we're waiting for a + // future ms tick. + wait_until(next_start_tick_ms, next_start_tick_us); + // Turn off interrupts of any kind during timing-sensitive code. mp_hal_disable_all_interrupts(); + #ifdef SAMD21 // Make sure the NVM cache is consistently timed. NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_DETERMINISTIC_Val; + #endif + uint32_t pin = digitalinout->pin->pin; port = &PORT->Group[GPIO_PORT(pin)]; // Convert GPIO # to port register @@ -56,31 +78,75 @@ void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout, for(;;) { *set = pinMask; + // This is the time where the line is always high regardless of the bit. + // For the SK6812 its 0.3us +- 0.15us + #ifdef SAMD21 asm("nop; nop;"); + #endif + #ifdef SAMD51 + delay_cycles(18); + #endif if(p & bitMask) { + // This is the high delay unique to a one bit. + // For the SK6812 its 0.3us + #ifdef SAMD21 asm("nop; nop; nop; nop; nop; nop; nop;"); + #endif + #ifdef SAMD51 + delay_cycles(25); + #endif *clr = pinMask; } else { *clr = pinMask; + // This is the low delay unique to a zero bit. + // For the SK6812 its 0.3us + #ifdef SAMD21 asm("nop; nop;"); + #endif + #ifdef SAMD51 + delay_cycles(25); + #endif } if((bitMask >>= 1) != 0) { + // This is the delay between bits in a byte and is the 1 code low + // level time from the datasheet. + // For the SK6812 its 0.6us +- 0.15us + #ifdef SAMD21 asm("nop; nop; nop; nop; nop;"); + #endif + #ifdef SAMD51 + delay_cycles(44); + #endif } else { if(ptr >= end) break; p = *ptr++; bitMask = 0x80; + // This is the delay between bytes. Its similar to the other branch + // in the if statement except its tuned to account for the time the + // above operations take. + // For the SK6812 its 0.6us +- 0.15us + #ifdef SAMD51 + delay_cycles(50); + #endif } } + #ifdef SAMD21 // Speed up! (But inconsistent timing.) NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_NO_MISS_PENALTY_Val; + #endif + + // ticks_ms may be out of date at this point because we stopped the + // interrupt. We'll risk it anyway. + current_tick(&next_start_tick_ms, &next_start_tick_us); + if (next_start_tick_us < 100) { + next_start_tick_ms += 1; + next_start_tick_us = 100 - next_start_tick_us; + } else { + next_start_tick_us -= 100; + } // Turn on interrupts after timing-sensitive code. mp_hal_enable_all_interrupts(); - // 50us delay to let pixels latch to the data that was just sent. - // This could be optimized to only occur before pixel writes when necessary, - // like in the Arduino library. - mp_hal_delay_us(50); } diff --git a/ports/atmel-samd/tick.c b/ports/atmel-samd/tick.c index ecef96992..f5fd57f5d 100644 --- a/ports/atmel-samd/tick.c +++ b/ports/atmel-samd/tick.c @@ -66,5 +66,17 @@ void tick_delay(uint32_t us) { start_ms = ticks_ms; us_between_ticks = 1000; } - while (SysTick->VAL > ((1000 - us) * ticks_per_us)) {} + while (SysTick->VAL > ((us_between_ticks - us) * ticks_per_us)) {} +} + +// us counts down! +void current_tick(uint64_t* ms, uint32_t* us_until_ms) { + uint32_t ticks_per_us = common_hal_mcu_processor_get_frequency() / 1000 / 1000; + *ms = ticks_ms; + *us_until_ms = SysTick->VAL / ticks_per_us; +} + +void wait_until(uint64_t ms, uint32_t us_until_ms) { + uint32_t ticks_per_us = common_hal_mcu_processor_get_frequency() / 1000 / 1000; + while(ticks_ms <= ms && SysTick->VAL / ticks_per_us >= us_until_ms) {} } diff --git a/ports/atmel-samd/tick.h b/ports/atmel-samd/tick.h index a987ff7ad..80e7bc9af 100644 --- a/ports/atmel-samd/tick.h +++ b/ports/atmel-samd/tick.h @@ -36,4 +36,9 @@ void tick_init(void); void tick_delay(uint32_t us); +void current_tick(uint64_t* ms, uint32_t* us_until_ms); +// Do not call this with interrupts disabled because it may be waiting for +// ticks_ms to increment. +void wait_until(uint64_t ms, uint32_t us_until_ms); + #endif // MICROPY_INCLUDED_ATMEL_SAMD_TICK_H |
