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


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

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

    1. /* USER CODE BEGIN PRIVATE_TYPES */
    2. #define BLOCKNUM 400
    3. /* USER CODE END PRIVATE_TYPES */
    4. #define STORAGE_LUN_NBR 1
    5. #define STORAGE_BLK_NBR BLOCKNUM
    6. #define STORAGE_BLK_SIZ 0x200

     STORAGE_BLK_SIZ=0x200 是指作为USB-Device时 一个扇区的大小。

    其他函数配置: 

    1. #define APP1_START_ADDR 0x08019000
    2. #define MASS_STORAGE_CLASS_START_ADDR 0x0804B000
    1. /* Private functions ---------------------------------------------------------*/
    2. /**
    3. * @brief Initializes over USB FS IP
    4. * @param lun:
    5. * @retval USBD_OK if all operations are OK else USBD_FAIL
    6. */
    7. int8_t STORAGE_Init_FS(uint8_t lun)
    8. {
    9. /* USER CODE BEGIN 2 */
    10. return (USBD_OK);
    11. /* USER CODE END 2 */
    12. }
    13. /**
    14. * @brief .
    15. * @param lun: .
    16. * @param block_num: .
    17. * @param block_size: .
    18. * @retval USBD_OK if all operations are OK else USBD_FAIL
    19. */
    20. int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
    21. {
    22. /* USER CODE BEGIN 3 */
    23. *block_num = STORAGE_BLK_NBR;
    24. *block_size = STORAGE_BLK_SIZ;
    25. return (USBD_OK);
    26. /* USER CODE END 3 */
    27. }
    28. /**
    29. * @brief .
    30. * @param lun: .
    31. * @retval USBD_OK if all operations are OK else USBD_FAIL
    32. */
    33. int8_t STORAGE_IsReady_FS(uint8_t lun)
    34. {
    35. /* USER CODE BEGIN 4 */
    36. // return (USBD_FAIL);
    37. return (USBD_OK);
    38. /* USER CODE END 4 */
    39. }
    40. /**
    41. * @brief .
    42. * @param lun: .
    43. * @retval USBD_OK if all operations are OK else USBD_FAIL
    44. */
    45. int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
    46. {
    47. /* USER CODE BEGIN 5 */
    48. return (USBD_OK);
    49. /* USER CODE END 5 */
    50. }
    51. /**
    52. * @brief .
    53. * @param lun: .
    54. * @retval USBD_OK if all operations are OK else USBD_FAIL
    55. */
    56. int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
    57. {
    58. /* USER CODE BEGIN 6 */
    59. uint32_t satrtAddr = MASS_STORAGE_CLASS_START_ADDR + blk_addr*STORAGE_BLK_SIZ;
    60. for(uint16_t i=0;i
    61. *(buf+i) = STMFLASH_ReadByte(satrtAddr+i);
    62. }
    63. return (USBD_OK);
    64. /* USER CODE END 6 */
    65. }
    66. /**
    67. * @brief .
    68. * @param lun: .
    69. * @retval USBD_OK if all operations are OK else USBD_FAIL
    70. */
    71. unsigned char tempBuff[2048] = {0};
    72. int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
    73. {
    74. /* USER CODE BEGIN 7 */
    75. uint32_t * p = (uint32_t *)tempBuff;
    76. FLASH_EraseInitTypeDef FlashEraseInit;
    77. uint32_t PageError = 0;
    78. // 要写入的地址
    79. uint32_t falshAddr = MASS_STORAGE_CLASS_START_ADDR + blk_addr*STORAGE_BLK_SIZ;
    80. // 要写入的地址所在的扇区的起始地址
    81. uint32_t sectorStartAddr = Get103VET6SectorAddr(falshAddr);
    82. uint32_t tempSectorStartAddr = sectorStartAddr;
    83. // 判断要写的内容在Flash哪一个扇区 将其中的内容全部取出到Buff中
    84. memcpy(tempBuff, (uint32_t *)sectorStartAddr, 2048);
    85. // 将数据写入Buff
    86. memcpy(tempBuff+(blk_addr%4)*512, buf, blk_len*512);
    87. SEGGER_RTT_printf(0,"addr %x start addr %x \r\n",falshAddr,sectorStartAddr);
    88. HAL_FLASH_Unlock(); //解锁
    89. // // 擦除当前Flash扇区内容
    90. FlashEraseInit.TypeErase = FLASH_TYPEERASE_PAGES; //擦除类型,页擦除
    91. FlashEraseInit.Banks = FLASH_BANK_1;
    92. FlashEraseInit.PageAddress = falshAddr;
    93. FlashEraseInit.NbPages = 1; //一次只擦除一页
    94. if(HAL_FLASHEx_Erase(&FlashEraseInit, &PageError) != HAL_OK)
    95. {
    96. SEGGER_RTT_printf(0,"Flash Erase err\r\n");
    97. HAL_FLASH_Lock();
    98. return (USBD_FAIL);
    99. }
    100. FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
    101. SEGGER_RTT_printf(0,"Flash Erase OK\r\n");
    102. // 将整个Buff存入Flash
    103. while(sectorStartAddr < tempSectorStartAddr+2048) //写数据
    104. { // 单次写入8字节 64位数据
    105. if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, sectorStartAddr, *(uint64_t*)p) != HAL_OK)//写入数据
    106. {
    107. HAL_FLASH_Lock();
    108. return (USBD_FAIL);
    109. break; //写入异常
    110. }
    111. sectorStartAddr += 8; //地址加8
    112. p += 2; //buff是32位的,所以这里+2 便是8个字节
    113. }
    114. FLASH_WaitForLastOperation(FLASH_WAITETIME); //等待上次操作完成
    115. HAL_FLASH_Lock();
    116. return (USBD_OK);
    117. /* USER CODE END 7 */
    118. }
    119. /**
    120. * @brief .
    121. * @param None
    122. * @retval .
    123. */
    124. int8_t STORAGE_GetMaxLun_FS(void)
    125. {
    126. /* USER CODE BEGIN 8 */
    127. // SEGGER_RTT_printf(0," STORAGE_GetMaxLun_FS %d \r\n",STORAGE_LUN_NBR - 1);
    128. return (STORAGE_LUN_NBR - 1);
    129. /* USER CODE END 8 */
    130. }

    主要是写入和读取函数配置:

    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)

    读取:参数同写入,用到了读取单个字节的函数

    1. /*************************************************************
    2. ** Function name: STMFLASH_ReadByte
    3. ** Descriptions: 读uint8操作
    4. ** Input parameters: None
    5. ** Output parameters: None
    6. ** Returned value: None
    7. ** Remarks: None
    8. *************************************************************/
    9. uint8_t STMFLASH_ReadByte(uint32_t faddr)
    10. {
    11. return *(uint8_t*)faddr;
    12. }

    然后我们利用写入文件连续的特性,就可以在内存中得到bin文件了。

    实验:

    可以看出来,固件拷贝到优盘之后,在0x0805 0600的位置。和bin文件是一样的。

    重新上电U盘中的内容也会存在,不会因为掉电丢失。 

    1. #define APP1_START_ADDR 0x08019000
    2. #define MASS_STORAGE_CLASS_START_ADDR 0x0804B000

     大容量存储设备 起始地址为0x0804B000, 在固件之前的内容为FAT系统的其他数据。

    【我们可以在Bin文件开头 做上Information(名字+版本号+StartAddr+CRC等等),结尾做上结束标记符号,这样就能判断是不是需要升级的固件】

     注意事项:升级的话U盘中只能放一个固件文件、放其他的可能会使文件内容不连续。具体可以搜一下FAT系统。

  • 相关阅读:
    黑马旅游网-配置项目(一)
    第3章 内存管理
    常见的NAS协议
    Redis持久化策略剖析
    智能语音外呼OKCC呼叫中心的各项指标KPI
    蓝牙官网demo的记录
    C语言学习之路(基础篇)—— 文件操作(上)
    机器人和自动化技术
    Docker从认识到实践再到底层原理(六-2)|Docker容器操作实例
    文字在一行或多行时超出显示省略号
  • 原文地址:https://blog.csdn.net/qq_44810226/article/details/127508789