:代碼審查、板級調試與工作流固化)
嵌入式軟件這行有個特別擰巴的地方代碼跑在資源受限的板子上調試靠串口打印和示波器但寫代碼的方式卻還停留在“手搓寄存器、翻數(shù)據手冊、對著參考手冊一行行摳”的階段。我做了十多年嵌入式從8位機裸跑到帶RTOS的Cortex-M再到最近兩年開始把AI編程工具引入日常開發(fā)最大的感受是——AI不會替你讀懂時序圖但它能幫你把那些重復的、模板化的、容易寫錯的底層代碼快速搭起來讓你把精力放在真正需要硬件直覺的地方。這個系列寫到第19篇前面聊了怎么選AI編程工具、怎么寫提示詞、怎么讓AI理解寄存器手冊。這一篇是“第一個AI協(xié)同開發(fā)項目”的第三部分也是收尾部分。前兩部分我們把項目框架搭起來了把外設驅動骨架生成了這一部分要解決的是怎么讓AI生成的代碼真正跑通、怎么排查AI寫出來的坑、怎么把AI協(xié)同開發(fā)變成一套可復用的工作流。如果你正在學嵌入式或者已經工作但想試試AI編程到底能不能用在正經項目上這篇的內容應該能給你一些直接能抄的作業(yè)。1. 項目收尾階段的核心任務拆解1.1 為什么收尾階段比生成階段更考驗人很多人對AI編程的想象是“輸入需求輸出代碼編譯通過收工”。實際做過嵌入式項目的人都知道編譯通過只是萬里長征第一步。AI生成的代碼在語法層面通常沒問題但在嵌入式場景下它可能踩的坑包括但不限于寄存器位域順序搞反、時鐘使能順序不對、中斷優(yōu)先級配置沖突、DMA和Cache一致性沒處理、延時函數(shù)在中斷里用了阻塞式實現(xiàn)。這些問題編譯器不會報錯但板子就是跑不起來。所以收尾階段的核心任務不是“繼續(xù)讓AI寫代碼”而是“驗證AI寫的代碼在真實硬件上的行為”。這個階段我把它拆成四塊代碼審查與硬件對齊、編譯與靜態(tài)檢查、板級調試與問題定位、工作流固化。每一塊都有AI能幫上忙的地方也有AI幫不上、必須靠人的地方。搞清楚這個邊界是AI協(xié)同開發(fā)能不能落地的關鍵。1.2 收尾階段的四個核心環(huán)節(jié)先給一個整體視圖后面每個環(huán)節(jié)展開講。環(huán)節(jié)主要目標AI能做的必須人做的代碼審查發(fā)現(xiàn)邏輯與硬件不符對照手冊檢查位域、生成審查清單判斷時序是否滿足硬件要求編譯檢查消除語法與鏈接問題解釋報錯、建議修改處理芯片特定的鏈接腳本板級調試讓代碼在硬件上跑通分析日志、推測故障點用示波器/邏輯分析儀實測工作流固化形成可復用流程整理提示詞模板、生成文檔根據項目特點調整流程這張表是我踩了不少坑之后總結出來的。剛開始用AI編程的時候我總想讓AI把活全干了結果發(fā)現(xiàn)它在“理解硬件真實行為”這件事上有天然短板——它沒見過你的板子不知道你的晶振是8M還是25M不知道你的上拉電阻是4.7K還是10K。所以協(xié)同的正確姿勢是AI負責它擅長的模式化工作人負責硬件相關的判斷。1.3 本部分要解決的具體問題回到我們這個項目。前兩部分已經完成了項目需求定義一個基于Cortex-M的傳感器數(shù)據采集與串口上報系統(tǒng)、外設驅動骨架生成GPIO、UART、定時器、ADC的初始化代碼。這一部分要解決的具體問題是AI生成的初始化代碼寄存器配置是否和手冊一致多個外設的初始化順序是否有依賴問題中斷服務函數(shù)里有沒有隱藏的阻塞操作主循環(huán)的任務調度邏輯是否合理怎么用AI輔助定位“代碼看著對但跑不通”的問題怎么把這套流程整理成下次能直接用的模板這幾個問題基本覆蓋了嵌入式項目收尾階段的主要痛點。下面逐個展開。2. AI生成代碼的審查與硬件對齊2.1 寄存器配置審查AI最容易出錯的地方AI生成寄存器配置代碼時最常見的錯誤不是語法錯誤而是“位域理解偏差”。舉個例子某個狀態(tài)寄存器里有一個3位的字段表示采樣速率AI可能會寫成// AI生成的代碼有問題 ADC-CR | (sample_rate 8);看起來沒問題但如果手冊里這個字段是從bit 9開始的或者這個字段是“寫1清除”而不是“寫值設置”這段代碼就會出問題。更隱蔽的是AI有時候會把“保留位”當成有效位來操作或者把只讀位當成可寫位。我的做法是讓AI生成代碼之后再讓AI做一次“對照審查”。具體操作是把手冊里相關寄存器的描述貼給AI然后問它“請逐位對照以下寄存器描述檢查這段代碼的位域操作是否正確列出所有不一致的地方。”這個方法的有效性在于AI在“對照檢查”任務上的表現(xiàn)比“憑空生成”要穩(wěn)定得多因為前者有明確的參照物。提示貼手冊描述的時候盡量貼原文的位域表格不要只貼文字描述。表格里的bit編號、字段名、讀寫屬性、復位值這些信息是審查的關鍵依據。2.2 時鐘樹與初始化順序的依賴檢查嵌入式系統(tǒng)里外設初始化順序是有嚴格依賴的。典型順序是系統(tǒng)時鐘配置 → 總線時鐘使能 → 外設時鐘使能 → 外設寄存器配置 → 中斷配置 → 使能外設。AI生成代碼時有時候會把這個順序打亂比如先配置了UART寄存器才去使能UART時鐘結果配置全部無效。這個問題在單外設的時候不明顯但多外設的時候就容易暴露。我讓AI做了一次“初始化順序審查”提示詞大概是這樣的以下是一個Cortex-M項目的初始化代碼片段包含GPIO、UART、TIM、ADC四個外設。 請檢查 1. 系統(tǒng)時鐘配置是否在所有外設配置之前 2. 每個外設的時鐘使能是否在其寄存器配置之前 3. 中斷優(yōu)先級配置是否在中斷使能之前 4. 是否存在外設之間的初始化依賴如ADC依賴TIM觸發(fā) 列出所有順序問題并給出修正后的順序。AI給出的結果里確實發(fā)現(xiàn)了一個問題ADC的初始化代碼里先配置了ADC的轉換模式然后才使能ADC時鐘。雖然在某些芯片上這恰好能工作因為時鐘使能有延遲但這是不可靠的。修正之后代碼的健壯性明顯提升。2.3 中斷服務函數(shù)的隱藏陷阱中斷服務函數(shù)是AI生成代碼的重災區(qū)。常見問題包括在ISR里調用了阻塞式延時、在ISR里做了浮點運算在沒有FPU的芯片上、在ISR里訪問了非原子性的共享變量、ISR執(zhí)行時間過長導致其他中斷丟失。我讓AI對生成的ISR做了一次專項審查提示詞是“請檢查以下中斷服務函數(shù)找出所有可能導致實時性問題的操作包括阻塞調用、浮點運算、長循環(huán)、非原子訪問并給出修改建議?!盇I找出了兩個問題一個是在UART接收中斷里用了printf阻塞式另一個是在定時器中斷里做了一個超過100次的循環(huán)。修改方案也很直接UART接收中斷里只把數(shù)據放進環(huán)形緩沖區(qū)主循環(huán)再去處理定時器中斷里的循環(huán)改成狀態(tài)機分次執(zhí)行。這兩個修改都是嵌入式開發(fā)的基本功但AI生成的時候不會主動考慮這些需要你引導它去檢查。2.4 審查清單的固化做完上面幾輪審查之后我把審查項整理成了一個清單下次直接拿來用。這個清單包括寄存器位域是否與手冊一致bit編號、讀寫屬性、復位值時鐘使能是否在寄存器配置之前中斷優(yōu)先級是否合理嵌套、搶占、子優(yōu)先級ISR里是否有阻塞操作、浮點運算、長循環(huán)共享變量是否有volatile修飾、是否原子訪問DMA配置是否處理了Cache一致性如果用了Cache延時函數(shù)是否在中斷上下文里被調用外設初始化順序是否符合依賴關系這個清單我讓AI幫我整理成了Markdown格式存在項目文檔里。下次新項目直接讓AI按這個清單審查效率高很多。3. 編譯、鏈接與靜態(tài)檢查的AI輔助3.1 編譯報錯的快速定位嵌入式項目的編譯報錯有時候很隱晦尤其是鏈接階段的報錯。比如“undefined reference to_sbrk”這種新手看了完全不知道從哪下手。AI在這方面的價值是它能快速解釋報錯的含義并給出常見的解決方向。我實測下來對于GCC工具鏈的常見報錯AI的解釋準確率很高。比如undefined reference to _sbrk→ 通常是沒實現(xiàn)堆管理或者鏈接腳本里沒定義堆區(qū)域region RAM overflowed→ 內存不夠需要優(yōu)化變量或調整鏈接腳本section .data will not fit in region RAM→ 初始化數(shù)據太大考慮放到Flash里multiple definition of xxx→ 頭文件里定義了變量而不是聲明這些報錯AI都能給出合理的解釋和修改方向。但要注意AI給的修改建議不一定適用于你的具體芯片比如鏈接腳本的修改不同芯片的地址映射完全不同必須結合手冊來改。3.2 靜態(tài)檢查工具的配合使用AI審查代碼是“語義層面”的靜態(tài)檢查工具是“規(guī)則層面”的兩者互補。我常用的組合是cppcheck檢查C/C代碼的常見缺陷clang-tidy更嚴格的靜態(tài)分析能發(fā)現(xiàn)一些潛在的bug-Wall -Wextra -WerrorGCC的編譯警告全開實際操作中我會先跑一遍靜態(tài)檢查工具把報出來的問題貼給AI讓它解釋每個問題的含義和修改方法。這樣比單純看工具的輸出要快得多因為工具只告訴你“哪里有問題”AI能告訴你“為什么有問題”和“怎么改”。注意靜態(tài)檢查工具報出來的問題不一定都是真問題有些是誤報。讓AI幫你判斷哪些需要改、哪些可以忽略能省不少時間。但最終判斷還是要靠你自己對代碼的理解。3.3 鏈接腳本的AI輔助修改鏈接腳本是嵌入式開發(fā)里比較“勸退”的部分語法特殊出錯信息也不友好。AI在鏈接腳本方面的能力有限因為它需要知道具體芯片的Flash和RAM地址、大小、以及各個段的布局要求。但如果你把這些信息都提供給AI它能幫你生成一個可用的鏈接腳本模板。我的做法是把芯片手冊里的內存映射表貼給AI告訴它Flash起始地址、大小RAM起始地址、大小然后讓它生成一個標準的鏈接腳本。生成之后我再對照芯片的啟動文件檢查一遍確認向量表、堆棧、堆的布局沒問題。這個過程比從零寫要快但檢查環(huán)節(jié)不能省。3.4 編譯優(yōu)化等級的取舍AI生成代碼的時候默認不會考慮編譯優(yōu)化等級的影響。但優(yōu)化等級對嵌入式代碼的影響很大尤其是涉及volatile變量、延時循環(huán)、中斷共享變量的時候。-O0和-O2下同一段代碼的行為可能完全不同。我一般建議在調試階段用-O0或-Og方便單步調試發(fā)布階段用-Os或-O2減小體積、提升速度。但切換優(yōu)化等級之后一定要重新測試尤其是延時函數(shù)和中斷相關的邏輯。AI可以幫你分析“哪些代碼在優(yōu)化后可能行為改變”但實測還是必須的。4. 板級調試與問題定位實錄4.1 “代碼看著對但跑不通”的排查思路這是嵌入式開發(fā)最經典的場景代碼邏輯沒問題編譯通過下載進去就是沒反應。這時候AI能幫上忙的地方是“根據現(xiàn)象推測原因”但前提是你要把現(xiàn)象描述清楚。我遇到的一個具體問題是UART初始化之后發(fā)送數(shù)據沒有輸出。代碼審查過了寄存器配置和手冊一致時鐘也使能了。我讓AI幫我列了一個排查清單確認UART時鐘源是否正確有些芯片UART掛在APB1有些在APB2確認GPIO的復用功能是否配置正確AF編號確認波特率計算是否匹配實際時鐘頻率確認TX引腳是否被其他外設占用確認發(fā)送函數(shù)是否真的被調用加個GPIO翻轉做標記確認硬件連接是否正確TX-RX是否交叉按這個清單逐項排查最后發(fā)現(xiàn)是GPIO的復用功能編號配錯了。AI在生成代碼的時候用了一個“常見值”但這個芯片的UART TX引腳對應的AF編號不是那個值。這個問題很隱蔽因為代碼本身沒有語法錯誤只是硬件配置不對。4.2 用GPIO翻轉做“窮人的邏輯分析儀”嵌入式調試有個特別實用的技巧在關鍵代碼位置翻轉一個GPIO然后用示波器或邏輯分析儀看波形。這個技巧在AI協(xié)同開發(fā)里同樣重要因為AI生成的代碼你不可能完全信任需要用它來驗證“代碼是否執(zhí)行到了這里”。我一般會預留一個調試GPIO在初始化的關鍵節(jié)點、中斷入口、主循環(huán)的關鍵分支都加上翻轉操作。這樣用邏輯分析儀一看就知道程序卡在哪一步。AI可以幫你生成這些調試代碼但翻轉的位置需要你自己判斷——哪些節(jié)點是關鍵的只有做過這個項目的人才知道。4.3 常見問題速查表下面這張表是我在實際項目中整理出來的AI生成代碼后經常遇到的問題和排查方法現(xiàn)象可能原因排查方法程序不運行啟動文件、向量表、復位地址檢查鏈接腳本和啟動文件外設無輸出時鐘未使能、AF配置錯誤查時鐘樹、查GPIO復用表中斷不觸發(fā)優(yōu)先級配置、NVIC使能、中斷標志未清查NVIC寄存器、查中斷標志數(shù)據錯亂共享變量未加volatile、非原子訪問加volatile、用臨界區(qū)保護偶爾死機堆棧溢出、中斷嵌套過深查棧使用、查中斷優(yōu)先級通信不穩(wěn)定波特率誤差、時序不滿足算波特率誤差、查時序圖功耗偏高未使用的外設時鐘未關、引腳懸空關時鐘、配置引腳狀態(tài)這張表我讓AI幫我擴充過加了一些它從常見問題里總結的條目。實際用下來覆蓋了八成以上的常見問題。4.4 一個真實的調試案例說一個我印象比較深的案例。項目里用到了ADC采集傳感器數(shù)據AI生成的代碼看起來沒問題但采集到的數(shù)據一直跳動很大。我先用萬用表量了傳感器輸出是穩(wěn)定的說明問題在ADC配置或采樣時序上。讓AI分析之后它提出了幾個可能采樣時間太短、參考電壓不穩(wěn)、DMA傳輸和ADC轉換不同步。我逐一排查最后發(fā)現(xiàn)是采樣時間設置得太短ADC的采樣保持電容還沒充夠電就開始了轉換。把采樣時間從最小的幾個周期改成幾十個周期之后數(shù)據就穩(wěn)定了。這個問題的教訓是AI生成ADC配置的時候默認用的是“能工作的最小配置”但實際硬件需要根據信號源阻抗來調整采樣時間。這個計算過程AI不會主動做需要你根據手冊里的公式自己算。我后來把這個計算過程也整理成了提示詞模板讓AI在生成ADC代碼時自動帶上采樣時間計算。5. AI協(xié)同開發(fā)工作流的固化5.1 從“一次性使用”到“可復用流程”前面幾個環(huán)節(jié)做完項目基本跑通了。但更重要的是把這套流程固化下來下次新項目直接復用。我整理的工作流包括四個階段需求階段用AI把項目需求拆解成外設清單和功能清單生成階段用提示詞模板讓AI生成外設驅動骨架審查階段用審查清單讓AI逐項檢查代碼調試階段用排查清單和AI一起定位問題每個階段都有對應的提示詞模板和檢查清單存在項目文檔里。下次新項目先復制這套模板改改芯片型號和外設列表就能快速啟動。5.2 提示詞模板的整理提示詞模板是這套工作流的核心資產。我整理了幾個常用的模板外設驅動生成模板芯片型號[型號] 外設[外設名] 功能需求[具體功能] 時鐘頻率[頻率] 請生成初始化代碼和基本操作函數(shù)要求 1. 寄存器配置對照手冊標注每個配置的依據 2. 初始化順序符合依賴關系 3. 中斷服務函數(shù)避免阻塞操作 4. 關鍵配置附上計算過程代碼審查模板請對照以下手冊描述審查代碼中的寄存器配置 [貼手冊位域表格] [貼代碼] 檢查項位域編號、讀寫屬性、復位值、時鐘使能順序、中斷配置 列出所有不一致的地方和修改建議。問題排查模板現(xiàn)象[描述現(xiàn)象] 已排查[列出已排查項] 相關代碼[貼代碼] 請列出可能的故障原因按可能性排序并給出排查方法。這幾個模板我用了大半年覆蓋了大部分日常開發(fā)場景。當然模板不是萬能的具體項目還需要根據芯片特點調整。5.3 哪些環(huán)節(jié)不該交給AI用了這么久AI編程我越來越清楚它的邊界。以下這些環(huán)節(jié)我建議不要交給AI或者只讓AI做輔助硬件原理圖設計AI看不到你的原理圖不知道引腳怎么連的時序關鍵路徑的最終判斷AI能算但最終要對照示波器實測安全相關的代碼比如看門狗、故障保護必須人工審查芯片特定的勘誤處理手冊里的errataAI不一定知道最終的性能優(yōu)化AI能給建議但實測調優(yōu)必須人工做把這些邊界搞清楚AI協(xié)同開發(fā)才能既高效又可靠。5.4 我個人的幾點體會最后分享幾點我自己的體會。第一AI生成的代碼一定要審查尤其是寄存器配置和中斷相關部分這是踩過坑的教訓。第二提示詞的質量直接決定生成代碼的質量花時間打磨提示詞模板是值得的。第三AI在“解釋”和“檢查”任務上比“生成”任務更可靠多用它做審查少用它做從零生成。第四嵌入式開發(fā)的核心能力——讀懂手冊、理解時序、調試硬件——AI替代不了但AI能讓你在這些核心能力上花更少的時間把精力放在真正需要經驗的地方。這個系列寫到這兒第一個AI協(xié)同開發(fā)項目就算完整走了一遍。從需求拆解到代碼生成從審查到調試再到工作流固化這套流程我實際跑下來效率提升大概在三成左右主要省在模板代碼編寫和問題排查上。但前提是你要愿意花時間調提示詞、做審查、整理模板。如果只是想讓AI“一鍵生成能跑的代碼”那大概率會失望。嵌入式這行硬件永遠是最誠實的裁判AI只是幫你更快地走到裁判面前。