高世皓
(北京郵電大學 泛網(wǎng)無線通信教育部重點實驗室,北京 100876)
利用HEX文件實現(xiàn)TMS320F28335的程序升級方法
高世皓
(北京郵電大學 泛網(wǎng)無線通信教育部重點實驗室,北京 100876)
提出了一種通過發(fā)送HEX文件到TMS320F28335進行程序升級的新方法。配置TMS320F28335為FLASH啟動模式,通過預燒寫升級程序到FLASH中,可實現(xiàn)在串口發(fā)送HEX文件升級用戶程序的功能。詳細介紹了該方法實現(xiàn)原理和開發(fā)過程,并且給出軟件流程圖和關鍵代碼。實驗證明,該方法操作方便,可靠性高,大大提高了效率。
TMS320F28335;FLASH啟動;HEX文件;程序升級
伴隨著科技的進步和用戶需求的不斷更新,在實際應用中需要對已安裝的DSP設備進行程序更新升級。在開發(fā)階段,更新程序的方法是采用CCS集成開發(fā)環(huán)境進行編譯,然后通過仿真器連接設備進行升級,但在實際復雜的工作環(huán)境中,取下設備連接仿真器會降低效率,而且有些情況下很難實現(xiàn),考慮到升級程序的需要,可以通過串口接收數(shù)據(jù)來實現(xiàn)程序的升級[1]。
利用串口更新DSP程序的方法有很多,本文創(chuàng)新地提出使用HEX文件實現(xiàn)TMS320F28335升級程序的方法。當DSP中已經(jīng)預燒寫了升級程序時,升級時只需PC機上具有用戶程序的HEX文件和串口調試助手即可完成程序的更新,不需要使用專業(yè)的CCS軟件和仿真器。
本方法是在TMS320F28335處于FLASH啟動模式下開發(fā)的,首先通過仿真器向DSP的FLASH扇區(qū)中燒寫升級程序,此程序不是用戶程序,當需要升級程序時,將DSP復位后發(fā)送升級命令,此時DSP進入升級狀態(tài),然后通過串口發(fā)送HEX文件到DSP,根據(jù)HEX文件內(nèi)容燒寫到相應的FLASH扇區(qū)完成升級,之后DSP開始運行新的用戶程序。硬件由TMS320F28335最小系統(tǒng)板和USB轉TTL模塊組成,使用DSP的SCI-B進行串口通信,引導模式設置為FLASH引導,即GPIO84~GPIO87引腳狀態(tài)配置為高電平,硬件組成框圖如圖1所示。
圖1 硬件組成框圖
HEX文件以行為單位,每一行為一條HEX記錄,HEX每行以冒號開頭,由一個回車和一個換行結束,內(nèi)容以十六進制數(shù)的ASCII碼形式顯示,其格式如下[2]:
“:”表示開始標志;第1字節(jié)表示長度域,代表這條記錄中數(shù)據(jù)的字節(jié)數(shù);第2、3字節(jié)表示地址域,代表相對于基地址的偏移地址;第4字節(jié)表示類型域,0x00表示數(shù)據(jù)記錄,0x01表示結尾記錄,用來標識文件結束,0x04表示擴展線性地址記錄;第5字節(jié)——倒數(shù)第2字節(jié)為數(shù)據(jù)域,表示本記錄的數(shù)據(jù);最后1字節(jié)為校驗和,計算本記錄中除了冒號和校驗字節(jié)之外的所有字節(jié)累加和(不計進位),校驗和=0x100-累加和。
分析以下例子:
:020000040031C9
:048000000071865134
:00000001FF
第1條記錄長度是0x02,偏移量為0x0000,類型域為0x04,代表該記錄為擴展線性地址記錄,數(shù)據(jù)為0x0031,校驗和為0xC9,從而可以計算基地址:0x0031 << 16 = 0x0031 0000,后面的數(shù)據(jù)都以此地址為基地址。
第2條記錄長度為0x04,偏移量為0x8000,類型域為0x00,代表該記錄為數(shù)據(jù)記錄,數(shù)據(jù)為0071 8651,校驗和為0x34,此時基地址為0x0031 0000,加上偏移量0x8000便可計算出此記錄數(shù)據(jù)的起始地址為0x0031 8000。
第3條記錄長度為0x00,偏移量為0x0000,類型域為0x01,代表該記錄為結尾記錄,表示文件的結尾。
系統(tǒng)開機時處于reset中斷,因此直接跳到中斷向量表中0x3F FFC0處的reset執(zhí)行,而這個地址下只放了一個指令,就是跳至初始化引導函數(shù)InitBoot,然后調用引導模式選擇函數(shù)SelectBootMode,用來檢測配置為輸入的GPIO84~GPIO87引腳的狀態(tài),從而確定為FLASH啟動,然后跳轉到入口地址0x33 FFF6,此地址下存放了程序最初執(zhí)行的第一條指令,通常是codestart,開始執(zhí)行應用程序。
TMS320F28335片上有256×16位嵌入式FLASH存儲器,其由8個32 KB×16位扇區(qū)組成,用戶可對其中任意一個扇區(qū)擦除、編程和校驗,而其他扇區(qū)不變[3]。
本方法在CCS5.5.0集成開發(fā)環(huán)境上開發(fā),最終體現(xiàn)在FLASH扇區(qū)上的是兩套程序:升級程序(用來實現(xiàn)串口通信接收HEX文件以及FLASH扇區(qū)的燒寫和驗證)和用戶程序(用來實現(xiàn)系統(tǒng)功能)。升級程序和用戶程序分別在兩個工程中設計,分別編譯,沒有直接聯(lián)系。本方法這樣分配FLASH空間:升級程序放置在FLASHG中,F(xiàn)LASH_API函數(shù)庫放置在FLASHH中,用戶代碼放置在FLASHE中,其他扇區(qū)作為備用盤,當用戶程序量大時可擴展使用,F(xiàn)LASH空間分配圖如圖2所示。
圖2 FLASH空間分配圖
4.1 升級程序設計
4.1.1 升級程序鏈接命令文件update.cmd
鏈接命令文件以后綴.cmd結尾,簡稱CMD文件,其作用就像倉庫的貨物擺放記錄一樣,為程序代碼和數(shù)據(jù)分配存儲空間[4]。update.cmd文件主要代碼為:
SECTIONS{
Flash28_API:{
-lFlash28335_API_V210.lib(.econst)
-lFlash28335_API_V210.lib(.text)
} LOAD = FLASHH,
RUN = RAML0,
LOAD_START(_Flash28_API_LoadStart),
LOAD_END(_Flash28_API_LoadEnd),
RUN_START(_Flash28_API_RunStart),
PAGE = 0
.USER_CODE : > FLASHE PAGE = 0
.cinit : > FLASHG PAGE = 0//升級程序
.pinit : > FLASHG PAGE = 0//升級程序
.text : > FLASHG PAGE = 0//升級程序
codestart : > BEGIN PAGE = 0
ramfuncs : LOAD = FLASHG,
//接收HEX數(shù)據(jù)并處理的函數(shù)
RUN = RAML0,
LOAD_START(_RamfuncsLoadStart),
LOAD_END(_RamfuncsLoadEnd),
RUN_START(_RamfuncsRunStart),
PAGE = 0
.econst : > FLASHG PAGE = 0//bootloader
.switch : > FLASHG PAGE = 0//bootloader
……
}
4.1.2 升級程序總體框架
升級程序總體流程圖如圖3所示,升級流程如下:
① InitSysCtrl()函數(shù)關閉看門狗,分別對鎖相環(huán)PLL和外設時鐘初始化,然后InitScibGpio()函數(shù)配置GPIO22為SCITXDB、GPIO23為SCIRXDB,SCIBInit()對SCI-B初始化,之后InitPieCtrl()函數(shù)初始化PIE控制寄存器為默認狀態(tài), InitPieVectTable()函數(shù)為PIE向量表中的所有中斷向量配置向量入口地址。
② IER = 0x0000為禁止CPU中斷,IFR=0x0000為清除所有CPU中斷標志。
③ Ram_Init()、InitFlash()、FlashAPI_Init()分別對RAM、FLASH和FlashAPI初始化,由于不能在其中一個FLASH扇區(qū)上執(zhí)行程序來操作其他扇區(qū),所以使用memcpy函數(shù)將對Flash的操作代碼拷貝到RAM中運行,最后解鎖CSM。
④ 若在2 s內(nèi)發(fā)送升級指令“u”,則進入UpdatePrj()函數(shù)進行程序的升級,此時在串口調試助手中發(fā)送HEX文件即可完成FLASH中用戶程序的更新,升級完成后運行User_Main()函數(shù),升級程序中有以下段的定義:
#pragma CODE_SECTION(User_Main,".USER_CODE");
在升級程序CMD文件中,USER_CODE段被放置在FLASHE扇區(qū),也就是說升級完成后執(zhí)行FLASHE扇區(qū)上的程序,巧妙的是用戶程序恰好被放置在此扇區(qū)上,所以升級完成后執(zhí)行的便是新的用戶程序。
⑤ 若在2 s內(nèi)未收到升級指令,則直接執(zhí)行FLASHE扇區(qū)上的用戶程序。
圖3 升級程序總體流程圖
4.1.3 升級函數(shù)UpdatePrj()
升級函數(shù)UpdatePrj()程序流程圖如圖4所示。
圖4 升級函數(shù)UpdatePrj()程序流程圖
程序升級時,首先調用API函數(shù)庫中的擦除函數(shù)Flash_Erase()擦除存放用戶程序的區(qū)域FLASHE,然后開始逐行讀取HEX文件,首行不同于其他行,因為在讀取首行時只需將前面的冒號讀取略掉,而其他行的十六進制數(shù)據(jù)前除了有冒號外,還存在換行和回車符,所以要區(qū)分處理,當讀取其他行時本方法采用不斷讀取判斷的方式,直到讀到的數(shù)據(jù)為冒號,這樣冒號的下一個十六進制數(shù)就代表了本行中數(shù)據(jù)的開始。之后調用SCIRx_Data()函數(shù)接收本行數(shù)據(jù),SCIRx_Process()函數(shù)得到目的地址和數(shù)據(jù),program()函數(shù)燒寫數(shù)據(jù)到FLASH扇區(qū)。
程序中定義結構體ALLSCIRXD,并聲明變量SCIRXD,結構體中的成員列表含義如表1所列,其中Uint16代表unsigned int,Uint32代表unsigned long。
表1 ALLSCIRXD結構體成員列表含義
(1) SCIRx_Data()函數(shù)
SCIRx_Data()函數(shù)程序流程圖如圖5所示。
圖5 SCIRx_Data()函數(shù)程序流程圖
此函數(shù)根據(jù)HEX文件的格式將某行記錄接收到數(shù)組SCIRXD.RcvData[50]中,首先接收前4個字節(jié)的數(shù)據(jù),然后可以根據(jù)第1個字節(jié)得到此行記錄的數(shù)據(jù)長度,之后再將剩余數(shù)據(jù)進行接收,最后計算校驗和是否與最后1個字節(jié)一致。SCIRx_Data()函數(shù)代碼如下:
void SCIRx_Data(void){
Uint16 temp1,temp2,temp3,temp4;
Uint16CheckSumValue;
Uint16 Count = 0;
SCIRXD.RecLength = 0;//初始化
memset(SCIRXD.RcvData, 0, sizeof(Uint16) * 50);
//數(shù)組RcvData清0
while(ScibRegs.SCIRXST.bit.RXRDY !=1) { }
for(Count=0; Count < 4; Count++){
while(ScibRegs.SCIRXST.bit.RXRDY !=1) { }
temp1 = (Uint16)ScibRegs.SCIRXBUF.bit.RXDT;
//如果HEX文件中發(fā)送的是C,則接收到的字節(jié)temp1=67,
//這為實際HEX記錄中一個字節(jié)的高位
temp2 = ASCIIconvert(temp1);
//將temp1=65通過函數(shù)ASCIIconvert轉換為temp2=0x0C
while(ScibRegs.SCIRXST.bit.RXRDY != 1) { }
temp3 = (Uint16)ScibRegs.SCIRXBUF.bit.RXDT;
temp4 = ASCIIconvert(temp3);
SCIRXD.RcvData[Count] = (temp2 << 4) | temp4;
//接收一個字節(jié)的低位并轉換,例如轉換之后為0x09,和0x0c
//合成為0xC9
}
SCIRXD.RecLength = SCIRXD.RcvData[0] + 5;
//接收到的某一行的長度
for(Count=4; Count < SCIRXD.RecLength; Count++){
while(ScibRegs.SCIRXST.bit.RXRDY != 1) { }
temp1 = (Uint16)ScibRegs.SCIRXBUF.bit.RXDT;
temp2 = ASCIIconvert(temp1);
while(ScibRegs.SCIRXST.bit.RXRDY != 1) { }
temp3 = (Uint16)ScibRegs.SCIRXBUF.bit.RXDT;
temp4 = ASCIIconvert(temp3);
SCIRXD.RcvData[Count] = (temp2 << 4) | temp4;
}
if (Count == SCIRXD.RecLength)
SCIRXD.RecOverFlag = 1;//接收一行完成
else
SCIRXD.RecOverFlag = 0;
//以下計算校驗和是否等于最后一個字節(jié)
CheckSumValue = CheckSum(SCIRXD.RcvData, SCIRXD.RecLength);
if(CheckSumValue != SCIRXD.RcvData[SCIRXD.RecLength - 1])//校驗失敗
RxValid = 0;
else //校驗通過
RxValid = 1;
}
Uint16ASCIIconvert(Uint16 temp){
if(( temp >= 48 ) && (temp <= 57))//0~9
temp = temp - 48;
else if(temp >= 65 && ( temp <= 90 ))//A~Z
temp = temp - 55;
return temp;
}
(2) SCIRx_Process()函數(shù)
SCIRx_Process()函數(shù)程序流程圖如圖6所示。
圖6 SCIRx_Process()函數(shù)程序流程圖
此函數(shù)根據(jù)本行記錄中的類型域分別執(zhí)行不同操作,類型域若為擴展線性地址記錄,則計算基地址;若為數(shù)據(jù)記錄,則計算偏移地址進而得到目的地址,并且將待存儲到FLASH中的數(shù)據(jù)放置于DataBuffer[50]數(shù)組中。SCIRx_Process()函數(shù)代碼如下:
void SCIRx_Process(void){
if(RxValid == 1){
RxValid = 0;
if (SCIRXD.RcvData[3] == 0x04){
//擴展線性地址記錄
SCIRXD.DestAddrHigh = (SCIRXD.RcvData[4] << 8) | (SCIRXD.RcvData[5]);
SCIRXD.DestAddrHigh = SCIRXD.DestAddrHigh << 16; //計算基地址
}
else if(SCIRXD.RcvData[3] == 0x00){ //數(shù)據(jù)記錄
Uint16 i;
SCIRXD.RawDataFlag = 1;
//兩個字節(jié)合在一起發(fā)
DataLength = SCIRXD.RcvData[0];//數(shù)據(jù)長度
DataLength = DataLength / 2;
//兩個數(shù)據(jù)字節(jié)合在一起存儲,數(shù)據(jù)長度減半
SCIRXD.DestAddrLow = SCIRXD.RcvData[1] << 8|SCIRXD.RcvData[2];//偏移地址
SCIRXD.DestAddr = SCIRXD.DestAddrHigh | (SCIRXD.DestAddrLow);
//目的地址=基地址+偏移地址
memset(DataBuffer, 0, sizeof(Uint16) * 50);
//數(shù)據(jù)清0
for(i = 0; i < DataLength; i++){
DataBuffer[i] = SCIRXD.RcvData[2 * i + 4] <<8 | SCIRXD.RcvData[2 * i + 5];
}
}
else if(SCIRXD.RcvData[3] == 0x01) //文件結尾記錄
return;
}
}
(3) program()函數(shù)
調用API函數(shù)庫中編程函數(shù)Flash_Program(),將DataBuffer[50]數(shù)組中長度為DataLength的數(shù)據(jù)燒寫到FLASH中以SCIRXD.DestAddr為起始地址的區(qū)域中,然后使用Flash_Verify函數(shù)驗證燒寫的正確性[5],函數(shù)代碼如下:
void program(void){//燒寫數(shù)據(jù)到FLASH
Uint16 status;
if(SCIRXD.RcvData[3] == 0x0000){//此行是數(shù)據(jù)
status = Flash_Program((Uint16 *) SCIRXD.DestAddr, (Uint16 *)DataBuffer, DataLength, &FlashStatus);
status = Flash_Verify((Uint16 *) SCIRXD.DestAddr, (Uint16 *)DataBuffer, DataLength, &FlashStatus);
}
}
4.2 用戶程序設計
用戶程序的設計主要包括CMD文件中程序段在FLASH扇區(qū)上的分配,目的是不與FLASH上的升級程序存放位置相沖突。用戶程序放置在FLASHE扇區(qū),如果后續(xù)用戶程序量大,可以將用戶程序擴展存放至FLASHB~FLASHE。
程序編寫完成后需要對用戶程序工程進行以下設置:右擊工程→Properties→Build→Steps→Apply Predefined Step→Create flash image: Intel-HEX,使得編譯工程時自動生成HEX文件,以供升級時使用。
4.2.1 用戶程序鏈接命令文件user.cmd
user.cmd文件中將codestart段分配在FLASHE扇區(qū)的起始地址0x31 8000,長度為:0x00 0010,然后將用戶代碼放在FLASHE扇區(qū)剩余空間中,用戶程序FLASH空間分配圖如圖7所示。
圖7 用戶程序FLASH空間分配圖
對應的主要cmd代碼為:
MEMORY{
PAGE 0: //程序空間
……
FLASHH : origin = 0x300000, length = 0x008000
FLASHG : origin = 0x308000, length = 0x008000
FLASHF : origin = 0x310000, length = 0x008000
FLASHE_BEGIN : origin = 0x318000, length = 0x000010/* codestart段*/
FLASHE_USERCODE :origin = 0x318010, length = 0x007FF0
FLASHD : origin =0x320000, length = 0x008000
FLASHC : origin =0x328000, length = 0x008000
FLASHA : origin = 0x338000, length = 0x007F80
……
}
SECTIONS{
.cinit : > FLASHE_USERCODE PAGE = 0
.pinit : > FLASHE_USERCODE PAGE = 0
.text : > FLASHE_USERCODE PAGE = 0
codestart : > FLASHE_BEGIN PAGE = 0
ramfuncs : LOAD = FLASHE_USERCODE,
RUN = RAML0,
LOAD_START(_RamfuncsLoadStart),
LOAD_END(_RamfuncsLoadEnd),
RUN_START(_RamfuncsRunStart),
LOAD_SIZE(_RamfuncsLoadSize),
PAGE = 0
.econst : > FLASHE_USERCODE PAGE = 0
.switch : > FLASHE_USERCODE PAGE = 0
……
}
4.2.2 user.hex文件
將用戶程序設計為每隔1 s向串口打印“USER CODE!”,然后編譯生成user. hex文件。打開此文件可以看到所有數(shù)據(jù)都被分配到以0x0031 8000為起始地址下(即FLASHE扇區(qū)),user. hex文件部分內(nèi)容如圖8所示。
圖8 user. hex文件部分內(nèi)容
以TMS320F28335最小系統(tǒng)板為測試平臺,通過SEED XDS510PLUS型號仿真器連接開發(fā)板與PC機,然后將開發(fā)板上電,升級程序的User_Main()函數(shù)設計為每隔1 s向串口打印“boot code!”,使用CCS5.5.0編譯升級程序,接著將程序通過仿真器燒寫到FLASH中,此時可以在串口上看到不斷打印出來的“boot code!”。將開發(fā)板斷電,仿真器斷開,然后通過USB轉TTL模塊連接PC機與開發(fā)板的串行通信接口SCI-B,模塊的RXD與SCITXDB相接,TXD與SCIRXDB相接,在PC機上打開串口調試助手,然后打開相應串口,設置波特率為9 600 bps,8位數(shù)據(jù)位,無校驗位,1個停止位。
再次給開發(fā)板上電,2 s內(nèi)在串口調試助手中向開發(fā)板發(fā)送升級命令“u”,此時進入升級狀態(tài),芯片等待串口調試程序發(fā)送HEX文件,之后選擇發(fā)送user.hex,此時便將用戶程序燒寫到FLASHE扇區(qū)中,升級完成后從FLASHE起始地址開始運行,即從用戶程序的codestart執(zhí)行,進而執(zhí)行用戶程序,可以看到在串口調試助手上每隔1 s打印出“user code!”。
[1] 羅秋鳳,葉慧,李勇,等.DSP28335嵌入式系統(tǒng)的SCI在線編程方法實現(xiàn)[J].河北科技大學學報,2013(4):318-324.
[2] 徐魯花,董小衛(wèi),張浩,等.嵌入式開發(fā)系統(tǒng)編程文件格式解析[J].單片機與嵌入式系統(tǒng)應用,2011(12):4-7.
[3] 張卿杰,徐友,左楠,等.手把手教你學DSP:基于TMS320F28335[M].北京:北京航空航天大學出版社,2015:61-67.
[4] 顧衛(wèi)鋼.手把手教你學DSP:基于TMS320X281x[M].北京:北京航空航天大學出版社,2011:135-144.
[5] 陶維青,任謙.通過串口通訊實現(xiàn)TMS320F2812的軟件更新[J].合肥工業(yè)大學學報:自然科學版,2008(4):569-572.
[6] 金鑫,史浩山,謝福平.通過無線串口實現(xiàn)TMS320F2812程序更新[J].微型電腦應用,2011(11):44-46,69.
[7] 李靜,張樹團.TMS320F2812片內(nèi)Flash在線燒寫技術研究[J].國外電子元器件,2008(10):37-38,40.
高世皓(碩士研究生),主要研究領域為物聯(lián)網(wǎng)、嵌入式系統(tǒng)開發(fā)。
Software Updating Method of TMS320F28335 Using HEX File
Gao Shihao
(Key Laboratory of Universal Wireless Communications,Ministry of Education,Beijing University of Posts and Telecommunications,Beijing 100876,China)
In the paper,a new method of FLASH updating by transmitting HEX file to TMS320F28335 is proposed.To realize the function of updating the application program by sending HEX file to the TMS320F28335,it is configured as a FLASH boot mode and the program used for updating is downloaded into the FLASH.The implementation principle and the development process of this method are introduced,and the software flowchart and the critical code are given.The experiment results indicate that the new method is simple to operate and has high reliability,so this method improves the working efficiency.
TMS320F28335;FLASH boot;HEX file;software update
TP311.52
A
?士然
2017-02-27)