1. 程式人生 > >STM32F4discovery_CDC_Device資料傳輸的一步步實現(USB2.0FS)

STM32F4discovery_CDC_Device資料傳輸的一步步實現(USB2.0FS)

1. 需求

某專案需要微控制器把ADC資料上傳到電腦。方法有兩種:1、USB;2、乙太網。百度必應了一番,發現有人用過NXP的LPC的一款帶USB2.0 HighSpeed phy的片子,但是採用BGA封裝,開發難度上了一個層次。這兩年內使用的STM32F1、F4比較多,對其開發比較熟悉,資料也豐富。因此決定使用STM32F4跑一下CDC_Device例程,調一下這部分的資料傳輸。

2. 環境

2.1 軟體

Win7、Atollic TrueSTUDIO for STM32

STM32CubeMX v4.27.0,裝上STM32CubeF4 Firmware Package V1.21.0

2.2 硬體

我採用的是STM32F4discovery。也可以是STM32F4的開發板。

3. 試驗

3.1 使用STM32Cube生成專案

需要用到的IO口資源:連線4個指示燈的GPIO(PD12-PD15)。連線了按鍵的PA0(但實際未用到)。連線了外部8MHz晶振的PH0和PH1。連線到USB插座的PA11和PA12。連線到STLINK模擬器的PA13、PA14。

3.1.1 晶振

Peripherals中需要配置RCC,STM32F4discovery未焊接外部低速晶振。因此LSE預設disable,而有8MHz晶振,因此配置為Crystal/Ceramic。

3.1.2 模擬/下載

由於使用的是STLINK(STM32discovery板載stlinkv2),配置debug模式為Serial Wire。

3.1.3 USB裝置

STM32F407VG晶片支援USB2.0 Fullspeed。但是usb2.0 high speed需要外接控制器如usb3300。這裡的方式是STM32F407VG的引腳pa11和pa12經過電阻直接連線USB插座。需要配置為usb_device_fullspeed。

配置USB_OTG_FS如下:PC為usb host,而STM32作為usb device。

選好了device_only後,Configuration欄的USB_DEVICE即有效。把USB_DEVICE中,配置Class For FS IP為Communication Device Class(CDC)。

3.1.4 時鐘樹配置

STM32F407使用外部的8MHz晶振,Input frequency輸入8後,選選單欄中的clock Configuration -> Resolve Clock Issues即可自動為晶片內部的各個模組配置好時鐘頻率。這裡需要注意,STM32F4內建的usb controller時鐘需要48HMz才能正常工作。

在這個頁面沒有紅色字型後,這個頁面的配置也就完成了。

3.1.5 內部模組配置

這個頁面的各個部分採用預設值就可以了。不需要改動。(這也是我第一次跑這個demo,儘量根據前人的步驟來就不怕出錯了)

3.1.6 生成程式碼

生成程式碼前需要選擇project-> Settings配置工程目錄以及IDE。記得把mimimum heap size和mimmum stack size提高。有網友反映這個size低了是會出error的。我把兩個size都改大了,分別設為0x600和0x1000。其他預設,點選OK。

然後點Project -> Generate Code。工程初始化已經完成。

4. 使用者程式碼修改

在main.c的合適區域增加以下程式碼:

4.1  引用標頭檔案以及引數宣告

顧名思義,第一個引數是接受資料,雙緩衝陣列結構。第二個引數是傳送資料。cdc每接收一個包,會重新整理接收資料。需要把其當前資料包的長度記錄。

#include "usbd_cdc_if.h"

extern uint8_t UserRxBufferFS[2][2048];
extern uint8_t UserTxBufferFS[2048];

extern uint32_t nRxLength;		//// 接收到的資料長度
extern uint8_t uRxBufIndex;		//// 當前使用的緩衝區索引號
extern uint8_t  uLastRxBufIndex;//// 上次使用的接收緩衝區索引號
int bSendMark = 0;				//// 傳送資料的標誌

4.2 main函式內的引數初始化區域

我想知道上傳到電腦的資料是否準確連續。故自行按順序初始化了傳送陣列中的每個數值。


  for(i=0;i<APP_TX_DATA_SIZE;i++)
  {
	  UserTxBufferFS[i]=i;
  }

4.3 main函式內的死迴圈

HAL_GPIO_TogglePin(LD4_GPIO_Port, LD4_Pin);

// 每次CDC_Itf_Receive()接收到新資料都會更換快取,所以發現快取切換過就是有新的資料。
// 這裡必須保證資料處理的速度足夠快,否則快取切換了兩次才處理的話,就沒法識別有新資料到來了。
if (uLastRxBufIndex != uRxBufIndex)
{
	// --> 指令譯碼開始。
	for (i=0; i<nRxLength; i++)
	{
		if (UserRxBufferFS[uLastRxBufIndex][i] == 0x55)	// 0x55, 開始傳送資料指令
			bSendMark = 1;
		if (UserRxBufferFS[uLastRxBufIndex][i] == 0xAA)	// 0xAA, 停止傳送資料指令
			bSendMark = 0;
	}
	// <-- 指令譯碼結束。
	uLastRxBufIndex = (uLastRxBufIndex + 1) & 1;
}

if (bSendMark)
{
	int32_t k = 0;
	while (CDC_Transmit_FS(UserTxBufferFS, APP_TX_DATA_SIZE) != USBD_OK)
	k++;
}

// 處理接收到的資料:解碼資料流中的指令。可以改成你自己的資料處理過程。
// 每次CDC_Itf_Receive()接收到新資料都會更換快取,所以發現快取切換過就是有新的資料。
// 這裡必須保證資料處理的速度足夠快,否則快取切換了兩次才處理的話,就沒法識別有新資料到來了。
if (uLastRxBufIndex != uRxBufIndex)
{
	// --> 指令譯碼開始。
	for (i=0; i<nRxLength; i++)
	{
		if (UserRxBufferFS[uLastRxBufIndex][i] == 0x55)	// 0x55, 開始傳送資料指令
			bSendMark = 1;
		if (UserRxBufferFS[uLastRxBufIndex][i] == 0xAA)	// 0xAA, 停止傳送資料指令
			bSendMark = 0;
	}
	// <-- 指令譯碼結束。
	uLastRxBufIndex = (uLastRxBufIndex + 1) & 1;
}
if (bSendMark)
{
	int32_t k = 0;
	while (CDC_Transmit_FS(UserTxBufferFS, APP_TX_DATA_SIZE) != USBD_OK)
	k++;
}

5 編譯錯誤

5.1 錯誤1

..\Src\usbd_cdc_if.c:201:39: warning: passing argument 2 of 'USBD_CDC_SetRxBuffer' makes pointer from integer without a cast [-Wint-conversion]
   USBD_CDC_SetRxBuffer(&hUsbDeviceFS, UserRxBufferFS[0]);

STM32CubeMX自動生成的UserRxBufferFS是1維陣列,我們的是2維資料。

因此需要把

uint8_t UserRxBufferFS[APP_RX_DATA_SIZE];

改為:

uint8_t UserRxBufferFS[2][APP_RX_DATA_SIZE];

6 試驗結果

硬體照:

紅色USB線用於下載,模擬除錯,stlink。下面的黑色USB線用於和電腦傳輸使用者資料,CDC。

下載好程式後,我的電腦可以自行下載了VCP驅動。可以看到虛擬出串列埠26。

使用網友提供的上位機軟體。測得傳送速度為828.48KB/s。接收速度為837.29KB/s。

開啟XCOM串列埠除錯助手。

往STM32傳送55,STM32接收到0x55後,會把傳送資料內的資料上傳到電腦。然後對STM32傳送0xaa即可停止STM32的傳送。

可以看到,資料是0x00-0xff,試驗中沒看到明顯的丟包、以及誤碼。

7. 小結

本文記錄了基於STM32F4的USB2.0FS資料傳輸的試驗過程。採用了最新的HAL庫,好在網友提供了不少資料。昨天蒐集資料,今天上機,現在是中午12:00。已經實現了demo功能。在此特別感謝http://bbs.21ic.com/icview-811704-1-1.html。