最近个大网盘都发出通知,用户的数据将会被清楚,其中就有金山快盘,上大学的时候用的金山,很多重要的资料在其中都保存了一份。现在将其全部下载了下来,然后发现了我的毕业论文,看了一遍,就有了这系类文章。今天将矩阵键盘的设计和编程。

背景

我的毕业设计是基于ZigBee网络的一个hub系统,协调器节点作为中枢,控制器和被控制器都连接在这个中枢上。控制器上使用矩阵键盘那就很平常了。

CC2530的端口数比较小,这也是单片机开发中遇到的常见问题,I/O资源有限,那么我们这边的矩阵键盘也是,在这里我们为了减少I/O端口的使用,我们使用了移位寄存器来减少端口的使用。我们使用的是74HC164。下面对其进行简单的介绍。

74HC164移位寄存器

74HC164是一种由边沿触发的8位移位寄存器器件,其以串行方式一位一位的接受数据输入,然后以并行方式输出一个8位的结果数据。通过两个数据输入端(DSA或DSB)中的一个进行数据的串行输入或者将两个输入端连在一起共同输入。
每次时钟信号(CP)由低变高时,即发生边沿触发,寄存器中的数据都要右移一个比特,这时Q0的电位即为此次的数据输入的结果,是0还是1,也就是数据输入端DSA和DSB的逻辑与。
当主复位(MR)接低电平是,将会使其他端口失效,重置寄存器,输出端口置低电平。
引脚为下图所示:

各引脚说明:

键盘模块设计

矩阵键盘是一种通用的,在工控行业应用广,使用成熟的布局类似于矩阵的按键组。
当实际应用需要很多按钮时,为了尽量减少占用的I/O端口数,通常需要使用一个矩阵键盘。矩阵键盘中每个行线和列线在相交处不直接连通,而是用一个按键作为连接两者的点。这样,如果一个需要包含16个按键的键盘,排成一个4x4的矩阵,就只需要8个端口,比一个I/O连接一个按键减了少一倍的端口数使用,这对于I/O较紧张的系统来说是很重要的,而且当线数越多时,两者之间的区别就越突出,比如再多加一条行线就可以多包含4个按键,而直接使用端口线则只能构成9个按键的键盘。由此可见,在需要的键数较多的情况下,采用矩阵法是在合适不过的。
相比较而言,矩阵式结构肯定比直接相连结构在设计布局上要复杂的多,对于按键按下操作的识别也要复杂。在对矩阵式键盘处理按下事件时一般采用行列扫描法和反转法。本设计使用的是反转法。
一般的反转法的接法是行线和列线接I/O口,其中一列为低,一列为高。比如一个器件有两排端口分别为P0和P1,现在P0接行线,P1接列线,如果P0初始状态为高,那么P1则为低。当所有的按键都是处于未按下的状态,则P0继续保持高电平的状态,P1也保持的原来低电平的状态。如果其中有一个按键被按下,因为按键连接着行列线,P0为高电平状态,P1为低电平状态,所以P0中会有一个I/O端口的电位会被拉低,此时我们就可以知道按下的按键位于哪一行。在检测到P0口有I/O端口电位被拉低后,记录下此时P0口的状态,在C51编程中,一般使用一个8bit数保存。记录下P0状态后,立刻有规律的改变P1的各位的电位,改变的规则为从一个方向开始,依次拉高一个I/O的电位,其他位保持低电平,检查P0的状态是否恢复到原来的高电平,如果为高电平则记录下P1口的状态,此时记录下的P0口和P1口的状态的两个数据,就可以唯一的代表一个按键。
在本课题中,由于CC2530的I/O端口数量少,为了尽量地减少I/O端口的使用,以便以后能够对系统进行拓展,我们使用了8位串入、并出移位寄存器74HC164来减少I/O的使用。我们的主键盘设计为2x8的矩阵,另外还有两个独立的按键(左右按键,现阶段只使用了左按键),一共18个按键,使用中断来通知CPU发生了按键事件,其中主键盘共用一个中断,两个独立的按键各自使用一个中断,不使用轮询,这样有效的提高了按键的稳定性,在复杂系统中提高了按键的响应度。本来主键盘的列线需要8位I/O口,但是现在我们使用74HC164,则列线只需要使用一个I/O口,行线数量不变,现在就只需要3个I/O口就可以完成,这大大降低了I/O的使用数量,为以后扩展提供了I/O空间。

键盘布局如下:

因为使用了中断,所以当按钮按下时,两行按键中会有一行电平被拉低,使用一个与门来得出按键按下的信号,也就是当有按键按下时,与门输入为低电平,触发中断。

键盘驱动程序设计

键盘的第一行与P1_4(P1端口的第5个引脚)相连,第二行与P1_5(P1端口的第6个引脚)相连,单独的左按键与P1_7(P1端口的第8个引脚)相连,74HC164的数据输入端与P1_2(P1端口的第2个引脚)相连,74HC164的时钟线与P1_3(P1端口的第4个引脚)相连,与门的输出端,也就是中断信号线与P1_6相连。
在键盘驱动初始化过程中需要对使用到的端口进行对应的设置。行线所接的端口和中断信号接口都是输入口,74HC164的数据和时钟都是输出口,中断的端口还需要进行相应的功能设置。
P1DIR为P1口的方向寄存器,可位寻址,当某一位设置为0时,此I/O口用于输入,为1时,则用于输出。所以将P1DIR的4~7设置为0,2和3设置为1。P1IEN为P1口的中断使能寄存器,为1开中断,为0关中断,6和7分别用于主键盘和左键盘中断。
D0~D3位被用来对端口的中断触发方式进行设置,取值为0时设置上升沿触发,取值为1时设置下降沿触发。P1_6和P1_7使用下降沿触发。P1SEL为P1功能选择寄存器,0代表普通的I/O口,1代表功能口,中断为功能口,所以P1_6和P1_7为功能口。因为P1使用到中断,所以需要P1口中断使用。P1IFG为端口1的终端状态标志寄存器,当端口的某个I/O口有中断请求时,P1IFG寄存器中相应的位将被置1。例如如果P1_6有中断,那么P1IFG的第六位就会置1,在初始化时,可以将它清空。在P1口的中断函数中,需要先判断P1IFG的第六位和第七位是否为1,如果第六位为1,那就是主键盘有按键按下,否则为左按键。
在程序中,使用了两个变量,一个用来标示是否有键按下,另一个用来记录了按下的按键值。当有按键按下时,中断函数起作用,会改变着两个值,那么当操作系统查询按键标识时,发现它标示有按键发生,然后查询按键值,这样就可以根据按键值进行相关的功能设计。那怎样得出是那个按键按下呢?我们根据硬件和设计的算法得出了一个按键的编码表。

主键盘算法描述如下:
1.获得行线当前的状态Th;
2.将{0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80}这八个数据依次发送给74HC164直到行线恢复到原来的状态,记录下此时的发送的数据Tl;
3.将Th和Tl进行或操作,得出的即为此按钮的编码。
对于左按键,使用独立的处理函数,给它的编码值为0xff。当有按键按下时,我们根据算法计算出编码,然后使用查表法,就能够确定按下的是那个按键。

code

void send_key(uint8 key_value)
{int i = 0;for (;i < 8;++i){CLCK = 0;S_IN = ((key_value & 0x80) >> 7);CLCK = 1;key_value <<= 1;        }delay_ms(1);get_row();
}void get_row()
{//00XX0000//0x30uint8 t = P2;uint8 t2 = P1_5;uint8 t3 = P1_4;row |= t2;row <<= 1;row |= t3;row <<= 6;
}void get_column()
{int j = 0;uint8 temp = 0x00;get_row();if (row != 0xc0) {delay_ms(4);get_row();if (row != 0xc0) {temp = row;} else {return;}} else {return;}for (;j < scan_size;++j){send_key(scan[j]);if (temp != row){is_click = 1;row = 0xc0;result_key = temp | (j + 1);CLEARZERO();break;}}
}//供中断调用
void do_button()
{get_column();
}uint8 click()
{uint8 re = is_click;if (is_click == 1){is_click = 0;}return re;
}
uint8 key()
{return result_key;
}char parse_key(uint8 key_value)
{char re;int i = 0;for (;i<16;i++){if (button_code[i] == key_value){if (i < 10){re = '0' + i;}else{switch(i){case 10:re = '=';break;case 11:re = '+';break;case 12:re = '-';break;case 13:re = '*';break;case 14:re = '/';break;case 15:re = 'C';break;}}break;}}return re;
}void button_init()
{//高电平为输出方式//000011xxP1DIR &= 0x0f;P1DIR |= 0x0c;P1IEN |= 0xc0;//7 6中断是能PICTL |= 0x04;//P1下降沿触发P1INP &= 0x0f;//上拉P1SEL |= 0xc0;//6 7功能口P1SEL &= 0xc0;//0-5 I/O口IEN2 |= 0x10;P1IFG = 0;CLEARZERO();
}void do_left_soft_button()
{is_click = 1;result_key = 0xff;
}#pragma vector = P1INT_VECTOR    //外部中断函数
__interrupt void P1_INT(void){IEN2 &= 0xef;;if (P1IFG > 0){if (P1IFG & 0x40) {do_button();} else {//左软键do_left_soft_button();}}IEN2 |= 0x10;P1IFG = 0;
}

其中有很多CC2530初始化中断和端口的代码。主要是send_key函数,使用在移位过程中,器件特性导致了一定的延时,当时调试的时候,反转的时候一直没用,然后想到74HC164可能在输入和并出有一点的间隔,然后加了延时就OK了。在读行的时候也加了延时,而且读两遍,不然可以就读不出来。

CC2530+74HC164矩阵键盘的设计相关推荐

  1. 矩阵键盘硬件设计及软件流程

    很多情况下我们芯片的IO口不够用,但是有需要很多按键的话,矩阵键盘是一个很好的选择,通过矩阵键盘的设计我们可以用很少的IO来实现多个按键的功能.下面我们来介绍一下矩阵键盘的相关设计,不多说直接上图 上 ...

  2. 矩阵键盘简易计算机设计报告,矩阵键盘显设计报告..doc

    摘要 在日常生活中,我们经常要用到键盘来实现对电子装置的控制.小到手表手机,中到电视电脑,大到各种复杂仪器,都需要通过各种按键来实现各种操作.本次课程设计以按键控制显示为主题,以MSP430G2553 ...

  3. c语言数码管加法程序怎么写,拜托大神写数码管与矩阵键盘结合设计简易计算器C语言的程序...

    我和你用的是一样的,给你一个我买板子送的矩阵程序 //4*4键盘检测程序,按下键后相应的代码显示在数码管上 #include sbit beep=P2^3; sbit dula=P2^6; sbit ...

  4. 单片机实验说明<四>矩阵键盘与LCD基本使用

    实验四 矩阵键盘识别设计 设计要求: 对4×4矩阵式键盘电路的键值进行编码,编程实现在LCD液晶显示器上显示每个按键的"0-F"序号 电路设计: 软件代码: #include &q ...

  5. 4x4矩阵键盘(STM32F103C8)

    一.前言 使用STM32F103C8T6最小系统,实现4x4矩阵键盘的键值上报,通过串口调试助手打印相关的键值,使用的是逐行逐列扫描法,通过库函数实现. 二.概述 ( 一)矩阵键盘简述 矩阵键盘是单片 ...

  6. 单片机(二):3个IO扩展n*8个IO,基于74hc595与74hc165的8x8矩阵键盘

    3个IO通过一片74hc595扩展8个输出IO,3个IO通过一片74hc1655扩展8个输入IO,最终成为8X8的矩阵键盘.对于普通的矩阵键盘,再加入防止鬼键.消抖和按下与松开识别. 一.74hc59 ...

  7. 进阶项目(11) 矩阵键盘程序设计讲解

    写在前面的话 在使用按键的时候,如果按键不多的话,我们可以直接让按键与FPGA相连接,但是如果按键比较多的时候,如果还继续使用直接让按键与FPGA相连接的话,会大量增加FPGA端口的消耗,为了减少FP ...

  8. Arduino智能闹钟设计(8x8矩阵键盘+LCD显示)

    Arduino智能闹钟设计(8x8矩阵键盘+LCD显示+蜂鸣器歌曲) 作者:STCode(公众号同名) 1.效果描述: 本设计基于Arduino Nano控制器,主要功能为定时闹钟和环境温湿度显示,使 ...

  9. 单片机实验——十六路抢答器 ,抢答器是一种应用十分广泛的设备,在各种竞赛、抢答场合中,它能迅速、客观地分辨出最先获得发言权的选手。本设计采用矩阵键盘节省输入的I/O口,带有奇偶校验的双单片通信。

    1.实验内容与要求 抢答器是一种应用十分广泛的设备,在各种竞赛.抢答场合中,它能迅速.客观地分辨出最先获得发言权的选手.本设计采用矩阵键盘节省输入的I/O口,带有奇偶校验的双单片通信,确保发送的正确接 ...

  10. 矩阵键盘简易计算机设计报告,矩阵键盘设计实验报告.doc

    南 京 林 业 大 学 实验报告 基于AT89C51 单片机4x4矩阵键盘接口电路设计 课 程 机电一体化设计基础 院 系 机械电子工程学院 班 级 学 号 姓 名 指导老师 杨雨图 201 南京林业 ...

最新文章

  1. ExecuteNonQuery() 返回值
  2. CTF-RSA-tool 安装全过程
  3. 在R.java中新建自定义的新类
  4. 用Xamarin 实现园友的 :Android浮动小球与开机自启动
  5. OpenCV安全栅栏摄像头security barrier camera的实例(附完整代码)
  6. JavaSE 学习参考:变量(1)
  7. java通过poi读取excel中的日期类型数据或自定义类型日期
  8. Django的各种初识
  9. java classifier_Java 朴素贝叶斯分类器、SVM(5行代码)实现乳腺癌分类
  10. 大型K8s沙龙来啦,火速报名——Kubernetes Cloud Native Meetup
  11. MyBatis框架学习笔记02:利用MyBatis实现CRUD操作
  12. mysql中使用case when语句
  13. Maven技巧和窍门:高级Reactor选项
  14. 图片字符串base64的判断
  15. bootstrap java web_JavaWEB开发05_Bootstrap
  16. centos7 如何安装部署k8s_如何在CentOS 7上正确安装Chromium
  17. Nginx的入门详解
  18. Exploring Visual Relationship for Image Captioning
  19. Jenkins安装插件报错解决方法
  20. wd 文件服务器客服电话,wd 云服务器

热门文章

  1. 针对s3c2440芯片制作交叉编译工具链
  2. base URL是什么意思?干什么用的?
  3. Java Code Review 介绍
  4. 江苏省领航杯人工智能竞技人车协同赛项记录
  5. 编译安装Vue-devtools
  6. dockerfile的端口映射
  7. Java读取Rinex2.10格式的广播星历n文件
  8. C51单片机行人过马路按钮控制信号灯及Protues仿真(有缺陷)
  9. _itemmod_creation_enchant
  10. 客户预付款处理和设置