一、请说明STM32的三种Boot模式的差异,并研究至少两种boot模式下,代码下载(烧录)运行后所在的地址位置,与理论对比验证。
1.什么是 Boot 模式
STM32 的 BOOT 管脚有什么用
在 STM32 的板子上,可以看到有 BOOT0 、BOOT1 这两个管脚,刚买的板子,用短路帽将 BOOT0、BOOT1都接地的,也就是 BOOT0 和 BOOT1 的电平都置为 0,用短路帽将 BOOT 连到 3V3 即可置 1。
BOOT 拥有三种启动模式,如下图所示:
所以说,平时呢我们都是默认使用的是主闪存存储器,将 BOOT 接不通的电平,可以选择不同的启动模式,这两个管脚在芯片复位时的电平状态决定了芯片复位后从哪个区域开始执行程序。
这三种启动模式是什么意思
所谓STM32的启动过程,一般来说就是指我们下好程序后,重启芯片时,SYSCLK(系统时钟)的第 4 个上升沿,BOOT 引脚的值将被锁存,也就是将两个 BOOT 的电平值( 0 或 1)存储起来,用户可以通过设置 BOOT1 和 BOOT0 引脚的状态,来选择在复位后的启动模式。
主闪存存储器
STM32 内置的 Flash,一般我们使用 JTAG 或者 SWD 模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。
系统存储器
从系统存储器启动,这种模式启动的程序功能是由厂家设置的。一般来说,这种启动方式用的比较少,系统存储器是芯片内部一块特定的区域,STM32 在出厂时,由 ST 在这个区域内部预置了一段 BootLoader, 也就是我们常说的 ISP 程序, 这是一块ROM,出厂后无法修改,一般来说,我们选用这种启动模式时,是为了从串口下载程序,因为在厂家提供的 BootLoader 中,提供了串口下载程序的固件,可以通过这个 BootLoader 将程序下载到系统的 Flash 中。但是这个下载方式需要以下步骤:
- 将 BOOT0 设置为 1,BOOT1 设置为 0,然后按下复位键,这样才能从系统存储器启动 BootLoader
- 最后在 BootLoader 的帮助下,通过串口下载程序到 Flash 中
- 程序下载完成后,又有需要将 BOOT0 设置为 GND,手动复位,这样,STM32 才可以从 Flash 中启动可以看到。
内置 SRAM
内置 SRAM,既然是 SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试,假如只修改了代码中一个小小的地方,然后就需要重新擦除整个 Flash,比较的费时,可以考虑从这个模式启动代码(也就是 STM32 的内存中),用于快速的程序调试,等程序调试完成后,在将程序下载到 SRAM 中。
这三种模式的本质区别是什么
三种模式的存储地址在哪
- 主闪存存储器——访问地址为 0x 0000 0000 或 0x 0800 0000
- 系统存储器——访问地址为 0x 0000 0000 或 0x 1FF0 0000
- 内置SRAM——启动时地址为 0x 0000 0000 或 0x 2000 0000
2.实例验证
主闪存存储器
首先置 BOOT1、BOOT0 均为 0 ,再烧录程序,串口调试助手马上有了显示结果,如下
主闪存存储器的访问地址是从 0x 0800 0000 开始的,最小的地址都没有低于 0x 0800 0000
系统存储器
首先置 BOOT1 为 0 、BOOT0 为 1,再烧录程序,烧录完后,串口调试助手没有反应,这时再置 BOOT1 为 0、BOOT0 为 0,并按 RESET 复位键,串口调试助手有了显示结果,如下
显示结果的地址相同
在验证过程中主闪存存储器烧录程序后立马就显示数据,系统存储器烧录程序后,需置 BOOT0 为 0,然后按复位显示数据。出现这种现象的原因是主闪存存储器hex 文件直接储存到 Flash 上,烧录后,程序直接就运行了,开发板和上位机就直接可以进行通信,系统存储器将 BOOT0 设置为 1,BOOT1 设置为 0,然后按下复位键,这样才能从系统存储器启动 BootLoader,在 BootLoader 的帮助下,通过串口下载程序到 Flash 中,程序下载完成后,又有需要将 BOOT0 设置为 GND,手动复位,这样,STM32 才可以从 Flash 中启动可以看到(也就是上面的三个步骤)。
二、基于 MDK 创建 STM32 汇编语言工程并分析 HEX 文件内容
1.新建工程
点击 Project ——> New uVision Project 创建新项目
输入工程名并保存
选择 STM32F103VE 芯片,然后点击 OK
ARM 的 CMSIS 已经把开发所需要的软件组件都封装好了,因此直接选择即可—— CMSIS 下选择 CORE;Device 下 Startup
点击下方 OK 即可
右击 Source Group 1 ,点击 Add New Item to Group ‘Source Group 1’…
点击 Asm Files (.s) 添加汇编文件,然后输入文件名,并点击 Add
添加完成后如下
2.编写代码并编译烧录
在 Test.s 中添加如下的汇编代码
AREA MYDATA, DATA
AREA MYCODE, CODE
ENTRY
EXPORT __main
__main
MOV R0, #10
MOV R1, #11
MOV R2, #12
MOV R3, #13
;LDR R0, =func01
BL func01
;LDR R1, =func02
BL func02
BL func03
LDR LR, =func01
LDR PC, =func03
B .
func01
MOV R5, #05
BX LR
func02
MOV R6, #06
BX LR
func03
MOV R7, #07
MOV R8, #08
BX LR
点击 魔法棒 ,在 Output 界面下,勾选 Create HEX File ,才能生成 hex 文件
在 Debug 界面下,选择 ST-Link Debugger ,具体根据自己的实际情况进行选择
然后点击 ST-Link Debugger 旁边的 Settings ,设置端口为 SW,根据自己的情况设置,设置完毕后会出现一个设备
然后点击 Flash Download ,点击 Add ,选中 STM32F10x High-density Flash ,然后点击 Add 即可添加完成
最后点击 确定 ,然后 OK 保存设置
硬件连接
使用 USB 线连上 USB 转串口,使用杜邦线连接 SWD
看板子的背面,这里我们只需要用杜邦线依次连接 SWCLK、GND、SWDIO、3V3 就行了
NRST:异步复位脚,重置除了 RTC 的寄存器以及后备存储器的寄存器;
SWCLK:时钟开关;
GND:接地;
SWDIO:输入/输出控制。
3V3:电源。
杜邦线的另一端接 ST_LINK ,根据正面提示的引脚来进行接入,如下图
然后将两个 USB 接口插到电脑上,并打开开关,之后可以进行程序的烧录
编译工程
没有错误即可
点击调试按钮
最后可以看到,结果符合预期,寄存器 R5,R6,R7,R8 的值和程序设置一致,具体如下图所示
3.分析生成的 hex 文件
最终生成的 hex 文件的各段大小
用记事本打开 hex 文件,都是一连串的十六进制
1.扩展线性地址记录(hex 文件的第一排十六进制)也叫作 32 位地址记录或 HEX386 记录,这些记录包含数据地址的高 16 位,扩展线性地址记录总是有两个数据字节,外观如下
当一个扩展线性地址记录被读取,存储于数据域的扩展线性地址被保存,它被应用于从 Intel HEX 文件读取来的随后的记录,线性地址保持有效,直到它被另外一个扩展地址记录所改变,通过把记录当中的地址域与被移位的来自扩展线性地址记录的地址数据相加获得数据记录的绝对存储器地址
2.Intel HEX 由任意数量的十六进制记录组成。每个记录包含 5 个域,它们按一定格式排列::llaaaatt[dd…]cc,每一组字母对应一个不同的域,每一个字母对应一个十六进制编码的数字,每一个域由至少两个十六进制编码数字组成,它们构成一个字节,就像以下描述的那样
Intel HEX文件由任意数量以回车换行符结束的数据记录组成,这里我们拿第二条数据记录分析
3.文件尾
在文件的最后一排,是一个文件的结束标志::00000001FF
这个是一个 END OF FILE RECORD,标识文件的结尾
三、在上面Keil 汇编基础上,用汇编程序完成一个输出“Hello world”到串口的程序
1.编写代码
按照上面一样,创建新的工程,不用勾选 CORE 和 Startup
创建完工程后,添加 asm 汇编文件,并添加如下的代码
LED0 EQU 0x422101a0
RCC_APB2ENR EQU 0x40021018
GPIOA_CRH EQU 0x40010804
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
AREA RESET, DATA, READONLY
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
AREA |.text|, CODE, READONLY
THUMB
REQUIRE8
PRESERVE8
ENTRY
Reset_Handler
BL LED_Init
MainLoop BL LED_ON
BL Delay
BL LED_OFF
BL Delay
B MainLoop
LED_Init
PUSH {R0,R1, LR}
LDR R0,=RCC_APB2ENR
ORR R0,R0,#0x04
LDR R1,=RCC_APB2ENR
STR R0,[R1]
LDR R0,=GPIOA_CRH
BIC R0,R0,#0x0F
LDR R1,=GPIOA_CRH
STR R0,[R1]
LDR R0,=GPIOA_CRH
ORR R0,R0,#0x03
LDR R1,=GPIOA_CRH
STR R0,[R1]
MOV R0,#1
LDR R1,=LED0
STR R0,[R1]
POP {R0,R1,PC}
LED_ON
PUSH {R0,R1, LR}
MOV R0,#0
LDR R1,=LED0
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF
PUSH {R0,R1, LR}
MOV R0,#1
LDR R1,=LED0
STR R0,[R1]
POP {R0,R1,PC}
Delay
PUSH {R0,R1, LR}
MOVS R0,#0
MOVS R1,#0
MOVS R2,#0
DelayLoop0
ADDS R0,R0,#1
CMP R0,#330
BCC DelayLoop0
MOVS R0,#0
ADDS R1,R1,#1
CMP R1,#330
BCC DelayLoop0
MOVS R0,#0
MOVS R1,#0
ADDS R2,R2,#1
CMP R2,#15
BCC DelayLoop0
POP {R0,R1,PC}
NOP
END
通过用零或空指令NOP填充,来使当前位置与一个指定的边界对齐 END 硬件连接、仿真器设置
按照上面所示即可
2.结果显示
编译工程
然后下载程序到 STM32 中,并打开串口调试助手,结果如下
一、请说明STM32的三种Boot模式的差异,并研究至少两种boot模式下,代码下载(烧录)运行后所在的地址位置,与理论对比验证。
1.什么是 Boot 模式
STM32 的 BOOT 管脚有什么用
在 STM32 的板子上,可以看到有 BOOT0 、BOOT1 这两个管脚,刚买的板子,用短路帽将 BOOT0、BOOT1都接地的,也就是 BOOT0 和 BOOT1 的电平都置为 0,用短路帽将 BOOT 连到 3V3 即可置 1。
BOOT 拥有三种启动模式,如下图所示:
所以说,平时呢我们都是默认使用的是主闪存存储器,将 BOOT 接不通的电平,可以选择不同的启动模式,这两个管脚在芯片复位时的电平状态决定了芯片复位后从哪个区域开始执行程序。
这三种启动模式是什么意思
所谓STM32的启动过程,一般来说就是指我们下好程序后,重启芯片时,SYSCLK(系统时钟)的第 4 个上升沿,BOOT 引脚的值将被锁存,也就是将两个 BOOT 的电平值( 0 或 1)存储起来,用户可以通过设置 BOOT1 和 BOOT0 引脚的状态,来选择在复位后的启动模式。
主闪存存储器
STM32 内置的 Flash,一般我们使用 JTAG 或者 SWD 模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。
系统存储器
从系统存储器启动,这种模式启动的程序功能是由厂家设置的。一般来说,这种启动方式用的比较少,系统存储器是芯片内部一块特定的区域,STM32 在出厂时,由 ST 在这个区域内部预置了一段 BootLoader, 也就是我们常说的 ISP 程序, 这是一块ROM,出厂后无法修改,一般来说,我们选用这种启动模式时,是为了从串口下载程序,因为在厂家提供的 BootLoader 中,提供了串口下载程序的固件,可以通过这个 BootLoader 将程序下载到系统的 Flash 中。但是这个下载方式需要以下步骤:
- 将 BOOT0 设置为 1,BOOT1 设置为 0,然后按下复位键,这样才能从系统存储器启动 BootLoader
- 最后在 BootLoader 的帮助下,通过串口下载程序到 Flash 中
- 程序下载完成后,又有需要将 BOOT0 设置为 GND,手动复位,这样,STM32 才可以从 Flash 中启动可以看到。
内置 SRAM
内置 SRAM,既然是 SRAM,自然也就没有程序存储的能力了,这个模式一般用于程序调试,假如只修改了代码中一个小小的地方,然后就需要重新擦除整个 Flash,比较的费时,可以考虑从这个模式启动代码(也就是 STM32 的内存中),用于快速的程序调试,等程序调试完成后,在将程序下载到 SRAM 中。
这三种模式的本质区别是什么
三种模式的存储地址在哪
- 主闪存存储器——访问地址为 0x 0000 0000 或 0x 0800 0000
- 系统存储器——访问地址为 0x 0000 0000 或 0x 1FF0 0000
- 内置SRAM——启动时地址为 0x 0000 0000 或 0x 2000 0000
2.实例验证
主闪存存储器
首先置 BOOT1、BOOT0 均为 0 ,再烧录程序,串口调试助手马上有了显示结果,如下
主闪存存储器的访问地址是从 0x 0800 0000 开始的,最小的地址都没有低于 0x 0800 0000
系统存储器
首先置 BOOT1 为 0 、BOOT0 为 1,再烧录程序,烧录完后,串口调试助手没有反应,这时再置 BOOT1 为 0、BOOT0 为 0,并按 RESET 复位键,串口调试助手有了显示结果,如下
显示结果的地址相同
在验证过程中主闪存存储器烧录程序后立马就显示数据,系统存储器烧录程序后,需置 BOOT0 为 0,然后按复位显示数据。出现这种现象的原因是主闪存存储器hex 文件直接储存到 Flash 上,烧录后,程序直接就运行了,开发板和上位机就直接可以进行通信,系统存储器将 BOOT0 设置为 1,BOOT1 设置为 0,然后按下复位键,这样才能从系统存储器启动 BootLoader,在 BootLoader 的帮助下,通过串口下载程序到 Flash 中,程序下载完成后,又有需要将 BOOT0 设置为 GND,手动复位,这样,STM32 才可以从 Flash 中启动可以看到(也就是上面的三个步骤)。
二、基于 MDK 创建 STM32 汇编语言工程并分析 HEX 文件内容
1.新建工程
点击 Project ——> New uVision Project 创建新项目
输入工程名并保存
选择 STM32F103VE 芯片,然后点击 OK
ARM 的 CMSIS 已经把开发所需要的软件组件都封装好了,因此直接选择即可—— CMSIS 下选择 CORE;Device 下 Startup
点击下方 OK 即可
右击 Source Group 1 ,点击 Add New Item to Group ‘Source Group 1’…
点击 Asm Files (.s) 添加汇编文件,然后输入文件名,并点击 Add
添加完成后如下
2.编写代码并编译烧录
在 Test.s 中添加如下的汇编代码
AREA MYDATA, DATA
AREA MYCODE, CODE
ENTRY
EXPORT __main
__main
MOV R0, #10
MOV R1, #11
MOV R2, #12
MOV R3, #13
;LDR R0, =func01
BL func01
;LDR R1, =func02
BL func02
BL func03
LDR LR, =func01
LDR PC, =func03
B .
func01
MOV R5, #05
BX LR
func02
MOV R6, #06
BX LR
func03
MOV R7, #07
MOV R8, #08
BX LR
点击 魔法棒 ,在 Output 界面下,勾选 Create HEX File ,才能生成 hex 文件
在 Debug 界面下,选择 ST-Link Debugger ,具体根据自己的实际情况进行选择
然后点击 ST-Link Debugger 旁边的 Settings ,设置端口为 SW,根据自己的情况设置,设置完毕后会出现一个设备
然后点击 Flash Download ,点击 Add ,选中 STM32F10x High-density Flash ,然后点击 Add 即可添加完成
最后点击 确定 ,然后 OK 保存设置
硬件连接
使用 USB 线连上 USB 转串口,使用杜邦线连接 SWD
看板子的背面,这里我们只需要用杜邦线依次连接 SWCLK、GND、SWDIO、3V3 就行了
NRST:异步复位脚,重置除了 RTC 的寄存器以及后备存储器的寄存器;
SWCLK:时钟开关;
GND:接地;
SWDIO:输入/输出控制。
3V3:电源。
杜邦线的另一端接 ST_LINK ,根据正面提示的引脚来进行接入,如下图
然后将两个 USB 接口插到电脑上,并打开开关,之后可以进行程序的烧录
编译工程
没有错误即可
点击调试按钮
最后可以看到,结果符合预期,寄存器 R5,R6,R7,R8 的值和程序设置一致,具体如下图所示
3.分析生成的 hex 文件
最终生成的 hex 文件的各段大小
用记事本打开 hex 文件,都是一连串的十六进制
1.扩展线性地址记录(hex 文件的第一排十六进制)也叫作 32 位地址记录或 HEX386 记录,这些记录包含数据地址的高 16 位,扩展线性地址记录总是有两个数据字节,外观如下
当一个扩展线性地址记录被读取,存储于数据域的扩展线性地址被保存,它被应用于从 Intel HEX 文件读取来的随后的记录,线性地址保持有效,直到它被另外一个扩展地址记录所改变,通过把记录当中的地址域与被移位的来自扩展线性地址记录的地址数据相加获得数据记录的绝对存储器地址
2.Intel HEX 由任意数量的十六进制记录组成。每个记录包含 5 个域,它们按一定格式排列::llaaaatt[dd…]cc,每一组字母对应一个不同的域,每一个字母对应一个十六进制编码的数字,每一个域由至少两个十六进制编码数字组成,它们构成一个字节,就像以下描述的那样
Intel HEX文件由任意数量以回车换行符结束的数据记录组成,这里我们拿第二条数据记录分析
3.文件尾
在文件的最后一排,是一个文件的结束标志::00000001FF
这个是一个 END OF FILE RECORD,标识文件的结尾
三、在上面Keil 汇编基础上,用汇编程序完成一个输出“Hello world”到串口的程序
1.编写代码
按照上面一样,创建新的工程,不用勾选 CORE 和 Startup
创建完工程后,添加 asm 汇编文件,并添加如下的代码
LED0 EQU 0x422101a0
RCC_APB2ENR EQU 0x40021018
GPIOA_CRH EQU 0x40010804
Stack_Size EQU 0x00000400
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
__initial_sp
AREA RESET, DATA, READONLY
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
AREA |.text|, CODE, READONLY
THUMB
REQUIRE8
PRESERVE8
ENTRY
Reset_Handler
BL LED_Init
MainLoop BL LED_ON
BL Delay
BL LED_OFF
BL Delay
B MainLoop
LED_Init
PUSH {R0,R1, LR}
LDR R0,=RCC_APB2ENR
ORR R0,R0,#0x04
LDR R1,=RCC_APB2ENR
STR R0,[R1]
LDR R0,=GPIOA_CRH
BIC R0,R0,#0x0F
LDR R1,=GPIOA_CRH
STR R0,[R1]
LDR R0,=GPIOA_CRH
ORR R0,R0,#0x03
LDR R1,=GPIOA_CRH
STR R0,[R1]
MOV R0,#1
LDR R1,=LED0
STR R0,[R1]
POP {R0,R1,PC}
LED_ON
PUSH {R0,R1, LR}
MOV R0,#0
LDR R1,=LED0
STR R0,[R1]
POP {R0,R1,PC}
LED_OFF
PUSH {R0,R1, LR}
MOV R0,#1
LDR R1,=LED0
STR R0,[R1]
POP {R0,R1,PC}
Delay
PUSH {R0,R1, LR}
MOVS R0,#0
MOVS R1,#0
MOVS R2,#0
DelayLoop0
ADDS R0,R0,#1
CMP R0,#330
BCC DelayLoop0
MOVS R0,#0
ADDS R1,R1,#1
CMP R1,#330
BCC DelayLoop0
MOVS R0,#0
MOVS R1,#0
ADDS R2,R2,#1
CMP R2,#15
BCC DelayLoop0
POP {R0,R1,PC}
NOP
END
通过用零或空指令NOP填充,来使当前位置与一个指定的边界对齐 END 硬件连接、仿真器设置
按照上面所示即可
2.结果显示
编译工程
然后下载程序到 STM32 中,并打开串口调试助手,结果如下
举报