例程:STM32USBdevice: 基于STM32的USB设备例子程序 - Gitee.com
本篇为使用芯片内部flash作为USB存储设备的例程,没有知识,全是实操,按照步骤就能获得一个STM32的U盘。本例子是在野火F103MINI开发板上验证的,如果代码中出现一些外设的配置,可以参考野火F103MINI开发板原理图对照。
设置外部晶振,必须要使用外部晶振,因为USB控制器需要48M的系统时钟,内部晶振无法倍频出48M。
配置外部时钟
配置调试口和系统基准源
开启USB设备
中间件中设备USB设备类型
标蓝色的部分需要根据你之前有没有使用过这两VID和PID,如果使用过最好换一下,避免使用之前的驱动引起一些奇奇怪怪的问题。
STM32扇区大小如下
Flash大小为64KB ,单个扇区大小:1KB=0x400;Flash大小为128KB,单个扇区大小:1KB=0x400;
Flash大小为256KB,单个扇区大小:2KB=0x800;Flash大小为512KB,单个扇区大小:2KB=0x800;
STM32容量划分
大容量:256-512k的Flash,主要为:STM32F103xC, STM32F103xD,STM32F103xE
中容量:64-128k的Flash,主要为:STM32F103x8 STM32F103xB
小容量:16-32k的Flash,主要为:STM32F103x4 STM32F103x6
如果你使用的是大容量,MSC_MEDIA_PACKET需要配置为2048,如果不是大容量设备这里需要配置为1024
我使用了freertos v2
配置外部系统时钟
分配较大的堆栈空间,使U盘读写转存更快
配置独立C和H文件
代码需要修改如下:
首先我们需要知道代码使用了多大,我们需要使用没有被使用的那部分flash
我们把我们代码编译后会看到编译信息,
text 代码和常量
data 初始化的全局变量
bss 未初始化的变量
dec 就是前面三个的加和
我们可以使用Build Analyzer查看已经使用的flash
其实就是text+data=48,832=47.6875KB
所以我们需要使用48KB之后的flash,我们这里使用60KB之后的flash
/* USER CODE BEGIN PRIVATE_DEFINES */
//这里定义读写的FLASH起始地址和大小
#define FLASH_START_NBR 0x0800F000 // 从60K地址开始
#define FLASH_PAGE_NBR 100 // 100k
/* USER CODE END PRIVATE_DEFINES */
其中F000就是60KB,0x08000000是flash起始地址。
简单讲解几个重要函数
static int8_t STORAGE_Init_FS(uint8_t lun);
存储的初始化函数,如果有SD卡或者片外FLASH初始化可以放在这里。
static int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size);
获取存储容量大小;
static int8_t STORAGE_IsReady_FS(uint8_t lun);
设备是否准备好;
static int8_t STORAGE_IsWriteProtected_FS(uint8_t lun);
设备是否是写保护,类似于大的SD卡边上的小卡扣,如果是写保护则没有写权限;
static int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
存储读函数;
static int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len);
存储写函数;
int8_t STORAGE_GetCapacity_FS(uint8_t lun, uint32_t *block_num, uint16_t *block_size)
{
/* USER CODE BEGIN 3 */
*block_num = FLASH_PAGE_NBR; // 块数量为100
*block_size = FLASH_PAGE_SIZE; // 块大小为2048字节
return (USBD_OK);
/* USER CODE END 3 */
}
FLASH_PAGE_SIZE宏是STM32固件库提供的,如果是大容量就是2k
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 6 */
if (lun == 0) { // 如果lun为0,则将FLASH_BLK_NBR + blk_addr * FLASH_BLK_SIZ地址处的数据复制到buf中,长度为blk_len * FLASH_BLK_SIZ字节
memcpy(buf, (uint8_t*) (FLASH_START_NBR + blk_addr * FLASH_PAGE_SIZE), blk_len * FLASH_PAGE_SIZE);
return USBD_OK;
}
return (USBD_FAIL);
/* 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 */
if (lun == 0) { // 如果lun为0,则执行以下操作
uint16_t i; // 定义一个变量i
HAL_FLASH_Unlock(); // 解锁Flash
FLASH_EraseInitTypeDef f; // 定义一个结构体f,用于存储擦除Flash的参数
f.TypeErase = FLASH_TYPEERASE_PAGES; // 设置擦除类型为页擦除
f.PageAddress = FLASH_START_NBR + blk_addr * FLASH_PAGE_SIZE; // 设置要擦除的页地址
f.NbPages = blk_len; // 设置要擦除的页数
uint32_t PageError = 0; // 定义一个变量PageError,用于存储擦除错误信息
HAL_FLASHEx_Erase(&f, &PageError); // 执行擦除操作
for (i = 0; i < blk_len * FLASH_PAGE_SIZE; i += 4) // 循环写入数据到Flash中
HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, FLASH_START_NBR + blk_addr * FLASH_PAGE_SIZE + i,
*(uint32_t *)(&buf[i])); // 将buf中的数据写入到Flash中
HAL_FLASH_Lock(); // 锁定Flash
}
return (USBD_OK); // 返回USBD_OK
/* USER CODE END 7 */
}
至此可以下载程序了,下载好后第一次接入电脑需要格式化U盘,格式化之后就可以正常使用了。
因为我这里还有200多KB的空闲,所以我可以分配200KB的flash。