STM32学习笔记——四、存储器和寄存器映射

1 存储器映射

STM32系统框图
在这里插入图片描述

  • 在图中,被控单元的FLASH、RAM、FSMC 和AHB 到APB 的桥(即片上外设),这些功能部件共同排列在一个4GB 的地址空间内。
  • 编程时我们通过地址操作他们

1.1 存储器映射

在这里插入图片描述

  • 存储器映射:存储器本身不具有地址信息,芯片厂商或用户,给存储器分配地址的过程。
  • 存储器重映射:给存储器再分配一个地址

1.2 存储区域功能划分

在这4GB 的地址空间中,ARM 已经粗线条的平均分成了8 个块,每块512MB。
在这里插入图片描述

  • Block0 用来设计成内部FLASH
  • Block1 用来设计成内部RAM
  • Block2 用来设计成片上的外设

1.2.1 存储器Block0内部区域功能划分

Block0 主要用于设计片内的FLASH:STM32F103ZET6的FLASH是512KB,属于大容量。
在这里插入图片描述

1.2.3 储存器Block1内部区域功能划分

Block1 用于设计片内的SRAM:STM32F103ZET6的SRAM 都是64KB,
在这里插入图片描述

1.2.4 储存器Block2内部区域功能划分

Block2 用于设计片内的外设,根据外设的总线速度不同,Block 被分成了APB 和AHB 两部分,其中APB 又被分为APB1 和APB2
在这里插入图片描述

2 寄存器映射

  1. 使用寄存器原由:
  • 片上外设每个单元对应一个起始地址,可以通过C语言指针的操作方式来访问这些单元。
  • 但是每次通过地址方式访问,不好记忆且容易出错
// GPIOB 端口全部输出高电平
 *(unsigned int*)(0x4001 0C0C) = 0xFFFF;
 //在编译器看来,这只是一个普通的变量,是一个立即数,得进行强制类型转换,转换成指针,让编译器认为是指针
  1. 寄存器(内存单元的别名):根据每个单元功能不同,以功能为名给这个内存单元取一个别名,这个别名就是寄存器
  2. 寄存器映射(过程):给已经分配好地址的有特定功能的内存单元取别名的过程
// GPIOB 端口全部输出高电平
#define GPIOB_ODR (unsigned int*)(GPIOB_BASE+0x0C)
* GPIOB_ODR = 0xFF;

把指针操作“*”也定义到寄存器别名里面

// GPIOB 端口全部输出高电平
#define GPIOB_ODR *(unsigned int*)(GPIOB_BASE+0x0C)
GPIOB_ODR = 0xFF;

3 STM32外设地址映射

片上外设区:分为三条总线

  • APB1:低速外设
  • APB2、AHB:高速外设

3.1 总线基地址

总线的基地址:总线的最低地址,挂载在该总线上的首个外设的地址
在这里插入图片描述相对外设基地址偏移:该总线地址与“片上外设”基地址0x4000 0000 的差值

3.2 外设基地址

外设基地址:APB1总线地址最低,片上外设从这里开始

  • 以GPIO外设来为例了解外设的基地址,GPIO 属于高速的外设,挂载到APB2 总线上
    在这里插入图片描述

3.3外设寄存器

在XX 外设的地址范围内,分布着的就是该外设的寄存器

3.3.1 了解外设寄存器(以GPIO外设为例)(通用输入输出端口,STM32可控制的引脚,控制引脚输出高低电平)

  • GPIO 有很多个寄存器,每一个都有特定的功能。
  • 每个寄存器为32bit,占四个字节
  • 在该外设的基地址上按照顺序排列,寄存器的位置都以相对该外设基地址的偏移地址来描述。
    在这里插入图片描述
  • 编程时,需要反复的查阅外设的寄存器说明,避免用错。

3.3.2 如何理解寄存器的说明(以“GPIO 端口置位/复位寄存器”为例)

在这里插入图片描述

  1. 名称:寄存器的名称“(GPIOx_BSRR)(x=A⋯E)”
  • 指寄存器名为“GPIOx_BSRR”其中的“x”可以为A-E,
  • 这个寄存器说明适用于GPIOA、GPIOB 至GPIOE
  • 这些GPIO 端口都有这样的一个寄存器。
  1. 偏移地址:本寄存器相对于这个外设的基地址的偏移
  • 本寄存器的偏移地址是0x10,
  • 从参考手册中查到GPIOA 外设的基地址为0x4001 0800
  • 算出GPIOA 的这个GPIOA_BSRR 寄存器的地址为:0x4001 0800+0x10;
  1. 寄存器位表:表中列出它的0-31 位的名称及权限
  • 表上方的数字为位编号
  • 表中间为位名称
  • 表最下方为读写权限(w只写,r只读,rw可读写)
  1. 位功能说明:介绍了寄存器每一位的功能
    例图中:
  • 本寄存器中有两种寄存器位(BRy 、BSy)
  • y 数值可以是0-15,表示端口的引脚号
  • BR1、BS1 就是控制GPIOA第1 个引脚。
  1. 其他内容说明、
  • “复位”是将该位设置为0 的意思,而“置位”表示
    将该位设置为1
  • 0时引脚输出低电平,1时引脚输出高电平

4 C语言对寄存器的封装

以下内容着重理解,记不住查表看得懂就好

4.1 封装总线和外设基地址

把总线基地址和外设基地址都以相应的宏定义起来
总线或者外设都以他们的名字作为宏名
  • 1、首先,定义了“片上外设”基地址:PERIPH_BASE
  • 2、接着,在PERIPH_BASE 上加入各个总线的地址偏移:得到APB1、APB2 总线的地址APB1PERIPH_BASE、APB2PERIPH_BASE
  • 3、接着,在APB1、APB2 总线的地址之上加入外设地址的偏移,得到GPIOA-G 的外设地址
  • 在GPIOx(A-G) 外设地址上加入各寄存器的地址偏移,得到特定寄存器的地址
    在这里插入图片描述在这里插入图片描述有了具体地址,就可以用指针读写
    在这里插入图片描述 使用(unsigned int ) 把GPIOB_BSRR 宏的数值强制转换成了地址,然后再用“”号做取指针操作,对该地址的赋值

4.2 封装寄存器列表

出发点:用上面的方法去定义地址,还是稍显繁琐,例如GPIOA-GPIOE 都各有一组功能相同的寄存器,如GPIOA_ODR/GPIOB_ODR/GPIOC_ODR 等等,它们只是地址不一样,但却要为每个寄存器都定义它的地址。
改进办法:引入C 语言中的结构体语法对寄存器进行封装
在这里插入图片描述在这里插入图片描述

  • 用typedef 关键字声明了名为GPIO_TypeDef 的结构体类型
  • 结构体内有7 个成员变量,变量名正好对应寄存器的名字。
  • C语言中,结构体内变量的存储空间是连续的,其中32位的变量占用4 个字节,16 位的变量占用2 个字节
    在这里插入图片描述假如这个结构体的首地址为0x4001 0C00(这也是第一个成员变量CRL 的地址),那么结构体中第二个成员变量CRH 的地址即为0x4001 0C00 +0x04,加上的这个0x04,正是代表CRL 所占用的4 个字节地址的偏移量,其它成员变量相对于结构
  • 只要给结构体设置好首地址,就能把结构体内成员的地址确定下来,然后就能以结构体的形式访问寄存器
    在这里插入图片描述 用GPIO_TypeDef 类型定义一个结构体指针GPIOx, 并让指针指向地址GPIOB_BASE(0x4001 0C00),使用地址确定下来,然后根据C 语言访问结构体的语法,用GPIOx->ODR 及GPIOx->IDR 等方式读写寄存器。
  • 更进一步,直接使用宏定义好GPIO_TypeDef 类型的指针,而且指针指向各个GPIO端口的首地址,使用时我们直接用该宏访问寄存器即可
    在这里插入图片描述在这里插入图片描述其他外设也同样可以用这种方法来封装。好消息是,这部分工作都由固件库帮我们完成了,这里我们只是分析了下这个封装的过程,让大家知其然,也只其所以然。

5 修改即存在的位操作方法

使用C 语言对寄存器赋值时,要求只修改该寄存器的某几位的值,且其它的寄存器位不变,需要用到C 语言的位操作方法。

5.1 把变量的某位清零

变量a代表寄存器,假设寄存器本来已有数值,需要把a的某一位清零,其他位不变

//定义一个变量a = 1001 1111 b (二进制数)
unsigned char a = 0x9f;
//对bit2 清零
a &= ~(1<<2);
//括号中的1 左移两位,(1<<2) 得二进制数:0000 0100 b
//按位取反,~(1<<2) 得1111 1011 b
//假如a 中原来的值为二进制数: a = 1001 1111 b
//所得的数与a 作”位与&”运算,a = (1001 1111 b)&(1111 1011 b),
//经过运算后,a 的值a=1001 1011 b
// a 的bit2 位被清零,而其它位不变。

5.2 对变量的某几位进行赋值

寄存器位经过清零操作后,可以对某几位写入所需要的数值,且其它位不变
写入的数值一般就是需要设置寄存器的位参数。

//a = 1000 0011 b
//此时对清零后的第2组bit4、bit5设置成二进制数“01 b ”
a |= (1<<2*2);
//a = 1001 0011 b,成功设置了第2组的值,其它组不变

5.3 对变量的某位取反

需要对寄存器的某个位进行取反操作,即1变0,0变1,直接如下操作,其它位不变

//a = 1001 0011 b
//把bit6 取反,其它位不变
a ^=(1<<6);
//a = 1101 0011 b

5.4 操作符总结

操作符操作符作用操作符示例
>>8>>38右移3位
<<1<<61左移6位
&=a &= ~(1<<2)按位与后赋值(两个二进制的对应位都为1时,结果为1,否则结果等于0)
^=x = 0x02, x ^= 0x01, x = 0x03按位异或后赋值(两个二进制的对应位相同,结果为0,否则结果为1)
|=x = 0x02, x ^= 0x01, x = 0x03按位或后赋值(两个二进制对应位都为0时,结果等于0,否则结果等于1)
  • 3
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值