浅析STM32位带操作

今天被同学问到 STM32 的位带操作,因为不太会所以仔细研究了一波,这里记录一下。

位带操作的原理和推算过程写在后面。这里提供 STM32F103 GPIOA 映射代码,如果不想看原理的话,直接用就好了。

欢迎关注我的公众号:烦躁的铭轩
我会不定时分享一些有意思的内容

/**	
  * @brief  F1芯片GPIO地址映射
  * @param  (addr & 0xF0000000)	这一步是为了区分要操作的地址是SRAM还是外设
  * @param  0x2000000			地址偏移
  * @param  (addr &0xFFFFF)<<5	将要操作的地址放大32倍
  * @param  bitnum				要操控的引脚
  */
#define BITBAND(addr, bitnum) 	((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5) + (bitnum << 2))

#define MEM_ADDR(addr)  		*((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 

#define GPIOA_ODR_Addr			(GPIOA_BASE + 0x0C)
#define GPIOA_IDR_Addr			(GPIOA_BASE + 0x0C)
#define PAout(n)				BIT_ADDR(GPIOA_ODR_Addr, n)//输出
#define PAin(n)					BIT_ADDR(GPIOA_IDR_Addr, n)//输入

零、什么是位带操作

位带操作 ,指的就是单独对一个bit位进行读和写

学过 51 单片机的朋友都知道,我们可以通过直接赋值的方式控制某个引脚的高低电平,这就是位带操作。

P1_0 = 1;	//控制 51 单片机 1.0 脚输出高电平

但在 STM32 中,我们不能直接控制某一位的值。

GPIOx->BSRR = (uint32_t)GPIO_Pin << 16U;//F4芯片控制GPIO引脚输出

由于 32 位处理器存储空间足够多,Cortex-M就利用了额外的空间实现了称为位带(Bit-Banding)操作的硬件属性,该技术使用地址空间的两个不同区域来指向同一物理地址

一、32 位处理器

在此问一个看起来与题无关的问题:32 位 CPU 最多支持多少 G 内存?

我们都知道 32 位处理器的地址线有 32 位,因此有

2 32 2^{32} 232 bit = 2 29 2^{29} 229 byte = 2 19 2^{19} 219 KB = 2 9 2^9 29 MB = 2 − 1 2^{-1} 21 GB = 0.5 GB

(1 byte = 8 bit 、1 MB = 1024 KB、1 GB = 1024 MB)

看起来这个问题的答案是 0.5 GB,但去百度搜的话,会发现答案是 4 GB

那么 32 位 CPU 最多支持 4G 内存是怎么算出来的?

在这里插入图片描述在这里插入图片描述

打开 STM32 的参考手册,我们发现从 CRH 到 IDR 地址偏移了 0x04,但却能控制多达 16 位寄存器。

原来 32 位处理器的“位”与我们平时说的“位(bit)”不是一个东西。或者说,32 位处理器指的是有 2 32 2^{32} 232 的位置。每个位置的容量是 8 bit。

因此不难算出,真正的内存应该是 0.5 GB * 8 = 4 GB

二、STM32的位带操作

由于这种操作不方便(主要是速度慢),以及 32 位处理器存储空间足够多,Cortex-M就利用了多余的空间实现了称为位带(Bit-Banding)操作的硬件属性,该技术使用地址空间的两个不同区域来指向同一物理地址
在这里插入图片描述

在STM32中,将外设SRAM 的前 1MB 内存放大 32 倍,映射到其它地址上。

而控制外设的寄存器正好位于前 1 MB 内存上,因此我们可以实现操作相应地址的方式实现操作某个位

在这里插入图片描述
在这里插入图片描述

三、代码

在这里插入图片描述

根据之前的理论,我们将代码写出

/**	
  * @brief  F1芯片GPIO地址映射
  * @param  (addr & 0xF0000000)	这一步是为了区分要操作的地址是SRAM还是外设
  * @param  0x2000000			地址偏移
  * @param  (addr &0xFFFFF)<<5	将要操作的地址放大32倍
  * @param  bitnum				要操控的引脚
  */
#define BITBAND(addr, bitnum) 	((addr & 0xF0000000) + 0x2000000 + ((addr & 0xFFFFF) << 5) + (bitnum << 2))

#define MEM_ADDR(addr)  		*((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 

#define GPIOA_ODR_Addr			(GPIOA_BASE + 0x0C)
#define GPIOA_IDR_Addr			(GPIOA_BASE + 0x0C)
#define PAout(n)				BIT_ADDR(GPIOA_ODR_Addr, n)//输出
#define PAin(n)					BIT_ADDR(GPIOA_IDR_Addr, n)//输入

在这里插入图片描述

注意

  1. F1和F4的寄存器不一样,不要搞错了!
  2. 位带操作其实很容易出现问题,比如上述代码 n 不在 0 ~ 15 之间会溢出出现BUG,使用需谨慎。

参考资料

32位CPU最多支持4G内存是怎么算出来的?(解惑篇)

STM32 位带操作

《嵌入式 - 深入剖析STM32》详解STM32位带操作

  • 3
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值