ArduinoCore-samd/bootloaders/zero/main.c
2018-08-30 14:07:08 -04:00

216 lines
5.8 KiB
C

/*
Copyright (c) 2015 Arduino LLC. All right reserved.
Copyright (c) 2015 Atmel Corporation/Thibaut VIARD. All right reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <sam.h>
#include "sam_ba_monitor.h"
#include "sam_ba_serial.h"
#include "board_definitions.h"
#include "board_driver_led.h"
#include "sam_ba_usb.h"
#include "sam_ba_cdc.h"
extern uint32_t __sketch_vectors_ptr; // Exported value from linker script
extern void board_init(void);
#if (defined DEBUG) && (DEBUG == 1)
volatile uint32_t* pulSketch_Start_Address;
#endif
static volatile bool main_b_cdc_enable = false;
/**
* \brief Check the application startup condition
*
*/
static void check_start_application(void)
{
// LED_init();
// LED_off();
#if defined(BOOT_DOUBLE_TAP_ADDRESS)
#define DOUBLE_TAP_MAGIC 0x07738135
if (PM->RCAUSE.bit.POR)
{
/* On power-on initialize double-tap */
BOOT_DOUBLE_TAP_DATA = 0;
}
else
{
if (BOOT_DOUBLE_TAP_DATA == DOUBLE_TAP_MAGIC)
{
/* Second tap, stay in bootloader */
BOOT_DOUBLE_TAP_DATA = 0;
return;
}
/* First tap */
BOOT_DOUBLE_TAP_DATA = DOUBLE_TAP_MAGIC;
/* Wait 0.5sec to see if the user tap reset again.
* The loop value is based on SAMD21 default 1MHz clock @ reset.
*/
for (uint32_t i=0; i<125000; i++) /* 500ms */
/* force compiler to not optimize this... */
__asm__ __volatile__("");
/* Timeout happened, continue boot... */
BOOT_DOUBLE_TAP_DATA = 0;
}
#endif
#if (!defined DEBUG) || ((defined DEBUG) && (DEBUG == 0))
uint32_t* pulSketch_Start_Address;
#endif
/*
* Test sketch stack pointer @ &__sketch_vectors_ptr
* Stay in SAM-BA if value @ (&__sketch_vectors_ptr) == 0xFFFFFFFF (Erased flash cell value)
*/
if (__sketch_vectors_ptr == 0xFFFFFFFF)
{
/* Stay in bootloader */
return;
}
/*
* Load the sketch Reset Handler address
* __sketch_vectors_ptr is exported from linker script and point on first 32b word of sketch vector table
* First 32b word is sketch stack
* Second 32b word is sketch entry point: Reset_Handler()
*/
pulSketch_Start_Address = &__sketch_vectors_ptr ;
pulSketch_Start_Address++ ;
/*
* Test vector table address of sketch @ &__sketch_vectors_ptr
* Stay in SAM-BA if this function is not aligned enough, ie not valid
*/
if ( ((uint32_t)(&__sketch_vectors_ptr) & ~SCB_VTOR_TBLOFF_Msk) != 0x00)
{
/* Stay in bootloader */
return;
}
/*
#if defined(BOOT_LOAD_PIN)
volatile PortGroup *boot_port = (volatile PortGroup *)(&(PORT->Group[BOOT_LOAD_PIN / 32]));
volatile bool boot_en;
// Enable the input mode in Boot GPIO Pin
boot_port->DIRCLR.reg = BOOT_PIN_MASK;
boot_port->PINCFG[BOOT_LOAD_PIN & 0x1F].reg = PORT_PINCFG_INEN | PORT_PINCFG_PULLEN;
boot_port->OUTSET.reg = BOOT_PIN_MASK;
// Read the BOOT_LOAD_PIN status
boot_en = (boot_port->IN.reg) & BOOT_PIN_MASK;
// Check the bootloader enable condition
if (!boot_en)
{
// Stay in bootloader
return;
}
#endif
*/
// LED_on();
/* Rebase the Stack Pointer */
__set_MSP( (uint32_t)(__sketch_vectors_ptr) );
/* Rebase the vector table base address */
SCB->VTOR = ((uint32_t)(&__sketch_vectors_ptr) & SCB_VTOR_TBLOFF_Msk);
/* Jump to application Reset Handler in the application */
asm("bx %0"::"r"(*pulSketch_Start_Address));
}
#if DEBUG_ENABLE
# define DEBUG_PIN_HIGH port_pin_set_output_level(BOOT_LED, 1)
# define DEBUG_PIN_LOW port_pin_set_output_level(BOOT_LED, 0)
#else
# define DEBUG_PIN_HIGH do{}while(0)
# define DEBUG_PIN_LOW do{}while(0)
#endif
/**
* \brief SAMD21 SAM-BA Main loop.
* \return Unused (ANSI-C compatibility).
*/
int main(void)
{
#if SAM_BA_INTERFACE == SAM_BA_USBCDC_ONLY || SAM_BA_INTERFACE == SAM_BA_BOTH_INTERFACES
P_USB_CDC pCdc;
#endif
DEBUG_PIN_HIGH;
/* Jump in application if condition is satisfied */
check_start_application();
/* We have determined we should stay in the monitor. */
/* System initialization */
board_init();
__enable_irq();
#if SAM_BA_INTERFACE == SAM_BA_UART_ONLY || SAM_BA_INTERFACE == SAM_BA_BOTH_INTERFACES
/* UART is enabled in all cases */
serial_open();
#endif
#if SAM_BA_INTERFACE == SAM_BA_USBCDC_ONLY || SAM_BA_INTERFACE == SAM_BA_BOTH_INTERFACES
pCdc = usb_init();
#endif
DEBUG_PIN_LOW;
/* Wait for a complete enum on usb or a '#' char on serial line */
while (1)
{
#if SAM_BA_INTERFACE == SAM_BA_USBCDC_ONLY || SAM_BA_INTERFACE == SAM_BA_BOTH_INTERFACES
if (pCdc->IsConfigured(pCdc) != 0)
{
main_b_cdc_enable = true;
}
/* Check if a USB enumeration has succeeded and if comm port has been opened */
if (main_b_cdc_enable)
{
sam_ba_monitor_init(SAM_BA_INTERFACE_USBCDC);
/* SAM-BA on USB loop */
while( 1 )
{
sam_ba_monitor_run();
}
}
#endif
#if SAM_BA_INTERFACE == SAM_BA_UART_ONLY || SAM_BA_INTERFACE == SAM_BA_BOTH_INTERFACES
/* Check if a '#' has been received */
if (!main_b_cdc_enable && serial_sharp_received())
{
sam_ba_monitor_init(SAM_BA_INTERFACE_USART);
/* SAM-BA on Serial loop */
while(1)
{
sam_ba_monitor_run();
}
}
#endif
}
}