LV16-10-存储器系统-02-STM32

本文主要是STM32开发——存储器映射的一些的相关笔记,若笔记中有错误或者不合适的地方,欢迎批评指正😃。

点击查看使用工具及版本
Windows windows11
Ubuntu Ubuntu16.04的64位版本
VMware® Workstation 16 Pro 16.2.3 build-19376536
SecureCRT Version 8.7.2 (x64 build 2214) - 正式版-2020年5月14日
开发板 正点原子 i.MX6ULL Linux阿尔法开发板
uboot NXP官方提供的uboot,NXP提供的版本为uboot-imx-rel_imx_4.1.15_2.1.0_ga(使用的uboot版本为U-Boot 2016.03)
linux内核 linux-4.15(NXP官方提供)
STM32开发板 正点原子战舰V3(STM32F103ZET6)
点击查看本文参考资料
  • 通用
分类 网址说明
官方网站https://www.arm.com/ARM官方网站,在这里我们可以找到Cotex-Mx以及ARMVx的一些文档
https://www.st.com/content/st_com/zh.htmlST官方网站,在这里我们可以找到STM32的相关文档
https://www.stmcu.com.cn/意法半导体ST中文官方网站,在这里我们可以找到STM32的相关中文参考文档
http://elm-chan.org/fsw/ff/00index_e.htmlFatFs文件系统官网
教程书籍《ARM Cortex-M3权威指南》ARM公司专家Joseph Yiu(姚文祥)的力作,中文翻译是NXP的宋岩
《ARM Cortex-M0权威指南》
《ARM Cortex-M3与Cortex-M4权威指南》
开发论坛http://47.111.11.73/forum.php开源电子网,正点原子的资料下载及问题讨论论坛
https://www.firebbs.cn/forum.php国内Kinetis开发板-野火/秉火(刘火良)主持的论坛,现也做STM32和i.MX RT
https://www.amobbs.com/index.php阿莫(莫进明)主持的论坛,号称国内最早最火的电子论坛,以交流Atmel AVR系列单片机起家,现已拓展到嵌入式全平台,其STM32系列帖子有70W+。
http://download.100ask.net/index.html韦东山嵌入式资料中心,有些STM32和linux的相关资料也可以来这里找。
博客参考http://www.openedv.com/开源网-原子哥个人博客
http://blog.chinaaet.com/jihceng0622博主是原Freescale现NXP的现场应用工程师
cortex-m-resources这其实并不算是一个博客,这是ARM公司专家Joseph Yiu收集整理的所有对开发者有用的官方Cortex-M资料链接(也包含极少数外部资源链接)
  • STM32
STM32STM32 HAL库开发实战指南——基于F103系列开发板野火STM32开发教程在线文档
STM32库开发实战指南——基于野火霸道开发板野火STM32开发教程在线文档
  • SD卡
SD Association提供了SD存储卡和SDIO卡系统规范
点击查看相关文件下载
STM32F103xx英文数据手册STM32F103xC/D/E系列的英文数据手册
STM32F103xx中文数据手册STM32F103xC/D/E系列的中文数据手册
STM32F10xxx英文参考手册(RM0008)STM32F10xxx系列的英文参考手册
STM32F10xxx中文参考手册(RM0008)STM32F10xxx系列的中文参考手册
Arm Cortex-M3 处理器技术参考手册-英文版Cortex-M3技术参考手册-英文版
STM32F10xxx Cortex-M3编程手册-英文版(PM0056)STM32F10xxx/20xxx/21xxx/L1xxxx系列Cortex-M3编程手册-英文版
SD卡相关资料——最新版本有关SD卡的一些资料可以从这里下载
SD卡相关资料——历史版本有关SD卡的一些历史版本资料可以从这里下载,比如后边看的SD卡2.0协议
SD 2.0 协议标准完整版这是一篇关于SD卡2.0协议的中文文档,还是比较有参考价值的,可以一看

课程是STM32F0系列的,所以在笔记的最后一部分介绍了一下STM32F0的存储器映射情况。

一、参考文档

这里写一下参考的在线文档,有些是可以有中文文档的。

STM32F0xx参考手册STM32F0xx系列的中文文档
STM32F0xx参考手册STM32F0xx系列的英文文档
STM32F051xx数据手册STM32F051xx系列数据手册的英文文档
STM32F4xx参考手册TM32F405/415, STM32F407/417, STM32F427/437 and STM32F429/439单片机中文参考手册
STM32F4xx数据手册STM32F427和STM32F429单片机的中文数据手册
STM32F10xxx参考手册(RM0008)STM32F10xxx单片机的中文参考手册
STM32F10xxx参考手册(RM0008)STM32F10xxx单片机的英文参考手册
STM32F103xx数据手册STM32F103xx英文数据手册

二、一些基本概念

这部分主要是一些基本概念。

1. STM32长什么样子?

image1

上边这张图是STM32F103ET6的芯片,芯片正面是丝印,ARM是表示该芯片使用的是ARM的内核,STM32F103VET6是芯片型号,后面的字应该是跟生产批次相关,最下面的是ST的LOGO。

芯片四周是引脚,左下角的小圆点表示1脚,然后从1脚起按照逆时针的顺序排列(所有芯片的引脚顺序都是逆时针排列的)。 开发板中把芯片的引脚引出来,连接到其他各种芯片上(比如传感器), 然后在STM32上编程(实际就是通过程序控制这些引脚输出高电平或者低电平)来控制其他芯片工作。

2. 芯片里有什么?

我们看到的STM32芯片是已经封装好的成品,主要由内核和片上外设组成。若与电脑类比,内核与外设就如同电脑上的CPU与主板、内存、显卡、硬盘的关系。

STM32F103采用的是Cortex-M3内核,内核即CPU,由ARM公司设计。ARM公司并不生产芯片,而是出售其芯片技术授权。 芯片生产厂商(SOC) 如ST、TI、NXP等,负责在内核之外设计部件并生产整个芯片,这些内核之外的部件被称为核外外设或片上外设。 如GPIO、USART(串口)、I2C、SPI等都叫做片上外设。

image3

芯片(这里指内核,或者叫CPU)和外设之间通过各种总线连接,其中驱动单元有4个,被动单元也有4个。为了方便理解,我们都可以把驱动单元理解成是CPU部分,被动单元都理解成外设。我们可以打开STM32中文参考手册的系统架构一节就可以看到更加详细的系统结构图(很熟悉吧,上一节学习系统架构的时候就是学习的这张图):

image-20230416163406225

我们对这个图做个区分:

image4

2.1 ICode总线

ICode中的 I 表示 Instruction,即指令。我们写好的程序经过编译之后都是一条条指令,存放在FLASH中,内核要读取这些指令来执行程序就必须通过ICode总线,它几乎每时每刻都需要被使用,它是专门用来取指的。

2.2 驱动单元

2.2.1 DCode总线

DCode中的 D 表示 Data,即数据,那说明这条总线是用来取数的。我们在写程序的时候,数据有常量和变量两种, 常量就是固定不变的,用C语言中的const关键字修饰,是放到内部的FLASH当中的,变量是可变的,不管是全局变量还是局部变量都放在内部的SRAM。 因为数据可以被Dcode总线和DMA总线访问,所以为了避免访问冲突,在取数的时候需要经过一个总线矩阵来仲裁,决定哪个总线在取数。

2.2.2 System系统总线

系统总线主要是访问外设的寄存器,我们通常说的寄存器编程,即读写寄存器都是通过这根系统总线来完成的。

2.2.3 DMA总线

DMA总线也主要是用来传输数据,这个数据可以是在某个外设的数据寄存器,可以在SRAM,可以在内部的FLASH。 因为数据可以被Dcode总线和DMA总线访问,所以为了避免访问冲突,在取数的时候需要经过一个总线矩阵来仲裁,决定哪个总线在取数。

2.3 被动单元

2.3.1 内部的闪存存储器

内部的闪存存储器即FLASH,我们编写好的程序就放在这个地方。内核通过ICode总线来取里面的指令。

2.3.2 内部的SRAM

内部的SRAM,即我们通常说的RAM,程序的变量,堆栈等的开销都是基于内部的SRAM。内核通过DCode总线来访问它。

2.3.3 FSMC

FSMC的英文全称是Flexible static memory controller,叫灵活的静态的存储器控制器,是STM32F10xx中一个很有特色的外设,通过FSMC,我们可以扩展内存,如外部的SRAM,NANDFLASH和NORFLASH。但有一点我们要注意的是,FSMC只能扩展静态的内存,即名称里面的S:static,不能是动态的内存,比如SDRAM就不能扩展。

2.3.4 AHB到APB的桥

从AHB总线延伸出来的两条APB2和APB1总线,上面挂载着STM32各种各样的特色外设。我们经常说的GPIO、串口、I2C、SPI这些外设就挂载在这两条总线上,这个是我们学习STM32的重点,就是要学会编程这些外设去驱动外部的各种设备。

3. 那什么是寄存器呢?

给有特定功能的内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。后边会再详述。

4. 什么是存储器映射?

我们会经常在英文参考手册中看到 Memory Map。它可以被翻译为存储器映射、内存映射有时也叫地址映射。

计算机最重要的功能单元之一是Memory,memory是众多存储单元的集合,为了使CPU准确地找到存储有某个信息的存储单元,必须为这些单元分配一个相互区别的编号,这个编号就是地址编码。在嵌入式处理器内,集成了多种类型的Memory,通常我们称同一类型的Memory为一个Memory Block。

一般情况下,处理器设计者会为每一个memory block分配一个数值连续、数目与其存储单元数相等、以16进制表示的自然数集合作为该memory block的地址编码。这种自然数集合与memory block的对应关系就是Memory Map(存储器映射、内存映射),有时也叫地址映射(Address Map)。

在这里需要强调的是memory Map是一个逻辑概念,是在计算机系统在(上电)复位后才建立起来的。Memory Map相当于这样一个函数:函数的输入量是地址编码,输出量是被寻址单元中的数据。当计算机系统掉电后或复位时,这个函数就不复存在,只剩下实现这个函数的物理基础——电路连接。也可以这样认为:Memory Map是计算机系统上电(复位)时的预备动作,是一个将CPU所拥有的地址编码资源向系统内各个物理存储器块分配的自动过程。简而言之,存储器映射就是为物理存储器按一定编码规则分配地址的行为

在完成存储器映射后,用户就可以按照存储器地址去访问对应的物理存储器。值得注意,一部分地址空间由 Arm Cortex-Mx的系统外设所占用,且不可更改。此外,其余部分地址空间可由芯片供应商(ST,意法半导体)定义使用。用户只能用而不能改(为了降低不同客户在相同应用时的软件复杂度)。用户只能在挂外部RAM或FLASH的情况下可进行自定义。

给存储器分配地址的过程叫存储器映射,再分配一个地址叫重映射。后边会再详述。

三、存储器映射

这一部分纯属个人理解,其实自己也不是特别的理解,在网上查阅了很多资料后,得出了这些结论,也有可能是错误的,暂时记录在这里,后边发现有错了再修改。

1. STM32存储空间

芯片能访问的存储空间有多大,是由谁定的?这个是由芯片内CPU的地址总线的数量决来定的,STM32芯片内部的地址总线为32根。

  • 1根地址线:可以传输的地址为0和1的,那么理论上就可以访问2个字节。

  • 2根地址线:可以传输地址为00、01、10、11,理论上可以访问4个字节。

  • 3根地址线:可以传输的地址为000、001、010、011、100、101、110、111,理论上可以访问8个字节。

  • 32根地址线:可以产生00000000 00000000 00000000 00000000 ~ 11111111 11111111 11111111 11111111 也就是的2的32次方个地址,范围刚好为4G,所以我们就说STM32的32根地址线,理论上可以访问4G字节的存储器空间。

img

在上图的最右边可以看到STM32地址是从0x00000000到0xFFFFFFFF,这就是4GB的存储空间。但是STM32真的有4GB的存储空间吗?答案当然不是,我们的PC电脑也才4GB的内存。一个小小的单片机怎么可能有4GB的存储空间!这个4GB的是STM32理论分配的地址空间。也就是说实际上并不是有这么大的存储单元。上图中第二排可以看到有很多预留的地址,这些地址并没有给他分配存储单元。

所有的存储器都是与地址线连着的,但是实际上如果只接了一个10M的存储器,而且是从0地址开始映射的,那么32根地址线所产生的0~10M的地址信号其实才是有意义的,因为这些地址信号才有对应真实的存储器,而所产生的10M以上地址信号其实并无意义,因为并不对应真实的存储器。

STM32中的32是32根地址线的意思吗?

答:不是,STM32的32不是32根地址线的意思,而是表示MCU芯片内部CPU在处理数据时,每次可以处理的数据位宽为32个bit。正是由于这个原因,STM32 内部的寄存器大小都是32位的,刚好等于位宽。

某个芯片是32位的,但是它的地址线完全可以只有16根、或者8根,对于 STM32 来说,刚好碰巧的是,CPU能够处理的数据位宽与地址线数量恰好都是 32。

2. 什么是存储器映射

这里可以看一下STM32F429的中文数据手册(为啥看F429?因为网上查阅到的资料以F429为例啦,有一张可以帮助理解,其他其实也一样,比如STM32F103系列的,在它们的数据手册中都会这样一个存储器映像的章节,对于STM32F103系列的,我们可以打开数据手册:STM32F10xxx英文数据手册,找到4 Memory mapping一节),F429的中文手册可以看这里:[RM0090_STM32F40xxx、STM32F41xxx、STM32F42xxx、STM32F43xx参考手册](https://www.stmcu.com.cn/Designresource/detail/localization_document /710005)

image-20230409200609189 img

映射其实就是对应的意思,事实上存储器本身并不具备地址,将芯片理论上的地址分配给存储器,这就是存储器映射。

存储器在产家制作完成后是一片没有任何信息的物理存储器,而CPU要进行访存就涉及到内存地址的概念,因此存储器映射就是为物理内存按一定编码规则分配地址的行为。值得注意,存储器映射一般是由产家规定,用户不能随意更改。

比如前面举的10M存储器的例子,这个10M存储器原本并没有地址,我将10M存储器映射到理论32根地址线可以传输4G个地址信号,每个地址信号访问一个字节,4G地址信号则可以访问4G个字节,所以理论上的可访问范围为4G。地址0往后的10M范围,这10M的存储器就有了0~10M的地址,地址线所产生的0~10M之间的地址信号,就可以访问10M的这个真实存储器。至于在生产芯片时,在工艺和技术上具体是怎么实现我们所描述的映射的,我们无需关心。

STM32的所有片内外设其实都是存储器,所以所有的这些存储器都需要被映射,只是理论上的4G范围远远大与实际的存储器空间,也就说实际的存储器空间并没有4G。

理论上地址其实就是门牌号,存储中的每个字节就是房间,存储器生产出来后,这些房间是没有地址的(门牌号),映射的过程其实就是将这些门牌号分配给这些房间,分配好后,每个门牌号只能访问自己的房间,没有被分配的地址就是保留地址,所谓保留地址的意思就是,没有对应实际存储空间。

可不可以保留一些地址不分配呢?

当然可以,因为理论上可以有4G的地址,但是实际上不可能给4G存储空间,否则这个单片机芯片可买不起,PC机的内存也才4G/8G,单片机怎么可能真的给4G存储空间呢?

所以呢,总的来说,对于将要学习的STM32来说,存储器映射就是将被控单元的FLASH,RAM,FSMC和AHB到APB的桥(即片上外设),这些功能部件共同排列在一个4GB的地址空间内。我们在编程的时候,可以通过他们的地址找到他们,然后来操作他们(通过C语言对它们进行数据的读和写)。

3. 存储器区域功能划分

在这4GB的地址空间中,ARM已经粗线条的平均分成了8个块,每块512MB,每个块也都规定了用途,具体分类见下表(。每个块的大小都有512MB,显然这是非常大的,芯片厂商在每个块的范围内设计各具特色的外设时并不一定都用得完,都是只用了其中的一部分而已。

序号 用途 地址范围
Block 0 Code 0x0000 0000 ~ 0x1FFF FFFF(512MB)
Block 1 SRAM 0x2000 0000 ~ 0x3FFF FFFF(512MB)
Block 2 片上外设 0x4000 0000 ~ 0x5FFF FFFF(512MB)
Block 3 FSMC的bank1 ~ bank2 0x6000 0000 ~ 0x7FFF FFFF(512MB)
Block 4 FSMC的bank3 ~ bank4 0x8000 0000 ~ 0x9FFF FFFF(512MB)
Block 5 FSMC 寄存器 0xA000 0000 ~ 0xCFFF FFFF(512MB)
Block 6 没有使用 0xD000 0000 ~ 0xDFFF FFFF(512MB)
Block 7 Cortex-M3内部外设 0xE000 0000 ~ 0xFFFF FFFF(512MB)

在这8个Block里面,有3个块非常重要,也是我们最关心的三个块。Block0用来设计成内部FLASH,Block1用来设计成内部RAM,Block2用来设计成片上的外设,下面我们简单的介绍下这三个Block里面的具体区域的功能划分。

四、寄存器映射

1. 寄存器与寄存器映射

存储器本身没有地址,给存储器分配地址的过程叫存储器映射,那什么叫寄存器映射?寄存器到底是什么?这里还是以STM32F4为例:

img

在存储器Block2这块区域,也就是地址从0x4000000~0x5FFFFFF这块区域,设计的是片上外设,它们以四个字节为一个单元,共32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。可以找到每个单元的起始地址,然后通过C语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,工程师就根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射

img

2. STM32的外设地址映射

片上外设区分为三条总线,根据外设速度的不同,不同总线挂载着不同的外设,APB1挂载低速外设,APB2和AHB挂载高速外设。相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。其中APB1总线的地址最低,片上外设从这里开始,也叫外设基地址。

2.1 总线基地址

总线名称 总线基地址 相对外设基地址的偏移
APB1 0x4000 0000 0x0
APB2 0x4001 0000 0x0001 0000
AHB 0x4002 0000 0x0002 0000

这个数据怎么来的,我们可以查看参考手册或者数据手册的存储器映像来找。

2.2 外设基地址

总线上挂载着各种外设,这些外设也有自己的地址范围,特定外设的首个地址称为“XX外设基地址”,也叫XX外设的边界地址。具体有关STM32F10xx外设的边界地址请参考《STM32F10xx参考手册》的 2.3小节的存储器映射的表1:STM32F10xx 寄存器组起始地址 。

2.3 外设寄存器

在XX外设的地址范围内,分布着的就是该外设的寄存器。以GPIO外设为例,GPIO是通用输入输出端口的简称,简单来说就是STM32可控制的引脚,基本功能是控制引脚输出高电平或者低电平。最简单的应用就是把GPIO的引脚连接到LED灯的阴极,LED灯的阳极接电源,然后通过STM32控制该引脚的电平,从而实现控制LED灯的亮灭。

GPIO有很多个寄存器,每一个都有特定的功能。每个寄存器为32bit,占四个字节,在该外设的基地址上按照顺序排列,寄存器的位置都以相对该外设基地址的偏移地址来描述。有关外设的寄存器说明可参考《STM32F10xx参考手册》中具体章节的寄存器描述部分,在编程的时候我们需要反复的查阅外设的寄存器说明。

五、地址重映射

这一部分还是以STM42F429为例。

1. 自举的概念

自举(bootstrap)计算机设备使用硬件加载的程序,用于初始化足够的软件来查找并加载功能完整的操作系统。也用来描述加载自举程序的过程。什么是单片机的自举?,单片机的自举就是单片机的启动。

我们说,单片机程序基本都是从0地址出开始运行的,F429的0x00000000~0x001FFFFF地址映射了到什么存储器上,那么就从该存储器上读取指令,开始运行。

至于说0x00000000~0x001FFFFF到底映射在了什么存储器上,这个要看F429 芯片 BOOT1、BOOT0这两个引脚的电平值,说白了就是,通过BOOT1和BOOT0 引脚的电平值,可以选择将0x00000000~0x001FFFFF映射到不同的存储器上。

img

STM32片内的FLASH分成两部分:主存储块、信息块。主存储块(主Flash)用于存储程序,我们写的程序一般存储在这里。信息块又分成两部分:系统存储器(系统FLASH)、选项字节。系统存储器存储用于存放在系统存储器自举模式下的启动程序(BootLoader),当使用ISP方式加载程序时,就是由这个程序执行。这个区域由芯片厂写入BootLoader,然后锁死,用户是无法改变这个区域的。选项字节存储芯片的配置信息及对主存储块的保护信息。

2. 主flash地址

主FLASH地址为0x0800 0000-0x081FFFFF,Jlink下载时的FLASH设置是不是通过Jlink下载到了地址为0x08000000的地方,大小是0x00100000,也就是1MB。我们打开一个STM32F4的keil-MDK工程看一下:

img

明明代码是下载到0x0800 0000往后的存储空间中,为什么说运行又是从0x0000 0000地址运行的呢?为什么不是从0x0800 0000开始运行的呢?有关这个问题,就是我们说的单片机的自举

3. 自举配置

这里可以看一下STM32F429的中文参考手册,中文手册可以看这里:[RM0090_STM32F40xxx、STM32F41xxx、STM32F42xxx、STM32F43xx参考手册](https://www.stmcu.com.cn/Designresource/detail/localization_document /710005)

image-20230409202814219

正常情况下都是映射到主FLASH上,所以都是从主FLASH上启动的,为了从FLASH启动,我们需要将代码下载到主FLASH上。

4. 重映射

如果0x00000000~0x001FFFFF之前是映射在系统存储器或者嵌入式 SRAM上的,现在改变BOOT0、BOOT1 的电平为 0、x。0x00000000~0x001FFFFF就被重新映射在了主FLASH上,这就是单片机的地址重映射

重映射就是本来是和张三进行映射的的,现在改为了和李四映射。换句话说重映射就是0x00000000 ~0x001FFFFF(1MB)本来映射在系统存储器 0x1FFF 0000~0x1FFF7A0F(30KB)上面,现在映射到了主FLASH 0x08000000 -0x081FFFFF(1MB)上面。

v2-98200698360c57048c6fe9820fa577e2_r

选择从主FLASH启动时,显然FLASH会被映射在了两片地址上。

  • 原本映射的地址(1MB):0x08000000~0x081F FFFF,进行Jlink下载时使用这个地址
  • 重映射的地址(1MB):0x00000000~0x001F FFFF,启动时CPU就是从重映射的地址读取指令

这两片地址都是有效的,重映射到FLASH上后,CPU从0地址开始运行时,就从FLASH上读取指令,当然前提是我们需要将代码下载FLASH中。

这就解释了为什么我们在keil中设置好程序的下载地址为0x0800 0000,但是单片机上电是确实从0开始执行。是因为我们在硬件上设置了BOOT0=1,BOOT1=X,从而导致了主FLASH区(也叫主闪存,大小1MB)被映射到了0x0000 0000~0x001F FFFF(1MB),故而代码是下载到 0x0800 0000 往后的存储空间中,却说运行又是从0x0000 0000地址运行的。

5. 程序下载地址?

下载时,能不能使用0x0000 0000地址来下载?

这个不行,因为下载时,0x0000 0000~0x001F FFFF还没有被重映射到FLASH上,只能使用0x0800 0000来下载。

上面说的是我们用JLink下载器下载代码,但是有时候我们还听说可以用串口来下载程序,这又是怎么回事?

用串口下载程序,也就是我们说的ISP在系统中编程。从系统存储器启动,即STM32的ISP了。此时硬件电路B00T0=1,B00T1=0。由于串口不能直接把程序下载到主FLASH里面,所以需要使用到ST公司内嵌于系统存储区的Bootloader来引导把程序下载到主FLASH里面。JLink能直接把程序下载到内置的FLASH里面,是因为JLink下载器内部有Bootloader来引导把程序下载到FLASH里面。程序下载完成后还需要配置BOOT引脚为BOOT0=0,BOOT1=X(即从主闪存存储器启动),复位后才能正常启动程序。

如果不修改BOOT引脚的话也就是B00T0=1,B00T1=0,那么0x0000 0000 ~ 0x001FFFFF是不是被重映射到系统存储器上面,而程序代码在主FLASH里面。复位后程序肯定不能正常运行,只有在使用串口下载程序后配置BOOT引脚为BOOT0=0,BOOT1=X,复位后才能正常执行代码。

总的来说,使用JLink下载代码,JLink下载器内部的Bootloader将程序引导下载到主FLASH里面。使用串口下载代码,由于串口没有Bootloader,就要使用ST官方内置在芯片系统存储区的Bootloader代码,将程序引导下载止主FLASH。又因为程序是从0开始执行的,所以我们复位后运行程序时一定要让BOOT0=0,BOOT1=X,将0x00000000~0x001FFFFF是重映射到主FLASH我们代码存在的地方,从0开始执行代码。

下图是使用FlyMcu串口下载程序,这个串口是USB-TTL,下载程序时让BOOT0=0,BOOT1=X即可。不是说在系统中编程需要将B00T0=1,B00T1=0吗?这是因为我们使用的是这个软件,这个软件可以通过DTR和RTS改变BOOT的引脚电平,达到不用修改BOOT引脚就可以下载运行代码,实际上是软件替我们做了改变BOOT引脚的操作。

v2-a4b1c6968c236d26ca79421bfc98c229_r

六、 ISP与IAP

  • ISP在系统编程,是指直接在目标电路板上对芯片进行编程,一般需要一个自举程序(BootLoader)来执行。ISP也有叫ICP在电路编程、在线编程。

  • IAP在应用中编程,是指最终产品出厂后,由最终用户在使用中对用户程序部分进行编程,实现在线升级。IAP要求将程序分成两部分:引导程序、用户程序。引导程序总是不变的。IAP也有叫在程序中编程。

    ISP与IAP的区别在于,ISP一般是对芯片整片重新编程,用的是芯片厂的自举程序。而IAP只是更新程序的一部分,用的是电器厂开发的IAP引导程序。综合来看,ISP受到的限制更多,而IAP由于是自己开发的程序,更换程序的时候更容易操作。

七、STM32F0的映射?

上边其实已经通过STM32F4了解了相关的概念,下边我们以课程中的STM32F0为例,熟悉一下相关的内容吧。

1. Cortex-M0存储器映射

image-20230409205001077

被控单元的FLASH,RAM和AHB到APB的桥(即片上外设),这些功能部件共同排列在一个 4GB 的地址空间内。我们在编程的时候,可以通过他们的地址找到他们,然后来操作他们。存储器本身没有地址,给存储器分配地址的过程叫存储器映射。

image-20230409205027990

2. STM32F05x实际映射

image-20230409205110604

STM32F05x 存储器映像和外设寄存器编址部分截图如下,详情可以看STM32F0参考手册的 存储器组织 一节。

image-20230409205939501

3. 如何访问寄存器

以GPIOA寄存器组为例、如何读写ODR寄存器?

1
2
3
4
5
6
7
8
9
以知GPIOA的起始地址为0x48000000,各寄存器的偏移地址如下:
MODER; /* Address offset: 0x00 */
OTYPER; /* Address offset: 0x04 */
OSPEEDR; /* Address offset: 0x08 */
PUPDR; /* Address offset: 0x0C */
IDR; /* Address offset: 0x10 */
ODR; /* Address offset: 0x14 */
BSRR; /* Address offset: 0x18 */
LCKR; /* Address offset: 0x1C */
  • 第一种方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define GPIOA_BASE ( (unsigned int ) 0x48000000 )
#define GPIOA_ODR ( GPIOA_BASE + 0x14 )

// 读操作
val = *(unsigned int *) GPIOA_ODR;

// 写操作
*(unsigned int *) GPIOA_ODR = val;

//==================================
//改进后的样子
#define GPIOA_ODR ( *(unsigned int *) ( GPIOA_BASE + 0x14 ))

val = GPIOA_ODR ; //读
GPIOA_ODR = val ; //写

  • 第二种方式

用上面的方法去定义地址,还是稍显繁琐、根据我们每一类外设对应的寄存器组地址都是连续增长的特点,我们引入 C 语言中的结构体语法对寄存器进行封装、

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct 
{
uint32_t MODER; /* Address offset: 0x00 */
uint32_t OTYPER; /* Address offset: 0x04 */
uint32_t OSPEEDR; /* Address offset: 0x08 */
uint32_t PUPDR; /* Address offset: 0x0C */
uint32_t IDR; /* Address offset: 0x10 */
uint32_t ODR; /* Address offset: 0x14 */
uint32_t BSRR; /* Address offset: 0x18 */
uint32_t LCKR; /* Address offset: 0x1C */
} GPIO_TypeDef;

#define GPIOA_BASE ( (unsigned int ) 0x48000000 )
#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)

我们访问 GPIOA 的控制寄存器组时、直接使用宏定义好 GPIO_TypeDef 类型的指针,而且指针指向 GPIOA 端口的首地址,这样我们直接用宏 GPIOA 访问改外设的任意一个寄存器:

1
2
GPIOA->MODER   = 0x20;
GPIOA->OSPEEDR = 0x16;

八、大端小端?

突然在STM32中文参考手册的存储器组织一节中发现了一段话,作为笔记记在这里吧:

程序存储器、数据存储器、寄存器和输入输出端口被组织在同一个4GB的线性地址空间内。数据字节以小端格式存放在存储器中。一个字里的最低地址字节被认为是该字的最低有效字节,而最高地址字节是最高有效字节

所以从这里来看,我们使用的STM32是小端模式,不看手册的话我们其实也是可以通过代码来测的,详情可以看前边的C语言的笔记。