From 4c3976bbcaf58266fdcaab264fee5b7a94a682e5 Mon Sep 17 00:00:00 2001 From: Damien George Date: Wed, 28 Oct 2020 00:44:18 +1100 Subject: [PATCH] stm32: Add MICROPY_BOARD calls in various places in stm32_main. For a board to have full configurability of the soft reset loop. Signed-off-by: Damien George --- ports/stm32/Makefile | 1 + ports/stm32/boardctrl.c | 183 ++++++++++++++++++++++++++++++++++++++++ ports/stm32/boardctrl.h | 80 ++++++++++++++++++ ports/stm32/main.c | 164 ++++++++--------------------------- 4 files changed, 298 insertions(+), 130 deletions(-) create mode 100644 ports/stm32/boardctrl.c create mode 100644 ports/stm32/boardctrl.h diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 61da9cc98..fa9998a8e 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -267,6 +267,7 @@ DRIVERS_SRC_C = $(addprefix drivers/,\ ) SRC_C += \ + boardctrl.c \ main.c \ stm32_it.c \ usbd_conf.c \ diff --git a/ports/stm32/boardctrl.c b/ports/stm32/boardctrl.c new file mode 100644 index 000000000..188068d70 --- /dev/null +++ b/ports/stm32/boardctrl.c @@ -0,0 +1,183 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/mphal.h" +#include "lib/utils/pyexec.h" +#include "boardctrl.h" +#include "led.h" +#include "usrsw.h" + +STATIC void flash_error(int n) { + for (int i = 0; i < n; i++) { + led_state(PYB_LED_RED, 1); + led_state(PYB_LED_GREEN, 0); + mp_hal_delay_ms(250); + led_state(PYB_LED_RED, 0); + led_state(PYB_LED_GREEN, 1); + mp_hal_delay_ms(250); + } + led_state(PYB_LED_GREEN, 0); +} + +#if !MICROPY_HW_USES_BOOTLOADER +STATIC uint update_reset_mode(uint reset_mode) { + #if MICROPY_HW_HAS_SWITCH + if (switch_get()) { + + // The original method used on the pyboard is appropriate if you have 2 + // or more LEDs. + #if defined(MICROPY_HW_LED2) + for (uint i = 0; i < 3000; i++) { + if (!switch_get()) { + break; + } + mp_hal_delay_ms(20); + if (i % 30 == 29) { + if (++reset_mode > 3) { + reset_mode = 1; + } + led_state(2, reset_mode & 1); + led_state(3, reset_mode & 2); + led_state(4, reset_mode & 4); + } + } + // flash the selected reset mode + for (uint i = 0; i < 6; i++) { + led_state(2, 0); + led_state(3, 0); + led_state(4, 0); + mp_hal_delay_ms(50); + led_state(2, reset_mode & 1); + led_state(3, reset_mode & 2); + led_state(4, reset_mode & 4); + mp_hal_delay_ms(50); + } + mp_hal_delay_ms(400); + + #elif defined(MICROPY_HW_LED1) + + // For boards with only a single LED, we'll flash that LED the + // appropriate number of times, with a pause between each one + for (uint i = 0; i < 10; i++) { + led_state(1, 0); + for (uint j = 0; j < reset_mode; j++) { + if (!switch_get()) { + break; + } + led_state(1, 1); + mp_hal_delay_ms(100); + led_state(1, 0); + mp_hal_delay_ms(200); + } + mp_hal_delay_ms(400); + if (!switch_get()) { + break; + } + if (++reset_mode > 3) { + reset_mode = 1; + } + } + // Flash the selected reset mode + for (uint i = 0; i < 2; i++) { + for (uint j = 0; j < reset_mode; j++) { + led_state(1, 1); + mp_hal_delay_ms(100); + led_state(1, 0); + mp_hal_delay_ms(200); + } + mp_hal_delay_ms(400); + } + #else + #error Need a reset mode update method + #endif + } + #endif + return reset_mode; +} +#endif + +void boardctrl_before_soft_reset_loop(boardctrl_state_t *state) { + #if !MICROPY_HW_USES_BOOTLOADER + // Update the reset_mode via the default + // method which uses the board switch/button and LEDs. + state->reset_mode = update_reset_mode(1); + #endif +} + +void boardctrl_top_soft_reset_loop(boardctrl_state_t *state) { + // Turn on a single LED to indicate start up. + #if defined(MICROPY_HW_LED2) + led_state(1, 0); + led_state(2, 1); + #else + led_state(1, 1); + led_state(2, 0); + #endif + led_state(3, 0); + led_state(4, 0); +} + +void boardctrl_before_boot_py(boardctrl_state_t *state) { + state->run_boot_py = state->reset_mode == 1 || state->reset_mode == 3; +} + +void boardctrl_after_boot_py(boardctrl_state_t *state) { + if (state->run_boot_py && !state->last_ret) { + flash_error(4); + } + + // Turn boot-up LEDs off + + #if !defined(MICROPY_HW_LED2) + // If there is only one LED on the board then it's used to signal boot-up + // and so we turn it off here. Otherwise LED(1) is used to indicate dirty + // flash cache and so we shouldn't change its state. + led_state(1, 0); + #endif + led_state(2, 0); + led_state(3, 0); + led_state(4, 0); +} + +void boardctrl_before_main_py(boardctrl_state_t *state) { + state->run_main_py = (state->reset_mode == 1 || state->reset_mode == 3) + && pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL; +} + +void boardctrl_after_main_py(boardctrl_state_t *state) { + if (state->run_main_py && !state->last_ret) { + flash_error(3); + } +} + +void boardctrl_start_soft_reset(boardctrl_state_t *state) { + state->log_soft_reset = true; +} + +void boardctrl_end_soft_reset(boardctrl_state_t *state) { + // Set reset_mode to normal boot. + state->reset_mode = 1; +} diff --git a/ports/stm32/boardctrl.h b/ports/stm32/boardctrl.h new file mode 100644 index 000000000..05bade305 --- /dev/null +++ b/ports/stm32/boardctrl.h @@ -0,0 +1,80 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2020 Damien P. George + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +#ifndef MICROPY_INCLUDED_STM32_BOARDCTRL_H +#define MICROPY_INCLUDED_STM32_BOARDCTRL_H + +#include "py/mpconfig.h" + +#ifndef MICROPY_BOARD_BEFORE_SOFT_RESET_LOOP +#define MICROPY_BOARD_BEFORE_SOFT_RESET_LOOP boardctrl_before_soft_reset_loop +#endif + +#ifndef MICROPY_BOARD_TOP_SOFT_RESET_LOOP +#define MICROPY_BOARD_TOP_SOFT_RESET_LOOP boardctrl_top_soft_reset_loop +#endif + +#ifndef MICROPY_BOARD_BEFORE_BOOT_PY +#define MICROPY_BOARD_BEFORE_BOOT_PY boardctrl_before_boot_py +#endif + +#ifndef MICROPY_BOARD_AFTER_BOOT_PY +#define MICROPY_BOARD_AFTER_BOOT_PY boardctrl_after_boot_py +#endif + +#ifndef MICROPY_BOARD_BEFORE_MAIN_PY +#define MICROPY_BOARD_BEFORE_MAIN_PY boardctrl_before_main_py +#endif + +#ifndef MICROPY_BOARD_AFTER_MAIN_PY +#define MICROPY_BOARD_AFTER_MAIN_PY boardctrl_after_main_py +#endif + +#ifndef MICROPY_BOARD_START_SOFT_RESET +#define MICROPY_BOARD_START_SOFT_RESET boardctrl_start_soft_reset +#endif + +#ifndef MICROPY_BOARD_END_SOFT_RESET +#define MICROPY_BOARD_END_SOFT_RESET boardctrl_end_soft_reset +#endif + +typedef struct _boardctrl_state_t { + uint8_t reset_mode; + bool run_boot_py; + bool run_main_py; + bool log_soft_reset; + int last_ret; +} boardctrl_state_t; + +void boardctrl_before_soft_reset_loop(boardctrl_state_t *state); +void boardctrl_top_soft_reset_loop(boardctrl_state_t *state); +void boardctrl_before_boot_py(boardctrl_state_t *state); +void boardctrl_after_boot_py(boardctrl_state_t *state); +void boardctrl_before_main_py(boardctrl_state_t *state); +void boardctrl_after_main_py(boardctrl_state_t *state); +void boardctrl_start_soft_reset(boardctrl_state_t *state); +void boardctrl_end_soft_reset(boardctrl_state_t *state); + +#endif // MICROPY_INCLUDED_STM32_BOARDCTRL_H diff --git a/ports/stm32/main.c b/ports/stm32/main.c index 7f8ee84fd..c749cac97 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -3,7 +3,7 @@ * * The MIT License (MIT) * - * Copyright (c) 2013-2018 Damien P. George + * Copyright (c) 2013-2020 Damien P. George * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -53,6 +53,7 @@ #include "extmod/modbluetooth.h" #endif +#include "boardctrl.h" #include "mpu.h" #include "rfcore.h" #include "systick.h" @@ -95,18 +96,6 @@ STATIC pyb_uart_obj_t pyb_uart_repl_obj; STATIC uint8_t pyb_uart_repl_rxbuf[MICROPY_HW_UART_REPL_RXBUF]; #endif -void flash_error(int n) { - for (int i = 0; i < n; i++) { - led_state(PYB_LED_RED, 1); - led_state(PYB_LED_GREEN, 0); - mp_hal_delay_ms(250); - led_state(PYB_LED_RED, 0); - led_state(PYB_LED_GREEN, 1); - mp_hal_delay_ms(250); - } - led_state(PYB_LED_GREEN, 0); -} - void NORETURN __fatal_error(const char *msg) { for (volatile uint delay = 0; delay < 10000000; delay++) { } @@ -310,83 +299,6 @@ STATIC bool init_sdcard_fs(void) { } #endif -#if !MICROPY_HW_USES_BOOTLOADER -STATIC uint update_reset_mode(uint reset_mode) { - #if MICROPY_HW_HAS_SWITCH - if (switch_get()) { - - // The original method used on the pyboard is appropriate if you have 2 - // or more LEDs. - #if defined(MICROPY_HW_LED2) - for (uint i = 0; i < 3000; i++) { - if (!switch_get()) { - break; - } - mp_hal_delay_ms(20); - if (i % 30 == 29) { - if (++reset_mode > 3) { - reset_mode = 1; - } - led_state(2, reset_mode & 1); - led_state(3, reset_mode & 2); - led_state(4, reset_mode & 4); - } - } - // flash the selected reset mode - for (uint i = 0; i < 6; i++) { - led_state(2, 0); - led_state(3, 0); - led_state(4, 0); - mp_hal_delay_ms(50); - led_state(2, reset_mode & 1); - led_state(3, reset_mode & 2); - led_state(4, reset_mode & 4); - mp_hal_delay_ms(50); - } - mp_hal_delay_ms(400); - - #elif defined(MICROPY_HW_LED1) - - // For boards with only a single LED, we'll flash that LED the - // appropriate number of times, with a pause between each one - for (uint i = 0; i < 10; i++) { - led_state(1, 0); - for (uint j = 0; j < reset_mode; j++) { - if (!switch_get()) { - break; - } - led_state(1, 1); - mp_hal_delay_ms(100); - led_state(1, 0); - mp_hal_delay_ms(200); - } - mp_hal_delay_ms(400); - if (!switch_get()) { - break; - } - if (++reset_mode > 3) { - reset_mode = 1; - } - } - // Flash the selected reset mode - for (uint i = 0; i < 2; i++) { - for (uint j = 0; j < reset_mode; j++) { - led_state(1, 1); - mp_hal_delay_ms(100); - led_state(1, 0); - mp_hal_delay_ms(200); - } - mp_hal_delay_ms(400); - } - #else - #error Need a reset mode update method - #endif - } - #endif - return reset_mode; -} -#endif - void stm32_main(uint32_t reset_mode) { #if !defined(STM32F0) && defined(MICROPY_HW_VTOR) // Change IRQ vector table if configured differently @@ -552,22 +464,17 @@ void stm32_main(uint32_t reset_mode) { MP_STATE_PORT(pyb_uart_obj_all)[MICROPY_HW_UART_REPL - 1] = &pyb_uart_repl_obj; #endif - #if !MICROPY_HW_USES_BOOTLOADER - // check if user switch held to select the reset mode - reset_mode = update_reset_mode(1); - #endif + boardctrl_state_t state; + state.reset_mode = reset_mode; + state.run_boot_py = false; + state.run_main_py = false; + state.last_ret = 0; + + MICROPY_BOARD_BEFORE_SOFT_RESET_LOOP(&state); soft_reset: - #if defined(MICROPY_HW_LED2) - led_state(1, 0); - led_state(2, 1); - #else - led_state(1, 1); - led_state(2, 0); - #endif - led_state(3, 0); - led_state(4, 0); + MICROPY_BOARD_TOP_SOFT_RESET_LOOP(&state); // Python threading init #if MICROPY_PY_THREAD @@ -656,29 +563,19 @@ soft_reset: // reset config variables; they should be set by boot.py MP_STATE_PORT(pyb_config_main) = MP_OBJ_NULL; + MICROPY_BOARD_BEFORE_BOOT_PY(&state); + // run boot.py, if it exists // TODO perhaps have pyb.reboot([bootpy]) function to soft-reboot and execute custom boot.py - if (reset_mode == 1 || reset_mode == 3) { + if (state.run_boot_py) { const char *boot_py = "boot.py"; - int ret = pyexec_file_if_exists(boot_py); - if (ret & PYEXEC_FORCED_EXIT) { + state.last_ret = pyexec_file_if_exists(boot_py); + if (state.last_ret & PYEXEC_FORCED_EXIT) { goto soft_reset_exit; } - if (!ret) { - flash_error(4); - } } - // turn boot-up LEDs off - #if !defined(MICROPY_HW_LED2) - // If there is only one LED on the board then it's used to signal boot-up - // and so we turn it off here. Otherwise LED(1) is used to indicate dirty - // flash cache and so we shouldn't change its state. - led_state(1, 0); - #endif - led_state(2, 0); - led_state(3, 0); - led_state(4, 0); + MICROPY_BOARD_AFTER_BOOT_PY(&state); // Now we initialise sub-systems that need configuration from boot.py, // or whose initialisation can be safely deferred until after running @@ -713,23 +610,24 @@ soft_reset: // At this point everything is fully configured and initialised. + MICROPY_BOARD_BEFORE_MAIN_PY(&state); + // Run the main script from the current directory. - if ((reset_mode == 1 || reset_mode == 3) && pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL) { + if (state.run_main_py) { const char *main_py; if (MP_STATE_PORT(pyb_config_main) == MP_OBJ_NULL) { main_py = "main.py"; } else { main_py = mp_obj_str_get_str(MP_STATE_PORT(pyb_config_main)); } - int ret = pyexec_file_if_exists(main_py); - if (ret & PYEXEC_FORCED_EXIT) { + state.last_ret = pyexec_file_if_exists(main_py); + if (state.last_ret & PYEXEC_FORCED_EXIT) { goto soft_reset_exit; } - if (!ret) { - flash_error(3); - } } + MICROPY_BOARD_AFTER_MAIN_PY(&state); + #if MICROPY_ENABLE_COMPILER // Main script is finished, so now go into REPL mode. // The REPL mode can change, or it can request a soft reset. @@ -750,12 +648,19 @@ soft_reset_exit: // soft reset + MICROPY_BOARD_START_SOFT_RESET(&state); + #if MICROPY_HW_ENABLE_STORAGE - printf("MPY: sync filesystems\n"); + if (state.log_soft_reset) { + mp_printf(&mp_plat_print, "MPY: sync filesystems\n"); + } storage_flush(); #endif - printf("MPY: soft reboot\n"); + if (state.log_soft_reset) { + mp_printf(&mp_plat_print, "MPY: soft reboot\n"); + } + #if MICROPY_PY_BLUETOOTH mp_bluetooth_deinit(); #endif @@ -774,10 +679,9 @@ soft_reset_exit: pyb_thread_deinit(); #endif - gc_sweep_all(); + MICROPY_BOARD_END_SOFT_RESET(&state); - // Set reset_mode to normal boot. - reset_mode = 1; + gc_sweep_all(); goto soft_reset; }