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 0000
到0x0800 b7FF
地址的flash块划分给bootloader,大小是46kb0x0800 B800
到0x0800 BFFF
的flash块划分为参数表(parameters),大小是2Kb0x0800 C000
到0x0804 3FFF
的flash块划分为应用程序区 (application),大小是224Kb0x0804 4000
到0x0807 BFFF
的flash块划分为程序升级区 (update region),大小跟应用程序区一样 224kb0x0807 C000
到0x0807 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 0000
到0x2000 FBFF
大小是63Kb。另一块是从0x2000 FC00
到0x2001 0000
大小是 1Kb,划分如下图
2、区域的作用
- 63Kb这块内存区域主要是用于
bootloader
和application
应用程序的可读可写段(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
的程序是没有__main
到main
函数部分的代码的。下一章节写程序的时候会有讲解。其他的内容也跟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这部分的程序,所以
bootloader
、application
和firmware
的程序都是使用烧写器来烧写到falsh的不同区域。所以需要设置一下。- 设置的地址跟大小也是根据上面的flash分区来设置的。
- 主要设置MDK里面的
Debug -> Flash Download
的这两个地方,设置完这里就可以用Jlink将不同的程序烧写到不同的区域了。- 下图是
bootloader
的设置,其他两个也是根据实际来设置
六、实验现象
说了这么多先来个实验现象吧, 对于下面的程序分析和编写就下一章再写了。
工程文件已经上传到码云---->点我project!!!
代码可能存在问题,有问题可以留言讨论。
hello bootloader
是在bootloader运行的时候打印出来的,打印完后就跳到应用程序执行了num = 10
和num1 = 0
这两个值是通过函数指针调用firmware固件区的函数接口来获取的- 打印完上面的两个值之后循环打印
hello app
现象如下图: