1. 程式人生 > >實現"通過串列埠升級嵌入式目標板軟體"功能的一些心得體會

實現"通過串列埠升級嵌入式目標板軟體"功能的一些心得體會

  最近由於專案需要,在AT91FR40162平臺上實現了通過串列埠傳輸目標板二進位制可執行檔案並更新固化到儲存執行程式碼的片內ROM中。在這之前,我進入公司以來,這個平臺上,通過模擬器把程式下載到目標板是更新目標板固化程式的唯一途徑。隨著對嵌入式系統的逐步瞭解,我認識到存在不通過模擬器升級程式的方法:我們日常使用的嵌入式裝置,如有線電視機頂盒,可以通過電視線升級軟體,如路由器,可以通過網線升級軟體,諸如此類,理論上有資料傳輸的通路就可以實現目標板軟體升級。上網翻了下AT91FR40162的資料,不知道現在還有多少人用這塊arm7...還好找到幾年前的一些文件資料,Q&A,還有ATMEL免費提供的上位機軟體ATMEL Host loader,但傳說中配合使用的AT91 Flash Uploader卻沒找到。結合某已離職大牛遺留下沒人再用的程式碼,還有其他平臺的經驗,終於在這個平臺實現了串列埠升級。當中的一些學習心得和經驗體會寫下來作個記錄並分享。
  首先簡要總結串列埠升級的原理。在我們的目標板裡,這塊ARM上電覆位並執行memory remap之後,0地址對映到片內RAM,0x400000對映到FLASH ROM。由於片內RAM為256k,FLASH ROM為2M,目標板的執行程式碼儲存在FLASH ROM,執行時大部分在FLASH ROM執行,只有少部分對速度要求高的對映到片內RAM。實現軟體串列埠升級的基本思路,就是編寫實現接收串列埠資料並把資料轉存至FLASH ROM功能的程式碼,將其用模擬器下載至目標板,並保持以後每次軟體串列埠升級之後FLASH ROM都保持有這樣功能的程式碼。
  接著就具體不同的需求來分析不同的實現方案。我之前在另一個DSP平臺實現過串列埠升級,方法是目標板的日常執行程式碼裡有串列埠升級的模組,當收到串列埠升級命令之後,按照預定的流程把二進位制可執行檔案資料從串列埠接收下來,儲存在RAM裡,在確認無誤後,再刷寫固化入FLASH ROM中。這個方案的要點是,負責串列埠升級的模組程式碼,特別是負責把資料刷寫入FLASH ROM以及之後操作的相關執行程式碼和訪問到的常量,必須載入到RAM裡,以免升級FLASH ROM後代碼位置改變而不能執行;此外RAM要提供一個大的緩衝區,用作儲存二進位制可執行檔案資料。我分析過這次ARM7平臺的程式碼量在100k多一點,也就是二進位制可執行檔案大小100多k,需要載入到RAM的程式碼和變數資料之和小於100k,這樣256k的RAM剛好能滿足要求。就這樣,參照離職大牛的程式碼,瞭解這塊ARM的FLASH操作之後,移植DSP平臺的串列埠升級模組程式碼,便實現了軟體串列埠升級的功能。我認為這方案特點是:一,對記憶體的使用量比較多,比較適合RAM相對較充足的情況;二,執行的時候隨時升級,但存在風險:一旦在寫FLASH的過程中掉電,或者刷入了不含串列埠升級功能的程式碼,之後就只能接模擬器刷了;三,編碼簡單,移植方便。然而,雖然目前我這個專案的程式碼量還不多,但如果以後要增加功能,勢必會增加程式碼大小和記憶體使用量,那時候使用這方案就不合適了,因而我必須思考其他實現方案。
  既然記憶體不充裕,那我就減少升級程式碼緩衝區的大小,例如設定一個固定的FLASH ROM頁面大小,剛好是FLASH進行清除操作的最小單元;這樣填滿了這個緩衝區,就進行寫FLASH操作,寫好繼續接收。現在我常用的串列埠波特率最高是115200bps,就是說每秒最多傳14400位元組,假如刷寫一個頁面的FLASH ROM需要x個毫秒,從串列埠驅動的接收緩衝區拷資料到升級程式碼緩衝區需要y毫秒,那麼串列埠驅動的接收緩衝區必須大於14.4(x+y)個位元組,才能避免丟失串列埠數。我沒有查資料和做準確測試,這x+y估計在100以下。所以,相對於方案一,這個方案節省了大量記憶體,從原來的100多k減少到一個頁面4k,而且還不需要限制程式碼量的大小,只要FLASH ROM裝得下;不過,風險大大增加:方案一在串列埠傳輸的過程中如果掉電,只是升級失敗,舊程式還在,而這方案如果在那時候掉電,新程式還沒完全刷好,那又得接模擬器重刷了。一般FLASH ROM的重刷只需不到一秒,但100k的資料傳輸需要好幾秒,為了避免這一風險的增加,我想到在FLASH ROM上做備份。例如現在FLASH ROM有2M位元組這麼多,假如我現在的程式碼翻十幾倍,還可以多放1個相同的映象。因此,我在接收時不刷寫舊程式碼所在的頁面,找空閒的頁面先把新的程式碼刷進去,在接收完成並確認無誤後,再把新的程式碼從備份頁面拷貝到舊程式碼的頁面。這樣掉電的風險將和方案一相差不大。
  參照方案二的改進版,要完全消除軟體升級時掉電的風險,可以在FLASH ROM中騰出專門一個頁面,放置串列埠升級相關程式碼;升級時只清空更新其他頁面,而這個頁面的程式碼永不擦除,駐留在FLASH ROM。而且,這部分程式碼每次啟動後都要能執行。這也就是ATMEL的Flash Uploader的實現方法。具體的思路如下:當ARM啟動之後,跳入串列埠升級相關程式碼,等待升級指令,如果接收到指令,便進入串列埠升級流程,否則等待一小段時間之後,跳入目標板執行的日常功能程式碼。例如,我在0x400000開始的FLASH ROM寫入啟動和串列埠升級程式碼,在0x500000開始的FLASH ROM寫入日常功能程式碼,ARM的入口程式碼位於0x400000;當我需要串列埠升級時,啟動目標板並不斷通過串列埠傳送預先定義好協議的同步指令,使目標板執行串列埠升級程式碼,從而更新位於0x500000開始的日常功能程式碼;當平時應用時,目標板啟動後沒有偵聽到同步指令,在一小段等待時間後跳入位於0x500000,執行日常功能程式碼。相比於方案一和二,這個方案完全不擔心掉電,代價是延長了日常功能程式碼的啟動。在我慢慢理解離職大牛的程式碼後,發現原來他的程式碼就是這種方案的一種實現。啟動程式碼就是bootloader,先執行串列埠升級的程式碼,再執行日常功能程式碼,而且在執行串列埠升級程式碼時,整個RAM除了中斷表和串列埠升級程式碼要用到變數外,一般還有很大的餘量,可以增大資料緩衝區,或者加入更多的機制來完善串列埠升級。受此啟發,我還想到了更多複雜的應用方案。
  例如,結合方案一和方案三,或者方案二和方案三,便既可以保留日常執行時升級的功能,又避免了偶然斷電等因素導致裝置不能再串列埠升級的風險。或者改進方案三,使目標板啟動後先判斷位於0x500000處的程式碼是否可執行,如果是則執行日常功能程式碼,否則跳入串列埠升級程式碼;在日常執行時也可以接收指令跳入串列埠升級程式碼,從而減少日常功能程式碼的啟動時間,並且在日常執行中執行串列埠升級時可獲得最大化的RAM資源,當然這裡如何準確判斷0x500000處的程式碼是否正常將是個重點。貌似現在有些ARM就駐留了這樣的啟動程式碼在FLASH ROM的首個扇區。
  最後,總結一些經驗體會:
一,這段時間為了實現這個功能,我翻查了不少資料,對ARM主要是AT91FR40162的啟動過程有了進一步理解,對memory remap,boot loader等都有了更多的認識。相信這對我學習其他cpu程式設計來說是個好的基礎。
二,通過一步一步對啟動程式碼的除錯,我克服了對ARM彙編的恐懼,雖然彙編指令沒懂幾個,而且看到一大段彙編程式碼還是會頭疼,但至少那種神祕感消失了;通過除錯彙編程式碼,某些問題的原因可以得到快速而準確的定位,從而採取了有效地解決方案。
三,經過多次串列埠升級的實踐,我認為檢測出傳輸錯誤、保證刷寫的內容正確是關鍵的。目前我的方案是加入CRC校驗,主要是日常功能程式碼裡有用到CRC,這樣可以減少加入額外程式碼,當然我得用相應上位機程式軟體進行加工或配合。
四,串列埠升級程式碼的設計,最好要有周全的考慮,例如對FLASH ROM扇區的規劃使用,串列埠升級指令的協議等等,不能只看目前應用的情況,還得考慮將來擴充套件的靈活性。雖說這部分程式碼本身也可以升級,但不變是最好的。“不變應萬變”
五,不怕做不了,只怕想不到,我也算是重複造了個輪子,而且還打算增加更多的功能,不過仔細想想還是罷了,太複雜的東西恐怕會像離職大牛的程式碼一樣,被丟著不用了。
六,簡要列舉三種主要方案的特點