您的位置:首页 > 其它

STM32学习笔记2:再探GPIO

2017-09-07 11:13 344 查看
输入模式

STM32中不仅有输出模式,同时还可以配置输入模式:上拉,下拉,浮空,模拟。如下图所示:


 

1.上拉:即用一个电阻与VDD相连,初始电平为高电平 
2.下拉:即用一个电阻与VSS(GND)相连,初始电平为低电平 
3.浮空:没有拉高或拉低,电压处于0~VDD之间不确定,故常用于外部检测,如按键, 
4.模拟:模拟输入 ,如ADC等 
我们同样可以仿造输出操作CRL和CRH寄存器配置GPIO输入模式如下图:


 
其中MODE[1,0]配置为00(复位状态),CNFX配置如下图:


 

位带操作

在51系列单片机中,我们可定义 sbit P1_1 = P1^1;就可以对P1的第一位进行直接操作,可以简便我们操作,那么STM32支持位操作吗?实际上,STM32提供位带和位带别名操作。寄存器映射如下图:


 
片上外设和SRAM均有1MB的位带区,位带区里面的每一个位都可以通过位带别名区的地址来访问。位带区的一个位,对应位带别名区的四个字节。即把1M位带区膨胀成32M位带别名区。故根据上面所述,我们有如下转换公式:

外设位带区与外设位带别名区的地址转换: 
AliasAddr = 0x42000000+ (A-0x40000000)*8*4 +n*4 

公式解释如下: 

0x42000000为外设位带别名区,(A-0x40000000)为偏移地址,又一字节有8位,其中每位又膨胀成4个字节,故*4*8

同理,SRAM位带区与SRAM位带别名区的地址转换: 
AliasAddr = 0x22000000+ (A-0x20000000)*8*4 +n*4 

A:表示我们要操作的那个位所在的寄存器的地址 

n:位号

如果我们把上述两个公式合并成一个公式,并用位操作表示(速度更快) 
((addr & 0xF0000000)+0x02000000+((addr &0x000FFFFF)<<5)+(bitnum<<2)) 

其中,addr:要操作的位所在寄存器的地址 

bitnum:位号,即在寄存器的第几位。 

公式理解如下: 

addr & 0xF0000000:注意SRAM区和外设区首位是4与2,故将它与F&,即可取出最高位4或者2。然后因为位带别名区都是从0x02000000开始,故加0x02000000 

addr &0x000FFFFF :屏蔽掉高3位。外设位带区的最高地址为0X400F 0000,SRAM位带区的最高地址为 0X200F 0000,(0X400F 0000 – 0X4000 0000)与(0x200F 0000 – 0X2000 0000)在求偏移地址相减的时候只有低 5位有效,所以就把剩下的高三位屏蔽掉,剩下的5位与F做与运算即可。 

bitnum<<2:即bitnum*4 

位操作相关知识:x<< n:x * 2^n,x&f:保留x,x&0:x清0

别名区实现通常如下:

/*求出位带别名的地址*/

#define BITBAND(addr,bitnum)     ((addr & 0xF0000000)+0x2000000+((addr & 0xFFFFF)<<5)+(bitnum<<2))

/*强制类型转换为一个指针并对指针*号操作*/

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

/*对位带别名地址进行操作*/

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

复制代码

注意:当你使用位带功能时,要访问的变量必须用 volatile 来定义。当用volatile声明变量时,优化器在用到这个变量时必须每次都小心地重新从内存里读取这个变量的值,而不是使用保存在寄存器里的备份。简洁的说声明该变量时可变的,编译器不进行任何优化。例如在本函数中,如果不对addr进行volatile声明,如果两次地址不同,分别为A,B在执行对B地址操作时候编译器有可能从直接从cpu读取A地址操作(访问cpu寄存器比访问ram快的多),造成程序错误 

接下来,我们用位带功能点亮LED灯。

/*求出位带别名的地址*/

#define BITBAND(addr,bitnum)    ((addr & 0xF0000000)+0x2000000+((addr & 0xFFFFF)<<5)+(bitnum<<2))

/*强制类型转换为一个指针并对指针*号操作*/

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

/*对位带别名地址进行操作*/

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

#define PERIPH_BASE             (0x40000000)

#define APB2PERIPH_BASE         (PERIPH_BASE+0x00010000)

#define GPIOB_BASE              (APB2PERIPH_BASE+0x0C00)

#define GPIOB_ODR_ADDR          (GPIOB_BASE+0x0c)

int main(){

    /*开启时钟*/

    RCC_APB2ENR |= 1<<3;

    /*设置PH8~15为推挽输出*/

    GPIOB_CRH |= 0x33333333;        

    BIT_ACTION(GPIOB_BSRR_ADDR,15) = 1;

    while(1);   

    }

复制代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: