【stm32@USB】应用-2: SPI_FLASH 模拟U盘(USB MSC设备)
环境说明:
开发板:野火STM32指南者
单片机:STM32F103VET6
SPI_FLASH:W25Q64(Q80, 16, 32, 64, 128均可)
IDE:Keil V5
USB大容量存储设备类(The USB mass storage device class),简称MSC(Mass Storage Class),允许一个通用串行总线(USB)设备来访问主机的计算设备,使两者之间进行文件传输。
本文将W25Q64模拟USB MSC设备,即当作U盘使用。
1. 学习大师的文章《STM32-外部FLASH(W25Q64)模拟U盘》
作者:不要让自己太懒
首先看的是这篇文章,思路很清晰:在CubeMX配置好时钟、系统、然后配置USB的GPIO和中间层(MiddleWare)中的USB_Device 的驱动。
关键步骤是在其中添加 bsp_spi_flash.c 文件,然后修改usbd_storage_if.c文件
大致过程如下:
//修改以下宏定义
#define STORAGE_LUN_NBR 1 /* 逻辑单元号,只有一个外部flash,设置为1 */
#define STORAGE_BLK_NBR 2048 /* 扇区的数量,外部flash的大小是8Mbyte,有128块,每块16个扇区,故128*16=2048个扇区 */
#define STORAGE_BLK_SIZ 4096 /* 每个扇区的大小,外部flash扇区的大小为4096byte */
//设置相应的初始化、状态、读、写4个函数
//初始化函数
int8_t STORAGE_Init_FS(uint8_t lun)
{
/* USER CODE BEGIN 2 */
bsp_spi1_init();
return (USBD_OK);
/* USER CODE END 2 */
}
//状态函数
int8_t STORAGE_IsReady_FS(uint8_t lun)
{
/* USER CODE BEGIN 4 */
if(spi_flash_read_ID() == FLASH_ID)
{
return (USBD_OK);
}
else
{
return USBD_FAIL;
}
/* USER CODE END 4 */
}
//读函数
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 6 */
/* 因为传进来的参数是扇区的地址和扇区的大小,在进行读的时候要转换成字节地址和字节大小 */
spi1_flash_read(blk_addr*STORAGE_BLK_SIZ, buf, blk_len*STORAGE_BLK_SIZ);
return (USBD_OK);
/* USER CODE END 6 */
}
//写函数
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 7 */
/* 因为传进来的参数是扇区的地址和扇区的大小,在进行写的时候要转换成字节地址和字节大小,flash写之前要擦除 */
spi1_flash_sector_erase(blk_addr*STORAGE_BLK_SIZ);
spi1_flash_write(blk_addr*STORAGE_BLK_SIZ, buf, blk_len*STORAGE_BLK_SIZ);
return (USBD_OK);
/* USER CODE END 7 */
}
2. 自己调试的过程
但是不知道什么原因,电脑就是无法识别:
于是我加了如下的调试代码:
printf("Hello world!\n");
uint32_t mflash_id = SPI_FLASH_ReadID();
printf("SPI ID=%#x\n",mflash_id);
然后发现能打印出来"Hello world!",但执行到第二句就卡住了。打开调试模式,全速执行发现卡在了 HardFault_Handler()(严格错误中断函数)
于是在调试模式下,一句一句执行,发现最终卡在 SPI_FLASH_SendByte(W25X_JedecDeviceID);
里面,卡在下面一句:
但是我之前用 CubeMX生成了 SPI 的初始化代码(spi.c),因此就把 bsp_spi_flash.c 文件里面的 SpiHandle 给条件编译注释了(相当于取消了)。
这就出现了一个问题:采用CubeMX生成的 spi.c 初始化? 还是采用野火例程中 bsp_spi_flash.c 进行初始化呢?
最终,我这样解决:
由于初始化是包括两个部分的:底层初始化(HAL_SPI_MspInit(SPI_HandleTypeDef *spiHandle)
)和上层初始化(void SPI_FLASH_Init(void)
)。
野火例程底层初始化的的GPIO和我的不一样,因此这部分我采用了 CubeMX生成的 spi.c 的HAL_SPI_MspInit(SPI_HandleTypeDef *spiHandle)
而上层的初始化野火的例程就是配置SPI模式,这部分我用了野火例程bsp_spi_flash.c的void SPI_FLASH_Init(void);
调试好 spi_flash之后,U盘就能正常被识别并且使用了。
3. 一些疑问
在上面的创建过程中,仍然留下了一些疑问:
就是上述教程文章《STM32-外部FLASH(W25Q64)模拟U盘》 生成工程时,要求将堆改大一些,但是这里感觉没有用到堆,为什么需要改这里呢???
经过我的实测,将栈的大小改为0后,烧录进板子,程序仍然能正常运行。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具