学单片机的都必须清楚微控制器的输入输出,点灯是练习基础的输出,而基础的输入就需要去练习按键了。我所使用电路原理图如下:
按键两种控制方式,一是查询(通过读取对应位寄存器的电平状态实现),二是中断(使用单片机内部中断资源),细说的话,会有很多可说的东西,所以当遇到任何疑问时,可查阅其他资料,而且本人水平有限,欠缺之处,还请见谅。
一、查询方式
#include <msp430g2553.h>
//#include <intrinsics.h> 包含430单片机初始的宏定义,可自行打开观看
//---main()函数前提前声明子函数---
void Key_Init();
void Led_Init();
void P13_Event();
/**
* main.c
*/
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; //关闭看门狗定时器
//硬件初始化
Led_Init();
Key_Init();
while(1)
{
if((P1IN & BIT3)==0) //查询相关寄存器的输入位
{
//-----延时一段时间,避开机械抖动区域-----
__delay_cycles(10000); //消抖延时,包含在<intrinsics.h>
if((P1IN & BIT3)==0) //按键确实按下
{
P13_Event(); //按键事件处理函数
}
}
}
}
/*********************************************************
* 名 称:Key_Init()
* 功 能:设定按键IO的方向,启用按键IO的上拉电阻
* 入 口 参 数:无
* 出 口 参 数:无
* 说 明:无
* 范 例:无
**********************************************************/
void Key_Init()
{
//---配合机械按键,启用内部上拉电阻---
P1DIR &= ~BIT3; //P1.3设为输入(可省略)
P1REN |= BIT3; //启用P1.3内部上下拉电阻
P1OUT |= BIT3; //将电阻设置为上拉
}
/*********************************************************
* 名 称:Led_Init()
* 功 能:设定LED控制IO的方向,初始为高电平
* 入 口 参 数:无
* 出 口 参 数:无
* 说 明:使用板载LED作为演示
* 范 例:无
**********************************************************/
void Led_Init()
{
P1DIR |= BIT0;
P1OUT |= BIT0; //初始为高电平,即点亮状态
}
/*********************************************************
* 名 称:P13_Event()
* 功 能:P1.3的事件处理函数,即当P1.3键被按下后,下一步干什么
* 入 口 参 数:无
* 出 口 参 数:无
* 说 明:使用事件处理函数的形式,可以增强代码的移植性和可读性
* 范 例:无
**********************************************************/
void P13_Event()
{
P1OUT ^= BIT0; //翻转P1.0管脚电平状态
}
本记使用的相关寄存器介绍:
输入寄存器(PxIN),反应对应I/O口当前的输入电平状态。0:输入电平为低电平;1:输入电平为高电平。注意:输入寄存器是只读寄存器,任何写操作均是无效的,使用如下:
P1DIR &= ~BIT3;
IO_Value = P1IN&BIT3;
电阻使能寄存器(PxREN),使能I/O口对应位的上下拉电阻。0:禁用对应管脚电阻上下拉;1:使能对应管脚电阻上下拉。上下拉电阻的配置还需要配合,由输出寄存器PxOUT来完成。在管脚电阻使能寄存器使能的情况下,如果PxOUT对应位为1,则该管脚配置为上拉状态;如果PxOUT对应位为0,则使能下拉状态,例如:
P1REN |= BIT0; //P1.0管脚上下拉电阻使能
P1OUT |= BIT0; //P1.0管脚电阻上拉
P1REN &= ~BIT0; //P1.0管脚上下拉电阻禁用
P1REN |= BIT0; //P1.0管脚上下拉电阻使能
P1OUT &= ~BIT0; //P1.0管脚电阻下拉
以上值得一提的是消抖延时代码__delay_cycles(10000),按键按下时会有抖动毛刺干扰,大致如图:
按键按下时,下降沿1被检测到,由于延时,下降沿2、3将不被检测。也有硬件消抖方式(加电容滤波),但考虑到批量生产的成本,所以大多都是软件消抖,还有疑问的可以再去专项搜索学习一下。
二、中断方式
下面这个例子以另一种编程风格书写,有需要封装的,请尝试用上面的风格编写。重在理解:
#include <msp430g2553.h>
/**
* main.c
*本函数实现的功能与查询方式一致,即按键(P1.3)控制LED(P1.0)亮灭
*/
void main()
{
WDTCTL = WDTPW + WDTHOLD; //关闭看门狗定时器
P1DIR |= BIT0;
P1DIR &= ~BIT3; //设置端口方向
P1REN |= BIT3; //启用P1.3内部上下拉电阻
P1OUT |= BIT3; //将电阻设置为上拉
P1IES |= BIT3; //P1.3设置为下降沿中断
P1IE |= BIT3; //允许P1.3中断
P1IFG &= ~BIT3; //清除P1.3中断标志位
_enable_interrupts(); //使能总中断
// while(1);
_bis_SR_register(LPM3_bits); //LPM3模式休眠
}
/*********************************************************
* 名 称:PORT13_ISR()
* 功 能:响应P1口的外部中断服务函数
* 入 口 参 数:无
* 出 口 参 数:无
* 说 明:单个IO的中断处理函数,多个中断判断处理函数后期会再描述。
P1IFG必须手动清除,否则将持续引发PORT1中断。
* 范 例:无
**********************************************************/
#pragma vector=PORT1_VECTOR
__interrupt void PORT13_ISR(void)
{
P1OUT ^= BIT0; //中断事件函数
P1IFG = 0; //退出中断前必须手动清除IO口中断标志
}
对于MSP430单片机来说,所有I/O口的P1和P2端口都具备中断功能,其每个管脚可以独立地使能中断功能,中断的触发源可以设置为上升沿或下降沿。
中断边沿选择寄存器(PxIES),用于选择触发沿,位0表示该位对应的管脚中断触发方式为上升沿触发;位1表示该位对应的管脚中断触发方式为下升沿触发。同样,设置触发沿有效的前提是使能了对应管脚的中断。
P1IES |= BIT0; //设置P1.0管脚的中断触发沿为下降沿
P1IES &= ~BIT0; //设置P1.0管脚的中断触发沿为上升沿
中断使能寄存器(PxIE),使能P1或P2端口的中断功能,位0表示对应管脚的中断功能被关闭;位1表示对应管脚的中断功能被使能。中断向量头文件位于msp430g2553.h的第929~1009行,如图,可自行去查看。
P1DIR &= ~BIT3; //配置P1.3的方向为输入
P1IE |= BIT3; //使能P1.3的中断功能
中断标志寄存器(PxIFG),当管脚状态满足中断触发条件的时候,管脚对应的中断标志位 置1。I/O口中断标志位 置位 后需要软件清除,否则会不停地请求中断。管脚方向寄存器的变化或者输出寄存器的变化可能会引起对应中断标志位的置位,因此,在I/O口中断功能配置完后,最好将I/O口中断标志位清零。
中断的写法初学者只需要模仿代码书写,编程已被规范成了那个形式,有些东西我也不清楚,就是拿来用,等到了一定地步就“水到渠成”了。