汽車電控系統bootloader知識介紹



01


原理概述

單片機通常燒錄有三種:
  • ISP(In-System Programming)
在系統編程,使用引導程序(Bootloader)加上外圍UART/SPI等接口進行燒錄。
  • ICP (In-circuit programmer)
在電路編程,使用SWD/JTAG接口。
  • IAP(In-Application Programming)
指MCU可以在系統中獲取新代碼并對自己重新編程,即用程序來改變程序。
平時開發使用主要IAP升級方式,對ECU更是如此。
單片機正常時運行上電/復位,第一條指令是固定的,程序正常順序運行到Bootloader,由Bootloader跳轉到APP程序運行。
汽車電控系統bootloader知識介紹的圖1
圖1-1 Bootloader簡易流程

02


技術分析

2.1 什么是Bootloader
單片機正常運行時總是從固定地方取指令,順序運行,這將對編寫程序的人產生巨大的挑戰,程序更新時需要使用燒錄器等工具燒錄,于是有人將程序設計成,由一個程序跳轉到另一個程序,這個程序通常稱作Bootloader,另一個叫做APP。
Bootloader程序常常具有通信接口和擦寫內部存儲空間的功能,可將需要更新的APP擦除,寫入新的APP。有時會設計成相互跳轉,技術也是可以實現的。有些為了保證程序不丟失,單獨預留出備份區和出廠版本,出現某些錯誤時可以恢復到出廠版本或使用其他APP均可。

汽車電控系統bootloader知識介紹的圖2

圖1-2 Bootloader擴展流程
2.2 ECU的Bootloader
ECU是MCU的一種,專門用在汽車上。ECU經常會用在汽車零部件中,零部件密封性等要求都比較苛刻,并且裝車,如果想取下零部件可能需要將車拆解才可以做到,這種行為是不被允許的,成本極高,操作復雜,因此大多主機廠(廠商)要求ECU具有升級功能,并且通過多年的積淀制定了行業標準UDS。
UDS簡介:
UDS(Unified Diagnostic Services,統一診斷服務)診斷協議是用于汽車行業診斷通信的需求規范,由ISO 14229系列標準定義。應用于OSI七層模型的應用層(第7層),它只規定了與診斷相關的服務需求,并未涉及通信機制,所以,它可以在不同的汽車總線(例如CAN, LIN, Flexray, Ethernet 和 K-line)上實現。
ISO 14229-1 定義了診斷服務,只有應用層,不涉及網絡及實現。
ISO 14229-3定義了UDS在CAN總線上的實現。
診斷通信用于建立診斷儀與ECU之間的通信連接,并負責將ECU中的診斷結果輸送到診斷儀中。
UDS的作用非常廣泛,幾乎跟隨ECU軟件開發的全過程。
  • ECU開發過程要用到它來構建bootloader,上傳和下載數據,即軟件刷寫,控制器Reset;

  • 測試時要用它來讀寫存儲,控制外設;

  • 產線上,要用它來校準機械件,控制例程,進行下線執行器測試,刷新軟件,配置防盜,讀取號碼,下線配置等。

  • 在行駛過程中,要用它來監測各種故障,并記下故障碼;

  • 4S店里,技師需要讀取當前故障、歷史故障,讀取故障發生時刻環境信息狀態,清除故障,判斷故障發生點,還可以用來升級ECU程序。

汽車明確規定通過UDS進行更新程序,主機廠要求擦寫內部存儲的代碼不可寫入正常代碼中。汽車電子中ECU一旦設計完成,裝車量產就很難再拆卸并返回零部件供應商完成功能升級或補丁修復。一旦出現售后質量問題,如果召回的話,零部件供應商和整車廠將面臨嚴重的經濟損失,因此設計基于CAN總線的ECU在線程序更新Bootloader可以很好的解決這一問題,將零部件供應商和整車廠的損失降低到最小。目前國外大部分汽車整機廠(主機廠)和全球的一級汽車零部件供應商 (Tier 1) 都要求在其設計的ECU實現Bootloader功能。
汽車電控系統bootloader知識介紹的圖3
圖1-3 Bootloader簡易框圖
假如使用CAN,框架則會設計成如圖1-3。

2.3 Bootloader框架
Bootloader由主機廠或者自己,可以選擇用或者不用,本次主要針對使用Bootloader情況進行分析。主機使用協議由自己進行定義,ECU啟動模式選擇由芯片廠商進行技術支持(如果沒有廠商支持是不可以的,是不被主機廠認可的,大多數是購買商業軟件包,由服務商進行技術支持與芯片廠商共同支持的)。內部編寫均需要遵循協議,大多數開發都是由多年開發經驗沉淀下來,修改而成的,協議依然在進步,代碼可能無法維護而無法支持,主機廠也會被迫選擇使用舊版協議。
汽車電控系統bootloader知識介紹的圖4
圖1-4 Bootloader架構
2.4 ECU Bootloader原理
主機廠規定不可把擦寫內部代碼的功能直接寫入程序中,因此,只能每次用時才能將代碼放入ECU,ECU內部可以有Bootloader,但不可以有擦寫內部代碼的功能,擦寫代碼的功能稱作NVM (None Valitale Momory–非易失性存儲器)驅動程序。
汽車電控系統bootloader知識介紹的圖5
圖1-5 NVM驅動示意圖
主機將NVM驅動程序下載到RAM區域,用NVM驅動程序對內部NVM進行擦除寫入新數據等,在最后跳轉即可完成更新。
UDS服務設計復雜,Bootloader升級一般分為以下三步:
1、預編程:主要進行一些環境配置
2、編程:刷寫過程
3、刷新完成:恢復配置
Bootloader可以保證在上述三個階段任一問題出現都能再次進入該過程重新刷新。
1、預編程階段
在進入刷新之前,UDS的85服務和28服務,關DTC和非診斷報文。使整個CAN網絡處于安靜的狀態。這是對整車網絡進行操作的,一般都是以功能尋址的方式來發送。注意先用85服務關閉DTC,再使用28服務關報文。通常都會關閉故障碼保存,關閉CAN通信是為了加快刷寫速度,過程如下:
汽車電控系統bootloader知識介紹的圖6
圖1-6 預編程流程
2、編程階段
汽車電控系統bootloader知識介紹的圖7
圖1-7 編程流程
UDS設計了安全訪問功能,安全訪問是為了保證ECU數據的安全,實現方式是由ECU發送一個種子到主機,主機通過dll文件算法算出結果與ECU算出結果進行比對,結果一致則解鎖成功通過安全驗證。ECU解鎖可以存在多個等級。
寫時候先寫DID指紋,標記寫軟件人的身份(按照主機廠要求),擦寫下載等操作。
3、編程結束
刷寫完成之后,ECU進行重啟,重新進入擴展會話,打開之前關閉的配置即可。

03


實現

3.1 Bootloader開發難點分析

(1) 硬件初始化:
當硬件上電之后,運行的第一條指令就是Bootloader的代碼(如果各式各樣的BIOS忽略不計的話)。Bootloader需要完成硬件初始化的一系列工作。然后才可以進入正常的邏輯,例如加載OS Image等。對于軟件工程師而言,硬件的初始化工作是很冗長乏味的。需要詳細閱讀各類硬件的資料規范。然后就是一系列的對寄存器的操作。雖然Bootloader中不需要初始化板子上的所有的硬件,而只需要初始化最基本的可以讓Loader正常工作的硬件就可以,有一些外設可以放到OS啟動的時候,甚至驅動加載的時候再進行初始化不遲。即使是這樣,要初始化的硬件也不在少數,對于一個典型的ARM系統而言,有可能要做的事:初始化內存控制器,初始化MMU,配置GPIO口,配置調試串口,對RTC進行讀寫操作,如果要通過以太網下載,還需要驅動網絡接口……這一系列的工作,沒有一個不是體力活。需要細心的琢磨,很有可能對寄存器某一個bit的粗心設置,就會導致整個Bootloader無法工作。所以這一部分內容通常馬虎不得,需要耐心完成。

(2) 代碼編寫和構建:
由于Bootloader是最底層的代碼,匯編語言肯定是少不了的了。就連一些整天喊“匯編已死”的人也不能否認,系統啟動的那一段代碼,還是需要匯編語言的。而匯編語言通常也是軟件工程師們不太希望去碰的“硬骨頭”。這也增加了Bootloader編寫的困難。
代碼寫好之后,當然要編譯成機器碼才可以在板子上運行。目前的編譯器大多數只會把代碼編譯成某些流行的可執行文件格式,例如Windows上的PE和*Nix上的ELF等。這些帶有格式的可執行文件,也沒有辦法再目標設備上直接運行。所以,通常OS都會提供一些工具,把這些可執行文件去頭去尾,轉成純二進制格式,這樣才可以在目標設備上運行。例如ADS提供的FromELF工具與Windows CE提供的ROMImage工具就是完成這類工作的。通常,我們需要為這類工具做一些配置,例如告訴這些工具代碼段放在什么地方,起始地址是多少等等。如果這些參數沒有配置正確,很有可能最終生成的Bootloader的映像是不可用的。那么燒寫到目標設備上,自然也就無法運行。同樣,對于這些參數的配置,也需要仔細檢查核對,一步步進行。

(3) 開發的效率:
Bootloader的另外一個開發困難的原因是它的開發效率。通常當我們做了一些代碼修改之后,都需要把修改后的二進制文件使用燒寫工具燒寫到目標設備的Flash中,無論是NAND還是NOR Flash,燒寫的過程都不快。所以,即使是改了一行代碼,也需要經過編譯->燒寫->運行這樣一個完整的流程。一般而言再快也要10分鐘左右。這樣算算,一個鐘頭可以修改個5次代碼,一天可以修改個50次代碼就相當不錯了。機械的重復這一過程,經常會使開發人員感到開發效率低下,從而產生反感和抵觸情緒。這也是Bootloader開發的一大劣勢。一個解決的方法是使用硬件調試工具把Bootloader的映像直接灌到RAM里面運行,往RAM里面灌通常比燒寫Flash要快。但是這樣需要調試工具來初始化RAM,又有很多的其他邏輯上的和細節上的事情要做。

(4) 調試
上面說的幾大問題,其實還都是可以克服的問題。其實在我看來,開發Bootloader最大的問題還是調試問題。試想:無論匯編多么難,我還是寫好了,無論燒寫多么煩,我還是燒寫下去了,但是當我懷著激動的心情按下Reset鍵的時候,整個硬件設備毫無反應。我怎么知道我的代碼寫的正確還是不正確呢?如果不正確,我又怎么能定位到我的錯誤呢?現在的軟件開發中,無論是編譯型語言還是script,一般都會提供相應的Debugger,讓開發人員來定位代碼錯誤。“摸黑”寫代碼是軟件工程師們最害怕的事情。代碼出了問題,如果沒有行之有效的手段來做問題定位,十有八九會造成項目“卡殼”。如果定位準確,那么問題也就解決了一大半了。所以歸根結底,還是調試的方法論問題。Bootloader中難以調試,是因為可以使用的手段非常少,也不常規。在OS下開發應用程序用到的那些調試手段手段,在Bootloader的開發中通常都用不上。需要有“非常”手段來調試。下面的專題,就向大家介紹一些Bootloader的常用調試方法。
3.2 一些調試技巧

(1) 硬件調試器
相信很多從應用開發走過來的開發人員,都會對Set Breakpoint,Step into, Step over等這些調試手段相當懷念。但是那些東西都是調試器在搞鬼。到了裸板上,可沒有Debugger來幫我們了。那怎么辦?好在我們有硬件調試器。很多CPU體系結構都提供了相應的硬件調試器,例如ARM CPU的仿真器。借助硬件調試器,我們可以完成匯編級的Set Breakpoint,Step into, Step over這些操作。這對于調試Bootloader來說,實在是太重要了。至少我們可以看到我們燒寫下去的代碼是否正確,然后可以看到是否在運行。這對于調試Reset后的第一段代碼來說,實在是雨后春筍一般的珍貴。
但是硬件調試器也有很多不足:首先,它們一般都價格不菲,不用國內自己的D的話,幾W是少不了了。其次,大多數硬件仿真器只能實現匯編級的調試,當代碼進入了C語言之后,硬件調試器就顯得力不從心了。第三,一劣質的仿真器,通常還無法對MMU打開后的虛擬內存進行仿真。所以限制也是很多的。

(2) 一閃一閃亮晶晶――LED燈
如果我們買不起硬件仿真器,或者手頭上根本就沒有。那么只好通過硬件的一些輸出端口來輸出一些信息,讓我們知道代碼到底運行到哪里了。在諸多的端口中,最簡單的恐怕就是LED燈了。一般而言,用個幾句匯編,就可以讓LED閃爍起來。這樣,我們就可以在代碼中安插讓LED閃爍的語句。來看我們的代碼到底在哪里跑飛或者掛掉的。可惜LED等只能閃爍,想通過LED來獲得更多的消息,是很困難的。例如我們想知道某個變量的值,用LED的閃爍表示出來,恐怕就很傷腦筋。用LED來閃爍出摩爾斯代碼是一種可行方案,但是除了開開玩笑之外,恐怕真的去實踐的人是不存在的……

(3) 終于可以Printf了――調試串口
目前為止,在Bootloader甚至整個OS開發過程中,串口可能是使用最廣泛的調試端口了。它的設備簡約卻不簡單。用一根串口線,把PC跟目標設備連接起來,這樣我們可以通過串口來輸出一些字符,在PC一端接收。終于,在有OS的應用程序開發中最傳統的,最原始的調試方法printf就可以用了。在Bootloader的開發中,如果可以通過串口輸出調試信息,那調試的手段就前進了一大步了。至少我們可以通過printf來跟蹤目前代碼在哪個函數里,分支語句走了哪個流程,某個變量的值到底是不是我們想要的……世界真美好。串口的問題是它不像LED那樣想用就用,還是需要初始化的。好多嵌入式CPU都把串口控制器集成到了里面,甚至有一些會在CPU上電的時候自動初始化串口,這使得串口初始化相對簡單。但是如果有一些串口是外掛的控制器,那么初始化一個串口有可能也需要耗費你半天時間。

(4) 走進新時代――內核調試器
隨著開發進一步復雜,串口輸出調試信息恐怕又不能滿足我們的要求了。首先,輸出的信息一多,很容易亂套。其次,輸出信息業是需要花費時間的。在中斷處理函數等一些時間敏感的地方使用串口輸出,有時候依然不明智。如果能把遠程調試器接上,那對BootLoader的調試跟對應用程序的調試就沒有什么二致了(源碼級設置斷點,一步步跟,隨時看某些數據結構的值,都成了可能)。Linux提供了GDB,Windows CE提供了KD。但是這都需要實現相當多的工作,實現一些調試器Stub。GDB可能串口就可走。但CE的KD通常是要通過KITL,通過以太網的。雖然復雜,長遠來看還是非常有必要的,所以,當感覺其它手段力不從心的時候,可以考慮啟用更高級別的調試器了。

(5) 其它歪門邪道
如果我的板子上沒有硬件調試器,沒有LED,串口也不工作,更別提內核調試器了,那我怎么辦?別著急,只要你的硬件有輸入輸出設備,我們都是可以想出辦法來的。只要能想辦法往輸出設備中輸出一點東西,并且可以通過一些手段得到這些輸出信息,我們調試的目的就達到了。例如:往喇叭里面輸出一段雜音,往塊設備上寫一個扇區,往屏幕上畫一些亂七八糟的東西……代碼運行起來之后,只要不是當“瞎子”,都可以實現調試的目的。再走投無路的時候,這些手段不妨想想。

3.3 實例
以NXP推薦設計簡單講解原理。NVM驅動NXP官方提供完整的實現函數庫,不需要自行實現,實際開發中也是如此,大部分是對工具的使用,而不是從零開始開發功能。
使用腳本文件抽取指定ECU的NVM驅動代碼,全部代碼是以C語言const數組存儲。
將NVM驅動函數地址存儲在指定地址作為 NVM 驅動函數地址表。
因為全部使用const關鍵詞修飾,全部存儲在常量區域,只讀數據段,修改鏈接文件將只讀數據段固定到RAM設定地址,則NVM驅動編譯后存儲在RAM區域,生成S19文件。
從得到的S19文件分離出NVM驅動和NVM 驅動函數地址表的S19文件,稱為NVM 驅動S19文件
將NVM 驅動S19文件與應用S19文件合并生成完整的Bootloader。NVM 驅動S19文件需要保持在整個文件的開始,以保證系統能夠正常運行找到NVM驅動。
NVM驅動是NVM 獨立驅動是靈活可裁剪的。因此可以根據 Bootloader 的功能選擇必要的 NVM驅動函數,從而減少其占用的 RAM 空間,以適應小RAM尺寸的ECU(比如 1KB RAM 的ECU 系列), 當然還需要改變其編譯地址和 NVM 驅動函數映射地址表。


04


總結

雖然是一個比較復雜的問題,在分析問題時,將問題分解,比如,整個Bootloader分為通信、存儲等,梳理過原理之后,可以預測到代碼實現邏輯,再追蹤定位,驗證預測。為保證安全提出并實現了一種基于總線通信將NVM驅動程序由上位機下載到 RAM 中運行而非讓其駐留于ECU片上FLASH的安全Bootloader 設計,有效避免了應用程序跑飛運行至駐留于片上 FLASH的NVM驅動代碼所造成的程序/數據丟失失效。但對于汽車上不是一朝一夕能實現的,雖然單一功能簡單,為了保證這個功能而設計很多框架用來保證,框架是各種協議的實現,難度極大,需要長時間積淀才能理解其中奧義。
登錄后免費查看全文
立即登錄
App下載
技術鄰APP
工程師必備
  • 項目客服
  • 培訓客服
  • 平臺客服

TOP