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


    一、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个基础概念,可以了解了解,段的概念,重定位的概念,散列文件的概念,异常向量的概念。

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

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

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

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

    三、最简单的bootloader程序

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

    1. void (*app) (void);
    2. app = (void (*)(void))addrA;
    3. app();

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

    下面是汇编的版本

    LDR PC = addrA

    第二种常见的情况就是bootloader需要把app拷贝到内存ram中去,然后跳转到内存ram中去执行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的程序。

    1. #include "uart.h"
    2. extern void start_app(unsigned int new_vector);
    3. int mymain()
    4. {
    5. unsigned int new_vector = 0x08040000;
    6. uart_init();
    7. putstr("bootloader\r\n");
    8. /* start app */
    9. start_app(new_vector);
    10. return 0;
    11. }

    下面是start.s的代码

    1. PRESERVE8
    2. THUMB
    3. ; Vector Table Mapped to Address 0 at Reset
    4. AREA RESET, DATA, READONLY
    5. EXPORT __Vectors
    6. __Vectors DCD 0
    7. DCD Reset_Handler ; Reset Handler
    8. AREA |.text|, CODE, READONLY
    9. ; Reset handler
    10. Reset_Handler PROC
    11. EXPORT Reset_Handler [WEAK]
    12. IMPORT mymain
    13. LDR SP, =(0x20000000+0x10000)
    14. BL mymain
    15. ENDP
    16. start_app PROC
    17. EXPORT start_app
    18. ; set vector base address as 0x08040000
    19. ldr r3, =0xE000ED08
    20. str r0, [r3]
    21. ldr sp, [r0] ; read val from addr 0x08040000
    22. ldr r1, [r0, #4] ; read val from addr 0x08040004
    23. BX r1
    24. ENDP
    25. END

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

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

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

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

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

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

  • 相关阅读:
    JS中JSON.stringify序列化和JSON.parse反序列化问题和解决
    Docker搭建Mylar
    FFmpeg —— 点播流程技术总结(公网、局域网)
    HummerRisk V0.5:新版云合规报告、资源风险联动、拓扑展示等内容
    vue最新前端面试题系列(1-5)
    一比一还原axios源码(六)—— 配置化
    React_Refs转发
    Verilig语法之——Generate 结构
    【SpringMVC】提问问题汇总
    ul滚动卡顿解决办法分享
  • 原文地址:https://blog.csdn.net/weixin_44795447/article/details/125556829