通过位带地址操作GPIO在数码管显示数字(STM32_05)

时间:2024-03-26 13:21:52

一、什么是位带操作

位带操作简单讲就是将一个对二进制位的操作映射到一个32位的地址上,通过给这个地址置0或1来给这个二进制位置0或1。

二、CM3的位带操作

在CM3支持的位段中,有两个区中实现了位段。

其中一个是 SRAM 区的最低 1MB 范围,0x20000000‐0x200FFFFF(SRAM 区中的最低 1MB);

第二个则是片内外设区的最低 1MB范围,0x40000000‐0x400FFFFF(片上外设区中的最低 1MB)。

通过位带地址操作GPIO在数码管显示数字(STM32_05)

从上图可以看出,SRAM中地址为0x20000000单元的32位Bit可以分别被映射到0x22000000-0x2200008F的128字节的地址范围中,即0x20000000-0x20000003的4个字节的32位Bit被分别映射到地址为0x22000000, 0x22000004, 0x22000008, 0x2200000C, 0x22000010, …, 0x22000080, 0x22000084,0x22000088, 0x2200008C的32个地址上。

    同样,对片内外设寄存器的操作也是类似的,只不过地址的范围不同。片内外设中地址为0x40000000单元的32位Bit可以分别被映射到0x42000000-0x2200008F的128字节的地址范围中,即0x40000000-0x40000003的4个字节的32位Bit被分别映射到地址为0x42000000, 0x42000004, 0x42000008, 0x4200000C, 0x42000010, …, 0x42000080, 0x42000084,0x42000088, 0x4200008C的32个地址上。

三、编程中如何进行位带地址转换

对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~0x200FFFFF之间,而在片内外设的某个地址肯定在0x40000000~0x400FFFFF之间,假设地址为A,可以通过:

A &0xF0000000 + 0x02000000

得到地址为A的数据对应的位带区的首地址,后面的(A-0x20000000)*32+n*4和(A-0x40000000)*32+n*4可以统一为(A-A&0xF0000000)*32+n*4,则两个区间的位带地址的表达式可以统一为:

AliasAddr= (A& 0xF0000000 + 0x02000000)+ (A-A&0xF0000000)*32+n*4

将乘法运算换成左移位运算,则表示为:

AliasAddr= (A& 0xF0000000 + 0x02000000)+ ((A-A&0xF0000000)<<5)+(n<<2)

而(A-A&0xF0000000)实际上就是地址A相对于0x20000000或者0x40000000的偏移地址,只需要保留低20位,高12位取0即可,所以(A-A&0xF0000000)等效于:A&0xFFFFF

在C编程中定义如下宏进行地址转换:

#defineBITBAND(addr,n) ((addr&0xF0000000) + 0x02000000 +((addr&0xFFFFF)<<5) + (n<<2))

下面的宏将地址转换成变量形式:

#defineMEM_ADDR(addr) *((volatile unsigned long *)(addr))

下面的宏将某一具体地址及第n位映射到位带

#defineBIT_ADDR(addr,n) MEM_ADDR(BITBAND(addr,n))

有了这些宏定义,就可以对位映射到位带区了。例如对GPIOA的数据输出寄存器的第0为操作,GPIOA的基地址为0x40010800,而端口输出寄存器GPIOx_ODR的地址偏移为12,则GPIOA_ODR寄存器的地址为0x4001080C,可以定义如下的宏:

#definePAOut(n)  BIT_ADDR(0x4001080C , n)

这样,如果在程序中想在GPIOA_0输出低电平,只需要如下赋值语句:

PAOut(0) = 0;

四、应用实例

1、硬件连接:现在将STM32F103ZET6的GPIOC的0-7引脚连接到共阳数码管的a-h端。

2、采用STM32F10X外设库函数,这里使用到stm32f10x_rcc.h, stm32f10x_rcc.c, stm32f10x_gpio.h, stmf10x_gpio.c,项目的创建过程请参看:https://blog.csdn.net/fanxp66/article/details/80215090

3、新建led.h头文件,输入以下内容:

/*

LED头文件,GPIO初始化

GPIO位带操作

*/

#ifndef __LED__H

#define __LED__H

#include "stm32f10x_gpio.h"

#include "stm32f10x_rcc.h"

//定义位带地址宏

#define BITBAND(addr,bitnum) ((addr&0xF0000000) + 0x02000000 + ((addr&0x000FFFFF)<<5) + (bitnum<<2))

#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))

#define BIT_ADDR(addr,bitnum) MEM_ADDR(BITBAND(addr,bitnum))

//IO口地址映射

//数据输出寄存器地址

#define GPIOC_ODR_Addr (GPIOC_BASE + 12)

//定义GPIOC的位地址变量宏,位输出宏

#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n)

 

#define LED_PORT   GPIOC

#define LED_PIN    (GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7)

#define LED_PORT_RCC RCC_APB2Periph_GPIOC

void LED_Init(void);

#endif

4、新建led.c程序文件,内容如下:

#include "led.h"

void LED_Init()

{

     GPIO_InitTypeDef GPIOC_0_mode;

     RCC_APB2PeriphClockCmd( LED_PORT_RCC, ENABLE );    //使能GPIOC时钟

     GPIOC_0_mode.GPIO_Pin = LED_PIN;

     GPIOC_0_mode.GPIO_Speed = GPIO_Speed_50MHz;

     GPIOC_0_mode.GPIO_Mode = GPIO_Mode_Out_PP;

     GPIO_Init(GPIOC, &GPIOC_0_mode);

}

5、将led.c加入到项目的"User"组中,在项目配置中,配置C/C++的包含路径有能找到led.h的路径。

6、修改main.c程序,内容为:

#include "stm32f10x.h"

#include "stm32f10x_rcc.h"

#include "led.h"

void delay(int t)

{

     int i;

     for( ;t>0; t--)

         for(i=0;i<1000;i++);

}

int main()

{

     u32 i,j;

     //共阳数码管’0’-‘9’的显示码

     u32 LED[10] = {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};

     LED_Init();

     while(1)

     {

         for(i=0;i<10;i++)

         {

              //根据 LED[n]数组的值决定数码管各个段位的显示

              for(j=0; j<8; j++)

                   if( ~(LED[i]) & 0x1<<j )

                       PCout(j) = 0;

                   else

                       PCout(j) = 1;

              delay(10000);

         }

     }

}