
1. 項(xiàng)目緣起為什么需要自己動(dòng)手燒錄Bootloader如果你玩Arduino有一段時(shí)間了手頭攢了幾塊空白的ATmega328P或者ATmega2560芯片或者從某寶上買了一些“裸片”你可能會(huì)發(fā)現(xiàn)一個(gè)尷尬的問題這些芯片沒法直接用Arduino IDE來上傳程序。IDE會(huì)報(bào)錯(cuò)提示找不到設(shè)備或者上傳失敗。這時(shí)候你就需要一個(gè)“啟動(dòng)程序”也就是Bootloader。Bootloader是固化在微控制器MCU里的一小段程序它的核心任務(wù)就是“引導(dǎo)”。當(dāng)芯片上電或復(fù)位時(shí)它首先運(yùn)行的不是你的主程序而是這段Bootloader。Bootloader會(huì)等待一小段時(shí)間通常是幾秒看看有沒有來自特定接口比如串口的“新程序”要上傳。如果有它就接管通信把新程序?qū)懭氲叫酒腇lash程序存儲(chǔ)器中如果沒有它就跳轉(zhuǎn)到Flash中已經(jīng)存在的用戶程序開始執(zhí)行。Arduino之所以對(duì)新手如此友好插上USB就能編程秘密就在于出廠時(shí)已經(jīng)燒錄好了對(duì)應(yīng)的Bootloader。那么當(dāng)你的芯片是“空白”的或者Bootloader損壞了該怎么辦常規(guī)做法是買一個(gè)專用的AVR ISPIn-System Programmer編程器比如USBasp。但今天我們要做的是把手頭可能就有的另一塊Arduino開發(fā)板比如Arduino Uno變成一個(gè)臨時(shí)的、功能完整的AVR ISP編程器去給目標(biāo)芯片燒錄Bootloader。這不僅僅是省下幾十塊錢買編程器的事更是一種對(duì)Arduino底層工作機(jī)制的深入理解。你會(huì)清晰地看到所謂的“編程器”本質(zhì)上就是一個(gè)遵循特定時(shí)序協(xié)議、能控制目標(biāo)芯片復(fù)位和編程引腳的數(shù)字邏輯設(shè)備。而Arduino恰好就是一個(gè)完美的、可編程的數(shù)字邏輯控制器。2. 硬件準(zhǔn)備不只是連線那么簡單這個(gè)項(xiàng)目的硬件連接看似簡單就是幾根杜邦線的事但其中每個(gè)引腳的選擇和連接順序都暗含玄機(jī)。我們以最經(jīng)典的組合為例使用一塊Arduino Uno作為編程器后文稱“主機(jī)”給另一塊ATmega328P目標(biāo)芯片后文稱“從機(jī)”燒錄Bootloader。首先你需要讓從機(jī)芯片進(jìn)入編程模式。對(duì)于ATmega系列這通常需要在其RESET引腳上施加一個(gè)足夠長時(shí)間的低電平信號(hào)。主機(jī)將通過一個(gè)特定的引腳來控制這個(gè)過程。核心接線清單如下主機(jī) (Arduino Uno) 引腳從機(jī) (ATmega328P) 引腳備注D10-RESET(Pin 1)最關(guān)鍵的一根線。主機(jī)通過此引腳拉低從機(jī)的復(fù)位腳使其進(jìn)入編程狀態(tài)。D11-MOSI(Pin 17)主機(jī)輸出從機(jī)輸入。數(shù)據(jù)從主機(jī)流向從機(jī)。D12-MISO(Pin 18)主機(jī)輸入從機(jī)輸出。數(shù)據(jù)從從機(jī)流向主機(jī)用于校驗(yàn)。D13-SCK(Pin 19)時(shí)鐘信號(hào)由主機(jī)產(chǎn)生同步數(shù)據(jù)傳輸。5V-VCC(Pin 7)電源正極。務(wù)必確認(rèn)從機(jī)芯片是5V邏輯電平。GND-GND(Pin 8)電源地。必須共地這是所有邏輯電平參考的基礎(chǔ)。注意這里的引腳編號(hào)指的是ATmega328P-PUDIP-28封裝的物理引腳號(hào)。如果你用的是表面貼裝SMD封裝或其他型號(hào)芯片請(qǐng)務(wù)必查閱對(duì)應(yīng)的芯片手冊(cè)Datasheet確認(rèn)引腳定義。幾個(gè)極易出錯(cuò)的細(xì)節(jié)電源隔離與上電順序在連接信號(hào)線特別是D10~D13之前務(wù)必先連接好GND。理想情況下你應(yīng)該使用一個(gè)面包板先將主機(jī)和從機(jī)的GND連接在一起再接VCC最后連接信號(hào)線。這能有效避免因電勢(shì)差導(dǎo)致的瞬間電流沖擊芯片。燒錄完成后斷開順序則建議先斷信號(hào)線再斷電源。復(fù)位引腳的上拉電阻ATmega328P的RESET引腳內(nèi)部有上拉電阻但在強(qiáng)干擾環(huán)境或線纜較長時(shí)為了穩(wěn)定可以在從機(jī)的RESET引腳和VCC之間連接一個(gè)10kΩ的電阻。這不是必須的但屬于“好習(xí)慣”能提高成功率。目標(biāo)芯片的時(shí)鐘Bootloader的編譯和運(yùn)行依賴于特定的時(shí)鐘頻率例如Arduino Uno常用的16MHz外部晶振。你需要確保你的“從機(jī)”芯片已經(jīng)焊接了正確的時(shí)鐘源16MHz晶振兩個(gè)22pF電容或者啟用了正確的內(nèi)部RC振蕩器。如果目標(biāo)板是全新的什么都沒有你需要先為其焊接好晶振電路否則燒錄過程會(huì)失敗。避開“雞生蛋”問題你想用Arduino A給芯片B燒Bootloader。那么Arduino A本身必須是一個(gè)功能正常的、帶有標(biāo)準(zhǔn)Arduino Bootloader的開發(fā)板。你不能指望一塊本身沒有程序的芯片去給另一塊芯片編程。3. 軟件配置將Arduino“變身”為ISP編程器硬件連接妥當(dāng)后我們需要讓主機(jī)Arduino運(yùn)行一段特殊的程序使其扮演AVR ISP的角色。幸運(yùn)的是Arduino IDE已經(jīng)內(nèi)置了這個(gè)功能我們無需自己從頭編寫復(fù)雜的SPI和協(xié)議代碼。操作步驟如下打開Arduino IDE確保你使用的是較新版本的IDE如1.8.x或2.x它們都包含所需的庫和示例。加載“ArduinoISP”示例程序在菜單欄依次點(diǎn)擊文件-示例-11. ArduinoISP-ArduinoISP。這個(gè)示例程序包含了實(shí)現(xiàn)AVR ISP協(xié)議的所有代碼。選擇正確的主板類型和端口在工具-開發(fā)板中選擇你用作編程器的主機(jī)Arduino型號(hào)例如“Arduino Uno”。在工具-端口中選擇它對(duì)應(yīng)的串口。上傳“ArduinoISP”程序點(diǎn)擊上傳按鈕向右的箭頭將這段編程器固件燒錄到你的主機(jī)Arduino中。上傳成功后主機(jī)Arduino的引腳D9、D8、D7上的LED會(huì)用于指示狀態(tài)心跳、編程中、錯(cuò)誤但這不是必須功能不影響核心燒錄。至此你的這塊Arduino Uno已經(jīng)不再是一塊普通的開發(fā)板而是一個(gè)專用于AVR芯片編程的ISP設(shè)備了。接下來我們要在IDE中配置告訴它“嘿我現(xiàn)在要用這個(gè)自制的編程器來干活了”。配置編程器繼續(xù)在Arduino IDE中點(diǎn)擊工具-編程器在長長的列表里找到并選擇Arduino as ISP。注意不是“ArduinoISP”而是“Arduino as ISP”。這個(gè)選項(xiàng)告訴IDE我們將使用運(yùn)行了ArduinoISP固件的這塊板子作為外部編程器。4. 核心操作為空白芯片燒錄Bootloader這是整個(gè)流程中最令人興奮的一步。請(qǐng)?jiān)俅未_認(rèn)你的硬件連接牢固無誤特別是RESET、MOSI、MISO、SCK這四根SPI信號(hào)線。選擇目標(biāo)芯片型號(hào)在工具-開發(fā)板中這次選擇你想要燒錄Bootloader的目標(biāo)芯片所對(duì)應(yīng)的Arduino板型。例如如果你的目標(biāo)芯片是ATmega328P并且你想讓它像標(biāo)準(zhǔn)的Arduino Uno一樣工作就選擇“Arduino Uno”。如果你想燒錄給ATmega2560就選擇“Arduino Mega 2560”。這個(gè)選擇至關(guān)重要因?yàn)樗鼪Q定了IDE會(huì)調(diào)用哪個(gè)型號(hào)的Bootloader文件以及相關(guān)的熔絲位Fuses配置。執(zhí)行“燒錄引導(dǎo)程序”點(diǎn)擊工具-燒錄引導(dǎo)程序。此時(shí)IDE會(huì)做以下幾件事通過之前選擇的“Arduino as ISP”編程器與你的主機(jī)Arduino通信。主機(jī)Arduino根據(jù)AVR ISP協(xié)議控制目標(biāo)芯片的RESET引腳使其進(jìn)入編程模式。IDE將對(duì)應(yīng)的Bootloader二進(jìn)制文件.hex文件和熔絲位配置發(fā)送給主機(jī)Arduino。主機(jī)Arduino通過SPI接口將這些數(shù)據(jù)逐字節(jié)地寫入目標(biāo)芯片的Flash存儲(chǔ)器和熔絲位空間。整個(gè)過程會(huì)在IDE底部的狀態(tài)欄有進(jìn)度顯示。如果一切順利最終會(huì)顯示“引導(dǎo)程序燒錄完成”。熔絲位Fuses看不見但至關(guān)重要的配置燒錄Bootloader時(shí)IDE會(huì)自動(dòng)設(shè)置一批“熔絲位”。你可以把熔絲位理解為芯片的“硬件配置開關(guān)”它們控制著芯片最底層的行為例如時(shí)鐘源選擇是使用外部晶振還是內(nèi)部RC振蕩器頻率是多少啟動(dòng)延遲上電后等待多長時(shí)間再開始運(yùn)行程序這給外部電路如液晶屏一個(gè)穩(wěn)定時(shí)間。Bootloader大小和起始位置告訴芯片F(xiàn)lash的哪一部分區(qū)域是留給Bootloader的。復(fù)位向量芯片復(fù)位后是首先執(zhí)行Bootloader區(qū)的程序還是直接跳轉(zhuǎn)到用戶程序區(qū)App區(qū)對(duì)于Arduino UnoATmega328P的Bootloader典型的熔絲位配置會(huì)設(shè)置為使用外部全幅振蕩器16MHz啟動(dòng)延遲為65msBootloader大小為2KB復(fù)位向量指向Bootloader區(qū)。強(qiáng)烈建議初學(xué)者不要手動(dòng)修改熔絲位除非你非常清楚自己在做什么。錯(cuò)誤的熔絲位設(shè)置比如禁用了SPI編程或選錯(cuò)了時(shí)鐘源可能導(dǎo)致芯片“變磚”無法再通過ISP方式編程恢復(fù)起來非常麻煩。5. 實(shí)戰(zhàn)驗(yàn)證與排錯(cuò)讓新芯片“活”過來燒錄完成后不要急著拆線。我們需要驗(yàn)證Bootloader是否真的工作正常。驗(yàn)證步驟一嘗試通過串口上傳一個(gè)簡單程序改造你的目標(biāo)板將剛剛燒錄好Bootloader的ATmega328P芯片安裝到一塊具有USB轉(zhuǎn)串口芯片如CH340G、CP2102和16MHz晶振電路的空白板上或者直接替換掉現(xiàn)有Arduino Uno上的芯片。連接USB線通過USB線將這塊目標(biāo)板連接到電腦。在IDE中選擇端口你應(yīng)該能在工具-端口中看到一個(gè)新的串口出現(xiàn)例如COM3或/dev/ttyUSB0選擇它。上傳Blink示例打開示例-01.Basics-Blink點(diǎn)擊上傳。如果一切正常IDE會(huì)顯示編譯和上傳進(jìn)度并且目標(biāo)板上的LED通常連接在D13開始閃爍。如果上傳失敗請(qǐng)按以下思路排查錯(cuò)誤avrdude: stk500_getsync() attempt X of 10: not in sync檢查1時(shí)鐘電路。這是最常見的原因。確保16MHz晶振和兩個(gè)22pF電容已正確焊接并且緊靠芯片的XTAL1和XTAL2引腳。用萬用表測(cè)量晶振兩腳對(duì)地電壓大約在2.5V左右且略有不同。檢查2Bootloader型號(hào)。確認(rèn)你燒錄的Bootloader與目標(biāo)板型嚴(yán)格對(duì)應(yīng)。給“Arduino Uno”燒的Bootloader不能用在“Arduino Nano舊Bootloader”的配置上雖然它們芯片都是328P但通信速率可能不同。檢查3串口芯片和連接。確保USB轉(zhuǎn)串口芯片工作正常驅(qū)動(dòng)已安裝TX/RX線沒有接反或虛焊。錯(cuò)誤avrdude: initialization failed, rc-1或device signature 0xffffff檢查1電源。用萬用表測(cè)量目標(biāo)芯片VCC和GND之間的電壓確保是穩(wěn)定的5V或3.3V取決于你的板子設(shè)計(jì)。電流是否充足檢查2ISP連線。重點(diǎn)檢查MOSI、MISO、SCK、RESET這四根線是否連接正確、牢固。特別是MISO線很容易被忽略或接錯(cuò)。檢查3芯片型號(hào)選擇。在“燒錄引導(dǎo)程序”時(shí)工具-開發(fā)板菜單里選擇的目標(biāo)板型必須與你的物理芯片型號(hào)匹配。給ATmega168芯片選了ATmega328P的配置肯定會(huì)失敗。檢查4熔絲位錯(cuò)誤。如果之前誤操作過熔絲位可能導(dǎo)致芯片無法進(jìn)入SPI編程模式。這時(shí)可能需要一個(gè)高壓并行編程器來挽救對(duì)于新手來說難度較大。錯(cuò)誤avrdude: usbdev_open(): did not find any USB device這個(gè)錯(cuò)誤通常與作為編程器的主機(jī)Arduino有關(guān)。檢查主機(jī)Arduino的USB連接是否正常在設(shè)備管理器中端口是否出現(xiàn)以及IDE中“編程器”選項(xiàng)是否已正確設(shè)置為“Arduino as ISP”。6. 進(jìn)階技巧與場景擴(kuò)展掌握了基礎(chǔ)操作后你可以嘗試更多玩法1. 拯救“變磚”的開發(fā)板你的Arduino Uno因?yàn)橐馔鈹嚯娀蝈e(cuò)誤操作導(dǎo)致原有的Bootloader損壞無法通過USB上傳程序。此時(shí)你可以用另一塊好的Arduino按照上述方法為這塊“磚頭”重新燒錄Bootloader讓它起死回生。這比重新買一塊板子劃算得多。2. 批量燒錄如果你需要制作多塊相同的自制Arduino板卡用這個(gè)方法非常高效。燒錄好一塊主控Arduino作為編程器后可以快速地為多塊空白板卡上的芯片燒錄Bootloader只需更換連接的目標(biāo)芯片即可。3. 燒錄不同的Bootloader并非所有ATmega328P都必須用Uno的Bootloader。例如如果你希望減少Bootloader占用的空間標(biāo)準(zhǔn)Uno的Bootloader約2KB可以嘗試燒錄“Optiboot”。Optiboot是一個(gè)更精簡、啟動(dòng)更快的Bootloader只有512字節(jié)。在工具-開發(fā)板中選擇類似“Arduino Uno (Optiboot)”的選項(xiàng)可能需要額外安裝板卡支持包然后執(zhí)行燒錄即可。4. 直接燒錄應(yīng)用程序不通過Bootloader“Arduino as ISP”編程器不僅可以燒錄Bootloader還可以直接繞過Bootloader將編譯好的.hex文件直接寫入芯片的Flash。方法是在IDE中點(diǎn)擊項(xiàng)目-使用編程器上傳。這在你開發(fā)需要最大化利用Flash空間連Bootloader的2KB都想省掉的產(chǎn)品時(shí)非常有用或者用于批量生產(chǎn)固化最終程序。5. 為其他AVR芯片燒錄這個(gè)方法不僅限于ATmega328P。理論上所有支持SPI接口編程的AVR芯片如ATtiny85、ATmega8、ATmega1284P等都可以通過這種方式燒錄。你只需要在Arduino IDE中安裝對(duì)應(yīng)的板卡支持包例如通過“開發(fā)板管理器”安裝attiny支持然后在工具-開發(fā)板中選擇對(duì)應(yīng)的型號(hào)即可進(jìn)行燒錄。接線原理相同但需要根據(jù)目標(biāo)芯片的Datasheet確認(rèn)其RESET、MOSI、MISO、SCK對(duì)應(yīng)的引腳。7. 從原理到實(shí)踐理解SPI與AVR ISP協(xié)議要真正吃透這個(gè)過程而不僅僅是“依葫蘆畫瓢”我們需要稍微深入一下底層協(xié)議。SPISerial Peripheral Interface是一種同步、全雙工的串行通信協(xié)議。它需要四根線SCK (Serial Clock)時(shí)鐘線由主機(jī)產(chǎn)生。MOSI (Master Out Slave In)主機(jī)輸出從機(jī)輸入。MISO (Master In Slave Out)主機(jī)輸入從機(jī)輸出。SS/CS (Slave Select/Chip Select)從機(jī)選擇線低電平有效。在這個(gè)場景里RESET引腳就扮演了類似SS的角色。當(dāng)主機(jī)將目標(biāo)芯片的RESET拉低時(shí)就相當(dāng)于“選中”了這顆芯片告訴它“準(zhǔn)備好我要通過SPI給你編程了”。AVR ISP協(xié)議則是建立在SPI物理層之上的一套特定指令集。主機(jī)通過MOSI線發(fā)送特定的命令字節(jié)例如0xAC代表“編程使能”從機(jī)通過MISO線返回響應(yīng)和數(shù)據(jù)。整個(gè)燒錄過程就是主機(jī)按照嚴(yán)格的時(shí)序發(fā)送一系列這樣的命令來完成芯片簽名讀取、擦除、寫入Flash、寫入熔絲位、校驗(yàn)等操作。ArduinoIDE內(nèi)置的“ArduinoISP”示例程序以及當(dāng)你選擇“Arduino as ISP”編程器時(shí)IDE調(diào)用的avrdude命令行工具它們共同協(xié)作封裝了所有這些底層復(fù)雜的命令和時(shí)序。avrdude是一個(gè)開源的多平臺(tái)AVR編程軟件它是實(shí)際與芯片對(duì)話的“大腦”而我們的Arduino主機(jī)只是avrdude命令的一個(gè)執(zhí)行終端。理解了這個(gè)層次關(guān)系你就能明白為什么我們不需要自己寫復(fù)雜的底層代碼——因?yàn)閍vrdude和“ArduinoISP”固件已經(jīng)幫我們實(shí)現(xiàn)了。這也解釋了為什么這個(gè)方法如此通用和穩(wěn)定。最后分享一個(gè)我個(gè)人的小習(xí)慣在每次用自制ISP燒錄重要芯片之前我會(huì)先用萬用表的“通斷檔”快速檢查一下所有連接線尤其是GND和VCC是否短路。這個(gè)簡單的動(dòng)作幫我避免了好幾次因?yàn)槎虐罹€內(nèi)部斷裂或插頭接觸不良導(dǎo)致的詭異失敗。硬件項(xiàng)目很多時(shí)候成功就藏在最基礎(chǔ)的連接可靠性里。當(dāng)你看到IDE提示“引導(dǎo)程序燒錄完成”并且新的芯片能成功跑起B(yǎng)link程序時(shí)那種親手讓一塊硅片“獲得生命”的成就感是單純使用成品開發(fā)板無法比擬的。這不僅僅是完成了一個(gè)任務(wù)更是打通了從軟件到硬件、從應(yīng)用到底層的一道重要關(guān)卡。