开关(按下 = 0)
C 源码(高亮当前执行行)
void main() { unsigned char LED_data[11]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00}; int i=0; while(1){ if(qd_kg==0){ if(fx_kg==0){ ++i; if(i>9999){ i=0; } } else{ --i; if(i<0){ i=9999; } } } if(cls_kg==0){ i=0; } P1=~LED_data[i/1000%10]; P3=~LED_data[i/100%10]; P2=~LED_data[i/10%10]; P0=~LED_data[i%10]; if(sd_kg==0){ DelayMS(100); } else{ DelayMS(300); } } }
端口电平 · 电路原理(实时同步,数字看上方数码管)
P1千位0x--→0x--
P3百位0x--→0x--
P2十位0x--→0x--
P0个位0x--→0x--
0300已暂停
教学面板
主循环流程图
上方运行区每走一步,这里就点亮对应节点和分支。让学生先看流程图、再对照源码,讲清「先判断再动作、延时只是空转」。
入口void main() 开始
循环while(1) —— 不断重复
判断qd_kg == 0 ?
(启动按下?)
(启动按下?)
是→ 计数
否→ 保持当前值
判断(仅计数时)fx_kg == 0 ?
(方向?)
(方向?)
是→ ++i 加计数
否→ --i 减计数
动作++i / --i (i 超 9999 或 负 → 回绕 0 / 9999)
判断cls_kg == 0 ?
(清零?)
(清零?)
是→ i = 0
否→ 跳过
动作送显:P1=~LED_data[i/1000%10] … P0=~LED_data[i%10]
判断sd_kg == 0 ?
(快速?)
(快速?)
是→ 延时 100ms
否→ 延时 300ms
动作DelayMS(...) —— 软件延时,CPU 空转等待
回到── 回到 while(1),再来一遍 ──
入口/循环
判断(菱形)
动作(矩形)
想一想:延时期间 CPU 在忙什么?
把速度切到 100ms 再切到 300ms,观察计数的快慢变化。
DelayMS 是"软件延时":让 CPU 原地执行一堆空指令、空转等待,什么正事都不做,只是拖慢循环节奏。所以速度挡只是改了延时常量,计数逻辑没变。
数码管段码探秘
点一个数字,看它的段码、二进制位和哪几段会亮,再理解为什么端口要取反。
0x3f
P1 = 0xC0
段码 0x3f = 二进制 011 1111,点亮 a b c d e f 六段,组成数字 "0"。
数组 LED_data 解密:为什么这么写?
unsigned char LED_data[11] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x00};
- 作用:一张"查表表"(查找表)。把 0~9 每个数字要点亮的段码提前存好,程序直接
LED_data[数字]一下查出段码去点亮数码管,不用每次现场算。这叫查表法。 - 为什么 11 个元素:下标 0~9 正好对应数字 0~9;第 11 个(下标 10)=
0x00,表示“逻辑熄灭”。送到共阳端口前仍要取反,实际输出是0xFF。 - 为什么是 unsigned char:段码
0x3f~0x00都是 8 位以内的小数(≤0xff),一个字节就装得下,用 char 省内存;unsigned保证是正数、没有负值。 - 查表思路:输入下标 → 输出段码。对应代码
P0 = ~LED_data[i%10],i 的个位是多少,就查第几个段码。
提示:上面"点一个数字"其实就是在查这张表——你点几,就显示 LED_data[几] 对应的段码。
测一测:你理解数组了吗?
硬件接线:先区分“逻辑演示”与“可落地方案”
重要:本课当前代码采用 P1/P3/P2/P0 分别直接驱动四位显示,会占用标准 8051 的全部 32 个 GPIO,因此启动、方向、清零、速度四个开关只是课堂逻辑输入,没有剩余引脚可直连。下方第一张图只表示代码与端口的对应关系,不是可直接搭建的完整电路。标准 8051 只有 P0~P3,没有 P4。
| 代码变量 | 端口 | 含义 |
|---|---|---|
| i/1000%10 | P1 | 千位段码 |
| i/100%10 | P3 | 百位段码 |
| i/10%10 | P2 | 十位段码 |
| i%10 | P0 | 个位段码 |
| qd/fx/cls/sd_kg | 课件输入 | 逻辑变量,不直连显示 |
真实 8051 的 P0 为开漏结构,输出高电平通常需要上拉;正式硬件还应加入限流电阻、晶振和复位电路。
查看可落地动态扫描参考方案
| 功能 | 参考端口 |
|---|---|
| a~g、dp 段码 | P0.0~P0.7 |
| 四位公共端选择 | P2.0~P2.3 |
| 四个输入按键 | P3.0~P3.3 |
| 晶振 | 12 MHz |
此方案为后续 Proteus 实操建议,不会自动替换当前讲课代码。采用动态扫描后需要按位轮流点亮,并重写显示函数。
课后实操包:源码、HEX、Proteus 工程、端口表与故障排查正在整理。为避免提供无效文件,本版本暂不放置下载链接。
课堂问答
共 6 道选择题(点选即反馈)+ 2 道开放思考题(先想再揭晓)。答对计入得分。
开放思考题
思考题 1
为什么计数器到 9999 后又 ++ 会回到 0,而不是显示 10000?
四位数码管最多只能表示 0–9999。代码用 if(i>9999){ i=0; } 主动回绕,避免超范围;反向也同理(i<0 → 9999)。这是处理越界的一种约定,做题时要注意是"回绕"还是"截断到上限"。
思考题 2
真实硬件里,四位数码管通常是一位一位轮流扫描点亮(动态扫描)。想一想:这段代码是一次性并行点亮四位,还是轮流点亮?为什么?
这段代码是把四位分别接到 P1/P3/P2/P0 四个端口,一次送显、四位数码管同时点亮(并行)。真实硬件为了省引脚、省功耗,常让 4 个位轮流点亮、靠人眼视觉暂留"看起来都在亮"——这就是"动态扫描"。本题是并行接法,属于理解层面的差异。
代码闯关 —— 先把样例补全,再换成自己的学号,最后加分优化
左边选关、选小关并看提示;右边写代码、看行号报错。第一关跟着提示补全样例;第二关换成学号在 00XX~XX00 计数;第三关加消隐和闪烁。填完点「检查本步」,最后「运行我的代码」看上方数码管。
你的程序(可直接编辑,左侧行号对照报错行)
尚未检查
未运行
运行后去点上方「启动 / 方向 / 清零 / 速度」四个开关,观察你的代码如何响应;回讲课模式点「停止」或切回「自动 / 单步」。