ATmega8仿真——键盘扫描的学习

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 响应该中断,确定哪个按键被按下,处理相应函数。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏瓜大三哥

UVM(十二)之各register model续

UVM(十二)之各register model续 1. register model的必要性 假设有如下的DUT: ? 这个DUT相当的简单,它只有一个寄存...

2075
来自专栏Linux驱动

18.Llinux-触摸屏驱动(详解)

本节的触摸屏驱动也是使用之前的输入子系统 1.先来回忆之前第12节分析的输入子系统 其中输入子系统层次如下图所示, ? 其中事件处理层的函数都是通过input_...

2049
来自专栏Samego开发资源

Android车轮之图片加载框架Android-Universal-Image-Loader

1275
来自专栏落影的专栏

直播APP的性能优化-礼物篇

介绍 记录、总结开发遇到一些问题,大家一起交流学习。 这次带来,对直播APP性能优化的总结,以QA的形式总结。 欢迎关注文集-直播Live 实现方式 1、Q:...

2956
来自专栏斑斓

AKKA中的事件流

在《企业应用集成模式》一书中,定义了许多与消息处理有关的模式,其中运用最为广泛的模式为Publisher-Subscriber模式,尤其是在异步处理场景下。 基...

3414
来自专栏互联网杂技

前端各种优化

1.  请减少HTTP请求   基本原理:   在浏览器(客户端)和服务器发生通信时,就已经消耗了大量的时间,尤其是在网络情况比较糟糕的时候,这个问题尤其的突...

3148
来自专栏函数式编程语言及工具

Akka(15): 持久化模式:AtLeastOnceDelivery-消息保证送达模式

  消息保证送达是指消息发送方保证在任何情况下都会至少一次确定的消息送达。AtleastOnceDelivery是一个独立的trait,主要作用是对不确定已送达...

2385
来自专栏黄Java的地盘

基于React与Redux的留言墙的实现

由于某事业群需要留言墙用于年会,同时需要调用大象公众号服务器接口,所以在今年年初开发了留言墙用于活动现场交流。

451
来自专栏河湾欢儿的专栏

Vuex

Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 V...

791
来自专栏前端杂货铺

跨浏览器tab页的通信解决方案尝试

目标 当前页面需要与当前浏览器已打开的的某个tab页通信,完成某些交互。其中,与当前页面待通信的tab页可以是与当前页面同域(相同的协议、域名和端口),也可...

2954

扫码关注云+社区