diff --git a/.clangd b/.clangd new file mode 100644 index 0000000..b5487ff --- /dev/null +++ b/.clangd @@ -0,0 +1,6 @@ +CompileFlags: + Add: [ + "-isystem", "D:/tool/arm-gnu-toolchain-14.2/arm-none-eabi/include", + "-isystem", "D:/tool/arm-gnu-toolchain-14.2/lib/gcc/arm-none-eabi/14.2.1/include", + "-isystem", "D:/tool/arm-gnu-toolchain-14.2/lib/gcc/arm-none-eabi/14.2.1/include-fixed" + ] \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 02d536e..17a4881 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ set(CMAKE_PROJECT_NAME bootloader) include("cmake/gcc-arm-none-eabi.cmake") # Enable compile command to ease indexing with e.g. clangd -set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE) +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) # Core project settings project(${CMAKE_PROJECT_NAME}) @@ -37,6 +37,11 @@ enable_language(C ASM) # Create an executable object type add_executable(${CMAKE_PROJECT_NAME}) +add_custom_command(TARGET bootloader POST_BUILD + COMMAND ${CMAKE_OBJCOPY} -O ihex $ $/bootloader.hex + COMMENT "Generate HEX file from ELF" +) + # Add STM32CubeMX generated sources add_subdirectory(cmake/stm32cubemx) diff --git a/Core/Src/stm32l4xx_it.c b/Core/Src/stm32l4xx_it.c index e54cf6c..5dd8904 100644 --- a/Core/Src/stm32l4xx_it.c +++ b/Core/Src/stm32l4xx_it.c @@ -23,6 +23,9 @@ /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "appTask.h" +#include "stdio.h" +#include +#include "bootapp.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ @@ -71,7 +74,7 @@ extern UART_HandleTypeDef huart1; void NMI_Handler(void) { /* USER CODE BEGIN NonMaskableInt_IRQn 0 */ - + printf("NMI_Handler\r\n"); /* USER CODE END NonMaskableInt_IRQn 0 */ /* USER CODE BEGIN NonMaskableInt_IRQn 1 */ while (1) @@ -86,7 +89,8 @@ void NMI_Handler(void) void HardFault_Handler(void) { /* USER CODE BEGIN HardFault_IRQn 0 */ - + printf("HardFault_Handler\r\n"); + bootApp_WriteHardFaultData(0x80000001); /* USER CODE END HardFault_IRQn 0 */ while (1) { @@ -101,7 +105,8 @@ void HardFault_Handler(void) void MemManage_Handler(void) { /* USER CODE BEGIN MemoryManagement_IRQn 0 */ - + printf("MemManage_Handler\r\n"); + bootApp_WriteHardFaultData(0x80000002); /* USER CODE END MemoryManagement_IRQn 0 */ while (1) { @@ -116,7 +121,8 @@ void MemManage_Handler(void) void BusFault_Handler(void) { /* USER CODE BEGIN BusFault_IRQn 0 */ - + printf("BusFault_Handler\r\n"); + bootApp_WriteHardFaultData(0x80000003); /* USER CODE END BusFault_IRQn 0 */ while (1) { @@ -131,7 +137,8 @@ void BusFault_Handler(void) void UsageFault_Handler(void) { /* USER CODE BEGIN UsageFault_IRQn 0 */ - + printf("UsageFault_Handler\r\n"); + bootApp_WriteHardFaultData(0x80000004); /* USER CODE END UsageFault_IRQn 0 */ while (1) { diff --git a/Drivers/Norflash/norflash.c b/Drivers/Norflash/norflash.c index 367dcbb..adf13ae 100644 --- a/Drivers/Norflash/norflash.c +++ b/Drivers/Norflash/norflash.c @@ -1,6 +1,7 @@ #include "norflash.h" #include "qspi.h" #include "stdio.h" +#include u16 NORFLASH_TYPE = W25Q64; // 默认是W25Q128 u8 NORFLASH_QPI_MODE = 0; // QSPI模式标志:0,SPI模式;1,QPI模式. @@ -345,6 +346,16 @@ void NORFLASH_Erase_Sector(u32 Dst_Addr) QSPI_Send_CMD(W25X_SectorErase, Dst_Addr, (0 << 6) | (2 << 4) | (3 << 2) | (3 << 0), 0); // QPI,写扇区擦除指令,地址为0,无数据_24位地址_4线传输地址_4线传输指令,无空周期,0个字节数据 NORFLASH_Wait_Busy(); // 等待擦除完成 } +void NORFLASH_Erase_Block(u32 blockid) +{ + uint32_t Dst_Addr = 0; + // printf("fe:%x\r\n",Dst_Addr); //监视falsh擦除情况,测试用 + Dst_Addr = 0x1000UL<CCR; - u32 addrreg=QUADSPI->AR; + //u32 addrreg=QUADSPI->AR; u8 status; vu32 *data_reg=&QUADSPI->DR; QUADSPI->DLR=datalen-1; //设置数据传输长度 diff --git a/STM32L431XX_FLASH.ld b/STM32L431XX_FLASH.ld index 2a4bfb1..1216e4c 100644 --- a/STM32L431XX_FLASH.ld +++ b/STM32L431XX_FLASH.ld @@ -62,7 +62,9 @@ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 48K RAM2 (xrw) : ORIGIN = 0x10000000, LENGTH = 16K +RAM_BOOT (xrw) : ORIGIN = 0x10003FC0, LENGTH = 0x40 FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 256K + } /* Define output sections */ @@ -193,6 +195,12 @@ SECTIONS . = ALIGN(8); } >RAM + .boot_flags (NOLOAD) : + { + . = ALIGN(4); + KEEP(*(.boot_flags)) + . = ALIGN(4); + } >RAM_BOOT /* Remove information from the standard libraries */ diff --git a/asw/appTask.c b/asw/appTask.c index 3c31ff9..de46d7c 100644 --- a/asw/appTask.c +++ b/asw/appTask.c @@ -36,6 +36,7 @@ #include "stdio.h" #include "norflash.h" #include "uartapp.h" +#include "bootapp.h" /*--------------------------------------------------------------------------- - D E F I N E S / M A C R O S ----------------------------------------------------------------------------*/ @@ -88,6 +89,8 @@ void apptask_init(void) //norflash_test(); printf("apptask_init finashed\n"); //toggle_led(); + + bootApp_init();//bootapp_init } extern UART_HandleTypeDef huart1; @@ -101,7 +104,8 @@ void apptask_maintask(void) //1mstsk uartapp_maintask(); - + bootApp_mainTask(); + if (timebase_counter >= 1000000) { timebase_counter = 0; @@ -113,7 +117,7 @@ void apptask_maintask(void) //toggle_led(); } - if (timebase_counter %1000 == 0) + if (timebase_counter %500 == 0) { //HAL_UART_Transmit(&huart1,uart_tx_buf,8,100); //printf("test\n"); diff --git a/asw/bootapp.c b/asw/bootapp.c index 37575b4..ea0c637 100644 --- a/asw/bootapp.c +++ b/asw/bootapp.c @@ -3,7 +3,15 @@ ******************************************************************************/ #include "bootapp.h" #include "norflash.h" +#include "stm32l4xx_hal_crc.h" +#include "stm32l4xx_hal_dma.h" +#include "stm32l4xx_hal_gpio.h" +#include "stm32l4xx_hal_uart.h" #include "uartapp.h" +#include "stdio.h" +#include "hardware.h" +#include +#include /******************************************************************************* * the defines ******************************************************************************/ @@ -20,11 +28,44 @@ typedef struct uint32_t AppCRC; uint32_t reverse; } APPCRC_type; +typedef struct +{ + uint32_t startflag; + uint32_t sBootloader_Req; + uint32_t reboot_times; + uint32_t hardfault_data; + uint32_t reverse[12]; +}NO_INIT_DATA_Type; + +typedef struct +{ + uint8_t reqFlag; + uint8_t eraseState; + uint8_t eraseLen; + uint8_t eraseStep; + +}EraseReq_Type; + +typedef enum { + BOOT_INIT, + BOOT_WAIT, + BOOT_APP_CHECK, + BOOT_APP_JUMP, + BOOT_STOP, + BOOT_ERASE, + BOOT_STATE_NUM, +}BOOT_STATE_Type; /******************************************************************************* * the globals ******************************************************************************/ - + volatile NO_INIT_DATA_Type noInitData __attribute__((section(".boot_flags"))); + static uint8_t bootflag = 0; + static BOOT_STATE_Type bootState = BOOT_INIT; + extern UART_HandleTypeDef huart1; + extern DMA_HandleTypeDef hdma_usart1_rx; + extern CRC_HandleTypeDef hcrc; + static EraseReq_Type eraseReq; /******************************************************************************* * the const ******************************************************************************/ @@ -32,19 +73,27 @@ typedef struct /******************************************************************************* * the functions ******************************************************************************/ +static void bootApp_EraseTask(void); -void BspQspiBoot_JumpToApp(void) + + +static void BspQspiBoot_JumpToApp(void) { uint32_t i = 0; void (*AppJump)(void); /* 声明一个函数指针 */ - __IO uint32_t AppAddr = 0x90000000; /* APP 地址 */ + __IO uint32_t AppAddr = EX_FLASH_APP_ADDR; /* APP 地址 */ /* 关闭全局中断 */ DISABLE_INT(); /* 设置所有时钟到默认状态,使用HSI时钟 */ HAL_RCC_DeInit(); - + HAL_UART_DeInit(&huart1); + HAL_DMA_DeInit(&hdma_usart1_rx); + HAL_CRC_DeInit(&hcrc); + // __HAL_RCC_SPI1_FORCE_RESET(); + // __HAL_RCC_SPI1_RELEASE_RESET(); + // HAL_GPIO_DeInit(GPIOB, GPIO_PIN_3|GPIO_PIN_5); /* 关闭滴答定时器,复位到默认值 */ SysTick->CTRL = 0; SysTick->LOAD = 0; @@ -82,7 +131,7 @@ void WriteCRCInfo(uint32_t len, uint32_t crc) appcrc.len = len; appcrc.AppCRC = crc; appcrc.reverse = 0xAAAAAAAA; - NORFLASH_Write_NoCheck((uint8_t*)&appcrc, EX_FLASH_CRC_ADDR, 0x10); + NORFLASH_Write_NoCheck((uint8_t*)&appcrc, W26Q64_CRC_ADDR, 0x10); } uint8_t ReadCRCInfo(uint32_t* len, uint32_t* crc) @@ -105,7 +154,7 @@ uint8_t Boot_checkApp(void) uint32_t len, crc; if (ReadCRCInfo(&len, &crc) == 1) { - if (crc_calc(EX_FLASH_START_ADDR, len) == crc) + if (crc_calc(EX_FLASH_APPINFO_ADDR, len) == crc) { return 1; } @@ -115,4 +164,160 @@ uint8_t Boot_checkApp(void) } } return 0; -} \ No newline at end of file +} + +void bootApp_init(void) +{ + if (noInitData.startflag != 0xAA5555AA) { + printf("init bootflag\n"); + noInitData.startflag = 0xAA5555AA; + noInitData.sBootloader_Req = 0; + noInitData.reboot_times = 0; + noInitData.hardfault_data = 0; + } + else { + noInitData.reboot_times++; + printf("reboot_times = %d\n", noInitData.reboot_times); + if (noInitData.sBootloader_Req == 0x778899AA) { + noInitData.sBootloader_Req = 0; + bootflag = 1; + } + else {//if(noInitData.sBootloader_Req != 0) + printf("bootreq = %lx\n",noInitData.sBootloader_Req); + } + if (noInitData.hardfault_data != 0) { + printf("hardfault_data = %lx\n", noInitData.hardfault_data); + } + } +} + + + +void bootApp_mainTask(void)//1ms +{ + static uint16_t timeout_count = 0,bootkey_delay=0; + switch (bootState) { + case BOOT_INIT: { + bootState = BOOT_WAIT; + + break; + } + case BOOT_WAIT: { + if (bootflag == 1) { + bootState = BOOT_STOP; + printf("get_bootreq,stop in boot\n"); + break; + } + if (timeout_count++ > 100) {//100ms + timeout_count = 0; + bootState = BOOT_APP_CHECK; + NORFLASH_MemroyMapMode(); + + break; + } + if (get_BOOTKEY() == 1) { + bootkey_delay++; + if (bootkey_delay > 5) {//5ms + bootkey_delay = 0; + bootState = BOOT_STOP; + printf("get_BOOTKEY,stop in boot\n"); + break; + } + } + else { + bootkey_delay = 0; + } + break; + } + case BOOT_APP_CHECK: { + if (Boot_checkApp() == 1) { + bootState = BOOT_APP_JUMP; + break; + } + else { + printf("app not found\n"); + NORFLASH_QuitMemroyMapMode(); + bootState = BOOT_STOP; + break; + } + } + case BOOT_APP_JUMP: { + printf("jump to app\n"); + BspQspiBoot_JumpToApp(); + break; + } + case BOOT_STOP: { + timeout_count++; + if (timeout_count >= 10) {//1000ms + timeout_count = 0; + bootApp_EraseTask(); + + } + break; + } + default: { + break; + } + } +} + +void bootApp_WriteHardFaultData(uint32_t data) +{ + noInitData.hardfault_data = data; +} + +void bootApp_ReqErase(uint8_t block_len) +{ + if (block_len > 128) { + printf("block_len error\n"); + return; + } + if (eraseReq.eraseState != 0) { + printf("erase is running\n"); + return; + } + if (block_len == 0) { + NORFLASH_Erase_Chip(); + } + else { + NORFLASH_Erase_Block(0); + } + eraseReq.reqFlag = 1; + eraseReq.eraseState = 1; + eraseReq.eraseLen = block_len; + eraseReq.eraseStep = 0; +} + +static void bootApp_EraseTask(void)//10ms task +{ + static uint32_t erase_count = 0; + if (eraseReq.reqFlag == 1) { + if (NORFLASH_Check_Busy() == 0) { + eraseReq.eraseStep++; + if (eraseReq.eraseStep > eraseReq.eraseLen) { + eraseReq.reqFlag = 0; + eraseReq.eraseState = 0; + eraseReq.eraseLen = 0; + printf("erase finashed %d ms\n",erase_count*10); + } + else { + NORFLASH_Erase_Block(eraseReq.eraseStep); + } + } + erase_count++; + if (erase_count % 400 == 0) { + printf("wait erase\n"); + } + if (erase_count > 3000) { + erase_count = 0; + eraseReq.reqFlag = 0; + eraseReq.eraseState = 0; + eraseReq.eraseLen = 0; + printf("erase timeout\n"); + } + } + else { + erase_count = 0; + } +} + diff --git a/asw/bootapp.h b/asw/bootapp.h index fb34232..3dc39b3 100644 --- a/asw/bootapp.h +++ b/asw/bootapp.h @@ -30,7 +30,11 @@ ******************************************************************************/ void WriteCRCInfo(uint32_t len, uint32_t crc); - +uint8_t ReadCRCInfo(uint32_t* len, uint32_t* crc); +void bootApp_init(void); +void bootApp_mainTask(void); +void bootApp_WriteHardFaultData(uint32_t data); +void bootApp_ReqErase(uint8_t block_len); diff --git a/asw/hardware.c b/asw/hardware.c index 7cd8bb1..f8b662c 100644 --- a/asw/hardware.c +++ b/asw/hardware.c @@ -65,3 +65,7 @@ void toggle_led(void) HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin); } +uint8_t get_BOOTKEY(void) +{ + return HAL_GPIO_ReadPin(BOOTKEY_GPIO_Port,BOOTKEY_Pin); +} diff --git a/asw/hardware.h b/asw/hardware.h index f405ae0..333eb28 100644 --- a/asw/hardware.h +++ b/asw/hardware.h @@ -57,7 +57,7 @@ - F U N C T I O N P R O T O T Y P E ----------------------------------------------------------------------------*/ void toggle_led(void); - +uint8_t get_BOOTKEY(void); #endif /*DEFINES_NAME*/ diff --git a/asw/pindef.h b/asw/pindef.h index 3de0e24..0e68c46 100644 --- a/asw/pindef.h +++ b/asw/pindef.h @@ -46,6 +46,8 @@ #define LCD_BL_GPIO_Port GPIOC #define LCD_DX_Pin GPIO_PIN_7 #define LCD_DX_GPIO_Port GPIOC +#define BOOTKEY_GPIO_Port GPIOH +#define BOOTKEY_Pin GPIO_PIN_3 /*--------------------------------------------------------------------------- - T Y P E D E F I N I T I O N S diff --git a/asw/uartapp.c b/asw/uartapp.c index c32aabf..9adb278 100644 --- a/asw/uartapp.c +++ b/asw/uartapp.c @@ -35,6 +35,8 @@ #include "stdio.h" #include "norflash.h" #include "hardware.h" +#include "bootapp.h" +#include /*--------------------------------------------------------------------------- - D E F I N E S / M A C R O S ----------------------------------------------------------------------------*/ @@ -50,7 +52,6 @@ typedef enum UARTAPP_RXCMD, UARTAPP_RXSPIDATA, UARTAPP_STOPRX, - UARTAPP_ERASING, UARTAPP_CRC, UARTAPP_STATENUM, }uartapp_mainstate_type; @@ -59,20 +60,22 @@ typedef enum CMD31_Idle, CMD31_Erase, CMD31_ReqDownload, - CMD31_CRC, + CMD31_WriteCRC, CMD31_MapMode, + CMD31_CRC, + CMD31_QuitMapMode, CMD31_MapModeTest, - CMD31_WriteCRC, + CMD31_NUM, }CMD31_type; /*--------------------------------------------------------------------------- - S T A T I C V A R I A B L E S ----------------------------------------------------------------------------*/ static uartapp_mainstate_type task_state; -static uint8_t rxCpltFlag = 0,idleReqFlag=0,downloadReqFlag=0,eraseReqFlag = 0; +static uint8_t rxCpltFlag = 0,idleReqFlag=0,downloadReqFlag=0; -static uint8_t rxbuf[512],txbuf[10]; +static uint8_t rxbuf[512]; static uint16_t rxsize = 0,timeout_count=0; static uint32_t writeaddr = 0; @@ -96,13 +99,15 @@ static void uartapp_cmd31(uint8_t *data,uint16_t len); //重定向,用于printf -#if 0 +#if __GNUC__ int _write(int fd, char *ptr, int len) { + (void)fd; //toggle_led(); // 这里测试是否走通 HAL_UART_Transmit(&huart1, (uint8_t *)ptr, len, HAL_MAX_DELAY); return len; } + #else int __write(int handle, char *buf, int size) { if (handle == 1 || handle == 2) { // stdout 或 stderr @@ -188,13 +193,6 @@ void uartapp_maintask(void) HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rxbuf,512); timeout_count = 0; } - else if (eraseReqFlag == 1) - { - eraseReqFlag = 0; - task_state = UARTAPP_ERASING; - timeout_count = 0; - } - else { maintask_gotoidle(); @@ -211,7 +209,7 @@ void uartapp_maintask(void) if (rxsize > 0) { NORFLASH_Write_NoCheck(rxbuf,writeaddr,rxsize); - printf("write 0x%06X,0x%03X,delay %d\n",writeaddr,rxsize,delaytest); + printf("write 0x%06X,0x%03X,delay %lu\n",writeaddr,rxsize,delaytest); } HAL_UARTEx_ReceiveToIdle_DMA(&huart1,rxbuf,512); rxCpltFlag = 0; @@ -232,30 +230,6 @@ void uartapp_maintask(void) } break; - case UARTAPP_ERASING: - timeout_count++; - if (timeout_count > 30000) - { - maintask_gotoidle(); - printf("erase err timeout\n"); - break; - } - if (timeout_count % 100 == 0) - { - if (NORFLASH_Check_Busy() == 0) - { - maintask_gotoidle(); - printf("erase success\n"); - break; - } - } - - if (timeout_count % 4000 == 0) - { - printf("wait erase\n"); - } - - break; default: @@ -304,6 +278,9 @@ static void uartapp_cmdpro(uint8_t *data,uint16_t len) static void uartapp_cmd31(uint8_t *data,uint16_t len) { + if (len < 1) { + return; + } CMD31_type subid = data[0]; switch (subid) { @@ -314,24 +291,42 @@ static void uartapp_cmd31(uint8_t *data,uint16_t len) if (mapModeFlag == 0) { - NORFLASH_Erase_Chip(); - eraseReqFlag = 1; - printf("erasing Chip\n"); + if (len >= 2) { + bootApp_ReqErase(data[1]); + } + else { + bootApp_ReqErase(0); + } + printf("start erase\n"); } break; case CMD31_ReqDownload: - writeaddr = 0; + writeaddr = 0x100; downloadReqFlag = 1; break; case CMD31_CRC: - delaytest = 0; - uint32_t starttime = SysTick->VAL; - uint32_t crcvalue = crc_calc(EX_FLASH_START_ADDR,0x32C50); - uint32_t endtime = (SysTick->VAL); - uint32_t usedtime = delaytest*(SysTick->LOAD + 1) + endtime - starttime; - printf("crc= %08X,time=%d\n",crcvalue,usedtime); - printf("starttime= %d,endtime=%d,delaytest=%d\n",starttime,endtime,delaytest); + uint32_t localcrc = 0,locallen = 0; + if (ReadCRCInfo(&locallen,&localcrc)) { + delaytest = 0; + uint32_t starttime = SysTick->VAL; + uint32_t crcvalue = crc_calc(EX_FLASH_APPINFO_ADDR,locallen); + uint32_t endtime = (SysTick->VAL); + uint32_t usedtime = delaytest*(SysTick->LOAD + 1) + endtime - starttime; + printf("crc= %08lX,time=%ld\n",crcvalue,usedtime); + printf("starttime= %ld,endtime=%ld,delaytest=%ld\n",starttime,endtime,delaytest); + if (crcvalue == localcrc) { + printf("crc ok\n"); + } + else { + printf("crc err\n"); + } + } + else { + printf("no crc info\n"); + } + + //crcReqFlag = 1; break; case CMD31_MapMode: @@ -345,7 +340,15 @@ static void uartapp_cmd31(uint8_t *data,uint16_t len) norflash_test(); break; case CMD31_WriteCRC: - + if (len == 9) { + uint32_t datalen = (data[1]<<24) | (data[2]<<16) | (data[3]<<8) | data[4]; + uint32_t crc = (data[5]<<24) | (data[6]<<16) | (data[7]<<8) | data[8]; + WriteCRCInfo(datalen, crc); + printf("write crc ok\n"); + } + else { + printf("err len\n"); + } break; default: break; diff --git a/cmake/gcc-arm-none-eabi.cmake b/cmake/gcc-arm-none-eabi.cmake index ade2d8f..e03afcd 100644 --- a/cmake/gcc-arm-none-eabi.cmake +++ b/cmake/gcc-arm-none-eabi.cmake @@ -26,7 +26,7 @@ set(TARGET_FLAGS "-mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard ") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${TARGET_FLAGS}") set(CMAKE_ASM_FLAGS "${CMAKE_C_FLAGS} -x assembler-with-cpp -MMD -MP") -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -fdata-sections -ffunction-sections") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -Wpedantic -fdata-sections -ffunction-sections -Wno-format") set(CMAKE_C_FLAGS_DEBUG "-O0 -g3") set(CMAKE_C_FLAGS_RELEASE "-Os -g0") diff --git a/烧录CMAKE.bat b/烧录CMAKE.bat new file mode 100644 index 0000000..0d8a3f6 --- /dev/null +++ b/烧录CMAKE.bat @@ -0,0 +1,3 @@ +::pyocd flash --erase chip --target CVM0144 -f 10m .\Debug_FLASH\Exe\cva_bootloader_m0146.hex --pack=.\SDK\CVA.M01.1.7.1.pack -u 000000800671ff515256656767161348a5a5a5a597969908 +pyocd flash --erase chip --target stm32l431rctx -f 10m .\build\debug\bootloader.hex -W +pause \ No newline at end of file