USB-cdc实践
对于一种全新的东西来说,我选择遵循”能用就行"原则,毕竟usb可是和fat文件系统一样大头的东西,想学深入的(或者以后就业想搞嵌入式的),好好学。
虽然说目前还不知道会有什么坑,但是基本的收发方式stm已经给我们做好铺垫了,希望没什么坑。
目前你需要知道的:
- 目前我只写了hal库版本,想要用固件库的同学,可以去参考一下鱼鹰的
- 接发每一次最大数据包都是64byte,如果超出64会自动到下一个包
- C板的(PA10)usart1_RX会和USB_FS_Id口冲突
- usb的协议知识量很多,看了一会直接崩溃,所以才是从实践到理论
cubeMX配置
将对应的3个GPIO口配置成USB_FS的引脚
以C板为例,USB_FS的三个引脚是PA12-PA10,点击配置
如果是ACE其他扩展板,请按照pcb询问硬件组同学usbfs的三个引脚是哪个进行配置
设置USB_FS模式
按照上面来配置后,左边的USB_OTG_FS应该是一个绿色√的状态
如果出现黄色感叹号,请查看usart1取消掉没有,因为id的io口被usb占用
强制生成会发现包含stm32_hal_pcd.h的宏定义并没有开启,导致全是error
之后到第二个箭头Mode设置成Device_Only
Computing项里的USB_DEVICE开启虚拟串口选项
打开Computing项,将 第二个箭头的选项选成如图所示项(usb-cdc协议)。
图片最下方还可以调整发送/接收缓冲区的大小
如果出现一些状况(比如上位机printf出现乱码/缺失,可以尝试调大,但是目前用感觉2048默认还是够用的
时钟配置
C板如下
生成代码后一般都可以正确识别到usb设备了,可以开始使用发送/接收函数
数据收发的函数
目前我们只用关注截图下面的这个文件
我们一般只用到CDC_Receive_FS
和CDC_Transmit_FS
两个函数
大部分已经给我们封装好了,到手即用。
如何使用收发函数?
发送:直接传进数组头指针和数组长度即可。(数组长度建议用sizeof或者ARR_SIZE
示例:
接收:接收比较麻烦,可以将CDC_Receive_FS
理解为空闲中断,要你过来搬数据了。(不过注意我这个改指针的方式可能会有点危险,建议再另起炉灶,毕竟我还没看完原理(太多啦
示例:
外置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 框图
FS模块说明
通过外部时钟从复位和时钟控制器 (RCC) 接收 48 MHz 的时钟,用于全速 (12 Mb/s) 驱动 48 MHz 域,必须在配置 OTG FS 模块前使能。(cubeMX 生成的自动使能)
CPU 通过 AHB 外设总线对 OTG FS 模块寄存器进行读写操作,通过usb中断线接收USB事件通知
CPU 通过向特定的 OTG_FS 单元(压栈寄存器)写入 32 位字来向 USB 提交数据。
数据随即自动存储到 USB 数据 RAM 中配置的数据发送 FIFO 中。每个 IN 端点(从机模式)或 OUT 通道(主机模式)都有一个 Tx-FIFO 压栈寄存器。
CPU 从特定的 OTG_FS 地址(出栈寄存器)读取 32 位字,以接收来自 USB 的数据。数据随即从在 1.25 KB USB 数据 RAM 内配置的共享 Rx-FIFO 中弹出。每个 OUT 端点或 IN 通 道都有一个 Rx-FIFO 出栈寄存器。
USB 协议层通过串行接口引擎 (SIE) 驱动,并通过片上物理层 (PHY) 中的全速/低速收发器 模块经由 USB 进行数据的串行通信。
由于1.2kb的FIFO很大,刚好可以满足一个满速的数据包,所以能刚好吃完usb fs的带宽。
对寄存器那些基础比较感兴趣或者想深入的哥们自己去看吧,我看累了。
参考文献
- STM32使用USB虚拟串口 - 知乎 (zhihu.com)
- (233条消息) STM32 USB使用记录:使用CDC类虚拟串口(VCP)进行通讯_Naisu Xu的博客-CSDN博客_usb通讯模式cdc
- STM32_F4xx中文参考手册
深入学习?
鱼鹰的图解usb 图解USB (qq.com)
正点?野火?
看源码