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标准,可以支持三种传输速率:

  1. 高速模式:最高可达480 Mbps  (部分型号支持,且需搭配外部芯片,不常用 )
  2. 全速模式:最高可达12 Mbps      (最常用)
  3. 低速模式:最高可达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读写命令(如 READ10WRITE10转换为对物理存储介质的操作

    例如,当你在电脑上复制文件到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博客

    作者:小咖大_

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32使用HAL库配置USB虚拟串口VPC(CDC)与U盘MSC功能,集成Flash文件系统配置指南

    发表回复