3个IO通过一片74hc595扩展8个输出IO,3个IO通过一片74hc1655扩展8个输入IO,最终成为8X8的矩阵键盘。对于普通的矩阵键盘,再加入防止鬼键、消抖和按下与松开识别。
一、74hc595介绍
74HC595是一个8位串行输入、并行输出的移位缓存器。通俗的来讲就是在输入时钟的上升沿数据输入端的数据可以位移进入芯片内部的位移缓存器,多位数据移位输入完成后,在输出锁存时钟的上升沿时将数据存入并行输出缓存器,在输出使能时数据输出到并行输出端。相当于可以通过3个IO口控制输出并行的8个IO口,甚至通过74hc595的级联扩展更多的IO,相比于74HC138更加的灵活和可扩展。这里介绍怎么使用,具体的性能参数靠参考技术手册。
符号 |
引脚 |
描述 |
Q0--Q7 |
第15脚, 第1-7脚 |
8位并行数据输出 |
GND |
第8脚 |
电源地 |
Q7’ |
第9脚 |
串行数据输出 |
SCLK#(MR) |
第10脚 |
主复位(低电平有效),可接VCC |
SCK(SH_CP) |
第11脚 |
数据输入时钟线 |
RCK(ST_CP) |
第12脚 |
输出存储器锁存时钟线 |
G#(OE) |
第13脚 |
输出有效(低电平有效),可接地 |
SER(DS) |
第14脚 |
串行数据输入 |
VCC |
第16脚 |
电源正级 |
表x 引脚定义
图x 74hc595操作示意图
74HC595可以进行级联,级联时共用数据输入时钟、数据锁存时钟和输出数据使能口。不同的是Q7’接下一片74HC595的串行数据输入口,相当于上一片595的数据移位溢出之后,数据就进入了下片595。
二、74HC165介绍
74HC165是一款高速CMOS八位并入串出移位寄存器,功能与74HC595相反。这里介绍怎么使用,具体的性能参数靠参考技术手册。
符号 |
引脚 |
描述 |
SH/LD(PL) |
第1脚 |
数据加载控制, 低电平时读取并行输入端口数据存储在移位寄存器中 高电平时串行数据从DS进入移位寄存器中,用于级联 |
CLK(CP) |
第2脚 |
时钟输入,上升沿触发。CE使能时,移位输出。 |
A-F |
第11-14 3-6脚 |
并行数据输入引脚 |
QH’ |
第7脚 |
串行数据互补输出 |
GND |
第8脚 |
电源地 |
QH |
第9脚 |
串行数据输出引脚 |
SER(DS) |
第10脚 |
串行数据输入引脚 |
CLK_INH(CE) |
第15脚 |
时钟输入使能引脚,低电平触发,可接地 |
VCC |
第16脚 |
电源正极 |
表x 引脚定义
图x 74hc165操作示意图
74HC165可以进行级联,级联时共用数据输入时钟、数据锁存时钟和输出数据使能口。不同的是DS接下一片74HC165的QH/QH’,相当于把多片165的移位寄存器串联起来,在时钟信号下数据逐步输出。
三、8x8矩阵键盘
8x8采用一片74hc595、一片74hc165、64个按键和64个二极管组成。其中595负责行扫描按键,165负责扫描选中的这一行的按键。如图595扫描第一行,即输出b01111111,165检测到第3列的电平为0,即1行3列的按键被按下,依次循环扫描检测。这样就是矩阵键盘的设计原理,但是在多个按键被按下的时候,会出现幽灵按键或者鬼键的问题。
图x 矩阵键盘示意图
幽灵按键或者鬼键出现在一个矩形的三个角的按键被按下后,会检测为剩余一个角上的按键被按下,就好像出现了一个幽灵按键。如下图,1行3,4列,2行3列被按下,但是会检测到按下的是2行4列的按键。解决的办法就是在按键的左边或者右边加入一个二极管,使得电流只能单方向的流动。
图x 鬼键示意图
四、软件设计
软件中包含了行扫描和列检测,其中还增加了按键的消抖检测。当按键按下时,电平会有一段信号不稳定的时间,要等到电平稳定之后才能判断按键确实按下。为了消抖,创建了两个二维数组,一个记录检测到一次按下信号后扫描的次数,一个记录按下信号的次数,比较这两个数,相差较大则不稳定,记录的数据多并且两个数据相差较小时则稳定。
/*
reg:
KeyScane -> 检测到按下就加1
KeyCount -> 当检测到一次按键按下后,每扫描一次就加1
KeyStatus -> 0未按下的检测按下状态,1按下的稳定状态,2未按下或者处于松开状态
size -> shape of matrix
*/
void ScanKey(unsigned char* KeyScane, unsigned char* KeyCount, unsigned char* KeyStatus, unsigned char size){
unsigned char ScanData = 0;
unsigned char index_scan, index_move;
// 行扫描
for(index_scan=0; index_scan<8; index_scan++){
ScanData = ~(0x01 << index_scan);
// 595写入数据,从高位开始,也就是先扫描QA,SCK上升沿将数据移入,RCK上升沿数据输出
KRCK = 0;
for(index_move=0; index_move<8; index_move++){
DATA_OUT = (ScanData << index_move) & 0x80;
KSCK = 0;
mDelayuS(10);
KSCK = 1;
}
KRCK = 1;
//列扫描
// 165检查输入,当PL拉高时,CP给一个上升沿,8位寄存器中的值就通过Q7输出一位
PL = 0;
mDelayuS(10);
PL = 1;
for(index_move=0; index_move<8; index_move++){
// 未按下的检测按下状态
if(KeyStatus[index_scan*size+index_move] == 0){
if(KeyScane[index_scan*size+index_move] >= 1){
KeyCount[index_scan*size+index_move] += 1;
}
if(DATA_IN == 0){
KeyScane[index_scan*size+index_move] += 1;
}
}
// 不处于检测按下状态
else{
if(DATA_IN == 1){
KeyStatus[index_scan*size+index_move] = 2;
}
}
CP = 0;
mDelayuS(10);
CP = 1;
mDelayuS(10);
}
}
}
在app应用中做如下操作:文章来源:https://www.toymoban.com/news/detail-497760.html
UINT8X AT(0x00c8) KeyScane[8][8];
UINT8X AT(0x0108) KeyCount[8][8];
UINT8X AT(0x0148) KeyStatus[8][8];
memset(KeyScane, 0, sizeof(KeyScane));
memset(KeyCount, 0, sizeof(KeyCount));
memset(KeyStatus, 0, sizeof(KeyStatus));
while(1){
ScanKey((unsigned char*)KeyScane, (unsigned char*)KeyCount, (unsigned char*)KeyStatus, 8);
// 当keycount比keyscane大n时都置位0,说明是误触;
// 当keyscane的值大于n,都置位0,说明按下去了
for(h=0; h<8; h++){
for(l=0; l<8; l++){
// 处于检测按下状态
if(KeyStatus[h][l]==0){
// 误触,计数清零,状态不变
if(KeyCount[h][l]-KeyScane[h][l] >= n){
KeyCount[h][l] = 0;
KeyScane[h][l] = 0;
}
// 检测到稳定按下,计数清零,置位按键处于稳定按下状态,将检测松开,处理按下业务
if(KeyScane[h][l] >= n){
KeyStatus[h][l] = 1;
KeyCount[h][l] = 0;
KeyScane[h][l] = 0;
}
}
// 检测到松开,计数清零,置位按键处于检测按下状态,处理松开业务
else if (KeyStatus[h][l]==2)
{
KeyStatus[h][l] = 0;
KeyCount[h][l] = 0;
KeyScane[h][l] = 0;
}
}
}
}
CH552的GPIO配置:文章来源地址https://www.toymoban.com/news/detail-497760.html
// 595 通用数据输出
#define DATA_OUT P1_1
// 按键 595
#define KRCK P3_1
#define KSCK P3_0
// 按键 165
#define CP P1_6
#define PL P1_7
#define DATA_IN P1_5
/*******************************************************************************
* Function Name : PortCfg()
* Description : 端口配置
* Input : PortN 1,3
Mode 0 = 浮空输入,无上拉
1 = 推挽输入输出
2 = 开漏输入输出,无上拉
3 = 类51模式,开漏输入输出,有上拉,内部电路可以加速由低到高的电平爬升
,UINT8 Pin (0-7)
* Output : None
* Return : None
*******************************************************************************/
void PortCfg(unsigned char PortN, unsigned char Mode, unsigned char Pin){
if(PortN==1){
switch(Mode){
case 0:
P1_MOD_OC = P1_MOD_OC & ~(1<<Pin);
P1_DIR_PU = P1_DIR_PU & ~(1<<Pin);
break;
case 1:
P1_MOD_OC = P1_MOD_OC & ~(1<<Pin);
P1_DIR_PU = P1_DIR_PU | (1<<Pin);
break;
case 2:
P1_MOD_OC = P1_MOD_OC | (1<<Pin);
P1_DIR_PU = P1_DIR_PU & ~(1<<Pin);
break;
case 3:
P1_MOD_OC = P1_MOD_OC | (1<<Pin);
P1_DIR_PU = P1_DIR_PU | (1<<Pin);
break;
default:
break;
}
}
else if(PortN==3){
switch(Mode){
case 0:
P3_MOD_OC = P3_MOD_OC & ~(1<<Pin);
P3_DIR_PU = P3_DIR_PU & ~(1<<Pin);
break;
case 1:
P3_MOD_OC = P3_MOD_OC & ~(1<<Pin);
P3_DIR_PU = P3_DIR_PU | (1<<Pin);
break;
case 2:
P3_MOD_OC = P3_MOD_OC | (1<<Pin);
P3_DIR_PU = P3_DIR_PU & ~(1<<Pin);
break;
case 3:
P3_MOD_OC = P3_MOD_OC | (1<<Pin);
P3_DIR_PU = P3_DIR_PU | (1<<Pin);
break;
default:
break;
}
}
}
void Init_GPIO_HC(void){
PortCfg(1, 3, 1);
PortCfg(1, 3, 6);
PortCfg(1, 3, 7);
PortCfg(1, 3, 5);
PortCfg(3, 3, 0);
PortCfg(3, 3, 1);
PortCfg(3, 3, 4);
PortCfg(3, 3, 3);
}
到了这里,关于单片机(二):3个IO扩展n*8个IO,基于74hc595与74hc165的8x8矩阵键盘的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!