最近,在研究F28035平台下采用CAN总线的方式更新Flash的方法。取得了一些进展,现介绍一下具体的实现思路。
这里所说的BootLoader并不是芯片自带的Boot ROM,而是经过Boot ROM引导,通过Flash方式启动后,执行C语言环境下的MCU程序,也称为二次BootLoader。BootLoader需要配合PC端上位机使用,所以本文也涉及到了PC端上位机的开发。
下面的内容从四个方面介绍:
1)BootLoader程序实现的功能
2)F28035系列芯片的启动流程
3)MCU中BootLoader实现的方式
4)C#开发PC端上位机
硬件:
TI的C2000平台下的F28035开发板(带CAN2.0总线收发功能)
CAN盒:周立功USBCAN-2E-U
下载仿真器:XDS100V3
软件:
CCS10.3.1
Visual Studio 2022
操作系统:
WIN10 IoT 企业版 LTCS(21H2)
一、BootLoader实现的功能
将MCU中的程序代码根据功能,可以划分成两个部分:
1)BootLoader(以下用BL代指)
2)Appilication(以下用app代指)
这两部分代码的关系如下图所示:
如上图,当MCU上电后,通过复位向量的跳转,进入Boot ROM,通过Boot ROM的引导,选择Flash方式启动,然后执行FlashA扇区中的BootLoader程序,BL通过CAN总线接收app的源代码,并写入到指定的MCU内部Flash扇区(比如C-H扇区),然后对app代码进行校验,确保接收的app源代码正确后,通过复位看门狗,重新启动MCU。MCU重启后,再经过复位向量--BootROM--BL--app这样的步骤,最终将MCU的软硬件的资源交给app,app来完成用户要求的功能。
Flash存储区域经过这样划分,保证了BL与app的隔离。app可以独立于BL进行开发,只要在app的CMD文件中将FlashA扇区空闲出来,不用就行,并不需要更改其它的地址空间中的内容。而且app也可以不用BL加载,直接通过TI官方的下载器,下载到MCU,app代码也能正常工作,增加了BL使用的可选择性。
至于CMD文件如何编写、如何从BL跳到app的入口等等,这些技术细节,会在后面详细阐述。
总结一下BL写入Flash的具体步骤:
1)先将BL编译完成的.out文件通过下载器(如XDS100V3)下载到MCU中,成功后,MCU关电。
2)连接好CAN盒,启动C#编写的上位机程序,选择好对应的要写入MCU的app程序(.out)文件,此时上位机开始尝试主动与MCU通讯,通过CAN发送握手信号。
3)MCU上电,接收到上位机发送的握手信号后,上位机和MCU开始通讯,MCU接收完整的app代码文件,并写入Flash指定扇区。
4)app代码校验成功后,MCU通过复位看门狗,使MCU重启。
5)MCU重启后,首先进入BL,当MCU在指定时间内没有接收到上位机发出的握手信号时,BL会将程序指针指向app的入口地址。然后app程序开始执行。
6)当app没有被成功写入时,则始终停留在BL程序中。
二、F28035的启动流程
理解MCU上电后的启动流程,对于开发BootLoader来说是非常重要的。对于C2000系列的MCU来说,启动流程基本一致,只是个别的跳转地址不一致。这些不一样的地方需要查找对应型号的MCU手册来确定。下面就以F28035芯片为例来说明:
F28035 MCU内存映射如下:
可看到此表格的最下端(地址:0x3F FFC0至0x3F FFFF),是上电启动后CPU的中断向量表(Vector)。当MCU上电后,首先将程序指针,指向此区域的0x3F FFC0处。此过程是固化在MCU硬件中的,不可改变。在0x3F FFC0处只能放下两个字节(16Bit)数据,功能就是让程序指针指向Boot ROM区域的InitBoot函数(0x3F F4B0)。下图是Boot ROM区域的放大:
注意根据不同型号的MCU,InitBoot函数(0x3F F4B0)地址也是不同的,具体是多少要查找手册,幸运的是这个跳转也是硬件自动完成的,不需要干预。
从上图中,可以看到红框中的区域就是Boot ROM了。这个区域是固化(只读)在内存中的,用户不能写入数据。从Boot ROM内部的功能划分来看,主要实现以下几部分功能:
1)Boot ROM版本和Boot ROM校验
2)Flash API库:我们需要的Flash擦除与写入函数就在此库中。
3)Boot引导函数:包括InitBoot函数和其它的功能函数。
4)定点型的数学函数:如sin、cos等。
5)定点型的数学表格。
最重要的就是2)和3)这两项。
Flash API库里包含着一些Flash相关的操作函数,如擦除、写入、数据校验等功能。在BL代码中Flash操作需要的函数都在这里能够找到。当然,也可以在编译BL代码时,从芯片外部引入TI官方提供的专门FlashAPI库,但是这样就增加了BL的代码体积。最好就是用这个包含在Boot ROM中的FlashAPI。如果要用BootROM中的FlashAPI,需要将一个符号库引入到BL的工程文件夹中。
当程序指针指向InitBoot函数,就开始依次执行下面的功能:
上图中SelectBootMode()函数做了简化,具体技术细节请参见手册。
_c_int00()在跳转到程序入口之前完成了以下的工作
1) 定义系统栈.stack,并初始化栈指针,配置相关寄存器
2) 初始化全局变量(.cinit)
3) 若使用C++,还会完成全局对象构造(.pinit)
4) 调用main函数运行C程序
5) 当main函数return时,调用exit函数
注意:_c_int00()是在rts2800_ml.lib库中。当修改程序代码后,再编译时,程序的入口地址就可能和上次的不一样。到此,MCU就完成了从上电到C程序执行的全过程。
BL与app各自编译后的入口地址是不一样的。
因为BL的程序代码先下载到MCU中,app的代码通过BL的CAN总线,下载到MCU的Flash扇区里,所以程序指针先指向BL的_c_int00(),执行BL的代码,然后BL再将程序指针指向app的_c_int00(),建立app的C语言环境。即app的内存分配方式会完全覆盖BL的内存分配方式,所以app和BL这两个工程的CMD编写中,除了各自代码存放的Flash扇区要隔离开以外,象RAMM0、1和RAML0-3分配的方式可以完全不一样,最终app的内存分配也会覆盖BL的内存分配,所以app通过BL下载到指定Flash后能正常运行,而此后BL代码将不能再执行,除非MCU重新上电或让app将程序指针指向BL的入口地址。(待续)