1.按键的使用特点
按键的应用主要是在按键闭合时改变电路的电平,但是一般情况下按键的开关都是机械弹性触点开关,即利用触点的接触和分离来实现电路的通断,所以在按键按下和释放时往往会产生抖动干扰。
消除抖动干扰的两种方式:
(1)硬件设计:硬件消抖要在硬件设计上增加消抖电路,如用R-S触发器等,这样就会增加系统成本。
(2)软件设计:在软件中对按键进行二次测试确认,即当第一次检测到按键被按下后,间隔10 毫秒左右再次检测该按键是否被按下,只有两次都册到按键按下时才确认该按键被按下了,从而消除抖动干扰。
2.单键盘扫描应用
用PB口接一个LED数码管,来显示按下按键的次数;
用PC0端口接一个按键电路;
实现的功能是每一次按键,LED数码管显示的数据加1,到9回0。
#include <iom8v.h>
#include "Delay.h"
/**
*PB口:连接一个LED数码管
*PC0:连接一个按键电路,按下呈低电平
*
*/
unsigned char CountNum; //全局变量,用来计数 //按键扫描函数
void ScanKey(void)
{
unsigned char key; key = PINC; //检测按键状态
if(0x01 == key) //未按下,退出
return;
delay_ms(); key = PINC; //再次检测,防抖动
if(0x01 == key) //未按下,退出
return;
CountNum++;
while( == key&0x01) //等到按键释放
key = PINC;
} //主函数,扫描按键显示数据
void main()
{
unsigned char num[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
//初始化端口
DDRB = 0xFF; //设置B口为输出模式
PORTB = 0xFF; //置高电平
DDRC = 0x00; //设置C口为输入模式 CountNum = ; //初始化全局变量
while()
{
ScanKey(); //扫描按键
if(CountNum >= )
CountNum -= ;
PORTB = num[CountNum];
}
} //按键长按的情况
void ScanKey_Long(void)
{
unsigned char key; key = PINC; //检测按键状态
if(0x01 == key) //未按下,退出
return;
delay_ms(); key = PINC; //再次检测,防抖动
if(0x01 == key) //未按下,退出
return; while(0x00 == key) //按键长按,不断加加
{
CountNum++;
key = PINC;
}
}
按键一次LED加一
问:上面的程序没有考虑长按的情况,如果向我们使用的键盘一样,长时间按下一个按键,在屏幕上就不断的打印该字符,在这个例子里怎样实现:若长时间按下按键,CountNum就不断加加?
答:只需要更改ScanKey函数,更改结果如下
//按键长按的情况
void ScanKey_Long(void)
{
unsigned char key; key = PINC; //检测按键状态
if(0x01 == key) //未按下,退出
return;
delay_ms(); key = PINC; //再次检测,防抖动
if(0x01 == key) //未按下,退出
return; while(0x00 == key) //按键长按,不断加加
{
CountNum++;
key = PINC;
}
}
长按按键不断加加
3.矩阵按键(键盘)扫描的应用
按键太多的情况下,为了节省I/O资源,通常采用矩阵式的接口。矩阵键盘由行和列组成,每个键都有它的行值和列值,行值和列值的组合就是识别每个键盘的编码。
确定是哪个按键的流程:(???)
(1)在行和列的一个口中输出高电平,在另一个行列口读取一个扫描码;
(2)在后一个行列口中输出高电平,在前一行列口读取第二个扫描码;
(3)查表确定哪个按键被按下。
电路图如下:
程序实现步骤:
(1)确定有无按键按下;
(2)确定是哪个按键;
(3)返回该按键值或处理对应的任务;
(4)再加上,考虑抖动消除,等待按键的断开。
要实现每按下一个按键,就在LED数码管显示出该按键对应的值,按键断开后或默认显示“-”:
#include <iom8v.h>
#include "Delay.h"
/**
*PB口:连接一个LED数码管
*PC0-PC5:连接9个按键电路,按下呈低电平
* PC0-PC2:按键的行
* PC3-PC5:按键的列
*
*1:确定有无按键按下,main函数两次判断实现;
*2:确定是哪个按键,ScanKey函数实现;
*3:返回该键值或处理对应的任务,main函数中处理。
*/ //按键扫描函数,确定是哪个按键,返回按键的值
unsigned char ScanKey(void)
{
unsigned char tempH,tempL,key; tempH = PINC & 0x07; //取PC0-PC2用于判断行
switch (tempH)
{
case 0x06:
DDRC = 0x07; PORTC = 0x38;//将PC3-PC5(列)置为输入模式
delay_us();
tempL = PINC & 0x38; //取PC3-PC5用于判断列
switch (tempL)
{
case 0x30: key = 0x01; //得到键值
break;
case 0x28: key = 0x02;
break;
case 0x18: key = 0x03;
break;
default: key = ;
break;
}
DDRC = 0x38; PORTC = 0x07;//输入完毕恢复默认输出模式
break; case 0x05:
DDRC = 0x07; PORTC = 0x38;//将PC3-PC5(列)置为输入模式
delay_us();
tempL = PINC & 0x38; //取PC3-PC5用于判断列
switch (tempL)
{
case 0x30: key = 0x04; //得到键值
break;
case 0x28: key = 0x05;
break;
case 0x18: key = 0x06;
break;
default: key = ;
break;
}
DDRC = 0x38; PORTC = 0x07;//输入完毕恢复默认输出模式
break; case 0x03:
DDRC = 0x07; PORTC = 0x38;//将PC3-PC5(列)置为输入模式
delay_us();
tempL = PINC & 0x38; //取PC3-PC5用于判断列
switch (tempL)
{
case 0x30: key = 0x07; //得到键值
break;
case 0x28: key = 0x08;
break;
case 0x18: key = 0x09;
break;
default: key = ;
break;
}
DDRC = 0x38; PORTC = 0x07;//输入完毕恢复默认输出模式
break; default:
key = ;
break;
}
return (key); } //主函数,扫描按键显示数据
void main()
{
unsigned char temp,keynum;
unsigned char num[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
//初始化端口
DDRB = 0xFF; //设置B口为输出模式
PORTB = 0xFF; //置高电平
//将PC0-PC2(行)置为输入模式
DDRC = 0x38;
PORTC = 0x07; while() //两次检测行中有没有按下
{
PORTC = 0x40; //没有按键时,LED默认显示-
//第一次检验
temp = PINC & 0x07;
if(0x07 == temp)
continue;
delay_ms();
//第二次检验
temp = PINC & 0x07;
if(0x07 == temp)
continue; //有按下,LED显示键值
keynum = ScanKey();
PORTB = num[keynum];
//等到按键被释放
while(0x07 != temp)
temp = PINC & 0x07;
}
}
矩阵键盘,LED显示其值
代码总结:
主函数:判断是否有按键按下,并消除抖动干扰,若有则将获得的键值显示在LED数码管中;
ScanKey函数:得到扫描码确定是哪个按键,等待按键释放,返回该按键的值。
方法扩展:
(1)除了像上面的对按键的接口不停的扫描,
(2)还可以使用定时扫描,例如用一个定时器,每隔10MS 对按键接口进行扫描,看是否有按键按下;
(3)也可以使用中断的方式去扫描,当按键按下时由硬件电路产生一个中断,MCU 响应该中断,确定哪个按键被按下,处理相应函数。