Skip to content

Commit 3177e10

Browse files
committed
atmel-samd: Add samd21 neopixel support.
Also, fix and enable the status neopixel. Fixes adafruit#264
1 parent d023879 commit 3177e10

7 files changed

Lines changed: 117 additions & 25 deletions

File tree

main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ bool start_mp(safe_mode_t safe_mode) {
146146
// Wait for connection or character.
147147
bool serial_connected_before_animation = false;
148148
rgb_status_animation_t animation;
149-
prep_rgb_status_animation(&result, found_main, &animation);
149+
prep_rgb_status_animation(&result, found_main, safe_mode, &animation);
150150
while (true) {
151151
#ifdef MICROPY_VM_HOOK_LOOP
152152
MICROPY_VM_HOOK_LOOP

ports/atmel-samd/boards/metro_m4_express/mpconfigboard.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#define MICROPY_HW_LED_TX PIN_PA27
77
#define MICROPY_HW_LED_RX PIN_PB06
88

9-
// #define MICROPY_HW_NEOPIXEL (&pin_PB17)
9+
#define MICROPY_HW_NEOPIXEL (&pin_PB17)
1010

1111
#define SPI_FLASH_BAUDRATE (1000000)
1212

ports/atmel-samd/common-hal/neopixel_write/__init__.c

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,21 @@
2929

3030
#include "shared-bindings/neopixel_write/__init__.h"
3131

32+
#include "tick.h"
33+
34+
#ifdef SAMD51
35+
static inline void delay_cycles(uint8_t cycles) {
36+
uint32_t start = SysTick->VAL;
37+
uint32_t stop = start - cycles;
38+
if (start < cycles) {
39+
stop = 0xffffff + start - cycles;
40+
}
41+
while (SysTick->VAL > stop) {}
42+
}
43+
#endif
44+
45+
uint64_t next_start_tick_ms = 0;
46+
uint32_t next_start_tick_us = 1000;
3247

3348
void common_hal_neopixel_write(const digitalio_digitalinout_obj_t* digitalinout, uint8_t *pixels, uint32_t numBytes) {
3449
// 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,
3752
uint32_t pinMask;
3853
PortGroup* port;
3954

55+
// This must be called while interrupts are on in case we're waiting for a
56+
// future ms tick.
57+
wait_until(next_start_tick_ms, next_start_tick_us);
58+
4059
// Turn off interrupts of any kind during timing-sensitive code.
4160
mp_hal_disable_all_interrupts();
4261

62+
#ifdef SAMD21
4363
// Make sure the NVM cache is consistently timed.
4464
NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_DETERMINISTIC_Val;
65+
#endif
66+
4567

4668
uint32_t pin = digitalinout->pin->pin;
4769
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,
5678

5779
for(;;) {
5880
*set = pinMask;
81+
// This is the time where the line is always high regardless of the bit.
82+
// For the SK6812 its 0.3us +- 0.15us
83+
#ifdef SAMD21
5984
asm("nop; nop;");
85+
#endif
86+
#ifdef SAMD51
87+
delay_cycles(18);
88+
#endif
6089
if(p & bitMask) {
90+
// This is the high delay unique to a one bit.
91+
// For the SK6812 its 0.3us
92+
#ifdef SAMD21
6193
asm("nop; nop; nop; nop; nop; nop; nop;");
94+
#endif
95+
#ifdef SAMD51
96+
delay_cycles(25);
97+
#endif
6298
*clr = pinMask;
6399
} else {
64100
*clr = pinMask;
101+
// This is the low delay unique to a zero bit.
102+
// For the SK6812 its 0.3us
103+
#ifdef SAMD21
65104
asm("nop; nop;");
105+
#endif
106+
#ifdef SAMD51
107+
delay_cycles(25);
108+
#endif
66109
}
67110
if((bitMask >>= 1) != 0) {
111+
// This is the delay between bits in a byte and is the 1 code low
112+
// level time from the datasheet.
113+
// For the SK6812 its 0.6us +- 0.15us
114+
#ifdef SAMD21
68115
asm("nop; nop; nop; nop; nop;");
116+
#endif
117+
#ifdef SAMD51
118+
delay_cycles(44);
119+
#endif
69120
} else {
70121
if(ptr >= end) break;
71122
p = *ptr++;
72123
bitMask = 0x80;
124+
// This is the delay between bytes. Its similar to the other branch
125+
// in the if statement except its tuned to account for the time the
126+
// above operations take.
127+
// For the SK6812 its 0.6us +- 0.15us
128+
#ifdef SAMD51
129+
delay_cycles(50);
130+
#endif
73131
}
74132
}
75133

134+
#ifdef SAMD21
76135
// Speed up! (But inconsistent timing.)
77136
NVMCTRL->CTRLB.bit.READMODE = NVMCTRL_CTRLB_READMODE_NO_MISS_PENALTY_Val;
137+
#endif
138+
139+
// ticks_ms may be out of date at this point because we stopped the
140+
// interrupt. We'll risk it anyway.
141+
current_tick(&next_start_tick_ms, &next_start_tick_us);
142+
if (next_start_tick_us < 100) {
143+
next_start_tick_ms += 1;
144+
next_start_tick_us = 100 - next_start_tick_us;
145+
} else {
146+
next_start_tick_us -= 100;
147+
}
78148

79149
// Turn on interrupts after timing-sensitive code.
80150
mp_hal_enable_all_interrupts();
81151

82-
// 50us delay to let pixels latch to the data that was just sent.
83-
// This could be optimized to only occur before pixel writes when necessary,
84-
// like in the Arduino library.
85-
mp_hal_delay_us(50);
86152
}

ports/atmel-samd/tick.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,5 +66,17 @@ void tick_delay(uint32_t us) {
6666
start_ms = ticks_ms;
6767
us_between_ticks = 1000;
6868
}
69-
while (SysTick->VAL > ((1000 - us) * ticks_per_us)) {}
69+
while (SysTick->VAL > ((us_between_ticks - us) * ticks_per_us)) {}
70+
}
71+
72+
// us counts down!
73+
void current_tick(uint64_t* ms, uint32_t* us_until_ms) {
74+
uint32_t ticks_per_us = common_hal_mcu_processor_get_frequency() / 1000 / 1000;
75+
*ms = ticks_ms;
76+
*us_until_ms = SysTick->VAL / ticks_per_us;
77+
}
78+
79+
void wait_until(uint64_t ms, uint32_t us_until_ms) {
80+
uint32_t ticks_per_us = common_hal_mcu_processor_get_frequency() / 1000 / 1000;
81+
while(ticks_ms <= ms && SysTick->VAL / ticks_per_us >= us_until_ms) {}
7082
}

ports/atmel-samd/tick.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,4 +36,9 @@ void tick_init(void);
3636

3737
void tick_delay(uint32_t us);
3838

39+
void current_tick(uint64_t* ms, uint32_t* us_until_ms);
40+
// Do not call this with interrupts disabled because it may be waiting for
41+
// ticks_ms to increment.
42+
void wait_until(uint64_t ms, uint32_t us_until_ms);
43+
3944
#endif // MICROPY_INCLUDED_ATMEL_SAMD_TICK_H

supervisor/shared/rgb_led_status.c

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -199,12 +199,15 @@ void set_rgb_status_brightness(uint8_t level){
199199
rgb_status_brightness = level;
200200
}
201201

202-
void prep_rgb_status_animation(const pyexec_result_t* result, bool found_main, rgb_status_animation_t* status) {
202+
void prep_rgb_status_animation(const pyexec_result_t* result,
203+
bool found_main,
204+
safe_mode_t safe_mode,
205+
rgb_status_animation_t* status) {
203206
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
204207
new_status_color(ALL_DONE);
205208
status->pattern_start = ticks_ms;
206-
207-
uint32_t total_exception_cycle = 0;
209+
status->safe_mode = safe_mode;
210+
status->total_exception_cycle = 0;
208211
status->ones = result->exception_line % 10;
209212
status->ones += status->ones > 0 ? 1 : 0;
210213
status->tens = (result->exception_line / 10) % 10;
@@ -224,7 +227,7 @@ void prep_rgb_status_animation(const pyexec_result_t* result, bool found_main, r
224227
}
225228
status->ok = result->return_code != PYEXEC_EXCEPTION;
226229
if (!status->ok) {
227-
status->total_exception_cycle = EXCEPTION_TYPE_LENGTH_MS * 3 + LINE_NUMBER_TOGGLE_LENGTH * digit_sum + LINE_NUMBER_TOGGLE_LENGTH * num_places;
230+
status->total_exception_cycle = EXCEPTION_TYPE_LENGTH_MS * 3 + LINE_NUMBER_TOGGLE_LENGTH * status->digit_sum + LINE_NUMBER_TOGGLE_LENGTH * num_places;
228231
}
229232
if (mp_obj_is_subclass_fast(result->exception_type, &mp_type_IndentationError)) {
230233
status->exception_color = INDENTATION_ERROR;
@@ -244,26 +247,26 @@ void prep_rgb_status_animation(const pyexec_result_t* result, bool found_main, r
244247

245248
void tick_rgb_status_animation(rgb_status_animation_t* status) {
246249
#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
247-
uint32_t tick_diff = ticks_ms - pattern_start;
250+
uint32_t tick_diff = ticks_ms - status->pattern_start;
248251
if (status->ok) {
249252
// All is good. Ramp ALL_DONE up and down.
250253
if (tick_diff > ALL_GOOD_CYCLE_MS) {
251-
pattern_start = ticks_ms;
254+
status->pattern_start = ticks_ms;
252255
tick_diff = 0;
253256
}
254257

255258
uint16_t brightness = tick_diff * 255 / (ALL_GOOD_CYCLE_MS / 2);
256259
if (brightness > 255) {
257260
brightness = 511 - brightness;
258261
}
259-
if (safe_mode == NO_SAFE_MODE) {
262+
if (status->safe_mode == NO_SAFE_MODE) {
260263
new_status_color(color_brightness(ALL_DONE, brightness));
261264
} else {
262265
new_status_color(color_brightness(SAFE_MODE, brightness));
263266
}
264267
} else {
265-
if (tick_diff > total_exception_cycle) {
266-
pattern_start = ticks_ms;
268+
if (tick_diff > status->total_exception_cycle) {
269+
status->pattern_start = ticks_ms;
267270
tick_diff = 0;
268271
}
269272
// First flash the file color.
@@ -275,34 +278,34 @@ void tick_rgb_status_animation(rgb_status_animation_t* status) {
275278
}
276279
// Next flash the exception color.
277280
} else if (tick_diff < EXCEPTION_TYPE_LENGTH_MS * 2) {
278-
new_status_color(exception_color);
281+
new_status_color(status->exception_color);
279282
// Finally flash the line number digits from highest to lowest.
280283
// Zeroes will not produce a flash but can be read by the absence of
281284
// a color from the sequence.
282-
} else if (tick_diff < (EXCEPTION_TYPE_LENGTH_MS * 2 + LINE_NUMBER_TOGGLE_LENGTH * digit_sum)) {
285+
} else if (tick_diff < (EXCEPTION_TYPE_LENGTH_MS * 2 + LINE_NUMBER_TOGGLE_LENGTH * status->digit_sum)) {
283286
uint32_t digit_diff = tick_diff - EXCEPTION_TYPE_LENGTH_MS * 2;
284287
if ((digit_diff % LINE_NUMBER_TOGGLE_LENGTH) < (LINE_NUMBER_TOGGLE_LENGTH / 2)) {
285288
new_status_color(BLACK);
286-
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * thousands) {
289+
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * status->thousands) {
287290
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH) {
288291
new_status_color(BLACK);
289292
} else {
290293
new_status_color(THOUSANDS);
291294
}
292-
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds)) {
293-
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + 1)) {
295+
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (status->thousands + status->hundreds)) {
296+
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (status->thousands + 1)) {
294297
new_status_color(BLACK);
295298
} else {
296299
new_status_color(HUNDREDS);
297300
}
298-
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + tens)) {
299-
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + 1)) {
301+
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (status->thousands + status->hundreds + status->tens)) {
302+
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (status->thousands + status->hundreds + 1)) {
300303
new_status_color(BLACK);
301304
} else {
302305
new_status_color(TENS);
303306
}
304307
} else {
305-
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + tens + 1)) {
308+
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (status->thousands + status->hundreds + status->tens + 1)) {
306309
new_status_color(BLACK);
307310
} else {
308311
new_status_color(ONES);

supervisor/shared/rgb_led_status.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include <stdbool.h>
3232

3333
#include "lib/utils/pyexec.h"
34+
#include "supervisor/port.h"
3435

3536
#include "mpconfigport.h"
3637
#include "rgb_led_colors.h"
@@ -59,6 +60,8 @@ typedef struct {
5960
bool ok;
6061
uint32_t pattern_start;
6162
uint32_t total_exception_cycle;
63+
safe_mode_t safe_mode;
64+
uint8_t digit_sum;
6265
uint8_t ones;
6366
uint8_t tens;
6467
uint8_t hundreds;
@@ -67,7 +70,10 @@ typedef struct {
6770
bool found_main;
6871
} rgb_status_animation_t;
6972

70-
void prep_rgb_status_animation(const pyexec_result_t* result, bool found_main, rgb_status_animation_t* status);
73+
void prep_rgb_status_animation(const pyexec_result_t* result,
74+
bool found_main,
75+
safe_mode_t safe_mode,
76+
rgb_status_animation_t* status);
7177
void tick_rgb_status_animation(rgb_status_animation_t* status);
7278

7379
#endif // MICROPY_INCLUDED_SUPERVISOR_RGB_LED_STATUS_H

0 commit comments

Comments
 (0)