F407之位带操作

位带操作是STM32F4系列微控制器中用于方便地读写单个IO口的一种技术。它将每个比特位映射到32位地址空间,通过访问特定地址实现对GPIO等寄存器的位级操作。例如,使用位带操作可以像51单片机那样简单地设置或清除GPIO端口的某个位。文章提到了位带操作的映射公式,并给出了GPIO操作的宏定义示例,使得开发人员能够更高效地控制硬件接口。
摘要由CSDN通过智能技术生成

位带操作的应用场景,通常在于对IO口进行输入输出读取和控制。

这就和51单片机中直接的端口赋值是一样的。

比如

P0.0 = 0;

P0.0 = 1;

直接就是对端口P0.0输出0和1。

如何实现呢?

什么是位带操作

位带操作简单的说,就是把每个比特膨胀为一个32位的字,当访问这些字的时候就达到了访问比特的目的,比如说GPIOODR寄存器有32个位,那么可以映射到32个地址上,我们去访问这32个地址就达到访问32个比特的目的。这样我们往某个地址写1就达到往对应比特位写1的目的,同样往某个地址写0就达到往对应的比特位写0的目的。

对于上图,我们往Address0地址写入1,那么就可以达到往寄存器的第0Bit0赋值1的目的。这里我们不讲得过于复杂,因为位带操作在实际开发中可能只是用来IO口的输入输出还比较方便,其他操作在日常开发中也基本很少用。

手册中的相关介绍

Cortex™-M4F 存储器映射包括两个位段区域。这些区域将存储器别名区域中的每个字映射 到存储器位段区域中的相应位。在别名区域写入字时,相当于对位段区域的目标位执行读-

-写操作。

STM32F4xx 器件中,外设寄存器和 SRAM 均映射到一个位段区域,这样可实现单个位段的读写操作。这些操作仅适用于Cortex™-M4F访问,对于其它总线主接口(如 DMA)无效。

可通过一个映射公式说明别名区域中的每个字与位段区域中各个位之间的对应关系。映射公

式为:

bit_word_addr = bit_band_base + (byte_offset x 32) + (bit_number × 4)

其中:

bit_word_addr 代表别名区域中将映射到目标位的字的地址

bit_band_base 代表别名区域的起始地址

byte_offset 代表目标位所在位段区域中的字节编号

bit_number 代表目标位的位位置 (0-7)

下例说明如何将 SRAM 地址 0x20000300 处字节的位 2 映射到别名区域:

0x22006008 = 0x22000000 + (0x300*32) + (2*4)

对地址 0x22006008 执行写操作相当于在 SRAM 地址 0x20000300 处字节的位 2 执行读-

-写操作。

对地址 0x22006008 执行读操作将返回 SRAM 地址 0x20000300 处字节的位 2 的值(0x01

表示位置位,0x00 表示位复位)。

有关位段的详细信息,请参见Cortex™-M4F编程手册。

具体定义

映射关系如下所示

具体不深究了,大概知道映射公式是这样的就行

膨胀关系示意图

通常可在头文件中定义

//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).M4同M3类似,只是寄存器地址变了.
//IO口操作宏定义
#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)) 
//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口操作,只对单一的IO口!
//确保n的值小于16!
#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)  //输入

以上代码的便是 GPIO 位带操作的具体实现,位带操作的详细说明,在权威指南中有详细讲解,请参考<<CM3 权威指南>>第五章(87 ~92 )。比如说,我们调用 PAout(1)=1是设置了 GPIOA 的第一个管脚 GPIOA.1 1,实际是设置了寄存器的某个位,但是我们的定义中可以跟踪过去看到却是通过计算访问了一个地址。上面一系列公式也就是计算GPIO 的某个 io 口对应的位带区的地址了。有了上面的代码,我们就可以像51/AVR一样操作STM32IO口了。比如,我要PORTA的第七个 IO 口输出 1,则可以使用 PAout(6)=1;即可实现。我要判断 PORTA 的第15个位是否等于 1,则可以使用 ifPAin14==1)…;就可以了。

注意:

使用前必须先进行IO口时钟的使能和 IO口功能初始化。

补充了解

STM32F407的位操作是指通过位别名区域来对特定的位进行操作,以提高代码的执行效率和简化编程过程。 在STM32F407中,位别名区域是一段特殊的内存区域,用来映射特定位的地址,以此来实现对这些位的直接访问和修改。通过位操作,我们可以通过直接读取和写入一个位的别名地址来实现对该位的操作,而不需要进行位运算或移位操作。 位别名区域的地址在内存映射中通常是高于SRAM的地址范围,所以它不会和其他数据冲突。在使用位操作时,首先需要确定要操作的位在内存中的位置,并计算其别名地址。然后,可以通过读取或写入该别名地址来操作这个位。 位操作的主要优势是提高代码的执行效率。因为在执行位操作时,可以直接换位别名地址的读写操作,从而避免了对位进行逐位运算的过程,大大节省了时间。此外,位操作还可以简化代码的书写,使代码更加清晰和易读。 需要注意的是,位操作只能在位可选操作的寄存器或内存位置上进行。对于不支持位操作的寄存器或内存位置,仍然需要使用位运算或移位操作。另外,由于位别名区域的映射地址与SRAM相隔较远,所以在使用位操作时需要额外的地址计算,这也可能增加了代码的复杂性。 总而言之,STM32F407的位操作是一种通过位别名区域来对特定位进行操作的方法。它可以提高代码执行效率和简化编程过程,但需要在位可选操作的寄存器或内存位置上进行,并需要进行额外的地址计算。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值