【举报再看养成习惯,噢 不对,点赞再看 养成习惯。感谢支持】
实验环境: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<blk_len*STORAGE_BLK_SIZ;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系统。