串口芯片跑AT指令)
告別USB轉(zhuǎn)串口芯片讓STM32F407用USB Host直連廣和通MC665跑AT指令最近做了一塊板子主控是STM32F407需要直接和廣和通MC665 4G模塊通信。按慣性思維第一反應是“串口嘛UART接過去就行了”。但真把MC665的調(diào)試串口引出來才發(fā)現(xiàn)模塊默認的USB口才是最高效、最穩(wěn)定的數(shù)據(jù)通道而傳統(tǒng)的USB轉(zhuǎn)串口芯片在這一層純屬多余。這個項目最核心的思路就是STM32F407自帶USB OTG外設直接把它配置成USB Host模式用USB線連MC665模塊讓4G模塊在USB總線上枚舉成一個CDC ACM設備也就是虛擬串口然后主控通過這個虛擬串口發(fā)AT指令、收AT應答完成撥號、查信號、收發(fā)數(shù)據(jù)。全程不經(jīng)過CH340、CP2102這類外置USB轉(zhuǎn)串口芯片也不需要額外的電平轉(zhuǎn)換電路。這篇文章是給兩類人看的一類是想在F407上跑USB Host但被ST官方USB庫搞得暈頭轉(zhuǎn)向的嵌入式開發(fā)另一類是正在調(diào)4G模塊AT指令發(fā)現(xiàn)UART方式又慢又容易受干擾、想換USB通道的工程師。我會把硬件連接、協(xié)議棧配置、CDC類驅(qū)動改造、AT指令狀態(tài)機這幾個核心環(huán)節(jié)完整走一遍附上能直接用的代碼思路和踩坑記錄。1. 整體設計思路為什么必須走USB Host而不是UART1.1 傳統(tǒng)UART方案的三個痛點很多4G模塊包括MC665都留有UART接口理論上可以直接和STM32的USART對接。但實際工程里這條路有很明顯的局限性。第一是速率瓶頸。MC665的UART口一般支持到921600bps但AT指令交互場景下尤其涉及TCP/IP數(shù)據(jù)透傳時UART的波特率會限制吞吐量。如果后續(xù)要做固件升級、大文件傳輸UART通道就成短板了。USB 2.0 High-Speed的理論帶寬是480Mbps差了幾個數(shù)量級。第二是電平匹配問題。STM32F407的USART是3.3V TTL電平廣和通模塊的UART接口也是1.8V或3.3V電平不同批次、不同封裝可能有差異。電平不匹配會導致通信不穩(wěn)定甚至燒毀管腳。雖然可以用電平轉(zhuǎn)換芯片但板上又多了一顆物料。第三是流控問題。4G模塊的AT應答經(jīng)常出現(xiàn)長串數(shù)據(jù)UART接收端如果緩沖區(qū)不夠或者中斷處理不及時很容易丟字節(jié)。硬件流控RTS/CTS能緩解但F407的USART只有部分型號支持而且還要額外占GPIO。相比之下USB Host方案把4G模塊當成一個標準USB設備來枚舉通信協(xié)議由USB協(xié)議棧保證數(shù)據(jù)完整性、流控、熱插拔都由硬件和協(xié)議棧處理軟件上只要專注于AT指令解析。1.2 USB Host方案的本質(zhì)模塊是一個CDC ACM設備MC665模塊內(nèi)部集成了一個USB Hub控制器USB口插上后主機會枚舉出多個接口。其中有一個接口是CDC ACMAbstract Control Model也就是標準的虛擬串口用于承載AT指令另外還有RNDIS或者ECM接口用于承載網(wǎng)絡數(shù)據(jù)也就是PPP/NDIS撥號后的IP數(shù)據(jù)包。在這個項目里我們只需要關心CDC ACM接口。因為AT指令是文本命令通過虛擬串口發(fā)送和接收完全兼容傳統(tǒng)UART的AT指令集。也就是說你之前用串口助手發(fā)的那些AT指令現(xiàn)在全部通過USB CDC通道發(fā)出去格式一模一樣。這里有一個隱蔽的好處USB CDC的底層是批量傳輸Bulk Transfer有CRC校驗和重傳機制傳輸可靠性遠高于UART。在電磁環(huán)境復雜的工業(yè)現(xiàn)場這能省掉很多排查丟字節(jié)的煩惱。1.3 選型對比F407的USB OTG FS與HSSTM32F407有兩套USB OTG外設OTG_FS內(nèi)置全速PHY和OTG_HS需要外接高速PHY或使用內(nèi)置全速PHY。MC665模塊的USB接口支持High-Speed480Mbps和Full-Speed12Mbps兩種模式。如果只跑AT指令Full-Speed也夠用但要注意MC665在Full-Speed模式下枚舉時的配置描述符會和High-Speed略有差異部分固件版本可能不支持Full-Speed枚舉。穩(wěn)妥起見我建議直接用OTG_HS外接USB3300高速PHY芯片跑High-Speed模式。這不僅兼容性最好也為后續(xù)走NDIS網(wǎng)絡數(shù)據(jù)通道預留了帶寬。不過話說回來如果手頭只有普通F407最小系統(tǒng)板沒有USB3300也可以先把OTG_FS配置成Host模式試一下。我實測過MC665在Full-Speed下能正常枚舉CDC接口AT指令完全可用只是網(wǎng)絡數(shù)據(jù)速率會受限。這個取舍后面會細說。2. 硬件連接與關鍵電路設計2.1 最小硬件拓撲主控端使用的是STM32F407VET6USB Host部分用了OTG_HS外接Microchip USB3300芯片。連接關系如下STM32F407的OTG_HS ULPI接口USB_CLKPA5、USB_DIRPC2、USB_NXTPC3、USB_STPPC0、USB_D0~D7PA3、PA4、PA6、PA7、PB0、PB1、PB10、PB11以及ULPI的RESET自定義GPIO比如PE1。USB3300的DP/DM接到USB Type-A座子座子的VBUS接5V電源。MC665模塊通過USB線連接到這個Type-A座子或者直接通過板對板連接器把USB差分線引到模塊接口。這里有個容易忽略的點USB3300是3.3V供電但它的I/O電壓域需要和STM32匹配。F407的I/O是3.3VUSB3300也支持3.3V I/O直接連沒問題。但USB3300的時鐘輸入我建議直接給它一個24MHz有源晶振不要從STM32的MCO引腳引避免時鐘抖動導致枚舉不穩(wěn)定。2.2 VBUS供電4G模塊的電流沖擊比想象中大這是整個項目里坑最多的地方。MC665在LTE網(wǎng)絡下發(fā)射瞬態(tài)電流可以達到2A級別如果VBUS直接從STM32開發(fā)板的5V引腳取電幾乎一定會出問題——要么枚舉失敗要么模塊反復重啟要么電腦端能看到設備而F407就是枚舉不成功因為VBUS電壓在模塊發(fā)射瞬間被拉低到4.2V以下USB設備的供電跌落超出規(guī)范。正確的做法是VBUS從獨立的5V電源輸入比如12V轉(zhuǎn)5V的DC-DC或者專門的USB電源管理芯片并且要在靠近USB座子處加大容量電容至少220uF以上最好再加一個470uF的電解電容。同時VBUS上串一個500mA到1A的限流開關比如MIC2026或者TPS2041一來保護主控二來方便軟件控制模塊上電時序。STM32的USB Host庫在初始化時會檢測VBUS電壓做一個過流保護判斷所以VBUS的電壓檢測引腳必須正確連接否則軟件會一直報HOST_ERROR。2.3 MC665的上電時序與USB枚舉的關系MC665需要先供電然后等待模塊內(nèi)部啟動完成USB口才會開始枚舉。模塊啟動時間一般在3到10秒之間取決于SIM卡檢測和網(wǎng)絡搜索。因此硬件設計上要留一個控制模塊電源的GPIO最好通過MOS管開關模塊的VBAT供電。軟件的時序是先拉高模塊電源延時3秒再初始化USB Host庫開始枚舉。如果反過來先初始化USB再上電模塊大概率枚舉失敗。還有一種做法是模塊一直供電通過模塊的PWRKEY引腳來控制開關機。MC665的PWRKEY需要拉低至少500ms才能開機。我建議是VBAT常供PWRKEY由GPIO控制開機后再初始化USB。3. USB Host協(xié)議棧與CDC類驅(qū)動配置3.1 用STM32CubeMX生成USB Host基礎工程STM32CubeMX對USB Host的支持已經(jīng)很成熟了。配置步驟如下選擇MCU型號配置時鐘樹確保USB_OTG_HS使用48MHz時鐘。F407的OTG_HS要跑High-Speed的話需要配置ULPI時鐘源USB3300的時鐘由外部24MHz晶振提供然后內(nèi)部PLL產(chǎn)生60MHz給ULPI接口。如果跑Full-Speed可以直接用PLL48CK48MHz。在Connectivity中選擇USB_OTG_HSMode選擇Host_Only激活VBUS sensingULPI接口選上。這時CubeMX會自動把PHY的時鐘、數(shù)據(jù)引腳分配好。在Middleware中勾選USB_HostClass選擇CDC類。ST的USB Host庫對CDC類的支持是獨立的一層生成后會有一個usbh_cdc.c文件。生成代碼后加入USB_HOST_APP相關的回調(diào)文件主循環(huán)里調(diào)用MX_USB_HOST_Process()。這一步是基礎真正關鍵的改造在下一步CDC類的接收處理。3.2 官方CDC驅(qū)動的局限URB接收緩沖區(qū)不足ST官方生成的USB Host CDC類驅(qū)動默認的接收邏輯是等待設備發(fā)送數(shù)據(jù)收到一段就回調(diào)一次。問題在哪MC665的AT應答往往不是一個UART幀而是連續(xù)的文本流比如ATCGDCONT?的響應可能有幾百字節(jié)還分多行。如果官方驅(qū)動每次只接收用戶指定的長度默認往往是64字節(jié)或者配置描述符里的MaxPacketSize就會把一條完整的AT響應切成好幾段軟件解析起來非常痛苦。我在實際調(diào)試中第一次就踩了這個坑發(fā)了一條ATCSQ響應短沒問題。但發(fā)ATCGDCONT?返回的響應被切成了3段而且每段長度還不固定導致解析狀態(tài)機各種錯亂。解決方案是改造CDC接收邏輯改成“持續(xù)接收”模式在底層每次提交一個較大的URB接收請求比如512字節(jié)或者1024字節(jié)收到數(shù)據(jù)后不立即交給應用層而是先存入一個環(huán)形緩沖區(qū)。應用層AT解析器從環(huán)形緩沖區(qū)中按行\(zhòng)r\n或\n提取數(shù)據(jù)。這樣無論底層怎么分包上層都只看到連續(xù)的字節(jié)流解析邏輯和傳統(tǒng)的UART AT解析完全一致。3.3 CDC類Host驅(qū)動改造要點具體改造分三步第一步在usbh_cdc.c里修改接收緩沖區(qū)。官方代碼中CDC_Receive函數(shù)接收長度參數(shù)是CDC_DATA_MAX_PACKET_SIZE我把它改成用戶自定義的CDC_RX_BUFF_SIZE默認1024。同時增加一個機制當一次接收完成后立即再次調(diào)用接收函數(shù)形成不間斷接收鏈。第二步增加一個環(huán)形緩沖區(qū)。我習慣用經(jīng)典的ring_buf實現(xiàn)capicity 2048字節(jié)put和get兩個函數(shù)配合中斷或者輪詢使用。CDC接收回調(diào)函數(shù)里把收到的數(shù)據(jù)全部put到環(huán)形緩沖區(qū)AT解析任務則從緩沖區(qū)get數(shù)據(jù)。第三步注意并發(fā)訪問問題。USB Host庫是通過中斷和主循環(huán)配合工作的CDC接收回調(diào)可能在USB中斷上下文或者主循環(huán)中被調(diào)用而AT解析在獨立任務中運行所以環(huán)形緩沖區(qū)需要做成臨界區(qū)保護。簡單起見可以在get/put時臨時關中斷__disable_irq()/__enable_irq()數(shù)據(jù)量不大不會影響實時性。3.4 配置描述符解析確認你枚舉到的是不是CDC口MC665枚舉后會有多個配置接口ST的USB Host庫在連接后會自動解析配置描述符尋找CDC類接口。但有一個坑如果模塊的USB接口配置成了“僅網(wǎng)絡數(shù)據(jù)”比如RNDIS Only或者ECM Only那么枚舉結(jié)果里沒有CDC ACM接口USB Host庫會一直報告Device not supported。廣和通模塊通常有AT指令可以切換USB模式比如ATQCFGusbnet,0就是關閉網(wǎng)絡數(shù)據(jù)口只保留AT口和診斷口。如果你遇到枚舉成功但ST庫不認設備先檢查模塊當前的USB工作模式。這個排查思路很重要我后面會專門列出來。4. AT通信框架與核心代碼實現(xiàn)4.1 AT指令的完整交互模式不是簡單的發(fā)和收很多人寫AT通信就是HAL_UART_Transmit發(fā)一條指令然后HAL_UART_Receive等回復。這在USB CDC虛擬串口上也能跑但只適用于最簡單的場景。真實項目中AT指令交互有幾個模式同步應答型發(fā)ATCSQ\r\n模塊返回CSQ: 15,99\r\n\r\nOK\r\n這個模式比較簡單發(fā)完等待OK或者ERROR。異步上報型URC模塊主動上報事件比如網(wǎng)絡注冊狀態(tài)變化、TCP/IP連接斷開、短信到達對應QUSIM: 1、QHTTPURC: 2之類。這些數(shù)據(jù)不是你發(fā)指令觸發(fā)的而是隨機出現(xiàn)的。分段響應型模塊先返回一部分數(shù)據(jù)過幾百毫秒再返回剩余數(shù)據(jù)最后才返回OK。比如查詢SIM卡信息ATQCCID在某些固件版本下就會分兩段返回。所以在USB CDC通道上做AT通信必須實現(xiàn)一個狀態(tài)機解析器而不能簡單地“發(fā)一條、等一條”。4.2 核心代碼AT狀態(tài)機與URC處理我用的方案是一個AT任務負責發(fā)送指令一個解析器負責從環(huán)形緩沖區(qū)取數(shù)據(jù)、按行切割、匹配響應關鍵字。偽代碼如下// 環(huán)形緩沖區(qū) extern ring_buf_t cdc_rb; // AT響應解析狀態(tài)機 typedef enum { AT_IDLE, // 空閑 AT_WAIT_RESP, // 等待完整響應 AT_WAIT_FINAL, // 等待最終結(jié)果OK/ERROR AT_URC_HANDLE // 處理URC } at_state_t; // 按行切割并處理 void at_parser_process(void) { char line[128]; while (ring_buf_get_line(cdc_rb, line, sizeof(line)) 0) { at_state_machine(line); } } void at_state_machine(char *line) { // 去除行尾\r\n str_trim_crlf(line); if (strncmp(line, OK, 2) 0) { // 最終ACK當前指令完成 at_current_cmd_done(AT_OK); return; } if (strncmp(line, ERROR, 5) 0) { at_current_cmd_done(AT_ERROR); return; } if (strncmp(line, CME ERROR:, 11) 0) { // 模塊級別錯誤帶錯誤碼 at_current_cmd_done(AT_CME_ERROR); return; } // 判斷是否為URC主動上報 if (is_urc_line(line)) { at_urc_dispatch(line); return; } // 否則是當前指令的響應數(shù)據(jù)緩存起來 at_current_cmd_append(line); }這里的關鍵是is_urc_line的判斷邏輯。URC有兩種特征一是以開頭且不是指令回顯的特定命令集合二是有固定的前綴比如QUSIM、QHTTPURC。我的做法是維護一張URC前綴表逐條匹配。匹配到就立刻處理不參與當前指令的響應緩存。4.3 發(fā)指令時的加鎖與超時機制AT通道是共享資源任何任務比如周期查信號、TCP/IP連接管理、用戶命令要發(fā)指令都需要通過一個互斥接口。我習慣實現(xiàn)一個at_cmd_exec函數(shù)int at_cmd_exec(const char *cmd, int timeout_ms, char *resp_buf, int resp_len) { // 加鎖確保同一時間只有一條指令在線 if (at_busy) return AT_BUSY; at_busy 1; at_response_len 0; // 發(fā)送指令注意必須以\r\n結(jié)尾 cdc_send(cmd, strlen(cmd)); cdc_send(\r\n, 2); // 等待超時 uint32_t start HAL_GetTick(); while (HAL_GetTick() - start timeout_ms) { at_parser_process(); if (at_current_cmd_done_flag) { // 復制響應到用戶緩沖 memcpy(resp_buf, at_response_cache, at_response_len); at_busy 0; return AT_OK; } } // 超時 at_busy 0; return AT_TIMEOUT; }超時時間要根據(jù)指令類型調(diào)整。比如ATCSQ一般500ms足夠但ATCGDCONT或網(wǎng)絡查詢指令可能需要3到5秒。建議在at_cmd_exec之外再包一層給每條指令維護一個建議超時表。4.4 典型4G操作序列開發(fā)過程中標準的上線流程是// 1. 模塊開機檢測 at_cmd_exec(AT, 500, buf, sizeof(buf)); // 2. 關閉USB網(wǎng)絡數(shù)據(jù)口僅保留AT口按需 at_cmd_exec(ATQCFG\usbnet\,0, 1000, buf, sizeof(buf)); // 3. 查詢SIM卡是否就緒 // 返回 QUSIM: 1 表示SIM卡已檢測到 at_cmd_exec(ATQUSIM?, 1000, buf, sizeof(buf)); // 4. 查詢網(wǎng)絡注冊狀態(tài) // CEREG: 0,1 表示已注冊 at_cmd_exec(ATCEREG?, 1000, buf, sizeof(buf)); // 5. 設置APN國內(nèi)常用運營商APN如cmnet、ctnet、3gnet at_cmd_exec(ATCGDCONT1,\IP\,\cmnet\, 1000, buf, sizeof(buf)); // 6. 查詢信號質(zhì)量 // CSQ: 15,99 表示RSRP在-95dBm左右信號可接受 at_cmd_exec(ATCSQ, 1000, buf, sizeof(buf)); // 7. 發(fā)起撥號需要PPP撥號或TCP/IP協(xié)議棧時 at_cmd_exec(ATQIACT1, 5000, buf, sizeof(buf));注意第2步很關鍵。如果模塊之前被配置成NDIS模式USB枚舉出來的接口可能不包含CDC ACM導致ST USB Host庫根本識別不了。所以第一次使用模塊時先用USB轉(zhuǎn)串口工具連模塊的調(diào)試串口執(zhí)行一次ATQCFGusbnet,0保證模塊默認USB模式是純AT口模式再交給STM32去枚舉。4.5 FreeRTOS下的任務劃分建議如果項目跑FreeRTOS我建議把AT相關功能拆成三個任務USB Host任務優(yōu)先級中等負責調(diào)用MX_USB_HOST_Process()處理USB枚舉和CDC數(shù)據(jù)收發(fā)。AT解析任務優(yōu)先級偏低從環(huán)形緩沖區(qū)取數(shù)據(jù)、按行解析、處理URC。業(yè)務任務比如HTTP請求優(yōu)先級最低調(diào)用at_cmd_exec發(fā)送指令通過信號量等待響應。發(fā)送AT指令和USB Host過程不能放在同一個任務里執(zhí)行否則長指令等待時會阻塞USB Host的狀態(tài)機導致接收處理不及時。個人經(jīng)驗是USB Host任務保持高頻運行2ms周期AT解析任務在USB事件觸發(fā)后或輪詢時處理。5. 常見問題與排查技巧實錄5.1 枚舉失敗設備沒有出現(xiàn)在總線上現(xiàn)象STM32的USB Host庫一直停在HOST_DEV_ATTACHED或者HOST_PORT_ENUMERATION狀態(tài)打印調(diào)試信息發(fā)現(xiàn)狀態(tài)機卡住。排查步驟確認模塊已經(jīng)正常開機??梢酝ㄟ^模塊的NETLIGHT引腳指示燈判斷常亮或慢閃說明模塊已啟動如果不亮查VBAT和PWRKEY時序。用USB分析儀或者電腦插上模塊確認模塊USB枚舉是否正常。這一步排除模塊本身的問題。檢查VBUS電壓在模塊發(fā)射瞬間用示波器抓VBUS波形如果跌落超過5%就要加強供電。檢查USB3300與F407的ULPI接口信號特別是USB_CLK。USB3300要求F407的ULPI接口時鐘必須是60MHz如果時鐘配置不對數(shù)據(jù)線完全沒法工作。檢查DP/DM走線USB差分線長度盡量短不建議超過5厘米否則信號質(zhì)量會變差。5.2 枚舉成功但AT指令無響應現(xiàn)象USB Host庫成功枚舉了CDC接口CDC_Receive也沒報錯但發(fā)AT\r\n后沒有任何回應。原因分析最常見的原因是模塊的USB虛擬串口通道還沒完全就緒。模塊開機后需要幾秒鐘初始化USB協(xié)議棧雖然枚舉發(fā)生在模塊啟動后但AT通道可能還要再等一會兒。解決方法是枚舉成功后再延時1到2秒再開始發(fā)AT指令。第二個常見原因是指令格式問題。AT指令必須以\r0x0D結(jié)尾有些模塊還要\r\n。我在調(diào)試時發(fā)現(xiàn)MC665對純\n結(jié)尾不響應必須在代碼里統(tǒng)一用\r\n。第三個原因是之前提到的USB模式不對。如果模塊被配成RNDIS OnlyST的CDC類驅(qū)動雖然能枚舉出CDC數(shù)據(jù)口但不是ACM控制口AT指令就沒有通道可走。用ATQCFGusbnet,0切回來。5.3 響應亂碼或半包現(xiàn)象收到的AT響應出現(xiàn)亂碼、缺字符、行被拆斷。原因多半是接收緩沖區(qū)處理的問題。如果CDC底層每次只接收64字節(jié)就回調(diào)一次且上層沒有做數(shù)據(jù)拼接就會出現(xiàn)半包。解決方法是改造接收鏈路用大緩沖區(qū)環(huán)形緩沖確保數(shù)據(jù)連續(xù)。另外當模塊同時啟用多個USB接口比如AT口和DIAG口操作系統(tǒng)枚舉時可能CDC類的接口編號變化USB Host庫選錯了接口。這種情況需要檢查配置描述符解析確保選擇的是Interface Class0x02CDC Communication Interface或者0x0ACDC Data Interface中的正確組合。5.4 信號質(zhì)量和網(wǎng)絡注冊的坑現(xiàn)象AT都能通但ATCEREG?返回CEREG: 0,0或者CEREG: 0,2網(wǎng)絡一直注冊不上。原因SIM卡沒插好或者天線信號太弱。MC665的ANT_MAIN天線如果不接模塊雖然能開機但網(wǎng)絡搜索會持續(xù)失敗。另外某些SIM卡需要PIN碼模塊沒有自動解鎖也會導致注冊失敗。用ATCPIN?查詢?nèi)绻祷谻ME ERROR: 10就說明SIM卡未就緒需要ATCPIN1234解鎖或者換一張無PIN卡的SIM卡。還有一個容易被忽視的點APN配置。ATCGDCONT1,\IP\,\cmnet\里的APN必須和SIM卡運營商匹配。聯(lián)通的APN是3gnet或wonet電信是ctnet。如果APN錯誤注冊網(wǎng)絡正常但ATQIACT1激活PDP上下文時會一直失敗返回CME ERROR: 50或者QACT: ERROR之類。5.5 實測數(shù)據(jù)USB Host vs UART 對比我在項目里做了幾組對比測試主控同一塊板子分別用USB Host通道和UART通道向MC665發(fā)100條ATCSQ指令統(tǒng)計響應時間和成功率通道平均響應時間成功率備注UART 11520012ms99%偶發(fā)丟字節(jié)UART 9216009ms96%高波特率下信號質(zhì)量敏感USB Full-Speed8ms100%無明顯丟字節(jié)USB High-Speed7ms100%最佳USB通道的優(yōu)勢主要體現(xiàn)在長時間大數(shù)據(jù)量傳輸上AT指令這種短報文其實差別不大。但省掉UART電平轉(zhuǎn)換芯片后硬件簡單了很多而且USB的可靠性在復雜電磁環(huán)境下更有保障。6. 項目收益與擴展方向6.1 硬件BOM成本降低與可靠性提升去掉USB轉(zhuǎn)串口芯片比如CH340G單顆成本在1元左右還占用一個USB Host口去掉電平轉(zhuǎn)換芯片省下的BOM成本雖然不多但PCB面積可以縮小不少而且減少了焊接點生產(chǎn)良率更高。更重要的是USB Host口同時可以連接其他USB設備比如U盤、鍵鼠如果后續(xù)要做數(shù)據(jù)采集和存儲這個口能復用。我在這個項目里還做了一個小擴展STM32F407做USB Host主控同時通過USB Hub掛接MC665和一塊USB攝像頭模塊。MC665負責4G回傳攝像頭負責圖像采集整機沒有一顆USB轉(zhuǎn)串口芯片。這個結(jié)構(gòu)在做手持巡檢設備時非常實用。6.2 從AT指令到網(wǎng)絡透傳AT指令只是第一步。如果要做TCP/IP數(shù)據(jù)透傳有兩種路徑一是用模塊內(nèi)置的TCP/IP協(xié)議棧通過ATQIOPEN、ATQISEND、ATQIRD這些指令實現(xiàn)Socket通信。這種方式的開發(fā)成本低但吞吐量受AT指令交互頻率限制適合小數(shù)據(jù)量場景。二是用USB Host連接模塊的RNDIS口把模塊變成一張?zhí)摂M網(wǎng)卡F407跑LWIP協(xié)議棧進行標準TCP/IP通信。這種方式吞吐量高但需要USB Host同時支持CDC ACMAT口和RNDIS數(shù)據(jù)口而且要處理多接口的配置復雜度上升一個檔次。AT指令開發(fā)是4G設備聯(lián)調(diào)的基礎把這條鏈路摸透了后面接MQTT、HTTP、OTA升級都是水到渠成的事。6.3 分享一個調(diào)USB Host時的小習慣我在調(diào)試STM32 USB Host時習慣在關鍵狀態(tài)切換點加一個串口打印宏把狀態(tài)機打印出來#define USB_DEBUG(fmt, ...) \ do { printf([USB] fmt \r\n, ##__VA_ARGS__); } while(0)然后在USBH_Process的switch-case的每個入口打狀態(tài)名。這樣模塊枚舉到哪一步卡住一眼就能看出來。實測下來排查枚舉問題效率提高一大截。MC665和F407這套組合我前前后后調(diào)了兩周最大的體會是USB Host沒有想象中那么難但也沒有CubeMX生成的代碼那么省心。數(shù)據(jù)鏈路層的問題解決之后AT指令層才是真正決定項目能不能跑通的地方。希望這篇記錄能幫你少走幾步彎路。