【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后,烧录进板子,程序仍然能正常运行。

posted @ 2023-11-28 17:08  FBshark  阅读(394)  评论(0编辑  收藏  举报