浮云落笔

浮云落笔

首页
瞬间
反馈
浮云落笔

浮云落笔

首页 瞬间 反馈
  1. 首页
  2. 8051
  3. 矩阵按钮控制数码显示

矩阵按钮控制数码显示

  • 8051
  • 发布于 2026-06-03
  • 2 次阅读
flor
flor

矩阵按键 + 数码管倒计时系统 — 使用手册

芯片: STC8A8K64D4 @ 24MHz | 函数库: ESD_STC8 V3.0


一、全局变量

变量名 类型 说明
gear uchar 挡位,范围 1~9,默认 1
time_set uchar 倒计时预设秒数,范围 1~99,默认 30
time_disp uchar 数码管当前显示的值(设置态 = time_set,运行态 = time_left)
running uchar 系统状态:0=设置模式,1=倒计时运行中
time_left uchar 倒计时剩余秒数,运行态时逐秒减 1
ms_cnt uint 毫秒累加器,每 1ms 加 1,满 1000 进 1 秒

变量关系图:

设置态 (running=0)              运行态 (running=1)
┌─────────────────┐            ┌─────────────────┐
│ time_set = 30   │──K5启动→  │ time_left = 30   │
│                 │            │   ↓ 每秒减1     │
│ time_disp = 30  │            │ time_left = 29   │
│                 │            │   ↓              │
│ 按键可调        │            │ time_disp = 29   │
│ gear 1~9       │            │   ↓              │
│ time_set 1~99  │            │ ...到0           │
│                 │←─到0自动回─│ running变回0     │
└─────────────────┘            └─────────────────┘

二、自定义函数

函数1:Scan_Keys()

原型:    uchar Scan_Keys(void)
参数:    无
返回:    0 = 无按键
        1 = K1 按下(挡位+)
        2 = K2 按下(挡位-)
        3 = K3 按下(时间+)
        4 = K4 按下(时间-)
        5 = K5 按下(启动)

原理:矩阵扫描法,2 行 × 3 列,分两轮检测

  • 第①轮:P16=0(选中行1)→ 读 P14 查 K1,读 P15 查 K2
  • 第②轮:P17=0(选中行2)→ 读 P14 查 K3,读 P15 查 K4,读 P35 查 K5

每次只激活一行,被激活行的"强 0"才能把列线拉低;另一行输出"强 1",列线不受影响。

坐标系理解:

  • Y 轴(行扫描):P16 / P17 轮流输出强 0
  • X 轴(列检测):P14 / P15 / P35 默认弱 1,被拉低 = 按键按下
第1步:Y=P16=0 → 查 X 轴 → 找到第1行的按键(K1 或 K2)
第2步:Y=P17=0 → 查 X 轴 → 找到第2行的按键(K3 或 K4 或 K5)

注意:如果同时按下多个键,函数只返回后检测到的那一个。


函数2:TM0_isr()

原型:    void TM0_isr(void)
调用者:  isr.c 中的 TM0_I() interrupt 1(每 1ms 自动调用)
参数:    无
返回:    无

功能 A:数码管动态扫描(333Hz 无闪烁刷新)

流程:

  1. 消隐:关闭所有位选(P36=P37=P33=0),关闭所有段选(P2.0~P2.7 全写 1 → 全灭)
  2. 轮流亮一位:
    • pos=0:送 gear 的段码到 P2 → 开 P36(第 1 位亮)
    • pos=1:送 time_disp 十位的段码 → 开 P37(第 2 位亮)
    • pos=2:送 time_disp 个位的段码 → 开 P33(第 3 位亮)
  3. pos 循环:0 → 1 → 2 → 0 ...

时间轴:

t=0ms   消隐 → 亮第1位(挡位)      pos=1
t=1ms   消隐 → 亮第2位(秒十位)    pos=2
t=2ms   消隐 → 亮第3位(秒个位)    pos=0
t=3ms   循环 ...

每位亮 1ms,3 位一轮 = 3ms,刷新率 333Hz

功能 B:倒计时(仅 running==1 时执行)

  1. ms_cnt 每 1ms 加 1
  2. ms_cnt 满 1000 → 经过了 1 秒
  3. time_left 减 1
  4. time_left 到 0 → running 置 0,LED 灭(Out_IO(10,1))

三、用到的库函数(ESD_STC8 V3.0)

库函数 说明
GPIO_init_allpin(mode) 初始化所有 IO,mode: 0=准双向 1=推挽 2=高阻 3=开漏
GPIO_init_8pin(port, mode) 初始化整组 IO,port: 0=P0 2=P2...
GPIO_init_pin(pin, mode) 初始化单个 IO,pin: 36=P3.6 14=P1.4...
Out_IO(pin, val) 设置 IO 输出,val: 0=低电平 1=高电平
Get_IO(pin) 读取 IO 输入,返回 bit(0 或 1)
PIT_init_ms(n, t) 启动定时器中断,n: 04,t: 132ms(24MHz)
Delay_X_mS(x) 毫秒延时,x: 1~1000
Delay_X_uS(x) 微秒延时,x: 1~1000

引脚编号规则:两位数字,十位 = 端口号,个位 = 引脚号

14 = P1^4 / 16 = P1^6 / 20 = P2^0 / 27 = P2^7 33 = P3^3 / 35 = P3^5 / 36 = P3^6 / 37 = P3^7


四、段码表

P2 口位:.7=DP .6=G .5=F .4=E .3=D .2=C .1=B .0=A

共阳规则:0 = 亮,1 = 灭

数字 十六进制 亮的段
0 0xC0 A B C D E F
1 0xF9 B C
2 0xA4 A B D E G
3 0xB0 A B C D G
4 0x99 B C F G
5 0x92 A C D F G
6 0x82 A C D E F G
7 0xF8 A B C
8 0x80 A B C D E F G(全部)
9 0x90 A B C D F G

五、硬件接线速查表

按键矩阵(2 行 × 3 列)

P14(列1) P15(列2) P35(列3)
P16(行1) K1 挡位+ K2 挡位- (空)
P17(行2) K3 时间+ K4 时间- K5 启动

省线技巧:K1、K2 朝向 P16 那端焊在一起,共用一根飞线到 P16;K3、K4、K5 朝向 P17 那端焊在一起,共用一根飞线到 P17。

电阻:不需要外部电阻(代码用内部上拉)。


数码管 5641BS(共阳极,12 脚)

段码(各串 100Ω 电阻):

P2 脚 数码管脚 段名
P2.0 脚11 A
P2.1 脚7 B
P2.2 脚4 C
P2.3 脚2 D
P2.4 脚1 E
P2.5 脚10 F
P2.6 脚5 G
P2.7 脚3 DP

位选:

单片机脚 数码管脚 说明
P3.6 脚12 第1位 = 挡位显示
P3.7 脚9 第2位 = 秒十位
P3.3 脚8 第3位 = 秒个位
— 脚6 悬空(第4位不用)

LED 指示灯

VCC(5V) → [1KΩ电阻] → LED正极(长脚) → LED负极(短脚) → P1.0
  • P1.0 = 高电平(5V):LED 两端无压差 → 灭
  • P1.0 = 低电平(0V):电流从 VCC 流向 P1.0 → 亮

六、程序执行流程

主循环(后台)

main()
  │
  ├→ GPIO 配置:全部准双向 → P2/位选/行扫/LED 改推挽
  ├→ LED 初始灭:Out_IO(10,1)
  ├→ 定时器启动:PIT_init_ms(0,1)  每 1ms 进中断
  │
  └→ while(1):
       │
       ├→ ① time_disp = running ? time_left : time_set
       │     更新数码管数据源
       │
       ├→ ② key = Scan_Keys()  扫描按键
       │     if (有键) {
       │       Delay_15ms 防抖
       │       再次确认
       │       if (!locked) {
       │         locked = 1
       │         if (!running) {   // 设置态才处理
       │           key=1 → gear++
       │           key=2 → gear--
       │           key=3 → time_set++
       │           key=4 → time_set--
       │           key=5 → 启动倒计时
       │         }
       │       }
       │     } else {
       │       locked = 0   // 松手解锁
       │     }
       │
       └──→ 循环

定时器中断(前台,每 1ms)

TM0_isr()
  │
  ├→ 消隐:关位选 + 关段选
  │
  ├→ 轮流亮位:
  │   pos=0: Seg[gear]       → P2口 → P36=1
  │   pos=1: Seg[time_disp十位] → P2口 → P37=1
  │   pos=2: Seg[time_disp个位] → P2口 → P33=1
  │
  └→ 倒计时(running==1 时):
       ms_cnt++
       ms_cnt 到 1000 → time_left--
       time_left 到 0 → running=0, LED灭

主循环和中断通过全局变量 gear / time_disp / running / time_left 通信,彼此独立运行。


七、按键防抖机制

问题:机械按键按下/松开的瞬间,金属弹片反复弹跳,产生一串极短的通→断→通→断脉冲(持续约 5~15ms)。

时间轴:

按下瞬间  ├── 抖动区(~15ms)──┤  稳定按住
电平: 1   │ 1 0 1 0 1 0 1 0 1  │  0 0 0 0 0
          ↑                    ↑
     第1次读数              第2次读数
     (可能是1)              (稳定是0)
     
     两次一致 → 确认有效 ✓

防长按(locked 标志位)

循环 locked 动作
第1次 0 执行功能 → locked=1
第2次 1 跳过(按住不放)
第N次 1 跳过
松手 — key=0 → locked=0
再按 0 可以再次执行

效果:按一次只触发一次,按住不放不会连续触发。


八、常见问题排查

Q:数码管不亮?

  1. 检查共阳脚是否接对(脚 12、9、8)
  2. 段码电阻是否每根都接了
  3. 万用表测 P36/P37/P33 有无轮流出现高电平
  4. 确认 5641BS 是共阳型(不是共阴)

Q:数码管显示乱码?

段码脚位和代码不匹配。用 5V → 100Ω 逐个碰段脚,确认 A/B/C/D/E/F/G/DP 对应哪个脚,调整 P2 接线。

Q:按一下数字跳了好几次?

按键接触不良或抖动没消干净。

  1. 杜邦线插紧
  2. Delay_X_mS(15) 可以改大到 20~30

Q:按了按键没反应?

  1. 检查按键矩阵接线是否正确(特别注意共用的行线)
  2. 万用表测按下去是否导通
  3. 检查 running 是否为 0(运行态按键被屏蔽)

Q:倒计时不准?

确认 config.h 中的 MAIN_Fosc 和 ISP 下载软件中设置的主频一致。

Q:编译有 MULTIPLE CALL 警告?

正常,不影响运行。Out_IO 在主循环和 ISR 中都被调用,Keil 提醒可能重入,但 Out_IO 内部无静态变量,实际不会出错。

Q:LED 不亮?

  1. 检查正负极是否接反(长脚 → 电阻 → VCC,短脚 → P1.0)
  2. 检查 running=1 时 P1.0 是否为低电平

目录
湘ICP备2025147565号-1
gongan beian 湘公网安备43102602000213号