Skip to content

Commit 3814e97

Browse files
authored
Merge pull request #802 from dhalbert/2.x-filesystem-work
2.x: handle bad power on reset better
2 parents ffc66e9 + 74a6edf commit 3814e97

File tree

7 files changed

+147
-27
lines changed

7 files changed

+147
-27
lines changed

atmel-samd/asf/sam0/drivers/tc/tc.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
#define TC_H_INCLUDED
4949

5050
/**
51-
* \defgroup asfdoc_sam0_tc_group SAM Timer/Counter (TC) Driver
51+
* \defgroup asfdoc_sam0_tc_group SAM Timer/Counter (TC) Driver
5252
*
5353
* This driver for Atmel® | SMART ARM®-based microcontrollers provides an interface for the configuration
5454
* and management of the timer modules within the device, for waveform
@@ -1146,12 +1146,14 @@ static inline void tc_enable_events(
11461146
/* Sanity check arguments */
11471147
Assert(module_inst);
11481148
Assert(module_inst->hw);
1149-
Assert(events);
1149+
Assert(events);
11501150

11511151
Tc *const tc_module = module_inst->hw;
11521152

11531153
uint32_t event_mask = 0;
11541154

1155+
#pragma GCC diagnostic push
1156+
#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
11551157
if (events->invert_event_input == true) {
11561158
event_mask |= TC_EVCTRL_TCINV;
11571159
}
@@ -1171,6 +1173,7 @@ static inline void tc_enable_events(
11711173
}
11721174

11731175
tc_module->COUNT8.EVCTRL.reg |= event_mask | events->event_action;
1176+
#pragma GCC diagnostic pop
11741177
}
11751178

11761179
/**

atmel-samd/common-hal/storage/__init__.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@
2727
#include <string.h>
2828

2929
#include "flash_api.h"
30+
#include "main.h"
3031
#include "py/mperrno.h"
3132
#include "py/runtime.h"
33+
#include "shared-bindings/microcontroller/__init__.h"
3234
#include "shared-bindings/storage/__init__.h"
3335

3436
extern volatile bool mp_msc_enabled;
@@ -44,3 +46,9 @@ void common_hal_storage_remount(const char* mount_path, bool readonly) {
4446

4547
flash_set_usb_writeable(readonly);
4648
}
49+
50+
void common_hal_storage_erase_filesystem(void) {
51+
init_flash_fs(false, true); // Force a re-format.
52+
common_hal_mcu_reset();
53+
// We won't actually get here, since we're resetting.
54+
}

atmel-samd/main.c

Lines changed: 79 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666

6767
#include "autoreload.h"
6868
#include "flash_api.h"
69+
#include "main.h"
6970
#include "mpconfigboard.h"
7071
#include "rgb_led_status.h"
7172
#include "shared_dma.h"
@@ -110,7 +111,7 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) {
110111

111112
// we don't make this function static because it needs a lot of stack and we
112113
// want it to be executed without using stack within main() function
113-
void init_flash_fs(bool create_allowed) {
114+
void init_flash_fs(bool create_allowed, bool force_create) {
114115
// init the vfs object
115116
fs_user_mount_t *vfs_fat = &fs_user_mount_flash;
116117
vfs_fat->flags = 0;
@@ -119,8 +120,8 @@ void init_flash_fs(bool create_allowed) {
119120
// try to mount the flash
120121
FRESULT res = f_mount(&vfs_fat->fatfs);
121122

122-
if (res == FR_NO_FILESYSTEM && create_allowed) {
123-
// no filesystem so create a fresh one
123+
if ((res == FR_NO_FILESYSTEM && create_allowed) || force_create) {
124+
// No filesystem so create a fresh one, or reformat has been requested.
124125

125126
// Wait two seconds before creating. Jittery power might
126127
// fail before we're ready. This can happen if someone
@@ -129,10 +130,10 @@ void init_flash_fs(bool create_allowed) {
129130

130131
// Then try one more time to mount the flash in case it was late coming up.
131132
res = f_mount(&vfs_fat->fatfs);
132-
if (res == FR_NO_FILESYSTEM) {
133+
if (res == FR_NO_FILESYSTEM || force_create) {
133134
uint8_t working_buf[_MAX_SS];
134135
res = f_mkfs(&vfs_fat->fatfs, FM_FAT, 0, working_buf, sizeof(working_buf));
135-
// Flush the new file system to make sure its repaired immediately.
136+
// Flush the new file system to make sure it's repaired immediately.
136137
flash_flush();
137138
if (res != FR_OK) {
138139
return;
@@ -528,6 +529,14 @@ void load_serial_number(void) {
528529
extern uint32_t _ezero;
529530

530531
safe_mode_t samd21_init(void) {
532+
533+
// Set brownout detection to ~2.7V. Default from factory is 1.7V,
534+
// which is too low for proper operation of external SPI flash chips (they are 2.7-3.6V).
535+
// Disable while changing level.
536+
SYSCTRL->BOD33.bit.ENABLE = 0;
537+
SYSCTRL->BOD33.bit.LEVEL = 39; // 2.77V with hysteresis off. Table 37.20 in datasheet.
538+
SYSCTRL->BOD33.bit.ENABLE = 1;
539+
531540
#ifdef ENABLE_MICRO_TRACE_BUFFER
532541
REG_MTB_POSITION = ((uint32_t) (mtb - REG_MTB_BASE)) & 0xFFFFFFF8;
533542
REG_MTB_FLOW = (((uint32_t) mtb - REG_MTB_BASE) + TRACE_BUFFER_SIZE_BYTES) & 0xFFFFFFF8;
@@ -683,9 +692,9 @@ int main(void) {
683692
// Create a new filesystem only if we're not in a safe mode.
684693
// A power brownout here could make it appear as if there's
685694
// no SPI flash filesystem, and we might erase the existing one.
686-
init_flash_fs(safe_mode == NO_SAFE_MODE);
695+
init_flash_fs(safe_mode == NO_SAFE_MODE, false);
687696

688-
// Reset everything and prep MicroPython to run boot.py.
697+
// Reset everything and prep to run boot.py.
689698
reset_samd21();
690699
reset_board();
691700
reset_mp();
@@ -694,38 +703,83 @@ int main(void) {
694703
autoreload_enable();
695704

696705
// By default our internal flash is readonly to local python code and
697-
// writeable over USB. Set it here so that boot.py can change it.
706+
// writable over USB.
698707
flash_set_usb_writeable(true);
699708

700709
// If not in safe mode, run boot before initing USB and capture output in a
701710
// file.
702711
if (safe_mode == NO_SAFE_MODE && MP_STATE_VM(vfs_mount_table) != NULL) {
703712
new_status_color(BOOT_RUNNING);
713+
714+
// Run the first of these files found at boot time (if any).
715+
static char const* BOOT_PY_FILENAMES[] = {
716+
"settings.txt",
717+
"settings.py",
718+
"boot.py",
719+
"boot.txt",
720+
};
721+
722+
// Check for existance of any of the BOOT_PY_FILENAMES.
723+
char const* boot_py_to_use = NULL;
724+
for (size_t i = 0; i < MP_ARRAY_SIZE(BOOT_PY_FILENAMES); i++) {
725+
if (mp_import_stat(BOOT_PY_FILENAMES[i]) == MP_IMPORT_STAT_FILE) {
726+
boot_py_to_use = BOOT_PY_FILENAMES[i];
727+
break;
728+
}
729+
}
730+
704731
#ifdef CIRCUITPY_BOOT_OUTPUT_FILE
705-
// Since USB isn't up yet we can cheat and let ourselves write the boot
706-
// output file.
707-
flash_set_usb_writeable(false);
708732
FIL file_pointer;
709733
boot_output_file = &file_pointer;
710-
f_open(&((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs,
711-
boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS);
712-
flash_set_usb_writeable(true);
713-
// Write version info to boot_out.txt.
714-
mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO);
715-
mp_hal_stdout_tx_str("\r\n");
734+
735+
// Get the base filesystem.
736+
FATFS *fs = &((fs_user_mount_t *) MP_STATE_VM(vfs_mount_table)->obj)->fatfs;
737+
738+
char file_contents[512];
739+
UINT chars_read = 0;
740+
bool skip_boot_output = false;
741+
742+
// If there's no boot.py file that might write some changing output,
743+
// read the existing copy of CIRCUITPY_BOOT_OUTPUT_FILE and see if its contents
744+
// match the version info we would print anyway. If so, skip writing CIRCUITPY_BOOT_OUTPUT_FILE.
745+
// This saves wear and tear on the flash and also prevents filesystem damage if power is lost
746+
// during the write, which may happen due to bobbling the power connector or weak power.
747+
748+
if (!boot_py_to_use && f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_READ) == FR_OK) {
749+
f_read(boot_output_file, file_contents, 512, &chars_read);
750+
f_close(boot_output_file);
751+
skip_boot_output =
752+
// + 2 accounts for \r\n.
753+
chars_read == strlen(MICROPY_FULL_VERSION_INFO) + 2 &&
754+
strncmp(file_contents, MICROPY_FULL_VERSION_INFO, strlen(MICROPY_FULL_VERSION_INFO)) == 0;
755+
}
756+
757+
if (!skip_boot_output) {
758+
// Wait 1.5 seconds before opening CIRCUITPY_BOOT_OUTPUT_FILE for write,
759+
// in case power is momentary or will fail shortly due to, say a low, battery.
760+
mp_hal_delay_ms(1500);
761+
762+
// USB isn't up, so we can write the file.
763+
flash_set_usb_writeable(false);
764+
f_open(fs, boot_output_file, CIRCUITPY_BOOT_OUTPUT_FILE, FA_WRITE | FA_CREATE_ALWAYS);
765+
// Write version info to boot_out.txt.
766+
mp_hal_stdout_tx_str(MICROPY_FULL_VERSION_INFO);
767+
mp_hal_stdout_tx_str("\r\n");
768+
}
716769
#endif
717770

718771
// TODO(tannewt): Re-add support for flashing boot error output.
719-
bool found_boot = maybe_run("settings.txt", NULL) ||
720-
maybe_run("settings.py", NULL) ||
721-
maybe_run("boot.py", NULL) ||
722-
maybe_run("boot.txt", NULL);
723-
(void) found_boot;
772+
if (boot_py_to_use) {
773+
maybe_run(boot_py_to_use, NULL);
774+
}
724775

725776
#ifdef CIRCUITPY_BOOT_OUTPUT_FILE
726-
f_close(boot_output_file);
727-
flash_flush();
728-
boot_output_file = NULL;
777+
if (!skip_boot_output) {
778+
f_close(boot_output_file);
779+
flash_flush();
780+
boot_output_file = NULL;
781+
}
782+
flash_set_usb_writeable(true);
729783
#endif
730784

731785
// Reset to remove any state that boot.py setup. It should only be used to

atmel-samd/main.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2018 Dan Halbert for Adafruit Industries
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
// Functions defined in main.c that are useful elsewhere.
28+
// Eventually they should be factored out.
29+
30+
void init_flash_fs(bool create_allowed, bool force_create);

esp8266/common-hal/storage/__init__.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,7 @@
3232
void common_hal_storage_remount(const char* mount_path, bool readonly) {
3333
mp_raise_NotImplementedError("");
3434
}
35+
36+
void common_hal_storage_erase_filesystem() {
37+
mp_raise_NotImplementedError("Use esptool to erase flash and re-upload Python instead");
38+
}

shared-bindings/storage/__init__.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,32 @@ mp_obj_t storage_remount(mp_obj_t mount_path, mp_obj_t readonly) {
115115
}
116116
MP_DEFINE_CONST_FUN_OBJ_2(storage_remount_obj, storage_remount);
117117

118+
//| .. function:: erase_filesystem()
119+
//|
120+
//| Erase and re-create the ``CIRCUITPY`` filesystem. Then call
121+
//| `microcontroller.reset()` to restart CircuitPython and have the
122+
//| host computer remount CIRCUITPY.
123+
//|
124+
//| This function can be called from the REPL when ``CIRCUITPY``
125+
//| has become corrupted.
126+
//|
127+
//| .. warning:: All the data on ``CIRCUITPY`` will be lost, and
128+
//| CircuitPython will restart.
129+
130+
mp_obj_t storage_erase_filesystem(void) {
131+
common_hal_storage_erase_filesystem();
132+
return mp_const_none;
133+
}
134+
MP_DEFINE_CONST_FUN_OBJ_0(storage_erase_filesystem_obj, storage_erase_filesystem);
135+
136+
118137
STATIC const mp_rom_map_elem_t storage_module_globals_table[] = {
119138
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_storage) },
120139

121140
{ MP_ROM_QSTR(MP_QSTR_mount), MP_ROM_PTR(&storage_mount_obj) },
122141
{ MP_ROM_QSTR(MP_QSTR_umount), MP_ROM_PTR(&storage_umount_obj) },
123142
{ MP_ROM_QSTR(MP_QSTR_remount), MP_ROM_PTR(&storage_remount_obj) },
143+
{ MP_ROM_QSTR(MP_QSTR_erase_filesystem), MP_ROM_PTR(&storage_erase_filesystem_obj) },
124144

125145
//| .. class:: VfsFat(block_device)
126146
//|

shared-bindings/storage/__init__.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,6 @@ void common_hal_storage_mount(mp_obj_t vfs_obj, const char* path, bool readonly)
3434
void common_hal_storage_umount_path(const char* path);
3535
void common_hal_storage_umount_object(mp_obj_t vfs_obj);
3636
void common_hal_storage_remount(const char* path, bool readonly);
37+
void common_hal_storage_erase_filesystem(void);
3738

3839
#endif // MICROPY_INCLUDED_SHARED_BINDINGS_STORAGE___INIT___H

0 commit comments

Comments
 (0)