8088单板机监控程序解读(四)
分析监控程序的按键扫描代码
8088kit用户手册资源-CSDN下载
监控程序的按键扫描代码位于SCAN1函数(源码第 1107-1180 行),采用动态扫描方式同时驱动 6 位数码管显示和检测 32 个按键。
硬件基础
根据手册和原理图(第 34 页),按键和数码管的硬件连接:
| 端口 | 地址 | 功能 |
|---|---|---|
PORT1 | 0001H | 数码管位选(选择哪一位点亮),同时用于键盘列扫描输出 |
PORT2 | 0002H | 数码管段选(显示什么字符),输出到 7 段数码管 |
PORT0 | 0100H | 键盘行输入(读取哪一行按键被按下),bit7 也用于 UART RXD |
键盘矩阵:4 列 × 8 行 = 32 个按键(但实际只用了部分,对应 KEYTAB 表)
SCAN1 函数完整源码分析
1107 C616 B300 MOV BL,0 ; BL = 列计数器(0-3) 1108 C618 B701 MOV BH,1 ; BH = 列扫描掩码,初始 0000 0001B 1109 C61A B4FF MOV AH,-1 ; AH = 返回值,-1 表示无按键 1110 C61C B108 MOV CL,8 ; CL = 总列数(4列)×2?实际是 8 次循环?外层循环:扫描 4 列
1113 C61E KCOL: 1114 C61E 8AC7 MOV AL,BH ; 取当前列掩码 1115 C620 F6D0 NOT AL ; 取反(因为列选可能是低电平有效) 1116 C622 E601 OUT PORT1,AL ; 输出到位选端口,选中当前列关键:这里复用了数码管的位选信号作为键盘的列扫描输出。当某一位为低电平时,该列被选中,按键的行线可以读出。
显示当前列的数码管内容
1120 C624 8A04 MOV AL,[SI] ; 从显示缓冲区读取该位的段码 1121 C626 E602 OUT PORT2,AL ; 输出到段选端口,显示字符 1123 C628 247F AND AL,7FH ; 屏蔽小数点位(位7) 1124 C62A 3C06 CMP AL,6 ; 检查是否是 '-' 符号(段码 06H) 1125 C62C 7504 JNZ SKIP10 1126 C62E B503 MOV CH,3 ; 如果是 '-',短延时 3 1127 C630 EB02 JMP DELAY1 1128 C632 B50A SKIP10: MOV CH,10 ; 否则延时 10 1129 C634 FECD DELAY1: DEC CH 1130 C636 75FC JNZ DELAY1这段是显示延时,让数码管点亮一段时间,保证亮度。不同字符延时不同(-符号笔画少,亮度高,所以延时短)。
关闭显示(消隐)
1137 C638 B000 MOV AL,0 ; 熄灭所有段 1138 C63A E602 OUT PORT2,AL关闭显示是为了避免在按键扫描时产生鬼影。
等待显示稳定
1141 C63C B51E MOV CH,30 ; 延时 30 1142 C63E FECD DELAY3: DEC CH 1143 C640 75FC JNZ DELAY3内层循环:扫描当前列的 8 行
1148 C642 B506 MOV CH,6 ; 扫描 6 行(实际只用了 6 行?) 1149 C644 BA0001 MOV DX,PORT0 ; 读行输入端口 1150 C647 EC IN AL,DX 1151 C648 DD08 KROW: RCR AL,1 ; 循环右移一位,检查最低位 1152 C64A 7202 JC NOKEY ; 如果该位为 1(无按键),跳转 1153 C64C 8AE3 MOV AH,BL ; 有按键!保存当前列号到 AH 1154 C64E FEC3 NOKEY: INC BL ; BL++(行号递增) 1155 C650 FECD DEC CH ; 行计数器减 1 1156 C652 75F4 JNZ KROW ; 继续扫描下一行按键编码:最终AH= 列号(0-3)×8 + 行号(0-7),范围 0-31。
切换到下一列
1162 C654 DD07 ROL BH,1 ; 列掩码左移,切换到下一列(0001→0010→0100→1000) 1163 C656 46 INC SI ; 显示缓冲区指针后移,指向下一位 1164 C657 FEC9 DEC CL ; 列计数器减 1 1165 C659 75C3 JNZ KCOL ; 4 列未扫完,继续检查串口数据
1170 C65B BA0001 MOV DX,PORT0 1171 C65E EC IN AL,DX 1172 C65F A880 TEST AL,80H ; 测试 RXD 位(bit7) 1173 C661 7502 JNZ no_serial ; 为高(空闲),无串口数据 1174 C663 B424 MOV AH,36 ; 有串口数据,返回特殊扫描码 36 1175 C665 90 no_serial: NOP 1176 C666 C3 RET返回:
AH = 0-35:有按键,返回按键扫描码AH = 36:有串口数据AH = 255 (FFH):无按键
扫描时序图
循环 4 次(每列一次): ┌─────────────────────────────────────────────────────────┐ │ 选中列 N(输出到 PORT1) │ │ 显示该列对应位的数码管(输出段码到 PORT2) │ │ 延时(保持显示) │ │ 关闭显示(PORT2=0) │ │ 延时(消隐) │ │ 循环 6-8 次(每行一次): │ │ 读取 PORT0 │ │ 测试该行是否有按键按下 │ └─────────────────────────────────────────────────────────┘ 切换到下一列,SI++(显示缓冲区指针后移)按键扫描码到内部码的转换
扫描得到AH(0-35)后,主程序通过查表KEYTAB转换为内部功能码:
243 COD4 BE69CA MOV SI,KEYTAB ; KEYTAB 表地址 244 COD7 8AC4 MOV AL,AH ; 扫描码 245 COD9 B400 MOV AH,0 246 COD8 03F0 ADD SI,AX ; SI = KEYTAB + 扫描码 247 CODD 2E SEG CS 248 CODE 8A04 MOV AL,[SI] ; 读取内部码KEYTAB 表(源码第 1985-2022 行)定义了每个按键对应的功能码,例如:
扫描码 0 →
10H(IP 键)扫描码 1 →
11H(REG 键)扫描码 2 →
12H(DATA 键)扫描码 3 →
13H(ADDR 键)...
设计特点总结
| 特点 | 说明 |
|---|---|
| 时分复用 | 数码管显示和键盘扫描共用同一套硬件,通过时间片分离 |
| 动态扫描 | 6 位数码管轮流点亮,利用视觉暂留效果 |
| 矩阵扫描 | 4 列 × 8 行矩阵键盘,节省 I/O 引脚 |
| 消隐处理 | 切换列时先关闭显示,避免鬼影 |
| 串口检测 | 键盘扫描同时也检测 UART 数据,实现“伪中断”式串口接收 |
| 返回值设计 | AH=255 无按键,AH=0-35 有按键,AH=36 有串口数据 |
这是一个非常经典的嵌入式系统键盘/显示扫描设计,充分利用了 8088 的 I/O 端口,硬件简洁但功能完整。
