bootloader是TI串口转以太网代码的一小部分,位于Flash开始的4KB空间内。它的一个重要作用是在应用远程升级,可以通过串口、USB、IIC、以太网等通道进行远程固件升级。bootloader是CPU启动后最先执行的程序,它会把自己拷贝到SRAM,并判断是否有固件升级,如果有升级请求,则执行升级程序;反之,执行用户程序。 一.流程图
由于这里只考虑基于以太网的bootloader,其流程图如图2-1所示:
图2-1 二.配置文件
由于bootlaoder可以使用串口、USB、IIC、以太网等通道进行远程固件升级,那么怎么样配置才可以使用以太网呢?这就牵扯到bl_config文件。此文件是专门配置bootloader的。代码就不贴了,看一下这里面几个必须配置的选项:
1. 以下至少且只能定义一个,用于指明使用何种方式升级。
- include bl_config.inc
复制代码包含bl_config.inc,这个文件是什么,从哪里来,有什么作用?再看bootloader工程Options---User---Run User Programs Before Build/Rebuild内的用户命令(见图2-2)又是什么?
图2-2 所有的一切,要从keil MDK的汇编器说起,在启动代码中要用到配置文件bl_config.h中定义的一些配置选项,但因为MDK汇编器不能通过C预处理器运行汇编代码,所以bl_config.h中的相关内容需要 转化为汇编格式并包含到MDK的启动代码中。这需要手动运行C预编译器进行格式转化。图2-2中红 {MOD}部分圈出的内容正是为了完成这个转换。在点击Build/Rebuild编译按钮之后,会先运行图2-2指定的命令,再进行编译。先来分析一下这条命令: armcc --device DLM -o bl_config.inc -E bl_config.c 这条命令的作用是将bl_config.c(包含bl_config.h文件)进行而且仅进行预编译处理,并生成bl_config.inc文件。 armcc是Keil MDK提供的C编译工具,语法为: armcc [Options] file1 file2 ... file n 介绍一下这里用到的Options选项: --device<dev>:设置目标的设备类型,DLM为Luminary的设备标识。 -I<directory> :目录列表 -E :仅执行预处理 -o<file> :指定输出文件的名字2. 看一下目标板上电后启动代码的运行流程 上电后程序先到Flash地址0x00处装载堆栈地址,这跟以前接触过的处理器不同,以前0x00处都是放置的复位处理代码,但Cortex M3内核却不是,0x00处是放置的堆栈地址,而不是跳转指令。 堆栈设置完成后,跳转到Reset处理程序处,调用处理器初始化函数ProcessorInit,该函数将bootloader从Flash拷贝到SRAM,将.bss区用零填充并将向量表重映射到SRAM开始处。 之后跳转到Reset_Handler_In_SRAM函数,在该函数中,如果用户提供了底层硬件初始化函数(在bl_config.h中使能),则调用这个函数。然后调用CheckForceUpdate函数,检查是否有升级请求。如果没有升级请求,跳转到CallApplication函数,在该函数中,将向量表重映射到应用程序开始处(这里为地址0x1000),装载用户程序堆栈地址,跳转到用户程序的Reset服务函数。 如果调用CheckForceUpdate函数检测到有升级请求,则配置以太网,跳转到升级程序UpdateBOOTP处执行。3. 如何在用户程序中调用升级程序 用户程序存在于Flash地址0x1000处,bootloader存放于Flash地址0x00处,并且用户程序在执行的时候已经将向量表重映射到了Flash地址0x1000处了,那么应用程序是如何调用位于bootloader中的升级程序呢? 再看bootloader启动代码的中断向量表,在Flash地址的0x2C中存放的是CPU SVC异常服务跳转地址: dcd UpdateHandler ; Offset 2C: SVCall handler 而bootloader正是用这个异常来处理升级请求的。那么,应用程序只要执行该地址处的跳转指令,就能进行一次程序升级,在应用程序中的swupdate.c中,使用了如下C代码来执行位于Flash地址0x2C内的跳转程序: (*((void (*)(void))(*(unsigned long *)0x2c)))(); 对C语言还没有入门的同学可能会比较的头痛,这像谜一样的语句是如何执行位于bootloader的SVC异常服务例程呢?还是分解一下吧: (*(unsigned long *)0x2c):将0x2C强制转化为unsigned long类型指针,并指向该地址所在的数据; void (*)(void) :函数指针,指针名为空,该函数参数为空,返回值为空 (void (*)(void))(*(unsigned long *)0x2c):将Flash地址0x2C中的内容强制转化为函数指针,该函数参数为空,返回值为空 (*((void (*)(void))(*(unsigned long *)0x2c)))();:调用函数,即开始从启动代码中的UpdateHandler标号处开始执行。
一周热门 更多>