1. 開發環境
使用stm32f103c8t6,HAL庫,使用CUBE自帶的USB庫。工程使用vscode+gcc編譯,工程文件在文末鏈接下載,提供makefile和keil兩個版本。
2. 功能介紹
使用stm32 USB功能完成USB轉串口功能,使用引腳配置如下:
使用USART1作為調試信息輸出,USART2作為串口輸出,LED為系統指示。
3. CubeMX 配置
設置系統時鐘為72MHZ,調試串口UASART1波特率為921600(選擇高波特率,少占用中斷時間),USART2波特率默認為115200,開啟中斷。
選擇USB Device功能,速度為默認全速USB設備12MHZ,并使能USB_DEVICE庫,選擇Virtual Port Com (虛擬串口,VPC),使用默認配置。
?
設置系統時鐘為72MHZ,然后生成工程。
4.軟件部分
使用cube生成的代碼編譯下載后,將USB插入電腦,在電腦設備管理器中將顯示新的串口設備(使用STM32的USB VPC時需要對應的驅動程序,驅動在程序也在文末的鏈接中)
在串口調試助手中,可打開或關閉串口,不過此時還沒有任何功能。
4.1 發送函數
接下來進行功能配置,虛擬串口的主要配置代碼在 src->usbd_cdc_if.c中,其中幾個重要函數為:
?
?
static?int8_t?CDC_Control_FS(uint8_t?cmd,?uint8_t*?pbuf,?uint16_t?length); static?int8_t?CDC_Receive_FS(uint8_t*?Buf,?uint32_t?*Len); uint8_t?CDC_Control_FS(uint8_t*?Buf,?uint16_t?Len);
?
?
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len);函數用于向USB VPC發送數據,Buf為待發送的數據緩沖區地址,Len為待發送數據長度。
?
?
/*示例一,在main函數中的while循環中輸入下列代碼 ????????在連接打開串口助手后可接收到數據*/ while(1) { ????CDC_Control_FS((uint8_t?*)"hello ",6); ????HAL_Delay(1000); }
?
?
在編譯下載后,將USB插入電腦,使用川酷哦調試助手將會每秒接收到一次"hello"。
4.2 USB上電重新枚舉
在使用上面代碼的時候存在一個問題:每次下載完程序后都需要重新拔插一次USB才可以識別串口,這是由于芯片在下載完程序后沒有重新枚舉所導致的。需要在設備上電后對USB進行重新枚舉即可,使用方法為將USB DP(PA12)引腳拉低一段時間后即可。
?
?
/*USB?重新枚舉函數*/ void?USB_Reset(void) { ??????GPIO_InitTypeDef?GPIO_InitStruct?=?{0}; ????__HAL_RCC_GPIOA_CLK_ENABLE(); ????GPIO_InitStruct.Pin?=?GPIO_PIN_12; ????GPIO_InitStruct.Mode?=?GPIO_MODE_OUTPUT_PP; ????GPIO_InitStruct.Pull?=?GPIO_NOPULL; ????GPIO_InitStruct.Speed?=?GPIO_SPEED_FREQ_LOW; ????HAL_GPIO_Init(GPIOA,?&GPIO_InitStruct); ????HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_RESET); ????HAL_Delay(100); ????HAL_GPIO_WritePin(GPIOA,GPIO_PIN_12,GPIO_PIN_SET); }
?
?
該函數使用時應放在USB初始化之前,或者使用其他IO控制三極管拉低電平。
?
?
?/*?Configure?the?system?clock?*/ ??SystemClock_Config(); ??/*?USER?CODE?BEGIN?SysInit?*/ ??USB_Reset(); ??/*?USER?CODE?END?SysInit?*/ ??/*?Initialize?all?configured?peripherals?*/ ??MX_GPIO_Init(); ??MX_USART1_UART_Init(); ??MX_USB_DEVICE_Init(); ??MX_USART2_UART_Init();
?
?
重新下載上電后,可發現串口已重新枚舉識別,只需重新開啟串口調試助手即可。
4.3 USB接收函數
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)為USB接收回調函數,在USB VPC接收到數據時,會進入該函數,在該函數中進行USB數據接收處理即可。
USB轉串口設備,需要在stm32的USB端接收到數據后轉發到stm32 串口端
?
?
static?int8_t?CDC_Receive_FS(uint8_t*?Buf,?uint32_t?*Len) { ??/*?USER?CODE?BEGIN?6?*/ ??extern?UART_HandleTypeDef?huart2,huart1; ??/*將USB接收到的數據轉發到USART2*/ ??HAL_UART_Transmit_IT(&huart2,Buf,*Len); ?? ??USBD_CDC_SetRxBuffer(&hUsbDeviceFS,?&Buf[0]); ??USBD_CDC_ReceivePacket(&hUsbDeviceFS); ??return?(USBD_OK); ??/*?USER?CODE?END?6?*/ }
?
?
在stm32虛擬的串口中發送數據,可在stm32的USART2的TX引腳(PA2)收接收到數據。
4.4 設置USB虛擬串口波特率
在前面的發送和接收中,均不能進行波特率設置,usb發送到串口的數據波特率為默認值115200。USB的波特率配置在static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length)函數中,cmd為usb cdc的控制命令,pbuf為數據接收指針,length為數據長度。
當cmd為0x20時為設置虛擬串口波特率;
其接收到的數據一共七位,數據格式定義(小端模式)如下:
由于stm32芯片并不支持這么多串口參數,在無對應參數時使用默認配置。在接收到修改波特率命令后修改USART配置。
?
?
case?CDC_SET_LINE_CODING: { ??????extern?UART_HandleTypeDef?huart2; ??????huart2.Init.BaudRate=*((uint32_t*)pbuf); ??????switch?(pbuf[4]) ??????{ ??????case?2: ????????huart2.Init.StopBits=UART_STOPBITS_2; ????????break; ??????default: ????????huart2.Init.StopBits=UART_STOPBITS_1; ????????break; ??????} ??????switch?(pbuf[5]) ??????{ ??????case?1: ????????huart2.Init.Parity=UART_PARITY_ODD; ????????break; ??????case?2: ????????huart2.Init.Parity=UART_PARITY_EVEN; ????????break; ??????default: ????????huart2.Init.Parity=UART_PARITY_NONE; ????????break; ??????} ??????huart2.Init.WordLength=UART_WORDLENGTH_8B; ??????HAL_UART_Init(&huart2); } break;
?
?
配置完成后,在串口調試助手中修改波特率,可該改變對應串口數據輸出波特率,實測1.5M波特率可正常運行。
4.5 串口接收數據
在前面部分已經完成了USB轉串口的發送部分,還有USB轉串口的接收部分未完成。
該部分實現思路為在串口中斷中接收數據,然后將數據發送至USB。
不過由于USB協議并不是實時發送,經過測試兩次連續調用CDC_Transmit_FS小于100us將導致數據丟包,.并且由于USB緩沖區大小原因,一次性發送或接收大量數據將會嚴重丟包。
故使用循環隊列對發送接收數據進行緩沖,在發送和接收數據時先進入緩沖區,然后使用定每隔500us定時將緩沖區數據分包發送。
編輯:黃飛
?
評論
查看更多