打印
[应用相关]

TI Cortex M3串口转以太网例程分析2-----bootloader

[复制链接]
657|4
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
wakayi|  楼主 | 2018-9-15 21:56 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
原文网址:https://blog.csdn.net/zhzht19861011/article/details/6675170

  bootloader是TI串口转以太网代码的一小部分,位于Flash开始的4KB空间内。它的一个重要作用是在应用远程升级,可以通过串口、USB、IIC、以太网等通道进行远程固件升级。bootloader是CPU启动后最先执行的程序,它会把自己拷贝到SRAM,并判断是否有固件升级,如果有升级请求,则执行升级程序;反之,执行用户程序。

  一.流程图      

           由于这里只考虑基于以太网的bootloader,其流程图如图2-1所示:


图2-1


沙发
wakayi|  楼主 | 2018-9-15 21:57 | 只看该作者

二.配置文件     

        由于bootlaoder可以使用串口、USB、IIC、以太网等通道进行远程固件升级,那么怎么样配置才可以使用以太网呢?这就牵扯到bl_config文件。此文件是专门配置bootloader的。代码就不贴了,看一下这里面几个必须配置的选项:

1. 以下至少且只能定义一个,用于指明使用何种方式升级。

        CAN_ENABLE_UPDATE,      

        ENET_ENABLE_UPDATE,

        I2C_ENABLE_UPDATE,

        SSI_ENABLE_UPDATE,

        UART_ENABLE_UPDATE,

        USB_ENABLE_UPDATE

2. 以下必须定义

        APP_START_ADDRESS                        用户程序启动地址

        VTABLE_START_ADDRESS                 用户程序向量表起始地址

        FLASH_PAGE_SIZE                               Flash页大小,TI的目前为止都为1K

        STACK_SIZE                                           堆栈大小

3. 当选择了以太网升级后,以下必须定义

        CRYSTAL_FREQ                                     目标板晶振频率


使用特权

评论回复
板凳
wakayi|  楼主 | 2018-9-15 22:06 | 只看该作者
三.bootloader启动代码分析

          不少人不喜欢分析汇编文件,甚至总想绕过汇编。网络上也出现一些人教导初学者学习单片机的时候直接用C语言编程,避开汇编。我个人是极其不同意这种“速成”方法的。作为一名合格的嵌入式工程师或者说爱好者,汇编绝不可回避。汇编能帮助理解硬件,特别是CPU结构、存储和寻址等等;现在的嵌入式程序虽然绝大多数是用C编写的,但要想精通C语言,必须具有汇编基础,任何技术都是入门容易,精通难,因此要想深入理解C的指针、数组甚至是变量存储,还非少不了汇编不可;再者,有些地方必须使用汇编,比如一些实时性要求高的模块(不常见),还有就是接下来要说的启动代码。先附源代码。


见楼下源码


使用特权

评论回复
地板
wakayi|  楼主 | 2018-9-15 22:13 | 只看该作者
































476695b9d127eac598.png (60.88 KB )

476695b9d127eac598.png

99465b9d13549d891.png (29.97 KB )

99465b9d13549d891.png

使用特权

评论回复
5
wakayi|  楼主 | 2018-9-15 22:16 | 只看该作者

  • 接上楼 上源代码 见附件 新建文本文档.zip (3.15 KB)
  • 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中红色部分圈出的内容正是为了完成这个转换。在点击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标号处开始执行。





使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

84

主题

4083

帖子

1

粉丝