summaryrefslogtreecommitdiff
path: root/ports/atmel-samd
diff options
context:
space:
mode:
authorScott Shawcroft <scott.shawcroft@gmail.com>2017-10-31 22:23:45 -0700
committerScott Shawcroft <scott.shawcroft@gmail.com>2017-10-31 22:38:09 -0700
commit3177e10e9e7b6ce720ca4cd0a952c884c876bcd6 (patch)
treeed5376099ee8b7b178494e6a6ed79309177d9adb /ports/atmel-samd
parentd023879bea7c711e2a65c11bbe82f840afe9a664 (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.h2
-rw-r--r--ports/atmel-samd/common-hal/neopixel_write/__init__.c74
-rw-r--r--ports/atmel-samd/tick.c14
-rw-r--r--ports/atmel-samd/tick.h5
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