韓雨泓,陳良勇
(1.四川大學(xué) 計(jì)算機(jī)學(xué)院,四川 成都 610000;2.原成都軍區(qū)聯(lián)勤部后勤信息中心,四川 成都 610000)
在實(shí)際的生產(chǎn)工作中,從經(jīng)濟(jì)和實(shí)用性方面考慮,選擇嵌入式系統(tǒng)MCU控制器時(shí)常常會(huì)使用到一些性能相對較弱,功能配置也不太強(qiáng)大的單片機(jī),這類單片機(jī)有時(shí)候僅僅夠用,無法從根本上滿足多元化的需求。如果需要更多的功能就需要選擇更高端的單片機(jī),同時(shí)也意味著成本的增加。本文在項(xiàng)目過程中選用的MCU即為這種類型。在產(chǎn)品的整個(gè)生命周期中,需要對應(yīng)用程序進(jìn)行及時(shí)的升級換代,這就用到了IAP功能[1-2],而該款MCU不具備硬件級的中斷向量重定位功能,因此只能另辟蹊徑,以軟件的方式解決該問題。
在傳統(tǒng)的單片機(jī)應(yīng)用開發(fā)中,程序員將寫好的代碼通過IDE編譯鏈接后生成可執(zhí)行的二進(jìn)制文件下載到單片機(jī)Flash中即可[3],功能的修改或者錯(cuò)誤的修正需要程序員在完成代碼修改后,再次通過專用調(diào)試器下載到單片機(jī)中才能實(shí)現(xiàn)。在簡單小批量產(chǎn)品功能的實(shí)現(xiàn)中,這已經(jīng)可以滿足要求。如果是已經(jīng)量產(chǎn)的產(chǎn)品,要想再修改功能幾乎不可能。但是面對越來越復(fù)雜的應(yīng)用環(huán)境,越來越多的應(yīng)用要求,一次性調(diào)試好代碼并下載運(yùn)行的情況已經(jīng)變得越來越難,而且隨著代碼量的增多,隱藏的BUG也越不容易被發(fā)現(xiàn),需要在后期的應(yīng)用中不斷完善。
IAP(In Application Programming)功能即是為了應(yīng)對這種情況應(yīng)運(yùn)而生的。IAP是一段完整的程序,它與應(yīng)用程序(APP,即具體實(shí)現(xiàn)功能的單片機(jī)代碼)是相互獨(dú)立和分開的兩部分,它的功能是在產(chǎn)品全生命周期中隨時(shí)根據(jù)需要對應(yīng)用程序部分代碼進(jìn)行擦除和燒寫,目的是為了在產(chǎn)品發(fā)布后可以方便地通過預(yù)留的通信接口對產(chǎn)品中的功能進(jìn)行更新升級[4]。
IAP和APP是同時(shí)存在于單片機(jī)上的兩個(gè)完整的代碼段。IAP實(shí)現(xiàn)對APP段代碼的擦除和重新燒寫,APP則負(fù)責(zé)實(shí)現(xiàn)具體的功能任務(wù),當(dāng)需要時(shí)跳轉(zhuǎn)至IAP進(jìn)行程序更新。它們分別燒寫在單片機(jī)Flash的兩個(gè)區(qū)域。對于本文所使用的華芯微特SWM240D8U7,它擁有64 KB的片上Flash(0x0——0xFFFF),就可以劃分16 KB給IAP(0x0——0x3FFF),余下48 KB留給APP(0x4000——0xFFFF)。需要特別注意的是IAP代碼和APP代碼編譯后的大小不能超過所分配的空間大小。
本文使用的SWM240單片機(jī)在上電后固定地從Flash地址0處開始執(zhí)行代碼[5],因本文將IAP程序放于此處,因此首先運(yùn)行IAP程序,在IAP程序中通過檢查預(yù)先設(shè)置的標(biāo)志信息來確定APP代碼段是否正常,若符合要求就跳轉(zhuǎn)到APP段開始執(zhí)行,否則進(jìn)入等待升級燒寫狀態(tài)。
ARM的Cortex M0單片機(jī)將代碼的最初幾十個(gè)字作為中斷向量入口,該區(qū)域存放的是對應(yīng)的中斷處理程序代碼所在的開始位置。中斷發(fā)生時(shí)由硬件將PC指針拉到對應(yīng)的中斷入口處,取得地址后跳轉(zhuǎn)到中斷處理程序,完成完整的中斷處理過程[6]。在IAP和APP共存的理想情況下,如果在APP中發(fā)生中斷,中斷指針應(yīng)該指向APP入口處的中斷向量;同樣,如果在IAP中發(fā)生中斷,中斷指針就應(yīng)該指向IAP入口處的中斷向量??蓪?shí)際情況是,Cortex M0系列沒有像其他M3/M4/M0+系列核心所具備的中斷矢量表重定位寄存器[7],其中斷發(fā)生時(shí)會(huì)始終會(huì)指向從0x0開始的某入口向量處,即本文存放IAP程序的向量位置。那么當(dāng)APP中發(fā)生中斷時(shí),取得的是IAP中的處理程序地址如圖1所示。
圖1 IAP啟動(dòng)流程及中斷流程
由于每次中斷發(fā)生時(shí)指針都指向從IAP開始的入口地址[8],因此可以考慮將IAP的所有可屏蔽中斷(或者只是用到的中斷)程序都寫成一個(gè)跳轉(zhuǎn)程序,根據(jù)當(dāng)前所處的位置來取得不同的跳轉(zhuǎn)地址,這在單片機(jī)的啟動(dòng)文件中可以通過匯編語言來實(shí)現(xiàn)。跳轉(zhuǎn)地址的取得可以有多種方法。這里通過將向量地址拷貝到RAM指定位置來實(shí)現(xiàn)。在IAP進(jìn)程時(shí)內(nèi)存存儲(chǔ)的是IAP中斷向量地址,當(dāng)跳轉(zhuǎn)到APP時(shí),在APP中將該內(nèi)存地址存儲(chǔ)內(nèi)容改寫為APP的中斷向量地址。同樣當(dāng)再次跳轉(zhuǎn)回IAP時(shí)也會(huì)執(zhí)行同樣的操作。這樣無論是在哪個(gè)運(yùn)行空間,雖然中斷都是跳回IAP向量位置,但是獲取的中斷地址卻是與運(yùn)行空間對應(yīng),如圖2所示。
圖2 修改后的中斷處理流程
首先要實(shí)現(xiàn)的是將原IAP入口處的中斷函數(shù)改寫為跳轉(zhuǎn)函數(shù)。在ARM的啟動(dòng)文件中定義了與硬件相對應(yīng)的中斷函數(shù)名稱,如圖3所示。通過修改啟動(dòng)文件中的函數(shù)內(nèi)容,使用LDR和BX兩條指令來實(shí)現(xiàn)取值和跳轉(zhuǎn)。
圖3 啟動(dòng)文件定義的中斷函數(shù)名
用戶如果在自己的代碼空間中實(shí)現(xiàn)了該函數(shù),并且在啟動(dòng)文件中EXPORT了該函數(shù),中斷后就會(huì)跳轉(zhuǎn)到用戶函數(shù)中。可以修改為自定義的跳轉(zhuǎn)指令。以GPIOA0外部中斷函數(shù)為例,圖4顯示的是未修改前在啟動(dòng)文件中的代碼段,它的作用是當(dāng)發(fā)生該中斷時(shí),使程序跳轉(zhuǎn)到GPIOA0_Handler函數(shù)去執(zhí)行(函數(shù)名其實(shí)就是一個(gè)地址)。圖5顯示的是修改以后的代碼段,它的作用是當(dāng)發(fā)生中斷時(shí),使程序跳轉(zhuǎn)到GPIOA0_ISR_FUN_ADD所指的地址去執(zhí)行。這里__GPIOA0_ISR_FUN_ADD是強(qiáng)行指定的RAM地址,該地址的內(nèi)容根據(jù)是在IAP還是在APP有所不同,如圖6所示。
圖4 修改前的中斷函數(shù)
圖5 修改后的中斷函數(shù)
圖6 指定存儲(chǔ)中斷地址的內(nèi)存位置
其次,在完成跳轉(zhuǎn)函數(shù)后,需要分別在IAP和APP代碼段將自己空間的中斷函數(shù)地址填入對應(yīng)的RAM位置。做法是在代碼中定義全局變量,并將變量指定在RAM的固定位置,實(shí)現(xiàn)代碼如下:
uint32_t VectorTable[4] __attribute__((at(0x20000000)));
此處只使用了4個(gè)中斷并將該數(shù)組固定在RAM起始位置。最后,需要分別在IAP和APP代碼初始化階段,將自己運(yùn)行空間的處理函數(shù)地址填入對應(yīng)的數(shù)組中即可。實(shí)現(xiàn)代碼如下:
VectorTable[0] = (uint32_t)GPIOA0_Handler;
VectorTable[1] = (uint32_t)TIMR0_Handler;
VectorTable[2] = (uint32_t)GPIOC0_Handler;
VectorTable[3] = (uint32_t)TIMR1_Handler;
需要說明的是,在編寫代碼時(shí),由于IAP和APP是兩個(gè)獨(dú)立的工程,分別都有自己的啟動(dòng)文件,而單品機(jī)發(fā)生中斷時(shí)起作用的是IAP中的啟動(dòng)文件,因此只需要將IAP中的啟動(dòng)文件進(jìn)行修改即可,APP中的不做改動(dòng)。另外,在IAP和APP初始化過程中,文中所述獲取中斷處理函數(shù)地址的方法,APP中還可以通過直接拷貝對應(yīng)中斷向量表中的內(nèi)容來得到,但是在IAP中必須按照上面的方法,如果拷貝中斷向量表的內(nèi)容,得到的就是啟動(dòng)代碼中XXX_Handler PROC的地址,這將導(dǎo)致程序進(jìn)入死循環(huán)。
經(jīng)過上面的設(shè)置后,分別將IAP和APP燒入對應(yīng)的Flash空間,就可以實(shí)現(xiàn)兩個(gè)空間的中斷。如果用戶有多個(gè)運(yùn)行空間,也可以按照此方法來進(jìn)行處理。
按照本文所述,在某國產(chǎn)SWM240D8U7單片機(jī)上分別建立了基于CAN通信的IAP和APP程序。程序上電后首先按照硬件默認(rèn)設(shè)置從0x0處運(yùn)行代碼,該處代碼完成MCU的啟動(dòng),中斷向量的定位,APP應(yīng)用標(biāo)志的檢測(檢查APP代碼段標(biāo)志是否正常),如果一切正常則跳轉(zhuǎn)到應(yīng)用程序段執(zhí)行,否則等待接收從CAN通信接口傳送過來的升級數(shù)據(jù)包。經(jīng)測試文章所述方法完全滿足要求,在IAP和APP之間相互跳轉(zhuǎn)的效果和基于硬件設(shè)置的單片機(jī)效果一致,達(dá)到了設(shè)計(jì)初衷。