找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 2602|回复: 6
收起左侧

求助单片机矩阵按键的问题,已经调试了好多天了,谢谢

[复制链接]
ID:320812 发表于 2018-5-2 21:48 | 显示全部楼层 |阅读模式
我已经调试了好多天了,因为白天上班,晚上才有时间,但是连续4,5个晚上都发现不了原因,目前已经可以做到加法了,但是我只要一按键,比如按1,按一下马上弹起,就会有N个1出现在8位数码管上,出现多少根据按的时间决定,求好心人帮帮我。。我仿佛是知道因为程序记录了我多次进行了按1的操作,可是我在按键检测的时候用了if(keysta[j]==0),相当于按键弹起的时候才显示1。我用的两个74HC573接的8位数码管,DUAN WEI两个IO扣控制锁存器的,拜谢了。。拜谢了。。

单片机源程序:
  1. #include<reg52.h>

  2. sbit L0=P1^0;                   //定义L0-L7小灯IO口
  3. sbit L1=P1^1;
  4. sbit L2=P1^2;
  5. sbit L3=P1^3;
  6. sbit L4=P1^4;
  7. sbit L5=P1^5;
  8. sbit L6=P1^6;
  9. sbit L7=P1^7;

  10. sbit o4=P3^0;                    //定义矩阵键盘IO口
  11. sbit o1=P3^1;
  12. sbit o2=P3^2;
  13. sbit o3=P3^3;
  14. sbit i1=P3^4;
  15. sbit i2=P3^5;
  16. sbit i3=P3^6;
  17. sbit i4=P3^7;

  18. sbit DUAN=P2^0;                                 //定义段选开关
  19. sbit WEI=P2^1;                                 //定义位选开关


  20. unsigned char ledbuff[8]={                       //数码管显示缓冲区
  21.     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
  22. };

  23. unsigned char duanma[]={                                                           //定义单个数码管段位,显示0-F
  24.         0x3f,0x06,0x5b,0x4f,
  25.         0x66,0x6d,0x7d,0x07,
  26.         0x7f,0x6f,0x77,0x7c,
  27.         0x39,0x5e,0x79,0x71
  28.         };
  29. unsigned char weima[]={                                                                   //定义单个数码管位,显示第几个数码管
  30.         0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe
  31.         };        

  32. unsigned char keysta[4][4]={                                                 //定义矩阵按键的当前状态
  33.         {1,1,1,1},
  34.         {1,1,1,1},
  35.         {1,1,1,1},
  36.         {1,1,1,1}
  37.         };
  38. unsigned char code keycodemap[4][4]={                                        //矩阵键盘标准键码映射表
  39.         {0x31,0x32,0x33,0x26},                                                        //数字键1、数字键2、数字键3、加号
  40.         {0x34,0x35,0x36,0x25},                                                        //数字键4、数字键5、数字键6、减号
  41.         {0x37,0x38,0x39,0x28},                                                        //数字键7、数字键8、数字键9、乘号
  42.         {0x30,0x1b,0x0d,0x27}                                                        //数字键0、ESC键、  等号、   除号
  43. };

  44. void keydriver();

  45. unsigned char o=0;
  46. unsigned char cnt=0;

  47. void main()
  48. {

  49.         TMOD=0x01;          //计时器模式选择为01,16位计时器
  50.         TH0=0xfc;          //为T0赋初值0xFC67,定时1ms
  51.         TL0=0x67;
  52.         TR0=1;                  //打开计时器
  53.         EA=1;                  //打开中断总开关
  54.         ET0=1;                  //打开中断T0
  55.         ledbuff[0]=duanma[0];

  56.         while(1)
  57.         {
  58.                 keydriver();                   //调用按键驱动函数
  59.         }
  60. }



  61. void shownumber(unsigned long num)                           /* 将一个无符号长整型的数字显示到数码管上,num-待显示数字 */
  62. {

  63.         signed char i;
  64.         unsigned char buf[8];
  65.         for(i=0;i<8;i++)
  66.         {
  67.                 buf[i]=num%10;
  68.                 num=num/10;
  69.         }
  70.         L1=1;
  71.         for(i=7;i>=1;i--)                                                   //从最高位起,遇到0转换为空格,遇到非0则退出循环
  72.         {
  73.                 if(buf[i]==0x00)
  74.                 {
  75.                         ledbuff[i]=0x00;
  76.                 }
  77.                 else
  78.                         break;
  79.         }
  80.         for(;i>=0;i--)                                                                 //剩余低位都如实转换为数码管显示字符
  81.         {
  82.                 ledbuff[i]=duanma[buf[i]];
  83.         }
  84. }

  85. void keyaction(unsigned char keycode)
  86. {
  87.         static unsigned long result=0;                           //用于保存运算结果
  88.         static unsigned long addend=0;                           //用于保存输入的加数
  89.         if((keycode>=0x30)&&(keycode<=0x39))                //输入0-9的数字
  90.         {
  91.                 addend=(addend*10)+(keycode-0x30);           //整体十进制左移,新数字进入个位
  92.                 shownumber(addend);                                                //运算结果显示到数码管
  93.         }
  94.         else if(keycode==0x26)                   //按下加号
  95.         {
  96.                 result+=addend;
  97.                 addend=0;
  98.                 shownumber(result);
  99.         }
  100.         else if(keycode==0x0d)                        //按下等号
  101.         {
  102.                 result+=addend;
  103.                 addend=0;
  104.                 shownumber(result);
  105.         }
  106.         else if(keycode==0x1b)                         //按下ESC
  107.         {
  108.                 addend=0;
  109.                 result=0;
  110.                 shownumber(addend);
  111.         }
  112. }


  113. void keydriver()
  114. {
  115.         unsigned char i,j;
  116.         unsigned char backup[4][4]={                   //按键值备份,保存前一次的值
  117.         {1,1,1,1},
  118.         {1,1,1,1},
  119.         {1,1,1,1},
  120.         {1,1,1,1}
  121.         };
  122.         
  123.         for(i=0;i<4;i++)                                //循环检测4*4的矩阵按键
  124.                 {
  125.                         for(j=0;j<4;j++)
  126.                         {
  127.                                 if(keysta[i][j]!=backup[i][j])                                //检测按键动作
  128.                                 {                                                                                    //按键按下时执行动作
  129.                                         if(keysta[i][j]==0)
  130.                                         {
  131.                                                 keyaction(keycodemap[i][j]);               //调用按键动作函数
  132.                                         }
  133.                                 backup[i][j]=keysta[i][j];                                //刷新前一次的备份值
  134.                                 }

  135.                         }
  136.                 }
  137. }

  138. void keyscan()                                                                         //键盘消抖,检测键盘是否按下
  139. {
  140.     static        unsigned char keyout=0;
  141.         unsigned char j;
  142.         static unsigned char keybuf[4][4]={
  143.         {0xff,0xff,0xff,0xff},
  144.         {0xff,0xff,0xff,0xff},
  145.         {0xff,0xff,0xff,0xff},
  146.         {0xff,0xff,0xff,0xff}
  147.         };
  148.         keybuf[keyout][0]=(keybuf[keyout][0]<<1)|i1;                        //消抖功能,对第一列键盘将keybuf[][]赋值为1111 1111或者0000 0000
  149.         keybuf[keyout][1]=(keybuf[keyout][1]<<1)|i2;
  150.         keybuf[keyout][2]=(keybuf[keyout][2]<<1)|i3;
  151.         keybuf[keyout][3]=(keybuf[keyout][3]<<1)|i4;

  152.         for(j=0;j<4;j++)
  153.         {
  154.                 if((keybuf[keyout][j])==0x00)                                //如果keybuf为0000 0000 则视为按下按键
  155.                 {
  156.                         keysta[keyout][j]=0;
  157.                 }
  158.                 else if((keybuf[keyout][j])==0xff)                         //如果keybuf为1111 1111 则视为弹起按键
  159.                 {
  160.                         keysta[keyout][j]=1;

  161.                 }
  162.         }



  163.         switch(keyout)                                                                         //对键盘进行行扫描
  164.         {
  165.                 case 0:o4=1;o1=0;break;
  166.                 case 1:o1=1;o2=0;break;
  167.                 case 2:o2=1;o3=0;break;
  168.                 case 3:o3=1;o4=0;break;
  169.                 default:break;
  170.         }

  171.          keyout++;
  172.          keyout=keyout&0x03;                                                                  //keyout到4清0


  173.                
  174. }

  175. void ledscan()                                                                                        //键盘显示函数
  176. {

  177.     static unsigned char i = 0;  //动态扫描的索引
  178.         P0 = 0xFF;   //显示消隐
  179.         switch(i)
  180.         {
  181.         case 0:
  182.                 WEI=1;P0=weima[0];WEI=0;DUAN=1;P0=ledbuff[0];i++;DUAN=0;break;                 //显示个位
  183.         case 1:
  184.                 WEI=1;P0=weima[1];WEI=0;DUAN=1;P0=ledbuff[1];i++;DUAN=0;break;                 //显示十位
  185.         case 2:
  186.                 WEI=1;P0=weima[2];WEI=0;DUAN=1;P0=ledbuff[2];i++;DUAN=0;break;                 //显示百位
  187.         case 3:                        
  188.                 WEI=1;P0=weima[3];WEI=0;DUAN=1;P0=ledbuff[3];i++;DUAN=0;break;
  189.         case 4:                        
  190.                 WEI=1;P0=weima[4];WEI=0;DUAN=1;P0=ledbuff[4];i++;DUAN=0;break;
  191.         case 5:                        
  192.                 WEI=1;P0=weima[5];WEI=0;DUAN=1;P0=ledbuff[5];i++;DUAN=0;break;
  193.         case 6:                        
  194.                 WEI=1;P0=weima[6];WEI=0;DUAN=1;P0=ledbuff[6];i++;DUAN=0;break;
  195.         case 7:
  196.                 WEI=1;P0=weima[7];WEI=0;DUAN=1;P0=ledbuff[7];i++;DUAN=0;break;
  197.         default:i=0;break;
  198.         }
  199. }

  200. void interrupttimer0() interrupt 1                        //定时中断检测键盘,刷新数码管
  201. {



  202.         TH0=0xfc;
  203.         TL0=0x67;
  204.         keyscan();   //调用按键扫描函数
  205.         ledscan();   //调用数码管显示扫描函数






  206. }
复制代码
回复

使用道具 举报

ID:164602 发表于 2018-5-3 08:25 | 显示全部楼层
根据你所说的:可是我在按键检测的时候用了if(keysta[j]==0),相当于按键弹起的时候才显示1。

这个是没有用的。
试想:在按键的抖动时,是不是会有很多次的等于零,而你的程序却判断有零就显示,这与没有消抖是一回事嘛。

所以,我的建议是:先消抖,就是延时啊,按下看时10ms左右,松开也延时10ms左右。要先消抖,稳定后才用你的判断是否显示。

这个应该是很基础的了,你不会没有例子吧?!
给你一个:
/*******************************************************************************
* 函 数 名         : KeyDown
* 函数功能                   : 检测有按键按下并读取键值
* 输    入         : 无
* 输    出         : 无
*******************************************************************************/
void KeyDown(void)
{
        char a=0;
        GPIO_KEY=0x0f;
        if(GPIO_KEY!=0x0f)//读取按键是否按下
        {
                Delay10ms();//延时10ms进行消抖
                if(GPIO_KEY!=0x0f)//再次检测键盘是否按下
                {
                       
                        //测试列
                        GPIO_KEY=0X0F;
                        switch(GPIO_KEY)
                        {
                                case(0X07):        KeyValue=0;break;
                                case(0X0b):        KeyValue=1;break;
                                case(0X0d): KeyValue=2;break;
                                case(0X0e):        KeyValue=3;break;
                        }
                        //测试行
                        GPIO_KEY=0XF0;
                        switch(GPIO_KEY)
                        {
                                case(0X70):        KeyValue=KeyValue;break;
                                case(0Xb0):        KeyValue=KeyValue+4;break;
                                case(0Xd0): KeyValue=KeyValue+8;break;
                                case(0Xe0):        KeyValue=KeyValue+12;break;
                        }
                        while((a<50)&&(GPIO_KEY!=0xf0))         //检测按键松手检测
                        {
                                Delay10ms();
                                a++;
                        }
                }
        }
}

这个例子程序,会返回按下键的值——从而可以判断按下的是哪个键,你可以再根据返回的键值进行你程序需要的操作,比你的程序好多了,特别是消抖。
回复

使用道具 举报

ID:213173 发表于 2018-5-3 09:12 | 显示全部楼层
楼主的按键扫描程序缺少自锁语句,导致长按时重复响应,推荐一款精简的4*4按键扫描程序和按键服务程序参考。
#define value 10                                //中断周期1ms  消抖延时 10
unsigned char KeySec=0;                        //定义键值全局变量
/***********************************************************/
void keyscan()                                        //按键扫描程序(放在1ms中断中)
{
        static bit sign=0;                        //按键自锁标志
        static unsigned char count=0;//消抖计数变量                       
        unsigned char num=0;                //临时变量
        P3=0xf0;                                        //赋值P3 1111 0000
        if(P3!=0xf0)                                //检测有按键按下
        {
                count++;                                //消抖计数
                if((count>=value)&&(sign==0))
                {                       
                        sign=1;                                //按键自锁标志置1,防止长按重复响应
                        num=P3;                                //保存P3值xxxx 0000,x为0或1
                        num|=0x0f;                        //保存num按位或0x0f值xxxx 1111
                        P3=num;                                //赋值P3 xxxx 1111
                        num=P3;                                //保存P3值xxxx xxxx
                        switch(num)
                        {
                                case 0xee: KeySec= 1; break;
                                case 0xde: KeySec= 2; break;
                                case 0xbe: KeySec= 3; break;
                                case 0x7e: KeySec= 4; break;
                                case 0xed: KeySec= 5; break;
                                case 0xdd: KeySec= 6; break;
                                case 0xbd: KeySec= 7; break;
                                case 0x7d: KeySec= 8; break;
                                case 0xeb: KeySec= 9; break;
                                case 0xdb: KeySec=10; break;
                                case 0xbb: KeySec=11; break;
                                case 0x7b: KeySec=12; break;
                                case 0xe7: KeySec=13; break;
                                case 0xd7: KeySec=14; break;
                                case 0xb7: KeySec=15; break;
                                case 0x77: KeySec=16; break;
                        }
                }
        }
        else                                                //按键抬起
        {
                sign=0;                                        //按键自锁标志清0
                count=0;                                //消抖计数清0
        }
}
void key_service()                      //按键服务程序,放在主循环中
{
        switch(KeySec)
        {
                case 1:                                //事例1号键触发
                //任务1
                KeySec=0;                          //键值清零,避免重复触发
                break;                        //跳出当前程序

                case 2:                                //事例2号键触发
                        //任务2
                KeySec=0;                          //键值清零,避免重复触发
                break;                        //跳出当前程序
               
                //......
                //......

                case 16:                        //事例16号键触发
                //任务16
                KeySec=0;                          //键值清零,避免重复触发
                break;                         //跳出当前程序
        }                  
}
回复

使用道具 举报

ID:213173 发表于 2018-5-3 10:16 | 显示全部楼层
我用的两个74HC573接的8位数码管,DUAN WEI两个IO扣控制锁存器的
这是用两个74HC573接的8位共阴数码管动态显示程序
void ledscan()//键盘显示函数
{
        static unsigned char i=0;//静态变量
        P0=0x00;                                //消隐
        DUAN=1;
        DUAN=0;
        P0=weima;                //位码
        WEI=1;
        WEI=0;
        P0=ledbuff;                //段码
        DUAN=1;
        DUAN=0;
        i++;
        if(i>=8)
                i=0;
}
回复

使用道具 举报

ID:126172 发表于 2018-5-3 12:00 | 显示全部楼层
弹起判断应该放在按键执行后
回复

使用道具 举报

ID:320812 发表于 2018-5-3 22:14 | 显示全部楼层
谢谢各位大佬了,今天翘班研究这个程序,发现132.void keydriver()函数里面backup[4][4]没有定义静态,前面加个static一下就好了~唉~~~思维啊,思维啊~~板凳哥这个程序我仔细研究了下,确实比我的好多了~~拜谢
回复

使用道具 举报

ID:332416 发表于 2018-5-18 15:19 | 显示全部楼层
51单片机独立按键
#include<reg52.h>
#define uint unsigned int
#define uchar unsigned char
sbit dula=P2^6;
sbit wela=P2^7;
sbit K1=P3^4;
sbit K2=P3^5;
sbit K3=P3^6;
sbit K4=P3^7;
uchar code table[]={
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71};
uint aa;
uchar num1,num2,shi1,ge1,shi2,ge2;

void delay(uint z)
{
        uint x,y;
        for(x=z;x>0;x--)
                for(y=110;y>0;y--);
}

void display(uchar num1,uchar num2)
{
        shi1=num1/10;
        ge1=num1%10;
        shi2=num2/10;
        ge2=num2%10;
       
        dula=1;
        P0=table[shi1];
        dula=0;
        P0=0xff;
        wela=1;
        P0=0xfe;
        wela=0;
        delay(1);

        dula=1;
        P0=table[ge1];
        dula=0;
        P0=0xff;
        wela=1;
        P0=0xfd;
        wela=0;
        delay(1);

        dula=1;
        P0=table[shi2];
        dula=0;
        P0=0xff;
        wela=1;
        P0=0xfb;
        wela=0;
        delay(1);

        dula=1;
        P0=table[ge2];
        dula=0;
        P0=0xff;
        wela=1;
        P0=0xf7;
        wela=0;
        delay(1);
}

void key()
{
        if(K1==0)
        {
                delay(5);
                if(K1==0)
                {
                        TR0=0;
                        while(!K1);
                }
        }

        if(K2==0)
        {
                delay(5);
                if(K2==0)
                {
                        if(num1==0)
                        {
                                num1=60;
                        }
                        num1--;
                        while(!K2);
                }
        }

        if(K3==0)
        {
                delay(5);
                if(K3==0)
                {
                        num1++;
                        if(num1>=60)
                        {
                                num1=num1-60;
                        }
                        while(!K3);
                }
        }

        if(K4==0)
        {
                delay(5);
                if(K4==0)
                {
                        TR0=1;
                        while(!K4);
                }
        }
}

void inint()
{
        TMOD=0x01;
        TH0=(65536-50000)/256;
        TL0=(65536-50000)%256;
        EA=1;
        ET0=1;
        TR0=1;
}

void main()
{
        inint();
        while(1)
        {
                key();
                display(num1,num2);
        }
}

void zhongduan()interrupt 1
{
        TH0=(65536-50000)/256;
        TL0=(65536-50000)%256;
        aa++;
        if(aa==20)
        {
                aa=0;
                num2++;
                if(num2==60)
                {
                        num2=0;
                        num1++;
                        if(num1==60)
                                num1=0;
                }
        }
}
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表