STM32 IAP固件升级(一)

章节说明

STM32 IAP固件升级实验分为一下的章节(加粗的字体是本章节的内容):

一、Flash和RAM的区域划分、工程建立、程序分散加载、程序烧写
二、Stm32 bootloader、application、firmware 程序的分析和编写
三、使用DMA收发串口的不定长数据
四、通信协议的设计
五、STM32 IAP程序的设计
六、上位机的程序的编写

一、Flash区域的划分

1.区域划分

  • 实验使用的是 STM32F103VET6型号的MCU。这个单片机的型号的内部flash的大小是512Kb, RAM的大小是64Kb。
  • 因为一般使用的都是flash的启动方式(即flash 的起始地址0x0800 0000被映射到 0x0000 0000。STM32的启动方式可以查看《STM32中文参考手册》) 所以flash的区域划分如下:

2. flash 分区说明

  • 0x0800 00000x0800 b7FF 地址的flash块划分给bootloader,大小是46kb
  • 0x0800 B8000x0800 BFFF 的flash块划分为参数表(parameters),大小是2Kb
  • 0x0800 C0000x0804 3FFF 的flash块划分为应用程序区 (application),大小是224Kb
  • 0x0804 40000x0807 BFFF 的flash块划分为程序升级区 (update region),大小跟应用程序区一样 224kb
  • 0x0807 C0000x0807 FFFF 的flash块划分为固件区(firmware),大小是16Kb

3. 每个分区作用

  • bootloader 启动加载程序,主要用来加载和启动应用程序,还有更新应用程序
  • parameters 主要用来记录信息,bootloader 和 application 的信息交互
  • application 应用程序区,主要用来实现所需要的业务的程序
  • update region 程序更新区,主要存放更新下载的程序,当然也可以直接下载到 application 区,但是如果更新失败了,应用程序就不能执行了。所以单独划分一块区域,存放更新下载的序,更新完后再覆盖到application然后复位运行
  • firmware 固件区,主要存放一些函数接口,用户可以通过函数指针直接调用。像tiny-4412、smart210、esp32等这些板子的芯片在irom里面都提供了一些函数接口,通过地址可以直接调用这些函数。

二、RAM内存的划分

1、区域划分

RAM(内存)的起始地址是 0x2000 0000,大小是64Kb。在实验主要将它划分为两块。一块是从 0x2000 00000x2000 FBFF 大小是63Kb。另一块是从 0x2000 FC000x2001 0000 大小是 1Kb,划分如下图

2、区域的作用

  • 63Kb这块内存区域主要是用于 bootloaderapplication 应用程序的可读可写段(RW),未初始化或者初始化为0的数据段(ZI)、堆空间、栈空间
  • 1kb这块内存区域主要是用于 firmware 区域的可读可写段(RW),未初始化或者初始化为0的数据段(ZI)
  • 注意:firmware因为没有初始化堆,所以不能使用malloc这类堆内存管理函数。

3、简单说明

  • 因为STM32的flash 是norflash 读的时候可以像内存一样读,但是写的时候不能像内存那样子写,所以这些 RW 段 ZI 段都是加载到内存里。加载到哪里,这是由分散加载文件.sct文件(和链接脚本一个意思)里面说明。至于怎么加载 ,stm32 函数库的start_up.s文件的__main 到 main函数的过程就是加载数据到RAM空间。后面再介绍.sct文件

三、工程建立

工程中需要三个project,分别是 bootloader application firmware 如下图:

四、分散加载

因为程序存放在不同的区域了,并且每段区域的程序是独立的,所以在链接每个程序的时候需要告诉连接器,程序怎么放,程序加载启动的时候又应该怎么加载,堆区和栈区的位置这些都要告诉链接器。我们可以通过分散加载文件.sct 文件来告诉链接器。下面来看一下这个文件

1.原来的分散加载文件

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
;LR_IROM1 段名/域名
;0x08000000 程序加载的起始地址
;0x00080000 加载域的大小
LR_IROM1 0x08000000 0x00080000  {    ; load region size_region
  ;ER_IROM1 执行域的名称  
  ;0x08000000 程序的执行起始地址
  ;0x00080000 执行域的大小
  ;一般程序是在 flash 里面运行的所以 程序的执行地址等于加载地址,如果想要让程序运行在RAM中,执行地址修改为RAM的地址
  ER_IROM1 0x08000000 0x00080000  {  ; load address = execution address
   ;下面就是指定某些段怎么链接了
   *.o (RESET, +First)   ;链接的时候最先存放的是 所有.o文件的RESET段,紧跟的是First段(RESET段在start_up.s文件中有定义)
   *(InRoot$$Sections)   ;然后存放 __main 到 main的这段代码,这段代码主要是将flash的RW和ZI段加载到RAM中
   .ANY (+RO)            ;紧跟着的是所有文件的只读数据(RO段),一般是向量表、代码段和常量(字符串常量,和const修饰的数据)
  }
;RW_IRAM1 可读可写数据的段名
;0x20000000起始地址  
;0x00010000大小
  RW_IRAM1 0x20000000 0x00010000  {  ; RW data
   .ANY (+RW +ZI);紧接着的是(RW段),(ZI)段
  }
}

Region的名称可以自己定义或者修改,重要的是地址要分配对就可以了。
Heart_of_Eagle这位博主有一篇比较详细的博文值得参考,不过不是MDK的sct文件的,原理都是大同小异
MDK也是有详细的帮助文档,不过是纯英文的,想用好sct可以阅读里面的内容
1.

2.

2.修改bootloader的分散加载文件

根据上面的flash和RAM的划分修改sct加载文件,如下代码块:

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
;因为bootloader是第一个启动的程序,而且机器上电复位的时候是从0x0800 0000开始执行(因为被映射到了0x0000 0000)地址
;所以bootloader加载域的起始地址和执行地址都是0x0800 0000
; 0x0000B800域的大小(46kb)也是通过上面的设计的分区来填写
LR_IROM1 0x08000000 0x0000B800  {    ; load region size_region
  ER_IROM1 0x08000000 0x0000B800  {  ; load address = execution address
   ;里面段的存放位置使用默认的就可以
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
; 0x20000000 这个也是根据上面的RAW的分区填写,
;0x0000FC00 大小是 63Kb
  RW_IRAM1 0x20000000 0x0000FC00  {  ; RW data
   ;段的存放位置使用默认的就可以了
   .ANY (+RW +ZI)
  }
}

3.修改application的分散加载文件

修改如下,跟bootloader一样,就不啰嗦了

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************
LR_IROM1 0x0800C000 0x00038000  {    ; load region size_region
  ER_IROM1 0x0800C000 0x00038000  {  ; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
  }
  RW_IRAM1 0x20000000 0x0000FC00  {  ; RW data    0x0000FC00
   .ANY (+RW +ZI)
  }
}

4.修改firmware的分散加载文件

*(InRoot$$Sections) 被注释掉了,因为 firmware 的程序是没有 __mainmain 函数部分的代码的。下一章节写程序的时候会有讲解。其他的内容也跟bootloader文件差不多,上面的分区来写就可以了

; *************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************

LR_IROM1 0x0807C000 0x00004000  {    ; load region size_region
  ER_IROM1 0x0807C000 0x00004000  {  ; load address = execution address
   *.o (RESET, +First)
   ;*(InRoot$$Sections) 
   .ANY (+RO)
  }
  RW_IRAM1 0x2000FC00 0x00000400  {  ; RW data  
   .ANY (+RW +ZI)
  }
}

5. 注意事项

在MDK的默认设置里,链接的时候是不使用分散加载文件的 xxx.sct 文件。要使用 xxx.sct 文件需要将下面的设置去掉

五、程序的下载设置

  • 由于本章还没实现IAP这部分的程序,所以 bootloaderapplicationfirmware的程序都是使用烧写器来烧写到falsh的不同区域。所以需要设置一下。
  • 设置的地址跟大小也是根据上面的flash分区来设置的。
  • 主要设置MDK里面的 Debug -> Flash Download 的这两个地方,设置完这里就可以用Jlink将不同的程序烧写到不同的区域了。
  • 下图是 bootloader 的设置,其他两个也是根据实际来设置

六、实验现象

说了这么多先来个实验现象吧, 对于下面的程序分析和编写就下一章再写了。
工程文件已经上传到码云---->点我project!!!
代码可能存在问题,有问题可以留言讨论。

  1. hello bootloader 是在bootloader运行的时候打印出来的,打印完后就跳到应用程序执行了
  2. num = 10num1 = 0 这两个值是通过函数指针调用firmware固件区的函数接口来获取的
  3. 打印完上面的两个值之后循环打印hello app
    现象如下图:
posted @ 2020-01-18 17:37  古澜  阅读(10773)  评论(0编辑  收藏  举报