硬件环境
- STM32(支持USB)
- 存储介质(SPI FLASH、E2P、甚至是片内FLASH均可),本实验使用外部SPI flash(W25Q64)。
使用STM32CubeMX进行配置初始化信息
-
配置MCU的时钟,外设等信息,可以使用一路串口进行输出Log信息,串口波特率越高越好
-
MCU管脚配置
4. 时钟配置
我这里的硬件中有8M的HSE,然后需要注意的是,USB时钟需要配置到48M -
USB配置,记得打开中断
-
串口
-
存储介质的通信接口,如使用内部flash,则不需要配置
W25Q64 是SPI FLASH,配置SPI接口,这个配置可能需要根据芯片手册进行调整
-
中间件 USB_DEVICE
Class for FS IP选择大容量存储,其他可以默认,USB_DEBUG_LEVEL改不改都一样,做USB device时没看到打印出log来。如果配置了非0,那么需要重定向printf,并且勾选USE Micro LIB,否则可能会出现问题。
-
将堆栈空间改大之后就可以生成代码了
修改程序
生成完程序后,直接编译烧录,然后将USB连接到电脑上,就可以看到已经多出来一个盘符,但是看不到容量和大小,也不能成功格式化磁盘,那是因为我们还没有完成数据的读取和写入
修改添加存储介质的驱动代码
这个步骤很关键,如果你的驱动有问题,那么就会导致格式化失败,也就没办法使用。
W25Q64
必要的函数
W25Q64.h
typedef struct
{
void (*init) (void);
void (*read_sector) (uint32_t addr, uint8_t *pData, uint32_t length);
void (*write_sector) (uint32_t addr, uint8_t *pData, uint32_t length);
uint8_t (*get_status) (void);
}W25Q64_Dev_T;
extern W25Q64_Dev_T w25q64_drv;
W25Q64.c
static void w25q64_write_enable(void);
static void w25q64_write_disable(void);
static void w25q64_chip_init(void);
static void w25q64_page_write(uint32_t addr, uint8_t *pData, uint32_t length); //256byte max
static void w25q64_sector_write(uint32_t addr, uint8_t *pData, uint32_t length);//4kbyte max
static void w25q64_sector_read(uint32_t addr, uint8_t *pData, uint32_t length);
static void w25q64_erase_sector(uint32_t addr);
static uint8_t w25q64_read_satatus(void);
static uint8_t data_padding = 0xff;
W25Q64_Dev_T w25q64_drv =
{
w25q64_chip_init,
w25q64_sector_read,
w25q64_sector_write,
w25q64_read_satatus,
};
数据读写函数添加到USB驱动中
我们要修改的文件是 usbd_storage_if.c
#define STORAGE_LUN_NBR 1
#define STORAGE_BLK_NBR W25Q64_SECTOR_NBR//改为flash介质的sector 数量
#define STORAGE_BLK_SIZ W25Q64_SECTOR_SIZE//改为flash介质的sector 大小
-
int8_t STORAGE_Init_FS(uint8_t lun)
我们可以添加我们刚刚写的存储介质初始化,如果正常,则返回 USBD_OK
-
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
获取存储介质的大小,函数已经填充好,我们可以不动
-
int8_t STORAGE_IsReady_FS(uint8_t lun)
获取介质状态,我们要对存储介质的状态进行判断,这里我们要判断两点,一个是是否正在读写状态中,另外一个就是存储介质是否是不可工作状态
-
int8_t STORAGE_IsWriteProtected_FS(uint8_t lun)
判断是否是写保护,我们可以直接返回
USBD_OK
-
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
读取一个扇区,我们将准备好的读取一个扇区的代码填充进来就好
例上述W25Q64的驱动代码:
/** * @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 */ while(STORAGE_IsReady_FS(1) != 0); storage_status = 1; w25q64_drv.read_sector(blk_addr*W25Q64_SECTOR_SIZE, buf, blk_len*W25Q64_SECTOR_SIZE); storage_status = 0; 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)
写入一个扇区,我们将准备好的读取一个扇区的代码填充进来就好
例上述W25Q64的驱动代码:
/** * @brief . * @param lun: . * @retval USBD_OK if all operations are OK else USBD_FAIL */ int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len) { /* USER CODE BEGIN 7 */ while(STORAGE_IsReady_FS(1) != 0); storage_status = 1; w25q64_drv.write_sector(blk_addr*W25Q64_SECTOR_SIZE, buf, blk_len*W25Q64_SECTOR_SIZE); storage_status = 0; return (USBD_OK); /* USER CODE END 7 */ }
-
int8_t STORAGE_GetMaxLun_FS(void)
读取磁盘介质的个数,一般我们只虚拟出一个来,不用管就行
我们使用的spi flash的sector大小为4k,也就是4096,USB这边是520,所以我们需要将其修改一致
-
修改usbd_msc.h
/** @defgroup USBD_BOT_Exported_Defines * @{ */ /* MSC Class Config */ #ifndef MSC_MEDIA_PACKET #define MSC_MEDIA_PACKET 4096U //512U #endif /* MSC_MEDIA_PACKET */ #define MSC_MAX_FS_PACKET 0x40U //0x40 #define MSC_MAX_HS_PACKET 0x1000U //0x200 #define BOT_GET_MAX_LUN 0xFE #define BOT_RESET 0xFF #define USB_MSC_CONFIG_DESC_SIZ 32 #define MSC_EPIN_ADDR 0x81U #define MSC_EPOUT_ADDR 0x01U
-
修改usbd_conf.h
/** @defgroup USBD_CONF_Exported_Defines USBD_CONF_Exported_Defines * @brief Defines for configuration of the Usb device. * @{ */ /*---------- -----------*/ #define USBD_MAX_NUM_INTERFACES 1 /*---------- -----------*/ #define USBD_MAX_NUM_CONFIGURATION 1 /*---------- -----------*/ #define USBD_MAX_STR_DESC_SIZ 4096 //512 /*---------- -----------*/ #define USBD_DEBUG_LEVEL 3 /*---------- -----------*/ #define USBD_SELF_POWERED 1 /*---------- -----------*/ #define MSC_MEDIA_PACKET 4096 //512
至此,我们的U盘就做好了
格式化U盘
做好之后,首次插入到电脑上会比较慢,然后就可以对其进行格式化,格式化完成之后就可以进行使用了。格式化设置。分配单元大小即为4096bytes
格式化完成
传个文件试试,达到了惊人的177KB/s
传输完成