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

这篇具有很好参考价值的文章主要介绍了单片机(二):3个IO扩展n*8个IO,基于74hc595与74hc165的8x8矩阵键盘。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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 引脚定义

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

图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 引脚定义

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

图x 74hc165操作示意图

74HC165可以进行级联,级联时共用数据输入时钟、数据锁存时钟和输出数据使能口。不同的是DS接下一片74HC165的QH/QH’,相当于把多片165的移位寄存器串联起来,在时钟信号下数据逐步输出。

三、8x8矩阵键盘

8x8采用一片74hc595、一片74hc165、64个按键和64个二极管组成。其中595负责行扫描按键,165负责扫描选中的这一行的按键。如图595扫描第一行,即输出b01111111,165检测到第3列的电平为0,即1行3列的按键被按下,依次循环扫描检测。这样就是矩阵键盘的设计原理,但是在多个按键被按下的时候,会出现幽灵按键或者鬼键的问题。

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

图x 矩阵键盘示意图

幽灵按键或者鬼键出现在一个矩形的三个角的按键被按下后,会检测为剩余一个角上的按键被按下,就好像出现了一个幽灵按键。如下图,1行3,4列,2行3列被按下,但是会检测到按下的是2行4列的按键。解决的办法就是在按键的左边或者右边加入一个二极管,使得电流只能单方向的流动。

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

图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应用中做如下操作:

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模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【74HC595】51单片机(普中A2开发板)LED点阵屏点亮以及74HC595芯片的使用笔记

    纯小白写的文章,如有问题,欢迎评论区讨论,反驳,指正。 如果你只想了解74HC595如何使用,建议只看2.3。 通过查看普中A2开发板的原理图(图1)可以知道,通过P0(P0_0、P0_1…P0_7)口和DPa、DPb…DPh来控制哪一颗LED点亮。我这个开发板上的点阵模块没有按照他给的原理图上

    2024年02月05日
    浏览(86)
  • 三、51单片机用74HC595控制LED点阵

    (1)外观 (2)作用 LED点阵通过LED(发光二极管)组成,以灯珠亮灭来显示文字、图片等,LED点阵显示被广泛应用于各种公共场合,如汽车报站器、广告屏以及公告牌等。 以8*8LED点阵为例。 (1)单片机端口直接驱动。驱动8*8的点阵需要2个IO端口(16个IO口)。 (2)使用串转并移位锁存器驱

    2024年02月03日
    浏览(72)
  • 51单片机开发:通过74HC595控制LED点阵

    (1)LED点阵就是多个LED发光二极管的集合,在单个LED的发光原理上没有任何区别; (2)上面是16x16的LED点阵,总共有16x16=256个LED灯,由32个引脚来控制,其中16个引脚是正极(posn,n=1、2······、16),16个引脚是负极(NEGn,n=1、2······、16); (3)LED点阵可以用坐标系的思想来理解,

    2024年02月12日
    浏览(52)
  • 51单片机驱动8位数码管(74HC595驱动)滚动显示

    原文链接:https://www.yourcee.com/newsinfo/2929591.html 点击图片购买 主要关注下供电电压是3.0-5.0v(建议5V),请在规定范围内供电。 模块上芯片引出5个引脚,芯片引脚与模块引脚对应关系是:VCC-VCC SCK-SCLK, RCK-RCLK,SER-DIO,GND-GND 可以对比程序学习时序图,便于理解。 1.显示异常可排除

    2024年02月15日
    浏览(58)
  • 使用74HC595完成16*16led矩阵广告牌(51单片机软件仿真)

    目录 74HC595简介(个人理解)与模块代码 软件仿真和代码  SHCP是写入595芯片数据的配置 STCP是发送595芯片内信息的配置 DS是数据配置的区域 595芯片有9个输出引脚,最后一个是供给下一块串联的595使用的,下一块串联的595stcp和shcp与上一块连接的引脚一致,下一块的DS与上一块

    2024年02月06日
    浏览(54)
  • 51单片机LED点阵控制原理、74HC595工作原理和字母和滚动显示实验代码

    目录 1. 74HC595工作原理图解  2.LED点阵控制原理 3.点阵LED实验 3.1 配置74HC595代码 3.2 D点阵LED显示H   3.3 滚动LED  (1) 滚动扫描图示  (2)程序代码 4.遇到奇怪的bug    :        输出使能,输入低电平允许输出,因此需要用接线帽j24让oe和地短接。 (但是我普中A2的板子,不

    2024年02月04日
    浏览(61)
  • 51单片机通过两片74HC595级联,用8位LED数码管,分别显示当前日期,如:“2”、“0”、“-”、“0”、“5”、“-”、“2”、“6”,用Proteus仿真实现。

    1.先上仿真图,但这么连仿真图是不太正确的,要注意。展示的话能用就行。 2.从上到下依次放上数据手册上的引脚解释和逻辑功能和时序图    3.贴上代码:写了发送一字节数据函数,发送全部字节函数,加上一些写了很久的解释(求赞)。

    2024年02月08日
    浏览(60)
  • 基于51单片机hc-05,hc-06蓝牙传送数据到手机APP

           通过蓝牙芯片(HC-05)与手机 APP 通信,每隔 1s (自己可设定)传输一批传感器数据      最近在封装一些传感器的程序,在通过蓝牙连接手机调试数据的时候遇到的一点小问题。蓝牙的介绍网上有很多,这里就不说。把51代码丢出来供大家移植。 通过蓝牙发送浮点型、

    2024年02月11日
    浏览(63)
  • 51单片机---编程实现流水灯----键盘控制数码管显示--利用74LS164扩展并行输出口---定时中断方式驱动一个数码管

    目录 基于51单片机,用c语言编程实现流水灯 代码: 使用C语言编写的基于51单片机的键盘控制数码管显示 代码: 基于51单片机,用c语言编程实现利用74LS164扩展并行输出口 代码: 基于51单片机,用c语言编程实现定时中断方式驱动一个数码管  代码:  1- 2- 上述代码中,使用

    2024年02月16日
    浏览(51)
  • 超声波测距模块HC-SR04详解(基于51单片机)

    本篇文章是个人整理的包含超声波测距模块HC-SR04的基本介绍与基本工作原理以及分别通过LCD1602、数码管和串口显示距离的实例讲解与代码的笔记,部分内容来自《HC-SR04超声波测距模块说明书》,代码使用模块化编辑,部分模块来自江科大自化协的51单片机教学视频。 希望大

    2023年04月16日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包