利用HEX文件实现TMS320F28335的程序升级方法

2017-07-31 21:57高世皓
单片机与嵌入式系统应用 2017年7期
关键词:用户程序扇区字节

高世皓

(北京邮电大学 泛网无线通信教育部重点实验室,北京 100876)

利用HEX文件实现TMS320F28335的程序升级方法

高世皓

(北京邮电大学 泛网无线通信教育部重点实验室,北京 100876)

提出了一种通过发送HEX文件到TMS320F28335进行程序升级的新方法。配置TMS320F28335为FLASH启动模式,通过预烧写升级程序到FLASH中,可实现在串口发送HEX文件升级用户程序的功能。详细介绍了该方法实现原理和开发过程,并且给出软件流程图和关键代码。实验证明,该方法操作方便,可靠性高,大大提高了效率。

TMS320F28335;FLASH启动;HEX文件;程序升级

引 言

伴随着科技的进步和用户需求的不断更新,在实际应用中需要对已安装的DSP设备进行程序更新升级。在开发阶段,更新程序的方法是采用CCS集成开发环境进行编译,然后通过仿真器连接设备进行升级,但在实际复杂的工作环境中,取下设备连接仿真器会降低效率,而且有些情况下很难实现,考虑到升级程序的需要,可以通过串口接收数据来实现程序的升级[1]。

利用串口更新DSP程序的方法有很多,本文创新地提出使用HEX文件实现TMS320F28335升级程序的方法。当DSP中已经预烧写了升级程序时,升级时只需PC机上具有用户程序的HEX文件和串口调试助手即可完成程序的更新,不需要使用专业的CCS软件和仿真器。

1 方法介绍

本方法是在TMS320F28335处于FLASH启动模式下开发的,首先通过仿真器向DSP的FLASH扇区中烧写升级程序,此程序不是用户程序,当需要升级程序时,将DSP复位后发送升级命令,此时DSP进入升级状态,然后通过串口发送HEX文件到DSP,根据HEX文件内容烧写到相应的FLASH扇区完成升级,之后DSP开始运行新的用户程序。硬件由TMS320F28335最小系统板和USB转TTL模块组成,使用DSP的SCI-B进行串口通信,引导模式设置为FLASH引导,即GPIO84~GPIO87引脚状态配置为高电平,硬件组成框图如图1所示。

图1 硬件组成框图

2 HEX文件格式解析

HEX文件以行为单位,每一行为一条HEX记录,HEX每行以冒号开头,由一个回车和一个换行结束,内容以十六进制数的ASCII码形式显示,其格式如下[2]:

“:”表示开始标志;第1字节表示长度域,代表这条记录中数据的字节数;第2、3字节表示地址域,代表相对于基地址的偏移地址;第4字节表示类型域,0x00表示数据记录,0x01表示结尾记录,用来标识文件结束,0x04表示扩展线性地址记录;第5字节——倒数第2字节为数据域,表示本记录的数据;最后1字节为校验和,计算本记录中除了冒号和校验字节之外的所有字节累加和(不计进位),校验和=0x100-累加和。

分析以下例子:

:020000040031C9

:048000000071865134

:00000001FF

第1条记录长度是0x02,偏移量为0x0000,类型域为0x04,代表该记录为扩展线性地址记录,数据为0x0031,校验和为0xC9,从而可以计算基地址:0x0031 << 16 = 0x0031 0000,后面的数据都以此地址为基地址。

第2条记录长度为0x04,偏移量为0x8000,类型域为0x00,代表该记录为数据记录,数据为0071 8651,校验和为0x34,此时基地址为0x0031 0000,加上偏移量0x8000便可计算出此记录数据的起始地址为0x0031 8000。

第3条记录长度为0x00,偏移量为0x0000,类型域为0x01,代表该记录为结尾记录,表示文件的结尾。

3 FLASH引导启动流程

系统开机时处于reset中断,因此直接跳到中断向量表中0x3F FFC0处的reset执行,而这个地址下只放了一个指令,就是跳至初始化引导函数InitBoot,然后调用引导模式选择函数SelectBootMode,用来检测配置为输入的GPIO84~GPIO87引脚的状态,从而确定为FLASH启动,然后跳转到入口地址0x33 FFF6,此地址下存放了程序最初执行的第一条指令,通常是codestart,开始执行应用程序。

4 软件设计

TMS320F28335片上有256×16位嵌入式FLASH存储器,其由8个32 KB×16位扇区组成,用户可对其中任意一个扇区擦除、编程和校验,而其他扇区不变[3]。

本方法在CCS5.5.0集成开发环境上开发,最终体现在FLASH扇区上的是两套程序:升级程序(用来实现串口通信接收HEX文件以及FLASH扇区的烧写和验证)和用户程序(用来实现系统功能)。升级程序和用户程序分别在两个工程中设计,分别编译,没有直接联系。本方法这样分配FLASH空间:升级程序放置在FLASHG中,FLASH_API函数库放置在FLASHH中,用户代码放置在FLASHE中,其他扇区作为备用盘,当用户程序量大时可扩展使用,FLASH空间分配图如图2所示。

图2 FLASH空间分配图

4.1 升级程序设计

4.1.1 升级程序链接命令文件update.cmd

链接命令文件以后缀.cmd结尾,简称CMD文件,其作用就像仓库的货物摆放记录一样,为程序代码和数据分配存储空间[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数据并处理的函数

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()函数关闭看门狗,分别对锁相环PLL和外设时钟初始化,然后InitScibGpio()函数配置GPIO22为SCITXDB、GPIO23为SCIRXDB,SCIBInit()对SCI-B初始化,之后InitPieCtrl()函数初始化PIE控制寄存器为默认状态, InitPieVectTable()函数为PIE向量表中的所有中断向量配置向量入口地址。

② IER = 0x0000为禁止CPU中断,IFR=0x0000为清除所有CPU中断标志。

③ Ram_Init()、InitFlash()、FlashAPI_Init()分别对RAM、FLASH和FlashAPI初始化,由于不能在其中一个FLASH扇区上执行程序来操作其他扇区,所以使用memcpy函数将对Flash的操作代码拷贝到RAM中运行,最后解锁CSM。

④ 若在2 s内发送升级指令“u”,则进入UpdatePrj()函数进行程序的升级,此时在串口调试助手中发送HEX文件即可完成FLASH中用户程序的更新,升级完成后运行User_Main()函数,升级程序中有以下段的定义:

#pragma CODE_SECTION(User_Main,".USER_CODE");

在升级程序CMD文件中,USER_CODE段被放置在FLASHE扇区,也就是说升级完成后执行FLASHE扇区上的程序,巧妙的是用户程序恰好被放置在此扇区上,所以升级完成后执行的便是新的用户程序。

⑤ 若在2 s内未收到升级指令,则直接执行FLASHE扇区上的用户程序。

图3 升级程序总体流程图

4.1.3 升级函数UpdatePrj()

升级函数UpdatePrj()程序流程图如图4所示。

图4 升级函数UpdatePrj()程序流程图

程序升级时,首先调用API函数库中的擦除函数Flash_Erase()擦除存放用户程序的区域FLASHE,然后开始逐行读取HEX文件,首行不同于其他行,因为在读取首行时只需将前面的冒号读取略掉,而其他行的十六进制数据前除了有冒号外,还存在换行和回车符,所以要区分处理,当读取其他行时本方法采用不断读取判断的方式,直到读到的数据为冒号,这样冒号的下一个十六进制数就代表了本行中数据的开始。之后调用SCIRx_Data()函数接收本行数据,SCIRx_Process()函数得到目的地址和数据,program()函数烧写数据到FLASH扇区。

程序中定义结构体ALLSCIRXD,并声明变量SCIRXD,结构体中的成员列表含义如表1所列,其中Uint16代表unsigned int,Uint32代表unsigned long。

表1 ALLSCIRXD结构体成员列表含义

(1) SCIRx_Data()函数

SCIRx_Data()函数程序流程图如图5所示。

图5 SCIRx_Data()函数程序流程图

此函数根据HEX文件的格式将某行记录接收到数组SCIRXD.RcvData[50]中,首先接收前4个字节的数据,然后可以根据第1个字节得到此行记录的数据长度,之后再将剩余数据进行接收,最后计算校验和是否与最后1个字节一致。SCIRx_Data()函数代码如下:

void SCIRx_Data(void){

Uint16 temp1,temp2,temp3,temp4;

Uint16CheckSumValue;

Uint16 Count = 0;

SCIRXD.RecLength = 0;//初始化

memset(SCIRXD.RcvData, 0, sizeof(Uint16) * 50);

//数组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文件中发送的是C,则接收到的字节temp1=67,

//这为实际HEX记录中一个字节的高位

temp2 = ASCIIconvert(temp1);

//将temp1=65通过函数ASCIIconvert转换为temp2=0x0C

while(ScibRegs.SCIRXST.bit.RXRDY != 1) { }

temp3 = (Uint16)ScibRegs.SCIRXBUF.bit.RXDT;

temp4 = ASCIIconvert(temp3);

SCIRXD.RcvData[Count] = (temp2 << 4) | temp4;

//接收一个字节的低位并转换,例如转换之后为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;

//以下计算校验和是否等于最后一个字节

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()函数

SCIRx_Process()函数程序流程图如图6所示。

图6 SCIRx_Process()函数程序流程图

此函数根据本行记录中的类型域分别执行不同操作,类型域若为扩展线性地址记录,则计算基地址;若为数据记录,则计算偏移地址进而得到目的地址,并且将待存储到FLASH中的数据放置于DataBuffer[50]数组中。SCIRx_Process()函数代码如下:

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){ //数据记录

Uint16 i;

SCIRXD.RawDataFlag = 1;

//两个字节合在一起发

DataLength = SCIRXD.RcvData[0];//数据长度

DataLength = DataLength / 2;

//两个数据字节合在一起存储,数据长度减半

SCIRXD.DestAddrLow = SCIRXD.RcvData[1] << 8|SCIRXD.RcvData[2];//偏移地址

SCIRXD.DestAddr = SCIRXD.DestAddrHigh | (SCIRXD.DestAddrLow);

//目的地址=基地址+偏移地址

memset(DataBuffer, 0, sizeof(Uint16) * 50);

//数据清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()函数

调用API函数库中编程函数Flash_Program(),将DataBuffer[50]数组中长度为DataLength的数据烧写到FLASH中以SCIRXD.DestAddr为起始地址的区域中,然后使用Flash_Verify函数验证烧写的正确性[5],函数代码如下:

void program(void){//烧写数据到FLASH

Uint16 status;

if(SCIRXD.RcvData[3] == 0x0000){//此行是数据

status = Flash_Program((Uint16 *) SCIRXD.DestAddr, (Uint16 *)DataBuffer, DataLength, &FlashStatus);

status = Flash_Verify((Uint16 *) SCIRXD.DestAddr, (Uint16 *)DataBuffer, DataLength, &FlashStatus);

}

}

4.2 用户程序设计

用户程序的设计主要包括CMD文件中程序段在FLASH扇区上的分配,目的是不与FLASH上的升级程序存放位置相冲突。用户程序放置在FLASHE扇区,如果后续用户程序量大,可以将用户程序扩展存放至FLASHB~FLASHE。

程序编写完成后需要对用户程序工程进行以下设置:右击工程→Properties→Build→Steps→Apply Predefined Step→Create flash image: Intel-HEX,使得编译工程时自动生成HEX文件,以供升级时使用。

4.2.1 用户程序链接命令文件user.cmd

user.cmd文件中将codestart段分配在FLASHE扇区的起始地址0x31 8000,长度为:0x00 0010,然后将用户代码放在FLASHE扇区剩余空间中,用户程序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文件。打开此文件可以看到所有数据都被分配到以0x0031 8000为起始地址下(即FLASHE扇区),user. hex文件部分内容如图8所示。

图8 user. hex文件部分内容

5 实验验证

以TMS320F28335最小系统板为测试平台,通过SEED XDS510PLUS型号仿真器连接开发板与PC机,然后将开发板上电,升级程序的User_Main()函数设计为每隔1 s向串口打印“boot code!”,使用CCS5.5.0编译升级程序,接着将程序通过仿真器烧写到FLASH中,此时可以在串口上看到不断打印出来的“boot code!”。将开发板断电,仿真器断开,然后通过USB转TTL模块连接PC机与开发板的串行通信接口SCI-B,模块的RXD与SCITXDB相接,TXD与SCIRXDB相接,在PC机上打开串口调试助手,然后打开相应串口,设置波特率为9 600 bps,8位数据位,无校验位,1个停止位。

再次给开发板上电,2 s内在串口调试助手中向开发板发送升级命令“u”,此时进入升级状态,芯片等待串口调试程序发送HEX文件,之后选择发送user.hex,此时便将用户程序烧写到FLASHE扇区中,升级完成后从FLASHE起始地址开始运行,即从用户程序的codestart执行,进而执行用户程序,可以看到在串口调试助手上每隔1 s打印出“user code!”。

结 语

[1] 罗秋凤,叶慧,李勇,等.DSP28335嵌入式系统的SCI在线编程方法实现[J].河北科技大学学报,2013(4):318-324.

[2] 徐鲁花,董小卫,张浩,等.嵌入式开发系统编程文件格式解析[J].单片机与嵌入式系统应用,2011(12):4-7.

[3] 张卿杰,徐友,左楠,等.手把手教你学DSP:基于TMS320F28335[M].北京:北京航空航天大学出版社,2015:61-67.

[4] 顾卫钢.手把手教你学DSP:基于TMS320X281x[M].北京:北京航空航天大学出版社,2011:135-144.

[5] 陶维青,任谦.通过串口通讯实现TMS320F2812的软件更新[J].合肥工业大学学报:自然科学版,2008(4):569-572.

[6] 金鑫,史浩山,谢福平.通过无线串口实现TMS320F2812程序更新[J].微型电脑应用,2011(11):44-46,69.

[7] 李静,张树团.TMS320F2812片内Flash在线烧写技术研究[J].国外电子元器件,2008(10):37-38,40.

高世皓(硕士研究生),主要研究领域为物联网、嵌入式系统开发。

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)

猜你喜欢
用户程序扇区字节
分阶段调整增加扇区通行能力策略
No.8 字节跳动将推出独立出口电商APP
变速箱控制系统Bootloader设计与实现
No.10 “字节跳动手机”要来了?
U盘故障排除经验谈
简谈MC7字节码
基于CCS5.5的TMS320C6000程序Flash上电自举设计及三种固化方法对比
基于贝叶斯估计的短时空域扇区交通流量预测
重建分区表与FAT32_DBR研究与实现
C8051F410单片机BootLoader的实现