wkhn
认证:优质创作者
所在专题目录 查看专题
一种采用CAN总线方式升级F28035芯片Flash的方法(一)
一种采用CAN总线方式升级F28035芯片Flash的方法(二)
一种采用CAN总线方式升级F28035芯片Flash的方法(三)
作者动态 更多
一种采用CAN总线方式升级F28035芯片Flash的方法(三)
05-13 15:31
一种采用CAN总线方式升级F28035芯片Flash的方法(二)
05-13 15:31
一种采用CAN总线方式升级F28035芯片Flash的方法(一)
05-13 15:31
STM32G4系列MCU——数字电源入门(4)
2023-03-14 10:46
STM32G4系列MCU——数字电源入门(3)
2023-03-13 20:05

一种采用CAN总线方式升级F28035芯片Flash的方法(二)

三、MCU中BootLoader实现的方式

      要先将BL的代码通过下载器烧录到MCU中,然后再通过CAN总线将接收到的app代码写入到指定的Flash扇区中,校验接收的app代码,如果完全正确,则将程序指针指向app的入口地址,完成了BL向app的转换。在此过程中要解决下面几个问题:

       1)BL代码的CMD文件如何编写,即BL阶段,MCU内存如何分配。

       2)对应的app的CMD文件中,如何分配MCU内存。

       3)app的编译输出文件.out用什么格式传送给MCU。

       4)接收app代码的协议和Flash写入机制

       5)如何指向app的入口地址

1)BL的CMD文件一般分成两部分

上图中,CMD文件是在以前编写的F28034.CMD文件基础上改写的,其实是F28035内存的分配方式

从上图中,可以看出来,BL与app的Flash存储区域是分开的。而且app明显要比BL区域大的多。当将BL下载到MCU中时,要同时将所有的Flash扇区擦除。

接收的app代码做为CAN传送的数据,需要一个缓冲区。为了避免数据溢出,在内存选择了一段速度快且容量大的区域做为CAN数据缓冲区。

可以看到,将RAML0-3这段连续的区域合并起来使用,容量是8K×16bit当做数据缓冲。如果app用到CLA外设,会占用RAML1-L3的一部分空间,这样会和BL的内存分配产生冲突吗?

并不会。因为app被引导进入自己的_c_int00后,会重新按照app本身的CMD分配方式分配RAML0-3代码或数据的存放。

这样就将BL的CMD编写完成。

2)app的存储分配

可知,app的Flash的分配与BL的Flash分配完全一致,而RAMM0-1、RAML0-3则完全不一样,但这不影响app的正常运行。

app程序可以先独立开发,即不通过BL下载到MCU中。app编译完成后,用TI的XDS100V3下载器烧录到MCU中,只要避开FlashA扇区的使用即可。

3)app的编译输出文件.out用什么格式传送给MCU。

当app编译完成后,会输出一个.out文件,但我们不可能用CAN传送这种格式的文件内容。在TI官方的BootROM中,关于串口或CAN的引导方式中,提到了一种app代码文件的传送方式。将.out文件转换成数据流结构。具体请参考TI的文档SPRUGO0A.PDF的第22页。

这里提到用hex2000.exe工具将.out转换成流数据文件,然后再通过CAN总线传送到MCU。MCU收到后,就按照该文件指定的方式,将app代码写入到指定的Flash扇区。

其实就是将.out转换成ASCII-Hex格式

转换完成

一共生成了5个块.cinit、.text、codestart、ramfuncs、Cla1Prog。

这个后缀为.a00的文件可以用记事本打开。

根据SPRUGO0A.PDF这篇文档的说明,将此文件处理了一下,方便观察。

可以看到hex2000将.out文件转换成几个块状的数据结构,这几个块就是要写入到Flash对应扇区的数据。最上面有该app的入口地址(0x003E F293) 。还可以查看编译后生成的对应.map文件。

入口地址确实是0x003E F293。然后再对比一下第一个段的地址和大小

从上面可以知道,只要按照生成的.a00文件的内容,依次将收到的app代码写入到指定的Flash地址中,然后在BL中将程序指针指向.a00的入口地址,就能实现BL启动app的目地。

4)接收app代码的协议和Flash写入机制

接收app代码的协议是根据自己的习惯,自行制定的,没有一定之规。

作者的方法是:

  1. 上位机先将.a00文件转换成数据流结构,然后上位机会以.a00文件中每个块做为一个段,以段为单位向MCU发送段的内容。
  2. 先传送此段的大小(长度)和地址。
  3. 当MCU收到段长度和段地址后,会发送一个确认信号给上位机。上位机收到确认信号后,再传送此段的内容。这个段的长度、地址和内容经过CAN接收,全部缓存在MCU的内存RAML0-3中,此内存区域容量为8K×16bit,所以每个段的大小不能超过此数值,否则会出现溢出。目前,作者编写的MCU程序代码的每段大小还没有超过此数值的。
  4. 一个段传送完成后,MCU一次性将RAML0-3中的内容写入到段指定的Flash地址中。然后再进行下一个段的传送。重复2、3的步骤
  5. 当读到某个段的大小为0时,表示所有段都传送完毕,接着传送app的入口地址,要将这个入口地址也保存在Flash里,通过前面BL和app的CMD存储区域的划分,可以知道,有一个FlashB扇区既没用于BL也没用于aap。此区域就是用来存储app入口地址的。
  6. 进行app代码的校验。校验成功后,通过看门狗复位,MCU重启,BL在规定时间内收不到PC端上位机发出的握手信号,则自动进入app的程序空间。

5)BL是如何指向app的入口地址的

      首先,上位机已经正确将app的入口地址传送给MCU,MCU将入口地址存放在FlashB扇区。声明一个32bit的无符号整型变量,如下:

volatile Uint32 ProgramEntry = 0;

然后,读出FlashB中存储的入口地址,并声明的变量指向入口地址。

当BL在规定的时间内,没有接收到上位机的握手信号,则执行下面的语句。

(*((void(*)(void))ProgramEntry))();
  1. (void(*)(void)):这是一个类型转换,它将ProgramEntry转换为一个指向返回void类型且不接受任何参数的函数的指针。换句话说,它将ProgramEntry视为一个函数指针,该函数不返回任何值,也不接收任何参数。

  2. *(...):这是间接引用操作符,它用于调用函数指针指向的函数
  3. (*((void(*)(void))ProgramEntry))();:将上述所有部分组合起来,这行代码首先将ProgramEntry转换为一个函数指针,然后调用这个函数。

用上面的方法就可以将程序指针指向ProgramEntry变量存储的入口地址。(待续)

声明:本内容为作者独立观点,不代表电子星球立场。未经允许不得转载。授权事宜与稿件投诉,请联系:editor@netbroad.com
觉得内容不错的朋友,别忘了一键三连哦!
赞 6
收藏 6
关注 518
成为作者 赚取收益
全部留言
0/200
  • qfw88 11-15 16:20
    29071311@qq.com 发一下工程模板
    回复
  • abc97 10-24 09:24
    666
    回复
  • 电气小学生8 05-21 21:45
    老师可以发一下工程模板吗?跪求
    回复