本文主要介绍了课本第5章第3、7节数码管与矩阵式键盘接口设计的实验,本节内容更侧重对原理的理解,可能会出简答题
LED数码管的显示原理
静态显示
当LED位于静态显示时,无论多少位LED数码管都处于同时显示的状态,但1位数码管独占1个IO口
实验一
#include <reg51.h>
#define uchar unsigned char
#define seg P0
sbit button = P3^2;
uchar mode = 0;
void delayms(uchar ms) {
uchar i;
while(ms--) {
for(i = 0; i < 120; i++);
}
}
void check_button()
{
if(button == 0) {
delayms(10); // 按键消抖
if(button == 0) { // 确认按键状态
mode++;
if(mode > 3) {
mode = 1;
}
while(button == 0); // 等待按键释放
}
}
}
void seg_mode()
{
switch(mode)
{
case 0:
seg = 0x03; // 显示0
break;
case 1:
seg = 0x9F; // 显示1
break;
case 2:
seg = 0x25; // 显示2
break;
case 3:
seg = 0x0D; // 显示3
break;
}
}
void main()
{
while(1) {
check_button();
seg_mode();
}
}
动态显示
动态显示的核心是通过快速轮流显示每个位,并利用人眼的视觉暂留效应让多个数码管看起来像是同时显示各自的内容。
视觉暂留效应
- 人眼有一个“视觉暂留时间”,大约是 0.1 秒以内。当图像在短时间内变化得足够快时,人眼无法分辨出变化,而会认为它们是同时显示的。
- 动态显示利用这一效应,在不同的数码管之间快速切换,使人感到它们都同时在显示数字。
步骤与过程
-
分时点亮:
- 对于一个多位数码管,按顺序点亮每一位。例如,如果有 4 个数码管(显示 4 位数字),程序会按顺序依次点亮第 1 位、第 2 位、第 3 位、第 4 位。
- 只点亮一个数码管,并在显示完数字后迅速切换到下一个数码管。
-
快速刷新:
- 每个位显示的时间非常短,通常是几毫秒(例如每个位 2 ms)。4 个数码管全显示完一轮后,立刻开始下一轮。
- 由于切换速度快,人眼会觉得 4 个数码管是同时显示的。
void delay(uint t)
{
uchar i;
while(t--)for(i=0;i<200;i++);
}
void main(){
uchar i,j = 0x80;
while(1)
{
for(i=0;i<8;i++){
j = _crol_(j,1);
P0 = discode[i];
P2 = j;
delay(1);
}
}
位选码和段选码
矩阵式键盘的接口设计
原理:键盘矩阵中无按键按下时,行线处于高电平状态;当有按键按下时,行线电平状态将由与此行线相连的列线的电平决定。如果列线的电平为低,则行线电平为低,反之亦然。
【课本例题逻辑】假设以行扫描为例:
- 逐行拉低行线:
- 将 R1~R4 设置为输出,并逐行拉低(例如,先让 R1 为低电平,其余为高电平)。
- 逐列检测:
- 读取 C1~C4 的电平状态。如果某列检测到低电平,说明当前激活行对应的按键被按下。
- 例如,R1 为低电平,C2 为低电平,表示 R1 和 C2 的交点(按键 “2”)被按下。
- 重复扫描:
- 逐行重复上述过程,直到扫描完整个键盘矩阵。
#include <reg51.h>
#define uchar unsigned char
#define ucode unsigned code
#define SEG P0 //定义IO口
#define BUTTON P2
sbit LED = P3^7;
//延时函数
void delayms(uchar ms) {
uchar i;
while(ms--) {
for(i = 0; i < 120; i++);
}
}
//读键值函数
char getKey(void) {
uchar temp, row, column, i;
uchar ColumnCode[4] = {0xef, 0xdf, 0xbf, 0x7f}; // 列扫描
BUTTON = 0x0f; // 低四位输入状态
temp = BUTTON & 0x0f;
if (temp != 0x0f) { // 检测到按键
delayms(5); // 消抖
temp = BUTTON & 0x0f;
if (temp != 0x0f) {
switch(temp) {
case 0x07: row = 3; break;
case 0x0b: row = 2; break;
case 0x0d: row = 1; break;
case 0x0e: row = 0; break;
default: return -1; // 未识别的行
}
for (i = 0; i < 4; i++) {
BUTTON = ColumnCode[i]; // 列扫描
temp = BUTTON & 0x0f; // 读按键状态
if (temp != 0x0f) { // 检测按键
column = i;
return (row * 4 + column); // 计算按键编号0~15
}
}
}
}
return -1; // 没有按键被按下
}
void begin(void){ //数码管显示闪烁“8”
SEG = 0x00;
delayms(2000);
SEG = 0xFF;
delayms(2000);
}
void right(void){ //数码管闪烁显示“P”
SEG = 0x8C;
delayms(2000);
SEG = 0xFF;
delayms(2000);
}
void wrong(void){ //数码管闪烁显示“E”
SEG = 0x86;
delayms(2000);
SEG = 0xFF;
delayms(2000);
}
void main(void)
{
int tempkey;
LED = 1; //LED灭,表示上锁
begin();
begin();
SEG = 0xBF; //数码管显示“-”,进入待机状态
while(1)
{
tempkey = getKey();
if (tempkey == 6) { // 如果密码输入正确
LED = 0; // LED亮
right();
right();
SEG = 0xBF;
LED = 1; // 进入待机状态
}
else if (tempkey >= 0 && tempkey < 16) { // 有效按键
LED = 1; // LED灭
wrong();
wrong();
SEG = 0xBF; // 进入待机状态
} else {
SEG = 0xBF; //保持待机状态
}
}
}
按键函数逻辑
- 行扫描检测按键按下:
- 通过读取行的电平状态,确定按下按键所在的行。
- 列扫描确认具体按键:
- 逐列拉低列线,读取行的状态变化,确认按下按键所在的列。
- 按键编号计算:
- 按键编号 = 行编号 * 4 + 列编号。
- 返回按键编号:
- 返回按键的编号(0~15);若无按键按下,返回
-1
。
- 返回按键的编号(0~15);若无按键按下,返回