简介:
写Boot是为了让准备从事、或者已经从事该工作的相关人员,能快速的了解什么是Boot,Boot的作用,以及ECU-Boot 的开发过程。
主要参考了两本书籍《嵌入式Linux系统教程》,《汽车嵌入式微处理器的工作原理与应用—英飞凌XC2000家族MCU》
第一次写这么多的,格式不是很会弄,还请谅解。
1目的
1、为了能让新手快速入门
2、让更多的人了解BootLoader
3、如何维护、开发BootLoader
1、不仅在嵌入式系统在有Bootloader,在通常的PC机系统中,其引导加载的程序是由BIOS和位于硬盘MBR中的OS BootLoader完成的。BIOS在完成硬件检测和资源分配后,将硬盘MBR中的BootLoader读到系统的RAM中,然后将控制权交给OS BootLoader。
2、BootLoader的主要运行任务就是将内核映象从硬盘上读到RAM中,然后跳转到内核的入口点去运行,也即开始启动操作系统。
3、但对于嵌入式系统而言,出于经济性、价格方面的考虑,虽然一些嵌入式CPU中会嵌入一段短小的启动程序,但是通常并没有像BIOS那样的固件程序,所以相对于PC机上的OS Bootloader所做的工作,嵌入式系统的Bootloader不仅要完成将内核映象从硬盘上读到RAM中,然后引导启动操作系统内核,还需要BIOS所做的硬件检测和资源分配工作。可见,嵌入式系统中的Bootloader比起PC机中的Bootloader更强大,功能更多。
4、就如同XC2000系列的英飞凌芯片的嵌入式系统中(如XC2331D),系统在上电或复位时通常都从地址0x00000000处开始执行,执行一系列操作之后,在进入0x00C00000地址段(在这里BootLoader的地址段为0x00C00000 ----0xC04000)而以其他芯片的处理器为核心的嵌入式系统,通常都有某种类型的固态存储设备(如EEPROM、FLASH等)被映射到这个预先设置好的地址上。
5、在系统加电复位后,处理器将首先处理Bootloader程序。因此Bootloader是系统加电后、操作系统内核或用户应用程序运行之前,首先运行的一段代码,通过这段代码,可以初始化硬件设备,建立内存空间的映射图(有的CPU没有内存映射功能),从而将系统的软硬件环境设定在一个合适的状态,为最终调用操作系统内核,运行用户程序准备好正确的环境。
6、通常一个嵌入式系统软件架构可以分为四个层次:用户应用程序,文件系统,嵌入式操作系统内核和引导加载程序(即Bootloader)。从底往上各层次完成的主要功能如下如图:
对于开发人员有两中模式:
1.引导加载应用程序(Bootloading)
2.下载应用程序(Downloading)
对于最终用户: 1.不晓得???
1.引导加载应用程序(Bootloading)
引导加载应用程序(Bootloading),是指Bootloader从目标机上的某个固件存储设备上将操作系统加载到RAM中运行,整个过程没有用户的介入。这种模式是Bootloader的正常工作模式。当嵌入式产品最终发布时,Bootloader就被默认在这种模式下
2.下载应用程序(Downloading)
目标机上的Bootloader将通过串口或者网络或者USB等其他通信手段从主机下载文件,比如下载内核镜像、根文件系统镜像等,从主机下载的文件通常首先被Bootloader保存到目标机的RAM中,然后被Bootloader写到目标机的Flash内固态存储设备中。Bootloader的这种模式通常在第一次安装内核与根文件系统时使用。此外,以后的系统更新也会使用Bootloader的这种工作模式。工作于这种模式下的Bootloader通常都会向他的中断用户提供一个简单的命令接口。
1、BOOT主要功能就是:电路初始化和为高级语言编写的软件运行做准备。
Bootloader启动的具体流程如图所示,主要的过程如下:
I.启动代码的第一步是设置中断和异常向量;
II.完成系统启动所必须的最小配置,某些处理器芯片包含一个或几个全局寄存器,这些寄存器必须在系统启动的最初进行配置;
III.设置看门狗,用户设计的部分外围电路如果必须在系统启动时初始化,就可以放在这一步;
2、
配置系统所使用的存储器,包括 Flash,SRAM 和DRAM 等,并为它们分配地址空间。如果系统使用了DRAM或其它外设,就需要设置相关的寄存器,以确定其刷新频率,数据总线宽度等信息,初始化存储器系统。有些芯片可通过寄存器编程初始化存储器系统,而对于较复杂系统通常集成有MMU来管理内存空间;为处理器的每个工作模式设置栈指针,处理器有多种工作模式,每种工作模式都需要设置单独的栈空间;
3、
变量初始化,这里的变量指的是在软件中定义的已经赋好初值的全局变量,启动过程中需要将这部分变量从只读区域,也就是Flash拷贝到读写区域中,因为这部分变量的值在软件运行时有可能重新赋值。还有一种变量不需要处理,就是已经赋好初值的静态全局变量,这部分变量在软件运行过程中不会改变,因此可以直接固化在只读的Flash或EEPROM中;
数据区准备,对于软件中所有未赋初值的全局变量,启动过程中需要将这部分变量所在区域全部清零;
最后一步是调用高级语言入口函数,比如main函数等。
1 ##这些协议我都传上了,自行下载看看,也可以去ISO找##
实现 Bootloader 通信,且适用于ECU通过CAN总线、LIN线、K线、OTA其中之一的方式实现程序的在线更新。
首先需要熟悉以下几个ISO规范-----Road Vehicles - Diagnostics on Controller Area Networks (CAN):
ISO 15765-2: Network layer services
ISO 15765-3: (UDS on CAN)
ISO 14229-1: Specification and requirements
ISO 14230-1:物理层
ISO 14230-2:数据链路层
ISO 14230-3:应用层
实现 Bootloader 功能,且适用于ECU通过BOOT实现程序按照一定的流程 更新程序。而这流程一般是供应商根据主机厂的需求而来,不同的主机厂所定义的下载流程规范是不一样的。
例如:
保时捷、布加迪、帕加尼、迈凯伦、柯尼塞格、迈巴赫、兰博基尼、雷克萨斯、劳斯劳斯、比亚迪、吉利、东方小康、上海通用五菱、等、、、他们的需求是不一样的。
提问:这里的需求不一致和看国际标准协议就没有冲突?那还看什么国际标准协议啊?
如果两者的协议有冲突,以什么协议规范为准呢?
答:这时候如果协议有冲突往往是在应用层协议有冲突,底层的物理、数据链路层是几乎不会变的,这就是为什么要分层开发的原因。如果协议不一致,主机厂往往会标明:按照主机厂定义的协议来开发(这时候主机厂协议的优先级高与国标协议),当然开发者也可以和主机厂进行协调。写偏差报告(反正很麻烦。)
这里推荐大家看看这个博主写得ISO 14229写得很详细。这是应用层相关的协议,开发中用的比较多。
链接: link.
TASKING不仅功能强大,还能在线仿真调试。
特别适合开发,找bug。他是基于eclipse移植而来,许多使用方法是和JAVA是一样的:
例如:
Ctrl + D : 删除一行的程序:把光标放在要删除的哪一行。
Alt + “/” : 智能代码提示
Ctrl + “/“ : 代码注释
Ctrl + O:快速outline视图
Ctrl + i:格式化代码
ctrl+shift+x和ctrl+shift+y:英文字母大小写的转换
TASKING VX-toolset 编译器支持的 C 数据类型
TASKING C 编译器有五种内部的寻址类型,即巨程(huge)、远程(far)、近程(near)、位寻址和 SFR。
巨程寻址: 32位线性地址,从0至16MB。
远程寻址: 32位页地址,高字包含一个10位的页数字,低字包含一个14位的16 kB页内偏移量。
近程寻址: 16位地址,高两位包含一个DPP数字,低14位包含一个14位的16 kB页内偏移量。
位寻址: 12位地址,包含一个8位的字偏移量位地址空间和一个4位位数字
建议:指针访问堆栈效率: __far 指针访问堆栈对象的代码产生效率比__shuge 指针好
编译器使用__at()将一个对象或函数配置在存储器的一个绝对地址,这是一个 32 位
线性(巨程)地址。如果对象或函数使用__bit 修饰,那么这个地址是一个位地址。编译器
检查这个地址范围、存储器对齐方式和一个对象是否跨页边界。下面是的三个相关例子。
unsigned char Display[80*24] __at( 0x2000 );
int i __at(0x1000) = 1;
void f(void) __at( 0xf0ff + 1 ) { }
如果对一个变量配置一个绝对地址,必须注意:
1.) __at()的参数必须是一个常数地址;
2.) 这个变量仅是一个全局变量,函数的参数、函数的局部变量不适用;
3.) 一个声明 extern 的全局变量在当前的模块不被编译器分配地址,不可能在一个外部变量使用__at();
4.) 结构体的成员不能分配一个绝对地址;
5.) 变量的绝对地址不能重叠,否则尽管编译器不检查,但汇编器或连接器将出错
test.dav
这是一个 DAvE 保存的用户配置目标系统的二进制文件,是生成其它工程文件的基础。
test.dpt
与 TASKING VX‐toolset 等 IDE 无缝链接的配置文件,内容包括 XC2000 目标芯片类型、编
译器类型、存储器模式、系统时钟和文件信息,不允许用户修改或添加任何信息。
test.rtf
该文件为说明 DAvE 生成的文件所包含的函数,它是一个可用 word 阅读的富文本文件。
MAIN.H
主函数的头文件,包含主函数拟调用模块的头文件和宏定义,允许在“//USER CODE
BEGIN”和“//USER CODE END”之间添加用户代码。
MAIN.C
包含主函数 main 的文件,允许用户代码设计的关键文件。它包括五个函数:
void MAIN_vInit(void),该函数用来初始化微控制器,包含各个模块的初始化子函数。
void MAIN_vUnlockProtecReg(void),该函数可以对受保护的寄存器进行写操作。
void MAIN_vLockProtecReg(void),该函数可以实现对受保护的寄存器禁止写操作。
void MAIN_vChangeFreq(void),该函数用来选择外部晶振并将系统频率配置到希望的工
作频率,如 80MHz、 66MHz 或 40MHz。
void main(void),主函数,用户设计程序的函数。
SCS.H
该头文件包含 SCS 驱动,是系统时钟配置的关键文件,不允许用户进行任何改动,包含
一些宏定义和编译预处理命令,含有两个函数。
INLINE void Scs_EnableHighPrecOsc(unsigned int Enable),当外部晶振或时钟输入时,该
函数用来禁止或使能外部晶振或时钟振荡器,用户调用该函数能够减少等待时间。
INLINE void Scs_CopyWords(unsigned int SHUGE *DestPtr, const unsigned int SHUGE
*SrcPtr, unsigned int Words),该全局函数将源地址的字复制到目的地址。
SCS.C
包含多个 SCS 的驱动函数,与系统时钟配置相关,对应用户的 DAvE 工程配置,不允许
用户修改。
XC22XXNREGS.H
该文件定义用户目标微控制器 XC22XXN 的寄存器,不允许修改,可供用户参考查看。
IO.H
相对于用户配置的端口模块的头文件,包含该模块的函数定义和宏定义,用户可以添加
代码。
IO.C
相对于用户配置的端口模块的 C 文件,包含该模块的初始化函数。
cstart.h 和 cstart.c
包含目标芯片初始化信息,包括寄存器的初始化和变量的初始化,建议用户不修改这两个文件。
TEST.simulator.launch 或 TEST.launch
软 件 仿 真 的 编 译 配 置 文 件 为 TEST.simulator.launch , 硬 件 调 试 的 编 译 配 置 文 件 为
TEST.launch,该文件不允许修改。
TEST.lsl
该文件包含存储器地址分配,定义了 DPP 四个页地址、中断矢量表和系统堆栈,不允
许修改。
TEST.mapxml
在编译完成后,可以在Debug中找到相应的 .mapxml文件。这文件是对 各种变量 函数的内存分配情况,可以详细的看到
注:.lsl是链接脚本文件:如果项目对地址分配比较严格的话,开发前是已经将Flash资源分配好了的,随着开发到后期,Boot功能越来越多,可能会超出内存使用,这时就要修改 .lsl文件,对内存资源重新分配。(对于.lsl的语法规则在芯片手册上有,Tasking的Help里也有)
Memtool 是一个支持 XC2000 家族微控制器程序烧录的专业工具,特点如下:
(1) 通过 PLS 硬件工具,能够对微控制器内部和外部的 Flash/OTP 存储器烧录。
(2) 通过引导程序装载器、 K 线、调试器/仿真器 JTAG/DAP 接口烧录程序。
(3) 对于免费下载的工具,只能对片上的 Flash/OTP 烧录程序,仅支持引导程序装载器,支持带非易失性存储器的 XC2000 微控制器。
总之:就是可以读、写、芯片中的程序(以Hex方式的程序,不是源程序)
主要是用来配置底层程序,初始化各种IO,分配各种资源。(很强大)
这两款软件都是周立功的,功能很强大,相比Vector上手简单,且价格实惠、实现人手一部到两部完全没有任何经济压力,大大加快开发的速度。
这需要配合硬件can卡使用,主要是实现Can网络报文的收发、监控、报文分析、等。
缺点:时间精度只有1ms,相比Vector的canoe(精度1us)差了一点。但开发中已经够用的,除了时间要求特别的严格的主机厂测试的一些功能,的时候就建议使用canoe来进行开发调试。
项目需求是一个项目的来源,后续所有工作都是围绕需求在进行,那怕其中有一点不明确,都会影响项目整体进度及软件质量,需和主机厂或者项目确认无误后在开发。
软件除了模块的实现外,还需考虑模块间的偶合性,模块之间的接口。在本模块功能实现的同时,考虑是否会影响其他模块,因为每个人对需求的理解程度不一致,可能导致整个软件出现功能上问题。此类问题需模块开发人员了解模块间的功能关系,以及模块对软件功能的影响。
提高开发人员编码的统一性,好的编码规范可以尽可能的减少软件维护成本,让开发人员快速而彻底的理解新的代码,长期规范性编码可以让人养成好的编码习惯,甚至可以锻炼出更加严谨的思维。
软件测试决定了软件是否完成,能投入使用,也是整个开发过程中最重要的一步。软件测试分为开发自测和专业测试组测试,因为开发的测试用例和开发需求一致,所以发现BUG的可能性很小。所以需要专业测试组不断挖掘测试需求,优化测试手法,测试各种工况软件现象,做好软件释放的最后一道关卡。