一、前言
原來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;
}
}