1.按键的使用特点
按键的应用主要是在按键闭合时改变电路的电平,但是一般情况下按键的开关都是机械弹性触点开关,即利用触点的接触和分离来实现电路的通断,所以在按键按下和释放时往往会产生抖动干扰。
消除抖动干扰的两种方式:
(1)硬件设计:硬件消抖要在硬件设计上增加消抖电路,如用R-S触发器等,这样就会增加系统成本。
(2)软件设计:在软件中对按键进行二次测试确认,即当第一次检测到按键被按下后,间隔10 毫秒左右再次检测该按键是否被按下,只有两次都册到按键按下时才确认该按键被按下了,从而消除抖动干扰。
2.单键盘扫描应用
用PB口接一个LED数码管,来显示按下按键的次数;
用PC0端口接一个按键电路;
实现的功能是每一次按键,LED数码管显示的数据加1,到9回0。
1 #include <iom8v.h>
2 #include "Delay.h"
3 /**
4 *PB口:连接一个LED数码管
5 *PC0:连接一个按键电路,按下呈低电平
6 *
7 */
8 unsigned char CountNum; //全局变量,用来计数
9
10 //按键扫描函数
11 void ScanKey(void)
12 {
13 unsigned char key;
14
15 key = PINC; //检测按键状态
16 if(0x01 == key) //未按下,退出
17 return;
18 delay_ms(10);
19
20 key = PINC; //再次检测,防抖动
21 if(0x01 == key) //未按下,退出
22 return;
23 CountNum++;
24 while(0 == key&0x01) //等到按键释放
25 key = PINC;
26 }
27
28 //主函数,扫描按键显示数据
29 void main()
30 {
31 unsigned char num[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
32 //初始化端口
33 DDRB = 0xFF; //设置B口为输出模式
34 PORTB = 0xFF; //置高电平
35 DDRC = 0x00; //设置C口为输入模式
36
37 CountNum = 0; //初始化全局变量
38 while(1)
39 {
40 ScanKey(); //扫描按键
41 if(CountNum >= 10)
42 CountNum -= 10;
43 PORTB = num[CountNum];
44 }
45 }
46
47 //按键长按的情况
48 void ScanKey_Long(void)
49 {
50 unsigned char key;
51
52 key = PINC; //检测按键状态
53 if(0x01 == key) //未按下,退出
54 return;
55 delay_ms(10);
56
57 key = PINC; //再次检测,防抖动
58 if(0x01 == key) //未按下,退出
59 return;
60
61 while(0x00 == key) //按键长按,不断加加
62 {
63 CountNum++;
64 key = PINC;
65 }
66 }
问:上面的程序没有考虑长按的情况,如果向我们使用的键盘一样,长时间按下一个按键,在屏幕上就不断的打印该字符,在这个例子里怎样实现:若长时间按下按键,CountNum就不断加加?
答:只需要更改ScanKey函数,更改结果如下
1 //按键长按的情况
2 void ScanKey_Long(void)
3 {
4 unsigned char key;
5
6 key = PINC; //检测按键状态
7 if(0x01 == key) //未按下,退出
8 return;
9 delay_ms(10);
10
11 key = PINC; //再次检测,防抖动
12 if(0x01 == key) //未按下,退出
13 return;
14
15 while(0x00 == key) //按键长按,不断加加
16 {
17 CountNum++;
18 key = PINC;
19 }
20 }
3.矩阵按键(键盘)扫描的应用
按键太多的情况下,为了节省I/O资源,通常采用矩阵式的接口。矩阵键盘由行和列组成,每个键都有它的行值和列值,行值和列值的组合就是识别每个键盘的编码。
确定是哪个按键的流程:(???)
(1)在行和列的一个口中输出高电平,在另一个行列口读取一个扫描码;
(2)在后一个行列口中输出高电平,在前一行列口读取第二个扫描码;
(3)查表确定哪个按键被按下。
电路图如下:
程序实现步骤:
(1)确定有无按键按下;
(2)确定是哪个按键;
(3)返回该按键值或处理对应的任务;
(4)再加上,考虑抖动消除,等待按键的断开。
要实现每按下一个按键,就在LED数码管显示出该按键对应的值,按键断开后或默认显示“-”:
1 #include <iom8v.h>
2 #include "Delay.h"
3 /**
4 *PB口:连接一个LED数码管
5 *PC0-PC5:连接9个按键电路,按下呈低电平
6 * PC0-PC2:按键的行
7 * PC3-PC5:按键的列
8 *
9 *1:确定有无按键按下,main函数两次判断实现;
10 *2:确定是哪个按键,ScanKey函数实现;
11 *3:返回该键值或处理对应的任务,main函数中处理。
12 */
13
14
15 //按键扫描函数,确定是哪个按键,返回按键的值
16 unsigned char ScanKey(void)
17 {
18 unsigned char tempH,tempL,key;
19
20 tempH = PINC & 0x07; //取PC0-PC2用于判断行
21 switch (tempH)
22 {
23 case 0x06:
24 DDRC = 0x07; PORTC = 0x38;//将PC3-PC5(列)置为输入模式
25 delay_us(1);
26 tempL = PINC & 0x38; //取PC3-PC5用于判断列
27 switch (tempL)
28 {
29 case 0x30: key = 0x01; //得到键值
30 break;
31 case 0x28: key = 0x02;
32 break;
33 case 0x18: key = 0x03;
34 break;
35 default: key = 0;
36 break;
37 }
38 DDRC = 0x38; PORTC = 0x07;//输入完毕恢复默认输出模式
39 break;
40
41 case 0x05:
42 DDRC = 0x07; PORTC = 0x38;//将PC3-PC5(列)置为输入模式
43 delay_us(1);
44 tempL = PINC & 0x38; //取PC3-PC5用于判断列
45 switch (tempL)
46 {
47 case 0x30: key = 0x04; //得到键值
48 break;
49 case 0x28: key = 0x05;
50 break;
51 case 0x18: key = 0x06;
52 break;
53 default: key = 0;
54 break;
55 }
56 DDRC = 0x38; PORTC = 0x07;//输入完毕恢复默认输出模式
57 break;
58
59 case 0x03:
60 DDRC = 0x07; PORTC = 0x38;//将PC3-PC5(列)置为输入模式
61 delay_us(1);
62 tempL = PINC & 0x38; //取PC3-PC5用于判断列
63 switch (tempL)
64 {
65 case 0x30: key = 0x07; //得到键值
66 break;
67 case 0x28: key = 0x08;
68 break;
69 case 0x18: key = 0x09;
70 break;
71 default: key = 0;
72 break;
73 }
74 DDRC = 0x38; PORTC = 0x07;//输入完毕恢复默认输出模式
75 break;
76
77 default:
78 key = 0;
79 break;
80 }
81 return (key);
82
83 }
84
85 //主函数,扫描按键显示数据
86 void main()
87 {
88 unsigned char temp,keynum;
89 unsigned char num[10] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
90 //初始化端口
91 DDRB = 0xFF; //设置B口为输出模式
92 PORTB = 0xFF; //置高电平
93 //将PC0-PC2(行)置为输入模式
94 DDRC = 0x38;
95 PORTC = 0x07;
96
97 while(1) //两次检测行中有没有按下
98 {
99 PORTC = 0x40; //没有按键时,LED默认显示-
100 //第一次检验
101 temp = PINC & 0x07;
102 if(0x07 == temp)
103 continue;
104 delay_ms(10);
105 //第二次检验
106 temp = PINC & 0x07;
107 if(0x07 == temp)
108 continue;
109
110 //有按下,LED显示键值
111 keynum = ScanKey();
112 PORTB = num[keynum];
113 //等到按键被释放
114 while(0x07 != temp)
115 temp = PINC & 0x07;
116 }
117 }
代码总结:
主函数:判断是否有按键按下,并消除抖动干扰,若有则将获得的键值显示在LED数码管中;
ScanKey函数:得到扫描码确定是哪个按键,等待按键释放,返回该按键的值。
方法扩展:
(1)除了像上面的对按键的接口不停的扫描,
(2)还可以使用定时扫描,例如用一个定时器,每隔10MS 对按键接口进行扫描,看是否有按键按下;
(3)也可以使用中断的方式去扫描,当按键按下时由硬件电路产生一个中断,MCU 响应该中断,确定哪个按键被按下,处理相应函数。