汽车电控系统bootloader知识介绍
2022年2月22日 浏览:1828
01
原理概述
-
ISP(In-System Programming)
-
ICP (In-circuit programmer)
-
IAP(In-Application Programming)
02
技术分析
ECU开发过程要用到它来构建bootloader,上传和下载数据,即软件刷写,控制器Reset;
测试时要用它来读写存储,控制外设;
产线上,要用它来校准机械件,控制例程,进行下线执行器测试,刷新软件,配置防盗,读取号码,下线配置等。
在行驶过程中,要用它来监测各种故障,并记下故障码;
4S店里,技师需要读取当前故障、历史故障,读取故障发生时刻环境信息状态,清除故障,判断故障发生点,还可以用来升级ECU程序。
03
实现
(1) 硬件初始化:
当硬件上电之后,运行的第一条指令就是Bootloader的代码(如果各式各样的BIOS忽略不计的话)。Bootloader需要完成硬件初始化的一系列工作。然后才可以进入正常的逻辑,例如加载OS Image等。对于软件工程师而言,硬件的初始化工作是很冗长乏味的。需要详细阅读各类硬件的资料规范。然后就是一系列的对寄存器的操作。虽然Bootloader中不需要初始化板子上的所有的硬件,而只需要初始化最基本的可以让Loader正常工作的硬件就可以,有一些外设可以放到OS启动的时候,甚至驱动加载的时候再进行初始化不迟。即使是这样,要初始化的硬件也不在少数,对于一个典型的ARM系统而言,有可能要做的事:初始化内存控制器,初始化MMU,配置GPIO口,配置调试串口,对RTC进行读写操作,如果要通过以太网下载,还需要驱动网络接口……这一系列的工作,没有一个不是体力活。需要细心的琢磨,很有可能对寄存器某一个bit的粗心设置,就会导致整个Bootloader无法工作。所以这一部分内容通常马虎不得,需要耐心完成。
(2) 代码编写和构建:
由于Bootloader是最底层的代码,汇编语言肯定是少不了的了。就连一些整天喊“汇编已死”的人也不能否认,系统启动的那一段代码,还是需要汇编语言的。而汇编语言通常也是软件工程师们不太希望去碰的“硬骨头”。这也增加了Bootloader编写的困难。
代码写好之后,当然要编译成机器码才可以在板子上运行。目前的编译器大多数只会把代码编译成某些流行的可执行文件格式,例如Windows上的PE和*Nix上的ELF等。这些带有格式的可执行文件,也没有办法再目标设备上直接运行。所以,通常OS都会提供一些工具,把这些可执行文件去头去尾,转成纯二进制格式,这样才可以在目标设备上运行。例如ADS提供的FromELF工具与Windows CE提供的ROMImage工具就是完成这类工作的。通常,我们需要为这类工具做一些配置,例如告诉这些工具代码段放在什么地方,起始地址是多少等等。如果这些参数没有配置正确,很有可能最终生成的Bootloader的映像是不可用的。那么烧写到目标设备上,自然也就无法运行。同样,对于这些参数的配置,也需要仔细检查核对,一步步进行。
(3) 开发的效率:
Bootloader的另外一个开发困难的原因是它的开发效率。通常当我们做了一些代码修改之后,都需要把修改后的二进制文件使用烧写工具烧写到目标设备的Flash中,无论是NAND还是NOR Flash,烧写的过程都不快。所以,即使是改了一行代码,也需要经过编译->烧写->运行这样一个完整的流程。一般而言再快也要10分钟左右。这样算算,一个钟头可以修改个5次代码,一天可以修改个50次代码就相当不错了。机械的重复这一过程,经常会使开发人员感到开发效率低下,从而产生反感和抵触情绪。这也是Bootloader开发的一大劣势。一个解决的方法是使用硬件调试工具把Bootloader的映像直接灌到RAM里面运行,往RAM里面灌通常比烧写Flash要快。但是这样需要调试工具来初始化RAM,又有很多的其他逻辑上的和细节上的事情要做。
(4) 调试
上面说的几大问题,其实还都是可以克服的问题。其实在我看来,开发Bootloader最大的问题还是调试问题。试想:无论汇编多么难,我还是写好了,无论烧写多么烦,我还是烧写下去了,但是当我怀着激动的心情按下Reset键的时候,整个硬件设备毫无反应。我怎么知道我的代码写的正确还是不正确呢?如果不正确,我又怎么能定位到我的错误呢?现在的软件开发中,无论是编译型语言还是script,一般都会提供相应的Debugger,让开发人员来定位代码错误。“摸黑”写代码是软件工程师们最害怕的事情。代码出了问题,如果没有行之有效的手段来做问题定位,十有八九会造成项目“卡壳”。如果定位准确,那么问题也就解决了一大半了。所以归根结底,还是调试的方法论问题。Bootloader中难以调试,是因为可以使用的手段非常少,也不常规。在OS下开发应用程序用到的那些调试手段手段,在Bootloader的开发中通常都用不上。需要有“非常”手段来调试。下面的专题,就向大家介绍一些Bootloader的常用调试方法。
(1) 硬件调试器
相信很多从应用开发走过来的开发人员,都会对Set Breakpoint,Step into, Step over等这些调试手段相当怀念。但是那些东西都是调试器在搞鬼。到了裸板上,可没有Debugger来帮我们了。那怎么办?好在我们有硬件调试器。很多CPU体系结构都提供了相应的硬件调试器,例如ARM CPU的仿真器。借助硬件调试器,我们可以完成汇编级的Set Breakpoint,Step into, Step over这些操作。这对于调试Bootloader来说,实在是太重要了。至少我们可以看到我们烧写下去的代码是否正确,然后可以看到是否在运行。这对于调试Reset后的第一段代码来说,实在是雨后春笋一般的珍贵。
但是硬件调试器也有很多不足:首先,它们一般都价格不菲,不用国内自己的D的话,几W是少不了了。其次,大多数硬件仿真器只能实现汇编级的调试,当代码进入了C语言之后,硬件调试器就显得力不从心了。第三,一劣质的仿真器,通常还无法对MMU打开后的虚拟内存进行仿真。所以限制也是很多的。
(2) 一闪一闪亮晶晶――LED灯
如果我们买不起硬件仿真器,或者手头上根本就没有。那么只好通过硬件的一些输出端口来输出一些信息,让我们知道代码到底运行到哪里了。在诸多的端口中,最简单的恐怕就是LED灯了。一般而言,用个几句汇编,就可以让LED闪烁起来。这样,我们就可以在代码中安插让LED闪烁的语句。来看我们的代码到底在哪里跑飞或者挂掉的。可惜LED等只能闪烁,想通过LED来获得更多的消息,是很困难的。例如我们想知道某个变量的值,用LED的闪烁表示出来,恐怕就很伤脑筋。用LED来闪烁出摩尔斯代码是一种可行方案,但是除了开开玩笑之外,恐怕真的去实践的人是不存在的……
(3) 终于可以Printf了――调试串口
目前为止,在Bootloader甚至整个OS开发过程中,串口可能是使用最广泛的调试端口了。它的设备简约却不简单。用一根串口线,把PC跟目标设备连接起来,这样我们可以通过串口来输出一些字符,在PC一端接收。终于,在有OS的应用程序开发中最传统的,最原始的调试方法printf就可以用了。在Bootloader的开发中,如果可以通过串口输出调试信息,那调试的手段就前进了一大步了。至少我们可以通过printf来跟踪目前代码在哪个函数里,分支语句走了哪个流程,某个变量的值到底是不是我们想要的……世界真美好。串口的问题是它不像LED那样想用就用,还是需要初始化的。好多嵌入式CPU都把串口控制器集成到了里面,甚至有一些会在CPU上电的时候自动初始化串口,这使得串口初始化相对简单。但是如果有一些串口是外挂的控制器,那么初始化一个串口有可能也需要耗费你半天时间。
(4) 走进新时代――内核调试器
随着开发进一步复杂,串口输出调试信息恐怕又不能满足我们的要求了。首先,输出的信息一多,很容易乱套。其次,输出信息业是需要花费时间的。在中断处理函数等一些时间敏感的地方使用串口输出,有时候依然不明智。如果能把远程调试器接上,那对BootLoader的调试跟对应用程序的调试就没有什么二致了(源码级设置断点,一步步跟,随时看某些数据结构的值,都成了可能)。Linux提供了GDB,Windows CE提供了KD。但是这都需要实现相当多的工作,实现一些调试器Stub。GDB可能串口就可走。但CE的KD通常是要通过KITL,通过以太网的。虽然复杂,长远来看还是非常有必要的,所以,当感觉其它手段力不从心的时候,可以考虑启用更高级别的调试器了。
(5) 其它歪门邪道
如果我的板子上没有硬件调试器,没有LED,串口也不工作,更别提内核调试器了,那我怎么办?别着急,只要你的硬件有输入输出设备,我们都是可以想出办法来的。只要能想办法往输出设备中输出一点东西,并且可以通过一些手段得到这些输出信息,我们调试的目的就达到了。例如:往喇叭里面输出一段杂音,往块设备上写一个扇区,往屏幕上画一些乱七八糟的东西……代码运行起来之后,只要不是当“瞎子”,都可以实现调试的目的。再走投无路的时候,这些手段不妨想想。
04
总结
点赞 评论 收藏