如果我们想要深入去学习STM32单片机的存储原理和方式,就要花时间去了解STM32单片机有关寄存器的基本原理
单片机型号:正点原子STM32F103ZET6
目录
寄存器的定义
寄存器分类
存储器映射
寄存器映射
通过地址访问控制单元运作
通过定义宏来取代直接访问绝对地址的控制方式
寄存器说明的使用
利用C语言对地址进行封装
寄存器的定义
寄存器功能是存储二进制代码,由具有存储功能和触发器组成的。寄存器是CPU内部一块小型存放数据的区域,用来暂存运算的数据结果。它拥有很高的数据传递速度。寄存器也是存储器的一种。
下图为寄存器常在位置。
寄存器分类
控制寄存器(xxx_CR):用来控制,配置中某些部件的工作方式;
状态寄存器(xxx_SR):存储了当前外设的工作状态;
数据寄存器(xxx-DR):存储外设进行输入输出数据;
存储器映射
1.存储:首先存储型外设FSMC,RAM,FLASH以及AHB桥到APB桥上的外设都存储于一块4GB大的空间内部,我们给这块空间分好大小并编上地址(类似于我们通过门牌号来找到家,我们可以通过地址调用不同存储单元)
2.存储器映射:存储器本身不具有地址信息,它的地址是由芯片厂商或用户配,给存储器分配地址的过程就称为存储器映射(类似于给新建的房子编上门牌号)。且如果给存储器再分配一个地址就叫存储器重映射。
(上图为存储器映射图)
3.功能划分:在这 4GB 的地址空间中,ARM 已经粗线条的平均分成了 8 个块,每块 512MB,每个块也都规定了用途。而芯片厂商在每个块的范围内设计各具特色的外设时并不一定都用得完,都是只用了其中的一部分而已。
(上图为存储器功能区域划分)
Block0:此块为Flash的存放区域,一般ST公司只使用512KB
Block1:此块为SRAM的存放区域,一般ST公司仅用了64KB
Block2:此处为外接的一大串外设的存储区域
Block3-Block4:此处是FSMC的存储区域
Block5:此区域是FSMC寄存器的区域
Block6:此处为无使用区
Block7:此处为芯片内核里面的寄存器存储的位置
寄存器映射
在存储器 Block2 这块区域,设计的是片上外设,它们以四个字节为一个单元,共32bit,每一个单元对应不同的功能,当我们控制这些单元时就可以驱动外设工作。我们可以找到每个单元的起始地址,然后通过 C 语言指针的操作方式来访问这些单元,如果每次都是通过这种地址的方式来访问,不仅不好记忆还容易出错,这时我们可以根据每个单元功能的不同,以功能为名给这个内存单元取一个别名,这个别名就是我们经常说的寄存器,这个给已经分配好地址的有特定功能的内存单元取别名的过程就叫寄存器映射。
通过地址访问控制单元运作
如将GPIOB 16个IO口都输出高电平:
方法一:由地址来找到对应单元并且进行操作
方法二:通过访问寄存器别名来找到相应单元进行操作
通过定义宏来取代直接访问绝对地址的控制方式
在外设的内存块中由下到上挂着ABP1,ABP2,AHB三条总线,每条总线都外连一些外设,我们将从总线APB1为初始设置地址。相应总线的最低地址我们称为该总线的基地址,总线基地址也是挂载在该总线上的首个外设的地址。
其中 APB1 总线的地址最低,片上外设从这里开始,也叫总线外设基地址。如下图为GPIOx的外设基地址。外设基地址相对于总线及地址的距离就是相对总线的地址偏移。
而在某一个外设的地址范围内就分布着这个外设的寄存器。以GPIO为例,每个GPIO寄存器都以4个字节为单位划分。而外设寄存器位置都可以用寄存器外设基地址偏移来表示。下图以GPIOB端口为例:
通过上面我们就可以定义宏来替代寄存器绝对地址,而且有了偏移的数值,就可以更加简便由外设/总线基地址来找到每一个寄存器地址。
寄存器说明的使用
以GPIO端口设置\清除寄存器说明为例
- 寄存器说明中首先列出了该寄存器中的名称,“(GPIOx_BSSR)(x=A…E)”这段的意 思是该寄存器名为“GPIOx_BSSR”其中的“x”可以为 A-E,也就是说这个寄存器说明适 用于 GPIOA、GPIOB 至 GPIOE,这些 GPIO 端口都有这样的一个寄存器。
- 偏移地址是指本寄存器相对于这个外设的基地址的偏移。
- 3是寄存器的位表,表中列出它的 0-31 位的名称及权限。表上方的数字为位、编号,中间为位名称,最下方为读写权限,其中w表示只写,r表示只读,r/w表示可读写。本寄存器中的位权限都是 w,所以只能写,如果读本寄存器,是无法保证读取到它真正内容的。而有的寄存器位只读,一般是用于表示 STM32 外设的某种工作状态的,由 STM32 硬件自动更改,程序通过读取那些寄存器位来判断外设的工作状态。
- 位功能是寄存器说明中最重要的部分,它详细介绍了寄存器每一个位的功能。例如本寄存器中有两种寄存器位,分别为 BRy 及 BSy,其中的 y 数值可以是 0-15,这里的 0-15 表示端口的引脚号,如 BR0、BS0 用于控制 GPIOx 的第 0 个引脚,若 x 表示 GPIOA,那就是 控制 GPIOA 的第 0 引脚,而 BR1、BS1 就是控制 GPIOA 第 1 个引脚。 其中 BRy 引脚的说明是“0:不会对相应的 ODRx 位执行任何操作;1:对相应 ODRx 位进行复位”。这里的“复位”是将该位设置为 0 的意思,而“置位”表示将该位设置为 1;说明中的 ODRx 是另一个寄存器的寄存器位,我们只需要知道 ODRx 位为1 的时候,对应的引脚 x 输出高电平,为 0 的时候对应的引脚输出低电平即可。所以,如果对 BR0 写入“1”的话,那么 GPIOx 的第0个引脚就会输出“低电平”,但是对 BR0 写入“0”的话,却不会影响 ODR0 位,所以引 脚电平不会改变。要想该引脚输出“高电平”,就需要对“BS0”位写入“1”,寄存器位BSy 与 BRy 是相反的操作。
利用C语言对地址进行封装
为方便于记忆,我们将总线基地址和外设基地址和寄存器基地址进行宏定义,便于我们通过偏移值准确找到寄存器位置
一旦我们找到了地址,就可以用指针操作进行读写
(该代码使用 (unsigned int *) 把 GPIOB_BSRR 宏的数值强制转换成了地址,然后再用 “*” 号做取指针操作,对该地址的赋值,从而实现了写寄存器的功能。同样,读寄存器也是用 取指针操作,把寄存器中的数据取到变量里,从而获取 STM32 外设的状态。)
为更简化,我们选择用结构体指针来访问寄存器。例如GPIOA-E组每组都有相同功能的寄存器,但我们用时却要每个寄存器都要进行宏定义。所以我们引入一个C语言结构体语法对寄存器进行封装,如下图
随后通过地址偏移和基地址就可以准确找出每个寄存器地址并作出相应操作,如下图
参考资料:
1、正点原子STM32开发板附带资料
2、(stm32学习总结)—对寄存器的理解 - 北极星! - 博客园
3、第5小节:寄存器介绍_哔哩哔哩_bilibili
4、5.STM32外设都有哪几类寄存器?(详解)_魏波.的博客-CSDN博客_外设控制器中的寄存器有
5、STM32 对外设基地址,总线外设基地址和寄存器基地址的理解_wuyuzun的博客-CSDN博客_基地址