从0写BootLoader

BootLoader

Bootloader与OTA
  1. 当Flash比较小的时候,程序指令在Flash上,同时也在Flash上运行,这种叫做XIP

    1. XIP解释:

      1. XIP 是 "eXecute In Place"(芯片内执行)的缩写。它是一种技术,允许程序代码直接从非易失性存储器(如 NOR Flash 闪存)中执行,而无需先将代码加载到 RAM(随机存取存储器)中。

      • 原理:CPU 可以直接从存储器中读取指令(取指),然后进行解码和执行。通常用于 NOR Flash,因为它支持快速随机访问,而 NAND Flash 由于读写特性不适合直接执行。 原因:NOR Flash的读取类似与RAM随机读取,而NANDFlash顺序读取。

      • 优点:减少系统启动时间,降低 RAM 使用量,适合资源受限的嵌入式设备。

      • 例子:在嵌入式系统中,XIP 常用于存储引导程序(Bootloader)或固件,直接从 Flash 运行初始化代码。

    2. NAND Flash 和 NOR FLash区别:

      1. 基本结构:

        1. NAND Flash :

          1. 存储单元以串行方式连接,类似 NAND 逻辑门的结构。

          2. 数据按页面(page,通常 2KB-16KB)或块(block,通常 64KB-512KB)为单位访问,不支持字节级随机访问。

          3. 容量大。

        2. NOR Flash:

          1. 存储单元以并行方式连接,类似于传统 RAM 的结构。

          2. 每个存储单元独立寻址,支持字节级(byte-level)随机访问。 、

          3. 内部设计更接近于逻辑门(NOR 门命名来源)

          4. 支持XIP技术

          5. 容量较小。

      2. 使用特性:

        1. NAND Flash:

          1. 顺序访问:数据按页面和块读写,适合大容量存储,不支持 XIP。

          2. 可靠性:位错误率较高,需搭配纠错码(ECC)。

          3. 擦写寿命:通常 1 万到 10 万次,视具体技术(如 SLC、MLC、TLC)而定。

        2. NOR Flash:

          1. 随机访问:支持直接执行代码(XIP,eXecute In Place),无需加载到 RAM。

          2. 可靠性:位错误率低,适合存储关键代码。

          3. 擦写寿命:一般为 10 万次左右。

  2. Flash比较小的单片机APP更新自己是不可能的,在更新APP的过程中会覆盖下载函数和烧写函数,这就没有办法更新。

  3. 单片机的RAM比较大,把Flash的程序copy到RAM上面在运行,这样就可以下载更新APP。

    1. 缺点:

      1. 当烧写到一半,如果烧写中断,板子就变成砖头,必须返回原厂修复。

  4. 在单片机内,Flash上烧写有BootLoader和APP,启动过程如下:

    1. 上电的时候BootLoader先运行;

    2. BootLoader判断发现:Flash上有APP并且无需升级,BootLoader就会启动APP

    3. BootLoader判断发现:Flash上没有APP或者需要升级,BootLoader执行升级操作

    4. 当发现需要升级的时候,从WIFI获取要下载的APP,把它放到RAM上,下载完之后,将某个标志位置一,来标记是否完成,下次启动的时候检查标志位,如果发现不是1,重新去下载,这样开发板就不会变成砖。

BootLoader主要功能:
  1. 初始化硬件:比如设置时钟,初始化内存

  2. 启动内核/APP:从Flash读出内核,存入内存,给内核设置参数,启动内核

  3. 调试作用:在开发产品时需要经常调试内核,使用BootLoader可以方便的更新内核

  4. 当使用SPI外扩一个Flash的时候,它不是XIP设备,不能直接执行,需要靠BootLoader将外部Flash中的指令给copy到RAM中。CPU才能执行到相应的指令。

STM32中的start_up文件
  1. 不能人认为是BootLoader,只能认为是BootLoader的一小段代码

链接地址、加载地址、运行地址,烧录地址

在嵌入式开发(尤其是 STM32 这类 MCU)中,链接地址(Link Address)、烧录地址(Flash Address)、运行地址(Run Address)、加载地址(Load Address) 这四个地址有着不同的含义,它们之间的关系如下:


1. 各地址的定义
  1. 链接地址(Link Address)

    • 链接器(Linker)在链接阶段确定的地址,即编译生成的 ELF/HEX 文件中的代码和数据的逻辑地址

    • 这个地址决定了代码在程序执行时应该在哪个地址运行。

    • 这个地址可以在链接脚本(.ld 文件)*或*链接选项中人为设定。

  2. 烧录地址(Flash Address)

    • 代码最终被烧录到 MCU 的存储器(通常是 Flash)中的物理地址。

    • 这个地址一般和 MCU 的 Flash 地址范围一致,比如 STM32F103 的 Flash 从 0x08000000 开始。

    • 这个地址是 MCU 启动时 CPU 读取指令的首地址,通常由烧录工具(如 ST-Link Utility)决定。

  3. 运行地址(Run Address)

    • 代码执行时,程序在RAM 或 Flash 中实际运行的地址。

    • 在某些情况下,程序会从 Flash 拷贝到 RAM 运行(如 IAP、XIP),则运行地址 ≠ 烧录地址

    • 但在普通 STM32 应用中,代码直接从 Flash 运行,运行地址 = 烧录地址

  4. 加载地址(Load Address)

    • 二进制文件(如 .bin.hex)中记录的代码加载的起始地址,通常和烧录地址相同。

    • 该地址告诉烧录工具:程序应该烧录到 Flash 的哪个位置。


2. 地址之间的关系
  • 一般情况(代码直接在 Flash 运行,典型的 STM32 项目)

    • 链接地址 = 运行地址 = 烧录地址 = 加载地址

    • 例如:

      Link Address  = 0x08000000
      Load Address  = 0x08000000
      Run Address   = 0x08000000
      Flash Address = 0x08000000
    • 这意味着代码在 0x08000000 运行,也从 0x08000000 烧录到 Flash。

  • 特殊情况(如 Bootloader + 应用程序的方式)

    • Bootloader 运行在

      0x08000000

      ,应用程序从

      0x08004000

      开始:

      Bootloader:
        Link Address  = 0x08000000
        Load Address  = 0x08000000
        Run Address   = 0x08000000
        Flash Address = 0x08000000
      ​
      应用程序:
        Link Address  = 0x08004000
        Load Address  = 0x08004000
        Run Address   = 0x08004000
        Flash Address = 0x08004000
    • 这里 0x08004000 是人为设定的,告诉 Bootloader 应用程序要从这个位置启动。

  • 特殊情况(如代码拷贝到 RAM 运行)

    • 代码烧录在 Flash,但运行时拷贝到 RAM,例如:

      Link Address  = 0x20000000  (RAM)
      Load Address  = 0x08000000  (Flash)
      Run Address   = 0x20000000  (RAM)
      Flash Address = 0x08000000  (Flash)
    • 这里 0x20000000 是 STM32 的 SRAM 地址,代码在 Flash 中存储,但在启动时拷贝到 RAM 运行,提高执行速度。


3. 哪些地址可以人为改变?
  • 可变的地址

    • 链接地址:可以在 .ld 链接脚本或编译选项里改(如修改 FLASH (rx) : ORIGIN = 0x08004000)。

    • 运行地址:可以通过软件控制,如 Bootloader 解析程序后跳转到某个地址执行。

    • 烧录地址:可以用烧录工具(如 STM32CubeProgrammer)设定程序烧录到不同 Flash 地址。

    • 加载地址:一般等于烧录地址,但如果是二次加载(如 Bootloader 先加载),可以人为控制。

  • 等价关系(默认情况)

    • 运行地址 = 链接地址(如果代码不拷贝到 RAM 运行)

    • 加载地址 = 烧录地址(默认情况下一致)


4. 结论
  1. 大多数情况下(代码直接运行在 Flash)

    • 链接地址 = 运行地址 = 烧录地址 = 加载地址

  2. 如果程序被 Bootloader 加载(如 IAP),运行地址可以改变

    • 运行地址 ≠ 烧录地址

  3. 如果代码从 Flash 拷贝到 RAM 运行,运行地址也不同

    • 运行地址 ≠ 烧录地址

你现在是要做 Bootloader 方案,还是要运行代码到 RAM,还是有别的应用场景?

不能将链接地址和加载地址理解为就是程序的烧录位置:
  1. 烧录通常指的是将程序写入非易失行存储器的过程;

  2. 链接地址:

    1. 链接地址不一定是烧录位置,而是程序设计时假设的运行位置。

    2. 如果程序需要直接在烧录位置运行(如 NOR Flash 的 XIP),链接地址通常与烧录位置一致。

    3. 如果程序烧录后需要加载到其他地方运行(如 NAND Flash 加载到 RAM),链接地址可能是 RAM 的地址,与烧录位置无关。

    4. 例子:

      1. 程序烧录到 NOR Flash(0x08000000),支持 XIP,链接地址也是 0x08000000。

      2. 程序烧录到 NAND Flash(0x80000000),但运行在 RAM(0x20000000),链接地址是 0x20000000。

  3. 加载地址:

    1. 对于直接运行的程序(如 XIP),加载地址通常就是烧录位置。

    2. 对于需要加载的程序(如从 NAND Flash 加载到 RAM),加载地址是程序被放入 RAM 的地址,与烧录位置不同。

    3. 例子:

      1. NOR Flash XIP:烧录到 0x08000000,加载地址也是 0x08000000。

      2. NAND Flash:烧录到 0x80000000,加载到 RAM(0x20000000),加载地址是 0x20000000。

函数地址的设定:
  1. 函数地址的bit0 = 1表示的是Thumb(16位的)指令集,bit0 = 0表示使用的是ARM指令集(32位的)

Cortex-M3,M4上电之后的流程:
  1. 上电之后从异常向量表里取出第一个数值,然会把数值赋值给SP寄存器,然后以第2个数值为指令地址,跳转,这是硬件来完成的。

当使用BootLoader时上电的启动流程;
  1. BootLoader这段的程序的启动流程,还是由硬件来完成,但是APP程序就不是硬件来完成。

  2. 需要BootLoader来帮助APP完成启动流程:

    1. _Vectors    DCD 0   //地址为addressA
                  DCD Reset_Handler   //表示的是Reset_Handler这个函数的地址
    2. 取出栈的初始值赋给SP寄存器,也就是DCD 0位置上(addressA)的值赋值给SP

    3. 然后addressA的值+4到下一条指令;

    4. 把Reset_Handler函数的地址赋值给PC。

BootLoader在APP有向量表时的流程:
  1. 重新设置vector地址

  2. 从新的vector中取出第一个数值,也就是栈的首地址,赋值给SP

  3. 从新的vector中取出第二个数值,也就是函数Reset_Handler函数的地址,赋值给PC

start_app       PROC
                EXPORT  start_app
                ;设置Vector的偏移地址根据寄存器 0xE000ED08
                ldr r3,=0xE000ED08          ;将地址0xe000ed08写入到r3中
                str r0,[r3];                ;将r0的值写入r3所指的位置 r0 = 0x08040000 向量表的地址
                ;设置新的vector值
                
                ldr sp,[r0]                 ;取出r0地址上的值 sp = *r0
                ldr r1,[r0,#4]              ;r1 = *[r0+4],得到Reset_Handle函数的地址
                bx r1
                ENDP
BootLoader分类:
  1. APP烧写在Flash上,APP也在Flash上直接运行,BootLoader直接跳到APP位置即可

  2. APP烧写在Flash上,APP应该在RAM中去运行,但是APP会把自己复制到RAM,BootLoader直接跳到APP位置即可:

    1. 自己复制自己,根据自己设置的链接地址,把自己的程序从存储地址放到指定的链接地址处,APP会被拆分为只读段(代码段),可读可写段(数据段)。

  3. APP烧写在Flash上,APP应该在RAM里运行,BootLoader把APP复制到RAM,再跳到RAM里执行APP

  4. 修改异常向量的地址:两种方法

    1. 方法1:硬件支持修改vector地址

    2. 方法2:硬件不支持,需要进行跳转

散列文件:
; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
​
LR_IROM1 0x20000000 0x00010000  {    ; load region size_region
  ER_IROM1 0x20000000 0x00010000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
   .ANY (+XO)
   .ANY (+RW +ZI)
  }
}
  1. LR_IROM1:描述了程序的加载地址

  2. ER_IROM1:描述了程序确切的运行地址

  3. *.o (RESET, +First):将所有 .o 目标文件的包含 RESET(复位向量表),确保复位处理程序排在第一位,确保正确的启动。

  4. *(InRoot$$Sections):确保某些关键代码(如 C 运行时初始化代码)放在根段(Root Section),避免被优化掉。

  5. .ANY (+RO):所有只读段(如 代码、常量)都放在 0x20000000 RAM 里。

  6. .ANY (+XO):可执行代码(通常是 code 段)放在 0x20000000

  7. .ANY (+RW +ZI): 所有可读写数据(初始化全局变量)放在 0x20000000。所有没有初始化的全局变量(bss段)也放在0x20000000

BootLoader、APP烧写在Flash上,但是APP应该在RAM中去运行:
  1. 例如APP烧写的位置为0x08040000但是我们使用了散列文件,将加载地址和运行地址设置为了0x20000000,这个时候会出现问题,当MCU上电的时候,会从0x20000000的位置上,开始执行,但是这个位置上没有正确的指令或数据,导致程序崩溃或者无法启动,解决方法:

    1. APP自己把0x08040000位置上的代码给复制到0x20000000上去。如果没有 Bootloader,通常需要在程序中编写代码,手动将 Flash 中的程序复制到 0x20000000 地址(RAM),然后跳转到该地址开始执行。

    2. BootLoader来帮助APP进行复制操作,Bootloader 会在上电时运行,负责从 Flash 加载程序到 RAM 中,然后跳转到 RAM 地址进行执行。

  2. 适用场景:

    1. RAM比较大,Flash比较小,希望APP尽快的运行,所以要把APP复制到RAM上

    2. SPI Flash和SD卡不是XIP设备,不能直接运行,需要拷贝到RAM上运行,比如IMX6ULL的SD卡启动和eMMC启动方式,eMMC是对NANDFlash的封装

BootLoader、APP烧写在Flash上,但是APP应该在RAM中去运行,自我复制的代码:
__Vectors   DCD 0x20000000+0x10000
            DCD 0x08040009              ;Reset_Handler
Reset_Handler   PROC
                EXPORT  Reset_Handler             [WEAK]
                IMPORT  mymain
                IMPORT copy_myself
                IMPORT |Image$$ER_IROM1$$Length|
                
                
                adr r0,Reset_Handler                ;r0 = 0x08040000
                bic r0,r0,#0xff
                ldr r1,=__Vectors               ;r1 = 0x20000000
                ldr r2,= |Image$$ER_IROM1$$Length|
                BL copy_myself
                ldr pc, =mymain
                ;BL mymain
​
                ENDP
                
                
void copy_myself(int *from,int *to,int len)
{
    //从哪里拷贝,拷贝到什么地方,拷贝多大//
    int i;
    for(i = 0;i<len/4+1;i++)
    {
        to[i] = from[i];
    }
}
  1. 使用了BL来进行跳转,进行的是相对跳转,还是在Flash里面运行,还有一个原因是Reset_Handler地址还是被设置为了程序在Flash里面的地址。

  2. ldr pc, =mymain改为这个语句的时候,就会跳转到RAM里面执行,因为这里使用了绝对地址,并且链接地址在0x20000000的位置

BootLoader、APP烧写在外部的Flash上,Boot Loader来帮助APP复制到RAM中
  1. 因为外部的Flash不具有XIP功能,所以APP没有办法进行自己把自己copy到RAM里面,因此需要BootLoader来帮助完成

  2. 我么一般在APP程序的前面加入一个头部,这个头部包含:、

    1. 加载地址,程序加载到RAM的地址

    2. 入口地址,第一条指令运行的地址

    3. APP的长度

    4. CRC校验码

  3. 我们使用mkimage来制作带头部的APP.bin文件,加入这个文件烧写在0x08040000这个位置:

    1. Bootloader读0x08040000这个位置得到头部

    2. 解析头部

    3. 读APP.bin文件写入RAM里面

    4. 执行第一条命令

    5. 主要代码:

    6. ​
      start_app       PROC
                      EXPORT  start_app
                      ;设置Vector的地址根据寄存器 VTOR寄存器:0xe000ed0c
                      ldr r3,=0xE000ED08          ;将地址0xe000ed0c写入到r3中
                      str r0,[r3];                ;将r0的值写入r3所指的位置
                      ;设置新的vector值
                      
                      ldr sp,[r0]                 ;取出r0地址上的值 sp = *r0
                      ldr r1,[r0,#4]              ;r1 = *[r0+4],得到Reset_Handle函数的地址
                      bx r1
                      ENDP
      ​
      #include "uart.h"
      ​
      typedef unsigned int  __be32;
      typedef unsigned char uint8_t;
      ​
      #define IH_MAGIC        0x27051956
      #define IH_NMLEN        32
      ​
      //#define be32_to_cpu(x)  ((((x) & 0xFF000000) >> 24)  |  \
      //                        (((x) & 0x00FF0000) >> 8)    |  \
      //                        (((x) & 0x0000FF00) << 8)    |  \
      //                        (((x) & 0x000000FF) << 24))
      ​
      unsigned int be32_to_cpu(unsigned int x)
      {
          unsigned char *p = (unsigned char *)&x;
          unsigned int le;
          //将原来在低地址的高字节段移动到高地址
          le = (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + (p[3]);
          return le;
      }
      ​
      ​
      typedef struct image_header {
          __be32  ih_magic;           /* Image Header Magic Number    */
          __be32  ih_hcrc;            /* Image Header CRC Checksum    */
          __be32  ih_time;            /* Image Creation Timestamp */
          __be32  ih_size;            /* Image Data Size      */
          __be32  ih_load;            /* Data  Load  Address      */
          __be32  ih_ep;              /* Entry Point Address      */
          __be32  ih_dcrc;            /* Image Data CRC Checksum  */
          uint8_t ih_os;              /* Operating System     */
          uint8_t ih_arch;            /* CPU architecture     */
          uint8_t ih_type;            /* Image Type           */
          uint8_t ih_comp;            /* Compression Type     */
          uint8_t ih_name[IH_NMLEN];  /* Image Name       */
      } image_header_t;
      ​
      extern void start_app(unsigned int);
      ​
      void delay(int d)
      {
          while(d--);
      }
      ​
      void copy_myself(int *from,int *to,int len)
      {
          //从哪里拷贝,拷贝到什么地方,拷贝多大//
          int i;
          for(i = 0;i<len/4+1;i++)
          {
              to[i] = from[i];
          }
      }
      ​
      ​
      ​
      void relocate_and_start_app(unsigned int pos)
      {
          image_header_t *head;
          unsigned int load;
          unsigned int size;
          unsigned int new_add;
          /*读出头部*/
          head = (image_header_t *)pos;
          load = be32_to_cpu(head->ih_load);
          size = be32_to_cpu(head->ih_size);
          putstr("load = ");
          puthex(load);
          putstr("\r\n");
          
          putstr("size = ");
          puthex(size);
          putstr("\r\n");
          new_add = pos+sizeof(image_header_t);
          
          /*把程序复制到内存*/
          copy_myself((int *)new_add,(int *)load,size);
          
          /*跳转执行*/
          start_app(new_add);
      }
      ​
      ​
      int mymain()
      {
          unsigned int app_pos = 0x08040000;    //获取带头节点的.bin文件的地址
      ​
          uart_init();
      ​
          putstr("bootloader\r\n");
          relocate_and_start_app(app_pos);
      ​
          return 0;
      }
      ​

BootLoader中的异常向量表:
  1. 如果APP没有就绪,发生异常,执行BootLoader的异常处理函数,也就是当前运行的是BootLoader

  2. 如APP已经运行,如果处理器不能重新设置异常向量表的位置,那么就还是使用BootLoader的异常函数,但是在BootlLoaader的异常函数里面,要设置跳转去执行APP对应函数。

在内存中调试程序:
  1. 修改链接脚本(散列文件)把FreeRTOS程序的链接地址指定为RAM

  2. 配置工程使用散列文件

  3. 散列文件内容:

    1. LR_IROM1 0x20000000 0x00010000  {    ; load region size_region
        ER_IROM1 0x20000000 0x00010000  {  ; load address = execution address
         *.o (RESET, +First)
         *(InRoot$$Sections)
         .ANY (+RO)
         .ANY (+XO)
         .ANY (+RW +ZI)
        }
      }
  4. 设置初始文件

    1. FUNC void Setup(void){
          sp = _RDWORD(0x20000000);
          pc = _RDWORD(0x20000004);
          __WDWORD(0xE0000ED08,0x20000000);
      }
      LOAD F103_Module\F103_Module.axf INCREMENTAL
      Setup();
      g,main
          

  5. 设置Debug Pack

第四个BootLoader问题:
  1. static struct vectors* new_vector;
    //反汇编文件为:
    address     size       variable name                            type
    0x20000000  0x4        new_vector                               pointer to vectors
    ​
    /*把程序复制到内存*/
    copy_myself((int *)new_add,(int *)load,size);
    ​
    /*跳转执行*/
    set_new_vector(new_add);
    start_app(new_add);
    1. 这就存在问题,这个变量的值存在RAM里面,并且和APP在RAM上的位置相同,当使用set_new_vector这个函数的时候会破坏先前在RAM上拷贝好的程序,这就会导致程序执行失败。

    2. 第四个BootLoader主要针对的是没有0xE000ED08这个寄存器的处理器。

    3. FreeRTOS中的问题:

      1. APP发生异常的时候硬件帮我们保存一部分的寄存器,让LR = EXC_RETURN这个特殊值。

      2. 当BX LR的时候这个特殊值会根据PSP或者MSP从中恢复寄存器。

          1. bit3 = 1表示返回线程模式,说明

            1. bit2 = 1 表示返回线程栈,第一个任务的时候伪造了线程栈,说明任务切换完成。

      3. 根据BootLoader中的代码和FreeRTOS中的代码

        1. void  SVC_Handler(void)
          {
              /*跳转去执行APP的SVC_Handler*/
              //new_vector->SVC_Handler();
              struct vectors* new_vector = (struct vectors*)(*((unsigned int*)RAM_END));
               new_vector->SVC_Handler();
          }
          ​
          __asm void vPortSVCHandler( void )
          {
              PRESERVE8
          ​
              ldr r3, =pxCurrentTCB   /* Restore the context. */
              ldr r1, [r3]            /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */
              ldr r0, [r1]            /* The first item in pxCurrentTCB is the task top of stack. */
              ldmia r0!, {r4-r11}     /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */
              msr psp, r0             /* Restore the task stack pointer. */
              isb
              mov r0, #0
              msr basepri, r0
              orr r14, #0xd
              bx r14
          }
          1. 当发生异常的时候先到BootLoader中,去执行BootLoader的异常处理函数,再去跳到APP中去执行他的异常函数,但是vPortSVCHandler这个异常函数进行任务的切换的,需要LR是一个特殊值,这个特殊值产生在异常发生的时候,但是我们是从BootLoader中跳过来执行的所以LR已经不是一个特殊值,因此程序执行到这里就会崩溃。

          2. 解决方法:

            1. SVC_Handler     PROC
                              EXPORT  SVC_Handler
                              ldr r0, =(0x20000000+0x10000-4)
                              ldr r0, [r0]    ;r0 = new vector
                              ldr r0, [r0,#0x2c]    ;r0 = (APP)SVC_Handler
                              bx r0
                              ENDP
              1. 使用汇编来完成SVC_Handler这个函数,如果用C语言去跳转到APP去执行,会使用BLX指令来跳转,会改变LR的值,我们使用BX指令来进行跳转的话就不会修改LR的值了

      4. FreeRTOS源码中

        __asm void prvStartFirstTask( void )
        {
            PRESERVE8
        ​
            /* Use the NVIC offset register to locate the stack. */
            //ldr r0, =0xE000ED08
            //ldr r0, [r0]    //r0 = vector address
            //ldr r0, [r0]    //r0 = first item of vector
            ldr r0 ,=0x20000000
            ldr r0, [r0]
            /* Set the msp back to the start of the stack. */
            msr msp, r0
            /* Globally enable interrupts. */
            cpsie i
            cpsie f
            dsb
            isb
            /* Call SVC to start the first task. */
            svc 0
            nop
            nop
        }

        使用了0xE000ED08这个寄存器,

命令系统:
  1. 执行help:列出所有命令的简短用法

  2. 执行help cmd:列出命令cmd的详细用法

  3. 执行 cmd

  4. 定义命令结构体:

    struct command{
        char *short_help;
        char *long_help;
        int (*function)(int argc,char*argv[]);
    };
  5. 管理命令:

    struct command *g_commands[] = {
        &help_cmd,
        &load_cmd,
        &md_cmd,
        &mw_cmd,
        &flash_cmd,
    };
  6. 执行命令:

    1. 在shell里接下字符串,得到第一个字符
    2. 根据第一个字符在g_commands找到某项
    3. 构造一系列参数(argc,argv),执行这项的function函数
实现zmodem下载命令:
  1. 单片机通过串口和PC机相连接,通过程序Mobuxterm发送文件给开发板

分析rz命令:
rz -b  binary transfer
rz -z  use ZMODEM protocol
rz -b -z 发送一个二进制文件到开发板
分析源码:
main()
{
Rxbinary=TRUE
protocol=ZM_ZMODEM
/* initialize zsendline tab */
zsendline_init();
readline_setup(0, HOWMANY, MAX_BLOCK*2);
if (wcreceive(npats, patts)==ERROR) {
        exitcode=0200;
        canit(STDOUT_FILENO);
    }
}
//核心函数wcreceive
BootLoader的散列文件:
  1. 使用到了全局变量,全局变量的的初始值被放到Flash里面,当程序运行的时候把初始值给拷贝到RAM里面,因此要进行重定位。

  2. 如果没有使用这个重定位的话,会有这个情况:比如int rw = 10;这个全局变量在程序开始运行的时候放到了0x08000100,但程序运行时试图修改它,会失败(Flash 只读)。程序崩溃。

  3. ZI段如果不进行清零的话,可能会导致随机值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值