產(chǎn)MCU實(shí)戰(zhàn)復(fù)盤(pán):GD32項(xiàng)目中的選型、移植與調(diào)試經(jīng)驗(yàn))
上個(gè)月接了一個(gè)小終端產(chǎn)品的硬件設(shè)計(jì)需求不復(fù)雜一個(gè)電池供電的環(huán)境監(jiān)測(cè)節(jié)點(diǎn)采集溫濕度和光照數(shù)據(jù)一塊小屏做本地顯示兩三個(gè)按鍵做交互數(shù)據(jù)通過(guò)串口上報(bào)給上位機(jī)。本來(lái)這種項(xiàng)目我應(yīng)該閉著眼睛選型結(jié)果供應(yīng)鏈那邊說(shuō)常用的那款MCU現(xiàn)在交期和價(jià)格都很難談建議我評(píng)估一下國(guó)產(chǎn)MCU。就這么著我開(kāi)始了這次重新認(rèn)識(shí)之旅。說(shuō)實(shí)話過(guò)去幾年我對(duì)國(guó)產(chǎn)MCU的印象停留在能用但別較真的階段??傆X(jué)得資料沒(méi)有國(guó)外大廠全工具鏈沒(méi)人家順生態(tài)更是差了半條街。但這次項(xiàng)目做下來(lái)我得承認(rèn)有些想法過(guò)時(shí)了。這個(gè)項(xiàng)目讓我真實(shí)體驗(yàn)到國(guó)產(chǎn)MCU這幾年的進(jìn)步也踩了不少只有實(shí)際動(dòng)手才能發(fā)現(xiàn)的坑。整個(gè)過(guò)程走完有些話確實(shí)不吐不快。這篇內(nèi)容不是評(píng)測(cè)也不是選型報(bào)告就是一次真實(shí)的項(xiàng)目復(fù)盤(pán)。我會(huì)把從需求拆解、選型、原理圖設(shè)計(jì)、PCB回板、軟件移植到調(diào)試見(jiàn)效的完整過(guò)程捋一遍把我遇到的坑、查過(guò)的資料、改過(guò)的代碼和一些只有踩過(guò)才知道的經(jīng)驗(yàn)盡量原原本本地寫(xiě)出來(lái)。如果你正在評(píng)估國(guó)產(chǎn)MCU或者準(zhǔn)備把一個(gè)老項(xiàng)目的國(guó)外MCU方案切換到國(guó)產(chǎn)芯片這篇東西應(yīng)該能幫你節(jié)省不少時(shí)間。1. 項(xiàng)目需求與總體方案設(shè)計(jì)1.1 需求梳理與選型思路先把這個(gè)項(xiàng)目具體是什么說(shuō)清楚。硬件上需要一顆MCU做系統(tǒng)主控傳感器有SHT30溫濕度傳感器和一顆光敏電阻屏幕用0.96寸的OLEDSSD1306控制器供電是一節(jié)18650電池通過(guò)DCDC降到3.3V所以MCU必須支持低功耗模式平時(shí)休眠定時(shí)醒來(lái)采集數(shù)據(jù)。交互就兩個(gè)按鍵一個(gè)喚醒一個(gè)切換顯示頁(yè)面。通信接口留一個(gè)UART方便調(diào)試和上報(bào)數(shù)據(jù)。這種需求放在五年前我第一反應(yīng)肯定是STM32F103系列但今時(shí)不同往日那顆芯片的價(jià)格和市場(chǎng)流通量已經(jīng)不適合新項(xiàng)目使用了。所以這次我把選型范圍定在了國(guó)產(chǎn)MCU上。綜合手頭資料和社區(qū)口碑我篩選了三款備選兆易創(chuàng)新GD32F303CBT6Cortex-M4F內(nèi)核120MHz主頻64KB Flash20KB SRAM極海APM32F103RCT6Cortex-M3內(nèi)核96MHz主頻256KB Flash48KB SRAM沁恒CH32V307VCT6RISC-V內(nèi)核144MHz主頻256KB Flash64KB SRAM選擇GD32F303CBT6有幾個(gè)原因。第一它的引腳和STM32F103系列高度兼容老項(xiàng)目改板成本低PCB布局不用大動(dòng)。第二網(wǎng)上GD32的資料相對(duì)另外兩家更豐富社區(qū)討論也多出了問(wèn)題容易搜到答案。第三Cortex-M4F內(nèi)核帶硬件浮點(diǎn)雖然這個(gè)項(xiàng)目用不上但未來(lái)如果要加傳感器融合算法性能余量是有的。這里插一句選型的核心邏輯對(duì)量產(chǎn)產(chǎn)品來(lái)說(shuō)選MCU不能只看主頻和Flash大小要看供應(yīng)鏈穩(wěn)定性、資料完整度、引腳兼容性、開(kāi)發(fā)工具鏈成熟度以及你在網(wǎng)上能不能搜到別人踩坑的記錄。GD32在這幾項(xiàng)的綜合表現(xiàn)目前是國(guó)產(chǎn)里的第一梯隊(duì)。1.2 架構(gòu)設(shè)計(jì)與外設(shè)分配系統(tǒng)架構(gòu)我用文字描述一下不畫(huà)框圖了省得顯得教學(xué)。MCU是整個(gè)系統(tǒng)的中樞SHT30掛在I2C1總線上地址0x44數(shù)據(jù)線接了上拉電阻。OLED屏幕掛在SPI1上這里用了硬件SPI因?yàn)槠聊凰⑿滦枰l繁傳輸數(shù)據(jù)軟件模擬SPI會(huì)占用大量CPU時(shí)間。光敏電阻和電池電壓檢測(cè)分別接到ADC1的通道0和通道1。兩個(gè)按鍵一個(gè)接外部中斷引腳一個(gè)接普通GPIO。串口用USART1波特率115200。外設(shè)分配看起來(lái)簡(jiǎn)單但這里每個(gè)選擇背后都有原因。傳感器用I2C是因?yàn)镾HT30只有I2C接口而且I2C只需要兩根線省引腳。OLED用SPI是因?yàn)镾PI的速率遠(yuǎn)高于I2C刷屏不卡頓體驗(yàn)好很多。電池電壓檢測(cè)不能直接接MCU引腳因?yàn)殇囯姵貪M電4.2V超過(guò)MCU的3.3V耐壓所以通過(guò)兩個(gè)電阻分壓后再進(jìn)ADC。光敏電阻就更簡(jiǎn)單了和固定電阻串聯(lián)中間抽頭進(jìn)ADC通過(guò)分壓比反推光照強(qiáng)度。在做這個(gè)設(shè)計(jì)的時(shí)候我還專(zhuān)門(mén)捋了一遍MCU的啟動(dòng)流程因?yàn)檫@次的固件需要支持后續(xù)OTA升級(jí)我會(huì)在bootloader里做跳轉(zhuǎn)。順便說(shuō)一嘴MCU和SoC的啟動(dòng)流程完全是兩碼事。SoC通常需要從片內(nèi)BootROM開(kāi)始執(zhí)行引導(dǎo)代碼再由引導(dǎo)程序初始化DDR、加載操作系統(tǒng)鏡像整個(gè)啟動(dòng)過(guò)程是分階段、依賴(lài)外部存儲(chǔ)的。而MCU的啟動(dòng)簡(jiǎn)單直接復(fù)位后從映射地址取向量表根據(jù)Boot引腳電平?jīng)Q定從主Flash、系統(tǒng)存儲(chǔ)器還是SRAM啟動(dòng)向量表里第一個(gè)字是棧頂?shù)刂返诙€(gè)字是復(fù)位中斷入口CPU直接從那里開(kāi)始執(zhí)行。搞清楚了這一點(diǎn)后面做bootloader跳轉(zhuǎn)時(shí)就不會(huì)犯用SoC思維寫(xiě)MCU固件的錯(cuò)了。2. 真正的門(mén)檻硬件細(xì)節(jié)與外設(shè)差異2.1 I2C通信沒(méi)有想象中那么兼容這可能是整個(gè)項(xiàng)目里讓我印象最深的一個(gè)坎。選型的時(shí)候網(wǎng)上鋪天蓋地的宣傳都說(shuō)GD32和STM32是引腳兼容、軟件兼容。我也天真地認(rèn)為代碼層面改一下底層庫(kù)應(yīng)用層直接搬過(guò)去就行。結(jié)果第一版固件燒進(jìn)去SHT30的溫濕度數(shù)據(jù)偶爾就卡住不更新了。用邏輯分析儀看波形發(fā)現(xiàn)I2C總線上SDA被拉低總線進(jìn)入死鎖狀態(tài)標(biāo)準(zhǔn)的總線卡死現(xiàn)象。為什么STM32上從來(lái)沒(méi)遇到這個(gè)問(wèn)題我去查了GD32F303的參考手冊(cè)又翻了社區(qū)里的討論發(fā)現(xiàn)GD32的硬件I2C模塊在狀態(tài)機(jī)處理和時(shí)鐘延展行為上和ST的實(shí)現(xiàn)確實(shí)有差異。尤其是在多字節(jié)連續(xù)讀操作和NACK響應(yīng)的時(shí)序窗口上GD32的處理方式跟傳感器從機(jī)的期望不完全一致再加上SHT30內(nèi)部狀態(tài)機(jī)比較復(fù)雜就容易出現(xiàn)通信錯(cuò)亂。網(wǎng)上還有一種說(shuō)法是GD32部分批次的I2C模塊存在設(shè)計(jì)缺陷官方建議直接用軟件模擬I2C。這個(gè)說(shuō)法真?zhèn)挝覠o(wú)法考證但我在實(shí)際項(xiàng)目中確實(shí)用硬件I2C遇到了偶發(fā)卡死換軟件模擬I2C之后問(wèn)題消失而且連續(xù)跑了72小時(shí)壓測(cè)一次都沒(méi)再出問(wèn)題。所以這里的方案就很明確了I2C外設(shè)驅(qū)動(dòng)全部改用GPIO模擬掉坑概率小不做定時(shí)器分頻配置代碼寫(xiě)起來(lái)也直觀。我寫(xiě)了一個(gè)精簡(jiǎn)的軟件I2C驅(qū)動(dòng)標(biāo)準(zhǔn)100kHz速率帶超時(shí)退避和總線恢復(fù)邏輯檢測(cè)到SDA被拉低超過(guò)一定時(shí)間就額外發(fā)9個(gè)SCL脈沖嘗試釋放總線。這套邏輯在工控環(huán)境里經(jīng)受過(guò)考驗(yàn)比單純依賴(lài)硬件外設(shè)可靠得多。還有一點(diǎn)這個(gè)I2C的問(wèn)題不是GD32獨(dú)有其他國(guó)產(chǎn)MCU也或多或少存在類(lèi)似現(xiàn)象。原因是不同廠家的半導(dǎo)體工藝和外設(shè)IP設(shè)計(jì)不同硬件寄存器的微秒級(jí)時(shí)序完全對(duì)齊是不可能的。所以做跨廠商移植時(shí)外設(shè)行為驗(yàn)證必須放在實(shí)際硬件上做不能只在軟件層覺(jué)得兼容。我后來(lái)在調(diào)試HUSB238這顆PD誘騙芯片時(shí)也驗(yàn)證了這一點(diǎn)。HUSB238和MCU之間走I2C通信用來(lái)配置PD誘騙電壓檔位MCU通過(guò)發(fā)送指令讓HUSB238去和充電器協(xié)議握手。如果MCU的I2C時(shí)序稍微有一點(diǎn)點(diǎn)偏差HUSB238返回的ACK都可能丟失導(dǎo)致配置不生效。這類(lèi)電源協(xié)商芯片對(duì)時(shí)序比一般傳感器更敏感驗(yàn)證外設(shè)行為時(shí)值得多花點(diǎn)時(shí)間。2.2 ADC采樣、參考電壓與濾波ADC部分是另一個(gè)體驗(yàn)差異很大的地方。這項(xiàng)目需要采集兩路模擬量電池電壓和光敏電阻電壓。GD32F303的ADC是12位精度理論上足夠用但實(shí)際采樣數(shù)據(jù)有個(gè)非常明顯的問(wèn)題數(shù)值會(huì)規(guī)律性跳變跳動(dòng)幅度大約有十幾到二十幾個(gè)LSB。這個(gè)問(wèn)題在STM32上沒(méi)這么明顯換成GD32之后開(kāi)始頻繁出現(xiàn)一度懷疑是PCB布線的問(wèn)題。排查過(guò)程很曲折。先用示波器測(cè)了參考電壓VREF紋波只有10mV左右排除電源問(wèn)題。又換了不同批次的芯片現(xiàn)象依舊。最后翻到參考手冊(cè)里的ADC章節(jié)發(fā)現(xiàn)GD32的ADC采樣時(shí)間配置對(duì)于高內(nèi)阻輸入源確實(shí)不夠充分。當(dāng)信號(hào)源內(nèi)阻較大時(shí)ADC內(nèi)部的采樣電容在有限采樣時(shí)間內(nèi)充不滿導(dǎo)致采樣結(jié)果偏差這個(gè)就是經(jīng)典的采樣時(shí)間不足導(dǎo)致建立誤差。實(shí)際解決思路分三路走。第一路把ADC采樣周期從默認(rèn)的1.5個(gè)周期調(diào)到最大239.5個(gè)周期保證采樣電容充足充電時(shí)間。第二路子對(duì)每路數(shù)據(jù)連續(xù)采樣16次去掉最大值和最小值后取平均相當(dāng)于做了一次中值加均值混合濾波。第三路啟動(dòng)ADC自校準(zhǔn)功能讀取校準(zhǔn)值并寫(xiě)入校準(zhǔn)寄存器。改完這三處之后ADC數(shù)據(jù)跳動(dòng)從二十幾個(gè)LSB降到了三四個(gè)LSB以內(nèi)完全滿足項(xiàng)目精度要求。這里我額外說(shuō)一句ADC采樣問(wèn)題在MCU項(xiàng)目里實(shí)際上是千古難題國(guó)產(chǎn)MCU和國(guó)外MCU都存在只是表現(xiàn)程度和藏坑位置不同。做信號(hào)采集類(lèi)應(yīng)用時(shí)采樣時(shí)間、參考電壓、PCB布局、濾波算法這四件事必須一起考慮單靠調(diào)一個(gè)參數(shù)解決不了問(wèn)題。采樣的應(yīng)用場(chǎng)景還可以延展一下。比如咪頭麥克風(fēng)輸出ADC給MCU的電路這種音頻采集場(chǎng)景比我的電池分壓采樣復(fù)雜得多。麥克風(fēng)輸出的是交流小信號(hào)必須加偏置電路把信號(hào)抬高到ADC輸入范圍的中間點(diǎn)比如1.65V然后再進(jìn)ADC。同時(shí)還要考慮采樣率和抗混疊濾波器。如果用MCU的ADC采音頻采樣率至少要8kHz以上采樣時(shí)間要匹配信號(hào)帶寬否則高頻成分會(huì)混疊到低頻段聲音就變了。光模塊里的監(jiān)控MCU也是類(lèi)似邏輯那些小封裝MCU要采集激光器的溫度、偏置電流、光功率等多個(gè)模擬量通過(guò)ADC轉(zhuǎn)成數(shù)字量后走I2C或SPI上報(bào)給上位機(jī)對(duì)ADC通道數(shù)、精度和小封裝的要求都很高。這類(lèi)應(yīng)用的共通點(diǎn)就是ADC必須做校準(zhǔn)采樣時(shí)間必須按源阻抗去算濾波必須結(jié)合信號(hào)頻率設(shè)計(jì)。3. 實(shí)操過(guò)程與工具鏈體驗(yàn)3.1 從STM32工程移植到GD32的步驟軟件移植是整個(gè)項(xiàng)目里工作量最集中的部分。我把一個(gè)之前的STM32工程基礎(chǔ)框架遷到GD32上大體步驟可以整理成下面幾條。這套流程同樣適用于其他國(guó)產(chǎn)MCU平臺(tái)切換值得收藏。第一步替換啟動(dòng)文件。不同的MCU啟動(dòng)文件不一樣主要差別在中斷向量表和堆棧初始化。GD32的啟動(dòng)文件里中斷向量表的前兩項(xiàng)和ST是一樣的但后面的中斷號(hào)順序可能不同比如某些外設(shè)中斷號(hào)會(huì)錯(cuò)位。如果用錯(cuò)啟動(dòng)文件中斷回調(diào)永遠(yuǎn)不會(huì)觸發(fā)而且很難排查。這個(gè)坑我在另一個(gè)項(xiàng)目里踩過(guò)當(dāng)時(shí)RTC鬧鐘中斷怎么都不進(jìn)折騰了一整天才發(fā)現(xiàn)是啟動(dòng)文件里中斷向量表對(duì)不上。第二步調(diào)整鏈接腳本。Flash和RAM的起始地址、大小必須和具體芯片匹配。GD32F303CBT6是64KB Flash、20KB SRAM和STM32F103C8T6的64KB、20KB一樣所以鏈接腳本基本不用改。但如果你選的是GD32F303RCT6這種256KB Flash的型號(hào)而且原來(lái)的工程是128KB Flash的就要檢查有沒(méi)有越界的情況。鏈接腳本不對(duì)編譯能過(guò)燒進(jìn)去直接HardFault這種問(wèn)題最容易讓人懷疑人生。第三步換外設(shè)庫(kù)。這是最繁瑣的一步也是理解兼容兩字含義的關(guān)鍵。GD32官方提供了一套標(biāo)準(zhǔn)外設(shè)庫(kù)函數(shù)名和ST的標(biāo)準(zhǔn)外設(shè)庫(kù)很相似但并非完全一致。比如I2C初始化ST的代碼是I2C_InitStructure.I2C_ModeGD32則變成了i2c_clock_config加上i2c_mode_config不同的函數(shù)封裝方式。直接把ST代碼搬過(guò)去編譯會(huì)有大量報(bào)錯(cuò)需要逐個(gè)函數(shù)對(duì)照替換。第四步時(shí)鐘配置。這一條單獨(dú)抽出來(lái)說(shuō)因?yàn)樘菀缀雎郧液蠊麌?yán)重。GD32F303支持8到32MHz外部晶振內(nèi)部有PLL倍頻。我之前把STM32工程直接拿過(guò)來(lái)外部晶振8MHzPLL配置也是按STM32F103來(lái)的9倍頻得到72MHz系統(tǒng)時(shí)鐘。結(jié)果GD32這部分代碼執(zhí)行之后串口輸出全部是亂碼排查了半天才發(fā)現(xiàn)GD32F303的PLL倍頻上限更高而且默認(rèn)的flash等待周期配置和ST不一樣導(dǎo)致CPU超過(guò)了Flash的訪問(wèn)速度極限程序跑飛。詳細(xì)過(guò)程放在3.2節(jié)說(shuō)。3.2 時(shí)鐘配置的坑為什么串口亂碼這個(gè)坑單獨(dú)拿出來(lái)講因?yàn)橹灰阕鰢?guó)產(chǎn)MCU移植基本都會(huì)遇到類(lèi)似的串口亂碼問(wèn)題。亂碼的根源基本可以鎖定在系統(tǒng)時(shí)鐘配置錯(cuò)誤導(dǎo)致波特率發(fā)生器算出來(lái)的分頻值和USB外設(shè)的實(shí)際工作頻率偏離預(yù)期。當(dāng)時(shí)第一版代碼刷進(jìn)去上電后串口助手收到的全是0x00、0xFF這種毫無(wú)規(guī)律的字節(jié)。第一時(shí)間用萬(wàn)用表測(cè)晶振兩腳電壓有正常波形排除外部晶振不起振。接著用示波器抓MCU的MCO引腳可以把內(nèi)部時(shí)鐘輸出到引腳發(fā)現(xiàn)輸出的時(shí)鐘頻率完全不對(duì)說(shuō)明問(wèn)題在PLL配置環(huán)節(jié)。我對(duì)照GD32F303的參考手冊(cè)重新算了一遍。外部晶振8MHzPLL倍頻選擇18倍得到144MHz這不滿足USB要求的48MHz144/348MHz理論上可以但系統(tǒng)主頻超過(guò)120MHz工作范圍了Flash等待周期必須要設(shè)置正確。我抄的STM32代碼里設(shè)置的是2個(gè)等待周期而GD32F303在120MHz主頻下需要至少3個(gè)等待周期。等待周期不夠Flash讀取會(huì)出錯(cuò)程序就跑飛。這是在為什么串口亂碼表象下藏得很深的原因。改法也很直接設(shè)置PLL為15倍頻8MHz乘15等于120MHz正好踩在最高主頻點(diǎn)上然后Flash等待周期配置為3個(gè)USB分頻器配置為2.5分頻即120/2.548MHz這樣整個(gè)時(shí)鐘樹(shù)就合理了。改動(dòng)就幾行代碼但背后是整個(gè)時(shí)鐘路徑的重新計(jì)算。這個(gè)問(wèn)題的通用教訓(xùn)是跨廠商移植時(shí)時(shí)鐘樹(shù)必須對(duì)照參考手冊(cè)重新核算不能直接沿用舊工程參數(shù)。尤其是外部晶振頻率、PLL倍頻系數(shù)、AHB/APB分頻器、Flash等待周期這四項(xiàng)任何一個(gè)不匹配都可能造成系統(tǒng)時(shí)鐘跑錯(cuò)最終表現(xiàn)就是串口亂碼、定時(shí)器時(shí)間不對(duì)、外設(shè)時(shí)序異常這些讓人抓狂的問(wèn)題。做這一步時(shí)務(wù)必打開(kāi)芯片手冊(cè)的時(shí)鐘樹(shù)頁(yè)面一條路徑一條路徑地核對(duì)別怕花時(shí)間這個(gè)時(shí)間花得值。3.3 VSCode GCC AI輔助寫(xiě)MCU工程的體驗(yàn)這個(gè)項(xiàng)目還有一個(gè)新變量我嘗試了用VSCode做主力IDE同時(shí)用Claude Code輔助生成和重構(gòu)底層代碼。以前這種項(xiàng)目我都是在Keil里做但Keil的編輯體驗(yàn)和代碼補(bǔ)全能力說(shuō)實(shí)話一般。這次趁著評(píng)估新MCU的機(jī)會(huì)把工具鏈也換了一遍。工具鏈的搭建并不復(fù)雜。編譯器用的是arm-none-eabi-gcc調(diào)試器用DAP-LinkST-Link也能用但GD32的SWD接口實(shí)測(cè)DAP-Link更穩(wěn)VSCode里裝EIDE插件管理工程配置文件用JSON維護(hù)編譯、燒錄、調(diào)試都在VSCode里完成。這套組合在社區(qū)里的使用人數(shù)已經(jīng)很多了遇到問(wèn)題搜一下基本都有答案。Claude Code在這個(gè)項(xiàng)目里的角色其實(shí)挺典型的。我確實(shí)讓它幫我生成了一大段GD32的外設(shè)初始化代碼包括GPIO復(fù)用配置、USART初始化、ADC掃描組配置這些重復(fù)度高的東西。它的速度和規(guī)范性很好生成的代碼結(jié)構(gòu)工整注釋也到位比手敲要快很多。這里這個(gè)環(huán)節(jié)反而是最有代表性的我可以讓AI先按GD32的庫(kù)函數(shù)寫(xiě)一版然后我再把生成的代碼和參考手冊(cè)對(duì)照檢查一遍特別是寄存器位域和時(shí)鐘使能部分確認(rèn)沒(méi)有任何遺漏。但這里有個(gè)很重要的提醒AI生成的代碼只能作為起點(diǎn)不能直接燒進(jìn)板子。我在這個(gè)項(xiàng)目里就遇到一個(gè)典型的錯(cuò)誤。我讓Claude Code寫(xiě)一段使用LL庫(kù)函數(shù)的代碼因?yàn)樗敖佑|過(guò)STM32的LL庫(kù)但GD32的標(biāo)準(zhǔn)外設(shè)庫(kù)根本沒(méi)有LL庫(kù)這套API生成的代碼連編譯都過(guò)不了。還有一個(gè)更隱蔽的問(wèn)題AI根據(jù)經(jīng)驗(yàn)自動(dòng)把某個(gè)GPIO的復(fù)用功能配置成AFIO模式但實(shí)際上GD32F303該引腳的復(fù)用功能映射和STM32F103并不完全一致導(dǎo)致功能不正常。這些問(wèn)題只能靠人來(lái)判斷AI解決不了你用的是哪顆芯片這種語(yǔ)義層面的問(wèn)題。所以我的建議是可以用AI輔助生成模板代碼、寫(xiě)注釋、做代碼審查但關(guān)鍵外設(shè)的寄存器配置和時(shí)鐘樹(shù)參數(shù)一定要拿參考手冊(cè)一手核對(duì)。讓AI做閱讀理解人類(lèi)做最終決策這是目前最穩(wěn)的方式。4. 常見(jiàn)問(wèn)題與調(diào)試經(jīng)驗(yàn)整理4.1 問(wèn)題速查表做項(xiàng)目過(guò)程中踩了不少坑我把最典型的幾個(gè)整理成一張速查表。這張表對(duì)正在做國(guó)產(chǎn)MCU開(kāi)發(fā)的人應(yīng)該有點(diǎn)參考價(jià)值。現(xiàn)象可能原因解決方案?jìng)渥⒋谳敵鰜y碼PLL倍頻配置錯(cuò)誤、Flash等待周期不足按參考手冊(cè)時(shí)鐘樹(shù)重算PLL和等待周期核對(duì)外部晶振頻率8MHz倍頻15120MHz時(shí)等待周期為3I2C總線卡死、SDA被拉低硬件I2C狀態(tài)機(jī)與從機(jī)兼容性差改用軟件模擬I2C加總線恢復(fù)邏輯采用GPIO模擬超時(shí)退避最穩(wěn)ADC數(shù)值規(guī)律性跳變采樣時(shí)間不足、參考電壓不穩(wěn)設(shè)置最長(zhǎng)采樣周期多次采樣濾波開(kāi)啟校準(zhǔn)信號(hào)源內(nèi)阻高時(shí)需要更長(zhǎng)的采樣時(shí)間喚醒后外設(shè)不工作低功耗模式下外設(shè)時(shí)鐘未重新使能喚醒后重新初始化外設(shè)時(shí)鐘和DMA檢查喚醒中斷里是否需要重建時(shí)鐘配置看門(mén)狗頻繁復(fù)位喂狗位置不對(duì)或主循環(huán)卡死在任務(wù)調(diào)度最頻繁處喂狗避免在中斷里喂中斷喂狗會(huì)導(dǎo)致主循環(huán)卡死但看門(mén)狗不復(fù)位Flash寫(xiě)入導(dǎo)致程序卡死擦寫(xiě)操作期間CPU停止取指將Flash寫(xiě)入代碼放到RAM中執(zhí)行或使用官方Flash編程接口寫(xiě)Flash時(shí)關(guān)中斷或搬到RAM執(zhí)行空指針/棧溢出導(dǎo)致HardFault啟動(dòng)文件堆棧大小配置不足調(diào)整鏈接腳本中的堆棧大小檢查中斷向量表裸機(jī)開(kāi)發(fā)時(shí)預(yù)留足夠??臻g給中斷嵌套printf只能在調(diào)試模式下輸出printf重定向函數(shù)未實(shí)現(xiàn)重寫(xiě)fputc函數(shù)映射到UART發(fā)送寄存器用GCC工具鏈時(shí)要確保用retarget機(jī)制這個(gè)表看起來(lái)像是八股文但這幾個(gè)問(wèn)題每一個(gè)都是我在實(shí)際項(xiàng)目中碰到的有些還是折騰了兩三天才定位的。千萬(wàn)別小看這類(lèi)低級(jí)問(wèn)題它們往往最消耗時(shí)間。4.2 調(diào)試心得與必備工具如果要在這次項(xiàng)目里提煉幾條調(diào)試心得我想重點(diǎn)說(shuō)工具。第一個(gè)必配的是邏輯分析儀幾十塊錢(qián)的USB邏輯分析儀就夠用在分析I2C通信、UART數(shù)據(jù)、SPI時(shí)序的時(shí)候能省掉大量瞎猜時(shí)間。我當(dāng)時(shí)排查SHT30卡死問(wèn)題就是靠邏輯分析儀抓到SDA在一組數(shù)據(jù)后沒(méi)有正常釋放直接鎖定了問(wèn)題方向。第二個(gè)是示波器邏輯分析儀看的是協(xié)議對(duì)不對(duì)示波器看的是電平好不好。MCU引腳輸出的信號(hào)上升沿太緩、上拉電阻阻值不對(duì)、串?dāng)_太多這些模擬域的問(wèn)題必須用示波器才能看出來(lái)。我測(cè)VREF紋波的時(shí)候就用示波器看如果只有邏輯分析儀這個(gè)問(wèn)題基本沒(méi)法定位。第三個(gè)是調(diào)試器的選擇。GD32的SWD接口對(duì)某些調(diào)試器兼容性一般我實(shí)測(cè)DAP-Link的表現(xiàn)最穩(wěn)定其次是J-Link。有些山寨ST-Link在連接GD32時(shí)偶爾會(huì)報(bào)錯(cuò)Cannot access target主要是復(fù)位時(shí)序和速度設(shè)置的問(wèn)題可以嘗試降低SWD時(shí)鐘頻率解決。另外還有一個(gè)心得國(guó)產(chǎn)MCU的勘誤表一定要看。不管是GD32還是其他品牌芯片廠商都會(huì)定期更新勘誤表里面記載了已知的設(shè)計(jì)缺陷和規(guī)避方法。當(dāng)初如果先查勘誤表我可能在畫(huà)板之前就知道I2C的問(wèn)題可以提前在硬件層面做好方案。這一點(diǎn)是很多工程師容易忽視的因?yàn)榇蠹伊?xí)慣性地只翻參考手冊(cè)而勘誤表往往藏在官網(wǎng)一個(gè)不起眼的角落。5. 關(guān)于國(guó)產(chǎn)MCU選型與工程落地的建議5.1 什么時(shí)候可以放心用國(guó)產(chǎn)MCU這次項(xiàng)目做下來(lái)我得出的結(jié)論是國(guó)產(chǎn)MCU已經(jīng)在大部分場(chǎng)景下可以放心使用了但需要區(qū)分場(chǎng)景。如果你的產(chǎn)品是成本敏感、量大的消費(fèi)類(lèi)硬件對(duì)極端環(huán)境要求不高那國(guó)產(chǎn)MCU幾乎是必然選擇。便宜只是一方面更重要的是供貨穩(wěn)定。國(guó)外MCU動(dòng)不動(dòng)就漲價(jià)斷貨的教訓(xùn)這兩年大家都領(lǐng)教過(guò)了。供應(yīng)鏈的安全性和確定性有時(shí)候比一顆芯片的性能更重要。如果你的產(chǎn)品工作環(huán)境惡劣比如汽車(chē)電子那評(píng)估就要非常謹(jǐn)慎。汽車(chē)嵌入式MCU開(kāi)發(fā)跟消費(fèi)類(lèi)完全是兩個(gè)世界。車(chē)規(guī)MCU除了要過(guò)AEC-Q100認(rèn)證還要看功能安全等級(jí)、長(zhǎng)期供貨承諾、文檔鏈完整性。目前很多國(guó)產(chǎn)車(chē)規(guī)MCU在硬件參數(shù)上已經(jīng)不輸國(guó)外但完整的工具鏈、生態(tài)配套、功能安全認(rèn)證體系還在建設(shè)中。如果你的項(xiàng)目周期緊、安全等級(jí)要求高現(xiàn)階段還是慎重為主。我個(gè)人的判斷是非安全苛求類(lèi)產(chǎn)品國(guó)產(chǎn)MCU可以大膽用安全苛求類(lèi)產(chǎn)品先做詳細(xì)評(píng)估再?zèng)Q策。這個(gè)判斷會(huì)隨著時(shí)間推移越來(lái)越偏向國(guó)產(chǎn)畢竟這些廠商的迭代速度擺在那里。5.2 落地方法論先打樣驗(yàn)證再整機(jī)設(shè)計(jì)這次項(xiàng)目最讓我受益的不是哪段代碼怎么寫(xiě)的而是一個(gè)流程上的教訓(xùn)。做方案評(píng)估的時(shí)候我沒(méi)有直接畫(huà)整機(jī)原理圖而是先用一顆最小系統(tǒng)板把芯片所有要用到的外設(shè)都跑了一圈包括I2C、SPI、UART、ADC、低功耗喚醒。這個(gè)預(yù)研階段大概花了兩三天卻幫我避開(kāi)了整機(jī)回來(lái)才發(fā)現(xiàn)外設(shè)不兼容的深坑。具體做法是芯片廠商的評(píng)估板會(huì)有但更推薦自己畫(huà)一個(gè)最小系統(tǒng)板把MCU的所有電源引腳、晶振、SWD調(diào)試口、常用的外設(shè)接口都引出來(lái)。打樣回來(lái)之后挨個(gè)外設(shè)跑測(cè)試?yán)逃涗浢恳粋€(gè)子模塊是否正常工作。I2C能不能穩(wěn)定跑通ADC的噪聲水平是多少低功耗模式的電流是多少喚醒之后外設(shè)有沒(méi)有正常工作。這些數(shù)據(jù)會(huì)直接決定你的整機(jī)方案能不能落地。這個(gè)流程不僅適用于國(guó)產(chǎn)MCU適用于所有新平臺(tái)引入。但國(guó)產(chǎn)MCU和國(guó)外大廠相比社區(qū)資料相對(duì)少所以提前做外設(shè)驗(yàn)證的價(jià)值更大。關(guān)于系統(tǒng)時(shí)鐘調(diào)試還是要強(qiáng)調(diào)一遍新平臺(tái)回來(lái)第一步就是驗(yàn)證時(shí)鐘樹(shù)。這步?jīng)]問(wèn)題后面的UART和定時(shí)器才能正常工作否則后面所有調(diào)試都會(huì)建立在錯(cuò)誤的時(shí)間基準(zhǔn)上。最后再分享一個(gè)經(jīng)驗(yàn)當(dāng)你從某個(gè)國(guó)外MCU平臺(tái)切到國(guó)產(chǎn)MCU平臺(tái)時(shí)不要試圖一行代碼都不改。哪怕引腳兼容外設(shè)寄存器行為也有差異。接受按新平臺(tái)的邏輯重寫(xiě)底層這個(gè)現(xiàn)實(shí)反而會(huì)讓整個(gè)項(xiàng)目推進(jìn)得更快。我這次就是前期一直想著盡量少改代碼結(jié)果在I2C和時(shí)鐘配置上反復(fù)折騰。后來(lái)索性把底層外設(shè)驅(qū)動(dòng)按GD32的庫(kù)函數(shù)重新寫(xiě)了一遍應(yīng)用層保持不變整個(gè)系統(tǒng)就穩(wěn)定下來(lái)了。國(guó)產(chǎn)MCU這幾年進(jìn)步是真的快但兼容兩個(gè)字要打上引號(hào)來(lái)看。引腳兼容不意味著行為兼容芯片手冊(cè)寫(xiě)得再詳細(xì)也要親自驗(yàn)證社區(qū)里的口碑再高也要自己做壓測(cè)。這是這次項(xiàng)目最核心的體會(huì)。選型的時(shí)候多花時(shí)間看勘誤表方案落地的時(shí)候先做最小系統(tǒng)驗(yàn)證量產(chǎn)之前做幾輪小批量抽檢這套流程走下來(lái)國(guó)產(chǎn)MCU在我的項(xiàng)目清單里已經(jīng)和國(guó)外主流的芯片放在同一個(gè)位置了。