Skip to content

Commit 20946d9

Browse files
authored
Merge pull request #4743 from tannewt/simplify_status_led
Simplify the status LED to save power
2 parents 22e8b20 + 561ffec commit 20946d9

File tree

53 files changed

+552
-1220
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

53 files changed

+552
-1220
lines changed

main.c

Lines changed: 139 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
#include "supervisor/port.h"
5454
#include "supervisor/serial.h"
5555
#include "supervisor/shared/autoreload.h"
56-
#include "supervisor/shared/rgb_led_status.h"
5756
#include "supervisor/shared/safe_mode.h"
5857
#include "supervisor/shared/stack.h"
5958
#include "supervisor/shared/status_leds.h"
@@ -114,7 +113,6 @@ static void reset_devices(void) {
114113
}
115114

116115
STATIC void start_mp(supervisor_allocation* heap) {
117-
reset_status_led();
118116
autoreload_stop();
119117
supervisor_workflow_reset();
120118

@@ -251,7 +249,6 @@ STATIC void cleanup_after_vm(supervisor_allocation* heap) {
251249
#endif
252250
reset_port();
253251
reset_board();
254-
reset_status_led();
255252
}
256253

257254
STATIC void print_code_py_status_message(safe_mode_t safe_mode) {
@@ -286,8 +283,6 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
286283
bool found_main = false;
287284

288285
if (safe_mode == NO_SAFE_MODE) {
289-
new_status_color(MAIN_RUNNING);
290-
291286
static const char * const supported_filenames[] = STRING_LIST(
292287
"code.txt", "code.py", "main.py", "main.txt");
293288
#if CIRCUITPY_FULL_BUILD
@@ -317,6 +312,8 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
317312
serial_write_compressed(translate("WARNING: Your code filename has two extensions\n"));
318313
}
319314
}
315+
#else
316+
(void) found_main;
320317
#endif
321318

322319
// Finished executing python code. Cleanup includes a board reset.
@@ -334,42 +331,62 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
334331
}
335332

336333
// Program has finished running.
337-
338334
bool printed_press_any_key = false;
339335
#if CIRCUITPY_DISPLAYIO
340-
bool refreshed_epaper_display = false;
336+
size_t time_to_epaper_refresh = 1;
341337
#endif
342338

343-
rgb_status_animation_t animation;
344-
prep_rgb_status_animation(&result, found_main, safe_mode, &animation);
339+
// Setup LED blinks.
340+
#if CIRCUITPY_STATUS_LED
341+
uint32_t color;
342+
uint8_t blink_count;
343+
bool led_active = false;
344+
#if CIRCUITPY_ALARM
345+
if (result.return_code & PYEXEC_DEEP_SLEEP) {
346+
color = BLACK;
347+
blink_count = 0;
348+
} else
349+
#endif
350+
if (result.return_code != PYEXEC_EXCEPTION) {
351+
if (safe_mode == NO_SAFE_MODE) {
352+
color = ALL_DONE;
353+
blink_count = ALL_DONE_BLINKS;
354+
} else {
355+
color = SAFE_MODE;
356+
blink_count = SAFE_MODE_BLINKS;
357+
}
358+
} else {
359+
color = EXCEPTION;
360+
blink_count = EXCEPTION_BLINKS;
361+
}
362+
size_t pattern_start = supervisor_ticks_ms32();
363+
size_t single_blink_time = (OFF_ON_RATIO + 1) * BLINK_TIME_MS;
364+
size_t blink_time = single_blink_time * blink_count;
365+
size_t total_time = blink_time + LED_SLEEP_TIME_MS;
366+
#endif
367+
368+
#if CIRCUITPY_ALARM
345369
bool fake_sleeping = false;
370+
#endif
371+
bool skip_repl = false;
346372
while (true) {
347373
RUN_BACKGROUND_TASKS;
348374

349375
// If a reload was requested by the supervisor or autoreload, return
350376
if (reload_requested) {
351-
#if CIRCUITPY_ALARM
352-
if (fake_sleeping) {
353-
board_init();
354-
}
355-
#endif
356377
reload_requested = false;
357-
return true;
378+
skip_repl = true;
379+
break;
358380
}
359381

360382
// If interrupted by keyboard, return
361383
if (serial_connected() && serial_bytes_available()) {
362-
#if CIRCUITPY_ALARM
363-
if (fake_sleeping) {
364-
board_init();
365-
}
366-
#endif
367384
// Skip REPL if reload was requested.
368-
bool ctrl_d = serial_read() == CHAR_CTRL_D;
369-
if (ctrl_d) {
385+
skip_repl = serial_read() == CHAR_CTRL_D;
386+
if (skip_repl) {
370387
supervisor_set_run_reason(RUN_REASON_REPL_RELOAD);
371388
}
372-
return ctrl_d;
389+
break;
373390
}
374391

375392
// Check for a deep sleep alarm and restart the VM. This can happen if
@@ -378,9 +395,9 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
378395
#if CIRCUITPY_ALARM
379396
if (fake_sleeping && common_hal_alarm_woken_from_sleep()) {
380397
serial_write_compressed(translate("Woken up by alarm.\n"));
381-
board_init();
382398
supervisor_set_run_reason(RUN_REASON_STARTUP);
383-
return true;
399+
skip_repl = true;
400+
break;
384401
}
385402
#endif
386403

@@ -403,26 +420,21 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
403420
printed_press_any_key = false;
404421
}
405422

406-
// Refresh the ePaper display if we have one. That way it'll show an error message.
407-
#if CIRCUITPY_DISPLAYIO
408-
// Don't refresh the display if we're about to deep sleep.
409-
#if CIRCUITPY_ALARM
410-
refreshed_epaper_display = refreshed_epaper_display || result.return_code & PYEXEC_DEEP_SLEEP;
411-
#endif
412-
if (!refreshed_epaper_display) {
413-
refreshed_epaper_display = maybe_refresh_epaperdisplay();
414-
}
415-
#endif
416-
417423
// Sleep until our next interrupt.
418424
#if CIRCUITPY_ALARM
419425
if (result.return_code & PYEXEC_DEEP_SLEEP) {
420426
// Make sure we have been awake long enough for USB to connect (enumeration delay).
421427
int64_t connecting_delay_ticks = CIRCUITPY_USB_CONNECTED_SLEEP_DELAY * 1024 - port_get_raw_ticks(NULL);
422-
// Until it's safe to decide whether we're real/fake sleeping, just run the RGB
423-
if (connecting_delay_ticks < 0 && !fake_sleeping) {
424-
fake_sleeping = true;
425-
new_status_color(BLACK);
428+
// Until it's safe to decide whether we're real/fake sleeping
429+
if (fake_sleeping) {
430+
// This waits until a pretend deep sleep alarm occurs. They are set
431+
// during common_hal_alarm_set_deep_sleep_alarms. On some platforms
432+
// it may also return due to another interrupt, that's why we check
433+
// for deep sleep alarms above. If it wasn't a deep sleep alarm,
434+
// then we'll idle here again.
435+
common_hal_alarm_pretending_deep_sleep();
436+
} else if (connecting_delay_ticks < 0) {
437+
// Entering deep sleep (may be fake or real.)
426438
board_deinit();
427439
if (!supervisor_workflow_active()) {
428440
// Enter true deep sleep. When we wake up we'll be back at the
@@ -431,27 +443,85 @@ STATIC bool run_code_py(safe_mode_t safe_mode) {
431443
// Does not return.
432444
} else {
433445
serial_write_compressed(translate("Pretending to deep sleep until alarm, CTRL-C or file write.\n"));
446+
fake_sleeping = true;
434447
}
448+
} else {
449+
// Loop while checking the time. We can't idle because we don't want to override a
450+
// time alarm set for the deep sleep.
435451
}
436-
}
452+
} else
437453
#endif
454+
{
455+
// Refresh the ePaper display if we have one. That way it'll show an error message.
456+
#if CIRCUITPY_DISPLAYIO
457+
if (time_to_epaper_refresh > 0) {
458+
time_to_epaper_refresh = maybe_refresh_epaperdisplay();
459+
}
438460

439-
if (!fake_sleeping) {
440-
tick_rgb_status_animation(&animation);
441-
} else {
442-
// This waits until a pretend deep sleep alarm occurs. They are set
443-
// during common_hal_alarm_set_deep_sleep_alarms. On some platforms
444-
// it may also return due to another interrupt, that's why we check
445-
// for deep sleep alarms above. If it wasn't a deep sleep alarm,
446-
// then we'll idle here again.
461+
#if !CIRCUITPY_STATUS_LED
462+
port_interrupt_after_ticks(time_to_epaper_refresh);
463+
#endif
464+
#endif
447465

448-
#if CIRCUITPY_ALARM
449-
common_hal_alarm_pretending_deep_sleep();
450-
#else
451-
port_idle_until_interrupt();
466+
#if CIRCUITPY_STATUS_LED
467+
uint32_t tick_diff = supervisor_ticks_ms32() - pattern_start;
468+
469+
// By default, don't sleep.
470+
size_t time_to_next_change = 0;
471+
if (tick_diff < blink_time) {
472+
uint32_t blink_diff = tick_diff % (single_blink_time);
473+
if (blink_diff >= BLINK_TIME_MS) {
474+
if (led_active) {
475+
new_status_color(BLACK);
476+
status_led_deinit();
477+
led_active = false;
478+
}
479+
time_to_next_change = single_blink_time - blink_diff;
480+
} else {
481+
if (!led_active) {
482+
status_led_init();
483+
new_status_color(color);
484+
led_active = true;
485+
}
486+
time_to_next_change = BLINK_TIME_MS - blink_diff;
487+
}
488+
} else if (tick_diff > total_time) {
489+
pattern_start = supervisor_ticks_ms32();
490+
} else {
491+
if (led_active) {
492+
new_status_color(BLACK);
493+
status_led_deinit();
494+
led_active = false;
495+
}
496+
time_to_next_change = total_time - tick_diff;
497+
}
498+
#if CIRCUITPY_DISPLAYIO
499+
if (time_to_epaper_refresh > 0 && time_to_next_change > 0) {
500+
time_to_next_change = MIN(time_to_next_change, time_to_epaper_refresh);
501+
}
502+
#endif
503+
504+
// time_to_next_change is in ms and ticks are slightly shorter so
505+
// we'll undersleep just a little. It shouldn't matter.
506+
port_interrupt_after_ticks(time_to_next_change);
452507
#endif
508+
port_idle_until_interrupt();
453509
}
454510
}
511+
// Done waiting, start the board back up.
512+
#if CIRCUITPY_STATUS_LED
513+
if (led_active) {
514+
new_status_color(BLACK);
515+
status_led_deinit();
516+
}
517+
#endif
518+
519+
#if CIRCUITPY_ALARM
520+
if (fake_sleeping) {
521+
board_init();
522+
}
523+
#endif
524+
return skip_repl;
455525
}
456526

457527
FIL* boot_output_file;
@@ -468,7 +538,6 @@ STATIC void __attribute__ ((noinline)) run_boot_py(safe_mode_t safe_mode) {
468538
bool skip_boot_output = false;
469539

470540
if (ok_to_run) {
471-
new_status_color(BOOT_RUNNING);
472541

473542
#ifdef CIRCUITPY_BOOT_OUTPUT_FILE
474543
FIL file_pointer;
@@ -578,13 +647,27 @@ STATIC int run_repl(void) {
578647
#endif
579648

580649
autoreload_suspend();
650+
651+
// Set the status LED to the REPL color before running the REPL. For
652+
// NeoPixels and DotStars this will be sticky but for PWM or single LED it
653+
// won't. This simplifies pin sharing because they won't be in use when
654+
// actually in the REPL.
655+
#if CIRCUITPY_STATUS_LED
656+
status_led_init();
581657
new_status_color(REPL_RUNNING);
658+
status_led_deinit();
659+
#endif
582660
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
583661
exit_code = pyexec_raw_repl();
584662
} else {
585663
exit_code = pyexec_friendly_repl();
586664
}
587665
cleanup_after_vm(heap);
666+
#if CIRCUITPY_STATUS_LED
667+
status_led_init();
668+
new_status_color(BLACK);
669+
status_led_deinit();
670+
#endif
588671
autoreload_resume();
589672
return exit_code;
590673
}
@@ -593,9 +676,8 @@ int __attribute__((used)) main(void) {
593676
// initialise the cpu and peripherals
594677
safe_mode_t safe_mode = port_init();
595678

596-
// Turn on LEDs
597-
init_status_leds();
598-
rgb_led_status_init();
679+
// Turn on RX and TX LEDs if we have them.
680+
init_rxtx_leds();
599681

600682
// Wait briefly to give a reset window where we'll enter safe mode after the reset.
601683
if (safe_mode == NO_SAFE_MODE) {

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@
66
// Rev E
77

88
#define MICROPY_HW_LED_STATUS (&pin_PA23)
9-
#define MICROPY_HW_NEOPIXEL (&pin_PB03)
9+
#define MICROPY_HW_NEOPIXEL (&pin_PB02)
10+
#define CIRCUITPY_STATUS_LED_POWER (&pin_PB03)
1011

1112
// These are pins not to reset.
1213
// QSPI Data pins
1314
#define MICROPY_PORT_A (PORT_PA08 | PORT_PA09 | PORT_PA10 | PORT_PA11)
1415
// QSPI CS, QSPI SCK and NeoPixel pin
15-
#define MICROPY_PORT_B (PORT_PB03 | PORT_PB10 | PORT_PB11)
16+
#define MICROPY_PORT_B (PORT_PB10 | PORT_PB11)
1617
#define MICROPY_PORT_C (0)
1718
#define MICROPY_PORT_D (0)
1819

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
// QSPI Data pins
1313
#define MICROPY_PORT_A (PORT_PA08 | PORT_PA09 | PORT_PA10 | PORT_PA11)
1414
// QSPI CS, QSPI SCK and NeoPixel pin
15-
#define MICROPY_PORT_B (PORT_PB03 | PORT_PB10 | PORT_PB11)
15+
#define MICROPY_PORT_B (PORT_PB10 | PORT_PB11)
1616
#define MICROPY_PORT_C (0)
1717
#define MICROPY_PORT_D (0)
1818

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

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,9 @@
3333
#define DEFAULT_SPI_BUS_SCK (&pin_PA17)
3434
#define DEFAULT_SPI_BUS_MOSI (&pin_PA16)
3535

36-
// #define CP_RGB_STATUS_R (&pin_PA06)
37-
// #define CP_RGB_STATUS_G (&pin_PA05)
38-
// #define CP_RGB_STATUS_B (&pin_PA07)
39-
// #define CP_RGB_STATUS_INVERTED_PWM
40-
// #define CP_RGB_STATUS_LED
36+
// #define CIRCUITPY_RGB_STATUS_R (&pin_PA06)
37+
// #define CIRCUITPY_RGB_STATUS_G (&pin_PA05)
38+
// #define CIRCUITPY_RGB_STATUS_B (&pin_PA07)
39+
// #define CIRCUITPY_RGB_STATUS_INVERTED_PWM
4140

4241
#define MICROPY_HW_LED_STATUS (&pin_PA06)

ports/atmel-samd/boards/qtpy_m0/board.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,8 @@
2525
*/
2626

2727
#include "supervisor/board.h"
28-
#include "common-hal/microcontroller/Pin.h"
29-
#include "supervisor/shared/board.h"
30-
#include "hal/include/hal_gpio.h"
3128

3229
void board_init(void) {
33-
gpio_set_pin_function(PIN_PA15, GPIO_PIN_FUNCTION_OFF);
34-
gpio_set_pin_direction(PIN_PA15, GPIO_DIRECTION_OUT);
35-
gpio_set_pin_level(PIN_PA15, true); // Turn on neopixel by default
36-
never_reset_pin_number(PIN_PA15);
3730
}
3831

3932
bool board_requests_safe_mode(void) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define MICROPY_HW_MCU_NAME "samd21e18"
33

44
#define MICROPY_HW_NEOPIXEL (&pin_PA18)
5+
#define CIRCUITPY_STATUS_LED_POWER (&pin_PA15)
56

67
#define MICROPY_PORT_A (0)
78
#define MICROPY_PORT_B (0)

ports/atmel-samd/boards/qtpy_m0_haxpress/board.c

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,15 +25,8 @@
2525
*/
2626

2727
#include "supervisor/board.h"
28-
#include "common-hal/microcontroller/Pin.h"
29-
#include "supervisor/shared/board.h"
30-
#include "hal/include/hal_gpio.h"
3128

3229
void board_init(void) {
33-
gpio_set_pin_function(PIN_PA15, GPIO_PIN_FUNCTION_OFF);
34-
gpio_set_pin_direction(PIN_PA15, GPIO_DIRECTION_OUT);
35-
gpio_set_pin_level(PIN_PA15, true); // Turn on neopixel by default
36-
never_reset_pin_number(PIN_PA15);
3730
}
3831

3932
bool board_requests_safe_mode(void) {

0 commit comments

Comments
 (0)