You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
104 lines
5.7 KiB
104 lines
5.7 KiB
/* This program is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* This program 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 General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
* |
|
*/ |
|
/** USB DFU bootloader |
|
* @file bootloader.c |
|
* @author King Kévin <kingkevin@cuvoodoo.info> |
|
* @date 2017-2019 |
|
*/ |
|
/* 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 |
|
|
|
/** 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 if (0 == (RCC_CSR & 0xfc000000)) { // no reset flag present -> this was a soft reset using scb_reset_core() after clearing the flags using RCC_CSR_RMVF, this was the legacy way to start the DFU mode |
|
dfu_force = true; |
|
} 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)) |
|
#if ((GPIO(B) == GPIO_PORT(DFU_FORCE_PIN)) && (GPIO(4) == GPIO_PIN(DFU_FORCE_PIN))) |
|
// JNTRST pin is used as DFU pin |
|
rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternate function domain |
|
gpio_primary_remap(AFIO_MAPR_SWJ_CFG_FULL_SWJ_NO_JNTRST, 0); // keep SWJ enable bit don't use JNTRST |
|
#elif ((GPIO(B) == GPIO_PORT(DFU_FORCE_PIN)) && (GPIO(3) == GPIO_PIN(DFU_FORCE_PIN))) || ((GPIO(A) == GPIO_PORT(DFU_FORCE_PIN)) && (GPIO(15) == GPIO_PIN(DFU_FORCE_PIN))) |
|
// JTAG but not SWD pin used as DFU pin |
|
rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternate function domain |
|
gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0); // disable JTAG but keep SWD |
|
#elif ((GPIO(A) == GPIO_PORT(DFU_FORCE_PIN)) && (GPIO(14) == GPIO_PIN(DFU_FORCE_PIN))) || ((GPIO(A) == GPIO_PORT(DFU_FORCE_PIN)) && (GPIO(13) == GPIO_PIN(DFU_FORCE_PIN))) |
|
// JTAG and SWD pin used as DFU pin |
|
rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternate function domain |
|
gpio_primary_remap(AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_OFF, 0); // disable JTAG and SWD |
|
#endif // DFU_FORCE_PIN |
|
rcc_periph_clock_enable(GPIO_RCC(DFU_FORCE_PIN)); // enable clock for GPIO domain |
|
gpio_set_mode(GPIO_PORT(DFU_FORCE_PIN), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN(DFU_FORCE_PIN)); // set GPIO to input |
|
// pull on the opposite of the expected value |
|
#if (DFU_FORCE_VALUE == 1) |
|
gpio_clear(GPIO_PORT(DFU_FORCE_PIN), GPIO_PIN(DFU_FORCE_PIN)); // pull down to be able to detect when tied to high |
|
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_set(GPIO_PORT(DFU_FORCE_PIN), GPIO_PIN(DFU_FORCE_PIN)); // pull up to be able to detect when tied to low |
|
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) |
|
} |
|
|
|
// 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 STM32F1xx SRAM begins at 0x2000 0000, and on STM32F103xx there is up to 96 KB of RAM (0x18000). |
|
* since the stack grown "downwards" it should start at the end of the RAM: max 0x2001 8000 |
|
* 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) & 0xFFFE0000) == 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) |
|
} |
|
|
|
rcc_clock_setup_in_hse_8mhz_out_72mhz(); // start main clock |
|
board_setup(); // setup board to control LED |
|
led_on(); // indicate bootloader started |
|
#if defined(BUSVOODOO) |
|
led_toggle(); // switch from blue to red LED |
|
#endif |
|
usb_dfu_setup(); // setup USB DFU for firmware upload |
|
usb_dfu_start(); // run DFU mode |
|
}
|
|
|