handle bad power on reset better

This commit is contained in:
Dan Halbert 2018-05-03 15:53:14 -04:00
parent 4dd9e0da2c
commit 74a6edff4d
7 changed files with 147 additions and 27 deletions

View File

@ -48,7 +48,7 @@
#define TC_H_INCLUDED #define TC_H_INCLUDED
/** /**
* \defgroup asfdoc_sam0_tc_group SAM Timer/Counter (TC) Driver * \defgroup asfdoc_sam0_tc_group SAM Timer/Counter (TC) Driver
* *
* This driver for Atmel® | SMART ARM®-based microcontrollers provides an interface for the configuration * This driver for Atmel® | SMART ARM®-based microcontrollers provides an interface for the configuration
* and management of the timer modules within the device, for waveform * and management of the timer modules within the device, for waveform
@ -1146,12 +1146,14 @@ static inline void tc_enable_events(
/* Sanity check arguments */ /* Sanity check arguments */
Assert(module_inst); Assert(module_inst);
Assert(module_inst->hw); Assert(module_inst->hw);
Assert(events); Assert(events);
Tc *const tc_module = module_inst->hw; Tc *const tc_module = module_inst->hw;
uint32_t event_mask = 0; uint32_t event_mask = 0;
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
if (events->invert_event_input == true) { if (events->invert_event_input == true) {
event_mask |= TC_EVCTRL_TCINV; event_mask |= TC_EVCTRL_TCINV;
} }
@ -1171,6 +1173,7 @@ static inline void tc_enable_events(
} }
tc_module->COUNT8.EVCTRL.reg |= event_mask | events->event_action; tc_module->COUNT8.EVCTRL.reg |= event_mask | events->event_action;
#pragma GCC diagnostic pop
} }
/** /**

View File

@ -27,8 +27,10 @@
#include <string.h> #include <string.h>
#include "flash_api.h" #include "flash_api.h"
#include "main.h"
#include "py/mperrno.h" #include "py/mperrno.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/storage/__init__.h" #include "shared-bindings/storage/__init__.h"
extern volatile bool mp_msc_enabled; extern volatile bool mp_msc_enabled;
@ -44,3 +46,9 @@ void common_hal_storage_remount(const char* mount_path, bool readonly) {
flash_set_usb_writeable(readonly); flash_set_usb_writeable(readonly);
} }
void common_hal_storage_erase_filesystem(void) {
init_flash_fs(false, true); // Force a re-format.
common_hal_mcu_reset();
// We won't actually get here, since we're resetting.
}

View File

@ -66,6 +66,7 @@
#include "autoreload.h" #include "autoreload.h"
#include "flash_api.h" #include "flash_api.h"
#include "main.h"
#include "mpconfigboard.h" #include "mpconfigboard.h"
#include "rgb_led_status.h" #include "rgb_led_status.h"
#include "shared_dma.h" #include "shared_dma.h"
@ -110,7 +111,7 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) {
// we don't make this function static because it needs a lot of stack and we // we don't make this function static because it needs a lot of stack and we
// want it to be executed without using stack within main() function // want it to be executed without using stack within main() function
void init_flash_fs(bool create_allowed) { void init_flash_fs(bool create_allowed, bool force_create) {
// init the vfs object // init the vfs object
fs_user_mount_t *vfs_fat = &fs_user_mount_flash; fs_user_mount_t *vfs_fat = &fs_user_mount_flash;
vfs_fat->flags = 0; vfs_fat->flags = 0;
@ -119,8 +120,8 @@ void init_flash_fs(bool create_allowed) {
// try to mount the flash // try to mount the flash
FRESULT res = f_mount(&vfs_fat->fatfs); FRESULT res = f_mount(&vfs_fat->fatfs);
if (res == FR_NO_FILESYSTEM && create_allowed) { if ((res == FR_NO_FILESYSTEM && create_allowed) || force_create) {
// no filesystem so create a fresh one // No filesystem so create a fresh one, or reformat has been requested.
// Wait two seconds before creating. Jittery power might // Wait two seconds before creating. Jittery power might
// fail before we're ready. This can happen if someone // fail before we're ready. This can happen if someone
@ -129,10 +130,10 @@ void init_flash_fs(bool create_allowed) {
// Then try one more time to mount the flash in case it was late coming up. // Then try one more time to mount the flash in case it was late coming up.
res = f_mount(&vfs_fat->fatfs); res = f_mount(&vfs_fat->fatfs);
if (res == FR_NO_FILESYSTEM) { if (res == FR_NO_FILESYSTEM || force_create) {
uint8_t working_buf[_MAX_SS]; uint8_t working_buf[_MAX_SS];
res = f_mkfs(&vfs_fat->fatfs, FM_FAT, 0, working_buf, sizeof(working_buf)); res = f_mkfs(&vfs_fat->fatfs, FM_FAT, 0, working_buf, sizeof(working_buf));
// Flush the new file system to make sure its repaired immediately. // Flush the new file system to make sure it's repaired immediately.
flash_flush(); flash_flush();
if (res != FR_OK) { if (res != FR_OK) {
return; return;
@ -528,6 +529,14 @@ void load_serial_number(void) {
extern uint32_t _ezero; extern uint32_t _ezero;
safe_mode_t samd21_init(void) { safe_mode_t samd21_init(void) {
// Set brownout detection to ~2.7V. Default from factory is 1.7V,
// which is too low for proper operation of external SPI flash chips (they are 2.7-3.6V).
// Disable while changing level.
SYSCTRL->BOD33.bit.ENABLE = 0;
SYSCTRL->BOD33.bit.LEVEL = 39; // 2.77V with hysteresis off. Table 37.20 in datasheet.
SYSCTRL->BOD33.bit.ENABLE = 1;
#ifdef ENABLE_MICRO_TRACE_BUFFER #ifdef ENABLE_MICRO_TRACE_BUFFER
REG_MTB_POSITION = ((uint32_t) (mtb - REG_MTB_BASE)) & 0xFFFFFFF8; REG_MTB_POSITION = ((uint32_t) (mtb - REG_MTB_BASE)) & 0xFFFFFFF8;
REG_MTB_FLOW = (((uint32_t) mtb - REG_MTB_BASE) + TRACE_BUFFER_SIZE_BYTES) & 0xFFFFFFF8; REG_MTB_FLOW = (((uint32_t) mtb - REG_MTB_BASE) + TRACE_BUFFER_SIZE_BYTES) & 0xFFFFFFF8;
@ -683,9 +692,9 @@ int main(void) {
// Create a new filesystem only if we're not in a safe mode. // Create a new filesystem only if we're not in a safe mode.
// A power brownout here could make it appear as if there's // A power brownout here could make it appear as if there's
// no SPI flash filesystem, and we might erase the existing one. // no SPI flash filesystem, and we might erase the existing one.
init_flash_fs(safe_mode == NO_SAFE_MODE); init_flash_fs(safe_mode == NO_SAFE_MODE, false);
// Reset everything and prep MicroPython to run boot.py. // Reset everything and prep to run boot.py.
reset_samd21(); reset_samd21();
reset_board(); reset_board();
reset_mp(); reset_mp();
@ -694,38 +703,83 @@ int main(void) {
autoreload_enable(); autoreload_enable();
// By default our internal flash is readonly to local python code and // By default our internal flash is readonly to local python code and
// writeable over USB. Set it here so that boot.py can change it. // writable over USB.
flash_set_usb_writeable(true); flash_set_usb_writeable(true);
// If not in safe mode, run boot before initing USB and capture output in a // If not in safe mode, run boot before initing USB and capture output in a
// file. // file.
if (safe_mode == NO_SAFE_MODE && MP_STATE_VM(vfs_mount_table) != NULL) { if (safe_mode == NO_SAFE_MODE && MP_STATE_VM(vfs_mount_table) != NULL) {
new_status_color(BOOT_RUNNING); new_status_color(BOOT_RUNNING);
// Run the first of these files found at boot time (if any).
static char const* BOOT_PY_FILENAMES[] = {
"settings.txt",
"settings.py",
"boot.py",
"boot.txt",
};
// Check for existance of any of the BOOT_PY_FILENAMES.
char const* boot_py_to_use = NULL;
for (size_t i = 0; i < MP_ARRAY_SIZE(BOOT_PY_FILENAMES); i++) {
if (mp_import_stat(BOOT_PY_FILENAMES[i]) == MP_IMPORT_STAT_FILE) {
boot_py_to_use = BOOT_PY_FILENAMES[i];
break;
}
}
#ifdef CIRCUITPY_BOOT_OUTPUT_FILE #ifdef CIRCUITPY_BOOT_OUTPUT_FILE
// Since USB isn't up yet we can cheat and let ourselves write the boot
// output file.
flash_set_usb_writeable(false);
FIL file_pointer; FIL file_pointer;
boot_output_file = &file_pointer; boot_output_file = &file_pointer;
f_open(&((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs,
boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS); // Get the base filesystem.
flash_set_usb_writeable(true); FATFS *fs = &((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
// Write version info to boot_out.txt.
mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO); char file_contents[512];
mp_hal_stdout_tx_str("\r\n"); UINT chars_read = 0;
bool skip_boot_output = false;
// If there's no boot.py file that might write some changing output,
// read the existing copy of CIRCUITPY_BOOT_OUTPUT_FILE and see if its contents
// match the version info we would print anyway. If so, skip writing CIRCUITPY_BOOT_OUTPUT_FILE.
// This saves wear and tear on the flash and also prevents filesystem damage if power is lost
// during the write, which may happen due to bobbling the power connector or weak power.
if (!boot_py_to_use && f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) {
f_read(boot_output_file, file_contents, 512, &chars_read);
f_close(boot_output_file);
skip_boot_output =
// + 2 accounts for \r\n.
chars_read == strlen(MICROPY_FULL_VERSION_INFO) + 2 &&
strncmp(file_contents, MICROPY_FULL_VERSION_INFO, strlen(MICROPY_FULL_VERSION_INFO)) == 0;
}
if (!skip_boot_output) {
// Wait 1.5 seconds before opening CIRCUITPY_BOOT_OUTPUT_FILE for write,
// in case power is momentary or will fail shortly due to, say a low, battery.
mp_hal_delay_ms(1500);
// USB isn't up, so we can write the file.
flash_set_usb_writeable(false);
f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS);
// Write version info to boot_out.txt.
mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO);
mp_hal_stdout_tx_str("\r\n");
}
#endif #endif
// TODO(tannewt): Re-add support for flashing boot error output. // TODO(tannewt): Re-add support for flashing boot error output.
bool found_boot = maybe_run("settings.txt", NULL) || if (boot_py_to_use) {
maybe_run("settings.py", NULL) || maybe_run(boot_py_to_use, NULL);
maybe_run("boot.py", NULL) || }
maybe_run("boot.txt", NULL);
(void) found_boot;
#ifdef CIRCUITPY_BOOT_OUTPUT_FILE #ifdef CIRCUITPY_BOOT_OUTPUT_FILE
f_close(boot_output_file); if (!skip_boot_output) {
flash_flush(); f_close(boot_output_file);
boot_output_file = NULL; flash_flush();
boot_output_file = NULL;
}
flash_set_usb_writeable(true);
#endif #endif
// Reset to remove any state that boot.py setup. It should only be used to // Reset to remove any state that boot.py setup. It should only be used to

30
atmel-samd/main.h Normal file
View File

@ -0,0 +1,30 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Dan Halbert for Adafruit Industries
*
* 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.
*/
// Functions defined in main.c that are useful elsewhere.
// Eventually they should be factored out.
void init_flash_fs(bool create_allowed, bool force_create);

View File

@ -32,3 +32,7 @@
void common_hal_storage_remount(const char* mount_path, bool readonly) { void common_hal_storage_remount(const char* mount_path, bool readonly) {
mp_raise_NotImplementedError(""); mp_raise_NotImplementedError("");
} }
void common_hal_storage_erase_filesystem() {
mp_raise_NotImplementedError("Use esptool to erase flash and re-upload Python instead");
}

View File

@ -115,12 +115,32 @@ mp_obj_t storage_remount(mp_obj_t mount_path, mp_obj_t readonly) {
} }
MP_DEFINE_CONST_FUN_OBJ_2(storage_remount_obj, storage_remount); MP_DEFINE_CONST_FUN_OBJ_2(storage_remount_obj, storage_remount);
//| .. function:: erase_filesystem()
//|
//| Erase and re-create the ``CIRCUITPY`` filesystem. Then call
//| `microcontroller.reset()` to restart CircuitPython and have the
//| host computer remount CIRCUITPY.
//|
//| This function can be called from the REPL when ``CIRCUITPY``
//| has become corrupted.
//|
//| .. warning:: All the data on ``CIRCUITPY`` will be lost, and
//| CircuitPython will restart.
mp_obj_t storage_erase_filesystem(void) {
common_hal_storage_erase_filesystem();
return mp_const_none;
}
MP_DEFINE_CONST_FUN_OBJ_0(storage_erase_filesystem_obj, storage_erase_filesystem);
STATIC const mp_rom_map_elem_t storage_module_globals_table[] = { STATIC const mp_rom_map_elem_t storage_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_storage) }, { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_storage) },
{ MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&storage_mount_obj) }, { MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&storage_mount_obj) },
{ MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&storage_umount_obj) }, { MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&storage_umount_obj) },
{ MP_ROM_QSTR(MP_QSTR_remount), MP_ROM_PTR(&storage_remount_obj) }, { MP_ROM_QSTR(MP_QSTR_remount), MP_ROM_PTR(&storage_remount_obj) },
{ MP_ROM_QSTR(MP_QSTR_erase_filesystem), MP_ROM_PTR(&storage_erase_filesystem_obj) },
//| .. class:: VfsFat(block_device) //| .. class:: VfsFat(block_device)
//| //|

View File

@ -34,5 +34,6 @@ void common_hal_storage_mount(mp_obj_t vfs_obj, const char* path, bool readonly)
void common_hal_storage_umount_path(const char* path); void common_hal_storage_umount_path(const char* path);
void common_hal_storage_umount_object(mp_obj_t vfs_obj); void common_hal_storage_umount_object(mp_obj_t vfs_obj);
void common_hal_storage_remount(const char* path, bool readonly); void common_hal_storage_remount(const char* path, bool readonly);
void common_hal_storage_erase_filesystem(void);
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_STORAGE___INIT___H #endif // MICROPY_INCLUDED_SHARED_BINDINGS_STORAGE___INIT___H