将STM32 Flash的一部分虚拟为大容量存储设备 USB_Device

  • Post author:
  • Post category:其他



【举报再看养成习惯,噢 不对,点赞再看 养成习惯。感谢支持】

实验环境: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系统。



版权声明:本文为qq_44810226原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。