Microchip公司16位单片机dsPIC33E/PIC24E系列bootloader的开发(2)
上一篇“Microchip公司16位单片机dsPIC33/PIC24E系列bootloader的开发(1)”文章中提到了bootloader一些基本知识以及不同设计架构之间的比较,最终选择了把bootloader置于闪存空间尾部的方案。这篇文章我们要讨论一下bootloader的工作流程,让你了解一下整套bootloader所涉及的方方面面。
一般来说,设计完你的单片机程序以后,编译器会将你的C/C++或者汇编代码编译成二进制代码,通常保存在“.hex”结尾的文件里。这个hex文 件遵循Intel Hex32格式,我会在稍后细谈。然后你需要打开编程器的PC端软件,读入hex文件,确保编程器(Microchip的话是ICD2或者ICD3)已正 确连好,然后二进制代码就通过芯片上的PGC/PGD口写入单片机的闪存里。在这个流程里,Microchip已经帮我们做好了其中所有的工具链,分别 是:编译器(MPLAB IDE X)- 编程器PC端软件(MPLAB IPE X)- 编程器(ICD2或者ICD3) – 单片机接收端(PGC/PGD口)。在bootloader的流程了,你就得自己来开发其中的一些组件了,比如你可以继续使用MPLAB IDE X做为编译器,但是你得有自己开发的INTEL HEX32解析器,接下来就是你自己开发的硬件/软件负责将固件传到单片机上,有很多途径,可以是USB、UART、SPI、I2C甚至是SD卡,然后你 还要开发单片机上的接收/烧制代码,也就是真正的bootloader了。可见,所谓开发bootloader,其实是差不多整一个生态系统。
- 理解INTEL Hex32标准
编译完单片机程序以后,编译器会生成一个hex后缀名的单片机固件。如果你用16进制文本编辑器打开这个文件,你会看到一行一行的16进制码。所有的这些16进制码都服从下面的格式:
[sourcecode language=”text”]
:BBAAAATTHHHH… …HHHHCC
[/sourcecode]一个字母表示一个16进制数字(即一个字节byte),其中:
- [:] 每一行开始比有一个分号,没有列外
- [BB] HHHH… …HHHH中字节的长度
- [AAAA] 该行数据在芯片闪存中的起始地址
- [TT] 该行数据类型,其中
- 00:数据
- 01:结尾标志
- 02:Extended Segment Address Record,片地址偏移
- 03:片地址偏移起始标志
- 04:Extended Linear Address Record,块地址偏移
- 05:块地址偏移起始标志
- [HHHH… …HHHH] 数据部分
- [CC] CRC循环冗余校验码
详细的INTEL HEX32标准细节就不在这里解释了,网上很容易找到。Mirochip的DS70619B文档:dsPIC33E/PIC24E闪存编程规范就是一个很 好的参考。接下来我会详细讲述一下INTEL HEX32在dsPIC33/PIC24E上的具体实现,总结一下散落在多个文档中的一些零星要点。让我们从一个具体的例子开始来加深你的理解:
[sourcecode language=”text”]
:020000040108EA
:0200000212FFBD
:0401000090FFAA5502
:00000001FF
[/sourcecode]- 块地址偏移0108
- 片地址偏移12FF
- 原起始地址0100
- 计算真实地址:0108左移16位变成0108 0000,12FF左移4位变成0001 2FF0,原起始地址0000 0100,加上偏移量后得到真实地址为:0109 30F0
- 最终可以将这段hex解释为[sourcecode language=”text”]
地址 数据
010930F0 90
010930F1 FF
010930F2 AA
010930F3 55
[/sourcecode]值得注意的是,一旦含有“02”或者“04”数据类型的16进制代码行出现,则之后的所有行的数据地址都需要偏移,直至出现新一个“02”或者“04”,那么就需要重新计算新的偏移量。
- INTEL HEX32在dsPIC33E/PIC24E上的具体实现
Microchip在INTEL HEX32的基础上增加了一些自己的规则。要想正确地解析dsPIC33E/PIC24E的hex十六进制文件,就必须深入Microchip为数众多的文档才能窥探到庐山真面目,下面就是一些汇总:- dsPIC33E/PIC24E的每个指令(instruction)占用3个字节(byte)或者24位(bit),与此相对应的,用户闪存程 序存储区也是同样的3个字节宽度。这里问题就来了,标准INTEL HEX32格式中,每个数据总是4字节宽度。Microchip为了遵循此标准,编译生成的hex文件中每个指令数据也是4个字节,只不过最右边的一个字 节总是0×00,被称为“phantom byte(虚拟字节)”,顾名思义,就是没有实际作用纯粹用来充数字节。所以上面提到的BB部分除以4就是该行十六进制码中所含指令的数量。
- 用户闪存程序存储区的地址是以2为递增单位,这是为了照顾上面所述的3个字节宽度的指令。可以这样理解,闪存构造是以两个字节为一最小单位,3个 字节的指令将占去两个闪存地址空间,尽管有一个字节是“phantom byte”。有这个“phantom byte”也不意味着浪费了一个字节,因为这个字节在闪存里没有实现,只是为了照顾INTEL HEX32标准而凑数的。可以从Microchip的DS70609C_CN文档中得到验证“24位闪存程序存储器可被视为并排放置的两个16位空间,每 个空间都有相同的地址范围……由于闪存程序存储器只有24位宽,因此TBLRDH和TBLWTH指令能寻址闪存程序存储器中并不存在的最高字节。该字节被 称为“虚拟字节”。同时这也解释了dsPIC33EP512MU810的用户闪存存储区的地址上限为0x557EE(即十进制350,206)却只能存储 175,104(即350,206的一半)个指令了。
- INTEL HEX32文件中的“AAAA”部分表示的是该行数据的起始地址,但是需要除以2才是真正相对应芯片闪存的实际地址。
- 理解GOTO和RESET指令 Microchip编译好的Intel Hex32文件中,第二行一定是GOTO和RESET指令。该指令在芯片重置或者启动的时候被访问,告知应该从哪里开始读取用户程序。这样的其实地址为 “0”,只含有两个指令即8个字节(实际有用字节为6,2各位虚拟字节)。第一个指令总是以0×04结尾(不包括虚拟字节),表示所指向地址的低16位, 前两个字节就是改16位的地址。第二个指令总是以0×00结尾,表示所指向地址的高16位,同样的第一第二个字节就是该16位的地址。下面是一个例子:
[sourcecode language=”text”]
:0800000000a80400020000004a
:08 0000 00 00a80400 02000000 4a
(1)(2) (3)(4) (5) (6)
[/sourcecode](1)这一行有8个字节数据
(2)起始地位为0
(3)类型为数据
(4)第一个指令:00 a8 04 00(虚拟字节),可以解释为:指向低16位为0xa800的地址
(5)第二个指令:02 00 00 00(虚拟字节),可以解释为:指向高16位为0×0002的地址
(6)CRC循环冗余校正码
这一行综合起来可以解释为,跳转到0x02a800地址。再来一个例子:[sourcecode language=”text”]
:080000000002040000000000f2
[/sourcecode]这一行的意思是,跳转到0×200地址。(这是一个dsPIC33FJ256GP710A芯片正常编译后的实际hex文件,是指在启动后跳向用户程 序的第一行。dsPIC33F的一个储存页为512个指令大小,0×200即跳过第一个含有GOTO/RESET以及中断向量表的储存页。不同于 dsPIC33F,我们讨论的dsPIC33E系列的储存页大小升级到了1024个指令,这个我会在后面一篇文章里提到。)
One Reply to “Microchip公司16位单片机dsPIC33E/PIC24E系列bootloader的开发(2)”