草庐IT

bootloader学习笔记---第一篇以stm32为例

Embedded learner 2023-04-29 原文

目录

一、bootloader的任务

二、bootloader开发的基础知识

段的概念

重定位的概念

散列文件的概念

异常向量(待补充)

三、最简单的bootloader程序

 四、使用汇编跳转

五、备注

一、bootloader的任务

本系列笔记主要记录我学习汽车电子的bootloader程序开发中做的笔记,我学习的芯片是英飞凌的aurix系列的tc377,tc234,tc222,这主要是涉及到两块的东西,驱动程序和协议栈(我学习的是uds协议),前面几篇笔记是我学习的stm32的bootloader。

bootloader的目的是OTA,即为了给板子上面的程序(app)进行升级,这个是bootloader的主要任务。ps:一般不会对bootloader本身进行更新。

有的程序中有双bootloader的设计,不过需要有硬件支持,比如板子上有rom空间,一上电后先执行rom中的程序,然后到指定的flash位置去判断执行哪一个bootloader,如果没有硬件支持,一上电就会只去第一个bootloader的位置执行程序,第二个bootloader就失去了意义。

Linux中的bootloader程序的任务有,1、初始化硬件:比如设置时钟、初始化内存;2、启动内核:从flash读出内核,存入内存,给内核设置参数,启动内核;3、调试作用:在开发产品时经常需要调试内核,使用bootloader可以方便地更新内核。

单片机以stm32f103使用keil举例,假设我们要把程序下载到0x08040000中去,那么我们单击下载后,程序就会被下载到这个地址中去。

 

二、bootloader开发的基础知识

这里有4个基础概念,需要我们了解了解,段的概念,重定位的概念,散列文件的概念,异常向量的概念。

段的概念

const修饰的全局变量和没有修饰的全局变量是有区别的,用const修饰过的变量是常量,会保存在rom中,没有用const修饰过的变量保存在ram中,对于stmf103来说就是,下图中的两个空间。

我们需要在设置完sp指针后(即第一条语句之后),进入main函数之前,应该要重定位,重定位分为两块,一是代码,代码可以放在rom上面运行;二是数据(可读可写的数据),我们要把rom上面定义为可读可写的数据复制到ram上面去。

所以我们的程序可以分为几个段:

  • 代码段(ro-code):就是程序本身,不会被修改
  • 可读可写的数据段(rw-data):有初始值的全局变量、静态变量,需要从rom复制到内存。
  • 只读的数据段(ro-data):可以放在rom上,不需要复制到内存。
  • bss段或zi段:

    初始值为0的全局变量或者静态变量,没必要放在rom上,使用之前清0就可以了

    未初始化的全局变量或者静态变量,没必要放在rom上,使用之前清0就可以了

  • 局部变量:保存在栈中,运行时生成
  • 堆:一个空闲空间,使用malloc函数来管理它,malloc函数可以自己写,堆并不是必须的

重定位的概念

  1. 保存在rom上的全局变量的值,在使用前要复制到内存,这就是数据段重定位。
  2. 想把代码移动到其他位置,这就是代码重定位。
  3. 重定位的本质就是移动数据

散列文件的概念

把代码段、只读数据段、数据段,移动到它的链接地址处,也就是复制。

我们需要注意,对于数据复制来说三要数是,源,目的,长度,

  • 数据保存在哪里?加载地址
  • 数据要复制在哪里?加载地址
  • 长度

在keil中使用散列文件来描述这些信息,分散排列,在STM32F103这类资源紧缺的单片机芯片中,代码段保存在Flash上,直接在Flash上运行(当然也可以重定位到内存),数据段保存在Flash上,使用前被复制到内存上。

 

可以从上面的图片中分析出三要素来,在加载域中有两个可执行域,第一个可执行域的源地址是0x0800 0000,目的是0x0800 0000,长度则是由这个可执行域中的所有具体文件大小之后决定的;第二个可执行域的源地址是紧随第一个之后,目的是0x2000 0000,长度则是由这个可执行域中的所有具体文件大小之后决定的。简述就是,要把可读可写的数据复制到内存ram上面去。

异常向量(待补充)

如果板子上面有bootloader程序,同时板子的ram 内存比较大,那么我们可以把应用程序放在外部的flash,bootloader放在内部flash,这样的设计,cpu的地址线和数据线可以访问到内部的flash、ram、spi控制器等等,而没办法直接访问到外部flash。

对于stm32来说,startup文件是bootloader程序的一部分。

看门狗会监控整个系统,如果系统崩溃,没人设置看门狗的话,看门狗会让整个程序复位。

bootloader获得新的app程序时,同时也会获得校验码,bootloader会对app程序重新计算校验码,校验码一样表示获得了正常的app。

bootloader做好以后可以使用串口升级,可以使用蓝牙升级,也可以使用can总线升级。

三、最简单的bootloader程序

第一种是最简单的bootloader,如果bootloader和app都在烧写在内部flash内存里面,app也在flash上直接运行,那么bootloader可以直接跳转到app的地址里面去执行程序,下面是c语言的版本。

void (*app) (void);
app = (void (*)(void))addrA;
app();

举个例子,如果跳转的地址是0x08040000,如果板卡的芯片使用的指令集是thumb指令(比如cortex-m3),那么代码应该是app=(void(*)(void)0x08040001),如果指令集是ARM指令集,代码为app=(void(*)(void)0x08040000)。

下面是汇编的版本

LDR PC = addrA

第二种常见的情况就是app烧写在flash上,app应该在ram内存里运行,app或者是bootloader需要把app拷贝到内存ram中去,如果是bootloader拷贝的,那么bootloader要跳转到内存ram中去执行app;如果是app自己把自己复制到内存中去的,那么bootloader直接跳转到app的位置就可以了。

第三种情况,硬件支持重新设置vector地址,可以在应用程序中,设置对应的寄存器(cortex-m3对应的寄存器是SCB寄存器,SCB->VTOR,直接设置SCB->VTOR=app下载的地址),指定中断向量表的位置;如果硬件不支持重新设置异常地址,cpu永远只使用原来的vector,a判断有无新的vector,b老函数调用新vector的函数。

 四、使用汇编跳转

bootloader需要一个中断向量表,app也需要给他指定一个中断向量表。

在这里给出韦东山老师编写的最简单的bootloader程序(含汇编程序),即从bootloader中跳转到app程序中去,以stm32f系列为例,bootloader程序放在0x08000000,app程序放在0x08040000,下面这个是main.c的程序。


#include "uart.h"

extern void start_app(unsigned int new_vector);


int mymain()
{
	unsigned int new_vector = 0x08040000;	
	
	uart_init();

	putstr("bootloader\r\n");
	
	/* start app */
	start_app(new_vector);
	
	return 0;
}

下面是start.s的代码


                PRESERVE8
                THUMB


; Vector Table Mapped to Address 0 at Reset
                AREA    RESET, DATA, READONLY
				EXPORT  __Vectors
					
__Vectors       DCD     0                  
                DCD     Reset_Handler              ; Reset Handler

				AREA    |.text|, CODE, READONLY

; Reset handler
Reset_Handler   PROC
				EXPORT  Reset_Handler             [WEAK]
                IMPORT  mymain

				LDR SP, =(0x20000000+0x10000)
				BL mymain

                ENDP

start_app   PROC
				EXPORT  start_app

				; set vector base address as 0x08040000
				ldr r3, =0xE000ED08
				str r0, [r3]
				
				ldr sp, [r0]      ; read val from addr 0x08040000
				ldr r1, [r0, #4]  ; read val from addr 0x08040004

				BX r1

                ENDP
                

                 END

在主函数中调用了start_app程序,这是一个编写在汇编文件中的函数,在c程序的函数中,函数的第一个参数保存在r0寄存器中,第二个参数保存在r1寄存器中,这里我们用到了一个参数,这个参数就在r0中;在start_app函数中主要做了三件事情,第一,重定位vector,即在跳转之前给app程序重新设置中断向量表的位置,第二,设置sp栈顶指针,即从要跳转的地址中取出第一个值赋给sp指针,第三,设置pc指针,即从要跳转的地址中取出第二个值赋给pc指针,实现跳转。设置栈顶的指针本来是由m3的硬件实现,在这里是我们是以软件的形式实现的。

如果在bootloader中没有设置中断向量表的值,可以在app程序中进行设置。

app的程序可以任意选取对应芯片程序即可,注意改掉对应的keil设置。

在这里我有问题还没有解决,1、多核单片机的bootloader怎么进行引导?和单核单片机类似吗?

2、其他芯片的第一条指令也是设置sp指针吗?如果不是,那是什么呢?

五、备注

本文的大部分内容是根据韦东山老师的视频整理编写的笔记从0写BootLoader(适用于单片机)

=文档信息=
本学习笔记由博主整理编辑,仅供非商用学习交流使用
由于水平有限,错误和纰漏之处在所难免,欢迎大家交流指正
如本文涉及侵权,请随时留言博主,必妥善处置
版权声明:非商用自由转载-保持署名-注明出处

有关bootloader学习笔记---第一篇以stm32为例的更多相关文章

  1. STM32读取串口传感器数据(颗粒物传感器,主动上传) - 2

    文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,

  2. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  3. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  4. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  5. ruby - 我正在学习编程并选择了 Ruby。我应该升级到 Ruby 1.9 吗? - 2

    我完全不是程序员,正在学习使用Ruby和Rails框架进行编程。我目前正在使用Ruby1.8.7和Rails3.0.3,但我想知道我是否应该升级到Ruby1.9,因为我真的没有任何升级的“遗留”成本。缺点是什么?我是否会遇到与普通gem的兼容性问题,或者甚至其他我不太了解甚至无法预料的问题? 最佳答案 你应该升级。不要坚持从1.8.7开始。如果您发现不支持1.9.2的gem,请避免使用它们(因为它们很可能不被维护)。如果您对gem是否兼容1.9.2有任何疑问,您可以在以下位置查看:http://www.railsplugins.or

  6. ruby-on-rails - CarrierWave - PDF - 只选择第一页 - 2

    我的Rails应用程序中安装了carrierwave。但是,当用户上传多页pdf时,我只希望应用程序获取文档中的第一页并将其转换为jpeg。这可能吗?用什么命令?这是我的uploader。#encoding:utf-8classImageUploader[200,300]##defscale(width,height)##dosomething#end#Createdifferentversionsofyouruploadedfiles:version:thumbdoprocess:resize_to_fill=>[150,210]process:convert=>:jpgdefful

  7. ruby - 如何跳过 CSV 文件的第一行并将第二行作为标题 - 2

    有没有办法跳过CSV文件的第一行,让第二行作为标题?我有一个CSV文件,第一行是日期,第二行是标题,所以我需要能够在遍历它时跳过第一行。我尝试使用slice但它会将CSV转换为数组,我真的很想将其读取为CSV,以便我可以利用header。 最佳答案 根据您的数据,您可以使用另一种方法和skip_lines-option此示例跳过所有以#开头的行require'csv'CSV.parse(DATA.read,:col_sep=>';',:headers=>true,:skip_lines=>/^#/#Markcomments!)do|

  8. ruby - 我如何学习 ruby​​ 的正则表达式? - 2

    如何学习ruby​​的正则表达式?(对于假人) 最佳答案 http://www.rubular.com/在Ruby中使用正则表达式时是一个很棒的工具,因为它可以立即将结果可视化。 关于ruby-我如何学习ruby​​的正则表达式?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/1881231/

  9. arrays - 在一行中选择数组的第一个和最后一个元素 - 2

    我的任务是从数组中选择最高和最低的数字。我想我很清楚我想做什么,但只是努力以正确的格式访问信息以满足通过标准。defhigh_and_low(numbers)array=numbers.split("").map!{|x|x.to_i}array.sort!{|a,b|ba}putsarray[0,-1]end数字可能看起来像"80917234100",要通过,我需要输出"9234"。我正在尝试putsarray.first.last,但一直无法弄明白。 最佳答案 有Array#minmax完全满足您需要的方法:array=[80,

  10. ruby-on-rails - Ruby 或 Rails 有只将第一个字符大写的方法吗? - 2

    或者好像我必须自己写方法?(保持DHA不变):ruby-1.9.2-p180:001>s='omega-3(DHA)'=>"omega-3(DHA)"ruby-1.9.2-p180:002>s.capitalize=>"Omega-3(dha)"ruby-1.9.2-p180:003>s.titleize=>"Omega3(Dha)"ruby-1.9.2-p180:005>s[0].upcase+s[1..-1]=>"Omega-3(DHA)" 最佳答案 如果我的回答只是垃圾,我深表歉意(我不做ruby)。但我相信我已经为您找到了答

随机推荐