stm32f4的位带操作

1.位带介绍
通过访问位带别名区,将每个比特位膨胀成一个32位字,当访问这些字的时候就达到了访问比特的目的。
比方说BSRR寄存器有32个位,那么可以映射到32个地址上,当访问这32个地址就达到访问32个比特的目的
2.位带区和位带别名区的地址转换
支持位带操作的区域是SRAM区的最低1M范围和片内外设区的最低1M范围
一般使用外设区的位带,我们只需要操作位带别名区的地址就可以操作位带区的地址
外设位带区与外设位带别名区的地址转换公式:
AliasAddr = 0x42000000 + (A-0x40000000)84 + n4
A是我们要操作的位所在的寄存器地址,n是位序号
(位带区的一个位在位带别名区会被膨胀成4个字节)
SRAM位带区与SRAM位带别名区的地址转换公式:
AliasAddr = 0x22000000 +(A-0x20000000)
84 + n4
将两个公式合并成为一个
((A & 0xF0000000)+0x02000000+((A & 0X000FFFFF)<<5)+(n<<2))
左移五位相当于乘以32,左移两位相当于乘以4
3.位带操作的优点
(1)控制GPIO输入输出非常简单
(2)操作串行接口芯片非常方便(DS1302、74HC595等)。
(3)代码简洁,阅读方便。

通过位带操作,控制LED进行闪烁(也就是控制外设管脚)
通过上述的公式 我们了解到了 需要通过操作位带别名区来对位带区进行操作,
我们需要再添加两个文件,名为system.c和system.h来对位带进行配置
system.h
首先我们要得到从位带区转移到位带别名区的地址,所以define一个BITBAND(addr,bitnum)
addr是我们要操作的位所在的寄存器地址,bitnum是位序号,代码如下:

#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))
/到这里我们进行的封装是,MEM_ADDR(addr)、BIT_ADDR(addr,bitnum)
MEM_ADDR是将地址转换为指针操作,直接对地址进行操作,然后MEM_ADDR(BITBAND(addr,bitnum))是对
位带别名区的地址进行转换,得到的值是一个指针,然后宏定义为BIT_ADDR(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) //输入

#endif

system.c文件中只需要包含.h文件就行,然后再main.c和led.h中
包含system.h
再把两个文件添加到工程里,不报错就行

接下来是点亮LED,
led1对应PF9,LED2对应PF10所以
再LED_Init中使能F端口时钟,将管脚设置为9和10,代码如下(led.c):

#include “led.h”

/***

  • 函 数 名 : LED_Init

  • 函数功能 : LED初始化函数

  • 输 入 : 无

  • 输 出 : 无

  • **/
    void LED_Init()
    {
    GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE); //使能端口F时钟

    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT; //输出模式
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;//管脚设置F9
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度为100M
    GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出
    GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//上拉
    GPIO_Init(GPIOF,&GPIO_InitStructure); //初始化结构体
    GPIO_ResetBits(GPIOF,GPIO_Pin_9|GPIO_Pin_10);
    }

main函数中不需要大量的代码来点亮使能管脚再点亮led,代码如下:
int main()
{
LED_Init();
while(1)
{
led1=!led1; //D1状态取反
delay(6000000);
}
}
这样就是stm32f4的位带操作