1. 開發(fā)環(huán)境
使用stm32f103c8t6,HAL庫,使用CUBE自帶的USB庫。工程使用vscode+gcc編譯,工程文件在文末鏈接下載,提供makefile和keil兩個版本。
2. 功能介紹
使用stm32 USB功能完成USB轉(zhuǎn)串口功能,使用引腳配置如下:

使用USART1作為調(diào)試信息輸出,USART2作為串口輸出,LED為系統(tǒng)指示。
3. CubeMX 配置
設(shè)置系統(tǒng)時鐘為72MHZ,調(diào)試串口UASART1波特率為921600(選擇高波特率,少占用中斷時間),USART2波特率默認為115200,開啟中斷。

選擇USB Device功能,速度為默認全速USB設(shè)備12MHZ,并使能USB_DEVICE庫,選擇Virtual Port Com (虛擬串口,VPC),使用默認配置。

?

設(shè)置系統(tǒng)時鐘為72MHZ,然后生成工程。

4.軟件部分
使用cube生成的代碼編譯下載后,將USB插入電腦,在電腦設(shè)備管理器中將顯示新的串口設(shè)備(使用STM32的USB VPC時需要對應(yīng)的驅(qū)動程序,驅(qū)動在程序也在文末的鏈接中)

在串口調(diào)試助手中,可打開或關(guān)閉串口,不過此時還沒有任何功能。
4.1 發(fā)送函數(shù)
接下來進行功能配置,虛擬串口的主要配置代碼在 src->usbd_cdc_if.c中,其中幾個重要函數(shù)為:
?
?
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);函數(shù)用于向USB VPC發(fā)送數(shù)據(jù),Buf為待發(fā)送的數(shù)據(jù)緩沖區(qū)地址,Len為待發(fā)送數(shù)據(jù)長度。
?
?
/*示例一,在main函數(shù)中的while循環(huán)中輸入下列代碼
????????在連接打開串口助手后可接收到數(shù)據(jù)*/
while(1)
{
????CDC_Control_FS((uint8_t?*)"hello
",6);
????HAL_Delay(1000);
}
?
?
在編譯下載后,將USB插入電腦,使用川酷哦調(diào)試助手將會每秒接收到一次"hello"。
4.2 USB上電重新枚舉
在使用上面代碼的時候存在一個問題:每次下載完程序后都需要重新拔插一次USB才可以識別串口,這是由于芯片在下載完程序后沒有重新枚舉所導致的。需要在設(shè)備上電后對USB進行重新枚舉即可,使用方法為將USB DP(PA12)引腳拉低一段時間后即可。
?
?
/*USB?重新枚舉函數(shù)*/
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);
}
?
?
該函數(shù)使用時應(yīng)放在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();

?
?
重新下載上電后,可發(fā)現(xiàn)串口已重新枚舉識別,只需重新開啟串口調(diào)試助手即可。
4.3 USB接收函數(shù)
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)為USB接收回調(diào)函數(shù),在USB VPC接收到數(shù)據(jù)時,會進入該函數(shù),在該函數(shù)中進行USB數(shù)據(jù)接收處理即可。
USB轉(zhuǎn)串口設(shè)備,需要在stm32的USB端接收到數(shù)據(jù)后轉(zhuǎn)發(fā)到stm32 串口端
?
?
static?int8_t?CDC_Receive_FS(uint8_t*?Buf,?uint32_t?*Len)
{
??/*?USER?CODE?BEGIN?6?*/
??extern?UART_HandleTypeDef?huart2,huart1;
??/*將USB接收到的數(shù)據(jù)轉(zhuǎn)發(fā)到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虛擬的串口中發(fā)送數(shù)據(jù),可在stm32的USART2的TX引腳(PA2)收接收到數(shù)據(jù)。

4.4 設(shè)置USB虛擬串口波特率
在前面的發(fā)送和接收中,均不能進行波特率設(shè)置,usb發(fā)送到串口的數(shù)據(jù)波特率為默認值115200。USB的波特率配置在static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length)函數(shù)中,cmd為usb cdc的控制命令,pbuf為數(shù)據(jù)接收指針,length為數(shù)據(jù)長度。
當cmd為0x20時為設(shè)置虛擬串口波特率;

其接收到的數(shù)據(jù)一共七位,數(shù)據(jù)格式定義(小端模式)如下:

由于stm32芯片并不支持這么多串口參數(shù),在無對應(yīng)參數(shù)時使用默認配置。在接收到修改波特率命令后修改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;
?
?
配置完成后,在串口調(diào)試助手中修改波特率,可該改變對應(yīng)串口數(shù)據(jù)輸出波特率,實測1.5M波特率可正常運行。

4.5 串口接收數(shù)據(jù)
在前面部分已經(jīng)完成了USB轉(zhuǎn)串口的發(fā)送部分,還有USB轉(zhuǎn)串口的接收部分未完成。
該部分實現(xiàn)思路為在串口中斷中接收數(shù)據(jù),然后將數(shù)據(jù)發(fā)送至USB。
不過由于USB協(xié)議并不是實時發(fā)送,經(jīng)過測試兩次連續(xù)調(diào)用CDC_Transmit_FS小于100us將導致數(shù)據(jù)丟包,.并且由于USB緩沖區(qū)大小原因,一次性發(fā)送或接收大量數(shù)據(jù)將會嚴重丟包。
故使用循環(huán)隊列對發(fā)送接收數(shù)據(jù)進行緩沖,在發(fā)送和接收數(shù)據(jù)時先進入緩沖區(qū),然后使用定每隔500us定時將緩沖區(qū)數(shù)據(jù)分包發(fā)送。
編輯:黃飛
?
電子發(fā)燒友App





















評論