在STM32中使用位带操作(参考Cortex-M3权威指南,STM32中文参考手册,DS小龙哥教学视频)

2,139 阅读4分钟

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+((A0x20000000)*8+n)*4=0x22000000+ (A0x20000000)*32 + n*4   

对片上外设位带区的某个比特,记它所在字节地址为 A,位序号为 n(0<=n<=7),则该比特在别名区的地址为:

AliasAddr=0x42000000+((A0x40000000)*8+n)*4=0x42000000+ (A0x40000000)*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;
	 }
 }
}