跳到主要内容

USB-cdc实践

对于一种全新的东西来说,我选择遵循”能用就行"原则,毕竟usb可是和fat文件系统一样大头的东西,想学深入的(或者以后就业想搞嵌入式的),好好学。

虽然说目前还不知道会有什么坑,但是基本的收发方式stm已经给我们做好铺垫了,希望没什么坑。

目前你需要知道的:

  1. 目前我只写了hal库版本,想要用固件库的同学,可以去参考一下鱼鹰的
  2. 接发每一次最大数据包都是64byte,如果超出64会自动到下一个包
  3. C板的(PA10)usart1_RX会和USB_FS_Id口冲突
  4. usb的协议知识量很多,看了一会直接崩溃,所以才是从实践到理论

cubeMX配置

将对应的3个GPIO口配置成USB_FS的引脚

以C板为例,USB_FS的三个引脚是PA12-PA10,点击配置

如果是ACE其他扩展板,请按照pcb询问硬件组同学usbfs的三个引脚是哪个进行配置

image-20221011142002778

设置USB_FS模式

按照上面来配置后,左边的USB_OTG_FS应该是一个绿色√的状态

如果出现黄色感叹号,请查看usart1取消掉没有,因为id的io口被usb占用

强制生成会发现包含stm32_hal_pcd.h的宏定义并没有开启,导致全是error

之后到第二个箭头Mode设置成Device_Only

image-20221011142540388

Computing项里的USB_DEVICE开启虚拟串口选项

打开Computing项,将 第二个箭头的选项选成如图所示项(usb-cdc协议)。

image-20221011142910504

image-20221011143123490

图片最下方还可以调整发送/接收缓冲区的大小

如果出现一些状况(比如上位机printf出现乱码/缺失,可以尝试调大,但是目前用感觉2048默认还是够用的

时钟配置

C板如下

image-20221011143414345

生成代码后一般都可以正确识别到usb设备了,可以开始使用发送/接收函数

数据收发的函数

目前我们只用关注截图下面的这个文件

image-20221011144658462

我们一般只用到CDC_Receive_FSCDC_Transmit_FS两个函数

大部分已经给我们封装好了,到手即用。

image-20221011144924809

如何使用收发函数?

发送:直接传进数组头指针和数组长度即可。(数组长度建议用sizeof或者ARR_SIZE

示例:image-20221011145543970

接收:接收比较麻烦,可以将CDC_Receive_FS理解为空闲中断,要你过来搬数据了。(不过注意我这个改指针的方式可能会有点危险,建议再另起炉灶,毕竟我还没看完原理(太多啦

示例:image-20221011145722670

外置USB的printf

/************************** Dongguan-University of Technology -ACE**************************
* @file bsp_usb.c
* @brief
* @author pansyhou侯文辉 ([email protected])
* @version 1.0
* @date 2022-10-11
*
*
* @history
* <table>
* Date Version Author Description
* 2022-10-11 1.0 侯文辉
* @verbatim
* ==============================================================================
* ==============================================================================
* @endverbatim
************************** Dongguan-University of Technology -ACE***************************/
#include "bsp_usb.h"
#include "usbd_cdc_if.h"
#include "usb_device.h"
#include "stdarg.h"

/**
* 重写printf,通过usbcdc虚拟串口
* 注意:buff可能要足够大,不然发送乱码或者不全
* @param format
* @param ...
*/
void printf_usb(const char *format, ...){
va_list args;
uint32_t length;
uint8_t buff[APP_TX_DATA_SIZE];

va_start(args, format);
length = vsnprintf((char *)buff, APP_TX_DATA_SIZE, (char *)format, args);
va_end(args);
CDC_Transmit_FS(buff, length);
}
//.h文件
#ifndef __BSP_USB_H
#define __BSP_USB_H
#include "main.h"
void printf_usb(const char *format, ...);


#endif

一些注意事项(待补充

  • 建议整一个双缓冲(可参考遥控接收的方式),不然容易冲数据,太快了,usb收发大部分是靠硬件的
  • 待补充

USB-FS理论

f4的中文参考手册上看,比较显著的特性有

  • 具有采用高级 FIFO 控制的 1.25 KB 专用 RAM

    可将 RAM 空间划分为不同 FIFO,以便灵活有效地使用 RAM

    每个 FIFO 可存储多个数据包

    动态分配存储区

    FIFO 大小可配置为非 2 的幂次方值,以便连续使用存储单元

  • 一帧之内可以无需要应用程序干预,以达到最大 USB 带宽

FS 框图

image-20221011133303119

FS模块说明

  1. 通过外部时钟从复位和时钟控制器 (RCC) 接收 48 MHz 的时钟,用于全速 (12 Mb/s) 驱动 48 MHz 域,必须在配置 OTG FS 模块前使能。(cubeMX 生成的自动使能)

  2. CPU 通过 AHB 外设总线对 OTG FS 模块寄存器进行读写操作,通过usb中断线接收USB事件通知

  3. CPU 通过向特定的 OTG_FS 单元(压栈寄存器)写入 32 位字来向 USB 提交数据。

    数据随即自动存储到 USB 数据 RAM 中配置的数据发送 FIFO 中。每个 IN 端点(从机模式)或 OUT 通道(主机模式)都有一个 Tx-FIFO 压栈寄存器。

  4. CPU 从特定的 OTG_FS 地址(出栈寄存器)读取 32 位字,以接收来自 USB 的数据。数据随即从在 1.25 KB USB 数据 RAM 内配置的共享 Rx-FIFO 中弹出。每个 OUT 端点或 IN 通 道都有一个 Rx-FIFO 出栈寄存器。

  5. USB 协议层通过串行接口引擎 (SIE) 驱动,并通过片上物理层 (PHY) 中的全速/低速收发器 模块经由 USB 进行数据的串行通信。

由于1.2kb的FIFO很大,刚好可以满足一个满速的数据包,所以能刚好吃完usb fs的带宽。

对寄存器那些基础比较感兴趣或者想深入的哥们自己去看吧,我看累了。

参考文献

  1. STM32使用USB虚拟串口 - 知乎 (zhihu.com)
  2. (233条消息) STM32 USB使用记录:使用CDC类虚拟串口(VCP)进行通讯_Naisu Xu的博客-CSDN博客_usb通讯模式cdc
  3. STM32_F4xx中文参考手册

深入学习?

鱼鹰的图解usb 图解USB (qq.com)

正点?野火?

看源码