GPIO即通用输入输出端口,GPIO基本上都是用于芯片与片外器件或设备的交互,GPIO是MCU与外界交互的重要途径,他具有如下特性:
- 可以独立控制每个GPIO口的方向(输入输出模式)
- 可以独立设置每个GPIO的独立状态(高低电平)
- 所有GPIO口在复位后都有个默认方向(或输入或输出)
一般每个IO口都需要做两个寄存器位:1.选择口线方向(输入输出)2.一个数据位(用于设置输出数据和读取输入数据),所以一组GPIO口至少会有两个寄存器GPIOxDIR(控制各个IO口的方向)、GPIOxDATA(用于各个GPIO口的输入输出数据)。
有些IO口已经和硬件相连了,比如P1.0口与LED1相连、P4.7口与LED2相连、P2.1与开关S1相连等。
GPIO相关寄存器(引用哈工大所作图)
I0口常用寄存器有:PXDIR、PXIN、PXOUT、PXREN、PXSEL、PXDS;其中,X可以是1~8,也可以是A~D,因为P1、P2可以合称为PA。例如(PAREN=0 x0480)就等价于(P2REN=0 x04、P1 REN=0 x80)。msp430f5529.h中定义了BITO~BITF,方便用户进行位操作。例如要设置P1.7和P2.2为输入,代码可如下:PADIR & = ~(BITA+BIT7)。
#define BIT1 (0x0002)
#define BIT2 (0x0004)
#define BIT3 (0x0008)
#define BIT4 (0x0010)
#define BIT5 (0x0020)
#define BIT6 (0x0040)
#define BIT7 (0x0080)
那么P1DIR = 0x81就可以改写为P1DIR = BIT0 + BIT7;这样就不用每次都将想要写入寄存器的值换算成16进制了。
默认为0,端口被设置为输入模式,1为输出模式。
补充一下C语言知识:
a%=b 等效于 a=a%b 模除并赋值。
a|=b 等效于 a=a|b 按位或并赋值。
a&=b 等效于 a=a&b 按位与并赋值。
a^=b 等效于 a=a^b 按位异或并赋值。
a!=b 逻辑判断,a不等于b,当ab不等时为真。
a+=b 等效于 a=a+b 按位与并赋值
a-=b 等效于 a=a-b 按位与并赋值
&& 逻辑与,均为真时结果为真。
|| 逻辑或,均为假时结果为假,否则为真。
!a 逻辑非, a为真时结果为假,否则反。
| 按位或
^ 按位异或
& 按位与
~按位取反
(1)PxDIR寄存器
例: P4DIR |= BIT7; //P4.7设置为输出
P1DIR &=0x77 ; // P1.3和P1.7输入
- PxOUT--输出寄存器
利用方向寄存器将I/O口配置为输出以后,就可以通过写输出寄存器PxOUT来给端口赋值了。例如要将P1.7设为高电平,直接写P1OUT|= 0x80;或者P1OUT |= BIT7;
- PxIN--输入寄存器
当引脚作为输入时,引脚上的电平值会被缓存到输入寄存器PxIN当中。读取PxIN的值就可以得知当前的引脚状态。
与输出相比,I/O口的输入状态配置起来会更复杂一点。原因在于一个问题:如果一个引脚作为输入,当它没有被外部电路赋值时,引脚是什么电平?实际上这种情况发生时引脚处于浮动状态——也就是说即有可能是高电平也有可能是低电平。浮动状态是我们不想看到的,因为它即可能影响程序的逻辑,也会在电平转换时消耗不必要的能量。因此一般我们在引脚作为输入时会通过一个电阻将该引脚接到电源或地,这样就形成了一个弱上拉/下拉状态,这个电阻成为上拉/下拉电阻。接了上拉/下拉电阻以后,输入引脚的电平就不会乱跑了,而当外部电路的高/低电平接到引脚上时,该引脚又可以根据外部电平改变状态。
为了使用方便,MSP430单片机内集成了内部的上拉/下拉电阻。通过配置PxREN寄存器可以使能上拉/下拉电阻,然后再配置PxOUT寄存器可以选择是上拉还是下拉。
下面来看一个例子,如果要将P1.7配置为输入,同时使能内部上拉电阻,代码如下:
P1DIR &=~ BIT7; // 设置P1.7作为输入
P1OUT |= BIT7; // P1.7上拉
P1REN |= BIT7; // P1.7设置上拉电阻
编译并下载程序,开始运行后按下S2按键,如果一切正常,绿色LED会亮起,松开S2后绿色LED会熄灭。
本实验中按键的状态检测是通过不断读取P1IN来实现的,这种方式叫做轮询。轮询是一种比较消耗CPU资源的方式,因为CPU需要不断读取GPIO的状态,就好像主人在家等快递,但家里没有门铃(也不能敲门),主人只能一遍又一遍的打开门看看外面有没有人。有没有另一种方式能够帮主人节省精力呢?