位置:首页 > C > 8051 单片机矩阵键盘控制实战:4x4 扫描、8255 配置与 LED 显示

8051 单片机矩阵键盘控制实战:4x4 扫描、8255 配置与 LED 显示

时间:2026-08-25  |  作者:骑光打字机  |  阅读:0

目录

  1. 8255 与键盘、LED 的端口分配怎么定
  2. 延时函数和逐行扫描为什么缺一不可
  3. 键值扫描出来后,LED 是怎样显示的
  4. 主程序如何把扫描和显示连成实时交互
  5. 这个案例适合拿来理解哪些关键点

前言

在 8051 单片机实验里,矩阵键盘和 LED 显示经常一起出现,但很多教程只贴代码,没有把 8255 的端口分工和扫描逻辑讲透。本文按端口初始化、逐行扫描、键值显示三个环节重新梳理这套实现,帮助你看懂 `COM8255=0x81` 为什么这样配、`keyscan` 怎样完成译码,以及出现显示异常时该先查哪几处。

在 8051 单片机实验里,矩阵键盘和 LED 显示往往是最先碰到的一组输入输出组合,但不少资料只给出代码,没把扫描过程和端口分工讲清楚。本文按“硬件端口如何分配、程序如何逐行扫描、键值又是怎样送到 LED”这条主线重写这个案例,帮助你判断这套方案为什么能工作,以及后续该从哪几处入手做调试。

8255 与键盘、LED 的端口分配怎么定

这个案例的目标很明确:使用 8051 微控制器配合 8255 并行接口芯片,完成一个 4x4 矩阵键盘的扫描,并把识别到的键值实时显示到 LED 显示器上。

8255 端口分工与矩阵键盘、LED 连接关系图
端口分工示意用端口分工图把 8255 在本案例中的输入输出角色讲清楚,方便读者先建立整体硬件映射。

程序首先定义了 8255 的几个关键寄存器地址,其中控制寄存器位于 0x8006,A、B、C 三个端口分别位于 0x8000、0x8002、0x8004。这部分地址映射决定了后面每一次读写究竟落到哪个外设端口上。

#include 
#include 
xdata unsigned char COM8255   _at_  0x8006;
xdata unsigned char PA8255   _at_  0x8000;
xdata unsigned char PB8255   _at_  0x8002;
xdata unsigned char PC8255   _at_  0x8004;

unsigned char tab[16]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e};
unsigned char dat,col,rol;

其中,tab[16] 是数码管显示编码表,保存了 16 个按键值对应的段码。也就是说,矩阵键盘扫描得到的结果不是直接显示,而是先作为索引,再去查这张表,最后输出到显示端口。

初始化阶段最关键的一句是:

COM8255=0x81;         //8255 PA、PB输出,PC7--PC4输出,PC3--PC0输入

这条配置决定了整套方案的工作分工:

  • PA 作为输出口,用来送数码管段码;
  • PB 作为输出口,用来配合 LED 显示控制;
  • PC 高四位作为输出,用于矩阵键盘逐行扫描;
  • PC 低四位作为输入,用于读取列线状态。

这也是矩阵键盘最典型的接法之一:一侧负责“轮流拉低”行线,另一侧负责“读取变化”列线。只要有按键按下,当前被扫描到的那一行就会和某一列形成导通,从而把列输入拉成低电平。

延时函数和逐行扫描为什么缺一不可

矩阵键盘扫描不是一次读端口就能稳定完成的。原因有两个:一是机械按键本身存在抖动,二是行线切换之后需要一点点时间让电平稳定下来。所以程序单独准备了两个延时函数,分别承担不同任务。

矩阵键盘逐行扫描与键值计算流程图
键盘扫描与译码流程把逐行拉低、读取列线、匹配键值的过程拆成步骤图,比直接看 `switch` 更容易理解。
void  delay20ms(unsigned char  t)
{
   unsigned char a,b;
   a=0xff;
   b=t;
   while(b)
   { while(a--);
      b--;
   }
}
void delay(unsigned char m)
{
   unsigned char n;
   while(m)
   {
      for(n=110;n>0;n--);
	 m--;
   }
}

delay20ms 用于较长时间保持显示,delay 用于较短的扫描等待和简单去抖。它们本质上都是循环延时,真实时长会受到单片机时钟频率影响,因此在不同实验板上,显示稳定性和响应速度可能还需要重新校准。

真正的核心在 keyscan 函数。它采用的是逐行扫描法:先让 PC 口高四位中的某一位输出低电平,其余保持高电平,再读取低四位列线状态;之后把这个“低电平窗口”左移,继续扫描下一行,直到四行全部检查完毕。

unsigned char  keyscan(void)
{
   unsigned char i,t,k,m;
   m=0xef;           //扫描初值。   PC4输出0,即扫描PC4线
   for(i=0;i<4;i++)
   {
   PC8255=m;     //输出行线(高四位) 
   delay(1);
   t=PC8255;     //读取列线(低四位)
   t|=0xf0;
   switch(t)
   {
      case 0xfe: k=4*i;
	         break;
      case 0xfd: k=4*i+1;
	         break;
      case 0xfb: k=4*i+2;
	         break;
      case 0xf7: k=4*i+3;
	         break;
    
   }
   m=m<<1;       //“0”向左移动,扫描下一行
   m|=0x01;
 }
 return(k);
}

这里有几个细节值得特别注意:

扫描初值为什么是 0xef

0xef 的二进制形式是 1110 1111,也就是 PC4 为 0,其余位为 1。由于 PC 高四位承担行扫描任务,这就意味着第一次先扫描第一行。

为什么读取后还要执行 t|=0xf0

因为程序只关心低四位列输入,高四位原本是扫描输出位。执行 t|=0xf0 后,高四位被统一置 1,这样后面的 switch 就只需要针对列线低电平组合做判断,匹配会更直接。

主循环中键盘扫描到 LED 显示的数据流关系图
主循环与显示数据流用数据流图说明 `main`、`keyscan`、`display` 和 `tab`。

键值是怎么计算出来的

当读到 0xfe、0xfd、0xfb、0xf7 时,分别表示当前扫描行上的第 1、2、3、4 列被按下。程序因此按 4*i 到 4*i+3 的方式给出键值索引。这样一来,4x4 键盘的 16 个按键就被映射成了 0 到 15。

从实现思路上看,这段代码完成的是“行列定位”而不是字符解释。它先得到一个键位编号,再交给显示部分去处理,因此后续无论你想显示数字、十六进制字符,还是改成控制命令,入口都比较清晰。

键值扫描出来后,LED 是怎样显示的

得到键值以后,程序会进入 display 函数,把这个编号转换成实际的数码管段码输出。

void  display(unsigned char ch)
{
   PB8255=0x1;
  
   PA8255=tab[ch];
   delay20ms(100);
  
}

这段逻辑不复杂,但职责很明确:

  • 先通过 PB8255=0x1; 使能或选通 LED 显示;
  • 再把 tab[ch] 对应的段码送到 PA 口;
  • 最后通过 delay20ms(100) 保持一段显示时间。

因为 tab 表里已经预先放好了 16 个编码,所以只要扫描结果 ch 落在 0 到 15 之间,数码管就能显示出对应内容。这也是为什么前面扫描函数必须返回一个稳定、连续的键值索引,而不是零散的端口状态。

如果你在仿真时发现按键已经识别,但 LED 显示不对,通常就要优先检查两件事:一是 tab 编码表是否与实际数码管类型匹配,二是 PA、PB 端口在电路中的连接方向是否与程序假设一致。

主程序如何把扫描和显示连成实时交互

主函数部分实际上就是把前面的几块逻辑串起来:先完成 8255 初始化,再不断轮询键盘,最后把结果显示出来。

void main(void)
 { 
    COM8255=0x81;         //8255 PA、PB输出,PC7--PC4输出,PC3--PC0输入
   
   // Write your code here
   while (1)
   {     
      dat=keyscan();
      display(dat);
   }
}

这里的执行流程可以概括为三步:

  1. 写入 COM8255=0x81,确定 8255 各端口输入输出方向;
  2. 在死循环里不断调用 keyscan(),轮询矩阵键盘;
  3. 把返回的 dat 交给 display(dat),实时刷新 LED。

这种写法的优点是结构简单,适合实验、课程设计和基础功能验证。它没有中断机制,也没有更复杂的状态管理,因此理解门槛低,调试时可以直接从“端口配置是否正确、扫描值是否稳定、显示编码是否匹配”三条线逐项排查。

当然,这份程序也保留了很明显的教学式特征。比如,按键去抖处理比较基础,没有看到无按键状态的明确返回分支;如果多个按键同时按下,现有 switch 结构也没有专门处理冲突输入。但对于“8051 + 8255 + 4x4 矩阵键盘 + LED 显示”这个入门场景来说,它已经完整展示了从硬件扫描到显示输出的基本链路。

这个案例适合拿来理解哪些关键点

把整个程序串起来看,这个案例最适合用来理解三件事:第一,8255 如何扩展 8051 的并行 I/O;第二,矩阵键盘怎样通过逐行拉低、逐列读取完成定位;第三,扫描结果如何通过查表方式转换成 LED 可显示的数据。

如果你接下来要继续扩展这个实验,可以优先考虑优化去抖、增加无键按下判断,或者把显示从单个键值拓展到更多位数码管。等这几步都跑通之后,再回头看这份基础程序,就更容易分清哪些是核心逻辑,哪些只是为了演示而做的简化处理。

免责声明:文中图文均来自网络,如有侵权请联系删除,心愿游戏发布此文仅为传递信息,不代表心愿游戏认同其观点或证实其描述。

相关文章

更多

精选合集

更多

大家都在玩

热门话题

大家都在看

更多