本帖最后由 梁子 于 2015-5-5 09:45 编辑
网页看着不方便,可以下载PDF,如果文章有问题,可以跟帖或私信!
序:关于系统的讲解飞思卡尔升级的文章,比较少,也比较零碎,对于新手来说,可能整了半天还一头雾水,还不知道错在哪里,几年前我就是这样,用了一周多的时间,终于搞定了,现在在坛子里还有人问我一些关于升级的问题,时间有限,没办法及时答复,甚至选择了不回复。 为了坛子,为了新手,为了抛砖引玉,这里抽时间写一下本例程。
一、关于升级 本文以MCF51JM128芯片,对飞思卡尔(专家系统)实现基于串口的升级方法(Boot数据包大小为1K字节,串口配置96N81)。 也是最近要做的东西,因此边做边整理,以供大家学习参考,少走弯路,尽快掌握Boot程序编程方法和主程序的注意事项。 因为飞思卡尔芯片的继承性比较好,因此本文讲述的内容,差不多可以应用、移植到飞思卡尔全系列芯片。 同时大家不要认为基于串口升级的程序,不能作为基于CAN等方式的升级程序,基于串口只是其中一条路,完全可以在此基础上做成基于CAN、SPI、I2C、甚至是1BUS的Boot程序。 二、硬件平台 MCF51JM128VLD,串口1(串口2也可以,本文以串口1为例); 三、软件平台 CodeWarriorfor ColdFire V6.3(V6.2也可,V10.x也可) 四、S19文件格式详解(原文来自网络,这里为方便查看,进行了一定的整理) 做为下位机程序员来说,如果你有一个好的上位机软件,对S19文件则不需要过多的理解和掌握,上位机软件会先对S19文件进行整理,再发给下位机,上、下位机两人搞明白各自的工作即可,但你将永远都是普通程序员了,呵呵; 对于S19文件,可以先略过,当看完下一节时,再回头看这段,印象会更深刻!也可现用现看,记忆力好的就另说了。 S-record格式文件是Freescale CodeWarrior编译器生成的后缀名为.S19的程序文件,是一段直接烧写进MCU的ASCII码,英文全称问Motorolaformat for EEPROM programming。 上面“是一段直接烧写进MCU的ASCII码”,这样说可能会误导初学者,因为并不是直接将ASCII码写进MCU,写进MCU前还要对S19文件进行一个解析,把有用的机器代码分解出来,然后再进行写入,写入的也不是ASCII而是二进制码。 1、格式定义及含义 S-record每行最大是78个字节,156个字符 S-record 格式: 1)、Type(类型):2个字符。用来描述记录的类型(S0,S1,S2,S3,S5,S7,S8,S9)。 S0 Record:记录类型是“S0” (0x5330)。地址场没有被用,用零置位(0x0000)。此行表示程序的开始,不需烧入memory。 数据场中的信息被划分为以下四个子域: Name(名称):20个字符,用来编码单元名称; Ver(版本):2个字符,用来编码版本号; Rev(修订版本):2个字符,用来编码修订版本号; Description(描述):0-36个字符,用来编码文本注释; S1 Record:记录类型是“S1” (0x5331)。地址场由2个字节地址来说明。数据场由可载入的数据组成。 S2 Record:记录类型是“S2” (0x5332)。地址场由3个字节地址来说明。数据场由可载入的数据组成。 S3 Record:记录类型是“S3” (0x5333)。地址场由4个字节地址来说明。数据场由可载入的数据组成。 S5 Record:记录类型是“S5” (0x5335)。地址场由2字节的值说明,包含了先前传输的S1、S2、S3记录的计数。没有数据场。 S7 Record:记录类型是“S7” (0x5337)。地址场由4字节的地址说明,包含了开始执行地址。没有数据场。此行表示程序的结束,不需烧入memory。 S8 Record:记录类型是“S8” (0x5338)。地址场由3字节的地址说明,包含了开始执行地址。没有数据场。此行表示程序的结束,不需烧入memory。 S9 Record:记录类型是“S9” (0x5339)。地址场由2字节的地址说明,包含了开始执行地址。没有数据场。此行表示程序的结束,不需烧入memory。 根据不同的描述信息,在以上三种不同的结束行中选择一种使用。
2)、Count(计数):2个字符。 用来组成和说明了一个16进制的值,显示了在记录中剩余成对字符的计数。 3)、Address(地址):4或6或8个字节。用来组成和说明了一个16进制的值,显示了数据应该装载的地址, 这部分的长度取决于载入地址的字节数。2个字节的地址占用4个字符,3个字节的地址占用6个字符,4个字节的地址占用8个字符。 4)、Data(数据):0—64字符。用来组成和说明一个代表了内存载入数据或者描述信息的16进制的值。 5)、Checksum(校验和):2个字符。这些字符当被配对并换算成16进制数据的时候形成了一个最低有效字符节,该字符节用来表达作为补充数据,地址和数据库的字符对所代表的(字节的)补码的byte总和。即计数值、地址场和数据场的若干字符以两个字符为一对,将它们相加求和,和的溢出部分不计,只保留最低两位字符NN,checksum =0xFF-0xNN。 2、EXAMPLE 1)、 ExampleI S19文件首行:S021000036384B50524F47202020313143524541544544204259204541535936384B6D 首行翻译信息: S0 0000 6 8 K P R O G 1 1C R E A T E D B Y E A S Y 6 8 K 色块图例:module name version number revision number checksum Checksum的算法: 0x21+0x00+0x00+0x36+0x38+0x4B+0x50+0x52+0x4F+0x47+0x20+0x20+0x20+0x31+0x31+0x43+0x52+0x45+0x41+0x54+0x45+0x44+0x20+0x42+0x59+0x20+0x45+0x41+0x53+0x59+0x36+0x38+0x4B=0x792 checksum=0xFF-0x92=6D 注意:EASy68K总是用S8 record作为结束行。 2)、ExampleII S123C000CF1400790011CC09395B105A124A8046304A8000300001C01BCB73140007340027 色块图例:type count address data checksum 3)、ExampleIII S224308000C61E877C1516C6197B151ACE04C07E15187A153EC74A90F9301D026A01C6017BF0 色块图例:type count address data checksum
五、Boot程序部分先从程序流程图开始吧! 图一、Boot程序流程图 1、串口通讯部分: 在串口函数中要解决的事情有:数据接收、指令回复、数据校验、数据解析。 一般做Boot这类程序都要用一个握手包,我使用的是“版本号询问”,大家可以根据自己的情况使用“开始升级”,或者自己定义一个握手包。 也可以对软件自行“加花”,我自己对“增加一些可有可无,有则更好,没有也可的程序”叫“加花”,可增加“波特率调整指令及代码”,提高一点软件升级的通讯速度。 2、FLASH的擦写操作: 擦Flash: for(i=0;i<120;i++) { Flag =IFsh1_EraseSector((IFsh1_TAddress)(i * 1024 + 0x2000));//(主程序空间范围8K-128K) } 我使用了专家系统下的IFsh1模块,因此擦、写Flash就这几行代码,主程序空间范围8K-128K,因为代码空间充裕,128K的零头都留给了Boot使用,大家可以自行根据自己的Boot代码的大小,灵活调整。 写Flash: if(FlashAddr > 0x1FFF)//写地址>0x1FFF(8K以后才是主程序) { Flag =IFsh1_NonDestructiveUnsecureWrite((IFsh1_TAddress)&Uart_rBuffer[10],FlashAddr, Packet.DataLong);//待写入数据的起始地址,Flash的写入起始起址,数据长度(字节) } 擦写操作都有了,Boot程序还没完,还有一个很重要的事情,Boot空间分配! 3、Boot程序的Flash空间分配及程序入口代码:本文的重点了!
工程默认空间分配
图二、专家系统建立工程后,默认的Flash空间配置 我在专家系统下对Flash空间进行如下的分配,如图三:
Boot空间分配
图三、为Boot程序更改FLASH空间分配 ROM/RAM segments中的Segment0从430开始,长度1BD0,因此Flash的前8K都属于Boot程序,所以在升级时擦Flash时就擦8K以后的空间。 有人可能眼力好,看到了ROM/RAM segments中的Segment0从410变成了430,多出的这点空间用于存放了Boot和主程序的入口地址。用于实现Boot程序和主程序的相互跳转。 并在Boot程序中增加下如代码 //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx //此xxx分隔线中间的代码,不得更改. /*----------------------------------------------------------- / 函数名称 : void (* const_BootEntry2[])()@(BOOT_ENTRY_ADDRESS+4) / 函数功能 : Boot段指针入口 / 说 明 : 目的:主程序工作状态下,可重新进入Boot模式,进行必要的升级等工作. /-----------------------------------------------------------*/ extern asm void _startup(void); #define BOOT_ENTRY_ADDRESS 0x00000410 //Boot程序入口 #define MAIN_ENTRY_ADDRESS 0x00002410 // 主 程序入口(主程序入口为什么是2410和Boot程序入口为什么410一样,这里做了一个间接跳转;也可以查看中断相量表,写成直接跳转,这里推荐间接跳转) //下面这两个小函数编译后合到一起的机器代码的意思是:“一次空操作后,立即跳到程序的启动代码处(_startup)” const byte _BootEntry[] @ GMBOOT_ENTRY_ADDRESS= { 0x4E,0x71, 0x4E,0xF9 //asm NOP(0x4E71), asmJMP(0x4EF9) }; void (* const _BootEntry2[])()@(GMBOOT_ENTRY_ADDRESS+4)= { _startup }; /*----------------------------------------------------------- / 函数名称 : void EnterMain(void) / 函数功能 : 主程序入口 / 说 明 :在Boot程序适当位置写出asm (JMP MAIN_ENTRY_ADDRESS);即可,为本文说明方便,单独拿出来写个函数. /-----------------------------------------------------------*/ void EnterMain(void) { asm (JMPMAIN_ENTRY_ADDRESS);//跳入主程序入口 } //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 至此,Boot程序已经完成了(串口、Flash的擦写、Flash空间分配及程序入口代码)。 因为串口比较简单没有详细说,这里补充的是,数据包一定要有一个合理的格式(包头、包尾、长度、校验)。
六、主程序部分在讲Boot的时候,特意用倒序的方法说的;到了主程序,就得先从空间分配上开始了(讲是这么讲,当你自己做程序的时候,就随意了,要活学活用); 1、主程序空间分配:
工程默认空间分配-1
图四、专家系统建立工程后,默认的Flash空间配置 图二、图四是一样的,我都是用专家系统建立的工程、做程序,从接触飞思卡尔开始,一直在用专家系统,仅发现几个小问题,基本都可以不改专家系统生成的底层代码就可以搞定。 将主程序工程默认的空间进行更改,如图五:
主程序空间分配
图五、主程序空间分配 ROM/RAMsegments: Segment0:主程序代码空间 Segment1:主程序RAM空间 Segment2:中断向量的代码段 Segment3:中断向量的RAM段 中断向量的代码段中的内容,要在程序初始化之前,复制到RAM中,不然程序没有中断向量表,是不会正常执行的。或许有更好的办法(比如更改中断向量的指针),由于工作关系,没有大块时间细抠,希望本文抛砖引玉,能找到更好的方法。 空间分配完成后,在进入主程序的第一件事儿就是中断向量表的复制,代码如下: dword *pdst,*psrc; word i;
asm (move.l #0x00800000,d0); asm (movec d0,vbr);
pdst=(dword*)0x00800000; psrc=(dword*)0x00003000;
for(i=0;i<=0x6E;i++)//0x6E中断向量数量(双字) { *pdst++ = *psrc++; } //此行以后,就是PE_low_level_init();
2、主程序的入口代码和Reboot功能现在主程序代码,可以在Boot代码之后运行了;为了主程序具有Reboot功能,为了Boot程序能跳转到主程序,还需要增加如下代码; //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx // 此xxx分隔线中间的代码,不得更改. /*----------------------------------------------------------- / 说 明 : 1. 主程序入口地址 / 2.主程序状态下,重新进入Boot模式,进行必要的升级等工作. /-----------------------------------------------------------*/ extern asm void _startup(void);
#define BOOT_ENTRY_ADDRESS 0x00000410 //Boot程序入口 #define MAIN_ENTRY_ADDRESS 0x00002410 // 主 程序入口
const byte _UserEntry[] @ MAIN_ENTRY_ADDRESS= { 0x4E, 0x71, 0x4E,0xF9 //asm NOP(0x4E71), asmJMP(0x4EF9) }; void (* const_UserEntry2[])()@(MAIN_ENTRY_ADDRESS+4)= { _startup }; /*----------------------------------------------------------- / 函数名称 : void EnterBoot (void) / 函数功能 : 主程序入口 / 说 明 :在Boot程序适当位置写出asm (JMP BOOT_ENTRY_ADDRESS);即可,为本文说明方便,单独拿出来写个函数. /-----------------------------------------------------------*/ void EnterBoot(void) { asm (JMP BOOT_ENTRY_ADDRESS);//跳入主程序入口 } //xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 七、其他本文用了半天的时间写的,有可能有一些错误、或者不当之处,希望各位提出指正!
作者:梁子
2015/5/5
Bug1:20150505,图五错误,更换图片(重新上传PDF文档).
|