1、概念
这边文章讲在STM32中如何使用位带操作,位带操作表示是用普通的加载/存储指令来对每一个位进行读写,类似于51单片机的使用,sbit LED1=P1^1,可以对每一个IO口可以直接进行操作进行赋值运算,实现高低电平的输入。
2、位带区
位带区:支持位带操作的地址区; 位带别名:对别名地址的访问最终作用到位带区的访问上(注意:这中途有一个地址映射过程) 在CM3中,在SRAM 、片内外设两个区中实现了位带。这两个区中的地址除了可以像普通的 RAM 一样使用外,它们还都有自己的“位带别名区”,位带别名区把每个比特膨胀成一个 32 位的字。当你通过位带别名区访问这些字时,就可以达到访问原始比特的目的。 支持位带操作的两个内存区的范围是: 0x2000_0000‐0x200F_FFFF(SRAM 区中的最低 1MB),0x4000_0000‐0x400F_FFFF(片上外设区中的最低 1MB)。
对 SRAM 位带区的某个比特,记它所在字节地址为 A,位序号为 n(0<=n<=7),则该比特在别名区的地址为:
AliasAddr=0x22000000+((A‐0x20000000)*8+n)*4=0x22000000+ (A‐0x20000000)*32 + n*4
对片上外设位带区的某个比特,记它所在字节地址为 A,位序号为 n(0<=n<=7),则该比特在别名区的地址为:
AliasAddr=0x42000000+((A‐0x40000000)*8+n)*4=0x42000000+ (A‐0x40000000)*32 + n*4
SRAM 区中的位带地址映射:
位带区 等效的别名地址
0x20000000.0 0x22000000.0
0x20000000.1 0x22000004.0
0x20000000.2 0x22000008.0
...
0x20000000.31 0x2200007C.0
0x20000004.0 0x22000080.0
0x20000004.1 0x22000084.0
0x20000004.2 0x22000088.0
...
0x200FFFFC.31 0x23FFFFFC.0
对于片上外设区中的位带地址映射
位带区 等效的别名地址
0x40000000.0 0x42000000.0
0x40000000.1 0x42000004.0
0x40000000.2 0x42000008.0
…
0x40000000.31 0x4200007C.0
0x40000004.0 0x42000080.0
0x40000004.1 0x42000084.0
0x40000004.2 0x42000088.0
…
0x400FFFFC.31 0x43FFFFFC.0
3、在 C 语言中使用位带操作(参考: Cortex-M3权威指南)
为了我们可以更加方便使用位带操作,我们可以定义宏,核心思想:“位带地址(基地址)+位序号(偏移量)”转换成别名地址的宏,再建立一个把别名地址转换成指针类型的宏。 //把“位带地址+位序号”转换成别名地址的宏 addr表示寄存器的基地址,bitnum表示寄存器里的第几位
//addr表示寄存器的基地址,bitnum表示寄存器里的第几位
#define BITBAND(addr,bitnum) ((addr&0xF0000000)+0x2000000+((addr&0xFFFFF)<<5)+(bitnum<<2))
//将地址转换为指针类型
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
4、实际应用
在正点原子的MiniSTM32F103开发板,芯片为STM32F103RET6使用位带操作点亮LED灯。 sys.h文件
//完成位带地址的转换
#define BITBAND(addr,bitnum) ((addr&0xF0000000)+0x2000000+((addr&0xFFFFF)<<5)+(bitnum<<2))
//将地址转换为指针类型
#define MEM_ADDR(addr) *((volatile u32 *)addr)
/*定义GPIO口输入输出寄存器的地址*/
#define GPIOA_IDR (0x40010800+0x8)
#define GPIOA_ODR (0x40010800+0xC)
#define GPIOB_IDR (0x40010C00+0x8)
#define GPIOB_ODR (0x40010C00+0xC)
#define GPIOC_IDR (0x40011000+0x8)
#define GPIOC_ODR (0x40011000+0xC)
#define GPIOD_IDR (0x40011400+0x8)
#define GPIOD_ODR (0x40011400+0xC)
#define GPIOE_IDR (0x40011800+0x8)
#define GPIOE_ODR (0x40011800+0xC)
#define GPIOF_IDR (0x40011C00+0x8)
#define GPIOF_ODR (0x40011C00+0xC)
#define GPIOG_IDR (0x40012000+0x8)
#define GPIOG_ODR (0x40012000+0xC)
/*定义GPIO口位操作宏*/
#define PAin(bitnum) MEM_ADDR(BITBAND(GPIOA_IDR,bitnum))
#define PAout(bitnum) MEM_ADDR(BITBAND(GPIOA_ODR,bitnum))
#define PBin(bitnum) MEM_ADDR(BITBAND(GPIOB_IDR,bitnum))
#define PBout(bitnum) MEM_ADDR(BITBAND(GPIOB_ODR,bitnum))
#define PCin(bitnum) MEM_ADDR(BITBAND(GPIOC_IDR,bitnum))
#define PCout(bitnum) MEM_ADDR(BITBAND(GPIOC_ODR,bitnum))
#define PDin(bitnum) MEM_ADDR(BITBAND(GPIOD_IDR,bitnum))
#define PDout(bitnum) MEM_ADDR(BITBAND(GPIOD_ODR,bitnum))
#define PEin(bitnum) MEM_ADDR(BITBAND(GPIOE_IDR,bitnum))
#define PEout(bitnum) MEM_ADDR(BITBAND(GPIOE_ODR,bitnum))
#define PFin(bitnum) MEM_ADDR(BITBAND(GPIOF_IDR,bitnum))
#define PFout(bitnum) MEM_ADDR(BITBAND(GPIOF_ODR,bitnum))
#define PGin(bitnum) MEM_ADDR(BITBAND(GPIOG_IDR,bitnum))
#define PGout(bitnum) MEM_ADDR(BITBAND(GPIOG_ODR,bitnum))
#endif
led.h文件
#ifndef LED_H
#define LED_H
#include "stm32f10x.h"
#include "sys.h"
void LED_Init(void);
#define LED0 PAout(8)
#define LED1 PDout(2)
#endif
main.c文件
#include "stm32f10x.h"
#include "led.h"
#include "delay.h"
#include "key.h"
#include "sys.h"
int main(void)
{
int Value=0;
KEY_Init();
LED_Init`();»`
while(1)
{
Value=KEY_GetValue();
switch(Value)
{
case 1:
LED1=!LED1;
break;
case 2:
LED0=!LED0;
break;
case 3:
LED1=!LED1;
LED0=!LED0;
break;
}
}
}