0%

MSP430单片机(三)——I/O口操作

学习了MSP430单片机的一些基础操作,如I/O口和看门狗的使用,以及低功耗模式。

1. 逻辑操作

I/O和寄存器需要涉及到很多运算符的操作,先简要复习一下C语言中运算符的使用方法做个备忘。

与、& 、AND1 & 1 = 1,1 & 0 = 0有0为0

或、| 、OR1 | 1 = 11 | 0 = 1有1为1

异或、^ 、XOR1 ^ 0 = 1,0 ^ 1 = 1, 1 ^ 1 = 0, 0 ^ 0 = 0不同为1

取反、~ ** : ~ 1 = 0, ~ 0 = 1 , **01互换

2. GPIO口概述

在前面的示例程序中我们发现对单片机进行编程实际上就是改写寄存器的值。单片机各个外设的功能其实是预先固定的,而寄存器就像是这些功能的控制接口,通过改变寄存器的值就可以在这些功能中进行选择。还有一些寄存器直接对应引脚的状态,例如GPIO的输入/输出寄存器。像以下语句: P1OUT = 0x01 ;

这句话其实就是赋值给P1OUT这个寄存器,这个寄存器是GPIO的输出寄存器,赋值之后对应引脚的输出状态就会发生改变。

P1OUT寄存器共有8位,分别是从P1.0-P1.7。要给这个寄存器赋值,其实就是写入一个8位的二进制数值,例如00000001。在C语言中,为了尽量精简代码,一般把这个8位二进制数换算成2位16进制数,前面加上“0x”表示这是一个16进制的值。这样二进制的00000001也可以表示为0x01,那么上面一句P1OUT =0x01实际上是把P1.0赋值为1,P1.1-P1.7赋值为0。

——MSP430G2 LaunchPad入门系列第1篇 - MSP430简介及CCS开发环境

单片机最基础的功能是通过引脚来控制或者采集外部电路的接口,这是我们知道的单片机最基础的功能。

不同的单片机可以提供一个或多个8位I/O口,引脚的数量越多,芯片提供的I/O口就越多,相应的功能也更丰富一点;

例如 8051 提供了 P0、P1、P2、P3这四组IO:

I/O口 引脚 第一功能 第二功能
P0口 P0.0~P0.7 输入与输出 分时的传送地址低8位与数据线
P1口 P1.0~P1.7 输入与输出 无第二功能
P2口 P2.0~P2.7 输入与输出 传送地址的高八位
P3口 P3.0~P3.7 输入与输出 P3.0——RXD:串行口输入端
P3.1——TXD:串行口输出端
P3.2——INT0 :外部中断0中断请求输入端
P3.3——INT1 :外部中断1中断请求输入端
P3.4——T0:定时器/计数器0外部输入端
P3.5——T1:定时器/计数器1外部输入端
P3.6——WR:外部数据存储器写选通信号
P3.7——RD:外部数据存储器读选通信号

MSP430特点:

  • 类型丰富:MSP430提供了12组IO口,分别是 P1-P11,PJ.,以及S、COM端口。其中除了第十一P11的宽度是3位,其他的端口全是8为宽度;

    相邻的端口可以组合成端口对,分别命名为 PA、PB、PC、PD等,通过字节(byte)形式访问 单独端口,通过字(word)的形式访问 端口对;

  • 功能丰富:P1和P2具有9个(或7个)寄存器,其他有6个(或4个)寄存器

    • 每个I/O口可单独编程
    • 输入和输出可任意组合
    • P1、P2所有I/O口都具有边沿可选的输入中断功能
    • 能使用指令对寄存器操作
    • 可设置I/O口上拉或者下拉
    • 配置I/O驱动能力
  • 输出特性:MSP430只有数据端口,可用端口的某一位或几位来传送状态信息来确定外设状态;

    MSP430输入端口的最大漏电流为50na,输入端口的漏电流对系统的耗电影响很大;

    每个端口的输出晶体管都能够限制输出电流(最大约为25mA)

MSP430的I/O口功能:

  • PxDIR输入/输出方向寄存器:作为输入时,只能读,作为输出时,可读可写
  • PxIN输入寄存器:是只读寄存器,用户不能对其写入
  • PxOUT输出寄存器:可读可写
  • PxREN上拉或下拉电阻使能寄存器(提高信号的驱动能力):该寄存器的每一位可以使能或禁用I/O引脚的上拉/下拉电阻——0为禁止,1为使能
  • PxSEL功能选择寄存器:P1和P2端口还具有其他片内外设功能,0-选择引脚为I/O端口,1-选择引脚为外围模块功能
  • PxDS输出驱动强度寄存器:每位设置引脚的输出强度为高驱动强度(“1”)或低驱动强度(“0”),默认值为低驱动强度
  • PxIE中断使能寄存器:该寄存器的8位与该端口的8个引脚一一对应,某一位置位表示允许对应的引脚在电平变化(上升沿或下降沿)时产生中断
  • PxIES中断触发沿选择寄存器:如果允许Px口某个引脚中断,还需定义该引脚的中断触发方式,该寄存器8位对应Px口的8个引脚(”0:上升沿使相应标志置位,1:下降沿使相应标志置位“)
  • PxIFG中断标志寄存器:用来表示对应引脚是否产生了有PxIES设定的电平跳变,如果GIE置位,引脚对应的中断使能寄存器PxIE位置位,则会向CPU请求中断处理

3. I/O寄存器

​ (1)PxDIR:输入/输出方向寄存器

​ 相互独立的8位分别定义了8个引脚的输入/输出方向。8位在PUC后都被复位。使用输入和输出功能时,应该先定义端口的方向,输入/输出才能满足设计者的要求。

​ 0:输入模式;

​ 1:输出模式。

​ (2)PxIN:输入寄存器

​ 输入寄存器是 只读寄存器。用户不能对它进行写入,只能通过该寄存器内容知道I/O端口的输入信号。此时引脚方向必须选定为输入。

​ (3) PxOUT:输出寄存器

​ 该寄存器为I/O端口的输出缓冲寄存器,在读取时输出缓存内容与引脚方向定义无关。

​ (4) PxIFG:中断标志寄存器

​ 该寄存器有8个标志位,标志相应引脚是否有待处理中断的信息,即相应引脚是否有中断请求。寄存器定义如下:

7 6 5 4 3 2 1 0
PxIFG PxIFG.6 PxIFG.5 PxIFG.4 PxIFG.3 PxIFG.2 PxIFG.1 PxIFG.0

​ 0:没有中断请求;

​ 1:有中断请求。

​ (5) PxIES:中断触发沿选择寄存器

​ 如果允许Px口的某个引脚中断,还需定义该引脚的中断触发方式。该寄存器的8位分别对应Px口的8个引脚。

​ 0:上升沿使相应标志置位

​ 1:下降沿使相应标志置位

​ (6) PxIE:中断使能寄存器

​ Px口的每一个引脚都有一位用以控制该引脚是否允许中断。该寄存器定义如下:

7 6 5 4 3 2 1 0
PxIE.7 PxIE.6 PxIE.5 PxIE.4 PxIE.3 PxIE.2 PxIE.1 PxIE.0

​ 0:禁止中断;

​ 1:允许中断。

​ (7) PxSEL:功能选择寄存器

​ P1和P2俩端口还具有其他片内外设功能,这些功能与芯片外的联系通过复用P1和P2引脚的方式来实现。P1SEL和P2SEL用来选择引脚的I/O端口功能与外围模块功能。

​ 0:选择引脚位I/O模式;

​ 1:选择引脚为外围模块功能。

端口P3、P4、P5和P6没有中断能力,其余功能同P1和P2,可以实现输入/输出功能和外围模块功能。四个寄存器可以对它们进行访问和控制,这四个寄存器分别是PxDIR,PxIN,PxOUTPxSEL,定义和用法同P1、P2端口。

4. I/O口操作

430不支持直接对位操作,需要使用屏蔽符和掩码来实现修改寄存器的某一位而不影响其他位的目的。BIT0~F分别对16位寄存器操作。

配置MSP430的寄存器可以用两种方法来实现:

  1. 最直接的方式,直接给寄存器赋值,如 P1DIR = 0x81

  2. 利用msp430官方库文件中宏定义,更直观地表示要操作地寄存器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#define BIT0                (0x0001u)
#define BIT1 (0x0002u)
#define BIT2 (0x0004u)
#define BIT3 (0x0008u)
#define BIT4 (0x0010u)
#define BIT5 (0x0020u)
#define BIT6 (0x0040u)
#define BIT7 (0x0080u)
#define BIT8 (0x0100u)
#define BIT9 (0x0200u)
#define BITA (0x0400u)
#define BITB (0x0800u)
#define BITC (0x1000u)
#define BITD (0x2000u)
#define BITE (0x4000u)
#define BITF (0x8000u)

PxDIR = 0x81 就可以改写成 PxDIR = BIT0 + BIT7 ,可以省去计算寄存器换算16进制的值;

  1. 驱动库中提供了一个函数GPIO_setAsInputPin()来将I/O口配置为输出。

4.1 PxDIR

设置IO口方向,0-输入,1-输出。如P1.5、P1.6、P1.7 接有按键,P1.1、P1.3、P1.4接有LED,那么P1.5、P1.6、P1.7 要设为输入,P1.1、P1.3、P1.4 要设为输出:

1
2
3
P1DIR|=BIT1+BIT3+BIT4; // P1.1、P1.3、P1.4设为输出

P1DIR &=~ (BIT5+BIT6+BIT7); // P1.5、P1.6、P1.7设为输入

PxDIR 寄存器在复位过程中会被清0,没有被设置的IO口方向均为输入状态,因此第二句可以被省略

4.2 PxOUT和PxIN

对于所有已经设成输出状态的IO口,可以通过 PxOUT 寄存器设置其输出电平(当IO口配置为输入模式并且置高/置低使能时, PxOUT 寄存器:0置低1置高);

对于所有已经被设成输入状态的IO口,可以通过 PxIN 寄存器读回其输入电平。

如判断P1.5口上的开关状态,若低电平则从P1.1口输出高电平点亮LED:

1
if(( P1IN & BIT5) ==0 )  P1OUT|=BIT1;  //若P1.5为低电平则P1.1输出高电平

4.3 PxSEL

用于设置每一位IO的功能: 0=普通IO口 1=第二功能。

例如MSP4305418A中P5.6和P5.7和串口的TXD、RXD公用引脚,如果需要将两个引脚配置成串口收发脚,需将P5SEL的6,7位置高:

1
P5SEL |= BIT6 + BIT7; 	// P2.4,5 设为串口收发引脚

4.4 PxIE和PxIES

PxIE寄存器用于设置每一位IO的中断允许: 0=不允许 1=允许

PxIES寄存器用于选择每一位IO的中断触发沿: 0=上升沿 1=下降沿

使用方法: 使用IO口中断之前,需要先将IO 口设为输入状态,并允许该位IO的中断,再通过PxIES寄存器选择触发方式为上升沿触发或者下降沿触发。

例如将P1.5、P1.6、P1.7 口设为中断源,下降沿触发:

1
2
3
4
5
6
7
P1DIR &=~(BIT5 + BIT6 + BIT7); // P1.5、P1.6、P1.7设为输入(可省略)

P1IES |= BIT5 + BIT6 + BIT7; // P1.5、P1.6、P1.7设为下降沿中断

P1IE |= BIT5 + BIT6 + BIT7; // 允许P1.5、P1.6、P1.7中断

EINT(); // 总中断允许

4.5 PxIFG

PxIFG寄存器是IO中断标志寄存器:0=中断条件不成立1=中断条件曾经成立过.

这个寄存器存在的目的是为了尽可能地不漏掉每一次中断。

例如P1.5、P1.6、P1.7 发生中断后执行不同的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#pragma vector = PORT1_VECTOR //P1口中断源

__interrupt void P1_ISR(void) //声明一个中断服务程序,名为P1_ISR()

{

if(P1IFG & BIT5) //判断P1中断标志第5位(P1.5)

{

... ... //在这里写P1.5中断处理程序

}

if(P1IFG & BIT6) //判断P1中断标志第6位(P1.6)

{

... ... //在这里写P1.6中断处理程序

}

if(P1IFG & BIT7) //判断P1中断标志第7位(P1.7)

{

... ... //在这里写P1.7中断处理程序

}

P1IFG=0//清除P1所有中断标志位

}

PxIFG 的中断操作比较灵活,既可以被动执行中断判断,也可以通过主动向写 PxIFG 寄存器相应位写“1”。

因此,更改 PxIES 应该在关闭中断后进行,并在打开中断之前及时清除中断标志

5. 例程

使用TI官方提供的点亮LED的程序来进行430的入门操作——操作I/O口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
//*******************************************************************************
// MSP-FET430P140 Demo - Software Toggle P1.0
//
// Description: Toggle P1.0 by xor'ing P1.0 inside of a software loop.
// ACLK= n/a, MCLK= SMCLK= default DCO ~800k
//
// MSP430F149
// -----------------
// /|\| XIN|-
// | | |
// --|RST XOUT|-
// | |
// | P1.0|-->LED
//
// M. Buccini
// Texas Instruments Inc.
// Feb 2005
// Built with IAR Embedded Workbench Version: 3.21A
//******************************************************************************

#include <msp430x14x.h>

void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // 停止看门狗
P1DIR |= 0x01; // Set P1.0 to output direction

for (;;)
{
volatile unsigned int i;

P1OUT ^= 0x01; // Toggle P1.0 using exclusive-OR

i = 50000; // Delay
do (i--);
while (i != 0);
}
}

程序流程

​ 1.关看门狗;2.设置I/O口;3.延时、循环输出

源码解释

  1. 关看门狗
1
WDTCTL = WDTPW + WDTHOLD;                 // 停止看门狗

这句话实现了关开门狗的功能,要想理解这句话需要先查看官方的芯片手册:

image-20201107163354835

WDTPW:读取时必须是069h,写的时候必须是05Ah,否则就会出现一个PUC信号导致单片机复位

WDTHOLD:置为1时看门狗被停止

在头文件msp430x14x.h中查看WDTPWWDTHOLD的宏定义

430的库文件对这两个“变量”的宏定义如下:

1
2
#define WDTHOLD             (0x0080u)  	/* WDT - Timer hold */
#define WDTPW (0x5A00u)

WDTPW为5A表示写入,WDTHOLD为80,高位为1,地位为0,表示关闭看门狗。

将两个数相加,送入WDTCTL实现关门狗;如果缺失其中一个会导致产生一个PUC信号使单片机复位。

​ 2. 设置I/O口

P1DIR |= 0x01; // 提取01口,设置P1.1为输出

image-20201119165324316

​ 如上图,端口(Port)P1和P2具有输入/输出、中断和外部模块功能,这些功能可以通过它们各自的7个控制寄存器的设置来实现。

​ 这里P1.1端口默认无输出,而P1.3-1.6,P1.7-1.8都是默认输入(00h)

1
P1OUT ^= 0x01;                          // Toggle P1.0 using exclusive-OR
  1. 延时、循环输出
1
P1OUT ^= 0x01;                          // Toggle P1.0 using exclusive-OR

这句指令又可写成 P1OUT = P1OUT ^ 0x01; 每次对第四位进行异或,改为输出。0 ^ 1,循环之后又变成1 ^ 1,实现闪烁。

两种颜色的功德箱(逃