找回密码
 立即注册

QQ登录

只需一步,快速开始

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

关于矩阵键盘代码优化问题

[复制链接]
跳转到指定楼层
楼主
我是 51 单片机新手,在学习 [bilibili 51教程[6-1] 矩阵键盘] (https://www.bilibili.com/video/B ... eopod.episodes&p=15) 时发现这段代码有点冗余,可以优化一下吗?

原视频代码类似以下:

```c
# ifndef MATRIX_H
# define MATRIX_H

# include "8051.h"
# include "delay.h"

unsigned char matrix_keydown()
{
  /*
  unsigned char i;
  for (i = 3; i > 0; --i)
  {
    P1 = 0xff;
    // 我好烦啊,这个P1到底怎么回事
    P1 = P1 ^ (0x01 << i + 4);

    if (P1_7 == 0) {delay(3); while (P1_7 == 0); delay(3); return 1 + (3 - i);}
    if (P1_6 == 0) {delay(3); while (P1_6 == 0); delay(3); return 5 + (3 - i);}
    if (P1_5 == 0) {delay(3); while (P1_5 == 0); delay(3); return 9 + (3 - i);}
    if (P1_4 == 0) {delay(3); while (P1_4 == 0); delay(3); return 13 + (3 - i);}
  }
  */
  P1 = 0xff;
  P1_3 = 0;
  if (P1_7 == 0) {delay(3); while (P1_7 == 0); delay(3); return 1;}
  if (P1_6 == 0) {delay(3); while (P1_6 == 0); delay(3); return 5;}
  if (P1_5 == 0) {delay(3); while (P1_5 == 0); delay(3); return 9;}
  if (P1_4 == 0) {delay(3); while (P1_4 == 0); delay(3); return 13;}

  P1 = 0xff;
  P1_2 = 0;
  if (P1_7 == 0) {delay(3); while (P1_7 == 0); delay(3); return 2;}
  if (P1_6 == 0) {delay(3); while (P1_6 == 0); delay(3); return 6;}
  if (P1_5 == 0) {delay(3); while (P1_5 == 0); delay(3); return 10;}
  if (P1_4 == 0) {delay(3); while (P1_4 == 0); delay(3); return 14;}

  P1 = 0xff;
  P1_1 = 0;
  if (P1_7 == 0) {delay(3); while (P1_7 == 0); delay(3); return 3;}
  if (P1_6 == 0) {delay(3); while (P1_6 == 0); delay(3); return 7;}
  if (P1_5 == 0) {delay(3); while (P1_5 == 0); delay(3); return 11;}
  if (P1_4 == 0) {delay(3); while (P1_4 == 0); delay(3); return 15;}

  P1 = 0xff;
  P1_0 = 0;
  if (P1_7 == 0) {delay(3); while (P1_7 == 0); delay(3); return 4;}
  if (P1_6 == 0) {delay(3); while (P1_6 == 0); delay(3); return 8;}
  if (P1_5 == 0) {delay(3); while (P1_5 == 0); delay(3); return 12;}
  if (P1_4 == 0) {delay(3); while (P1_4 == 0); delay(3); return 16;}

  return 0;
}

# endif

```
可以看出代码量太长了。

我想进行优化类似以下:
```C
# ifndef MATRIX_H
# define MATRIX_H

# include "delay.h"

unsigned char math_key()
{
  // 简单的算法在哪里?
  // 不能将 P1 预设为 0x0f
  // 先暴力过了
  unsigned char S2, S1;
  S2 = P1 & 0x0f;
  S2 = P1 - 1 << 2 | 1;
  S1 = P1 & 0xf0 >> 4;
  return S2 + S1;
  // return ((P1 & 0x0f) - 1 << 2 | 1) + (P1 & 0xf0 >> 4);
}

unsigned char matrix_keydown()
{
  unsigned char key_number = 0;
  // 当 P1 被按下时用 math_key() 算出按键位置分别对应 1-16
  if (P1 != 0xff)
  {
    delay(3);
    key_number = math_key();
  }
  return key_number;
}

# endif
```
可惜失败了。。。
自己试错了许久,有点似懂非懂原因,觉得这个失败有点知识的缺陷,可以有版友帮助解答一下吗?
可以指点一下优化的方向吗?


分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏2 分享淘帖 顶1 踩
回复

使用道具 举报

沙发
ID:1109793 发表于 2024-11-29 16:14 | 只看该作者
不是有个反转法吗?
回复

使用道具 举报

板凳
ID:161164 发表于 2024-11-29 16:35 | 只看该作者
  1.                         switch(Keypad)
  2.                         {
  3.                                 case 0x0E:KeyVal=1;break;
  4.                                 case 0x0D:KeyVal=2;break;
  5.                                 case 0x0B:KeyVal=3;break;
  6.                                 case 0x07:KeyVal=4;break;
  7.                         }
  8.                         Keypad = 0xF0;
  9.                         _nop_();_nop_();_nop_();_nop_();_nop_();_nop_();
  10.                         switch(Keypad)
  11.                         {
  12.                                 case 0xE0:KeyVal+=0;break;
  13.                                 case 0xD0:KeyVal+=4;break;
  14.                                 case 0xB0:KeyVal+=8;break;
  15.                                 case 0x70:KeyVal+=12;break;
  16.                         }
复制代码
回复

使用道具 举报

地板
ID:1133081 发表于 2024-11-29 21:41 | 只看该作者
给你一个简单的4*4矩阵按键示例参考


  1. #include <reg51.h>
  2. #define uint unsigned int
  3. #define uchar unsigned char
  4. uchar  code table[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f};
  5. uchar KeyValue=0;//键值变量


  6. void delayms(uint k)
  7. {
  8.         uint i,j;
  9.         for(i=k;i>0;i--)
  10.                 for(j=115;j>0;j--);
  11. }

  12. void key_scan()                                        //按键扫描程序
  13. {
  14.         static bit sign=0;                        //位变量
  15.         static unsigned int count=0;//计数变量
  16.         P3=0xf0;                                        //赋值P3 1111 0000
  17.         if(P3!=0xf0)                                //检测有按键按下
  18.         {
  19.                 if(++count>=20 && sign==0)//计数消抖
  20.                 {                       
  21.                         sign=1;                        //按键自锁标志置1
  22.                         switch(P3)
  23.                         {
  24.                                 case(0Xe0):KeyValue = 1;break;
  25.                                 case(0Xd0):KeyValue = 2;break;
  26.                                 case(0Xb0):KeyValue = 3;break;
  27.                                 case(0X70):KeyValue = 4;break;
  28.                         }
  29.                         P3=0x0f;                        //赋值P3 0000 1111
  30.                         switch(P3)
  31.                         {
  32.                                 case(0X0e):KeyValue+= 0;break;
  33.                                 case(0X0d):KeyValue+= 4;break;
  34.                                 case(0X0b):KeyValue+= 8;break;
  35.                                 case(0X07):KeyValue+=12;break;
  36.                         }
  37.                 }
  38.         }
  39.         else                                                //键抬起
  40.         {
  41.                 sign=0;                                //按键自锁标志清0
  42.                 count=0;                                //消抖计数清0
  43.         }
  44. }

  45. void display()                                        //数码管显示程序
  46. {
  47.         static uchar i=0;
  48.         P0=0x00;
  49.         P2=~(0x01<<i);
  50.         if(i)P0=table[KeyValue%10];
  51.         else P0=table[KeyValue/10];
  52.         i=++i%2;
  53. }

  54. void main()                                                //主函数
  55. {
  56.         while(1)
  57.         {
  58.                 key_scan();                                //按键扫描程序
  59.                 display();                                //数码管显示键值1~16
  60.                 delayms(1);                                //延时控制主循环周期约1ms
  61.         }
  62. }
复制代码



回复

使用道具 举报

5#
ID:583948 发表于 2024-12-2 09:15 | 只看该作者
#include <stdint.h>

// 假设你有一个适当的 delay 函数
void delay(uint16_t ms);

// 假设 P1 和 P1_x (x = 0, 1, 2, 3, 4, 5, 6, 7) 已经被定义并连接到相应的硬件
#define ROW_COUNT 4
#define COL_COUNT 4

// 矩阵键盘扫描函数
unsigned char matrix_keydown()
{
    uint8_t row, col;
    static const uint8_t rowPins[] = {P1_0, P1_1, P1_2, P1_3};
    static const uint8_t colPins[] = {P1_4, P1_5, P1_6, P1_7};
    static const uint8_t keyMap[ROW_COUNT][COL_COUNT] = {
        {1, 5, 9, 13},
        {2, 6, 10, 14},
        {3, 7, 11, 15},
        {4, 8, 12, 16}
    };

    for (row = 0; row < ROW_COUNT; ++row)
    {
        P1 = 0xFF; // 设置所有列为高电平
        rowPins[row] = 0; // 将当前行设为低电平

        for (col = 0; col < COL_COUNT; ++col)
        {
            if (colPins[col] == 0) // 检查是否有列被拉低
            {
                delay(3); // 消抖
                while (colPins[col] == 0); // 等待按键释放
                delay(3); // 消抖
                return keyMap[row][col]; // 返回按键值
            }
        }

        rowPins[row] = 1; // 恢复当前行为高电平,为下一行做准备
    }

    return 0; // 没有按键被按下
}
可以尝试一下这个方法,没验证过,
回复

使用道具 举报

6#
ID:446156 发表于 2024-12-5 13:47 | 只看该作者
代码量长不是问题,问题是代码里while阻塞,这样你的单片机再跑其他任务就非常受影响
回复

使用道具 举报

7#
ID:1138698 发表于 2024-12-5 18:30 | 只看该作者
#ifndef MATRIX_H
#define MATRIX_H

#include "8051.h"
#include "delay.h"

unsigned char matrix_keydown() {
    unsigned char row, col;
    unsigned char key_code = 0;

    for (row = 0; row < 4; ++row) {
        P1 = 0xff; // Set all rows high
        P1_3 = (row & 0x01) ? 1 : 0; // Select row
        P1_2 = (row & 0x02) ? 1 : 0;
        P1_1 = (row & 0x04) ? 1 : 0;
        P1_0 = (row & 0x08) ? 1 : 0;

        for (col = 0; col < 4; ++col) {
            if ((P1 >> (col + 4)) & 0x01) { // Check column
                delay(3); // Debounce
                if ((P1 >> (col + 4)) & 0x01) { // Double check
                    key_code = 1 + row * 4 + col; // Calculate key code
                    while ((P1 >> (col + 4)) & 0x01); // Wait for key release
                    delay(3); // Debounce
                    return key_code;
                }
            }
        }
    }

    return 0; // No key pressed
}

#endif
这样会好一点,你参考一下
回复

使用道具 举报

8#
ID:752878 发表于 2024-12-6 10:05 | 只看该作者
为什么不放在定时器里去做按键扫描,阻塞的程序浪费资源啊,既然是优化那么实时行也是程序要考虑的点啊,不太赞同阻塞的做法
回复

使用道具 举报

9#
ID:446156 发表于 2024-12-7 09:44 | 只看该作者
空白名 发表于 2024-12-6 10:05
为什么不放在定时器里去做按键扫描,阻塞的程序浪费资源啊,既然是优化那么实时行也是程序要考虑的点啊,不 ...

小伙子我看好你
回复

使用道具 举报

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

本版积分规则

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

Powered by 单片机教程网

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