Skip to content

Commit b6f1eeb

Browse files
committed
atmel-samd: Add APA102 support and flash more advanced status.
The new sequence is as follows: * Solid blue during the boot/settings script. * Solid green during the main/code script. * After main while waiting to enter repl or reset: * Fading green once main is done successfully. * On error produce a series of flashes: * Long flash color of script. * Long flash color of error: * Green = IndentationError * Cyan = SyntaxError * White = NameError * Orange = OSError * Yellow = Other error * Line number of the exception by digit. Number of flashes represents value. * Thousands = White * Hundreds = Blue * Tens = Yellow * Ones = Cyan * Off for a period and then repeats. At any point a write to the flash storage will flicker red. Fixes adafruit#63
1 parent 6225b89 commit b6f1eeb

File tree

16 files changed

+371
-127
lines changed

16 files changed

+371
-127
lines changed

atmel-samd/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ SRC_C = \
178178
moduos.c \
179179
mphalport.c \
180180
samd21_pins.c \
181-
neopixel_status.c \
181+
rgb_led_status.c \
182182
tick.c \
183183
$(FLASH_IMPL) \
184184
asf/common/services/sleepmgr/samd/sleepmgr.c \

atmel-samd/boards/gemma_m0/mpconfigboard.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#define MICROPY_HW_BOARD_NAME "Adafruit Gemma M0 (Experimental)"
44
#define MICROPY_HW_MCU_NAME "samd21e18"
55

6-
#define MICROPY_HW_APA102_SERCOM SERCOM0
76
#define MICROPY_HW_APA102_MOSI &pin_PA04
87
#define MICROPY_HW_APA102_SCK &pin_PA05
98

atmel-samd/boards/trinket_m0/mpconfigboard.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
#define MICROPY_HW_BOARD_NAME "Adafruit Trinket M0 (Experimental)"
44
#define MICROPY_HW_MCU_NAME "samd21e18"
55

6-
#define MICROPY_HW_APA102_SERCOM SERCOM0
76
#define MICROPY_HW_APA102_MOSI &pin_PA04
87
#define MICROPY_HW_APA102_SCK &pin_PA05
98

atmel-samd/internal_flash.c

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
#include "asf/sam0/drivers/nvm/nvm.h"
3838
#include "asf/sam0/drivers/port/port.h"
3939

40-
#include "neopixel_status.h"
40+
#include "rgb_led_status.h"
4141

4242
#define TOTAL_INTERNAL_FLASH_SIZE 0x010000
4343

@@ -174,9 +174,7 @@ bool internal_flash_write_block(const uint8_t *src, uint32_t block) {
174174
#ifdef MICROPY_HW_LED_MSC
175175
port_pin_set_output_level(MICROPY_HW_LED_MSC, true);
176176
#endif
177-
#ifdef MICROPY_HW_NEOPIXEL
178-
temp_status_color(0x8f, 0x00, 0x00);
179-
#endif
177+
temp_status_color(ACTIVE_WRITE);
180178
// non-MBR block, copy to cache
181179
int32_t dest = convert_block_to_flash_addr(block);
182180
if (dest == -1) {
@@ -214,9 +212,7 @@ bool internal_flash_write_block(const uint8_t *src, uint32_t block) {
214212
return false;
215213
}
216214
}
217-
#ifdef MICROPY_HW_NEOPIXEL
218-
clear_temp_status();
219-
#endif
215+
clear_temp_status();
220216
#ifdef MICROPY_HW_LED_MSC
221217
port_pin_set_output_level(MICROPY_HW_LED_MSC, false);
222218
#endif

atmel-samd/main.c

Lines changed: 178 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "lib/fatfs/ff.h"
1212
#include "lib/fatfs/diskio.h"
13+
#include "lib/mp-readline/readline.h"
1314
#include "lib/utils/pyexec.h"
1415
#include "extmod/fsusermount.h"
1516

@@ -23,7 +24,7 @@
2324

2425
#include "autoreset.h"
2526
#include "mpconfigboard.h"
26-
#include "neopixel_status.h"
27+
#include "rgb_led_status.h"
2728
#include "tick.h"
2829

2930
fs_user_mount_t fs_user_mount_flash;
@@ -131,7 +132,7 @@ static char *stack_top;
131132
static char heap[16384];
132133

133134
void reset_mp(void) {
134-
new_status_color(0x8f, 0x00, 0x8f);
135+
new_status_color(0x8f008f);
135136
autoreset_stop();
136137
autoreset_enable();
137138

@@ -181,7 +182,7 @@ void reset_samd21(void) {
181182
system_pinmux_group_set_config(&(PORT->Group[1]), pin_mask[1] & ~MICROPY_PORT_B, &config);
182183
}
183184

184-
bool maybe_run(const char* filename, int* ret) {
185+
bool maybe_run(const char* filename, pyexec_result_t* exec_result) {
185186
FILINFO fno;
186187
#if _USE_LFN
187188
fno.lfname = NULL;
@@ -193,32 +194,171 @@ bool maybe_run(const char* filename, int* ret) {
193194
}
194195
mp_hal_stdout_tx_str(filename);
195196
mp_hal_stdout_tx_str(" output:\r\n");
196-
*ret = pyexec_file(filename);
197+
pyexec_file(filename, exec_result);
197198
return true;
198199
}
199200

200-
void start_mp(void) {
201+
bool start_mp(void) {
202+
bool cdc_enabled_at_start = mp_cdc_enabled;
201203
#ifdef AUTORESET_DELAY_MS
204+
if (cdc_enabled_at_start) {
202205
mp_hal_stdout_tx_str("\r\n");
203206
mp_hal_stdout_tx_str("Auto-soft reset is on. Simply save files over USB to run them.\r\n");
204-
mp_hal_stdout_tx_str("Type anything into the REPL to disable and manually reset (CTRL-D) to re-enable.\r\n");
207+
}
205208
#endif
206209

207-
new_status_color(0x00, 0x00, 0x8f);
208-
int ret = 0;
209-
bool found_boot = maybe_run("settings.txt", &ret) ||
210-
maybe_run("settings.py", &ret) ||
211-
maybe_run("boot.py", &ret) ||
212-
maybe_run("boot.txt", &ret);
213-
if (found_boot && ret & PYEXEC_FORCED_EXIT) {
214-
return;
210+
new_status_color(BOOT_RUNNING);
211+
pyexec_result_t result;
212+
bool found_boot = maybe_run("settings.txt", &result) ||
213+
maybe_run("settings.py", &result) ||
214+
maybe_run("boot.py", &result) ||
215+
maybe_run("boot.txt", &result);
216+
bool found_main = false;
217+
if (!found_boot || !(result.return_code & PYEXEC_FORCED_EXIT)) {
218+
new_status_color(MAIN_RUNNING);
219+
found_main = maybe_run("code.txt", &result) ||
220+
maybe_run("code.py", &result) ||
221+
maybe_run("main.py", &result) ||
222+
maybe_run("main.txt", &result);
223+
}
224+
225+
if (result.return_code & PYEXEC_FORCED_EXIT) {
226+
return reset_next_character;
215227
}
216228

217-
new_status_color(0x00, 0x8f, 0x00);
218-
maybe_run("code.txt", &ret) ||
219-
maybe_run("code.py", &ret) ||
220-
maybe_run("main.py", &ret) ||
221-
maybe_run("main.txt", &ret);
229+
// If not is USB mode then do not skip the repl.
230+
#ifndef USB_REPL
231+
return false;
232+
#endif
233+
234+
// Wait for connection or character.
235+
new_status_color(ALL_DONE);
236+
bool cdc_enabled_before = false;
237+
uint32_t pattern_start = ticks_ms;
238+
239+
printf("result code %d %d.\r\n", result.return_code, result.exception_line);
240+
uint32_t total_exception_cycle = 0;
241+
uint8_t ones = result.exception_line % 10;
242+
ones += ones > 0 ? 1 : 0;
243+
uint8_t tens = (result.exception_line / 10) % 10;
244+
tens += tens > 0 ? 1 : 0;
245+
uint8_t hundreds = (result.exception_line / 100) % 10;
246+
hundreds += hundreds > 0 ? 1 : 0;
247+
uint8_t thousands = (result.exception_line / 1000) % 10;
248+
thousands += thousands > 0 ? 1 : 0;
249+
uint8_t digit_sum = ones + tens + hundreds + thousands;
250+
uint8_t num_places = 0;
251+
uint16_t line = result.exception_line;
252+
for (int i = 0; i < 4; i++) {
253+
if ((line % 10) > 0) {
254+
num_places++;
255+
}
256+
line /= 10;
257+
}
258+
if (result.return_code == PYEXEC_EXCEPTION) {
259+
total_exception_cycle = EXCEPTION_TYPE_LENGTH_MS * 3 + LINE_NUMBER_TOGGLE_LENGTH * digit_sum + LINE_NUMBER_TOGGLE_LENGTH * num_places;
260+
}
261+
while (true) {
262+
#ifdef MICROPY_VM_HOOK_LOOP
263+
MICROPY_VM_HOOK_LOOP
264+
#endif
265+
if (reset_next_character) {
266+
return true;
267+
}
268+
if (usb_rx_count > 0) {
269+
// Skip REPL if reset was requested.
270+
return receive_usb() == CHAR_CTRL_D;
271+
}
272+
273+
if (!cdc_enabled_before && mp_cdc_enabled) {
274+
if (cdc_enabled_at_start) {
275+
mp_hal_stdout_tx_str("\r\n\r\n");
276+
} else {
277+
printf("result code %d %d.\r\n", result.return_code, result.exception_line);
278+
mp_hal_stdout_tx_str("Auto-soft reset is on. Simply save files over USB to run them.\r\n");
279+
}
280+
mp_hal_stdout_tx_str("Press any key to enter the REPL and disable auto-reset. Use CTRL-D to soft reset.\r\n");
281+
}
282+
if (cdc_enabled_before && !mp_cdc_enabled) {
283+
cdc_enabled_at_start = false;
284+
}
285+
cdc_enabled_before = mp_cdc_enabled;
286+
287+
uint32_t tick_diff = ticks_ms - pattern_start;
288+
if (result.return_code != PYEXEC_EXCEPTION) {
289+
// All is good. Ramp ALL_DONE up and down.
290+
if (tick_diff > ALL_GOOD_CYCLE_MS) {
291+
pattern_start = ticks_ms;
292+
tick_diff = 0;
293+
}
294+
295+
uint16_t brightness = tick_diff * 255 / (ALL_GOOD_CYCLE_MS / 2);
296+
if (brightness > 255) {
297+
brightness = 511 - brightness;
298+
}
299+
new_status_color(color_brightness(ALL_DONE, brightness));
300+
} else {
301+
if (tick_diff > total_exception_cycle) {
302+
pattern_start = ticks_ms;
303+
tick_diff = 0;
304+
}
305+
// First flash the file color.
306+
if (tick_diff < EXCEPTION_TYPE_LENGTH_MS) {
307+
if (found_main) {
308+
new_status_color(MAIN_RUNNING);
309+
} else {
310+
new_status_color(BOOT_RUNNING);
311+
}
312+
// Next flash the exception color.
313+
} else if (tick_diff < EXCEPTION_TYPE_LENGTH_MS * 2) {
314+
if (mp_obj_is_subclass_fast(result.exception_type, &mp_type_IndentationError)) {
315+
new_status_color(INDENTATION_ERROR);
316+
} else if (mp_obj_is_subclass_fast(result.exception_type, &mp_type_SyntaxError)) {
317+
new_status_color(SYNTAX_ERROR);
318+
} else if (mp_obj_is_subclass_fast(result.exception_type, &mp_type_NameError)) {
319+
new_status_color(NAME_ERROR);
320+
} else if (mp_obj_is_subclass_fast(result.exception_type, &mp_type_OSError)) {
321+
new_status_color(OS_ERROR);
322+
} else {
323+
new_status_color(OTHER_ERROR);
324+
}
325+
// Finally flash the line number digits from highest to lowest.
326+
// Zeroes will not produce a flash but can be read by the absence of
327+
// a color from the sequence.
328+
} else if (tick_diff < (EXCEPTION_TYPE_LENGTH_MS * 2 + LINE_NUMBER_TOGGLE_LENGTH * digit_sum)) {
329+
uint32_t digit_diff = tick_diff - EXCEPTION_TYPE_LENGTH_MS * 2;
330+
if ((digit_diff % LINE_NUMBER_TOGGLE_LENGTH) < (LINE_NUMBER_TOGGLE_LENGTH / 2)) {
331+
new_status_color(BLACK);
332+
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * thousands) {
333+
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH) {
334+
new_status_color(BLACK);
335+
} else {
336+
new_status_color(THOUSANDS);
337+
}
338+
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds)) {
339+
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + 1)) {
340+
new_status_color(BLACK);
341+
} else {
342+
new_status_color(HUNDREDS);
343+
}
344+
} else if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + tens)) {
345+
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + 1)) {
346+
new_status_color(BLACK);
347+
} else {
348+
new_status_color(TENS);
349+
}
350+
} else {
351+
if (digit_diff < LINE_NUMBER_TOGGLE_LENGTH * (thousands + hundreds + tens + 1)) {
352+
new_status_color(BLACK);
353+
} else {
354+
new_status_color(ONES);
355+
}
356+
}
357+
} else {
358+
new_status_color(BLACK);
359+
}
360+
}
361+
}
222362
}
223363

224364
#ifdef UART_REPL
@@ -283,7 +423,7 @@ void samd21_init(void) {
283423
// port_pin_set_config(MICROPY_HW_LED1, &pin_conf);
284424
// port_pin_set_output_level(MICROPY_HW_LED1, false);
285425

286-
neopixel_status_init();
426+
rgb_led_status_init();
287427
}
288428

289429
int main(int argc, char **argv) {
@@ -307,24 +447,29 @@ int main(int argc, char **argv) {
307447
udc_start();
308448
#endif
309449

310-
// Run boot and main.
311-
start_mp();
312-
313450
// Main script is finished, so now go into REPL mode.
314451
// The REPL mode can change, or it can request a soft reset.
315-
int exit_code = 0;
452+
int exit_code = PYEXEC_FORCED_EXIT;
453+
bool skip_repl = true;
454+
bool first_run = true;
316455
for (;;) {
317-
new_status_color(0x3f, 0x3f, 0x3f);
318-
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
319-
exit_code = pyexec_raw_repl();
320-
} else {
321-
exit_code = pyexec_friendly_repl();
456+
if (!skip_repl) {
457+
autoreset_disable();
458+
new_status_color(REPL_RUNNING);
459+
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
460+
exit_code = pyexec_raw_repl();
461+
} else {
462+
exit_code = pyexec_friendly_repl();
463+
}
322464
}
323465
if (exit_code == PYEXEC_FORCED_EXIT) {
324-
mp_hal_stdout_tx_str("soft reboot\r\n");
325-
reset_samd21();
326-
reset_mp();
327-
start_mp();
466+
if (!first_run) {
467+
mp_hal_stdout_tx_str("soft reboot\r\n");
468+
reset_samd21();
469+
reset_mp();
470+
}
471+
first_run = false;
472+
skip_repl = start_mp();
328473
} else if (exit_code != 0) {
329474
break;
330475
}

atmel-samd/mphalport.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ static volatile uint8_t usb_rx_buf_head;
3030
static volatile uint8_t usb_rx_buf_tail;
3131

3232
// Number of bytes in receive buffer
33-
static volatile uint8_t usb_rx_count;
33+
volatile uint8_t usb_rx_count;
3434

35-
static volatile bool mp_cdc_enabled = false;
35+
volatile bool mp_cdc_enabled = false;
3636

3737
void mp_keyboard_interrupt(void);
3838
int interrupt_char;

atmel-samd/mphalport.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ extern volatile uint64_t ticks_ms;
3737
static inline mp_uint_t mp_hal_ticks_ms(void) {
3838
return ticks_ms;
3939
}
40+
// Number of bytes in receive buffer
41+
volatile uint8_t usb_rx_count;
42+
volatile bool mp_cdc_enabled;
43+
44+
int receive_usb(void);
4045

4146
void mp_hal_set_interrupt_char(int c);
4247

atmel-samd/neopixel_status.c

Lines changed: 0 additions & 43 deletions
This file was deleted.

0 commit comments

Comments
 (0)