NANO130 · 開機載入程式

讓板子自己更新韌體

要改板子上的程式,一直都得接一台專用燒錄器。那台燒錄器只有一支, 所以三塊板子沒辦法同時借出去。這條軌跡要拆掉那個依賴—— 做完之後,一條 USB 轉序列埠的線就能更新韌體。

問題

這顆晶片的程式存在一塊叫 APROM 的記憶體裡,正常開機就是從那裡跑。 要換掉它,得有人從外面寫進去——那個「外面」一直是專用燒錄器。

但晶片還有第二塊小記憶體叫 LDROM,只有 4 KB, 而且可以設定成開機先跑那一塊。 如果把一支「會透過序列埠接收韌體、寫進 APROM」的小程式放進 LDROM, 那塊板子就自己成了自己的燒錄器。

這是很多現成開發板「插上 USB 就能更新」的原理。 差別只在那些板子出廠就有,而這塊沒有——所以要自己放一支進去。

為什麼不直接寫,要拆成三階

直接動手的話,會同時有三個不知道答案的問題:

三個綁在一起,失敗時分不出是哪一個。 更麻煩的是其中一個的失敗方式是「板子開不了機」—— 那時你連問它「哪裡錯了」的管道都沒有。

所以拆成三階,風險最大的放最後, 而且輪到它時前面兩個都已經是已知的。

要回答什麼結果
A設定值現在是多少?晶片能自己抹寫嗎?能。設定值是未寫過的初始狀態
B程式連結到 LDROM 還能成立嗎?能。1.3 KB,燒進去回讀比對通過
C能不能真的從 LDROM 開機?能,而且沒有動到任何永久設定

C 階原本被排成「高風險」,結果不必

原訂做法是改晶片的永久設定。那是寫下去就每次上電都生效的東西, 萬一 LDROM 裡的程式是壞的,板子就每次都開進壞掉的程式。

後來在晶片手冊裡找到另一個開關:它一樣能決定下次從哪裡開機, 但只要斷電再上電就會自動復原

「斷電就復原」把一個永久操作換成了臨時操作,而拿到的資訊一模一樣。 這是整條軌跡裡最划算的一個決定, 而它來自把手冊多讀了一段——不是來自更小心。

本體:不是把舊程式減肥,是換掉底下那一層

專案裡本來就有一支「用一塊板去燒另一塊板」的程式,6 KB 出頭。 直覺是把它壓進 4 KB。那個方向是錯的。

那支程式的體積有一大半,是用來透過除錯線去控制另一顆晶片的, 外加板子上的按鍵與螢幕操作介面。 而 bootloader 燒的是自己——那整層根本不存在, 也沒有人站在板子旁邊按鍵。

原程式的組成bootloader 要不要
序列埠收送、封包校驗、位址檢查、逐字回讀驗證
控制另一顆晶片的除錯協定不要
按鍵與螢幕介面不要

所以做的是「保留對外的溝通協定、換掉底下的執行層」。 最後 2 KB,一半的空間都沒用到。

框架錯了,省下來的力氣會花在錯的地方。 真去壓那層除錯協定的程式碼是白做的——它整個不該在裡面。

三道保險,其中一道自己是壞的

一、不可能寫壞自己

bootloader 住在 LDROM、寫的是 APROM。晶片硬體層面就禁止 一塊記憶體寫自己——所以這支程式不可能把自己弄壞。 這不是靠小心,是靠結構。

二、寫完一定讀回來比對

每寫一個字就讀回來對一次。「寫入沒有回報錯誤」和「寫進去的是對的」是兩件事—— 這個專案先前就吃過這個虧:一條看起來完全正常的傳輸,實際上會偶爾少掉一個位元。

三、APROM 是空的就不要跳過去

等待期間沒人要燒錄時,bootloader 會讓路給使用者程式。 但如果 APROM 是空的(例如剛抹掉、還沒寫新的),跳過去只會當掉, 而那時序列埠已經不在服務了——等於把自己鎖在門外。 所以跳之前先確認那邊真的有程式。

這道保險第一版是壞的。 從 LDROM 開機時,「位址 0」指到的是 LDROM 自己, 所以它每次都讀到自己、每次都判定「有程式」。 實測把 APROM 整個抹掉後叫它讓路,它照樣跳了過去,板子當場沒東西可跑—— 正是這道保險存在的理由,而它自己是壞的。

改成用記憶體控制器去問,才問得到真正的 APROM。 教訓是一句話:在 bootloader 裡,「位址 0」不是一個明確的概念—— 要問「哪一塊記憶體」,就得用管那塊記憶體的硬體去問,不能用程式的視角去看。

驗收:先把自己逼進最糟的狀態,再看它能不能爬出來

做了什麼結果
上電後敲一下序列埠bootloader 回應了
送一支韌體進去(936 byte)0.3 秒寫完,每一塊都通過回讀驗證
叫它跳去跑新韌體燈開始閃
用另一套獨立工具回讀比對內容正確
把整個 APROM 抹掉,再叫它讓路拒絕跳過去,留在原地繼續服務
在那個全空的狀態下重新送一次韌體燈又會閃了,全程沒有用到燒錄器

最後兩列才是這東西真正的價值。會動不稀奇,抹壞了還能自己爬回來才是。

最後一步:讓它每次上電都這樣

前面都還是臨時的。最後改掉晶片的永久設定,讓上電就先進 bootloader。 那個設定值不是猜的——是從晶片廠商自己的工具設定檔裡查到的, 而且只動其中一個位元。

驗證方式是真的拔掉電源再插上:只有真正斷電,那個設定才會被載入。 用軟體重置去測是測不出來的——那條路會被強制指回原本的開機來源。

量到的數字

項目數值
bootloader 大小約 2 KB(上限 4 KB)
上電後等待燒錄的時間2.26 秒,之後讓路給使用者程式
送一支 936 byte 的韌體0.3 秒,含逐字回讀驗證

那 2.26 秒原本在註解裡寫「大約 1.5 秒」,是猜的。 量過才知道差 1.5 倍——不算離譜,但猜的東西不該用肯定句寫

最花時間的不是程式的錯,是觀測方法的錯

這條軌跡真正燒掉的時間,有三次是耗在自己設計壞掉的實驗上, 而不是耗在程式的錯誤上。三次都是同一個形狀: 我以為我在測 A,其實我在測 B。

還有一次是時間量錯了原點:記錄上寫著第 18.7 秒收到訊號, 那個數字是真的,但它是從監聽程式啟動算起,而不是從插上電算起—— 中間那段是人去插電的時間。 我卻據此推論「開機要等快 20 秒、體驗很差、出借前必須修」,整串都不成立。 實際上是 2.26 秒。

時間戳的原點,跟時間戳本身一樣重要。 三個假線索都不是硬體在說謊——硬體從頭到尾都照實回答, 只是我問錯了問題。

老實說的限制

最後一條是個值得記住的情況:單位錯了,但所有使用者都已經照錯的單位校準過。 這時候改單位不是修 bug,是同時製造四十幾個 bug。