bootloader相关实现
main.cwhile(1){extern void start_app(unsigned int vector);start_app(0x08040000); //启动成功后不会往下走,而是去运行应用程序.....}
jump.SAREA |.text|, CODE, READONLY; Reset handler routinestart_app PROCEXPORT start_app ;set vector, 将函数传来的数(默认会保存在R0中)放入0xE000ED08(VTOR寄存器的地址)处LDR R1, =0xE000ED08STR R0, [R1];set SP, read val of address(0x08040000), set to SPLDR R1, [R0]MOV SP, R1;read val of address(0x08040004) ,jump LDR R1, [R0, #4]BX R1ENDPEND
注意在app里注释掉SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; 这里设置的是异常向量表的默认地址(0x08000000),不注释掉的话默认会使用bootloader的异常向量表。
使用bootloader下载功能需要定义下位机和上位机间的传输协议。
这里我们自定义了一个协议:
1.下位机发送 0x01;
2.上位机发送固件信息(包括a.长度,b.加载地址(即要烧写到flash的什么地方),c.CRC, d.文件名, e.版本号)
3.下位机判断版本信息是否比当前更高,更高的话发送0x02给上位机,请求升级
4.上位机发送bin文件给bootloader, bootloader进行CRC校验
5.bootloader烧写固件,并发送进度
改写h5程序实现下载功能
只保留usbx运行的默认任务,关闭其他任务宏。
bootloader.c#define CFG_OFFSET 0x081FE000 //flash中此地址开始保存配置信息(即FirmwareInfo结构体)#define UPDATE_TIMEOUT 1000static struct UART_Device* g_pUpdateUART;static void SoftReset(void) {__set_FAULTMASK(1);//关闭所有中断HAL_NVIC_SystemReset(); }static void start_app_c(){/* 触发软件复位 */。SoftReset();}static uint32_t BE32toLE32(uint8_t *buf){return (uint32_t)buf[0] << 24 | (uint32_t)buf[1] << 16| (uint32_t)buf[2] << 8 | (uint32_t)buf[3] << 0; }static int GetLocalFirmwareInfo(PFirmwareInfo ptFirmwareInfo){PFirmwareInfo ptFlashInfo = (PFirmwareInfo)CFG_OFFSET;if(ptFlashInfo->file_len = 0xFFFFFFFF){return -1;}*ptFirmwareInfo = *ptFlashInfo;return 0; }static int GetServerFirmwareInfo(PFirmwareInfo ptFirmwareInfo){uint8_t data = '1';uint8_t buf[sizeof(FirmwareInfo)];if(0!=g_pUpdateUART->Send(g_pUpdateUART, &data, 1, UPDATE_TIMEOUT))return -1;/* wait for response */while(1){if(0!=g_pUpdateUART->RecvByte(g_pUpdateUART, &data, UPDATE_TIMEOUT))return -1;if(data != 0x5a){buf[0] = data;break;}}/* get firmware info */ for(int i=1; i<sizeof(FirmwareInfo); ++i){if(0!=g_pUpdateUART->RecvByte(g_pUpdateUART, &buf[i], UPDATE_TIMEOUT))return -1;}ptFirmwareInfo->version = BE32toLE32(&buf[0]);ptFirmwareInfo->version = BE32toLE32(&buf[4]);ptFirmwareInfo->version = BE32toLE32(&buf[8]);ptFirmwareInfo->version = BE32toLE32(&buf[12]);strcpy((char*)ptFirmwareInfo->filename, (char*)&buf[16], 16);return 0; }static int GetServerFirmware(uint8_t* buf, uint32_t len){uint8_t data = '2';/* send 0x02 cmd to PC */if(0!=g_pUpdateUART->Send(g_pUpdateUART, &data, 1, UPDATE_TIMEOUT))return -1;/* get firmware */ for(int i=1; i<len; ++i){if(0!=g_pUpdateUART->RecvByte(g_pUpdateUART, &buf[i], UPDATE_TIMEOUT))return -1;}return 0; }static int GetCRC32(uint8_t* buf, uint32_t len){...... }void BootLoaderTask(void* pvParameter){struct UART_Device *pUABUART = GetUARTDevice("usb");FirmwareInfo tLocalInfo;FirmwareInfo tServerInfo;int err;int need_update = 0;uint8_t* firmware_buf;vTaskDelay(10000); //wait for pc readypUABUART->Init(pUABUART, 115200, 'N', 8, 1);g_pUpdateUART = pUABUART;while(1){/* read cfg info, to detect app's version */err = GetLocalFirmwareInfo(&tLocalInfo);if(err){/* update */need_update = 1;}else{pUSBUART->Send(pUSBUART, "GetLocalFirmwareInfo Failed\r\n", strlen("GetLocalFirmwareInfo Failed\r\n"), UPDATE_TIMEOUT);}err = GetServerFirmwareInfo(&tServerInfo);if(!err){if(tServerInfo.version > tLocalInfo.version){/* update */need_update = 1;}}else{need_update = 0;pUSBUART->Send(pUSBUART, "GetServerFirmwareInfo Failed\r\n", strlen("GetServerFirmwareInfo Failed\r\n"), UPDATE_TIMEOUT);}if(need_update){firmware_buf = pvPortMalloc(tServerInfo.file_len);if(!firmware_buf){/* error */pUSBUART->Send(pUSBUART, "Malloc Failed\r\n", strlen("Malloc Failed\r\n"), UPDATE_TIMEOUT);}err = GetServerFirmware(firmware_buf, tServerInfo.file_len);if(!err){/* calc CRC */uint32_t crc = GetCRC32(fireware_buf, tServerInfo.file_len);if(crc == tServerInfo.crc32){/* ok *//* burn */打印成功信息pUSBUART->Send(pUSBUART, "Download OK\r\n", 13, UPDATE_TIMEOUT);WriteFirmware(fireware_buf,tServerInfo.file_len);WriteFirmwareInfo(&tServerInfo);/* start app */start_app_c();}else{打印错误信息}}else{pUSBUART->Send(pUSBUART, GetServerFirmware Failed\r\n", strlen("GetServerFirmware Failed\r\n"), UPDATE_TIMEOUT)}}else{/* start app */}}}
bootloader.htypedef struct FirmwareInfo{uint32_t version;uint32_t file_len;uint32_t crc32;uint8_t file_name[16];}FirmwareInfo, *pFirmwareInfo;
注意将usb队列大小设置为10K
烧写应用程序和配置信息:
1. 擦除
2. 烧写
bootloader.cstatic int WriteFirmware(uint8_t* firmware_buf, uint32_t len, uint32_t flash_addr){FLASH_EraseInitTypeDef tEraseInit;uint32_t SectorError;uint32_t sectors = (len + (SECTOR_SIZE - 1)) / SECTOR_SIZE;uint32_t flash_offset = flash_addr - 0x08000000;uint32_t bank_sectors;uint32_t erased_sectors = 0;//解锁flashHAL_FLASH_Unlock();/* erase bank1 */if (flash_offset < 0x100000){tEraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;tEraseInit.Banks = FLASH_BANK_1;tEraseInit.Sector = flash_offset / SECTOR_SIZE;bank_sectors = (0x100000 - flash_offset) / SECTOR_SIZE;if (sectors <= bank_sectors)erased_sectors = sectors;elseerased_sectors = bank_sectors;tEraseInit.NbSectors = erased_sectors;if (HAL_OK != HAL_FLASHEx_Erase(&tEraseInit,&SectorError){g_pUpdateUART->Send(g_pUpdateUART, (uint8_t *)"HAL_FLASHEx_Erase Failed\r\n", strlen("HAL_FLASHEx_Erase Failed\r\n"), UPDATE_TIMEOUT);HAL_FLASH_Lock();return -1;}flash_offset += erased_sectors*SECTOR_SIZE;}sectors -= erased_sectors;flash_offset -= 0x100000;/* erase bank2 */if (sectors){tEraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;tEraseInit.Banks = FLASH_BANK_2;tEraseInit.Sector = flash_offset / SECTOR_SIZE;bank_sectors = (0x100000 - flash_offset) / SECTOR_SIZE;if (sectors <= bank_sectors)erased_sectors = sectors;elseerased_sectors = bank_sectors;tEraseInit.NbSectors = erased_sectors;if (HAL_OK != HAL_FLASHEx_Erase(&tEraseInit,&SectorError){g_pUpdateUART->Send(g_pUpdateUART, (uint8_t *)"HAL_FLASHEx_Erase Failed\r\n",strlen("HAL_FLASHEx_Erase Failed\r\n"), UPDATE_TIMEOUT);HAL_FLASH_Lock();return -1;}}/* program 烧写*/len = (len + 15) & ~15;for (int i = 0; i < len; i+=16){if (HAL_OK != HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, flash_addr,(uint32_t)firmware_buf)){g_pUpdateUART->Send(g_pUpdateUART, (uint8_t *)"HAL_FLASH_Program Failed\r\n", strlen("HAL_FLASH_Program Failed\r\n"), UPDATE_TIMEOUT);HAL_FLASH_Lock();return -1;}flash_addr += 16;firmware_buf += 16;}HAL_FLASH_Lock();return 0;}//擦除最后一个扇区并烧写固件信息 static int WriteFirmwareInfo(PFirmwareInfo ptFirmwareInfo){FLASH_EraseInitTypeDef tEraseInit;uint32_t SectorError;uint32_t flash_addr = CFG_OFFSET;uint8_t *src_buf = ptFirmwareInfo;HAL_FLASH_Unlock();/* erase bank2 */tEraseInit.TypeErase = FLASH_TYPEERASE_SECTORS;tEraseInit.Banks = FLASH_BANK_2;tEraseInit.Sector = (flash_addr – 0x08000000 – 0x100000) / SECTOR_SIZE;tEraseInit.NbSectors = 1;if (HAL_OK != HAL_FLASHEx_Erase(&tEraseInit, &SectorError)){g_pUpdateUART->Send(g_pUpdateUART, (uint8_t *)"HAL_FLASHEx_Erase Failed\r\n",strlen("HAL_FLASHEx_Erase Failed\r\n"), UPDATE_TIMEOUT);HAL_FLASH_Lock();return -1;}/* program */for (int i = 0; i < sizeof(FirmwareInfo); i+=16){if (HAL_OK != HAL_FLASH_Program(FLASH_TYPEPROGRAM_QUADWORD, flash_addr, (uint32_t)src_buf)) {g_pUpdateUART->Send(g_pUpdateUART, (uint8_t *)"HAL_FLASH_Program F ailed\r\n",strlen("HAL_FLASH_Program Failed\r\n"), UPDATE_TIMEOUT);HAL_FLASH_Lock();return -1;}flash_addr += 16;src_buf += 16;}HAL_FLASH_Lock();return 0; }
上电复位流程为: