global: add common function to start DFU and systeme memory
This commit is contained in:
parent
de36c7f3a2
commit
06de8d0be9
62
global.c
62
global.c
@ -16,6 +16,8 @@
|
||||
#include <libopencm3/stm32/rcc.h> // real-time control clock library
|
||||
#include <libopencm3/stm32/gpio.h> // general purpose input output library
|
||||
#include <libopencm3/stm32/exti.h> // external interrupt defines
|
||||
#include <libopencm3/stm32/syscfg.h> // system definitions
|
||||
#include <libopencm3/usb/dwc/otg_fs.h> // USB OTG utilities
|
||||
|
||||
#include "global.h" // common methods
|
||||
|
||||
@ -201,6 +203,66 @@ void board_setup(void)
|
||||
user_input_used = 0;
|
||||
}
|
||||
|
||||
/** disconnect USB by sending a reset condition */
|
||||
static void usb_disconnect(void)
|
||||
{
|
||||
if (OTG_FS_GUSBCFG & OTG_GUSBCFG_FDMOD) { // USB configured as device
|
||||
// pull USB D+ low for a short while
|
||||
OTG_FS_DCTL |= OTG_DCTL_SDIS; // disconnect DP pull-up to simulate a disconnect
|
||||
// in case there is an external pull-up resistor, pull DP low
|
||||
// I have no idea why, but once USB is configured, I can't use PA12/DP back as GPIO
|
||||
gpio_mode_setup(GPIOA, GPIO_MODE_OUTPUT, GPIO_PUPD_NONE, GPIO12); // be sure the D+ pin can be used as GPIO output
|
||||
gpio_set_output_options(GPIOA, GPIO_OTYPE_PP, GPIO_OSPEED_2MHZ, GPIO12); // use push-pull output
|
||||
gpio_clear(GPIOA, GPIO12); // pull D+ low
|
||||
for (volatile uint32_t i = 0; i < 0x2000; i++); // USB disconnected must be at least 10 ms long, at most 100 ms
|
||||
}
|
||||
}
|
||||
|
||||
void system_memory(void)
|
||||
{
|
||||
usb_disconnect(); // disconnect from USB (if necessary)
|
||||
|
||||
// for more details, see https://stm32f4-discovery.net/2017/04/tutorial-jump-system-memory-software-stm32/
|
||||
|
||||
// deinit RCC (according to STM32CubeF4 source code)
|
||||
RCC_CR |= RCC_CR_HSION; // enable high speed internal clock
|
||||
while (!(RCC_CR & RCC_CR_HSIRDY)); // wait until clock is ready
|
||||
RCC_CR |= (0x10U << RCC_CR_HSITRIM_SHIFT); // set HSITRIM[4:0] bits to the reset value
|
||||
RCC_CFGR = 0;// reset CFGR register
|
||||
while (((RCC_CFGR >> RCC_CFGR_SWS_SHIFT) & RCC_CFGR_SWS_MASK) != RCC_CFGR_SWS_HSI); // wait it till clock switch is ready
|
||||
RCC_CR &= ~(RCC_CR_HSEON | RCC_CR_HSEBYP | RCC_CR_CSSON); // clear HSEON, HSEBYP and CSSON bits
|
||||
while (RCC_CR & RCC_CR_HSERDY); // wait till HSE is disabled
|
||||
RCC_CR &= ~RCC_CR_PLLON; // Clear PLLON bit
|
||||
while (RCC_CR & RCC_CR_PLLRDY); // wait till PLL is disabled
|
||||
//RCC_PLLCFGR = 0x24003010; // reset PLLCFGR register to default value (value for STM32F401)
|
||||
RCC_CIR &= ~(RCC_CIR_LSIRDYIE | RCC_CIR_LSERDYIE | RCC_CIR_HSIRDYIE | RCC_CIR_HSERDYIE | RCC_CIR_PLLRDYIE); // disable all interrupts
|
||||
RCC_CIR |= (RCC_CIR_LSIRDYC | RCC_CIR_LSERDYC | RCC_CIR_HSIRDYC | RCC_CIR_HSERDYC | RCC_CIR_PLLRDYC | RCC_CIR_CSSC); // clear all interrupt flags
|
||||
RCC_CR &= ~RCC_CSR_LSION; // clear LSION bit
|
||||
RCC_CSR |= RCC_CSR_RMVF; // reset all CSR flags
|
||||
|
||||
// switch to system memory
|
||||
RCC_APB2ENR = RCC_APB2ENR_SYSCFGEN; // enable system configure clock (all others are not required)
|
||||
cm_disable_interrupts(); // disable all interrupts
|
||||
SYSCFG_MEMRM = 1; // map system memory to 0x0000 0000 (this bypasses the BOOT0 pin)
|
||||
const uint32_t address = 0x1FFF0000; // system memory address
|
||||
__asm__ volatile ("MSR msp,%0" : :"r"(*(uint32_t*)address)); // set stack pointer to address provided in the beginning of the bootloader (loaded into a register first)
|
||||
(*(void(**)(void))((uint32_t)address + 4))(); // start system memory (by jumping to the reset function which address is stored as second entry of the vector table)
|
||||
// we should not reach this point
|
||||
}
|
||||
|
||||
void dfu_bootloader(void)
|
||||
{
|
||||
usb_disconnect(); // disconnect from USB (if necessary)
|
||||
|
||||
// set DFU magic to specific RAM location
|
||||
__dfu_magic[0] = 'D';
|
||||
__dfu_magic[1] = 'F';
|
||||
__dfu_magic[2] = 'U';
|
||||
__dfu_magic[3] = '!';
|
||||
scb_reset_system(); // reset system (core and peripherals)
|
||||
while (true); // wait for the reset to happen
|
||||
}
|
||||
|
||||
#if defined(BUTTON_PIN)
|
||||
/** interrupt service routine called when button is pressed */
|
||||
void GPIO_EXTI_ISR(BUTTON_PIN)(void)
|
||||
|
9
global.h
9
global.h
@ -495,4 +495,11 @@ char user_input_get(void);
|
||||
void user_input_store(char c);
|
||||
/** setup board peripherals */
|
||||
void board_setup(void);
|
||||
|
||||
/** start embedded bootloader (e.g. system memory)
|
||||
* @warning the USB DFU bootloader does not start
|
||||
* @note after jumping to the bootloader, the USB device is detected, but does not enumerate. I have no idea about the cause. Not configuring the USB did not solve the issue. Re-initializing RCC did not help. Even the STM32CodeMX generated code was not able to have a working USB DFU reboot. Calibrating HSI according to https://community.st.com/s/question/0D50X0000BG0TfH/stm32f4-usb-dfu-bootloader-not-working-reliably did not solve the issue. Maybe there is something with the board. Booting the embedded USB DFU bootloader by hand by pressing the BOOT0 button works.
|
||||
* @note the USART bootloader works fine though
|
||||
*/
|
||||
void system_memory(void);
|
||||
/** start DFU bootloader */
|
||||
void dfu_bootloader(void);
|
||||
|
Loading…
Reference in New Issue
Block a user