8051 实现八位七段数码管循环显示:从 8255 端口映射到滚动刷新逻辑

晚来风
晚来风
发布时间:2026-08-25 | 33人浏览过
原创

前言

在 8051 的入门实验里,七段数码管几乎是最常见的显示外设之一,但真正写代码时,很多人会卡在“段码怎么配、位选怎么扫、数据又是怎么滚动”的细节上。本文把一段基于 8255 的示例程序重新拆开说明,目标很明确:让 8 个共阳极七段数码管循环显示数字序列,并看清每个函数在整套显示流程里的职责,这样你后续改成别的数字或字符时也更容易下手。

在 8051 的入门实验里,七段数码管几乎是最常见的显示外设之一,但真正写代码时,很多人会卡在“段码怎么配、位选怎么扫、数据又是怎么滚动”的细节上。本文把一段基于 8255 的示例程序重新拆开说明,目标很明确:让 8 个共阳极七段数码管循环显示数字序列,并看清每个函数在整套显示流程里的职责,这样你后续改成别的数字或字符时也更容易下手。

先明确任务与硬件映射

这段程序要实现的核心效果,是在八个七段数码管上循环显示数字 12345678。这里使用的是共阳极数码管,控制上又引入了 8255 作为 I/O 扩展芯片,因此程序一开始要先把和硬件地址直接对应的端口定义出来。

8255 端口与八位数码管显示分工示意图
端口分工与显示链路用端口分工图说明 8255 在本例中的角色:哪一路输出段码,哪一路负责位选。

代码里通过 xdata 关键字,把 8255 的控制口和三个数据口映射到固定地址。这样后面给这些变量赋值,本质上就是在直接操作外设寄存器。

#include <reg52.h>  
#include <stdio.h>  
  
xdata unsigned char COM8255  _at_ 0x8006;  
xdata unsigned char PA8255   _at_ 0x8000;  
xdata unsigned char PB8255   _at_ 0x8002;  
xdata unsigned char PC8255   _at_ 0x8004;

从这几个定义可以直接看出程序后面的分工:

  • COM8255 用来设置 8255 的工作方式。
  • PA8255 负责输出段码,也就是控制数码管 a、b、c、d、e、f、g 等段是否点亮。
  • PB8255 负责位选,决定当前点亮的是 8 个数码管中的哪一个。
  • PC8255 这段示例里虽然定义了,但后续并没有实际参与显示流程。

理解这一步很重要,因为后面的显示函数其实就是围绕“段码输出 + 位选切换”两个动作展开的。

段码表和显示缓冲区分别做什么

要让数码管显示数字,程序不能直接把 1、2、3 这样的值送到端口,而是要先查表,找到对应的七段编码。为此,代码定义了 tab[] 和 buff[] 两个数组。

unsigned char tab[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8}; //0-f  
unsigned char buff[8]={1,2,3,4,5,6,7,8} ;

tab[] 的作用,是保存数字在七段数码管上的段码。这里给出的内容实际覆盖了 0 到 7 的编码值,注释里虽然写了 //0-f,但从数组长度看,当前示例并没有把 8 到 F 全部列出来。对于这篇教程的目标来说,这并不影响理解:程序就是通过 tab[buff[d]] 这种方式,把要显示的数字转换成对应段码。

buff[8] 则是显示缓冲区,可以把它理解为“当前 8 位数码管屏幕上应当出现的内容”。初始化后,它存放的就是 1,2,3,4,5,6,7,8,也就是程序启动时的初始显示序列。

这种写法的好处是,显示逻辑不需要关心“下一个要亮什么”,它只负责读取 buff[];而数据变化逻辑则只需要修改 buff[] 的内容。显示和数据更新因此被拆成了两个相对独立的部分。

循环显示为什么要靠延时、扫描和移位配合

数码管的“循环显示”不是一次性把 8 个数字同时写进去就结束了,它通常依赖动态扫描。也就是说,程序会快速轮流点亮每一位,只要刷新得足够快,人眼就会感觉 8 位数码管一直稳定亮着。

显示缓冲区滚动与动态扫描配合流程图
滚动更新与逐位扫描的配合这张图把 buff 数组左移、末位补入新数字,以及 display()。

delay():决定单次显示停留时间

先看延时函数:

主循环中显示刷新与数字注入节奏示意图
主程序刷新与更新节奏把 main() 的双层循环拆成时间节奏图,方便看清 display() 重复刷新和。
void  delay(unsigned int z)  
{  
   unsigned int x,y;  
   for(x=z;x>0;x--)  
      for(y=255;y>0;y--);  
}

这个函数使用双重循环制造软件延时,参数 z 用来控制总延时长短。它的意义并不复杂:如果没有这个停顿,位选切换太快,某一位的点亮时间过短,显示效果就不稳定;如果延时过长,又会让扫描频率变低,出现肉眼可见的闪烁。

dataFlow():把显示内容向前滚动一位

接着是数据流动函数:

void dataFlow(unsigned char c)  
{  
   unsigned char h,s;  
   h=buff[0];  
   for(s=0;s<7;s++)  
   {  
      buff[s]=buff[s+1];  
   }  
   buff[7]=c;  
}

它做的事情很直接:把 buff[] 里原有的 8 个数整体向前移动一格,然后把新传入的字符 c 放到最后一位。这样每调用一次,显示序列都会更新一次。

例如初始内容是 1 2 3 4 5 6 7 8,调用一次 dataFlow(0) 之后,就会变成 2 3 4 5 6 7 8 0。如果再继续传入后续数字,就能形成连续滚动的视觉效果。

这里的变量 h 虽然保存了 buff[0],但在当前代码中没有继续使用,因此它对最终功能没有实际影响。

display():段码输出和位选扫描一起完成

真正把数据显示到数码管上的,是 display() 函数:

void display()  
{    
  unsigned char site=0x01,d;    
  for(d=0;d<8;d++)  
  {  
     PA8255=tab[buff[d]];  
     PB8255=site;  
     delay(300);  
     site<<=1;  
  }  
}

这个函数每执行一轮,就会从第 1 位扫到第 8 位,流程可以拆成 4 步:

  1. 从 buff[d] 取出当前位要显示的数字。
  2. 用这个数字去查 tab[],得到段码后送到 PA8255。
  3. 把当前位选值 site 送到 PB8255,选中某一位数码管。
  4. 调用 delay(300) 让这一位保持一小段时间,再通过 site<<=1 切到下一位。

其中 site 初始值是 0x01,每次左移一位,正好对应 8 位数码管依次选通的过程。这就是典型的动态扫描显示思路。

主函数怎样把 12345678 变成持续循环的效果

把前面的几个函数连起来之后,主程序逻辑就比较清楚了:

void main(void)  
 {   
	 unsigned char  j;  
   COM8255=0x80;  
   while(1)  
	 {  
		 for(i=0;i<10;i++)  
		 {  
			 for(j=0;j<8;j++)  
			   display();  
			 dataFlow(i);  
		 }  
	 }  
 }

程序先通过 COM8255=0x80 初始化 8255 工作模式,然后进入无限循环,不断刷新和更新显示内容。

这里可以把两层 for 循环分开看:

  • 外层 for(i=0;i<10;i++) 决定要依次送入显示缓冲区的新数字,范围是 0 到 9。
  • 内层 for(j=0;j<8;j++) display(); 并不改变缓冲区内容,它只是重复扫描显示,让当前 8 位内容维持一段可见时间。
  • 等当前画面维持够了,再调用 dataFlow(i),把新数字压入缓冲区末尾,形成下一帧显示内容。

于是,程序的运行节奏就变成了:

先稳定显示当前 8 位内容 -> 再把新数字塞到末尾并整体左移 -> 再显示新的 8 位内容

按照这个逻辑,最初的 12345678 会逐步演变成新的序列,形成持续循环滚动的效果。

需要注意的是,这段代码按教程意图说明没有问题,但如果你准备直接编译,还要留意一个细节:代码中的 for(i=0;i<10;i++) 使用了变量 i,而当前展示的 main() 里只显式定义了 j,因此实际工程里还需要补上 i 的定义。

这段程序适合理解哪些关键点

如果你是拿它做 8051 数码管实验,这段示例最值得掌握的不是把代码背下来,而是理清 3 个层次:

  • 硬件层:PA8255 管段码,PB8255 管位选,COM8255 负责模式设置。
  • 数据层:tab[] 决定“数字怎么变成段码”,buff[] 决定“当前 8 位显示什么”。
  • 时序层:display() 负责动态扫描,dataFlow() 负责序列滚动,delay() 负责显示停留时间。

把这三层关系看明白之后,你就可以继续往下扩展,比如把初始序列从 12345678 改成别的内容,补全 tab[] 的更多编码,或者进一步加入字母、符号和更复杂的滚动方式。

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表