STM32HAL----USB模擬串口(VPC)

      想要實現的功能是,USB模擬串口收發數據。串口助手發送數據至MCU,MCU接收後返回給串口助手。

      當初是想用標準庫做這個功能的。但是因爲後來瞭解到STM32CubeMX這個軟件,在嘗試之後實在是感覺,太方便了。所以,並沒有使用標準庫,而是直接用STM32CubeMX生成HAL庫的代碼用了。

(1)先點New Project,然後輸入自己的MCU型號

(2)配置引腳與外設

這裏我用的是ST-LINK進行DeBug,Tim5提供系統延時節拍,PE5與PB5點亮LED。而SysTick,用在FreeRtos提供系統節拍。

(3)時鐘樹配置

(4)配置外設

這個頁面可以對外設進行功能的設置,比如GPIO的輸出類型或者引腳初始電平。在這裏主要設置FreeRTOS,創建2個初始任務。其他的比如USB,默認就可以使用了。

(5)Poject Settings

這裏注意兩個地方,我使用的是MDK。所以IDE選項選擇的是MDK-ARM V5。CodeGenerator選項卡下,將Generated files下的第一個選項打上勾,這樣就會啓用模塊化編程,不同的外設封裝不同的.c   .h文件。至於Project Name跟Project Location,

自行設置便可。

然後,點擊STM32CubeMX主界面的Project,Generate Code。就能在我們指定的文件夾內直接生成工程文件。生成之後軟件提示你打開項目,點擊打開後,工程內分組如圖:

 

因爲使用了RTOS,所以編程主要圍繞兩個文件,“usbd_cdc_if.c”以及“freertos.c”

“usbd_cdc_if.h”添加一個USB管理結構體的定義,並將“usbd_cdc_if.c”中兩個定義移到“usbd_cdc_if.h”

/* USER CODE BEGIN PRIVATE_DEFINES */
/* Define size for the receive and transmit buffer over CDC */
/* It's up to user to redefine and/or remove those define */
#define APP_RX_DATA_SIZE  1000
#define APP_TX_DATA_SIZE  1000
	 
typedef struct  
{  
   uint8_t   OutFlag;
   uint8_t   EFlag[2];
   uint8_t   SFlag;  
   uint16_t  ReLen; 	
}USB_Dev; 	 
	 
/* USER CODE END PRIVATE_DEFINES */
/* USER CODE BEGIN INCLUDE */

“usbd_cdc_if.c”聲明USB管理結構體變量並賦值,且修改“CDC_Receive_FS”函數。

/* Private typedef -----------------------------------------------------------*/
USB_Dev  USB_S =
{
	0,
	{0x0D,0x0A},
	0,
	0,
};
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
	HAL_GPIO_TogglePin(GPIOE,GPIO_PIN_5); //開啓接收指示燈
	
	//將已接收數據長度賦值給USB_S.ReLen
        USB_S.ReLen += *Len;
	
	//判斷是否有結束標誌以及接收數據長度是否達到UserRxBufferFS長度上限
        if( USB_S.ReLen<APP_RX_DATA_SIZE && \
            UserRxBufferFS[USB_S.ReLen-2] != USB_S.EFlag[0] && \
	        UserRxBufferFS[USB_S.ReLen-1] != USB_S.EFlag[1]
	      ) 
		{
		  //設置下一次接收數據的位置
	        USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS + USB_S.ReLen);
	        USBD_CDC_ReceivePacket(&hUsbDeviceFS);   //準備接收數據
		}
		else  //長度達到,或者檢測到標誌位,觸發數據輸出
		{
		  USB_S.OutFlag = 1;
		}
		
        return (USBD_OK);
}

 

 

 

“freertos.c”中,添加頭文件“usbd_cdc_if.h”並在對應的任務中添加對應功能:

/* LED_Toggle function */
void LED_Toggle(void const * argument)
{

  for(;;)
  {
     HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_5);
     osDelay(500);
  }

}

extern USB_Dev  USB_S;
extern USBD_HandleTypeDef hUsbDeviceFS;
extern uint8_t UserRxBufferFS[APP_RX_DATA_SIZE];
/* USB_SendMess function */
void USB_SendMess(void const * argument)
{
   uint16_t timeout = 0xffff;
   uint8_t temp;
	
    for(;;)
   {
	 timeout = 0xffff;
		
	 if(USB_S.OutFlag)
	 {
	    temp = !(USB_S.ReLen%64);  //判斷長度是否爲64整數倍
	    while( CDC_Transmit_FS(UserRxBufferFS, USB_S.ReLen - temp) != USBD_OK && timeout--);
			 
          if(temp)  //當發送數據爲64整數倍時,無法發送成功,故分成2次發送
	    {
	       while( CDC_Transmit_FS(UserRxBufferFS + USB_S.ReLen -1, temp) != USBD_OK && timeout--);
	    }
				
	    USB_S.ReLen = 0;
	    USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS);
            USBD_CDC_ReceivePacket(&hUsbDeviceFS);
            USB_S.OutFlag = 0;	
            HAL_GPIO_WritePin(GPIOE, GPIO_PIN_5, GPIO_PIN_SET); //接收指示燈關閉				
	 }
    osDelay(1);
  }
}

 

啓動文件:需將堆的容量調大,因爲USB緩存使用的是堆空間。我將它調爲4K:

Heap_Size      EQU     0x1000

 

至此,代碼創建並修改完畢。

 

下載代碼到板子,下載完畢後關閉重上電,然後打開串口助手對應的串口。波特率之類全都不需設置。

發送一篇長文章,MCU接收後返回給串口助手

現象如圖:

 

 

 

寫在最後

(1)MCU端接收數據問題。

        USB傳輸一次最多64字節。所以,如果想一次傳輸大量數據給MCU。需制定協議。如圖的串口助手,在發送完畢數據之後,會補上2字節結束標誌:0x0D,0x0A。MCU端可根據結束標誌判斷數據是否接收完畢。需注意的是,不同的串口助手協議不一定相同。有的是根據數據包長度判斷是否接收完畢。

         

(2)連續輸出的問題。

        1、需要輪詢發送函數返回值是否是“USBD_OK”

        2、輸出字符串,如果不去掉字符串最後的結束符。某些上位機軟件,只能顯示1次發送的數據,第二次

             發送的數據不能正常顯示。(本博客使用的串口助手就是如此。。。。)  ,但使用過另外一款串口

             助手,並未發現此情況。    

        3、輪詢返回值有可能導致程序卡死,因爲主機若是沒有接收MCU發送的數據,MCU會一直輪詢

             直到主機接收完數據爲止,解決辦法是加個timeout--到while裏面,到時間跳出循環。

(3)WINDOWS下不能識別的串口有黃色感嘆號。

        這個有可能是堆設置不夠大。我在F4下遇到了這問題,將堆空間“Heap_Size      EQU     0x200”

        設置爲“Heap_Size      EQU     0x1000”,黃色感嘆號消失

  (4)通訊速度問題

       參考“http://bbs.21ic.com/icview-811704-1-1.html”,附件中有測試速度的軟件。

              串口助手當通訊速度40KB/S左右,接收不到數據。當然,如果是連續發送'0',串口助手上窗口不

       顯示字符,是可以接收到數據的,但當速度超過500KB/S,依舊不能接收數據。所以,網上很多反映

       VCP速度只有幾十KB/S的,估計是上位機軟件問題。

              使用附件中的軟件,可以成功測出速度。STM32CubeMX生成的程序,經測試發送到主機的速度可以

       達到1000KB/S以上。

      

(5)“CDC_Receive_FS”函數的解析

      這個函數的作用是兩個,一是設置下一次接收數據的Buff,二是處理接收端點。

      這個函數是在MCU接收完數據之後才調用的,而不是進入這個函數纔開始接收數據。比方MCU接收到64byte的數據,

      接收完成後進入這個函數,設置下一次接收數據的Buff。然後調用“USBD_CDC_ReceivePacket”處理接收端點。

      如果不調用“USBD_CDC_ReceivePacket”,是無法進行下一次的接收的,但發送是可以的。

(6)CDC_Transmit_FS 發送64整數倍字節數的數據出錯

      當我調用“CDC_Transmit_FS”發送64字節的數據時,串口助手並不能接收到數據。

      網上有帖子解釋了這種情況:http://bbs.21ic.com/icview-159300-1-1.html

 

      總的來說,USB的bulk協議以發送小於64字節或者是字長爲0的數據包作爲結束動作。

      發送64字節的包的時候,只需要末尾再發送個字長爲0的包即可。

 

 

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