找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 169|回复: 17
打印 上一主题 下一主题
收起左侧

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
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:384109 发表于 2025-8-15 08:53 | 只看该作者
去掉上拉电阻试试
回复

使用道具 举报

板凳
ID:1064915 发表于 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;
}
这样可以确认键盘扫描是否正确检测到所有按键。
回复

使用道具 举报

地板
ID:1133081 发表于 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. }
复制代码



回复

使用道具 举报

5#
ID:341045 发表于 2025-8-15 09:56 | 只看该作者
void debouncer( void )
{
    u8 i;
    for( i = 2000; i > 0; i -- );
}

u8 i; i最大只能到255,不可能=2000,应该是i = 200;
回复

使用道具 举报

6#
ID:1004920 发表于 2025-8-15 10:18 | 只看该作者
学习单片机 以我做项目的经验。我个人建议学习I串口通讯,软硬件IIC,SPI,PWM,单口按键等等的功能实现,中断也很重要。最不要学的就是矩阵键盘,这个根本用不上,真正要用到很多按键的时候有现成的芯片比如TM1668数码管驱动和矩阵键盘。不会有IO口给你做矩阵。
回复

使用道具 举报

7#
ID:1109793 发表于 2025-8-15 12:54 | 只看该作者
zch5200 发表于 2025-8-15 10:18
学习单片机 以我做项目的经验。我个人建议学习I串口通讯,软硬件IIC,SPI,PWM,单口按键等等的功能实现, ...

有道理
回复

使用道具 举报

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

约5年前我还真有客户要求用这种4x4的键盘。
回复

使用道具 举报

9#
ID:409878 发表于 2025-8-15 16:42 | 只看该作者
WL0123 发表于 2025-8-15 09:54
你这写法不但不易理解,更容易发生逻辑错误。改如下写法易懂。

其他算法很多,就是想知道这个算法为啥不行。
感觉应该是for循环里面列扫描那段,但是感觉也没问题,就是很奇怪为啥只能识别这几个。
回复

使用道具 举报

10#
ID:409878 发表于 2025-8-15 16:43 | 只看该作者
joyb 发表于 2025-8-15 09:18
(1) 单独测试数码管

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

串口调试怎么调?还没接触到这部分内容。
回复

使用道具 举报

11#
ID:384109 发表于 2025-8-15 19:59 | 只看该作者
应该是rowkey = ((~KEYP) & 0x0f);有问题,用一个变量保存KEYP,然后在运算试试
回复

使用道具 举报

12#
ID:409878 发表于 2025-8-15 21:00 | 只看该作者
人中狼 发表于 2025-8-15 19:59
应该是rowkey = ((~KEYP) & 0x0f);有问题,用一个变量保存KEYP,然后在运算试试

没用,看来不对。
回复

使用道具 举报

13#
ID:1157301 发表于 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();
    }
}
回复

使用道具 举报

14#
ID:1157301 发表于 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);
}
回复

使用道具 举报

15#
ID:161164 发表于 2025-8-15 22:00 | 只看该作者
如果试过明明代码是正确的,别人可以成功仿真
但在你的电脑上就是怎么弄都不行
就代表Proteus有Bughttp://www.51hei.com/bbs/dpj-236577-1.html

回复

使用道具 举报

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

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

回复

使用道具 举报

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

包含的那个补丁?也用的8.17 sP5版本的.
回复

使用道具 举报

18#
ID:409878 发表于 2025-8-16 09:47 | 只看该作者
skilldog 发表于 2025-8-15 21:34
或試試比較簡單的改法

void scanner( void )

明天看看这个办法ok不
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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