打印
[其他ST产品]

STM32学习笔记 - 位带操作

[复制链接]
238|6
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
qn7a12|  楼主 | 2023-7-27 23:42 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
STM32学习笔记 - 位带操作
目前掌握的对GPIO引脚的输入输出操作只能使用BSRRL/H、I/ODR寄存器,记得以前学51的时候,对于引脚的输入输出可以采用关键字sbit实现位定义,例如

sbit LED1 = P1^3;
1
在STM32中没有类似于sbit一样的关键字,但是提供了位带操作来实现类似于51的为才做。

图中可以看到,STM32在两个地方实现了位带。一个是SRAM区,另外一个是Peripherals(外设)区,以外设区为例,可以看到外设区位带实际占1MB空间,前面学习的寄存器操作都是在这个空间内操作的,对应的将外设的空间膨胀32倍,膨胀后的空间就是外设的位带别名区。
外设的寄存器位带区空间为0x400000000 ~ 0x400F0000,这1MB的空间包含了APB1/2和AHB1上所有外设的寄存器,AHB2/3总线上的寄存器没有包含在内。经过膨胀后,外设位带别名区的地址范围为0x42000000 ~ 0x43FFFFFF。
SRAM的不在这里说明,具体可以参照上图结合外设位带别名区的办法进行分析。

使用特权

评论回复
沙发
qn7a12|  楼主 | 2023-7-27 23:43 | 只看该作者
位带别名区地址的计算
片上外设的位带区某个寄存器的地址为A,位序号为n(0 ≤ n ≤ 7),那么这个寄存器的位带别名区地址应该为:

AliasAddr = 0x42000000+(A-0x40000000) * 8 * 4 + n * 4
1
例如F429开发板上的PH10的ODR寄存器 A = GPIOH_BASE+0x14,n = 10,那么其位带别名区的地址是:

Alias_Ph10_ODR= 0x42000000 + ( GPIOH_BASE + 0x14 -0x40000000) * 8 * 4 + 10 * 4
1
另外,这里给出SRAM的位带别名区的计算公式:

AliasAddr = 0x22000000+(A-0x20000000) * 8 * 4 + n * 4
1
整合一下,外设和SRAM可以用一个统一公式来计算位带别名区的地址:

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

使用特权

评论回复
板凳
qn7a12|  楼主 | 2023-7-27 23:43 | 只看该作者
公式分析:
①、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;

使用特权

评论回复
地板
qn7a12|  楼主 | 2023-7-27 23:43 | 只看该作者
用以上公式转出的位带别名区地址还不能立即使用,此时转换出来的地址是个立即数,并不能当作地址去使用,需要进行转换,这里用两个宏进行最终的操作:
//计算位带别名区地址,并转换为地址指针
#define BITBAND(addr,bitnum)    (*(__IO unsigned long *)((addr & 0xF0000000) + 0x2000000+((addr & 0xFFFFF) << 5) + (bitnum << 2)))

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

使用特权

评论回复
5
qn7a12|  楼主 | 2023-7-27 23:44 | 只看该作者
位带操作的内容基本就这些,不太明白的话可以在看一下完整的程序。
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__ */

使用特权

评论回复
6
qn7a12|  楼主 | 2023-7-27 23:44 | 只看该作者
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;
}

使用特权

评论回复
7
qn7a12|  楼主 | 2023-7-27 23:44 | 只看该作者
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;                                //红灯灭
        }      
    }
}

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

31

主题

507

帖子

1

粉丝