标题: 4X4键盘不能按意图显示 [打印本页]

作者: suiwinder    时间: 2025-8-14 22:27
标题: 4X4键盘不能按意图显示
最近在学习《例说51单片机》第三版,在第五章中学习4X4键盘的用法,程序就是按书上的程序来的,都没法显示。稍微调整了下,就只能显示0,4,8,C。本来是要显示0-E的,可其余的都显示不出来。代码和电路图如下,麻烦各位帮我看看程序哪里有问题,非常感谢。
#include <reg51.h>
#define SEG7P P0
#define KEYP P2
#define u8 unsigned char

u8 code TAB[] = { 0xc0, 0xf9, 0xa4, 0xb0, 0x99,
                  0x92, 0x82, 0xf8, 0x80, 0x98,
                  0xa0, 0x83, 0xa7, 0xa1, 0x84,
                  0x8e, 0xbf, 0x7f
                };
u8 disp = 0x7f;

u8 scan[] = { 0xef, 0xdf, 0xbf, 0x7f};

void debouncer( void )
{
    u8 i;
    for( i = 2000; i > 0; i -- );
}

void delay1ms( u8 x )
{
    u8 i, k;
    for( i = x; i > 0; i -- )
        for( k = 128; k > 0; k -- );
}

void scanner( void )
{
    u8 col, row;
    u8 rowkey, kcode;
    for( col = 0; col < 4 ; col ++ )
    {
        KEYP = scan[col];

        SEG7P = disp;
        if( KEYP != scan[ col ] )
        {
            debouncer( );
            if( KEYP != scan[ col ] )
            {
                //debouncer( );
                rowkey = ((~KEYP) & 0x0f);
                if( 0x01 == rowkey )
                    row = 0;
                else if( 0x02 == rowkey )
                    row = 1;
                else if( 0x04 == rowkey )
                    row = 2;
                else if( 0x08 == rowkey )
                    row = 3;
                kcode = (4* col + row);
                disp = TAB[ kcode ];
                SEG7P = disp;
            }
        }
    }
    delay1ms(4);
}

void main( void )
{
    while( 1 )
    {
        scanner( );
    }
}


捕获51.JPG (111.02 KB, 下载次数: 0)

捕获51.JPG

捕获510.JPG (112.13 KB, 下载次数: 0)

捕获510.JPG

捕获514.JPG (111.23 KB, 下载次数: 0)

捕获514.JPG

作者: 人中狼    时间: 2025-8-15 08:53
去掉上拉电阻试试
作者: joyb    时间: 2025-8-15 09:18
(1) 单独测试数码管

// 测试数码管是否能显示0-F
void test_display() {
    u8 i;
    for (i = 0; i < 16; i++) {
        SEG7P = TAB[i];
        delay1ms(500);
    }
}
如果数码管能正常显示0-F,说明问题在键盘扫描部分。

(2) 串口调试键值

// 通过串口打印键值(需初始化UART)
void send_keycode(u8 kcode) {
    SBUF = kcode + '0';  // 发送ASCII码
    while (!TI);
    TI = 0;
}
这样可以确认键盘扫描是否正确检测到所有按键。
作者: WL0123    时间: 2025-8-15 09:54
你这写法不但不易理解,更容易发生逻辑错误。改如下写法易懂。


  1. #include <reg51.h>
  2. #define SEG7P P0
  3. #define KEYP P2
  4. #define u8 unsigned char

  5. u8 code TAB[] = { 0xc0, 0xf9, 0xa4, 0xb0, 0x99,
  6.                   0x92, 0x82, 0xf8, 0x80, 0x98,
  7.                   0xa0, 0x83, 0xa7, 0xa1, 0x84,
  8.                   0x8e, 0xbf, 0x7f, 0xbf
  9.                 };
  10. u8 kcode=16;
  11. void delay1ms( u8 x )
  12. {
  13.     u8 i, k;
  14.     for( i = x; i > 0; i -- )
  15.         for( k = 128; k > 0; k -- );
  16. }

  17. void scanner( void )
  18. {
  19.         static bit sign=0;                        //位变量
  20.         static u8 count=0;//计数变量

  21.         KEYP=0xf0;                                        //赋值P2 1111 0000
  22.         if(KEYP!=0xf0)                                //检测有按键按下
  23.         {
  24.                 if(++count>=20 && sign==0)//
  25.                 {                       
  26.                         sign=1;                        //按键自锁标志置1
  27.                         switch(KEYP)
  28.                         {
  29.                                 case(0Xe0):kcode = 0;break;
  30.                                 case(0Xd0):kcode = 1;break;
  31.                                 case(0Xb0):kcode = 2;break;
  32.                                 case(0X70):kcode = 3;break;
  33.                         }
  34.                         KEYP=0x0f;                        //赋值P2 0000 1111
  35.                         switch(KEYP)
  36.                         {
  37.                                 case(0X0e):kcode+= 0;break;
  38.                                 case(0X0d):kcode+= 4;break;
  39.                                 case(0X0b):kcode+= 8;break;
  40.                                 case(0X07):kcode+=12;break;
  41.                         }
  42.                 }
  43.         }
  44.         else                                                //键抬起
  45.         {
  46.                 sign=0;                                //按键自锁标志清0
  47.                 count=0;                                //消抖计数清0
  48.         }
  49.         SEG7P = TAB[kcode];
  50.         delay1ms(1);
  51. }

  52. void main( void )
  53. {
  54.     while( 1 )
  55.     {
  56.         scanner( );
  57.     }
  58. }
复制代码




作者: lzts88    时间: 2025-8-15 09:56
void debouncer( void )
{
    u8 i;
    for( i = 2000; i > 0; i -- );
}

u8 i; i最大只能到255,不可能=2000,应该是i = 200;
作者: zch5200    时间: 2025-8-15 10:18
学习单片机 以我做项目的经验。我个人建议学习I串口通讯,软硬件IIC,SPI,PWM,单口按键等等的功能实现,中断也很重要。最不要学的就是矩阵键盘,这个根本用不上,真正要用到很多按键的时候有现成的芯片比如TM1668数码管驱动和矩阵键盘。不会有IO口给你做矩阵。
作者: xiaobendan001    时间: 2025-8-15 12:54
zch5200 发表于 2025-8-15 10:18
学习单片机 以我做项目的经验。我个人建议学习I串口通讯,软硬件IIC,SPI,PWM,单口按键等等的功能实现, ...

有道理
作者: skilldog    时间: 2025-8-15 13:55
zch5200 发表于 2025-8-15 10:18
学习单片机 以我做项目的经验。我个人建议学习I串口通讯,软硬件IIC,SPI,PWM,单口按键等等的功能实现, ...

约5年前我还真有客户要求用这种4x4的键盘。
作者: suiwinder    时间: 2025-8-15 16:42
WL0123 发表于 2025-8-15 09:54
你这写法不但不易理解,更容易发生逻辑错误。改如下写法易懂。

其他算法很多,就是想知道这个算法为啥不行。
感觉应该是for循环里面列扫描那段,但是感觉也没问题,就是很奇怪为啥只能识别这几个。
作者: suiwinder    时间: 2025-8-15 16:43
joyb 发表于 2025-8-15 09:18
(1) 单独测试数码管

// 测试数码管是否能显示0-F

串口调试怎么调?还没接触到这部分内容。
作者: 人中狼    时间: 2025-8-15 19:59
应该是rowkey = ((~KEYP) & 0x0f);有问题,用一个变量保存KEYP,然后在运算试试
作者: suiwinder    时间: 2025-8-15 21:00
人中狼 发表于 2025-8-15 19:59
应该是rowkey = ((~KEYP) & 0x0f);有问题,用一个变量保存KEYP,然后在运算试试

没用,看来不对。
作者: skilldog    时间: 2025-8-15 21:23
試試看

#include <reg51.h>
#define SEG7P P0
#define KEYP   P2
#define u8 unsigned char

u8 code TAB[] = {
    0xc0, 0xf9, 0xa4, 0xb0, 0x99,
    0x92, 0x82, 0xf8, 0x80, 0x98,
    0xa0, 0x83, 0xa7, 0xa1, 0x84,
    0x8e, 0xbf, 0x7f
};
u8 disp = 0x7f;

// 高 4 位掃描碼,低 4 位全為 1(輸入高阻)
u8 scan[] = { 0xE0 | 0x0F, 0xD0 | 0x0F, 0xB0 | 0x0F, 0x70 | 0x0F };

void debouncer(void)
{
    u8 i;
    for (i = 2000; i > 0; i--);
}

void delay1ms(u8 x)
{
    u8 i, k;
    for (i = x; i > 0; i--)
        for (k = 128; k > 0; k--);
}

void scanner(void)
{
    u8 col, row;
    u8 rowkey, kcode;

    for (col = 0; col < 4; col++)
    {
        // 輸出掃描信號,高 4 位決定列,低 4 位保持輸入(全 1)
        KEYP = scan[col];
        SEG7P = disp;

        // 讀取低 4 位(行)狀態
        rowkey = KEYP & 0x0F;

        if (rowkey != 0x0F) // 有按鍵被壓下
        {
            debouncer();
            rowkey = KEYP & 0x0F; // 再讀一次確認

            if (rowkey != 0x0F)
            {
                // 根據哪一位為 0 判斷行號
                if (rowkey == 0x0E) row = 0;
                else if (rowkey == 0x0D) row = 1;
                else if (rowkey == 0x0B) row = 2;
                else if (rowkey == 0x07) row = 3;

                kcode = (4 * col + row);
                disp = TAB[kcode];
                SEG7P = disp;
            }
        }
    }
    delay1ms(4);
}

void main(void)
{
    while (1)
    {
        scanner();
    }
}
作者: skilldog    时间: 2025-8-15 21:34
或試試比較簡單的改法

void scanner( void )
{
    u8 col, row;
    u8 rowkey, kcode;
    for( col = 0; col < 4 ; col ++ )
    {
        KEYP = scan[col]; // 輸出到 P2.0 ~ P2.3

        SEG7P = disp;
        if( KEYP != scan[ col ] )
        {
            debouncer( );
            if( KEYP != scan[ col ] )
            {
                rowkey = ((~KEYP) & 0xf0);
                if( 0x10 == rowkey )        
                    row = 0;
                else if( 0x20 == rowkey )   
                    row = 1;
                else if( 0x40 == rowkey )   
                    row = 2;
                else if( 0x80 == rowkey )   
                    row = 3;
                kcode = (4* col + row);
                disp = TAB[ kcode ];
                SEG7P = disp;
            }
        }
    }
    delay1ms(4);
}
作者: lkc8210    时间: 2025-8-15 22:00
如果试过明明代码是正确的,别人可以成功仿真
但在你的电脑上就是怎么弄都不行
就代表Proteus有Bughttp://www.51hei.com/bbs/dpj-236577-1.html


作者: WL0123    时间: 2025-8-16 07:30
suiwinder 发表于 2025-8-15 16:42
其他算法很多,就是想知道这个算法为啥不行。
感觉应该是for循环里面列扫描那段,但是感觉也没问题,就 ...

用楼主的程序不做任何修改在Proteus 8.9 仿真,没有发现任何异常。加电显示“.”,按矩阵键盘依次显示0~F无误。可能是14楼所言的Proteus版本有Bug。


作者: suiwinder    时间: 2025-8-16 09:44
WL0123 发表于 2025-8-16 07:30
用楼主的程序不做任何修改在Proteus 8.9 仿真,没有发现任何异常。加电显示“.”,按矩阵键盘依次显示0~F ...

包含的那个补丁?也用的8.17 sP5版本的.
作者: suiwinder    时间: 2025-8-16 09:47
skilldog 发表于 2025-8-15 21:34
或試試比較簡單的改法

void scanner( void )

明天看看这个办法ok不
作者: 房子    时间: 2025-8-16 10:32
从你提供的代码和现象(仅显示0、4、8、C)来看,问题核心在于行检测逻辑与键盘硬件接线的匹配错误,导致只能识别第0行的4个按键(对应0、4、8、C),其余行按键无法被检测。
作者: 房子    时间: 2025-8-16 10:49
你的代码中,rowkey是通过((~KEYP) & 0x0f)获取的,这意味着你默认键盘的4行对应P2口的低4位(P2.0~P2.3) ,但行判断逻辑却写反了:   - 代码中认为 rowkey=0x01(P2.0为低)对应第0行,但实际硬件中,按下不同行的按键,rowkey的“1”所在位是从高位到低位排列(比如第0行对应P2.3,rowkey=0x08;第3行对应P2.0,rowkey=0x01)。  - 这就导致只有第0行(rowkey=0x08)的按键能被错误识别为row=0,其余行的rowkey(0x04、0x02、0x01)无法匹配任何row,最终只能显示第0行的4个按键(0、4、8、C)。




欢迎光临 (http://www.51hei.com/bbs/) Powered by Discuz! X3.1