004 - STM32学习笔记 - 位带操作

STM32中的位带操作类似51单片机的sbit,允许直接对GPIO等寄存器的位进行操作。文章介绍了STM32在外设和SRAM区域的位带实现,详细解析了位带别名区的地址计算方法,并给出了宏定义简化编程的示例。通过位带操作,可以更方便地控制和检测引脚状态。
摘要由CSDN通过智能技术生成

004 - STM32学习笔记 - 位带操作

目前掌握的对GPIO引脚的输入输出操作只能使用BSRRL/H、I/ODR寄存器,记得以前学51的时候,对于引脚的输入输出可以采用关键字sbit实现位定义,例如

sbit LED1 = P1^3;

在STM32中没有类似于sbit一样的关键字,但是提供了位带操作来实现类似于51的为才做。
在这里插入图片描述
图中可以看到,STM32在两个地方实现了位带。一个是SRAM区,另外一个是Peripherals(外设)区,以外设区为例,可以看到外设区位带实际占1MB空间,前面学习的寄存器操作都是在这个空间内操作的,对应的将外设的空间膨胀32倍,膨胀后的空间就是外设的位带别名区。
外设的寄存器位带区空间为0x400000000 ~ 0x400F0000,这1MB的空间包含了APB1/2和AHB1上所有外设的寄存器,AHB2/3总线上的寄存器没有包含在内。经过膨胀后,外设位带别名区的地址范围为0x42000000 ~ 0x43FFFFFF。
SRAM的不在这里说明,具体可以参照上图结合外设位带别名区的办法进行分析。
位带别名区地址的计算
片上外设的位带区某个寄存器的地址为A,位序号为n(0 ≤ n ≤ 7),那么这个寄存器的位带别名区地址应该为:

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

例如F429开发板上的PH10的ODR寄存器 A = GPIOH_BASE+0x14,n = 10,那么其位带别名区的地址是:

Alias_Ph10_ODR= 0x42000000 + ( GPIOH_BASE + 0x14 -0x40000000) * 8 * 4 + 10 * 4

另外,这里给出SRAM的位带别名区的计算公式:

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

整合一下,外设和SRAM可以用一个统一公式来计算位带别名区的地址:

/*addr为外设寄存器地址,bitnum为位号(0 ≤ n ≤ 7)*/
AliasAddr = (addr & 0xF0000000) + 0x02000000 + ((addr & 0x000FFFFF) << 5) + (bitnum << 2)

公式分析:
①、addr & 0xF0000000是为了区别当前计算的是外设区还是SRAM区,比如地址为0x40006789,那么与0xF0000000与运算后,得结果为0x40000000,再加上0x02000000就等于0x42000000,那么就得出来这个寄存器为外设区寄存器,如果是0x22000000,那么就是SRAM区寄存器等同于原始公式中的0x42000000或者0x22000000
②、addr & 0x000FFFFF则是屏蔽高三位,取出低五位的地址,0x40006789计算后,取出值为0x06789,等同于原始公式中的A-0x40000000或者A-0x20000000
③、<< 5<< 2则是通过位运算分别提到了* 8 * 4* 4
用以上公式转出的位带别名区地址还不能立即使用,此时转换出来的地址是个立即数,并不能当作地址去使用,需要进行转换,这里用两个宏进行最终的操作:

//计算位带别名区地址,并转换为地址指针
#define BITBAND(addr,bitnum)    (*(__IO unsigned long *)((addr & 0xF0000000) + 0x2000000+((addr & 0xFFFFF) << 5) + (bitnum << 2))) 

//最终操作的寄存器
#define BIT_ACTION(addr,bitnum)   BITBAND(addr,bitnum)

位带操作的内容基本就这些,不太明白的话可以在看一下完整的程序。
bitband.h

#ifndef __BSP_BIT_BAND_H__
#define __BSP_BIT_BAND_H__

#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"

//计算位带别名区地址
#define BITBAND(addr,bitnum)    (*(__IO unsigned long *)((addr & 0xF0000000) + 0x2000000+((addr & 0xFFFFF) << 5) + (bitnum << 2))) 

#define BIT_ACTION(addr,bitnum)   BITBAND(addr,bitnum)

/*A-H端口ODR地址*/
#define GPIOA_ODR_ADDR  (GPIOA_BASE+0x14)
#define GPIOB_ODR_ADDR  (GPIOB_BASE+0x14)
#define GPIOC_ODR_ADDR  (GPIOC_BASE+0x14)
#define GPIOD_ODR_ADDR  (GPIOD_BASE+0x14)
#define GPIOE_ODR_ADDR  (GPIOE_BASE+0x14)
#define GPIOF_ODR_ADDR  (GPIOR_BASE+0x14)
#define GPIOG_ODR_ADDR  (GPIOG_BASE+0x14)
#define GPIOH_ODR_ADDR  (GPIOH_BASE+0x14)

/*A-H端口IDR地址*/
#define GPIOA_IDR_ADDR  (GPIOA_BASE+0x10)
#define GPIOB_IDR_ADDR  (GPIOB_BASE+0x10)
#define GPIOC_IDR_ADDR  (GPIOC_BASE+0x10)
#define GPIOD_IDR_ADDR  (GPIOD_BASE+0x10)
#define GPIOE_IDR_ADDR  (GPIOE_BASE+0x10)
#define GPIOF_IDR_ADDR  (GPIOR_BASE+0x10)
#define GPIOG_IDR_ADDR  (GPIOG_BASE+0x10)
#define GPIOH_IDR_ADDR  (GPIOH_BASE+0x10)

/*A-H端口输出宏*/
#define PAout(n)    BIT_ACTION(GPIOA_ODR_ADDR,n)
#define PBout(n)    BIT_ACTION(GPIOB_ODR_ADDR,n)
#define PCout(n)    BIT_ACTION(GPIOC_ODR_ADDR,n)
#define PDout(n)    BIT_ACTION(GPIOD_ODR_ADDR,n)
#define PEout(n)    BIT_ACTION(GPIOE_ODR_ADDR,n)
#define PFout(n)    BIT_ACTION(GPIOF_ODR_ADDR,n)
#define PGout(n)    BIT_ACTION(GPIOG_ODR_ADDR,n)
#define PHout(n)    BIT_ACTION(GPIOH_ODR_ADDR,n)

/*A-H端口输入宏*/
#define PAin(n)    BIT_ACTION(GPIOA_IDR_ADDR,n)
#define PBin(n)    BIT_ACTION(GPIOB_IDR_ADDR,n)
#define PCin(n)    BIT_ACTION(GPIOC_IDR_ADDR,n)
#define PDin(n)    BIT_ACTION(GPIOD_IDR_ADDR,n)
#define PEin(n)    BIT_ACTION(GPIOE_IDR_ADDR,n)
#define PFin(n)    BIT_ACTION(GPIOF_IDR_ADDR,n)
#define PGin(n)    BIT_ACTION(GPIOG_IDR_ADDR,n)
#define PHin(n)    BIT_ACTION(GPIOH_IDR_ADDR,n)

#endif  /* __BSP_BIT_BAND_H__ */

bsp_key.c中增加按键检测扫描函数Key1_Scan()和Key2_Scan()

uint8_t Key1_Scan(void)
{

        if(PAin(0) == KEY_ON)	        //判断按键是否按下
        {
            while(PAin(0) == KEY_ON);	//消抖,等待按键释放
            return KEY_ON;
        }
        else
            return KEY_OFF;
}

uint8_t Key2_Scan(void)
{
        if(PCin(13) == KEY_ON)       	//判断按键是否按下
        {
            while(PCin(13) == KEY_ON);   //消抖,等待按键释放
            return KEY_ON;
        }
        else
            return KEY_OFF;
}

main.c

#include "stm32f4xx.h"
#include ".\LED\bsp_led.h"
#include ".\KEY\bsp_key.h"
#include ".\BIT_BAND\bsp_bit_band.h"


int main(void)
{
    LED_Config();    
    Key_Config();   
    while(1)
    {
        if(Key1_Scan() == KEY_ON)		//K1按键检测
        {
            PHout(10) = 0;				//红灯亮
        }
        if(Key2_Scan() == KEY_ON)		//K2按键检测
        {
            PHout(10) = 1;				//红灯灭
        }       
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

放学校门口见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值