diff --git a/global.c b/global.c index 96d8c4d..9969037 100644 --- a/global.c +++ b/global.c @@ -16,6 +16,8 @@ #include // real-time control clock library #include // general purpose input output library #include // external interrupt defines +#include // system definitions +#include // 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) diff --git a/global.h b/global.h index 5e9171e..9a6d4d5 100644 --- a/global.h +++ b/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);