stm32f1/bootloader.c

78 lines
3.8 KiB
C

/** USB DFU bootloader
* @file
* @author King Kévin <kingkevin@cuvoodoo.info>
* @copyright SPDX-License-Identifier: GPL-3.0-or-later
* @date 2017-2020
*/
/* standard libraries */
#include <stdint.h> // standard integer types
#include <stdbool.h> // boolean types
/* STM32 (including CM3) libraries */
#include <libopencm3/cm3/scb.h> // vector table definition
#include <libopencm3/stm32/rcc.h> // clock utilities
#include <libopencm3/stm32/gpio.h> // GPIO utilities
/* own libraries */
#include "global.h" // board definitions
#include "usb_dfu.h" // USB DFU utilities
/** symbol for beginning of the application
* @note this symbol will be provided by the bootloader linker script
*/
extern char __application_beginning;
/** bootloader entry point */
void main(void);
void main(void)
{
// check of DFU mode is forced
bool dfu_force = false; // to remember if DFU mode is forced
// check if DFU magic DFU! has been written to RAM (e.g. by application to indicate we want to start the DFU bootloader)
if ('D' == __dfu_magic[0] && 'F' == __dfu_magic[1] && 'U' == __dfu_magic[2] && '!' == __dfu_magic[3]) { // verify if the DFU magic is set
dfu_force = true;
// clear DFU magic
__dfu_magic[0] = 0;
__dfu_magic[1] = 0;
__dfu_magic[2] = 0;
__dfu_magic[3] = 0;
} else { // check if the force DFU mode input is set
// disable SWJ pin to use as GPIO
#if (defined(DFU_FORCE_PIN) && defined(DFU_FORCE_VALUE))
rcc_periph_clock_enable(GPIO_RCC(DFU_FORCE_PIN)); // enable clock for button
#if (DFU_FORCE_VALUE == 1)
gpio_mode_setup(GPIO_PORT(DFU_FORCE_PIN), GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN(DFU_FORCE_PIN)); // set GPIO to input
if (gpio_get(GPIO_PORT(DFU_FORCE_PIN), GPIO_PIN(DFU_FORCE_PIN))) { // check if output is set to the value to force DFU mode
#else
gpio_mode_setup(GPIO_PORT(DFU_FORCE_PIN), GPIO_MODE_INPUT, GPIO_PUPD_PULLUP, GPIO_PIN(DFU_FORCE_PIN)); // set GPIO to input
if (0 == gpio_get(GPIO_PORT(DFU_FORCE_PIN), GPIO_PIN(DFU_FORCE_PIN))) { // check if output is set to the value to force DFU mode
#endif // DFU_FORCE_VALUE
dfu_force = true; // DFU mode forced
}
#endif // defined(DFU_FORCE_PIN)
rcc_periph_reset_pulse(GPIO_RST(DFU_FORCE_PIN)); // reset pin GPIO domain
rcc_periph_clock_disable(GPIO_RCC(DFU_FORCE_PIN)); // disable pin GPIO domain
}
// start application if valid
/* the application starts with the vector table
* the first entry in the vector table is the initial stack pointer (SP) address
* the stack will be placed in RAM
* on STM32F4 SRAM begins at 0x2000 0000, and on STM32F4xx there is up to 384 KiB of RAM (0x60000).
* since the stack grown "downwards" it should start at the end of the RAM: max 0x2006 0000
* if the SP is not in this range (e.g. flash has been erased) there is no valid application
* the second entry in the vector table is the reset address, corresponding to the application start
*/
volatile uint32_t* application = (uint32_t*)&__application_beginning; // get the value of the application address symbol (use a register instead on the stack since the stack pointer will be changed)
if (!dfu_force && (((*application) & 0xFFF80000) == 0x20000000)) { // application at address seems valid
SCB_VTOR = (volatile uint32_t)(application); // set vector table to application vector table (store at the beginning of the application)
__asm__ volatile ("MSR msp,%0" : :"r"(*application)); // set stack pointer to address provided in the beginning of the application (loaded into a register first)
(*(void(**)(void))((uint32_t)application + 4))(); // start application (by jumping to the reset function which address is stored as second entry of the vector table)
}
board_setup(); // setup board to control LED
led_on(); // indicate bootloader started
usb_dfu_setup(); // setup USB DFU for firmware upload
usb_dfu_start(); // run DFU mode
}