【举报再看养成习惯,噢 不对,点赞再看 养成习惯。感谢支持】
实验环境:STM32F103VET6 512KB Flash 64KB RAM
CubeMX生成代码+MDK编译
简介:
手中有给设备升级的工作,希望采用USB的方式。但是板卡没有外挂Flash,也不希望占用大量的RAM来接收数据,干脆直接使用Flash模拟成大容量存储设备。
这样一来直接固件就直接写入Flash中了。然后找到固件的位置,就可以升级了。
如下图所示:将APP2所用的区域作为 大容量存储设备使用的内存,其中就包括升级需要的bin文件。只需要找到bin文件的位置即可完成接下来的升级。

=============分割线=============
CubeMX配置: 配置USB 为大容量存储设备


配置USB设备代码:usbd_storage_if.c文件
BLOCKNUM=400 STORAGE_BLK_SIZ=0x200
即大容量存储设备容量=400*0x200=200KB
- /* USER CODE BEGIN PRIVATE_TYPES */
- #define BLOCKNUM 400
- /* USER CODE END PRIVATE_TYPES */
-
- #define STORAGE_LUN_NBR 1
- #define STORAGE_BLK_NBR BLOCKNUM
- #define STORAGE_BLK_SIZ 0x200
STORAGE_BLK_SIZ=0x200 是指作为USB-Device时 一个扇区的大小。
其他函数配置:
- #define APP1_START_ADDR 0x08019000
-
- #define MASS_STORAGE_CLASS_START_ADDR 0x0804B000
-
- /* Private functions ---------------------------------------------------------*/
- /**
- * @brief Initializes over USB FS IP
- * @param lun:
- * @retval USBD_OK if all operations are OK else USBD_FAIL
- */
- int8_t STORAGE_Init_FS(uint8_t lun)
- {
- /* USER CODE BEGIN 2 */
- return (USBD_OK);
- /* USER CODE END 2 */
- }
-
- /**
- * @brief .
- * @param lun: .
- * @param block_num: .
- * @param block_size: .
- * @retval USBD_OK if all operations are OK else USBD_FAIL
- */
- int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
- {
- /* USER CODE BEGIN 3 */
- *block_num = STORAGE_BLK_NBR;
- *block_size = STORAGE_BLK_SIZ;
- return (USBD_OK);
- /* USER CODE END 3 */
- }
-
- /**
- * @brief .
- * @param lun: .
- * @retval USBD_OK if all operations are OK else USBD_FAIL
- */
- int8_t STORAGE_IsReady_FS(uint8_t lun)
- {
- /* USER CODE BEGIN 4 */
-
- // return (USBD_FAIL);
- return (USBD_OK);
- /* USER CODE END 4 */
- }
-
- /**
- * @brief .
- * @param lun: .
- * @retval USBD_OK if all operations are OK else USBD_FAIL
- */
- int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
- {
- /* USER CODE BEGIN 5 */
- return (USBD_OK);
- /* USER CODE END 5 */
- }
-
- /**
- * @brief .
- * @param lun: .
- * @retval USBD_OK if all operations are OK else USBD_FAIL
- */
- int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
- {
- /* USER CODE BEGIN 6 */
- uint32_t satrtAddr = MASS_STORAGE_CLASS_START_ADDR + blk_addr*STORAGE_BLK_SIZ;
- for(uint16_t i=0;i
- *(buf+i) = STMFLASH_ReadByte(satrtAddr+i);
- }
- return (USBD_OK);
- /* USER CODE END 6 */
- }
-
- /**
- * @brief .
- * @param lun: .
- * @retval USBD_OK if all operations are OK else USBD_FAIL
- */
-
- unsigned char tempBuff[2048] = {0};
-
- int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
- {
- /* USER CODE BEGIN 7 */
- uint32_t * p = (uint32_t *)tempBuff;
- FLASH_EraseInitTypeDef FlashEraseInit;
- uint32_t PageError = 0;
- // 要写入的地址
- uint32_t falshAddr = MASS_STORAGE_CLASS_START_ADDR + blk_addr*STORAGE_BLK_SIZ;
- // 要写入的地址所在的扇区的起始地址
- uint32_t sectorStartAddr = Get103VET6SectorAddr(falshAddr);
- uint32_t tempSectorStartAddr = sectorStartAddr;
- // 判断要写的内容在Flash哪一个扇区 将其中的内容全部取出到Buff中
- memcpy(tempBuff, (uint32_t *)sectorStartAddr, 2048);
-
- // 将数据写入Buff
- memcpy(tempBuff+(blk_addr%4)*512, buf, blk_len*512);
- SEGGER_RTT_printf(0,"addr %x start addr %x \r\n",falshAddr,sectorStartAddr);
- HAL_FLASH_Unlock(); //解锁
- // // 擦除当前Flash扇区内容
- FlashEraseInit.TypeErase = FLASH_TYPEERASE_PAGES; //擦除类型,页擦除
- FlashEraseInit.Banks = FLASH_BANK_1;
- FlashEraseInit.PageAddress = falshAddr;
- FlashEraseInit.NbPages = 1; //一次只擦除一页
- if(HAL_FLASHEx_Erase(&FlashEraseInit, &PageError) != HAL_OK)
- {
- SEGGER_RTT_printf(0,"Flash Erase err\r\n");
- HAL_FLASH_Lock();
- return (USBD_FAIL);
- }
- FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
- SEGGER_RTT_printf(0,"Flash Erase OK\r\n");
-
- // 将整个Buff存入Flash
- while(sectorStartAddr < tempSectorStartAddr+2048) //写数据
- { // 单次写入8字节 64位数据
- if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, sectorStartAddr, *(uint64_t*)p) != HAL_OK)//写入数据
- {
- HAL_FLASH_Lock();
- return (USBD_FAIL);
- break; //写入异常
- }
- sectorStartAddr += 8; //地址加8
- p += 2; //buff是32位的,所以这里+2 便是8个字节
- }
-
- FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
- HAL_FLASH_Lock();
-
- return (USBD_OK);
- /* USER CODE END 7 */
- }
-
- /**
- * @brief .
- * @param None
- * @retval .
- */
- int8_t STORAGE_GetMaxLun_FS(void)
- {
- /* USER CODE BEGIN 8 */
- // SEGGER_RTT_printf(0," STORAGE_GetMaxLun_FS %d \r\n",STORAGE_LUN_NBR - 1);
- return (STORAGE_LUN_NBR - 1);
- /* USER CODE END 8 */
- }
主要是写入和读取函数配置:
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
写入:buf为要写入数据的指针、blk_addr为要写入的块的index、blk_len为要写入几个块(一般为1)
因为103VET6的flash块大小为2K,所以要判断blk_addr在那个Flash块中,然后将整个Flash块读出来重新写入。blk_len一般为1,所以如果不为1时,这段代码是有问题的。
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
读取:参数同写入,用到了读取单个字节的函数
- /*************************************************************
- ** Function name: STMFLASH_ReadByte
- ** Descriptions: 读uint8操作
- ** Input parameters: None
- ** Output parameters: None
- ** Returned value: None
- ** Remarks: None
- *************************************************************/
- uint8_t STMFLASH_ReadByte(uint32_t faddr)
- {
- return *(uint8_t*)faddr;
- }
然后我们利用写入文件连续的特性,就可以在内存中得到bin文件了。
实验:



可以看出来,固件拷贝到优盘之后,在0x0805 0600的位置。和bin文件是一样的。
重新上电U盘中的内容也会存在,不会因为掉电丢失。
- #define APP1_START_ADDR 0x08019000
-
- #define MASS_STORAGE_CLASS_START_ADDR 0x0804B000
大容量存储设备 起始地址为0x0804B000, 在固件之前的内容为FAT系统的其他数据。
【我们可以在Bin文件开头 做上Information(名字+版本号+StartAddr+CRC等等),结尾做上结束标记符号,这样就能判断是不是需要升级的固件】
注意事项:升级的话U盘中只能放一个固件文件、放其他的可能会使文件内容不连续。具体可以搜一下FAT系统。