2.1 MSP430 GPIO概述
通用输入/输出接口(General Purpose Input/Output,简称GPIO)是单片机通过引脚来控制或者采集外部电路的接口,是单片机最基础的功能之一。
不同型号的MSP430可以提供一个或者多个8位I/O口。一般来说引脚越多的芯片提供的I/O口也越多。每8位I/O口会被分为一组(port),在每组当中再给每位编号(bit)。例如Port1中的第一位I/O口就命名为P1.0。
每一组I/O口都由单片机中的寄存器来控制。例如要读取P1.0上的电平,可以通过读输入寄存器P1IN上的bit0来实现。本章中我们将介绍与I/O口有关的寄存器,以及如何用这些寄存器来控制I/O口。
2.2 GPIO寄存器
MSP430的每组GPIO都是由对应的寄存器控制的,其中有3个寄存器是最重要的,分别是方向寄存器(PxDIR)、输出寄存器(PxOUT)和输入寄存器(PxIN)。方向寄存器决定了引脚的功能是输入还是输出;输出和输入寄存器则直接关联引脚的电平状态。
本节所介绍的寄存器详细说明请见“MSP430x2xxFamily User's guide”的8.1-8.3节。
2.2.1 方向寄存器(PxDIR)
在MSP430中,每个I/O口都可以被单独配置成输入或者输出状态。是配置成输入还是输出是由方向寄存器(也叫DIR寄存器)决定的。方向寄存器为0是输入,为1是输出。例如,要把P1.0和P1.7同时配置成输出,需要设置P1DIR=0x81,如下图所示。
想要配置MSP430的寄存器,还有多种方式可以实现。
1) 上图中的Register example是直接给寄存器赋值,这是最直接的方式。
2) 利用MSP430FR6989.h中的宏定义,也可以让寄存器的配置更直观更好记。例如头文件中已有以下宏定义:
- #define BIT0 (0x0001)
- #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进制了。
3) 另外图中的 MSP430wareexample是调用了MSP430驱动库来实现同样的功能。驱动库中提供了一个函数GPIO_setAsInputPin()来将I/O口配置为输出。
2.2.2 输出寄存器(PxOUT)
利用方向寄存器将I/O口配置为输出以后,就可以通过写输出寄存器PxOUT来给端口赋值了。例如要将P1.7设为高电平,直接写
P1OUT|= 0x80;
或者
P1OUT |= BIT7;
就可以了。
2.2.3 输入寄存器(PxIN)
当引脚作为输入时,引脚上的电平值会被缓存到输入寄存器PxIN当中。读取PxIN的值就可以得知当前的引脚状态。
与输出相比,I/O口的输入状态配置起来会更复杂一点。原因在于一个问题:如果一个引脚作为输入,当它没有被外部电路赋值时,引脚是什么电平?实际上这种情况发生时引脚处于浮动状态——也就是说即有可能是高电平也有可能是低电平。浮动状态是我们不想看到的,因为它即可能影响程序的逻辑,也会在电平转换时消耗不必要的能量。因此一般我们在引脚作为输入时会通过一个电阻将该引脚接到电源或地,这样就形成了一个弱上拉/下拉状态,这个电阻成为上拉/下拉电阻。接了上拉/下拉电阻以后,输入引脚的电平就不会乱跑了,而当外部电路的高/低电平接到引脚上时,该引脚又可以根据外部电平改变状态。
为了使用方便,MSP430单片机内集成了内部的上拉/下拉电阻。通过配置PxREN寄存器可以使能上拉/下拉电阻,然后再配置PxOUT寄存器可以选择是上拉还是下拉。
下面来看一个例子,如果要将P1.7配置为输入,同时使能内部上拉电阻,代码如下:
- P1DIR &=~ BIT7; // Set P1.7 as input
- P1OUT |= BIT7; // Set pull-up resistor for P1.7
- P1REN |= BIT7; // Enable internal pull-up resistor
编译并下载程序,开始运行后按下S2按键,如果一切正常,绿色LED会亮起,松开S2后绿色LED会熄灭。
思考题:本实验中按键的状态检测是通过不断读取P1IN来实现的,这种方式叫做轮询。轮询是一种比较消耗CPU资源的方式,因为CPU需要不断读取GPIO的状态,就好像主人在家等快递,但家里没有门铃(也不能敲门),主人只能一遍又一遍的打开门看看外面有没有人。有没有另一种方式能够帮主人节省精力呢?下一章我们将介绍中断,中断就好像装上了门铃,可以提醒主人有人来了,大大节省主人(也就是单片机CPU)的精力和时间。
2.3 GPIO引脚复用
现在的单片机集成的功能越来越多,例如串口、定时器、ADC等,这些外设都需要引脚,但如果为每个外设都留下单独的引脚,单片机的引脚数量将会巨大,这既不经济也不实用。另外这些外设并一定会同时使用,因此单片机提供了引脚复用的功能,将GPIO和其他外设放在一个引脚上,使用的时候通过寄存器来选择用哪一个功能。
例如下图中,MSP430G2553的引脚8,它既可以作为GPIO模块中的P2.0,也可以作为定时器的TA1.0端口。其他引脚也是类似。具体每一个引脚有哪些功能请见MSP430G2553的datasheet。
复用的引脚可以通过PxSEL寄存器来选择功能。鉴于有些引脚的功能多于2个,1位寄存器不够用,所以MSP430G2553有PxSEL和PxSEL2两个功能选择寄存器,它们两个再加上PxDIR寄存器配合起来可以选择多个引脚功能。
每个MSP430单片机的引脚功能具体定义在芯片datasheet中,查找“PortSchematics”部分可以看到每个引脚有什么功能,以及如何配置寄存器来启用这些功能。
在User’s Guide中,也会介绍PxSEL寄存器,但不会具体介绍针对每个芯片的具体引脚定义。一般来说引脚复用功能可以分为Primaryfunction和Secondary function,对照datasheet和User’sGuide可以发现二者是可以对应的。
例如我们要将P1.0端口配置成TA0.TACLK功能(在这里先不关心这个功能具体有什么用),查阅datasheet可以找到该功能对应的3个寄存器的值,因此将寄存器对应赋值即可启用这个功能。
- P1DIR &=~ BIT0; // Set P1DIR
- P1SEL |= BIT0; // Set P1SEL and P1SEL2
- P1SEL2 ^=~ BIT0;
2.4 GPIO实验

2.4.1 GPIO输出实验——LED闪烁

- int main(void) {
- WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
- // Add your code below to initialize the GPIO
- // End of your code
- while(1)
- {
- // Add your code below to set P1.6 high
- // End of your code
- _delay_cycles(500000);
- // Add your code below to Set P1.6 low
- // End of your code
- _delay_cycles(500000);
- }
- return 0;







- 将S2对应的I/O口(P1.3)设置为输入;
- 设置P1.3的内部上拉电阻;
- 将绿色LED对应的I/O口设置为输出。
- int main(void) {
- WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
- // Add your code below to initialize the GPIO
- // End of your code
- S2按下:P1.3 = 0(接地)
- S2松开:P1.3 = 1(接Vcc)
- while (1) // Test P1.3
- {
- if (/* Add your code here to determine if P1.3 = 0 */)
- {
- // Add your code to turn on the green LED
- }
- else // Add your code to turn on the green LED
- }
本文参考:https://e2echina.ti.com/group/universityprogram/students/f/11/p/149721/424157#424157