STM32F103C8T6的USB—CDC虚拟端口组件(HAL)
常见的STM32USB端口是Micro-USB,Type-C,USB-BT型口,USB-B方口
我们最常见的32最小系统板上的USBD+和D-就接到了PA11和PA12单片机I/O端口上
新一版的小篮板STM32F103C8T6用的是Type-C,旧一版用的是Micro-USB,需要准备对应的线。
我们主要实现USB的虚拟串口的功能,用CubeMX新建工程例程很多,我只展示主要的部分
1.Connectivity中下拉找到USB,勾选Device(FS)设备全速模式,其他的保持默认
在Middleware and Software Packs(中间件和软件包)中找到USB_DEVICE,在 Class For FS IP 设备类别选择Communication Device Class(Virtual Port Com)虚拟串口。其他设置保持默认就行。
2.接下来进行时钟树配置,这是自动配置时钟树选项可以选择Yes让它帮你配,但是要注意配完后的时钟主频只有48,有要求的可以自己改到72M。
这是我的配置
后面的步骤和CubeMX新建工程一样,我就不说明了。
3.打开project工程
在这里我们要实现发什么回什么的类串口功能,该功能主要用到usbd_cdc_if.c和usbd_cdc_if.h这两个文件。
/** * @brief Data received over USB OUT endpoint are sent over CDC interface * through this function. * * @note * This function will issue a NAK packet on any OUT packet received on * USB endpoint until exiting this function. If you exit this function * before transfer is complete on CDC interface (ie. using DMA controller) * it will result in receiving more data while previous ones are still * not sent. * * @param Buf: Buffer of data to be received * @param Len: Number of data received (in bytes) * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL */ static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len) { /* USER CODE BEGIN 6 */ USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]); //将接收到的数据存到缓冲区 USBD_CDC_ReceivePacket(&hUsbDeviceFS); //准备主设备输出端口,进行数据发送 return (USBD_OK); /* USER CODE END 6 */ } /** * @brief CDC_Transmit_FS * Data to send over USB IN endpoint are sent over CDC interface * through this function. * @note * * * @param Buf: Buffer of data to be sent * @param Len: Number of data to be sent (in bytes) * @retval USBD_OK if all operations are OK else USBD_FAIL or USBD_BUSY */ uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len) { uint8_t result = USBD_OK; /* USER CODE BEGIN 7 */ USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData; //将USB当作类串口 if (hcdc->TxState != 0){ //如果串口发送内容不为0 return USBD_BUSY; //返回忙碌 } USBD_CDC_SetTxBuffer(&hUsbDeviceFS, Buf, Len); //发送内容 result = USBD_CDC_TransmitPacket(&hUsbDeviceFS); //将发送内容传给result /* USER CODE END 7 */ return result; }我们主要用到CDC_Receive_FS、CDC_Transmit_FS这两个函数
我们先用发送函数
编译后会出现函数隐式声明的警告,这是因为在#include "usb_device.h"头文件中并没有包含#include "usbd_cdc_if.h"头文件,所以我们要添加#include "usbd_cdc_if.h"头文件,再次编译就不会报警告了。
试验需要一根USB的线(要与自己的STM32USB口适配)、任意串口助手软件
可以按照图示连接,建议只先链接ST-LINK,将程序下载到STM32里,下载完成后断开ST-LINK,只用USB链接
打开设备管理器,检查USB是否正常连接,若没有则检查USB连接、STM32接口是否正常、电脑接口是否正常、USB驱动是否安装...
打开串口助手,我用的是微软商店自带的,选择与之对应的COM端口和115200波特率,可以看到成功接收到了信息。注意:因为是CDC虚拟串口波特率、停止位等设置对 USB CDC 是无效的
接下来我们改动回传函数达到发什么回什么的功能
在CDC_Receive_FS函数中加上CDC_Transmit_FS(Buf,*Len);这一句
注意: USBD_CDC_ReceivePacket(&hUsbDeviceFS);这个函数告诉 USB 控制器:“这包数据我处理完了,请准备好接收下一包”,如果漏掉这句,单片机只能收到电脑发来的第 1 包数据,之后就会“失联”,所以CDC_Transmit_FS(Buf,*Len);这一句应该加在USBD_CDC_ReceivePacket前面。
主函数什么都不写,编译下载,下载后拔掉ST-LINK接上USB,打开串口助手
可以看到成功发回了
注意事项:
1.不要在 CDC_Receive_FS 回调里加 HAL_Delay()、while() 死循环或 Flash/EEPROM 操作
该函数在中断上下文执行,阻塞会导致 USB 枚举超时、电脑端串口卡死。
2.漏写 USBD_CDC_ReceivePacket(&hUsbDeviceFS); 只能收到电脑发来的第 1 包数据,之后单片机“变聋”。
3.直接操作 Buf 指针而不拷贝 Buf 指向的是 USB 底层临时缓冲区。下一次接收会直接覆盖它。如需保留数据,必须 memcpy 到自定义数组。
4.在 main.c 里直接调用 CDC_Receive_FS它是底层自动触发的回调,手动调用会破坏 USB 状态机。
5.在使用DMA时要考虑缓冲区匹配问题。
链接: https://pan.baidu.com/s/1P-7RaN2Yy3CKXNVaj1LuuA?pwd=6524 提取码: 6524