STM32中位带操作

stm32中的位带操作

1.基础概念回顾

1…什么是位操作?
位操作就是可以单独对一个比特位
2.什么是位带操作?
支持了位带操作后,可以使用普通的加载/存储指令来对单一的比特讲行读写。在CM3中,有两个区中实现了位带。其中一个是SRAM区的最低1MB范围,第二个则是片内外设区的最低1MB范围。这两个位带中的地址除了可以像普通的RAM一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。
3.什么是位带区域?
位带区,就是就是你想单独操作的IO的区域,也就是PA,PB……等这一堆IO口的内存所在区
4.什么是位带别名区?
位带别名区,就是你给每一位重新起了个名字的那一片地址区域
5.位带区域与位带别名区的操作公式
1.对于SRAM位带区的某个比特,记它所在字节地址为A,位序号为n(0<=n<=7),则该比特在别名区的地址为:

 AliasAddr=0x22000000+((A-0x20000000)*8+n)*4 =Ox22000000+ (A-Ox20000000)*32 +n*4

2.对于片上外设位带区的某个比特,记它所在字节的地址为A,位序号为n(<=n<=7),则该比特在别名区的地址为:

AliasAddr=0x42000000+((A-0x40000000)*8+n)*4 =0x42000000+ (A-Ox40000000)*32 +n*4

5.位带区域与位带别名区的操作公式为何是这样的?
5.1先看存储器映像
在这里插入图片描述
在上图6.4中,被控单元的FLASH,RAM,FSMC和AHB到APB的桥(即片上外设),这些功能部件共同排列在一个4GB的地址空间内。我们在编程的时候,可以通过他们的地址找到他们,然后来操作他们。(通过c语言对他们进行数据的读和写)
在这里插入图片描述
在这4GB的地址空间中,ARM已经粗线条的平均分成了8个块,每块512MB,每个块也都规定了用途,具体分类见表格6-1。每个块的大小都有512MB,显然这是非常大的。
在这里插入图片描述
我们知道在CM3中,有两个地方实现了位带,一个是SRAM区最低1MB空间。一个是外设区最低1MB空间。
在这里插入图片描述

 AliasAddr=0x22000000+((A-0x20000000)*8+n)*4 =Ox22000000+ (A-Ox20000000)*32 +n*4
/*
则在SRAM区
AliasAddr是别名区的地址
0x22000000是SRAM位带别名区的起始地址
A是SRAM位带区的某个比特,记它所在字节的地址为A,位序号为n(0<=n<=7)
(A-0x20000000)代表该比特前面有多少个字节,一个字节8位所以乘以8,
一个位膨胀后是4字节,所以乘以4.n表示该比特在A地址的序号,因为一
个位膨胀后是4字节,所以也*4
*/

2.如何进行位带操作

你想进行位带操作去操作某个IO口的某一位,那么在STM32的环境下,你应该去找该位对应的别名区的地址,找到了这个地址,对这个地址进行操作,那么实际上也就是对该位进行操作了.

2.1统一公式:
1 //把"位带地址+位序号"转换成别名地址的宏
2 #define BITBAND(addr, bitnum)  ((addr & 0xF0000000)+0x02000000+ ( (addr & 0x00FFFFFF)<<5)+(bitnum<<2))
/*
addr & 0xF0000000是为了区别SRAM还是外设,实际效果就是取出4或者2,如果是外设则取出4+0x02000000就是0x42000000
addr & 0x00FFFFFF 屏蔽了高三位,相当于减去0x20000000或者0x40000000
为何屏蔽高三位?
因为外设地址最高地址是:0x20100000,和起始地址0x20000000之间相减时候
总是低五位才有效,所以才把高三位屏蔽来达到减去地址的效果,而<<5相当于
*4*8.<<2相当于*4
*/
2.2GPIO位带操作示例

这里我们通过设置GPIOx_IDR(端口输入数据寄存器)和GPIOx_ODR(端口输出数据寄存器)来进行操作。
在这里插入图片描述
在这里插入图片描述

查数据手册可知:GPIOx_IDR偏移地址是0x10(16)而 GPIOx_ODR的偏移地址是0x14(20)
则:

#ifndef __LED_H__
#define __LED_H__

//IO口操作宏定义
#define BITBAND(addr, bitnum) (0x42000000 +((addr-0x40000000)<<5)+(bitnum<<2)) 
#define MEM_ADDR(addr)  *((volatile unsigned long  *)(addr)) 
#define BIT_ADDR(addr, bitnum)   MEM_ADDR(BITBAND(addr, bitnum)) 
//IO口地址映射
#define GPIOA_ODR_Addr    (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr    (GPIOB_BASE+20) //0x40020414 
#define GPIOC_ODR_Addr    (GPIOC_BASE+20) //0x40020814 
#define GPIOD_ODR_Addr    (GPIOD_BASE+20) //0x40020C14 
#define GPIOE_ODR_Addr    (GPIOE_BASE+20) //0x40021014 
#define GPIOF_ODR_Addr    (GPIOF_BASE+20) //0x40021414    
#define GPIOG_ODR_Addr    (GPIOG_BASE+20) //0x40021814   
#define GPIOH_ODR_Addr    (GPIOH_BASE+20) //0x40021C14    
#define GPIOI_ODR_Addr    (GPIOI_BASE+20) //0x40022014     

#define GPIOA_IDR_Addr    (GPIOA_BASE+16) //0x40020010 
#define GPIOB_IDR_Addr    (GPIOB_BASE+16) //0x40020410 
#define GPIOC_IDR_Addr    (GPIOC_BASE+16) //0x40020810 
#define GPIOD_IDR_Addr    (GPIOD_BASE+16) //0x40020C10 
#define GPIOE_IDR_Addr    (GPIOE_BASE+16) //0x40021010 
#define GPIOF_IDR_Addr    (GPIOF_BASE+16) //0x40021410 
#define GPIOG_IDR_Addr    (GPIOG_BASE+16) //0x40021810 
#define GPIOH_IDR_Addr    (GPIOH_BASE+16) //0x40021C10 
#define GPIOI_IDR_Addr    (GPIOI_BASE+16) //0x40022010 
 //单一IO口操作
#define PAout(n)   BIT_ADDR(GPIOA_ODR_Addr,n)  
#define PAin(n)    BIT_ADDR(GPIOA_IDR_Addr,n)  

#define PBout(n)   BIT_ADDR(GPIOB_ODR_Addr,n) 
#define PBin(n)    BIT_ADDR(GPIOB_IDR_Addr,n) 

#define PCout(n)   BIT_ADDR(GPIOC_ODR_Addr,n) 
#define PCin(n)    BIT_ADDR(GPIOC_IDR_Addr,n)  

#define PDout(n)   BIT_ADDR(GPIOD_ODR_Addr,n)  
#define PDin(n)    BIT_ADDR(GPIOD_IDR_Addr,n)  

#define PEout(n)   BIT_ADDR(GPIOE_ODR_Addr,n) 
#define PEin(n)    BIT_ADDR(GPIOE_IDR_Addr,n) 

#define PFout(n)   BIT_ADDR(GPIOF_ODR_Addr,n) 
#define PFin(n)    BIT_ADDR(GPIOF_IDR_Addr,n) 

#define PGout(n)   BIT_ADDR(GPIOG_ODR_Addr,n) 
#define PGin(n)    BIT_ADDR(GPIOG_IDR_Addr,n) 

#define PHout(n)   BIT_ADDR(GPIOH_ODR_Addr,n)  
#define PHin(n)    BIT_ADDR(GPIOH_IDR_Addr,n) 

#define PIout(n)   BIT_ADDR(GPIOI_ODR_Addr,n)  
#define PIin(n)    BIT_ADDR(GPIOI_IDR_Addr,n)  
//°´¼ü³õʼ»¯º¯Êý
void led_init(void);

#endif

在主函数里调用即可

#include "stm32f4xx.h"
#include "led.h"

//延时
void delay(int x)
{
    
    
	int i,j;
	for(i=0;i<x;i++)
		for(j=0;j<10000;j++);
}

int main()
{
    
    
	led_init();
	while(1)
	{
    
    
			PAout(6)=0;//设置PA6为低电平,点亮led灯
		delay (100);
			PAout(6)=1;//设置PA6为高电平,熄灭led灯
		delay (100);
	}

}

猜你喜欢

转载自blog.csdn.net/qq_44772972/article/details/113354631