diff --git a/atmel-samd/Makefile b/atmel-samd/Makefile index ffc75859a6..025207fdc9 100644 --- a/atmel-samd/Makefile +++ b/atmel-samd/Makefile @@ -161,6 +161,7 @@ SRC_C = \ mphalport.c \ pin_named_pins.c \ samdneopixel.c \ + tick.c \ $(FLASH_IMPL) \ asf/common/services/sleepmgr/samd/sleepmgr.c \ asf/common/services/storage/ctrl_access/ctrl_access.c \ diff --git a/atmel-samd/autoreset.c b/atmel-samd/autoreset.c index ef7ca62a58..047e59259e 100644 --- a/atmel-samd/autoreset.c +++ b/atmel-samd/autoreset.c @@ -27,46 +27,28 @@ #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); - } +volatile uint32_t autoreset_delay_ms = 0; +bool autoreset_enabled = false; +volatile bool reset_next_character = false; - 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 +inline void autoreset_tick() { + if (autoreset_delay_ms == 0) { + return; + } + if (autoreset_delay_ms == 1 && autoreset_enabled && !reset_next_character) { + mp_keyboard_interrupt(); + reset_next_character = true; + } + autoreset_delay_ms--; } void autoreset_enable() { autoreset_enabled = true; + reset_next_character = false; } void autoreset_disable() { @@ -74,14 +56,9 @@ void autoreset_disable() { } void autoreset_start() { - if (!autoreset_initialized) return; - - tc_stop_counter(&autoreset_timer); - tc_start_counter(&autoreset_timer); + autoreset_delay_ms = AUTORESET_DELAY_MS; } void autoreset_stop() { - if (!autoreset_initialized) return; - - tc_stop_counter(&autoreset_timer); + autoreset_delay_ms = 0; } diff --git a/atmel-samd/autoreset.h b/atmel-samd/autoreset.h index da9803fdef..51b8285e70 100644 --- a/atmel-samd/autoreset.h +++ b/atmel-samd/autoreset.h @@ -27,10 +27,12 @@ #ifndef __MICROPY_INCLUDED_ATMEL_SAMD_AUTORESET_H__ #define __MICROPY_INCLUDED_ATMEL_SAMD_AUTORESET_H__ -#include "asf/sam0/drivers/tc/tc.h" +#include + +extern volatile bool reset_next_character; + +void autoreset_tick(); -void autoreset_callback(struct tc_module *const module_inst); -void autoreset_init(); void autoreset_start(); void autoreset_stop(); void autoreset_enable(); diff --git a/atmel-samd/boards/arduino_zero/mpconfigboard.h b/atmel-samd/boards/arduino_zero/mpconfigboard.h index 34a22c81ae..5b1c23d652 100644 --- a/atmel-samd/boards/arduino_zero/mpconfigboard.h +++ b/atmel-samd/boards/arduino_zero/mpconfigboard.h @@ -7,4 +7,6 @@ #define MICROPY_HW_LED_TX PIN_PA27 #define MICROPY_HW_LED_RX PIN_PB03 -#define AUTORESET_TIMER TC5 +#define MICROPY_HW_NEOPIXEL PIN_PB22 + +#define AUTORESET_DELAY_MS 10 diff --git a/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h b/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h index 8af47af1e6..f30c02be8f 100644 --- a/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h +++ b/atmel-samd/boards/feather_m0_adalogger/mpconfigboard.h @@ -6,4 +6,4 @@ #define MICROPY_HW_BOARD_NAME "Adafruit Feather M0 Adalogger" #define MICROPY_HW_MCU_NAME "samd21g18" -#define AUTORESET_TIMER TC5 +#define AUTORESET_DELAY_MS 10 diff --git a/atmel-samd/boards/feather_m0_basic/mpconfigboard.h b/atmel-samd/boards/feather_m0_basic/mpconfigboard.h index 29027c0b8f..4f884e3641 100644 --- a/atmel-samd/boards/feather_m0_basic/mpconfigboard.h +++ b/atmel-samd/boards/feather_m0_basic/mpconfigboard.h @@ -6,4 +6,4 @@ #define MICROPY_HW_BOARD_NAME "Adafruit Feather M0 Basic" #define MICROPY_HW_MCU_NAME "samd21g18" -#define AUTORESET_TIMER TC5 +#define AUTORESET_DELAY_MS 10 diff --git a/atmel-samd/boards/metro_m0_flash/mpconfigboard.h b/atmel-samd/boards/metro_m0_flash/mpconfigboard.h index 3cbd0a641e..50cbb271f7 100644 --- a/atmel-samd/boards/metro_m0_flash/mpconfigboard.h +++ b/atmel-samd/boards/metro_m0_flash/mpconfigboard.h @@ -8,6 +8,8 @@ #define MICROPY_HW_LED_TX PIN_PA27 #define MICROPY_HW_LED_RX PIN_PB03 +#define MICROPY_HW_NEOPIXEL PIN_PA30 + #define SPI_FLASH_BAUDRATE (1000000) // Off-board flash @@ -30,4 +32,4 @@ #define SPI_FLASH_CS PIN_PA13 #define SPI_FLASH_SERCOM SERCOM4 -#define AUTORESET_TIMER TC5 +#define AUTORESET_DELAY_MS 500 diff --git a/atmel-samd/internal_flash.c b/atmel-samd/internal_flash.c index 1bbbd23e8f..9bf4682eb4 100644 --- a/atmel-samd/internal_flash.c +++ b/atmel-samd/internal_flash.c @@ -23,6 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "internal_flash.h" #include #include @@ -36,7 +37,7 @@ #include "asf/sam0/drivers/nvm/nvm.h" #include "asf/sam0/drivers/port/port.h" -#include "internal_flash.h" +#include "samdneopixel.h" #define TOTAL_INTERNAL_FLASH_SIZE 0x010000 @@ -173,6 +174,9 @@ bool internal_flash_write_block(const uint8_t *src, uint32_t block) { #ifdef MICROPY_HW_LED_MSC port_pin_set_output_level(MICROPY_HW_LED_MSC, true); #endif + #ifdef MICROPY_HW_NEOPIXEL + temp_status_color(0x8f, 0x00, 0x00); + #endif // non-MBR block, copy to cache volatile uint32_t dest = convert_block_to_flash_addr(block); if (dest == -1) { @@ -210,6 +214,9 @@ bool internal_flash_write_block(const uint8_t *src, uint32_t block) { return false; } } + #ifdef MICROPY_HW_NEOPIXEL + clear_temp_status(); + #endif #ifdef MICROPY_HW_LED_MSC port_pin_set_output_level(MICROPY_HW_LED_MSC, false); #endif diff --git a/atmel-samd/internal_flash.h b/atmel-samd/internal_flash.h index 38886c9304..d59f7b38a4 100644 --- a/atmel-samd/internal_flash.h +++ b/atmel-samd/internal_flash.h @@ -26,6 +26,8 @@ #ifndef __MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_H__ #define __MICROPY_INCLUDED_ATMEL_SAMD_INTERNAL_FLASH_H__ +#include + #include "mpconfigport.h" #define INTERNAL_FLASH_BLOCK_SIZE (512) diff --git a/atmel-samd/main.c b/atmel-samd/main.c index 2a0dff8519..560e272877 100644 --- a/atmel-samd/main.c +++ b/atmel-samd/main.c @@ -25,6 +25,8 @@ #include "autoreset.h" #include "mpconfigboard.h" #include "modmachine_pin.h" +#include "samdneopixel.h" +#include "tick.h" fs_user_mount_t fs_user_mount_flash; @@ -173,6 +175,7 @@ static char *stack_top; static char heap[16384]; void reset_mp() { + new_status_color(0x8f, 0x00, 0x8f); autoreset_stop(); autoreset_enable(); @@ -198,14 +201,20 @@ void reset_mp() { } void start_mp() { - #ifdef AUTORESET_TIMER + #ifdef AUTORESET_DELAY_MS 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 + new_status_color(0x00, 0x00, 0x8f); mp_hal_stdout_tx_str("boot.py output:\r\n"); - pyexec_file("boot.py"); + int ret = pyexec_file("boot.py"); + if (ret & PYEXEC_FORCED_EXIT) { + return; + } + + new_status_color(0x00, 0x8f, 0x00); mp_hal_stdout_tx_str("\r\nmain.py output:\r\n"); pyexec_file("main.py"); } @@ -230,10 +239,6 @@ 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(); @@ -246,6 +251,7 @@ int main(int argc, char **argv) { // The REPL mode can change, or it can request a soft reset. int exit_code = 0; for (;;) { + new_status_color(0x3f, 0x3f, 0x3f); if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) { exit_code = pyexec_raw_repl(); } else { @@ -315,7 +321,9 @@ void MP_WEAK __assert_func(const char *file, int line, const char *func, const c #if MICROPY_MIN_USE_SAMD21_MCU +#ifdef UART_REPL struct usart_module usart_instance; +#endif #ifdef ENABLE_MICRO_TRACE_BUFFER // Stores 2 ^ TRACE_BUFFER_MAGNITUDE_PACKETS packets. @@ -364,8 +372,8 @@ void samd21_init(void) { board_init(); - // SysTick millisecond timer initialization. - SysTick_Config(system_cpu_clock_get_hz() / 1000); + // Configure millisecond timer initialization. + tick_init(); // Uncomment to init PIN_PA17 for debugging. // struct port_config pin_conf; @@ -374,6 +382,16 @@ void samd21_init(void) { // pin_conf.direction = PORT_PIN_DIR_OUTPUT; // port_pin_set_config(MICROPY_HW_LED1, &pin_conf); // port_pin_set_output_level(MICROPY_HW_LED1, false); + + + #ifdef MICROPY_HW_NEOPIXEL + struct port_config pin_conf; + port_get_config_defaults(&pin_conf); + + pin_conf.direction = PORT_PIN_DIR_OUTPUT; + port_pin_set_config(MICROPY_HW_NEOPIXEL, &pin_conf); + port_pin_set_output_level(MICROPY_HW_NEOPIXEL, false); + #endif } #endif diff --git a/atmel-samd/mphalport.c b/atmel-samd/mphalport.c index a581e0986d..d442fe8a8c 100644 --- a/atmel-samd/mphalport.c +++ b/atmel-samd/mphalport.c @@ -7,6 +7,7 @@ #include "asf/common2/services/delay/delay.h" #include "asf/sam0/drivers/port/port.h" #include "asf/sam0/drivers/sercom/usart/usart.h" +#include "lib/mp-readline/readline.h" #include "py/mphal.h" #include "py/mpstate.h" #include "py/smallint.h" @@ -68,40 +69,6 @@ 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; @@ -191,6 +158,9 @@ int mp_hal_stdin_rx_chr(void) { udi_msc_process_trans(); } #ifdef USB_REPL + if (reset_next_character) { + return CHAR_CTRL_D; + } if (usb_rx_count > 0) { #ifdef MICROPY_HW_LED_RX port_pin_toggle_output_level(MICROPY_HW_LED_RX); @@ -277,18 +247,6 @@ void mp_hal_delay_us(mp_uint_t delay) { delay_us(delay); } -// Global millisecond tick count (driven by SysTick interrupt handler). -volatile uint32_t systick_ticks_ms = 0; - -void SysTick_Handler(void) { - // SysTick interrupt handler called when the SysTick timer reaches zero - // (every millisecond). - systick_ticks_ms += 1; - // Keep the counter within the range of 31 bit uint values since that's the - // max value for micropython 'small' ints. - systick_ticks_ms = systick_ticks_ms > (0xFFFFFFFF >> 1) ? 0 : systick_ticks_ms; -} - // Interrupt flags that will be saved and restored during disable/Enable // interrupt functions below. static irqflags_t irq_flags; @@ -297,14 +255,10 @@ void mp_hal_disable_all_interrupts(void) { // Disable all interrupt sources for timing critical sections. // Disable ASF-based interrupts. irq_flags = cpu_irq_save(); - // Disable SysTick interrupt. - SysTick->CTRL &= ~SysTick_CTRL_TICKINT_Msk; } void mp_hal_enable_all_interrupts(void) { // Enable all interrupt sources after timing critical sections. - // Restore SysTick interrupt. - SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; // Restore ASF-based interrupts. cpu_irq_restore(irq_flags); } diff --git a/atmel-samd/mphalport.h b/atmel-samd/mphalport.h index 0e8635f648..858d628134 100644 --- a/atmel-samd/mphalport.h +++ b/atmel-samd/mphalport.h @@ -32,10 +32,10 @@ #define USB_RX_BUF_SIZE 128 // Global millisecond tick count (driven by SysTick interrupt). -extern volatile uint32_t systick_ticks_ms; +extern volatile uint32_t ticks_ms; static inline mp_uint_t mp_hal_ticks_ms(void) { - return systick_ticks_ms; + return ticks_ms; } void mp_hal_set_interrupt_char(int c); diff --git a/atmel-samd/samdneopixel.c b/atmel-samd/samdneopixel.c index e271de0467..684188c1bb 100644 --- a/atmel-samd/samdneopixel.c +++ b/atmel-samd/samdneopixel.c @@ -5,6 +5,30 @@ #include "samdneopixel.h" +#ifdef MICROPY_HW_NEOPIXEL +static uint8_t status_neopixel_color[3]; +#endif + +extern void new_status_color(uint8_t r, uint8_t g, uint8_t b) { + #ifdef MICROPY_HW_NEOPIXEL + status_neopixel_color[0] = g; + status_neopixel_color[1] = r; + status_neopixel_color[2] = b; + samd_neopixel_write(MICROPY_HW_NEOPIXEL, status_neopixel_color, 3, true); + #endif +} +extern void temp_status_color(uint8_t r, uint8_t g, uint8_t b) { + #ifdef MICROPY_HW_NEOPIXEL + uint8_t colors[3] = {g, r, b}; + samd_neopixel_write(MICROPY_HW_NEOPIXEL, colors, 3, true); + #endif +} +extern void clear_temp_status() { + #ifdef MICROPY_HW_NEOPIXEL + samd_neopixel_write(MICROPY_HW_NEOPIXEL, status_neopixel_color, 3, true); + #endif +} + void samd_neopixel_write(uint32_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz) { // This is adapted directly from the Adafruit NeoPixel library SAMD21G18A code: // https://github.com/adafruit/Adafruit_NeoPixel/blob/master/Adafruit_NeoPixel.cpp @@ -82,8 +106,8 @@ void samd_neopixel_write(uint32_t pin, uint8_t *pixels, uint32_t numBytes, bool // Turn on interrupts after timing-sensitive code. mp_hal_enable_all_interrupts(); - // 50ms delay to let pixels latch to the data that was just sent. + // 50us delay to let pixels latch to the data that was just sent. // This could be optimized to only occur before pixel writes when necessary, // like in the Arduino library. - delay_ms(50); + delay_us(50); } diff --git a/atmel-samd/samdneopixel.h b/atmel-samd/samdneopixel.h index 8448d45009..0ebc080485 100644 --- a/atmel-samd/samdneopixel.h +++ b/atmel-samd/samdneopixel.h @@ -4,6 +4,10 @@ #include #include +extern void new_status_color(uint8_t r, uint8_t g, uint8_t b); +extern void temp_status_color(uint8_t r, uint8_t g, uint8_t b); +extern void clear_temp_status(); + void samd_neopixel_write(uint32_t pin, uint8_t *pixels, uint32_t numBytes, bool is800KHz); #endif diff --git a/atmel-samd/spi_flash.c b/atmel-samd/spi_flash.c index 26234b2806..6816757b40 100644 --- a/atmel-samd/spi_flash.c +++ b/atmel-samd/spi_flash.c @@ -23,6 +23,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ +#include "spi_flash.h" #include #include @@ -35,7 +36,7 @@ #include "lib/fatfs/ff.h" #include "extmod/fsusermount.h" -#include "spi_flash.h" +#include "samdneopixel.h" #define SPI_FLASH_PART1_START_BLOCK (0x1) @@ -383,6 +384,9 @@ static void spi_flash_flush_keep_cache(bool keep_cache) { #ifdef MICROPY_HW_LED_MSC port_pin_set_output_level(MICROPY_HW_LED_MSC, true); #endif + #ifdef MICROPY_HW_NEOPIXEL + temp_status_color(0x8f, 0x00, 0x00); + #endif // If we've cached to the flash itself flush from there. if (ram_cache == NULL) { flush_scratch_flash(); @@ -390,6 +394,9 @@ static void spi_flash_flush_keep_cache(bool keep_cache) { flush_ram_cache(keep_cache); } current_sector = NO_SECTOR_LOADED; + #ifdef MICROPY_HW_NEOPIXEL + clear_temp_status(); + #endif #ifdef MICROPY_HW_LED_MSC port_pin_set_output_level(MICROPY_HW_LED_MSC, false); #endif diff --git a/atmel-samd/spi_flash.h b/atmel-samd/spi_flash.h index ec7e23ac5d..8688928b40 100644 --- a/atmel-samd/spi_flash.h +++ b/atmel-samd/spi_flash.h @@ -26,6 +26,8 @@ #ifndef __MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H__ #define __MICROPY_INCLUDED_ATMEL_SAMD_SPI_FLASH_H__ +#include + #include "mpconfigport.h" // Erase sector size. diff --git a/atmel-samd/tick.c b/atmel-samd/tick.c new file mode 100644 index 0000000000..bcbfcc0599 --- /dev/null +++ b/atmel-samd/tick.c @@ -0,0 +1,36 @@ +#include "autoreset.h" + +#include "tick.h" + +#include "asf/sam0/drivers/tc/tc_interrupt.h" + +// Global millisecond tick count +volatile uint32_t ticks_ms = 0; + +static struct tc_module ms_timer; + +static void ms_tick(struct tc_module *const module_inst) { + // SysTick interrupt handler called when the SysTick timer reaches zero + // (every millisecond). + ticks_ms += 1; + // Keep the counter within the range of 31 bit uint values since that's the + // max value for micropython 'small' ints. + ticks_ms = ticks_ms > (0xFFFFFFFF >> 1) ? 0 : ticks_ms; + + #ifdef AUTORESET_DELAY_MS + autoreset_tick(); + #endif +} + +void tick_init() { + 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_DIV1; + tc_set_top_value(&ms_timer, system_cpu_clock_get_hz() / 1000); + tc_init(&ms_timer, TC5, &config_tc); + tc_enable(&ms_timer); + tc_register_callback(&ms_timer, ms_tick, TC_CALLBACK_OVERFLOW); + tc_enable_callback(&ms_timer, TC_CALLBACK_OVERFLOW); + tc_start_counter(&ms_timer); +} diff --git a/atmel-samd/tick.h b/atmel-samd/tick.h new file mode 100644 index 0000000000..731a691469 --- /dev/null +++ b/atmel-samd/tick.h @@ -0,0 +1,35 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2013, 2014 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_ATMEL_SAMD_TICK_H__ +#define __MICROPY_INCLUDED_ATMEL_SAMD_TICK_H__ + +#include "mpconfigport.h" + +extern volatile uint32_t ticks_ms; + +void tick_init(); + +#endif // __MICROPY_INCLUDED_ATMEL_SAMD_TICK_H__