/* ---------------------------------------------------------------------------- * SAM Software Package License * ---------------------------------------------------------------------------- * Copyright (c) 2011-2014, Atmel Corporation * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following condition is met: * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the disclaimer below. * * Atmel's name may not be used to endorse or promote products derived from * this software without specific prior written permission. * * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * ---------------------------------------------------------------------------- */ /** * -------------------- * SAM-BA Implementation on SAMD21 * -------------------- * Requirements to use SAM-BA : * * Supported communication interfaces : * -------------------- * * SERCOM5 : RX:PB23 TX:PB22 * Baudrate : 115200 8N1 * * USB : D-:PA24 D+:PA25 * * Pins Usage * -------------------- * The following pins are used by the program : * PA25 : input/output * PA24 : input/output * PB23 : input * PB22 : output * PA15 : input * * The application board shall avoid driving the PA25,PA24,PB23,PB22 and PA15 * signals * while the boot program is running (after a POR for example) * * Clock system * -------------------- * CPU clock source (GCLK_GEN_0) - 8MHz internal oscillator (OSC8M) * SERCOM5 core GCLK source (GCLK_ID_SERCOM5_CORE) - GCLK_GEN_0 (i.e., OSC8M) * GCLK Generator 1 source (GCLK_GEN_1) - 48MHz DFLL in Clock Recovery mode * (DFLL48M) * USB GCLK source (GCLK_ID_USB) - GCLK_GEN_1 (i.e., DFLL in CRM mode) * * Memory Mapping * -------------------- * SAM-BA code will be located at 0x0 and executed before any applicative code. * * Applications compiled to be executed along with the bootloader will start at * 0x2000 (samd21) or 0x4000 (samd51) * The bootloader doesn't changes the VTOR register, application code is * taking care of this. * */ #include "uf2.h" static void check_start_application(void); static volatile bool main_b_cdc_enable = false; extern int8_t led_tick_step; #ifdef SAMD21 #define RESET_CONTROLLER PM #endif #ifdef SAMD51 #define RESET_CONTROLLER RSTC #endif #if defined(BOOT_KEY_ROW_PIN) && defined(BOOT_KEY_COL_PIN) /** * \brief Check whether the boot-hold key (bottom-right Cherry MX button) is * held down. Drives its matrix row low and reads its matrix column back, * the same polarity the app firmware's own matrix scan uses. */ static bool boot_key_pressed(void) { PORT_PINCFG_Type col_cfg = {0}; col_cfg.bit.PMUXEN = false; col_cfg.bit.INEN = true; // external 10k pull-up already on the board PORT_PINCFG_Type row_cfg = {0}; row_cfg.bit.PMUXEN = false; row_cfg.bit.DRVSTR = true; PINCFG(BOOT_KEY_COL_PIN) = col_cfg.reg; PINOP(BOOT_KEY_COL_PIN, DIRCLR); // column stays an input PINOP(BOOT_KEY_ROW_PIN, OUTCLR); // pre-set drive level before enabling output PINCFG(BOOT_KEY_ROW_PIN) = row_cfg.reg; PINOP(BOOT_KEY_ROW_PIN, DIRSET); // row -> output, driving low for (volatile int i = 0; i < 200; i++) { } // let the row settle through the diode/pull-up RC bool pressed = (PINIP(BOOT_KEY_COL_PIN) == 0); PINOP(BOOT_KEY_ROW_PIN, DIRCLR); // release row back to high-Z return pressed; } #endif /** * \brief Check the application startup condition * */ static void check_start_application(void) { uint32_t app_start_address; #if defined(BOOT_KEY_ROW_PIN) && defined(BOOT_KEY_COL_PIN) if (boot_key_pressed()) { /* Stay in bootloader */ return; } #endif // Check if there is an IO which will hold us inside the bootloader. #if defined(HOLD_PIN) && defined(HOLD_STATE) PORT_PINCFG_Type pincfg = {0}; pincfg.bit.PMUXEN = false; pincfg.bit.INEN = true; pincfg.bit.DRVSTR = true; PINOP(HOLD_PIN, DIRCLR); // Pin is an input #if defined(HOLD_PIN_PULLUP) pincfg.bit.PULLEN = true; PINOP(HOLD_PIN, OUTSET); // Pin is pulled up. #elif defined(HOLD_PIN_PULLDOWN) pincfg.bit.PULLEN = true; PINOP(HOLD_PIN, OUTCLR); // Pin is pulled up. #endif PINCFG(HOLD_PIN) = pincfg.reg; if (PINIP(HOLD_PIN) == HOLD_STATE) { /* Stay in bootloader */ return; } #endif /* Load the Reset Handler address of the application */ app_start_address = *(uint32_t *)(APP_START_ADDRESS + 4); /** * Test reset vector of application @APP_START_ADDRESS+4 * Sanity check on the Reset_Handler address */ if (app_start_address < APP_START_ADDRESS || app_start_address > FLASH_SIZE) { /* Stay in bootloader */ return; } #if USE_SINGLE_RESET if (SINGLE_RESET()) { if (RESET_CONTROLLER->RCAUSE.bit.POR || *DBL_TAP_PTR != DBL_TAP_MAGIC_QUICK_BOOT) { // the second tap on reset will go into app *DBL_TAP_PTR = DBL_TAP_MAGIC_QUICK_BOOT; // this will be cleared after succesful USB enumeration // this is around 1.5s resetHorizon = timerHigh + 50; return; } } #endif if (RESET_CONTROLLER->RCAUSE.bit.POR) { *DBL_TAP_PTR = 0; } else if (*DBL_TAP_PTR == DBL_TAP_MAGIC) { *DBL_TAP_PTR = 0; return; // stay in bootloader } else { if (*DBL_TAP_PTR != DBL_TAP_MAGIC_QUICK_BOOT) { *DBL_TAP_PTR = DBL_TAP_MAGIC; delay(500); } *DBL_TAP_PTR = 0; } LED_MSC_OFF(); #if defined(__SAMD21E18A__) RGBLED_set_color(COLOR_LEAVE); #endif /* Rebase the Stack Pointer */ __set_MSP(*(uint32_t *)APP_START_ADDRESS); /* Rebase the vector table base address */ SCB->VTOR = ((uint32_t)APP_START_ADDRESS & SCB_VTOR_TBLOFF_Msk); /* Ensure the MSP/VTOR writes are visible before jumping; without these * barriers the app's first fetch can race the pipeline (observed on * real hardware: identical MSP/VTOR/PC values injected by a halted * debugger boot fine, but the bootloader's own running jump hard-faults * every time). */ __DSB(); __ISB(); /* Jump to application Reset Handler in the application */ asm("bx %0" ::"r"(app_start_address)); } extern char _etext; extern char _end; /** * \brief SAMD21 SAM-BA Main loop. * \return Unused (ANSI-C compatibility). */ int main(void) { // if VTOR is set, we're not running in bootloader mode; halt if (SCB->VTOR) while (1) { } #if (USB_VID == 0x239a) && (USB_PID == 0x0013) // Adafruit Metro M0 // Delay a bit so SWD programmer can have time to attach. delay(15); #endif led_init(); logmsg("Start"); assert((uint32_t)&_etext < APP_START_ADDRESS); // bossac writes at 0x20005000 assert(!USE_MONITOR || (uint32_t)&_end < 0x20005000); assert(8 << NVMCTRL->PARAM.bit.PSZ == FLASH_PAGE_SIZE); assert(FLASH_PAGE_SIZE * NVMCTRL->PARAM.bit.NVMP == FLASH_SIZE); /* Jump in application if condition is satisfied */ check_start_application(); /* We have determined we should stay in the monitor. */ /* System initialization */ system_init(); __DMB(); __enable_irq(); #if USE_UART /* UART is enabled in all cases */ usart_open(); #endif logmsg("Before main loop"); usb_init(); // not enumerated yet RGBLED_set_color(COLOR_START); led_tick_step = 10; /* Wait for a complete enum on usb or a '#' char on serial line */ while (1) { if (USB_Ok()) { if (!main_b_cdc_enable) { #if USE_SINGLE_RESET // this might have been set resetHorizon = 0; #endif RGBLED_set_color(COLOR_USB); led_tick_step = 1; #if USE_SCREEN screen_init(); draw_drag(); #endif } main_b_cdc_enable = true; } #if USE_MONITOR // Check if a USB enumeration has succeeded // And com port was opened if (main_b_cdc_enable) { logmsg("entering monitor loop"); // SAM-BA on USB loop while (1) { sam_ba_monitor_run(); } } #if USE_UART /* Check if a '#' has been received */ if (!main_b_cdc_enable && usart_sharp_received()) { RGBLED_set_color(COLOR_UART); sam_ba_monitor_init(SAM_BA_INTERFACE_USART); /* SAM-BA on UART loop */ while (1) { sam_ba_monitor_run(); } } #endif #else // no monitor if (main_b_cdc_enable) { process_msc(); } #endif if (!main_b_cdc_enable) { // get more predictable timings before the USB is enumerated for (int i = 1; i < 256; ++i) { asm("nop"); } } } }