atmel-samd: Support auto-reset based on USB write activity.

It will soft-reboot micropython after a burst of writes to the
file system. This means that after you save files on your computer
they will be automatically rerun.

This can be disabled in the build by unsetting AUTORESET_TIMER in
mpconfigboard.h.

Using the REPL will also prevent the soft resets until you reset
with CTRL-D manually.
This commit is contained in:
Scott Shawcroft 2016-10-25 14:27:59 -07:00
parent 614c1fdba2
commit d189a3f3cf
14 changed files with 207 additions and 2 deletions

View File

@ -101,6 +101,7 @@ CFLAGS_CORTEX_M0 = \
-DEXTINT_CALLBACK_MODE=true \
-DUDD_ENABLE \
-DUSART_CALLBACK_MODE=true \
-DTC_ASYNC=true \
-DUSB_DEVICE_LPM_SUPPORT
CFLAGS = $(INC) -Wall -Werror -ansi -std=gnu99 -nostdlib $(CFLAGS_CORTEX_M0) $(COPT)
@ -135,6 +136,7 @@ SRC_ASF = $(addprefix asf/sam0/,\
drivers/system/interrupt/system_interrupt.c \
drivers/system/pinmux/pinmux.c \
drivers/system/system.c \
drivers/tc/tc_interrupt.c \
drivers/tc/tc_sam_d_r/tc.c \
drivers/tcc/tcc.c \
drivers/usb/stack_interface/usb_device_udd.c \
@ -144,6 +146,7 @@ SRC_ASF = $(addprefix asf/sam0/,\
SRC_C = \
access_vfs.c \
autoreset.c \
builtin_open.c \
fatfs_port.c \
main.c \

View File

@ -27,11 +27,13 @@
#include <string.h>
#include "access_vfs.h"
#include "autoreset.h"
#include "asf/common/services/usb/class/msc/device/udi_msc.h"
#include "extmod/fsusermount.h"
#include "lib/fatfs/diskio.h"
#include "py/mpconfig.h"
#include "py/mphal.h"
#include "py/mpstate.h"
#include "py/misc.h"
@ -140,7 +142,6 @@ Ctrl_status vfs_usb_read_10(uint32_t addr, volatile uint16_t nb_sector)
return CTRL_GOOD;
}
//! This function transfers the USB MSC data to the memory
//!
//! @param addr Sector address to start write
@ -183,5 +184,6 @@ Ctrl_status vfs_usb_write_10(uint32_t addr, volatile uint16_t nb_sector)
}
}
}
autoreset_start();
return CTRL_GOOD;
}

87
atmel-samd/autoreset.c Normal file
View File

@ -0,0 +1,87 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Scott Shawcroft 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.
*/
#include "autoreset.h"
#include "asf/sam0/drivers/tc/tc_interrupt.h"
#include "lib/mp-readline/readline.h"
#include "py/mphal.h"
struct tc_module autoreset_timer;
static bool autoreset_initialized = false;
static bool autoreset_enabled = false;
extern void inject_character(char);
void mp_keyboard_interrupt(void);
void autoreset_callback(struct tc_module *const module_inst) {
if (autoreset_enabled) {
mp_keyboard_interrupt();
inject_character(CHAR_CTRL_D);
}
tc_stop_counter(&autoreset_timer);
}
void autoreset_init() {
#ifdef AUTORESET_TIMER
struct tc_config config_tc;
tc_get_config_defaults(&config_tc);
config_tc.counter_size = TC_COUNTER_SIZE_16BIT;
config_tc.clock_prescaler = TC_CLOCK_PRESCALER_DIV256;
config_tc.counter_16_bit.compare_capture_channel[0] = 0xFFFF;
tc_init(&autoreset_timer, AUTORESET_TIMER, &config_tc);
tc_enable(&autoreset_timer);
tc_register_callback(&autoreset_timer, autoreset_callback, TC_CALLBACK_CC_CHANNEL0);
tc_enable_callback(&autoreset_timer, TC_CALLBACK_CC_CHANNEL0);
tc_stop_counter(&autoreset_timer);
autoreset_initialized = true;
#endif
}
void autoreset_enable() {
autoreset_enabled = true;
}
void autoreset_disable() {
autoreset_enabled = false;
}
void autoreset_start() {
if (!autoreset_initialized) return;
tc_stop_counter(&autoreset_timer);
tc_start_counter(&autoreset_timer);
}
void autoreset_stop() {
if (!autoreset_initialized) return;
tc_stop_counter(&autoreset_timer);
}

39
atmel-samd/autoreset.h Normal file
View File

@ -0,0 +1,39 @@
/*
* This file is part of the Micro Python project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016 Scott Shawcroft 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.
*/
#ifndef __MICROPY_INCLUDED_ATMEL_SAMD_AUTORESET_H__
#define __MICROPY_INCLUDED_ATMEL_SAMD_AUTORESET_H__
#include "asf/sam0/drivers/tc/tc.h"
void autoreset_callback(struct tc_module *const module_inst);
void autoreset_init();
void autoreset_start();
void autoreset_stop();
void autoreset_enable();
void autoreset_disable();
#endif // __MICROPY_INCLUDED_ATMEL_SAMD_AUTORESET_H__

View File

@ -8,3 +8,5 @@
#define MICROPY_HW_LED_TX PIN_PA27
#define MICROPY_HW_LED_RX PIN_PB03
#define AUTORESET_TIMER TC5

View File

@ -5,3 +5,5 @@
#define MICROPY_HW_BOARD_NAME "Adafruit Feather M0 Adalogger"
#define MICROPY_HW_MCU_NAME "samd21g18"
#define AUTORESET_TIMER TC5

View File

@ -5,3 +5,5 @@
#define MICROPY_HW_BOARD_NAME "Adafruit Feather M0 Basic"
#define MICROPY_HW_MCU_NAME "samd21g18"
#define AUTORESET_TIMER TC5

View File

@ -29,3 +29,5 @@
#define SPI_FLASH_PAD3_PINMUX PINMUX_PB11D_SERCOM4_PAD3 // SCK
#define SPI_FLASH_CS PIN_PA13
#define SPI_FLASH_SERCOM SERCOM4
#define AUTORESET_TIMER TC5

View File

@ -27,6 +27,7 @@
#include <stdint.h>
#include <string.h>
#include "py/mphal.h"
#include "py/obj.h"
#include "py/runtime.h"
#include "lib/fatfs/ff.h"
@ -40,7 +41,7 @@
#define TOTAL_INTERNAL_FLASH_SIZE 0x010000
#define INTERNAL_FLASH_MEM_SEG1_START_ADDR (0x00040000 - TOTAL_INTERNAL_FLASH_SIZE)
#define INTERNAL_FLASH_PART1_START_BLOCK (0x100)
#define INTERNAL_FLASH_PART1_START_BLOCK (0x1)
#define INTERNAL_FLASH_PART1_NUM_BLOCKS (TOTAL_INTERNAL_FLASH_SIZE / INTERNAL_FLASH_BLOCK_SIZE)
static bool internal_flash_is_initialised = false;

View File

@ -22,6 +22,7 @@
#include "asf/sam0/drivers/system/system.h"
#include <board.h>
#include "autoreset.h"
#include "mpconfigboard.h"
#include "modmachine_pin.h"
@ -172,6 +173,9 @@ static char *stack_top;
static char heap[16384];
void reset_mp() {
autoreset_stop();
autoreset_enable();
// Sync the file systems in case any used RAM from the GC to cache. As soon
// as we re-init the GC all bets are off on the cache.
disk_ioctl(0, CTRL_SYNC, NULL);
@ -191,8 +195,18 @@ void reset_mp() {
MP_STATE_PORT(mp_kbd_exception) = mp_obj_new_exception(&mp_type_KeyboardInterrupt);
pin_init0();
}
void start_mp() {
#ifdef AUTORESET_TIMER
mp_hal_stdout_tx_str("\r\n");
mp_hal_stdout_tx_str("Auto-soft reset is on. Simply save files over USB to run them.\r\n");
mp_hal_stdout_tx_str("Type anything into the REPL to disable and manually reset (CTRL-D) to re-enable.\r\n");
#endif
mp_hal_stdout_tx_str("boot.py output:\r\n");
pyexec_file("boot.py");
mp_hal_stdout_tx_str("\r\nmain.py output:\r\n");
pyexec_file("main.py");
}
@ -216,11 +230,18 @@ int main(int argc, char **argv) {
// as current dir.
init_flash_fs();
// Initialize the autoreset timer. It will automatically reset the repl
// after a burst of writes to the FS.
autoreset_init();
// Start USB after getting everything going.
#ifdef USB_REPL
udc_start();
#endif
// Run boot and main.
start_mp();
// Main script is finished, so now go into REPL mode.
// The REPL mode can change, or it can request a soft reset.
int exit_code = 0;
@ -233,6 +254,7 @@ int main(int argc, char **argv) {
if (exit_code == PYEXEC_FORCED_EXIT) {
mp_hal_stdout_tx_str("soft reboot\r\n");
reset_mp();
start_mp();
} else if (exit_code != 0) {
break;
}

View File

@ -1,5 +1,6 @@
#include <string.h>
#include "autoreset.h"
#include "compiler.h"
#include "asf/common/services/sleepmgr/sleepmgr.h"
#include "asf/common/services/usb/class/cdc/device/udi_cdc.h"
@ -67,6 +68,40 @@ void usb_rts_notify(uint8_t port, bool set) {
return;
}
void inject_character(char c) {
// Introduce a critical section to avoid buffer corruption. We use
// cpu_irq_save instead of cpu_irq_disable because we don't know the
// current state of IRQs. They may have been turned off already and
// we don't want to accidentally turn them back on.
irqflags_t flags = cpu_irq_save();
// If our buffer is full, then don't get another character otherwise
// we'll lose a previous character.
if (usb_rx_count >= USB_RX_BUF_SIZE) {
cpu_irq_restore(flags);
return;
}
uint8_t current_tail = usb_rx_buf_tail;
// Pretend we've received a character so that any nested calls to
// this function have to consider the spot we've reserved.
if ((USB_RX_BUF_SIZE - 1) == usb_rx_buf_tail) {
// Reached the end of buffer, revert back to beginning of
// buffer.
usb_rx_buf_tail = 0x00;
} else {
usb_rx_buf_tail++;
}
// The count of characters present in receive buffer is
// incremented.
usb_rx_count++;
// We put the next character where we expected regardless of whether
// the next character was already loaded in the buffer.
usb_rx_buf[current_tail] = c;
cpu_irq_restore(flags);
}
void usb_rx_notify(void)
{
irqflags_t flags;
@ -129,6 +164,9 @@ int receive_usb() {
return 0;
}
// Disable autoreset if someone is using the repl.
autoreset_disable();
// Copy from head.
cpu_irq_disable();
int data = usb_rx_buf[usb_rx_buf_head];

View File

@ -24,6 +24,8 @@
* THE SOFTWARE.
*/
#include "py/misc.h"
#define CHAR_CTRL_A (1)
#define CHAR_CTRL_B (2)
#define CHAR_CTRL_C (3)

View File

@ -226,6 +226,7 @@ STATIC int pyexec_friendly_repl_process_char(int c) {
} else if (ret == CHAR_CTRL_B) {
// reset friendly REPL
mp_hal_stdout_tx_str("\r\n");
mp_hal_stdout_tx_str("\r\n");
mp_hal_stdout_tx_str("Adafruit MicroPython " MICROPY_GIT_TAG " on " MICROPY_BUILD_DATE "; " MICROPY_HW_BOARD_NAME " with " MICROPY_HW_MCU_NAME "\r\n");
// mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n");
goto input_restart;

View File

@ -34,6 +34,8 @@
#include <stdint.h>
#include <stddef.h>
#include "mpconfig.h"
typedef unsigned char byte;
typedef unsigned int uint;