一、前言
原来STM32USB开发很复杂,在标准库上移植USB库需要修改不少地方,但是现在用HAL库,配合CubeMX就能快速生成USB工程了,这里用STM32F1来实现Virtual_COM_Port虚拟串口。原理图如下,STM32F1的USB是USB2.0全速总线,所以DP上拉,DM不接上拉。
二、CubeMX配置
(1)mcu使用STM32F103C8
(2)RCC里高速和低速时钟都选择外部晶振Crystal
时钟树设置如下
(3)SYS调试口根据需要选择,这里选Serial Wire
(4)Connectivity-USB勾线Device(FS),端口默认PA12(USB_DP),PA11(USB_DM)
(5)Middleware-USB-DEVICE里Class For FS IP选择Communication Device Class(Virtual Port Com)
(6)Project Manager填一下工程名和路径,堆栈改大一点,因为有人说堆栈小了会出问题,不过我试了一下其实不改也能用。IDE根据需要选择,这里用Keil所以选MDK-ARM V5
(7)代码生成里勾选复制所有库到工程,外设初始化单独.c/.h文件,方便以后工程拷贝,最后点生成GENERATE CODE
(8)生成后打开工程,编译器改成 compiler version 6,这样编译速度能快几个世纪
(9)编译没错误后,下载到板子上,插上USB还无法识别,接下来就要安装STM32USB虚拟串口驱动
驱动已经打包上传,但是驱动安装有可能失败,博主就遇到了,可以根据以下办法解决
虚拟串口驱动安装失败解决办法:
将mdmcpq.inf复制到c:\windows\inf
将usbser.sys复制到c:\windows\system32\drivers
(10)驱动安装成功后,再插上USB,如果设备管理器端口里有Virtual COM Port,说明识别成功了,工程模板就算配置好了。
三、程序编写
现在只是识别串口,但是还没有功能,下面就来编写一下简单的功能
虚拟串口主要用到usbd_cdc_if.c里的两个缓存数组和收发函数。
uint8_t UserRxBufferFS[APP_RX_DATA_SIZE];//接收缓存
uint8_t UserTxBufferFS[APP_TX_DATA_SIZE];//发送缓存
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)//接收回调函数
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len)//发送函数
CDC_Transmit_FS是发送函数,指定数据首地址和字节长度,数据就会发送到串口,底层是USB库实现的。
CDC_Receive_FS是接收到收据后的回调函数,数据是收到一帧后才调用的CDC_Receive_FS,所以每次的字节长度不一定相同,传入的两个参数是数据缓存首地址和数据长度。
(1)数据回环
这里简单的将接收到的数据原样返回,实现数据回环,只需要在CDC_Receive_FS函数添加一行:
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
CDC_Transmit_FS(Buf,*Len);//添加数据原样返回
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
/* USER CODE END 6 */
}
下载到开发板,发送一定字节的数据会原样收到,注意注意发送字节长度不要超过缓存数组的最大长度1000,因为是虚拟的串口,所以波特率、停止位等其实可以随意设置,
(2)指令控制
前面说了CDC_Receive_FS是接收回调函数,可以把它当作一个串口空闲中断
定义一个结构体变量
typedef struct
{
volatile uint8_t RecFlag;
volatile uint16_t RecLen;
}RxDef;
extern RxDef USB_COM;
在usbd_cdc_if.c里接收到数据后标志置1,记录数据长度
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
USB_COM.RecLen = *Len;//数据长度
USB_COM.RecFlag = 1;//收到标志
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
/* USER CODE END 6 */
}
主函数while循环里,处理接收到的指令数据
收到指令01 08 00 01开灯,并返回02 08 00 01
收到 01 08 00 00关灯,返回02 08 00 00
if(USB_COM.RecFlag)//接收到数据
{
//4字节指令接收正确
if(USB_COM.RecLen==4 && UserRxBufferFS[0]==0x01 && UserRxBufferFS[1]==0x08 && UserRxBufferFS[2]==0x00)
{
if(UserRxBufferFS[3])
{
UserTxBufferFS[3]=UserRxBufferFS[3];
LED_ON;//开灯
}
else
{
UserTxBufferFS[3]=UserRxBufferFS[3];
LED_OFF;//关灯
}
CDC_Transmit_FS(UserTxBufferFS,4);//返回指令
USB_COM.RecFlag = 0;
}
}