【STC8A8K64S4A12开发板】—4x4矩阵按键检测

版权声明:本文为博主原创文章,转载请附上原文出处链接。


前言

今天介绍下STC8A8K64S4A12系列单片机4x4矩阵按键检测电路的工作原理、4x4矩阵按键检测的程序设计。


一、硬件电路设计

1.矩阵按键检测介绍

在讲解输入按键检测时,总结了按键检测常见的有GPIO高低电平检测和ADC电阻分压检测。分压式接法,使用的单片机引脚须具有ADC功能,根据检测口测得不同的电压值来识别是哪个按键被按下。如下图。

在这里插入图片描述

图1:分压式接法原理

ADC电阻分压检测按键的方法适合单片机IO口资源非常紧张的场合,如一些电磁炉的按键使用的就是这种方法。高低电平式接法检测按键需要用到的单片机IO口较多,而高低电平式接法又可分为两种:独立式接法和行列式接法。

行列式接法是利用单片机的 GPIO口组成行与列,在行与列的每一个交点处连接按键。 故也称为矩阵式按键,该接线方法最大的优势是可以使用较少的GPIO口实现较多按键的检测。根据行和列的不同可以有很多种矩阵按键组合,比如2x3矩阵按键、2x4矩阵按键、3x3矩阵按键、4x4矩阵按键等等。

在这里插入图片描述

图2:矩阵按键原理图

☆注:行的英文是Row,列的英文是Line。

上述矩阵按键原理图中没有加任何保护电阻,市场上很多矩阵按键检测模块,都是参考上图来的。这样的设计可以实现矩阵检测,但是并不是最规范的。

在这里插入图片描述

图3:4x4矩阵模块

STC8A8K64S4A12开发板上设计了4x4矩阵按键检测电路,该检测电路在行检测和列检测连接单片机IO上串联了220Ω电阻,并且把行检测用IO口加了10K上拉电阻,如图。

在这里插入图片描述

图4:开发板4x4矩阵按键检测部分

★ 上图开发板板载矩阵按键电路的RN3和RN4都是串在单片机GPIO口引脚的220Ω电阻,作用为:

  1. 保护单片机GPIO口。若本该作为输入用的GPIO口不小心被配置成了输出(输出了高电平或低电平),按下按键外部会直接给GPIO口一个高电平或低电平。如果没有串接电阻,则若单片机GPIO口输出的电平和按键按下给的电平不一致,会损坏GPIO口。
  2. 便于检测。串接220Ω电阻可有效降低按键按下时产生的抖动峰值电压。

★ 上图开发板板载矩阵按键电路的RN2作用:保证行检测用单片机GPIO口在按键没有按下时为有效高电平。

★ 4x4矩阵按键检测电路占用的单片机的引脚如下表:

表1:4x4矩阵按键检测电路引脚分配

在这里插入图片描述

☆注:
1)独立GPIO表示开发板没有其他的电路使用这个GPIO,非独立GPIO说明开发板有其他电路用到了该GPIO。
2)在使用开发板4x4矩阵按键功能时,不仅J30和J31端子需使用8个短路帽短接,还要去掉J29的2个短路帽,以将P2.6和P2.7与用户指示灯断开连接。
3)开发板外部晶振电路没有焊接,也就意味着P1.6和P1.7可以使用。而如果外部晶振焊接了,则一定注意使用杜邦线连接其他没有占用的GPIO到 J31端子的L3和L4上。

2.矩阵按键检测原理介绍

矩阵按键检测的工作原理:按键设置在行、列线交点上,行、列线分别连接到按键开关的两端。行线通过上拉电阻接到VCC电源上。无按键按下时,行线处于高电平的状态,而当有按键按下时,行线电平由与此行线相连的列线电平决定。

矩阵按键检测方法有多种,这里介绍下比较常用的行列扫描法。原理解析如下:

  1. 将行线(R1Rm)配置为输出口并控制输出为高电平,将列线(L1Ln)配置为输出口并控制输出为低电平。再配置行线(R1Rm)为输入口、列线(L1Ln)仍是输出口,检测行线(R1Rm)的GPIO口电平变化。如果有按键按下,按键按下的对应行线被拉低,否则所有的行线(R1Rm)都为高电平。
  2. 将列线(L1Ln)配置为输出口并控制输出为高电平,将行线(R1Rm)配置为输出口并控制输出为低电平。再配置列线(L1Ln)为输入口、行线(R1Rm)仍是输出口,检测列线(L1Ln)的GPIO口电平变化。如果有按键按下,按键按下的对应列线被拉低,否则所有的列线(L1Ln)都为高电平。
  3. 在第一步和第二步判断有键按下后,延时10ms消除机械抖动,再次读取相应的行值或列值。
  4. 开始扫描按键位置,采用行扫描,得到行值;采用列扫描,得到列值,再分别把行值和列值保存起来。
  5. 将保存的行值和列值合并,得到按键对应的编码值,通过对编码值的操作可得到对应的按键信息,完成对矩阵按键的检测。

☆注:m代表矩阵按键检测电路的行数,n代表矩阵按键检测电路的列数。

二、软件设计

1.矩阵按键扫描实验 – 指示灯闪烁

1.1.工程需要用到的c文件

本例需要用到的c文件如下表所示,工程需要添加下表中的c文件。

表2:实验需要用到的c文件
序号文件名后缀功能描述
1led.c用户LED有关的用户自定义函数。
2key_4x4.c4x4矩阵按键有关的用户自定义函数。
3delay.c包含用户自定义延时函数。

1.2.头文件引用和路径设置

■ 需要引用的头文件

#include " led.h"  
#include " delay.h"  
#include " key_4x4.h"

■ 需要包含的头文件路径

本例需要包含的头文件路径如下表:

表3:头文件包含路径
序号路径描述
1…\ Sourceled.h,key_4x4.h和delay.h头文件在该路径,所以要包含。
2…\UserSTC8.h头文件在该路径,所以要包含。

MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。

在这里插入图片描述

图5:添加头文件包含路径

1.3.编写代码

首先,在key_4x4.c文件中对4x4矩阵按键进行扫描,并返回键值。该KeyScan函数代码如下。

程序清单:4x4矩阵扫描函数

/***************************************************************************** 
 * 描  述 : 4x4矩阵扫描函数 
 * 入  参 : 无 
 * 返回值 : 哪个按键按下的对应值 
 *****************************************************************************/  
uint8 KeyScan(void)  
{  
    uint8 X_temp,Y_temp,temp;  
      
     X_temp=0xF0;        //列值赋初值  
     Y_temp=0x0F;        //行值赋初值  
  
     P1M1 &= 0x3F;  P1M0 |= 0xC0;     //设置P1.6~P1.7为强推挽输出  
     P2M1 &= 0x3F;  P2M0 |= 0xC0;     //设置P2.6~P2.7为强推挽输出  
     P0M1 &= 0xF0;  P0M0 |= 0x0F;     //设置P0.0~P0.3为强推挽输出  
  
      
    ROW1=1;ROW2=1;ROW3=1;ROW4=1;  //行置高  
      COL1=0;COL2=0;COL3=0;COL4=0;  //列置低  
      
        //所用到行IO口配置为输入,进行检测  
      delay_ms(15);  
      P2M1 &= 0x3F; P2M0 &= 0x3F;     //设置P2.6~P2.7为准双向口  
      P0M1 &= 0xF6; P0M0 &= 0xF6;     //设置P0.0,P0.3为准双向口  
    delay_ms(15);  
      
        if(ROW1 == 0)         //检测行1电平是否为低电平  
        {  
            delay_ms(DELAY_TIME);  
            if(ROW1 == 0)  
            Y_temp &= 0x0E;   
        }  
        if(ROW2 == 0)         //检测行2电平是否为低电平  
        {  
            delay_ms(DELAY_TIME);  
            if(ROW2 == 0)  
            Y_temp &= 0x0D;   
        }  
        if(ROW3 == 0)         //检测行3电平是否为低电平  
        {  
            delay_ms(DELAY_TIME);  
            if(ROW3 == 0)  
            Y_temp &= 0x0B;   
        }  
        if(ROW4 == 0)         //检测行4电平是否为低电平  
        {  
            delay_ms(DELAY_TIME);  
            if(ROW4 == 0)  
            Y_temp &= 0x07;   
        }  
        
     P1M1 &= 0x3F;  P1M0 |= 0xC0;     //设置P1.6~P1.7为强推挽输出  
     P2M1 &= 0x3F;  P2M0 |= 0xC0;     //设置P2.6~P2.7为强推挽输出  
     P0M1 &= 0xF0;  P0M0 |= 0x0F;     //设置P0.0~P0.3为强推挽输出  
      
        ROW1=0;ROW2=0;ROW3=0;ROW4=0;  //行置低  
      COL1=1;COL2=1;COL3=1;COL4=1;  //列置高  
      
        //所用到列IO口配置为输入,进行检测  
        delay_ms(15);  
      P1M1 &= 0x3F; P1M0 &= 0x3F;     //设置P1.6~P1.7为准双向口  
      P0M1 &= 0xF9; P0M0 &= 0xF9;     //设置P0.1,P0.2为准双向口  
    delay_ms(15);  
      
        if(COL1 == 0)         //检测列1电平是否为低电平  
        {  
            delay_ms(DELAY_TIME);  
            if(COL1 == 0)  
            X_temp &= 0xE0;   
        }  
        if(COL2 == 0)         //检测列2电平是否为低电平  
        {  
            delay_ms(DELAY_TIME);  
            if(COL2 == 0)  
            X_temp &= 0xD0;   
        }  
        if(COL3 == 0)         //检测列3电平是否为低电平  
        {  
            delay_ms(DELAY_TIME);  
            if(COL3 == 0)  
            X_temp &= 0xB0;   
        }  
        if(COL4 == 0)         //检测列4电平是否为低电平  
        {  
            delay_ms(DELAY_TIME);  
            if(COL4 == 0)  
            X_temp &= 0x70;   
        }  
          
        //将行值和列值合并,得到按键对应的编码值,该值与16个按键一一对应  
        temp = X_temp|Y_temp;  
      temp = ~temp;  
              
        //将按键检测的原始编码值解析对应按键值信息  
    switch (temp)  
    {     
              case 0x11:return 1;   //1  
              case 0x21:return 2;   //2   
              case 0x41:return 3;   //3  
              case 0x81:return 4;   //4  
              case 0x12:return 5;   //5  
              case 0x22:return 6;   //6  
              case 0x42:return 7;   //7  
              case 0x82:return 8;   //8  
              case 0x14:return 9;   //9  
              case 0x24:return 10;  //0  
              case 0x44:return 11;  //a  
              case 0x84:return 12;  //b  
              case 0x18:return 13;  //c  
              case 0x28:return 14;  //d  
              case 0x48:return 15;  //e  
              case 0x88:return 16;  //f   
              default:  return 0;   
    }                   
}

然后,主函数中通过检测按键扫描的键值来控制用户指示灯闪烁不同的次数。具体代码如下。

代码清单:主函数

int main(void)  
{  
        uint8 temp;  
      
  while(1)  
  {  
        temp=KeyScan();     //得到键值  
      if(temp)  
        {                            
            switch(temp)  
            {                  
                case 1: //按下按键S3  
            LED_FLI_D3(1);   //用户指示灯D3闪烁1次  
                    break;  
                case 2: //按下按键S4  
                    LED_FLI_D3(2);   //用户指示灯D3闪烁2次  
                    break;  
                case 3: //按下按键S5   
          LED_FLI_D3(3);   //用户指示灯D3闪烁3次  
                    break;  
                case 4: //按下按键S6  
          LED_FLI_D3(4);   //用户指示灯D3闪烁4次  
                    break;  
                case 5: //按下按键S7  
            LED_FLI_D3(5);   //用户指示灯D3闪烁5次  
                    break;  
                case 6: //按下按键S8  
                    LED_FLI_D3(6);   //用户指示灯D3闪烁6次  
                    break;  
                case 7: //按下按键S9   
          LED_FLI_D3(7);   //用户指示灯D3闪烁7次  
                    break;  
                case 8: //按下按键S10  
          LED_FLI_D3(8);   //用户指示灯D3闪烁8次  
                    break;  
                case 9: //按下按键S11  
            LED_FLI_D4(1);   //用户指示灯D4闪烁1次  
                    break;  
                case 10:    //按下按键S12  
                    LED_FLI_D4(2);   //用户指示灯D4闪烁2次  
                    break;  
                case 11:    //按下按键S13  
          LED_FLI_D4(3);   //用户指示灯D4闪烁3次  
                    break;  
                case 12:    //按下按键S14  
          LED_FLI_D4(4);   //用户指示灯D4闪烁4次  
                    break;  
                case 13:    //按下按键S15  
            LED_FLI_D4(5);   //用户指示灯D4闪烁5次  
                    break;  
                case 14:    //按下按键S16  
                    LED_FLI_D4(6);   //用户指示灯D4闪烁6次  
                    break;  
                case 15:    //按下按键S17      
          LED_FLI_D4(7);   //用户指示灯D4闪烁7次  
                    break;  
                case 16:    //按下按键S18  
          LED_FLI_D4(8);   //用户指示灯D4闪烁8次  
                    break;  
            }  
        }  
    }  
}  

1.4.硬件连接

本矩阵按键实验需要用到P26和P27,所以J29的短路帽需去掉。矩阵按键检测用到8个GPIO,所以J30和J31端子均需短路帽短接,实验连接图如下。

在这里插入图片描述

图6:4x4矩阵按键检测实验连接图

2.矩阵按键扫描实验 – 串口调试助手

2.1.工程需要用到的c文件

本实验需要用到的头文件以及添加头文件包含路径的方法请参考“实验2-18-1:4x4矩阵按键扫描实验 - 指示灯闪烁”部分。

2.2.编写代码

首先,在key_4x4.c文件中对4x4矩阵按键进行扫描,并返回键值。该KeyScan函数代码请参考“实验2-18-1:4x4矩阵按键扫描实验 - 指示灯闪烁”部分。

然后,在主函数中对串口1进行初始化,并通过串口1发送按键扫描的键值。具体代码如下:

代码清单:主函数

int main(void)  
{  
  uint16 temp;  
  
    P3M1 &= 0xFE;   P3M0 &= 0xFE;                     //设置P3.0为准双向口  
    P3M1 &= 0xFD;   P3M0 |= 0x02;                     //设置P3.1为推挽输出  
      
    Uart1_Init();                                 //串口1初始化    
    EA = 1;                                       //使能总中断  
  delay_ms(10);                                 //初始化后延时  
      
  while(1)  
  {  
        temp=(uint16)KeyScan();                               //得到键值  
      if(temp)  
        {             
             printf("\r\n 4x4矩阵按键键值: %d\r\n",temp);    //串口打印上传的采集的原始值  
             temp = 0;                                        //清零        
        }  
    }  
}

2.3.硬件连接

本矩阵按键实验需要用到P26和P27,所以J29的短路帽需去掉。矩阵按键检测用到8个GPIO,所以J30和J31端子均需短路帽短接。另外,需使用USB线连接开发板USB口至电脑,以串口调试助手接收数据。

在这里插入图片描述

图7:4x4矩阵按键检测实验连接图

总结

以上就是今天要讲的内容,希望对你有帮助!

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

电子友人张

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值