×
单片机 > 单片机程序设计 > 详情

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

发布时间:2020-06-18 发布时间:
|

一、什么是位带操作


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


二、CM3的位带操作


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


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


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


从上图可以看出,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<


                       PCout(j) = 0;


                   else


                       PCout(j) = 1;


              delay(10000);


         }


     }


}



关键字:位带地址  操作GPIO  数码管  显示数字


『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
浅谈AVR中定时器几种工作模式