例程實(shí)戰(zhàn):從官方庫(kù)到完整枚舉)
簡(jiǎn)介面向嵌入式開(kāi)發(fā)者的GD32 USB鼠標(biāo)例程解決了在GD32上通過(guò)USB OTG與電容式觸摸傳感器構(gòu)建觸控鼠標(biāo)的關(guān)鍵問(wèn)題。壓縮包共182個(gè)文件大小約979KB以80個(gè)H頭文件和78個(gè)C源文件為主體H文件承載寄存器定義與接口聲明C文件實(shí)現(xiàn)USB協(xié)議處理和觸摸事件轉(zhuǎn)換另附Keil工程配置、hex固件、腳本及說(shuō)明文檔便于直接編譯、燒錄與二次開(kāi)發(fā)。代碼覆蓋USB設(shè)備枚舉、描述符配置、端點(diǎn)管理、中斷處理、觸摸數(shù)據(jù)采集、坐標(biāo)映射以及連接斷開(kāi)邏輯。目前已有1593人學(xué)習(xí)下載適合正在研究USB協(xié)議棧或規(guī)劃觸控交互產(chǎn)品的工程師無(wú)論用于課程設(shè)計(jì)還是產(chǎn)品預(yù)研都能快速定位所需模塊。例程完整走通了從初始化USB控制器、配置觸摸傳感器到注冊(cè)鼠標(biāo)事件處理并發(fā)送USB中斷報(bào)告的流程同時(shí)對(duì)枚舉過(guò)程、錯(cuò)誤處理和兼容性測(cè)試給出了可參考的代碼路徑讀者既可以按模塊逐段閱讀也能在現(xiàn)有工程上直接驗(yàn)證擴(kuò)展雙擊、滾輪或手勢(shì)識(shí)別對(duì)深入理解GD32外設(shè)驅(qū)動(dòng)與USB設(shè)備開(kāi)發(fā)很有幫助。一句話定位如果你手里正好有一塊GD32開(kāi)發(fā)板想快速跑通USB HID設(shè)備比如鼠標(biāo)的完整流程同時(shí)又不想被官方庫(kù)的層層封裝繞暈這篇實(shí)戰(zhàn)筆記應(yīng)該能幫你省下至少兩天的摸索時(shí)間。本文基于GD32F303系列其他型號(hào)思路通用從硬件準(zhǔn)備、官方例程結(jié)構(gòu)、代碼拆解、枚舉失敗排查到玩法擴(kuò)展逐層講透。1. 這套例程水有多深先看清官方打包了哪些東西很多人拿到GD32的USB鼠標(biāo)例程第一反應(yīng)是打開(kāi)工程直接編譯下載結(jié)果鼠標(biāo)不動(dòng)就懵了。其實(shí)這套例程真正的復(fù)雜度不在鼠標(biāo)怎么動(dòng)而在USB協(xié)議棧的完整鏈路。GD32官方庫(kù)GD32F30x_Firmware_Library里的USB鼠標(biāo)例程默認(rèn)放在Examples/USB/USB_Mouse目錄下它不是一個(gè)孤立的main.c文件而是一整套USB設(shè)備協(xié)議棧的骨架包含USB設(shè)備核心層負(fù)責(zé)枚舉、標(biāo)準(zhǔn)請(qǐng)求處理、端點(diǎn)調(diào)度。HID類(lèi)處理層負(fù)責(zé)HID描述符解析、報(bào)告收發(fā)、鼠標(biāo)按鍵與位移的編碼。硬件抽象層USB D / D- 引腳配置、中斷處理、端點(diǎn)寄存器讀寫(xiě)。應(yīng)用層main函數(shù)里初始化時(shí)鐘、配置GPIO、啟動(dòng)USB然后每隔一段時(shí)間模擬鼠標(biāo)移動(dòng)或按鍵。拿到例程后第一件事不是編譯而是先看usbd_hid_core.c和usbd_hw.c這兩個(gè)文件的代碼量——前者是HID協(xié)議實(shí)現(xiàn)后者是硬件底層。如果這兩個(gè)文件的結(jié)構(gòu)你能說(shuō)出個(gè)大概后面排查問(wèn)題就有方向了。官方例程默認(rèn)的鼠標(biāo)行為是上電后USB枚舉成功然后每500ms左右向右移動(dòng)一段像素同時(shí)周期性觸發(fā)左鍵點(diǎn)擊。這個(gè)行為被封裝在usbd_hid_mouse_process這類(lèi)函數(shù)里。也就是說(shuō)你只要編譯燒錄成功插上電腦不需要任何驅(qū)動(dòng)鼠標(biāo)指針就會(huì)自己動(dòng)起來(lái)。如果沒(méi)動(dòng)問(wèn)題一定出在枚舉鏈路或端點(diǎn)配置上。注意GD32和STM32的USB外設(shè)雖然都是USB 2.0 FS設(shè)備但寄存器映射、描述符緩沖區(qū)的訪問(wèn)方式、端點(diǎn)號(hào)分配邏輯有差異不能直接拿STM32的HAL庫(kù)代碼套。這也是為什么很多人用網(wǎng)上淘來(lái)的STM32鼠標(biāo)例程改GD32折騰半天也枚舉不了。2. 硬件準(zhǔn)備與開(kāi)發(fā)環(huán)境里那些容易埋雷的細(xì)節(jié)2.1 開(kāi)發(fā)板選型與USB接口電路GD32支持USB的型號(hào)很多以最常見(jiàn)的GD32F303系列為例它內(nèi)置了USB 2.0 FS設(shè)備控制器不需要外部PHY芯片只要在D引腳上接一個(gè)1.5kΩ上拉電阻到3.3V部分核心板已集成D-和D走差分線就能直接連電腦USB口。選板子時(shí)務(wù)必確認(rèn)三點(diǎn)板子上的USB座是不是直接連到MCU的PA11D-和PA12D。有些板子這兩個(gè)引腳被復(fù)用到別的功能比如USBDM和USBDP需要跳線或改配置。供電是否穩(wěn)定。USB枚舉瞬間電流較大如果板子靠USB口供電且板上還有其他負(fù)載比如OLED屏、蜂鳴器可能導(dǎo)致電壓跌落導(dǎo)致枚舉失敗。晶體頻率。GD32F303的USB要求USB時(shí)鐘必須是48MHz而USB時(shí)鐘來(lái)源于CK_PLL。如果外部晶振是8MHzPLL倍頻必須配置成48MHz或者使用內(nèi)部IRC48M。這一條是很多人忽略的重災(zāi)區(qū)——用25M晶振的板子直接跑例程USB時(shí)鐘根本不對(duì)枚舉必然失敗。2.2 IDE與固件庫(kù)版本的選擇GD32官方支持Keil MDK、IAR、Eclipse搭配編譯工具鏈。我日常用Keil MDK因?yàn)镚D32官方Demo工程默認(rèn)就是Keil格式雙擊.uvprojx就能打開(kāi)省去手動(dòng)移植的功夫。固件庫(kù)版本建議使用最新版GD32F30x_Firmware_Library_V2.x以上。老版本V1.x的USB協(xié)議棧和描述符結(jié)構(gòu)體定義差異較大網(wǎng)上搜到的很多解決方案都是基于老版本的直接照抄到新版本上會(huì)導(dǎo)致編譯錯(cuò)誤或邏輯錯(cuò)亂。如果你手里的例程是V2.x但報(bào)錯(cuò)提示找不到usbd_conf.h多半是沒(méi)有把Firmware_Library的USB_Device目錄完整添加到工程Include路徑。2.3 燒錄工具與調(diào)試方式GD32支持通過(guò)串口ISP、SWDJ-Link/ST-Link/DAP-Link等方式燒錄。這里有個(gè)小提醒不能用ST-Link直接連GD32F303的SWD接口刷固件雖然芯片是Cortex-M4內(nèi)核且SWD協(xié)議通用但ST-Link的固件版本和驅(qū)動(dòng)對(duì)GD32的支持時(shí)好時(shí)壞。我自己遇到過(guò)ST-Link能識(shí)別芯片、能擦除但寫(xiě)入后校驗(yàn)失敗的情況換用DAP-Link或J-Link就正常了。調(diào)試USB設(shè)備時(shí)除了常規(guī)的單步調(diào)試強(qiáng)烈建議準(zhǔn)備一個(gè)USB抓包工具——如果手里沒(méi)有邏輯分析儀可以用Wireshark配合USBPcapWindows平臺(tái)抓USB HID層的數(shù)據(jù)包。這在你后面排查枚舉失敗時(shí)會(huì)救命。3. 核心代碼逐段拆解描述符、端點(diǎn)和HID報(bào)告的配合關(guān)系3.1 設(shè)備描述符與配置描述符的結(jié)構(gòu)打開(kāi)usbd_desc.c你會(huì)看到一堆uint8_t數(shù)組。USB主機(jī)你的PC在枚舉時(shí)就是靠這些數(shù)組識(shí)別設(shè)備是誰(shuí)、要干什么。/* USB Device Descriptor */ uint8_t usbd_dev_desc[] { 0x12, // bLength 0x01, // bDescriptorType: Device 0x00, 0x02, // bcdUSB: 2.00 0x00, // bDeviceClass: Per interface ... 0x01, // iManufacturer 0x02, // iProduct 0x03, // iSerialNumber 0x01 // bNumConfigurations };這里有幾個(gè)坑點(diǎn)與業(yè)務(wù)直接相關(guān)bDeviceClass如果填0x00表示設(shè)備類(lèi)由各個(gè)接口的bInterfaceClass決定。HID鼠標(biāo)通常每個(gè)接口的bInterfaceClass填0x03HID類(lèi)。如果你在這里填了0x00以外其他值Windows可能把它當(dāng)復(fù)合設(shè)備或特定類(lèi)設(shè)備處理導(dǎo)致鼠標(biāo)功能異常。idVendor和idProduct如果沿用官方默認(rèn)值比如0x28E9是GD32的VID系統(tǒng)會(huì)識(shí)別為GD32設(shè)備。如果你后續(xù)要量產(chǎn)需要申請(qǐng)自己的VID/PID否則在電腦的設(shè)備管理器里會(huì)有黃色感嘆號(hào)提示“設(shè)備無(wú)法啟動(dòng)”的可能性實(shí)際原因是PID沖突或驅(qū)動(dòng)綁定錯(cuò)誤。字符串描述符iManufacturer、iProduct、iSerialNumber編碼方式是UTF-16LE即每個(gè)字符占2字節(jié)且需要以0x03類(lèi)型開(kāi)頭。很多新手直接改成純ASCII數(shù)組結(jié)果Windows上設(shè)備描述符字符串解析異常。3.2 配置描述符集合的組成配置描述符集合通常由以下部分串聯(lián)描述符類(lèi)型作用典型長(zhǎng)度配置描述符描述整個(gè)配置的總長(zhǎng)度、接口數(shù)量、供電方式9字節(jié)接口描述符描述一個(gè)接口的類(lèi)、端點(diǎn)數(shù)等9字節(jié)HID描述符描述HID版本、報(bào)告描述符長(zhǎng)度9字節(jié)端點(diǎn)描述符描述中斷輸入/輸出端點(diǎn)屬性7字節(jié)在usbd_hid_core.c中官方例程給了USBD_HID_config這個(gè)配置描述符集合。重點(diǎn)要看端點(diǎn)描述符里的bInterval查詢間隔。對(duì)鼠標(biāo)這類(lèi)低帶寬設(shè)備bInterval設(shè)為1~10單位ms都可以。如果設(shè)得太小比如1ms數(shù)據(jù)量不大但會(huì)增加CPU中斷頻率設(shè)得太大比如100ms鼠標(biāo)會(huì)明顯感到卡頓指針移動(dòng)不跟手。3.3 HID報(bào)告描述符才是鼠標(biāo)的靈魂這是整套例程里最容易被忽略又最關(guān)鍵的部分。鼠標(biāo)的按鍵狀態(tài)和位移必須按照HID報(bào)告描述符定義的格式打包上傳Windows才認(rèn)得出光標(biāo)向右移動(dòng)了X像素。__ALIGN_BEGIN static uint8_t usbd_hid_mouse_report_desc[] { 0x05, 0x01, // Usage Page (Generic Desktop) 0x09, 0x02, // Usage (Mouse) 0xA1, 0x01, // Collection (Application) 0x09, 0x01, // Usage (Pointer) 0xA1, 0x00, // Collection (Physical) 0x05, 0x09, // Usage Page (Button) 0x19, 0x01, // Usage Minimum (1) 0x29, 0x03, // Usage Maximum (3) 0x15, 0x00, // Logical Minimum (0) 0x25, 0x01, // Logical Maximum (1) 0x95, 0x03, // Report Count (3) 0x75, 0x01, // Report Size (1) 0x81, 0x02, // Input (Data, Variable, Absolute) ... 0xC0, // End Collection 0xC0 // End Collection };這段描述符定義了一份4字節(jié)的HID報(bào)告第1字節(jié)按鍵狀態(tài)bit0表示左鍵bit1表示右鍵bit2表示中鍵。第2字節(jié)X方向位移有符號(hào)數(shù)-127~127。第3字節(jié)Y方向位移有符號(hào)數(shù)。第4字節(jié)滾輪位移有符號(hào)數(shù)。實(shí)際發(fā)報(bào)告時(shí)你只需要構(gòu)造一個(gè)4字節(jié)數(shù)組并發(fā)送到中斷端點(diǎn)即可。官方例程里的usbd_hid_mouse_send_report函數(shù)就是干這件事的。如果你改了報(bào)告描述符比如增加了自定義按鍵、多媒體鍵那么發(fā)送緩沖區(qū)長(zhǎng)度和報(bào)告描述符的Report Count必須嚴(yán)格匹配否則Windows會(huì)報(bào)HID設(shè)備描述符請(qǐng)求失敗。4. 從編譯到枚舉鼠標(biāo)不動(dòng)時(shí)的完整排查鏈路我拿到這塊板子第一次跑例程燒錄完成后插上電腦設(shè)備管理器里什么反應(yīng)都沒(méi)有。排查了整整一晚上最終定位到問(wèn)題。這里我分享一下完整的排查鏈路按優(yōu)先級(jí)從低級(jí)到高級(jí)排列。4.1 第一步時(shí)鐘配置錯(cuò)誤導(dǎo)致USB時(shí)鐘不對(duì)打開(kāi)system_gd32f30x.c確認(rèn)系統(tǒng)時(shí)鐘使用的是外部高速晶振HXTAL還是內(nèi)部IRC。如果板子上的晶振是8MHz那么必須確保PLL配置成PLL_MUL128MHz * 12 96MHz然后USBCK CK_PLL / 2 48MHz。如果板子上的晶振是25MHzPLL倍頻系數(shù)就要重新算。一個(gè)非常隱蔽的點(diǎn)GD32F303的USB時(shí)鐘源選擇和STM32不一樣STM32是USBCLK由PLL輸出直接分頻GD32是USBCK CK_PLL / 2。很多人按STM32的思維去改時(shí)鐘結(jié)果差了一倍枚舉直接失敗。檢查方法在調(diào)試器里給RCC_CFG寄存器打個(gè)斷點(diǎn)查看USBCK位的值或者用邏輯分析儀抓D引腳如果枚舉正常D上應(yīng)該有一系列脈沖。沒(méi)有脈沖就是USB時(shí)鐘沒(méi)起來(lái)。4.2 第二步上拉電阻沒(méi)接好D上拉電阻是USB主機(jī)識(shí)別全速設(shè)備12Mbps的關(guān)鍵信號(hào)。FS設(shè)備通過(guò)把D拉高來(lái)告訴主機(jī)我在這里我是全速設(shè)備。如果這個(gè)1.5kΩ電阻沒(méi)接或者虛焊主機(jī)完全感知不到設(shè)備插入。很多開(kāi)發(fā)板為了節(jié)省一個(gè)電阻直接用MCU的GPIO控制上拉。例程里會(huì)初始化一個(gè)GPIO輸出高電平來(lái)接“模擬上拉”。此時(shí)要確認(rèn)GPIO是否復(fù)用為USB功能上拉動(dòng)作是否在USB初始化之前完成如果使用外部上拉電阻檢查其一端接D另一端接3.3V不能接5V——接5V可能直接燒壞MCU的USB PHY。4.3 第三步枚舉失敗但時(shí)鐘和上拉正常這時(shí)候要抓包一旦確認(rèn)時(shí)鐘和上拉沒(méi)問(wèn)題但Windows仍然不識(shí)別就要抓包看枚舉到哪一步卡住了。我用Wireshark加USBPcap抓過(guò)幾次常見(jiàn)失敗點(diǎn)有GET_DESCRIPTOR(Device)不響應(yīng)問(wèn)題在設(shè)備描述符數(shù)組導(dǎo)入不正確或者USB中斷沒(méi)開(kāi)啟。檢查usbd_core.c中的usbd_core_init是否被調(diào)用以及NVIC中USB中斷優(yōu)先級(jí)是否配置。GET_DESCRIPTOR(Config)返回長(zhǎng)度錯(cuò)誤配置描述符集合的總長(zhǎng)度wTotalLength與實(shí)際數(shù)組長(zhǎng)度不符。很多人在配置描述符里單獨(dú)加了一個(gè)自定義端點(diǎn)但忘記更新wTotalLength字段導(dǎo)致主機(jī)在讀取配置描述符集合時(shí)數(shù)據(jù)截?cái)?。SET_CONFIGURATION后無(wú)響應(yīng)設(shè)置配置完成后端點(diǎn)要立即激活。檢查usbd_hid_core.c里usbd_hid_set_config的實(shí)現(xiàn)確認(rèn)端點(diǎn)的EPTYPE是否配置為中斷傳輸、方向是否正確IN方向是設(shè)備到主機(jī)。4.4 第四步報(bào)告發(fā)送失敗導(dǎo)致鼠標(biāo)不動(dòng)枚舉成功設(shè)備管理器顯示HID-compliant mouse但鼠標(biāo)就是不動(dòng)問(wèn)題大概率出在報(bào)告發(fā)送鏈路。常見(jiàn)原因發(fā)送函數(shù)調(diào)用前沒(méi)有檢查USB狀態(tài)是否為USBD_CONFIGURED。枚舉結(jié)束后USB狀態(tài)會(huì)切到CONFIGURED但你在main循環(huán)里可能提前調(diào)用了發(fā)送函數(shù)此時(shí)端點(diǎn)還沒(méi)激活數(shù)據(jù)直接丟棄。端點(diǎn)發(fā)送緩沖區(qū)忙。USB中斷端點(diǎn)發(fā)送完畢后需要等待USBD_EPT_OK標(biāo)志。如果你在緊接著的第二次發(fā)送時(shí)上一次發(fā)送還沒(méi)完成數(shù)據(jù)會(huì)覆蓋導(dǎo)致偶發(fā)丟包。使用DMA傳輸時(shí)緩沖區(qū)地址沒(méi)有對(duì)齊。GD32的USB DMA要求緩沖區(qū)地址4字節(jié)對(duì)齊局部變量uint8_t buffer[4]在棧上的地址是隨機(jī)的可能導(dǎo)致DMA傳輸異常。解決方法是定義成全局?jǐn)?shù)組或者用__ALIGN_BEGIN修飾符。5. 從HID數(shù)據(jù)格式到上位機(jī)聯(lián)動(dòng)再往深處玩的花樣5.1 模擬鼠標(biāo)軌跡很多人在熱詞里提到模擬鼠標(biāo)運(yùn)動(dòng)軌跡這在GD32上其實(shí)可以做得非常平滑。HID報(bào)告里的X/Y位移是相對(duì)位移即每次上報(bào)的是一個(gè)增量。Windows的指針移動(dòng)是增量累加。所以如果想畫(huà)一條直線你可以在MCU里用Bresenham算法逐步生成位移坐標(biāo)每隔一小段時(shí)間發(fā)一次報(bào)告。但是如果想實(shí)現(xiàn)鼠標(biāo)移到指定坐標(biāo)點(diǎn)擊只靠相對(duì)位移是不行的——你沒(méi)法知道當(dāng)前指針的絕對(duì)坐標(biāo)。這種情況要么在PC端配合上位機(jī)軟件把絕對(duì)坐標(biāo)換算成相對(duì)位移再下發(fā)需要額外的串口或網(wǎng)絡(luò)通道要么使用HID絕對(duì)定位表Digitizer風(fēng)格讓上位機(jī)直接寫(xiě)屏。后者在Windows上同樣不需要驅(qū)動(dòng)但需要修改HID報(bào)告描述符中的Usage (X)和Usage (Y)并設(shè)置Logical Minimum / Maximum為屏幕分辨率范圍。5.2 USB轉(zhuǎn)串口的組合應(yīng)用GD32的USB鼠標(biāo)例程可以和USB轉(zhuǎn)串口CDC類(lèi)組合做一個(gè)串口遙控鼠標(biāo)PC上位機(jī)通過(guò)虛擬串口發(fā)送坐標(biāo)和點(diǎn)擊指令給GD32GD32收到后解析并模擬鼠標(biāo)上報(bào)。這種方案在工業(yè)控制場(chǎng)景非常常見(jiàn)比如自動(dòng)化測(cè)試臺(tái)、無(wú)人工位的電腦操作工具。實(shí)現(xiàn)組合時(shí)需要注意復(fù)合設(shè)備的配置描述符集合要包含兩個(gè)接口描述符一個(gè)HID接口鼠標(biāo)一個(gè)CDC接口虛擬串口。Windows會(huì)把它識(shí)別為“USB輸入設(shè)備 虛擬COM口”這時(shí)鼠標(biāo)功能仍然不需要驅(qū)動(dòng)CDC則需要系統(tǒng)自帶驅(qū)動(dòng)usbser.sys。兩個(gè)接口共享同一套USB設(shè)備地址和電源配置但端點(diǎn)號(hào)不能沖突。HID用IN端點(diǎn)1CDC的發(fā)送和接收分別用IN端點(diǎn)2、OUT端點(diǎn)2。枚舉時(shí)復(fù)合設(shè)備的bDeviceClass建議設(shè)為0xEFMiscellaneousbDeviceSubClass設(shè)為0x02bDeviceProtocol設(shè)為0x01這樣Windows能正確拆分接口功能。5.3 自制USB鍵鼠一體設(shè)備官方還有USB鍵盤(pán)例程Examples/USB/USB_Keyboard兩者可以合并成一個(gè)復(fù)合HID設(shè)備同時(shí)上報(bào)鼠標(biāo)和鍵盤(pán)事件。這個(gè)在電競(jìng)外設(shè)、快捷按鍵板、輔助輸入設(shè)備等場(chǎng)景很實(shí)用。合并時(shí)要把HID報(bào)告描述符改成一個(gè)總集合包含鍵盤(pán)用到的按鍵數(shù)據(jù)段和鼠標(biāo)用到的位移/按鍵數(shù)據(jù)段。此時(shí)Report ID就變得重要了——如果沒(méi)有Report ID兩個(gè)功能的數(shù)據(jù)會(huì)混在一起加了Report ID后比如鍵盤(pán)Report ID 1鼠標(biāo)Report ID 2PC就能區(qū)分包的類(lèi)型。這個(gè)改動(dòng)雖然不復(fù)雜但涉及描述符結(jié)構(gòu)大改建議先跑通單獨(dú)的鼠標(biāo)例程再去改。6. 調(diào)試經(jīng)驗(yàn)總結(jié)那些文檔里不會(huì)寫(xiě)的事最后分享幾條實(shí)打?qū)嵉慕?jīng)驗(yàn)USB例程里所有帶__ALIGN_BEGIN修飾的全局?jǐn)?shù)組不要輕易去掉對(duì)齊。GD32F303的DMA/描述符緩沖區(qū)要求4字節(jié)對(duì)齊全局變量編譯器默認(rèn)4字節(jié)對(duì)齊但局部變量不保證。如果你圖省事把描述符數(shù)組改成局部變量極大概率踩到對(duì)齊坑。USB中斷優(yōu)先級(jí)不是越高越好。在官方例程中USB中斷通常設(shè)置為NVIC_PRIGROUP_1或NVIC_PRIGROUP_2下的搶占優(yōu)先級(jí)1或2。如果低于系統(tǒng)滴答定時(shí)器優(yōu)先級(jí)那么在高頻中斷場(chǎng)景下可能丟包。如果高于串口中斷優(yōu)先級(jí)串口打印調(diào)試信息時(shí)會(huì)被USB打斷導(dǎo)致打印亂序。建議USB搶占優(yōu)先級(jí)設(shè)為2子優(yōu)先級(jí)設(shè)為0。熱插拔時(shí)的處理。Windows對(duì)USB設(shè)備熱插拔非常敏感如果你在調(diào)試時(shí)頻繁拔插建議使用帶ESD保護(hù)的USB線/座并且每次重新插拔后清空一下電腦的Device Manager緩存不然偶爾會(huì)遇到設(shè)備描述符請(qǐng)求失敗的假故障實(shí)際是系統(tǒng)緩存了舊的配置信息。用邏輯分析儀抓D/D-信號(hào)時(shí)采樣率建議至少100MS/s25MS/s也能看個(gè)大概但波形細(xì)節(jié)不夠。如果只是確認(rèn)有沒(méi)有枚舉動(dòng)作25MS/s能湊合要分析時(shí)序細(xì)節(jié)上100MS/s。最后如果你把鼠標(biāo)例程改成了鍵盤(pán)、游戲手柄甚至自定義HID設(shè)備報(bào)告描述符每改一次建議先用USBPcap抓一次包核對(duì)主機(jī)端枚舉時(shí)讀到的報(bào)告描述符長(zhǎng)度是否和你代碼里定義的一致。這個(gè)習(xí)慣能省掉大量看起來(lái)很對(duì)但不工作的排查時(shí)間。這套例程跑通之后你會(huì)對(duì)GD32的USB外設(shè)架構(gòu)有比較完整的認(rèn)知后面再接觸CDC、MSC或者做復(fù)合設(shè)備都會(huì)順暢得多。如果卡在某個(gè)具體報(bào)錯(cuò)上歡迎帶著寄存器截圖或枚舉抓包來(lái)交流。本文還有配套的精品資源點(diǎn)擊獲取