STM32使用HAL库配置USB虚拟串口VPC(CDC)与U盘MSC功能,集成Flash文件系统配置指南
芯片:STM32F407ZGT6
工具:STM32cubemx,keil
1、USB虚拟串口VPC(CDC)
USB,即为通用串行总线,是一个外部总线标准,用于规范电脑与外部设备的连接和通讯。是应用在PC领域的接口技术。
标准 USB 共四根线组成,除 VCC/GND 外,另外为 D+(DP),D-(DM);这两根数据线采用的是差分电压的方式进行数据传输的。在 USB 主机上, D-和 D+都是接了 15K 的电阻到GND,所以在没有设备接入的时候, D+、 D-均是低电平。而在 USB 设备中,如果是高速设备,则会在D+上接一个 1.5K 的上拉电阻到 VCC,而如果是低速设备,则会在 D-上接一个 1.5K 的上拉电阻到 VCC。即主机就可以判断是否有设备接入,并能判断设备是高速设备还是低速设备。
STM32内置的USB,均可支持USB 2.0标准,可以支持三种传输速率:
- 高速模式:最高可达480 Mbps (部分型号支持,且需搭配外部芯片,不常用 )
- 全速模式:最高可达12 Mbps (最常用)
- 低速模式:最高可达1.5 Mbps
USB虚拟串口,简称VPC,Virtual Port Com 的简写。但更习惯于把虚拟串口叫作: CDC,因为它是利用 USB 的 CDC类 实现的一种通信接口(可以实现类似串口USART一样得到功能)
2、CUBEMX配置
前面的的基础配置(RCC,SYS)略过
2.1、USB_OTG_FS配置
USB外设配置
参数使用默认的的就行
2.2、USB时钟配置
框出来的是USB的时钟频率
2.3、生成代码并修改
上面配置后,直接生成代码,打开工程文件
这个头文件是配置后自己生成的(不用担心哈)
这个文件中国有关于接收和发送的函数
发送函数:
uint8_t CDC_Transmit_FS ( uint8_t* Buf, uint16_t Len );
函数接受两个参数:数据缓冲区的地址、字节数。
如果USB设备正忙,它会返回USBD_BUSY状态。
这个函数的作用是设置传输数据的缓冲区,并标记数据包为待发送。数据并非立刻发出,而是被存储在USB外设的缓冲区中,等待主机轮询请求传输。
接收函数:
当USB CDC接收到来自USB主机的数据时,触发中断进入中断函数,继而自动调用接收回调函数(如下):
uint8_t CDC_Receieve_FS ( uint8_t* Buf, uint16_t Len );
uint8_t* Buf
: 指向接收缓冲区的指针,即数据缓存的地址。uint16_t* Len
: 当前数据包的字节数。注意事项:
每当接收到一包数据,硬件自动触发中断函数, 继而调用此接收回调函数,无需人工调用。
与发送机制相似,每间隔1ms,最多接收1包数据,每包最大64字节。。
如果需要接收超过64字节的数据帧,注意,指上位机发送的1个完整数据帧,而非USB的单包数据,如,上位机发来一张图片数据,8350个字节,则需要在此回调函数中添加额外的代码来判断帧数据传输完整结束 、手动将多个数据包拼接成完整的数据帧。
接收到数据时,缓存不会提前自动清零,新数据从Buf的起始位置开始,覆盖存放。
由于该回调函数是被中断函数调用的,因此建议函数内部的处理尽可能地简短,以避免影响系统的实时性(中断函数运行期间,会令程序持续挂起)。
到此关于USB_OTG_FS的基础配置已经完成,关于更多玩法参考如下文章杠STM32 — USB虚拟串口通信_stm32 usb-CSDN博客
3、 USB 系列之大容量设备(MSC)
使用STM32的大容量设备进行文件传输,通过STM32的USB大容量设备将电脑中的文件放到外部FALSH中,然后在程序运行时通过FATFS文件系统读取数据访问文件。
USB_OTG_FS配置
外设配置
时钟配置
参考其他文章建议将下面得到数值改大
4、代码修改和适配
使用的外部Flash(NORFlash)需要配置spi来实现外部flash和mcu通信(这部分的配置较为简单,参考文件较多故省略)。
需要flash的读写代码(一般使用 flash会有他的官方驱动程序)—-在主文件中不要忘记初始化
下面两个函数是USB大容量存储类(MSC)驱动的核心回调函数(函数usbd_storage_if.c),负责将USB协议层接收到的SCSI读写命令(如 READ10
、WRITE10
)转换为对物理存储介质的操作。
例如,当你在电脑上复制文件到U盘时,Windows/Linux会通过USB发送SCSI命令,最终由这两个函数执行实际的数据读写。
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
功能:当主机(如电脑)从设备的存储介质(如U盘)读取数据时,此函数被调用。
参数解析:
lun
(Logical Unit Number):逻辑单元号,用于区分多个存储设备(如多块磁盘)。此处仅支持 lun=0
,表示第一个存储设备(例如SPI Flash)。
buf
:数据缓冲区,用于暂存从存储介质读取到的数据,最终通过USB传输给主机。
blk_addr
:逻辑块地址(起始扇区号),主机请求读取的起始位置。
blk_len
:请求读取的扇区数量(每扇区通常为512字节)。
实现细节:
将主机请求的扇区地址和长度转换为物理地址(如 blk_addr * 512
),调用底层Flash驱动(norflash_read
)读取数据到缓冲区 buf
。
若读取失败,设置错误标志 g_usb_state_reg
并打印错误信息
参考示例:
int8_t STORAGE_Read_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 6 */
int8_t res = 0;
g_usb_state_reg |= 0X02; /* 标记正在读数据 */
switch (lun)
{
case 0: /* SPI FLASH */
norflash_read(buf, USB_STORAGE_FLASH_BASE + blk_addr * 512, blk_len * 512);
break;
}
if (res)
{
printf("rerr:%d,%d", lun, res);
g_usb_state_reg |= 0X08; /* 读错误! */
}
return res;
/* USER CODE END 6 */
}
norflash_read:flash的写入函数
int8_t STORAGE_Write (uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
功能:当主机(如电脑)向设备的存储介质写入数据时,此函数被调用。
参数解析:
参数与 STORAGE_Read_FS
类似,但 buf
包含主机待写入的数据。
实现细节:
将主机请求的扇区地址和长度转换为物理地址,调用底层Flash驱动(norflash_write
)将缓冲区 buf
中的数据写入存储介质。
若写入失败,设置错误标志 g_usb_state_reg
并打印错误信息。
参考示例:
int8_t STORAGE_Write_FS(uint8_t lun, uint8_t *buf, uint32_t blk_addr, uint16_t blk_len)
{
/* USER CODE BEGIN 7 */
int8_t res = 0;
g_usb_state_reg |= 0X01; /* 标记正在写数据 */
switch (lun)
{
case 0: /* SPI FLASH */
norflash_write(buf, USB_STORAGE_FLASH_BASE + blk_addr * 512, blk_len * 512);
break;
}
if (res)
{
g_usb_state_reg |= 0X04; /* 写错误! */
printf("werr:%d,%d", lun, res);
}
return res;
/* USER CODE END 7 */
}
norflash_write:flash的写入函数
依赖底层存储驱动(如 norflash_read
/norflash_write
)实现具体介质的读写(norflash_read
/norflash_write一般在flash的驱动文件中实现,厂家会给出来的,不同的flash芯片的驱动文件不同
)
3.1、USB连接判断
主要处理函数在usbd_conf.c文件中
当USB主机(如电脑)检测到总线空闲(无数据传输)超时,会向USB设备发送挂起信号
void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd)
判断USB已经连接上
void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd)
判断USB没有连接
void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd)
/* 能执行到该函数,说明USB连接成功了 */
USBD_StatusTypeDef USBD_LL_SetUSBAddress(USBD_HandleTypeDef *pdev, uint8_t dev_addr)
在这些回调函数中使用标志位,后续方便再主函数中得到USB的状态
参考大佬文章如下:
STM32 — USB虚拟串口通信_stm32 usb-CSDN博客
STM32 USB 系列之大容量设备(MSC) 基于HAL库_stm32 msc+fatfs-CSDN博客
【STM32】HAL库USB虚拟U盘MSC配置及采用自带的Flash作为文件系统_stm32 usb 虚拟u盘-CSDN博客
作者:小咖大_