平臺:硬件設(shè)計(jì)、固件配置與調(diào)試實(shí)踐)
1. 項(xiàng)目起源一個“小而全”的雙USB口單片機(jī)平臺做嵌入式開發(fā)的朋友應(yīng)該都有過這種經(jīng)歷一塊核心板引腳不夠用、USB口只有一個、調(diào)試和通訊搶同一個接口來回插拔讓人抓狂。我斷斷續(xù)續(xù)折騰了一個“微型MCU開發(fā)平臺”核心思路是讓一塊極小尺寸的板子同時提供兩個獨(dú)立的USB端口一個固定留給調(diào)試下載另一個留給用戶應(yīng)用通訊。這樣日常開發(fā)時不需要反復(fù)拔線掛載設(shè)備后也不需要額外占用調(diào)試口。整個平臺的基礎(chǔ)是一顆支持USB外設(shè)的MCU板子尺寸控制在名片大小以內(nèi)雙端口設(shè)計(jì)讓它在開發(fā)調(diào)試、協(xié)議轉(zhuǎn)換、數(shù)據(jù)采集、甚至小型HID設(shè)備原型驗(yàn)證這幾個場景里都能直接用。這篇就從頭到尾拆一遍這個平臺的選型邏輯、硬件設(shè)計(jì)要點(diǎn)、固件初始化和實(shí)操排查過程適合正在做USB相關(guān)低功耗設(shè)備、或者想把手頭的MCU開發(fā)環(huán)境理順一點(diǎn)的工程師參考。有朋友問為什么不直接用現(xiàn)成的評估板我的回答是評估板把引腳引出來一大片但很少有評估板會專門為“調(diào)試口”和“用戶口”做獨(dú)立的雙USB設(shè)計(jì)。大多數(shù)時候你插一個USB轉(zhuǎn)串口模塊再插一個原生USB設(shè)備桌上已經(jīng)兩根線了。這個平臺想解決的就是把兩根線簡化成一根線調(diào)試供電共用再把另一根完全留給用戶功能互不干擾。2. 方案選型為什么選了這顆MCU為什么用雙端口2.1 USB端口的角色分配邏輯USB端口分配是整個平臺的靈魂也是很多開發(fā)者在做雙USB設(shè)計(jì)時最容易糾結(jié)的地方。我的選擇是一個端口固定配置為USB轉(zhuǎn)串口CDC類用來輸出日志和燒錄固件另一個端口配置為原生USB功能口根據(jù)應(yīng)用場景靈活切換為HID鍵鼠/自定義設(shè)備、MSCU盤或者自定義Vendor Class。這樣分配有三個考慮調(diào)試鏈路與業(yè)務(wù)鏈路物理隔離即使業(yè)務(wù)代碼把USB協(xié)議棧跑崩了調(diào)試口依然可以輸出崩潰現(xiàn)場信息不用硬件復(fù)位打斷現(xiàn)場。帶寬隔離日志輸出有固定端點(diǎn)帶寬不會擠占業(yè)務(wù)數(shù)據(jù)的傳輸帶寬這在批量傳輸數(shù)據(jù)時特別重要。使用便利性日常接一根線就能同時供電和調(diào)試用戶再插一根線的時候就可以跑業(yè)務(wù)不需要在PC端軟件里來回切換端口。第二個次要考慮是使用習(xí)慣大多數(shù)工程師的串口調(diào)試助手都只認(rèn)某一個固定COM編號如果這個編號天天變工具鏈就亂了。固定CDC端口可以確保COM編號穩(wěn)定省掉很多無謂的排查時間。2.2 MCU主控選型和USB外設(shè)資源盤點(diǎn)驅(qū)動雙USB端口手里這顆MCU至少需要滿足以下幾個條件內(nèi)部集成了至少一個支持Device模式的USB外設(shè)有足夠端點(diǎn)Endpoint數(shù)量能同時承載CDC和HID/其它類能從USB VBUS供電穩(wěn)定工作最好支持USB時鐘的外部晶振以保證通訊穩(wěn)定性。在實(shí)際選型時我對比過幾顆常見的MCU最終選擇了一顆內(nèi)部帶USB全速Full Speed 12 MbpsPHY的單芯片方案。很多朋友在選型時容易忽略的是MCU的USB外設(shè)和普通UART外設(shè)不一樣它需要穩(wěn)定的48 MHz時鐘源。內(nèi)部RC振蕩器雖然能跑但全速USB對時鐘精度要求較高RC振蕩器在全溫度范圍內(nèi)容易漂移長期運(yùn)行會出現(xiàn)幀錯誤。最終我選了帶外部晶振的版本配合內(nèi)部PLL把時鐘倍頻到48 MHz。我畫了一張簡化的端口資源分配表方便大家參考功能端口USB類典型端點(diǎn)需求用途是否固定USB口ACDC虛擬串口中斷IN 批量IN/OUT日志輸出、固件下載固定USB口BHID/MSC/自定義中斷IN/OUT或批量IN/OUT用戶業(yè)務(wù)可切換除了USB資源這顆MCU還留了一組足夠多的GPIO和一路ADC方便后續(xù)做些簡單的傳感器數(shù)據(jù)采集。Flash空間要裝得下一個USB協(xié)議棧和至少兩套應(yīng)用固件RAM也要給USB端點(diǎn)緩沖區(qū)留足空間。在USB全速下每個端點(diǎn)緩沖區(qū)從8字節(jié)到64字節(jié)不等數(shù)據(jù)傳輸量大的時候占用內(nèi)存很快這點(diǎn)在選型時務(wù)必確認(rèn)。2.3 雙USB的供電拓?fù)湓O(shè)計(jì)雙USB端口就意味著有兩個VBUS輸入。這里的第一個坑是兩個端口如果同時供電電流會互相倒灌。尤其是兩個口分別接在不同電腦上時兩邊的5V可能因?yàn)榈仉娢徊疃a(chǎn)生環(huán)流輕則USB口識別不了設(shè)備重則燒毀端口保護(hù)元件。這個問題我有兩個解決思路簡單方案兩個VBUS各串一個低壓差二極管做輸入隔離板子再從二極管之后取電。缺點(diǎn)是二極管帶來約0.2~0.4V壓降這在低電壓供電場景下有些浪費(fèi)。穩(wěn)妥方案用一顆電源路徑管理芯片做優(yōu)先選擇默認(rèn)從調(diào)試口取電當(dāng)檢測到業(yè)務(wù)口也接入時自動切換或并聯(lián)供電。這需要多一顆芯片但在涉及兩個主機(jī)同時接入的場景下更放心??紤]到開發(fā)板的使用場景一般不會真的有兩個電腦同時給板子供電我最終采用了兩個VBUS輸入加理想二極管方案的簡化版每個VBUS經(jīng)一個低導(dǎo)通壓降的P-MOS管后再合并避免實(shí)際裝配幾塊板之間出現(xiàn)倒灌。如果后期要做產(chǎn)品級設(shè)計(jì)建議換成正規(guī)的負(fù)載開關(guān)或電源路徑管理IC。3. 硬件設(shè)計(jì)要點(diǎn)雙USB口的PCB與關(guān)鍵器件布局3.1 最小系統(tǒng)硬件組成除MCU本體外這個平臺的最少系統(tǒng)器件包括48 MHz外部晶振 兩顆負(fù)載電容給USB提供參考時鐘一顆LDO或DCDC把5V降到MCU核心電壓USB口A和USB口B的接口我選擇的是Micro-B和Type-C各一個方便手頭不同線材使用兩顆USB數(shù)據(jù)線上的串聯(lián)電阻或者直接使用MCU內(nèi)置的匹配電阻視具體型號而定一顆復(fù)位按鍵和一顆用戶按鍵兩個狀態(tài)LED一個指示電源一個指示USB口B的枚舉狀態(tài)PCB層數(shù)上實(shí)際做的是兩層板因?yàn)閁SB全速12 Mbps的信號速率不是特別高兩層板只要布局合理也能滿足要求。如果把平臺升級到USB高速480 Mbps就強(qiáng)烈建議做四層板專門鋪一層完整地平面。全速USB雖然速率低但數(shù)據(jù)線上的差分阻抗匹配還是需要注意即便芯片內(nèi)部已經(jīng)有串聯(lián)匹配走線時也盡量做到兩條D/D-線等長、靠近避免大面積鋪銅分割造成信號回流路徑過亂。3.2 USB數(shù)據(jù)線的ESD與共模防護(hù)開發(fā)板放在桌面上不會頻繁熱插拔但天天接USB靜電問題還是不能被忽視。在裸露的USB接口附近我預(yù)留了ESD防護(hù)器件的位置。最初設(shè)計(jì)的板子上沒焊接ESD器件因?yàn)橹蛔龉δ茯?yàn)證后來發(fā)現(xiàn)環(huán)境干燥時熱插拔USB偶爾會導(dǎo)致設(shè)備枚舉失敗甚至MCU復(fù)位。這是明顯的靜電干擾加裝了TPD4E05U6這類四通道USB ESD保護(hù)器件后基本就沒再復(fù)現(xiàn)了。另外還推薦在USB數(shù)據(jù)線上預(yù)留共模電感的位置。尤其是當(dāng)USB線纜比較長或者接近電源模塊的時候共模電感能有效抑制EMI這屬于高速接口設(shè)計(jì)的常規(guī)配套。對于全速USB設(shè)備可焊可不焊但預(yù)留位置日后調(diào)整會方便很多。3.3 去耦電容放置的講究MCU電源引腳的去耦電容一定要靠近引腳放置這一點(diǎn)看似基礎(chǔ)但很多人做得不夠到位。USB通訊瞬間電流變化大尤其是枚舉階段會有較大的瞬態(tài)需求如果去耦電容離得遠(yuǎn)等效串聯(lián)電感增大根本來不及補(bǔ)充瞬態(tài)電流會出現(xiàn)USB枚舉間歇性失敗的情況。我習(xí)慣每對電源引腳放一組0.1uF 1uF的電容組合。兩個電容的諧振點(diǎn)互補(bǔ)低頻高頻都能覆蓋。另外在電源入口處放了10uF的鉭電容或陶瓷電容做儲能。USB枚舉前的電流需求變化特別劇烈這10uF是穩(wěn)定工作的關(guān)鍵。3.4 雙USB口的指示燈設(shè)計(jì)作為一塊開發(fā)調(diào)試平臺板子上的狀態(tài)指示寧可多做不可少做。我在USB口B旁邊加了一顆LED由MCU的GPIO控制只有當(dāng)USB口B被主機(jī)成功枚舉后才點(diǎn)亮。這張小卡片其實(shí)是排查問題的第一道工具插上去如果沒有主機(jī)的“叮咚”聲看LED就知道USB設(shè)備是否已經(jīng)配置成功免去反復(fù)打開設(shè)備管理器查看的流程。調(diào)試口A則沒有加獨(dú)立枚舉指示因?yàn)樗拿杜e狀態(tài)會立刻反映在串口工具上沒必要占用GPIO。如果追求的是一塊“全狀態(tài)可見”的板子也可以兩個口都加指示成本并不高。4. 固件設(shè)計(jì)一個USB協(xié)議棧兩個設(shè)備實(shí)例4.1 USB協(xié)議棧的選擇與配置思路固件部分是本平臺的重頭戲。選擇USB協(xié)議棧時需要考慮是使用MCU廠商官方協(xié)議棧還是第三方協(xié)議棧。在這個項(xiàng)目中我用了MCU官方SDK自帶的USB設(shè)備協(xié)議棧因?yàn)樗鼘π酒拇嫫鲗拥姆庋b完整底層穩(wěn)定而且省去了自己寫寄存器配置的麻煩。在協(xié)議棧上需要把兩個USB外設(shè)分別注冊為不同的設(shè)備實(shí)例初始化時配置各自的描述符和回調(diào)函數(shù)。這里有個非常關(guān)鍵的工程概念一個MCU內(nèi)部可能只有一個USB外設(shè)模塊但在有雙端口的情況下你得確認(rèn)這顆MCU究竟是物理上獨(dú)立的兩套USB控制器還是共用一套控制器但引出兩套引腳。前者可以直接跑兩個獨(dú)立的設(shè)備棧后者只能做端口切換無法同時工作。我選的這顆MCU是內(nèi)置了兩套USB控制器的型號這樣才能真正實(shí)現(xiàn)“兩個USB口同時在線”。如果MCU只有一套USB控制器但又需要雙口同時工作那就只能外接USB Hub芯片擴(kuò)展或者用USB OTG的Host模式再接一個Device設(shè)備邏輯鏈路會更加曲折。從這個角度看雙USB控制器的MCU是這塊平臺成立的基礎(chǔ)。4.2 CDC虛擬串口配置細(xì)節(jié)CDC配置是這個平臺的日志出口在USB協(xié)議棧里的注冊順序放在最前面。CDC實(shí)際是由兩個子接口復(fù)合而成的一個通信類接口用于控制信息的傳輸和一個數(shù)據(jù)類接口用于實(shí)際串口數(shù)據(jù)的傳輸。通信接口使用中斷端點(diǎn)數(shù)據(jù)接口使用批量端點(diǎn)。在枚舉配置中還要注意以下三點(diǎn)串口號字符串配置一個固定的iSerial字符串這樣Windows系統(tǒng)會把設(shè)備識別成同一個COM編號避免每次插拔都換串口號。緩沖區(qū)大小為批量端點(diǎn)分配兩個方向的大小合適的緩沖區(qū)比如64字節(jié)。因?yàn)閁SB全速的一次事務(wù)最大是64字節(jié)緩沖區(qū)太小會丟失數(shù)據(jù)。端點(diǎn)的方向描述IN和OUT的地址不要弄混。IN是設(shè)備到主機(jī)OUT是主機(jī)到設(shè)備搞反了就會表現(xiàn)為“能發(fā)送不能接收”或者“能接收不能發(fā)送”的怪象。我調(diào)試時曾遇到一個典型的CDC問題設(shè)備管理器里能看到端口但打開串口后沒有任何數(shù)據(jù)。查了好久才發(fā)現(xiàn)是描述符里數(shù)據(jù)接口的端點(diǎn)地址配置重復(fù)了IN端點(diǎn)和OUT端點(diǎn)用了同一個地址。USB規(guī)范要求每個端點(diǎn)地址必須唯一這也是后面排查經(jīng)驗(yàn)里要反復(fù)提醒的一點(diǎn)。4.3 HID人機(jī)交互設(shè)備類配置細(xì)節(jié)USB口B默認(rèn)配置為HID類設(shè)備。相比CDCHID的好處是Windows、Linux、macOS下基本不需要額外安裝驅(qū)動開箱即用。對于開發(fā)調(diào)試來說這意味著你可以把板子瞬間變成一個自定義按鍵面板或者數(shù)據(jù)采集器插上就能用。HID描述符里的關(guān)鍵參數(shù)有三個報(bào)告描述符Report Descriptor定義設(shè)備向主機(jī)報(bào)告的數(shù)據(jù)格式。經(jīng)典的自定義HID做的是64字節(jié)的輸入報(bào)表和64字節(jié)的輸出報(bào)表方便雙向透傳。輪詢間隔Polling Interval這個值定義主機(jī)每隔多久查詢一次設(shè)備。雖然HID是中斷傳輸?shù)賃SB的中斷IN端點(diǎn)有最短間隔約束一般是1ms或10ms。我配置成1ms每次傳輸64字節(jié)理論吞吐能到64KB/s。對于一般的傳感器采集足夠了。最大包大小Max Packet Size設(shè)置成64字節(jié)與端點(diǎn)能力匹配。如果想做鍵盤或者鼠標(biāo)協(xié)議棧里也提供了現(xiàn)成的類模板改一下報(bào)告描述符即可。但要注意把HID配置成鍵盤之后操作系統(tǒng)會把它當(dāng)作標(biāo)準(zhǔn)輸入設(shè)備這時的數(shù)據(jù)就變成“按鍵事件”而不再能隨意透傳數(shù)據(jù)了。4.4 兩個USB設(shè)備的同時運(yùn)行邏輯在固件主循環(huán)中兩套USB設(shè)備是并行工作的。實(shí)際操作中MCU運(yùn)行的主循環(huán)會輪詢USB任務(wù)調(diào)度器調(diào)度器負(fù)責(zé)維護(hù)兩個設(shè)備實(shí)例各自的狀態(tài)機(jī)和端點(diǎn)處理回調(diào)。USB的中斷服務(wù)程序里要快速響應(yīng)只做數(shù)據(jù)搬運(yùn)和狀態(tài)標(biāo)記實(shí)際的數(shù)據(jù)處理邏輯放到主循環(huán)中執(zhí)行。這樣能避免在中斷里做耗時操作把USB帶寬拖垮。一個重要的邏輯細(xì)節(jié)是兩個USB實(shí)例不能共享同一塊端點(diǎn)的緩沖區(qū)。每個設(shè)備實(shí)例的端點(diǎn)緩沖區(qū)是獨(dú)立分配的否則在多任務(wù)交錯訪問時會出現(xiàn)數(shù)據(jù)覆蓋。我踩過這個坑起初想省RAM把兩個實(shí)例的端點(diǎn)緩沖指向了同一個數(shù)組結(jié)果跑起來之后兩個口的數(shù)據(jù)互相“串門”一個口收到的數(shù)據(jù)出現(xiàn)在另一個口的發(fā)送隊(duì)列里查了一個下午才定位到問題。5. 實(shí)操過程從焊接硬板到兩個USB口同時枚舉5.1 板子焊接和上電自檢焊裝完硬件后第一次上電不要急著連USB。先做幾個基礎(chǔ)測量對地短路測試檢查電源軌對地阻抗特別是3.3V對地是否短路。USB口B的VBUS沒接通時整個板子應(yīng)該處于低功耗狀態(tài)。電源電壓測試USB調(diào)試口接入后先測LDO輸出是否為預(yù)期電壓如3.3V并且檢查MCU的復(fù)位腳是否為高電平。晶振起振測試用示波器量MCU的CLKOUT引腳如果有或者直接測量晶振兩腳波形確認(rèn)48MHz晶振已經(jīng)起振。晶振不起振USB枚舉基本沒有任何反應(yīng)。測量正常后進(jìn)入調(diào)試口點(diǎn)亮LED的測試流程這算是最基本的“點(diǎn)燈工程”確認(rèn)MCU的GPIO、時鐘和外設(shè)幀能正常工作再燒錄USB協(xié)議棧的測試固件。5.2 首次接入主機(jī)固件燒錄與枚舉順序用調(diào)試口A往MCU里燒錄固件。這里建議先用專門的下位機(jī)軟件配合芯片的Bootloader或者用SWD調(diào)試器。因?yàn)檫€沒穩(wěn)定的CDC口直接通過USB調(diào)試口燒錄需要芯片本身支持USB DFU模式。我的流程是按住板子上的BOOT鍵然后插入USB調(diào)試口APC端識別到一個DFU設(shè)備用官方燒錄工具寫入編譯好的固件復(fù)位板子拔出USB再重新插入這時USB口A應(yīng)該枚舉成一個虛擬串口如果一切正常串口工具中會出現(xiàn)打印信息這一步走通了雙USB平臺的最關(guān)鍵鏈路就有了。接著把USB口B也連到電腦上這時PC端應(yīng)該能看到一個新的HID設(shè)備。5.3 兩個USB口同時枚舉的實(shí)際效果實(shí)測時我同時插了兩根線到同一臺電腦的兩個USB口。設(shè)備管理器里能看到一個COM口來自調(diào)試口A一個HID兼容設(shè)備來自USB口B一個USB復(fù)合設(shè)備整體枚舉時的父節(jié)點(diǎn)兩個端口可以同時和PC通信互不干擾。為了驗(yàn)證可靠性我用一段腳本同時往兩個端口發(fā)數(shù)據(jù)連續(xù)運(yùn)行幾個小時兩邊都沒有丟包說明雙USB鏈路在硬件和協(xié)議棧配合下是穩(wěn)定工作的。5.4 如何驗(yàn)證數(shù)據(jù)鏈路的完整性驗(yàn)證雙USB數(shù)據(jù)鏈路最直接的方法就是用回環(huán)測試把USB口B收到的數(shù)據(jù)回傳到USB口A的串口日志里。具體操作為PC端向USB口B發(fā)送一串字節(jié)如0x00~0xFF循環(huán)MCU固件把這批數(shù)據(jù)原封不動搬進(jìn)USB口A的CDC發(fā)送緩沖區(qū)串口調(diào)試助手里收到與發(fā)送完全一致的數(shù)據(jù)這個測試能同時驗(yàn)證USB口B的接收鏈路、MCU內(nèi)部的數(shù)據(jù)搬運(yùn)和USB口A的發(fā)送鏈路。如果測試中途數(shù)據(jù)有CRC錯誤或長度不對就逐個點(diǎn)排查。不過要客觀說一句雙USB口同時在線也存在瓶頸兩個USB口雖然各自獨(dú)立枚舉但在同一顆MCU內(nèi)部總線帶寬和CPU處理能力依然是共享的。如果兩邊都進(jìn)行大數(shù)據(jù)量傳輸MCU主頻不夠時依然會互相干擾。這屬于方案本身的物理上限選型時心里要有數(shù)。6. 常見問題與排查技巧實(shí)錄6.1 枚舉失敗但硬件測量正常這是最讓人頭疼的一類問題量電壓、量晶振都正常插到PC上就是不枚舉。我遇到過三類原因USB數(shù)據(jù)線接反D/D-在PCB上標(biāo)反了這是布局時最原始也最常犯的錯誤。此時用萬用表量USB連接器的D/D-對地電壓正常時D-空閑為低D被上拉電阻拉高。如果兩根線的電平狀態(tài)反了幾乎可以斷定是走線接反。上拉電阻沒接對全速設(shè)備需要在D上接1.5kΩ上拉電阻讓主機(jī)識別為全速設(shè)備。如果這顆電阻沒焊接或接到了D-上主機(jī)就不會觸發(fā)枚舉。很多MCU內(nèi)部集成了這個上拉電阻需要使能相應(yīng)寄存器如果沒使能同樣是白搭。時鐘頻率偏差過大USB主機(jī)是靠SOF包來同步設(shè)備時鐘的。如果晶振實(shí)際頻率偏差太大主機(jī)幾次同步不上就會認(rèn)為設(shè)備異常直接斷開連接。遇到莫名其妙的枚舉失敗可以用示波器數(shù)一下晶振頻率。6.2 能識別到設(shè)備但無法打開串口這種問題和枚舉失敗完全不同設(shè)備管理器里能看到COM口但串口工具打開時報(bào)錯或者打開后立即自動關(guān)閉。第一類原因是CDC描述符里設(shè)置的接口數(shù)量與實(shí)際不符。CDC設(shè)備必須是一對接口同時存在的如果描述符里只聲明了一個接口Windows在打開串口時就會異常。第二類原因是設(shè)備被上一次會話鎖定尤其是在Linux系統(tǒng)里。如果應(yīng)用異常退出系統(tǒng)可能還認(rèn)為端口被占用此時需要手動釋放端口鎖文件而Windows下一般重啟或者拔插一下就能恢復(fù)。第三類原因是緩沖區(qū)大小配置有誤發(fā)送緩沖區(qū)大于端點(diǎn)最大包長時協(xié)議棧會返回錯誤。建議在調(diào)試階段把發(fā)送緩沖區(qū)大小和端點(diǎn)最大包長設(shè)為一致等穩(wěn)定運(yùn)行后再逐步加大。6.3 一個口工作時另一個口延遲飆升雙USB同時工作時的“互相干擾”大多是MCU中斷優(yōu)先級配置的問題。USB中斷如果配置成同一個優(yōu)先級兩個USB控制器同時觸發(fā)中斷時后到的只能排隊(duì)造成響應(yīng)延遲。建議把實(shí)時性要求高的那個端口的中斷優(yōu)先級調(diào)高。另外主循環(huán)的輪詢間隔也是影響因素。如果主循環(huán)里有耗時的阻塞操作比如等待Flash寫入、長延時USB的端點(diǎn)數(shù)據(jù)緩沖區(qū)可能被寫滿后續(xù)數(shù)據(jù)就會被丟棄。解決方法是把耗時操作盡量拆分到狀態(tài)機(jī)中執(zhí)行時時讓出主循環(huán)控制權(quán)。6.4 為什么USB口B枚舉成兩個設(shè)備不少朋友在配置復(fù)合設(shè)備時發(fā)現(xiàn)插上一個USB口B電腦上出現(xiàn)了兩個獨(dú)立的HID設(shè)備。這是因?yàn)閁SB協(xié)議棧默認(rèn)開啟了“獨(dú)立HID接口”模式每個接口都被當(dāng)成一個獨(dú)立的HID設(shè)備來處理。如果你只需要一個HID設(shè)備需要在描述符級別把兩個接口合并成一個復(fù)合設(shè)備IAD Interface Association Descriptor。在Linux下可以用lsusb -v查看設(shè)備描述符確認(rèn)usb_iad描述符是否存在Windows的設(shè)備管理器里看到的“USB輸入設(shè)備”數(shù)量也能側(cè)面反映描述符配置是否正確。我最終通過添加IAD描述符把鍵盤、鼠標(biāo)、自定義HID三個接口合并成一個復(fù)合HID設(shè)備在操作系統(tǒng)中看起來就是一個設(shè)備管理起來也舒服得多。6.5 常見問題速查表現(xiàn)象可能原因排查手段解決方案插入USB無任何反應(yīng)數(shù)據(jù)線/差分線接反、供電不足萬用表測D/D-靜態(tài)電壓檢查PCB與連接器接線枚舉后反復(fù)斷開重連時鐘不穩(wěn)、ESD器件參數(shù)異常示波器測晶振波形確認(rèn)外部晶振參數(shù)必要時更換ESD器件設(shè)備管理器無COM口CDC描述符接口數(shù)量錯誤USB抓包工具分析枚舉過程修正描述符打開COM口失敗驅(qū)動沖突或端口被占用嘗試更換USB口重啟系統(tǒng)或重新插拔兩個口交替掉線中斷優(yōu)先級配置不當(dāng)同時傳輸大量數(shù)據(jù)壓測調(diào)整中斷優(yōu)先級和主循環(huán)輪詢數(shù)據(jù)亂碼或丟字節(jié)緩沖區(qū)配置不足/共享緩沖檢查端點(diǎn)緩沖區(qū)分配獨(dú)立分配端點(diǎn)緩沖區(qū)加大隊(duì)列深度7. 這套平臺能做什么應(yīng)用場景擴(kuò)展USB口B的HID模式改一改描述符就能變成自定義HID設(shè)備再改一下類代碼和端點(diǎn)配置就能變成一個U盤MSC實(shí)現(xiàn)簡單的數(shù)據(jù)存儲和讀取。我把這套平臺在幾個場景中都實(shí)際跑過一遍數(shù)據(jù)采集透傳器MCU的ADC采集模擬信號打包后通過USB口B的HID通道發(fā)給PC同時串口A打日志。真正做到采集和調(diào)試互不干擾排查問題時自由度很高。自定義按鍵面板把USB口B配置為HID鍵盤GPIO上接幾個按鍵按下就發(fā)送對應(yīng)的按鍵碼。這套組合在一些自動化測試環(huán)境里可以充當(dāng)遙控觸發(fā)器用起來很方便。固件升級工具原型利用USB口A的CDC鏈路做IAP升級USB口B繼續(xù)提供用戶業(yè)務(wù)升級過程不影響業(yè)務(wù)鏈路的連接狀態(tài)這在一些不能斷連的設(shè)備里非常實(shí)用。協(xié)議轉(zhuǎn)換器驗(yàn)證USB口B配置成CDCUSB口A也配置成CDC然后在MCU內(nèi)部將兩個CDC口的數(shù)據(jù)互相轉(zhuǎn)發(fā)實(shí)現(xiàn)“USB橋接”。這個功能可以驗(yàn)證不同USB控制器之間的數(shù)據(jù)搬運(yùn)能力和帶寬上限。從擴(kuò)展性上看只要MCU的Flash和RAM足夠完全可以在同一顆芯片上跑更多的USB類比如同時支持CDC HID Vendor Class。UAC音頻類、UVC視頻類這類對帶寬和實(shí)時性要求更高的應(yīng)用就需要重新評估主頻和緩存策略了。8. 資源與后續(xù)擴(kuò)展建議8.1 常見的開發(fā)工具鏈和調(diào)試手段MCU開發(fā)工鏈可以視個人習(xí)慣選擇IDEVS Code加廠商插件足以完成編輯、編譯和燒錄調(diào)試器SWD調(diào)試器如DAPLink調(diào)試MCU內(nèi)部狀態(tài)抓包分析Wireshark結(jié)合USB監(jiān)控軟件可以拿到完整的USB枚舉過程示波器非必備但排查時鐘和數(shù)據(jù)線信號時極其有用USB協(xié)議棧的調(diào)試往往很依賴上位機(jī)的配合Windows上建議打開設(shè)備管理器的事件視圖Linux下直接用dmesg和lsusb動態(tài)查看熱插拔事件效率遠(yuǎn)高于反復(fù)開關(guān)串口工具。8.2 硬件升級方向現(xiàn)在這塊平臺是兩層板加全速USB如果后續(xù)想在帶寬上做文章可以考慮換帶USB高速480Mbps控制器的MCU那就要對PCB做四層板設(shè)計(jì)和高速差分對等長控制。同時USB供電部分如果負(fù)載增加LDO也可能需要升級成DCDC以降低功耗。8.3 低功耗模式適配另一個值得深入的方向是低功耗。USB設(shè)備大多數(shù)情況下是總線供電模式但如果做電池供電類產(chǎn)品就需要讓MCU在USB掛起Suspend時進(jìn)入低功耗狀態(tài)同時保證喚醒邏輯可靠。協(xié)議棧里需要監(jiān)聽總線事件在收到掛起信號后主動降頻、斷開內(nèi)部外設(shè)時鐘并把USB口B的外部信號作為喚醒源。這個優(yōu)化完成之后整板電流能降到微安級別續(xù)航能力會有質(zhì)的提升。9. 小結(jié)與個人體會這個“微型MCU雙USB開發(fā)平臺”做下來最大的體會是雙USB方案從硬件到軟件是一個整體工程任何一個環(huán)節(jié)的疏忽都會在枚舉和通訊階段暴露出來。硬件上要重視電源拓?fù)浜托盘柾暾攒浖弦眄槄f(xié)議棧的實(shí)例注冊、端點(diǎn)分配和中斷優(yōu)先級兩者之間是相輔相成的?;仡^再看如果當(dāng)初沒有堅(jiān)持在板子上做兩個獨(dú)立的USB口而是一直沿用“一個USB口加一個串口模塊”的老路子后面做協(xié)議轉(zhuǎn)換和數(shù)據(jù)采集時就不會有這么順手的調(diào)試體驗(yàn)?,F(xiàn)在這塊板子已經(jīng)成了我手頭高頻使用的開發(fā)工具之一無論是驗(yàn)證協(xié)議原型還是當(dāng)日常調(diào)試基準(zhǔn)板都非??孔V。最后再分享一個實(shí)用操作在USB口B的HID報(bào)文里我每次都會在報(bào)頭加一個自增序號上位機(jī)收到數(shù)據(jù)時先檢查序號連續(xù)性。這個習(xí)慣幫我抓出了好幾次因?yàn)橹餮h(huán)阻塞導(dǎo)致的丟包問題。如果你也在用HID通道做雙向通訊建議從第一天起就加上這個簡單的序號機(jī)制后面排查問題會省太多時間。