目錄:
第1部分:參照“正點原子USB虛擬串口工程移植步驟”移植ST的USB HID工程(失敗了);
第2部分:在1的基礎上,替換USB HID初始化代碼為ST 例程中的代碼,編譯后根據報錯調試(失敗了);
第3部分:直接移植ST的USB HID工程,根據報錯調試代碼(成功了)。
小孫想要總結這一年來學到的關於stm32的USB相關知識,但又不知道怎么總結,於是決定
從頭
開始調試固件庫代碼,直到實現USB功能為止!
首先准備參照正點原子《第88講 USB虛擬串口實驗-M3》,把HID相關庫包含進工程中,工
程選用
正點原子的USART試驗。
因為目前手里的開發板是“微雪電子”的stm32f103cbt6開發板,
其
硬件串口為:USART1(PA9和PA10)和
SART2(PA2和PA3)。准備先把正點原子《實驗4-串口
實驗》實例代碼移植
到這款開發板上,好方便以后調試使用。
因為正點原子stm32f1開發板使用的晶振和手里的開發板一樣都是8M的,所以在Keil里直接修
改MCU
型號和Flash大小后准備編譯下載。沒有報錯。

查看代碼中串口初始化函數,發現代碼中如下語句:

是使用串口1,於是把開發板上的USART1插上USB轉
串口模塊。准備下載后調試。

插上模塊后開始下載,結果發現不能下載,報錯信息如下圖所示:

猜測可能是串口模塊干擾到SWD下載,於是把串口模塊拔掉。結果發現開發板沒電了。
也就是說,剛剛是靠串口模塊供電的。即剛剛也可能是MCU供電不足導致無法下載。於是
先撥動開關,用外接電源給開發板供電,

然后再重新燒錄,結果一次就成功了。

下面先把USB轉串口模塊插回開發板的USART1,測試串口功能是否正常。
打開串口調試工具,選擇串口后,點擊“打開串口”,串口調試助手就開始接受數據了。

也就是說,正點原子的stm32f1串口代碼只要在Keil中修改MCU和flash大小后,可以在微雪
開發板上直接使用。
接下來就要准備開始USB相關的工程了。
《第1部分:
參照“正點原子USB虛擬串口工程移植步驟”移植ST的USB HID工程(失敗了)
》
這里准備大致分為四布調試:
第一步:了解正點原子的USB虛擬串口實現步驟(不需要記得,大致看一下,需要時再
回頭仔細看);
第二步:下載正點原子的USB虛擬串口代碼到微雪開發板,看是否能正常工作。如果能
正常工作,則可以輕松地執行第三步;否則需要先查找原因,直到可以正常工作時,再執
行第三步;
第三步:不管第二部分是否正常工作,都需要親自按照第一步的步驟重新移植一遍,為
移植HID做准備;
第四步:參照第三步的步驟,移植ST官方庫中的HID代碼到微雪開發板。
---------------
第一步: (不想看可以先跳過去,直接看第二步)
----------
正點原子視頻教程中USB虛擬串口實現步驟如下:
正點原子f1開發板中USB硬件用的是PA11和PA12。微雪電子開發板中用的也是PA11
和PA12。查看f103收據手冊發現,這款MCU就只有這一個USB接口,所以不用擔心搞錯
接口了。正點原子和微雪開發板上都是在USB_D+上接1.5k上拉電阻,說明都定義此USB
是高速設備。
下面是《第88講 USB虛擬串口試驗-M3》PPT中移植ST官方的Virtual_COM_Port例程的過程:




















《stm32f1開發指南-庫函數版本》中USB虛擬串口的實現步驟如下:

















---------------
第一步結束
------------------------------------------------------------------------
---------------
第二步:
---------------------------------------------------------------------------
好了,下面是第二部分,直接下載正點原子開發板的程序,看是否能正常用,如果能
正常工作,則可以放心的執行第三步;否則,需要先查找到原因后再執行第三步。
為了避免液晶和串口功能的干擾,我們先屏蔽掉代碼中的液晶和
串口部分的功能。
下載后實現效果如下:



很幸運,微雪開發板可以直接實現虛擬串口功能,可以直接進入第三步了。
(注:代碼見附件:第二步:實驗48 USB虛擬串口實驗 _微雪開發板)
--------------- 第二步結束---------------------------------------------------------------------------
---------------
第三步:
-----------------------------------------------------------------------------
親自按照第一步的步驟重新移植一遍虛擬串口的代碼
移植后編譯,結果
報錯如下:

在第二步和第三步中的程序中分別查找“ EXTI_ClearITPendingBit”和“EXTI_Init”兩個變量。
第二步程序中搜索結果:


第三步程序中搜索結果:


從上面結果看,第三步中工程缺少庫文件:stm32f10x_exti.c。於是在Keil中把這個文件包含進去,
重新編譯,結果如下:

把代碼下載到微雪電子開發板中,希望能直接成功。
結果如下:

看來電腦識別到USB虛擬串口了。
(注:代碼見附件:第三步:實驗4 串口實驗 _修改為VCP.rar)
--------------- 第三步結束---------------------------------------------------------------------
--------------- 第四步---------------------------------------------------------------------------
移植Custom_HID代碼到正點原子的串口代碼中,希望一切順利。
參照PPT《第88講 USB虛擬串口實驗-M3》,開始進行下列移植:
1.打開ST官方的Custom_HID例程,如下圖所示


2.移植USB通信需要的從機驅動代碼;
3.打開USUART通信實驗,拷貝USB從機驅動代碼;

4.打開
USART試驗工程,添加USB相關代碼,如下圖所示:

突然發現PPT中有下列一句話:
④, 修改usb_endp.c。

原來第三步中剛開始調試報錯,是因為沒看到這里的"添加stm32f10x_exti.c"。
還是太粗心了。
5.編譯代碼,根據報錯提示,修改相關內容。

①, platform_config.h, include部分, 使用如下代碼替代:
#include "sys.h"
另外, 去掉#define USE_STM32303C_EVAL等宏定義, 無需使用
②,修改hw_config.c。
2.1,去掉stm32_it.h,並添加一些其他頭文件,如下:
#include "usb_lib.h" #include "usb_prop.h" #include "usb_desc.h" #include "usb_istr.h" #include "hw_config.h" #include "usb_pwr.h" #include "usart.h" #include "string.h" #include "stdarg.h" #include "stdio.h"
因為HID例程中此處沒有stm32_it.h,所以直接替換成上面的代碼。
2.2,去掉HSEStartUpStatus和EXTI_InitStructure等結構體和變量的定義,
, 采用如下代碼替代 :

因為替換的代碼是串口相關的,所以此時不知道是否應該替換了!!!
那就先編譯一下,根據報錯提示修改看看是否能直接運行。
編譯結果如下:

發現有很多錯誤,是跟評估板硬件相關的內容,應該是哪個宏定義沒正確使用。
這樣一個一個修改太麻煩了,還是先根據PPT《第88講 USB虛擬串口實驗-M3》,
接着把" 能修改的部分 "修改了再根據情況調試。注:2.2這節暫時不修改了。
2.3,刪掉Set_System函數
並添加USBWakeUp_IRQHandler和USB_LP_
CAN1_RX0_IRQHandler函數(官方例程是在stm32_it.c里面, 我們將其移到
這里) , 代碼如下
//USB喚醒中斷服務函數 void USBWakeUp_IRQHandler(void) { EXTI_ClearITPendingBit(EXTI_Line18);//清除USB喚醒中斷掛起位 } //USB中斷處理函數 void USB_LP_CAN1_RX0_IRQHandler(void) { USB_Istr(); }
2.4,修改Set_USBClock函數為:
2.5,修改Enter_LowPowerMode函數為:
2.6,修改Leave_LowPowerMode函數為:
2.7,修改USB_Interrupts_Config函數為:
2.8,修改USB_Cable_Config函數為:
2.9,刪除USART_Config_Default函數,新增USB_Port_Set函數,代碼如下:
因為當前例程中沒有虛擬串口,所以不用刪除,直接添加USB_Port_Set函數即可。
2.10,修改USART_Config函數為:
(和串口相關,不用管):
2.11,修改USB_To_USART_Send_Data函數為
:
(和串口相關,不用管):
2.12,
刪除Handle_USBAsynchXfer和USART_To_USB_Send_Data這兩個
函數, 然后, 新增USB_USART_SendData函數, 用於虛擬串口發送一個字節
到USB(這里實際上只寫到了發送FIFO, 最終還是由EP1_IN_Callback函數實
現輸出給USB):
(和串口相關,不用管):
2.13, 刪除IntToUnicode函數前面的static關鍵字,
新增usb_printf函數, 用於
實現USB虛擬串口的printf, 代碼如下: (和串口相關,不用管):
2.14, 修改hw_config.h, 刪除MASS_MEMORY_START等宏定義, 然后, 新
增USB_USART_TXFIFO_SIZE等宏定義和結構體, 代碼如下:
(和串口相關,不用管):
2.15, 修改hw_config.h, 新增IntToUnicode、
USB_Port_Set
USB_USART_SendData、
和usb_printf等函數的聲明, 代碼如下: (和串口相關,不用管):
4.
1, 修改EP1_IN_Callback函數為: (和串口相關,不用管):
4.2, 修改SOF_Callback函數為:
(工程中沒有這個函數的定義,直接從虛擬串口的工程中復制過來):
⑤, 修改usb_prop.c。 本例程沒有用到USART_Config_Default函數, 所以:
注釋掉Virtual_Com_Port_init函數里面對該函數的調用
:(工程中沒有這個函數,不用管):
⑥, 修改usb_pwr.c。 修改Suspend函數為:
6, 修改main.c。
因為虛擬串口例程中的main.c里面執行的是串口收發,所以這里只用其初始化部分,while(1)
里面什么也不做。結果編譯后報錯如下:

還是很多宏定義沒有實現。接下來需要找到他們,定義他們或者刪除他們。
第一個報錯:
RCC_APB2Periph_GPIO_DISCONNECT未定義。定位到函數如下:
void GPIO_Configuration(void)
但是程序中並未調用它,所以可以直接屏蔽掉。
重新編譯后,第一個報錯:
KEY_BUTTON_EXTI_LINE未定義。定位到函數如下:
void EXTI_Configuration(void)
重新編譯后,第一個報錯:
ADC1_DR_Address未定義。定位到函數如下:
void ADC_Configuration(void)
但是程序中並未調用它,所以可以直接屏蔽掉。
重新編譯后,第一個報錯:
LED1未定義。定位到函數如下:
void EP1_OUT_Callback(void)
自定義,於是把里面原來開發板上的功能(主要是開關LED)屏蔽掉。只接收數據,
不做處理。
重新編譯后,第一個報錯:LED1未定義。定位到函數如下:
第一個報錯:還是
LED1未定義。定位到函數如下:
void CustomHID_Status_In(void)
該函數的功能是:狀態輸入。雖然不知道具體是做什么的,但是根據網上看過
的HID程序資料,這個函數不能刪除。於是只屏蔽掉里面關於LED等信息的語句。
重新編譯后,只有3個錯誤了:

第一個報錯: bDeviceState 未定義。定位到函數如下:
void SOF_Callback(void)
關於這個函數,網上找到下列解釋:
函數SOF_Callback定時查詢用戶是否有要發送的數據,如果有則進行發送,
在
發送完成后會觸發發送中斷EP1_IN_Callback函數,如果發送完畢就不調用
SetEPTxValid(ENDP1)函數,發送完成后就不會再觸發EP1_IN_Callback函數。
所以就不刪除了。
bDeviceState在代碼中肯定是定義過了的,應該是被屏蔽了。
搜索變量
bDeviceState,看它的定義在什么地方,為什么
被屏蔽了。結果如下:

看來確實定義過,還在頭文件中聲明過可以外部調用。那應該是這個頭文件
沒有被usb_endp.c包含導致的。打開usb_endp.c查看,果然沒有這個頭文件。
加上后,編譯結果如下:

很多關於usb_prop.h的錯誤,這個是才加進去的頭文件,卻導致個能多錯誤,
應該是加的位置不對。打開看到usb_endp.c開頭只有幾個頭文件:
#include "hw_config.h" #include "usb_lib.h" #include "usb_istr.h" #include "usb_prop.h"
干脆直接把USB虛擬串口相同文件中包含的頭文件都包含
進來編譯看看。結果如下:

老規矩,全局搜索變量“
VCOMPORT_IN_FRAME_INTERVAL”,結果如下:

竟然發現代碼中真的沒有定義過。因為這個代碼是從USB虛擬串口拷貝過來的,在
USB虛擬串口代碼中全局搜索,結果如下:

直接在usb_endp.c開頭加上這個宏定義,重新編譯結果如下:

直接點擊錯誤信息,不能跳轉,
全局搜索PrevXferComplete,結果如下:

發現竟然真的沒有定義。這部分是ST官方庫中的代碼,直接打開ST的HID工程,
全局搜索,發現是在main.c開始位置定義的。於是我們也在main.c處定義這個變量,
編譯后結果如下:

ADC_SoftwareStartConvCmd好像是和串口有關,但沒有直接證據,全局搜索發現

也就是說,這個函數只有聲明,沒有定義。
同理,在ST官方庫中HID工程中搜索,結果如下:

原來,這個函數在adc庫文件中。而我的工程還沒有添加這個庫文件。

添加后編譯,結果如下:

沒有報錯,很好,但是下載后結果如何還不知道,因為剛剛在ST的HID工程中看到main函數
初始化時的步驟和這里從USB虛擬串口移植過來的不一樣。不過還是先試一下看看。
開始下載......
微雪開發板上電后結果如下:

太神奇了,雖然初始化代碼和ST工程不一樣,但竟然一下子就識別成HID了!!!

趁熱打鐵,接下來就是實現HID發送和接收功能,否則小孫的三分鍾熱性一旦消失,就要再
耗費半天才能繼續下去了。
剛才細看USB虛擬串口代碼,發現其發送和接收功能其實和網上的HID類似,都是調用
UserToPMABufferCopy()和
PMAToUserBufferCopy()函數。而這兩個函數分別有另一個函
數調用,即
USB_SIL_Write()和
USB_SIL_Read()。在ST的HID工程中也有這兩個函數,這
里先實現他們看看是否有效果。

添加上面的代碼,編譯下載后結果如下:

一直沒有數據發送上來。看來需要讓開發板在調試模式運行,看是否是卡在哪里了。
結果調試模式下發現,代碼一直在循環執行,沒有卡頓問題。就是說,發送代碼執行了,
但是數據沒有發送出去。
打開USB的配置描述符發現,默認最大發送長度為2字節,而小孫的發送函數一次發送
3字節,需要修改一下。
修改之后,編譯下載,結果還是和上面一樣,不能發送數據。
把單片機多功能調試助手打開,打開開發板的端口檢測。結果仍然沒有數據發送。
把串口初始化代碼屏蔽掉時,PC端反而不能識別HID了。

分析uart_init()函數,沒發現什么特別功能的語句。屏蔽掉它,繼續調試。
但是目前進度卡住了,不知道怎么向下調試了。
這種情況,一年前剛剛學習stm32 USB時就遇到過,后來是別人替小孫
調好的。
當時小孫調了接近1個月,都沒有正常收發數據,后來覺得自己短時間內調
不出來
了跟領導說讓其重
新招人,自己准備辭職的。后來是領導花兩三天幫他調試出來的。
雖然當時已經有兩年半工作經驗了,但那幾天都在想自己是否真的適合走嵌入式這條路???
因為沒有刨根問底的精神,小孫這幾年的電子生涯一直是閉着眼睛瞎混的,沒有
好好的鑽研讓自己拿得出手的技術,所以每天都生活在自卑和失落中。
綜上所述,在屏蔽掉uart_init()函數后,程序並不能被PC識別為HID設備。
《第2部分:在1的基礎上,替換USB HID初始化代碼為ST 例程中的代碼,編譯后根據報錯調試(失敗了)》
俗話說,人不能讓不撞南牆不回頭,這里是裝了南牆必須回頭,否則沒有哪個
老板願意給你薪水。所以,小孫准備從ST的HID工程本身提供的初始化HID方式
嘗試實現HID功能。畢竟,網上大多數HID工程初始化部分都和ST的HID部分類似,
而跟他上面自己移植的不一樣。
****************** 根據ST的HID修改工程代碼 ***********************
預想中的修改步驟如下:
第一步:先用HID工程中的初始化代碼替換上面工程中的初始化代碼;
第二步:根據報錯,修改上面步驟中被修改過的相關函數,使HID初始化成功;
第三步:添加HID發送和接收函數,並實現功能;
---------------
第一步:
-----------------------------------------------------------------------------
移植ST的HID工程初始化代碼后編譯,結果如下:

發現報錯中提到的函數是上面在hw_config.c中屏蔽掉的,於是釋放開。重新編譯后
報錯如下:

發現其中大都是跟具體硬件相關的。
全局搜索第一處的未定義變量,發現在platform_config.h中有兩處根據開發板
類型分別定義的
變量。

在STM3210E_EVAL開發板原理圖上發現,上面的GPIO_DISCONNECT是
指接在USBDP上的上拉電阻控制線。

因為微雪電子開發板上USBPD上的上拉電阻通過三極管直接拉高了,所以這里
不用管,直接屏蔽掉。還有下面的配置USB_DISCONNECT_PIN的
GPIO_Configuration函數也一起屏蔽掉,再編譯,結果如下:

全局搜索“RCC_AHBPeriph_GPIOA”,結果如下:

並沒有找到其定義,在ST的HID官方庫中搜索結果如下:

即,其定義在stm32f30x_rcc.h中,文件對應MCU型號不對。在stm32f10x_rcc.h中
搜索GPIOA的時鍾外設對應的變量,有如下發現:

用RCC_APB2Periph_GPIOA替代上面的RCC_AHBPeriph_GPIOA,重新編譯結果如下:

下載后,PC端顯示如下:

調到這一步,小孫已經失去耐心了,不願意在USB HID的工程上花時間調試了。因為網上的HID
都是從stm32手柄工程修改過來的,准備第二天換成JoyStickMouse工程,從上面開始調試。

------------------------------------------------------ 兩天后 ------------------------------------------------------------
《第3部分:直接移植ST的USB HID工程,根據報錯調試代碼(成功了)》
小孫經過重新思考,准備重新從ST的HID庫直接修改,而不是先參考正點原子USB虛擬串口,把里面
的相關函數修改了。如果還是不成功,再參考網上從“ JoyStickMouse”修改。
把ST的HID代碼添加到正點原子的串口實驗代碼中,修改main.c中初始化語句為ST的HID代碼,
屏蔽掉原串口代碼並編譯。
結果如下:


缺少對開發板的宏定義,這里直接用“#include "stm32f10x.h"”替換。編譯結果如下:

“usb_type.h”在移植過來的USB庫中,直接用"
#include "../STM32_USB-FS-Device_Driver/inc/usb_type.h"
"包含進來。
重新編譯后結果為:

仿照上面操作,添加把“usb_lib.h”包含進來,重新編譯后結果為:

還有下列文件需要包含這些頭文件,一並處理了。
stm32_it.c,hw_config.c,usb_desc.c,usb_pwr.c,usb_endp.c,usb_prop.c
還有STM32_USB-FS-Device_Device\sr\usb_core.c,usb_init.c,usb_int.c,
usb_regs.c,usb_mem.c,usb_sil.c需要用“../inc/usb_lib.h”包含“usb_lib.h”文件。
編譯后報錯:

說明上面添加的頭文件正確。接下來需要屏蔽掉與具體硬件相關的代碼。定位到
“KEY_BUTTON_EXTI_LINE”,如下:

這個是開發板的按鍵中斷處理函數,直接屏蔽掉。
重新編譯,報錯如下:

第一個報錯:
RCC_APB2Periph_GPIO_DISCONNECT在上面看過了,是
USBDP的上拉電阻,微雪開發板不需要上拉電阻,是直接拉高的,屏蔽掉。
還有,stm32f103的usb管腳不需要配置,當設置好usb時鍾后,直接就用作usb功能了。
Set_System函數中把他們
屏蔽掉,還有按鍵,led,adc功能都屏蔽掉。
這樣,Set_System()函數的內容都屏蔽掉了。
重新編譯,報錯如下:

第一個報錯“STM_EVAL_LEDOn”在函數CustomHID_Status_In()"中,其功能是
控制LED的,直接屏蔽掉內容,保留外殼。
重新編譯,報錯如下:


根據上面信息,這里直接屏蔽GPIO相關的函數內容,保留外殼,重新編譯報錯信息如下:

還是按鍵燈功能,直接屏蔽函數內容,保留外殼。
重新編譯,報錯如下:


屏蔽掉部分函數體,保留外殼及部分功能,因為這個函數中需要把PC發來的數據接收下來,
以后才能繼續接收數據。

編譯結果如下:

移植來的stm32_it.c里面有很多中斷處理函數和stm32f10x_it.c重復,直接屏蔽掉重復部分。
重新編譯,報錯信息如下:

主要是hw_config.c、stm32_it.c,和usb_prop.cADC和DMA功能,直接屏蔽掉。
至於EXTI_ClearITPendingBit,如下所示,屏蔽掉宏定義的限制。

至於保留哪一個函數名,直接分別全局搜索他們,發現stm32f10x對應的是下面的函數名。

屏蔽后重新編譯,報錯如下:

全局搜索如下:

看來工程中缺少包含stm32f10x_exti.c,包含后重新編譯,報錯信息如下:

全局搜索如下:

未找到定義,在ST的HID工程中查找結果如下:

在main.c中找到了。添加到自己的工程中后,編譯結果如下:

終於把所有的錯誤調好了。下載看看是否識別到HID。

發現還是不能識別HID,看來usb初始化代碼有問題。下面從頭開始查看初始化代碼。

第一個“Set_System()”,根據上面結論,不用管;
第二個“USB_Interrupts_Config()”如下:

根據需要屏蔽上面的不相干代碼,結果如下所示:

重新編譯下載,結果如下

添加發送函數,如下:

下載結果如下:

屏蔽掉發送函數后,重新編譯下載,還是失敗。
修改VID后,重新下載,還是失敗。

重新按照上面的步驟生成新的工程,依然是“未知USB設備”,請求設備描述符失敗。
此后覺得自己永遠沒法獨立調試出來了,然后用BeyondCompare軟件比較上面
的最后一版軟件和別人幫我調試好的代碼,還有微雪開發板的USB手柄代碼,不斷
地屏蔽、復制和粘貼,以排除。最后發現是
Suspend()
函數被修改成了下面的樣子,
才會讓代碼從
不能
用變為能用。

此時,下載結果如下:

后來發現,在正點原子的USB虛擬串口實驗中,Suspend()函數也是被修改成上面的樣子的!!!
**********************************************************************************************************************
下面開始進行HID數據的發送!
在usb_sil.c中發現一個USB發送函數“
USB_SIL_Write”,將其放到while(1)中,如下所示:

編譯后下載,結果如下:

沒有數據輸出。
在網上找到發送函數的用法發現:

於是增加語句:SetEPRxValid(
ENDP1),編譯下載后,還是沒有數據發送。
在上面的USB虛擬串口代碼中查找發送代碼,發現發送函數USB_SIL_Write()並沒有被調用,
而是使用下列語句發送的:

而USB_SIL_Write()函數如下:

上面函數的前兩行和USB虛擬串口調用的發送代碼相同,但是USB虛擬串口還是用了
SetEPTxValid()函數,這和上面從網上找到的SetEPRxValid()函數不同。嘗試替換,看是
否能發送數據

結果如下:
BusHound報錯如下:

從網上搜到:

於是分別修改下列幾處代碼:
(1) usb_desc.c中:

(2)usb_prop.c中:

(3)stm32f10x_it.c中:
沒找到相關信息,暫時不改。
(4)在
usb_desc.c中:

(5)usb_desc.h中:

重新編譯后,下載,結果如下:

USB數據發送完成!
下面開始進行HID數據的接收!
在usb_endp.c中有接收數據,用於控制LED。

在keil的調試模式下,用BusHound發送數據


在Keil中的Watch1窗口中觀察變量:Receive_Buffer,結果發現數據並未變化。

本以為HID接收功能失敗了,結果在點擊調試暫停按鈕時意外發現數據收到了:


看來,數據確實收到了,只是暫時未顯示出來。
那么,
先判斷收到的數據,再把數據返回,以此來判斷數據收發是否正常。



看起效果如下:

所以,上面的數據收發功能正常。(代碼見附件:第5步:實驗4 串口實驗 從頭開始修改為HID_20170801_5.rar)