STM32 使用CubeMX HAL庫快速生成USBVCP虛擬串口工程

一、前言

原來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;
			}
        }

四、下載

例子下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章