其他分享
首页 > 其他分享> > STM32的位带操作

STM32的位带操作

作者:互联网

1. 什么是位带操作?

学习 51 单片机的时候就使用过位操作,通过关键字 sbit 对单片机IO口进行位定义。但STM32没有这样的关键字,于是便要通过访问位带别名区来实现。即:**将每一位膨胀成一个32位字,因此SRAM的1MB位带区就膨胀为32MB的位带别名区,通过访问位带别名区来实现访问位带中每一位的目的。**比如 BSRR 寄存器有 32 个位,那么可以映射到 32 个地址上(变成32个字),当我们去访问这 32 个地址就达到访问 32 个比特的目的。
在这里插入图片描述
由图,SRAM最低1MB区域地址范围0X2000 0000-0X200F FFFF。
而片内外设最低1MB区域地址范围是0X4000 0000-0X400F FFFF(这个地址范围包括APB1、APB2、AHB总线上所有外设寄存器)
SRAM区还有32MB空间地址范围是0X2200 0000-0X23FF FFFF,它是SRAM中1MB位带区膨胀后的位带别名区。
片内外设区也还有32MB地址范围是0X4200 0000-0X43FF FFFF,它是片内外设中1MB位带区膨胀后的位带别名区。
通常我们使用位带操作都是在外设区,在外设区中应用比较多的也就是GPIO 外设,在SRAM区内很少使用位操作。

.
.
.

2. 位带区与位带别名区地址转换

1)外设位带别名区地址
对于片上外设位带区的某个比特, 若它所在的字节地址为 A,位序号为 n (0~8), 则该比特在别名区地址:

AliasAddr = 0x4200 0000 + [ (A-0x4000 0000) * 8 + n ] * 4

0x42000000:外设位带别名区起始地址
0x40000000:外设位带区起始地址
A-0x40000000:该比特前面有多少个字节
(A-0x40000000) * 8:该比特所在字节的第0位在外设位带区的位序号
(A-0x40000000) * 8 + n:该比特在外设位带区的位序号
一个位膨胀后是4字节,所以地址*4(地址的单位是字节)

2)SRAM 位带别名区地址
对于SRAM位带区的某个比特, 若它所在的字节地址为 A,位序号为 n (0~8), 则该比特在别名区地址:

AliasAddr = 0x2200 0000 + [ (A-0x2000 0000) * 8 + n ] * 4

0x22000000:SRAM位带别名区起始地址
0x20000000:SRAM位带区起始地址
A-0x20000000:该比特前面有多少个字节
(A-0x20000000) * 8:该比特所在字节的第0位在SRAM位带区的位序号
(A-0x20000000) * 8 + n:该比特在SRAM位带区的位序号
一个位膨胀后是4字节,所以地址*4(地址的单位是字节)

为了操作方便,我们将这两个公式合并,通过一个宏来定义,并把位带地址和位序号作为这个宏的参数。公式如下:
AliasAddr = 0x□2000000 + (A-0x□0000000) * 32 + n * 4

//求位带别名区地址
#define BITBAND(addr, bitnum)	((addr&0xF0000000)+0x2000000 + ((addr&0xFFFFF)<<5) + (bitnum<<2))

FFFFF(H)=1048575(D) > 1000000(位带区共1M字节),*32即左移5位
(addr&0xF0000000)+0x2000000:外设(SRAM)位带别名区首地址(addr&0xFFFFF):外设(SRAM)位带区最高地址为0x400FFFFF(0x200FFFFF),保留低5位,也就相当于减去0x40000000(0x20000000)
.
.
.

3. 代码实现

最后通过指针形式来操作这些位带别名区地址, 实现位带区对应位的操作。 代码如下:

/*位带操作,实现类似51的GPIO控制功能*/
#define BITBAND(addr, bitnum)	((addr&0xF0000000)+0x2000000+((addr&0xFFFFF)<<5)+(bitnum<<2))
//求取位带别名区地址后,把地址强制转换为unsigned long型指针
#define MEM_ADDR(addr)			*((volatile unsigned long *)(addr))
//把位带别名区地址转换为指针,获取地址内数据从而操作位带区对应位
#define BIT_ADDR(addr, bitnum)	MEM_ADDR(BITBAND(addr, bitnum))

/*IO口地址映射*/
#define GPIOA_IDR_Addr	(GPIOA_BASE+8)	//0x40010808	输入
#define GPIOB_IDR_Addr	(GPIOB_BASE+8)	//0x40010C08	输入
#define GPIOC_IDR_Addr	(GPIOC_BASE+8)	//0x40011008	输入
#define GPIOD_IDR_Addr	(GPIOD_BASE+8)	//0x40011408	输入
#define GPIOE_IDR_Addr	(GPIOE_BASE+8)	//0x40011808	输入
#define GPIOF_IDR_Addr	(GPIOF_BASE+8)	//0x40011A08	输入
#define GPIOG_IDR_Addr	(GPIOG_BASE+8)	//0x40011E08	输入
#define GPIOA_ODR_Addr	(GPIOA_BASE+12)	//0x4001080C	输出
#define GPIOB_ODR_Addr	(GPIOB_BASE+12)	//0x40010C0C	输出
#define GPIOC_ODR_Addr	(GPIOC_BASE+12)	//0x4001100C	输出
#define GPIOD_ODR_Addr	(GPIOD_BASE+12)	//0x4001140C	输出
#define GPIOE_ODR_Addr	(GPIOE_BASE+12)	//0x4001180C	输出
#define GPIOF_ODR_Addr	(GPIOF_BASE+12)	//0x40011A0C	输出
#define GPIOG_ODR_Addr	(GPIOG_BASE+12)	//0x40011E0C	输出

/*IO口操作,只对单一IO口*/
#define PAin(n)		BIT_ADDR(GPIOA_IDR_Addr,n)	//输入
#define PBin(n)		BIT_ADDR(GPIOB_IDR_Addr,n)	//输入
#define PCin(n)		BIT_ADDR(GPIOC_IDR_Addr,n)	//输入
#define PDin(n)		BIT_ADDR(GPIOD_IDR_Addr,n)	//输入
#define PEin(n)		BIT_ADDR(GPIOE_IDR_Addr,n)	//输入
#define PFin(n)		BIT_ADDR(GPIOF_IDR_Addr,n)	//输入
#define PGin(n)		BIT_ADDR(GPIOG_IDR_Addr,n)	//输入
#define PAout(n)	BIT_ADDR(GPIOA_ODR_Addr,n)	//输出
#define PBout(n)	BIT_ADDR(GPIOB_ODR_Addr,n)	//输出
#define PCout(n)	BIT_ADDR(GPIOC_ODR_Addr,n)	//输出
#define PDout(n)	BIT_ADDR(GPIOD_ODR_Addr,n)	//输出
#define PEout(n)	BIT_ADDR(GPIOE_ODR_Addr,n)	//输出
#define PFout(n)	BIT_ADDR(GPIOF_ODR_Addr,n)	//输出
#define PGout(n)	BIT_ADDR(GPIOG_ODR_Addr,n)	//输出

volatile关键字,volatile提醒编译器它后面所定义的变量随时可能改变, 因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有volatile关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值, 如果这个变量由别的程序更新了的话,将出现不一致的现象。

--------------------------------------------------------------------------------------------------------------------------->以上部分内容援引自《普中开发攻略》

标签:位带,SRAM,别名,STM32,地址,操作,位带区,外设
来源: https://blog.csdn.net/weixin_45442591/article/details/114521992