尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AT89C51与74LS164移位寄存器驱动数码管动态扫描

AT89C51与74LS164移位寄存器驱动数码管动态扫描 简介这是一份面向单片机与电子初学者的Proteus仿真工程以AT89C51单片机作为控制核心驱动74LS164移位寄存器完成多位数码管动态显示电路设计与验证核心展示了用少量引脚扩展多个显示输出的典型应用方式有助于理解串行数据转并行输出的控制原理。压缩包共四十七个文件约一百六十九千字节内部同时提供程序源代码、编译生成的十六进制烧录文件、仿真电路原理图工程文件以及调试辅助配置覆盖从代码编写到虚拟仿真的完整流程目录结构清晰便于按需查阅。当前已有一千二百八十六人学习下载说明该案例受到相关学习者广泛关注。通过阅读源码与打开仿真工程可以直观掌握移位寄存器工作时序、数码管段码编码、动态扫描刷新频率、并行输出控制以及常见连接错误的排查思路无论用于课程设计还是课外自学都有较强参考价值。1. 数码管显示点不够用AT89C51 为什么选 74LS164一个四位数码管用常规接法最少要占 12 个 I/O8 根段线加 4 根位线。AT89C51 本身有 32 个引脚表面看并不紧张可一旦设计里还要接按键、蜂鸣器、继电器或传感器读数I/O 就开始捉襟见肘。这个项目标题的核心动作是用 P1 口的两根线模拟串行时钟把 74LS164 变成 8 个并行输出段码全交给它位选扫描仍由单片机直管。Proteus 里 20 分钟能搭完电路C 程序只有两个基础函数——发送一个字节、点亮一个位。它适合正在学 51、想把「每位数码管占 8 引脚」改成「两根线扩展出 8 引脚」的读者也适合在动态扫描里被 I/O 复用拖住的工程问题。看懂 74LS164 的移位过程后面所有串行显示程序都是同一套路。2. 74LS164 的移位时序与 AT89C51 的 send_byte 实现2.1 74LS164 引脚怎么看数据脚、时钟脚和级联输出74LS164 是 8 位串入并出移位寄存器常被叫成「串转并」。它没有输入锁存数据在 CP 脚每个上升沿从 DS 脚装进内部最低位原来 8 位数据整体向高位移一格。8 个时钟上升沿之后Q0 到 Q7 的电平组合就变成刚才串行送入的 8 位数据。MR 是异步清零脚低电平会把所有输出清 0正常显示时直接接 VCC。引脚方向作用DS输入串行数据输入也叫 A/B 数据端CP输入时钟端上升沿触发移位Q0输出并行输出第 1 位Q1~Q6输出并行输出第 2~7 位Q7输出并行输出第 8 位也用作级联到下一片 DSMR输入清零端低电平有效显示时接 VCCAT89C51 没有硬件 SPI 外设但移位寄存器只认时钟和电平所以用两根普通 I/O 口「敲」时序即可。关键是发送端要保证数据在上升沿之前已经放在 DS 上否则 74LS164 锁进的是上一拍的旧值显示会错位。2.2 上升沿移位的 C 模拟发送代码与参数说明用 Keil C51 写一个 send_byte 函数逻辑是先摆数据、再给上升沿、最后移动数据位。#include reg52.h sbit DATA_PIN P1^0; // 接 74LS164 的 DS sbit CLK_PIN P1^1; // 接 74LS164 的 CP void send_byte(unsigned char dat) { unsigned char i; for(i 0; i 8; i) { if(dat 0x80) // 先取出最高位 { DATA_PIN 1; } else { DATA_PIN 0; } CLK_PIN 0; // 时钟从低到高产生上升沿 CLK_PIN 1; dat 1; // 下一位准备移动到最高位 } }这段代码里有两个容易写错的地方。一是 CLK 必须先置低再置高不能只写高电平有些例程把 CLK 直接翻转结果首尾多发半个脉冲段码整体移一位。二是dat 0x80判断后要显式赋 0 或 1虽然 Keil 里直接写DATA_PIN dat 0x80也能编译但 sbit 只认 0 和非 0遇到 0x80 这种值行为不够明确统一写成 if-else 更稳妥。函数本身不关心段码含义只负责把 8 位二进制从最高位到最低位推出去。2.3 共阴共阳段码表的位序映射关系发送代码只是搬运真正决定数码管显示什么数字的是段码表。Proteus 里常见共阴共阳两种器件选错后要么全亮要么显示反码。段码表的 bit0 对应 74LS164 的 Q0如果 Q0 接到数码管的 a 段那么 bit0 就对应 a 段依次类推到 dp。显示数字共阴段码 0~9共阳段码 0~900x3F0xC010x060xF920x5B0xA430x4F0xB040x660x9950x6D0x9260x7D0x8270x070xF880x7F0x8090x6F0x90匹配关系很容易反过来记忆共阴段码 0x3F 表示 a~g 中 a、b、c、d、e、f 点亮也就是数字 0共阳段码是它的按位取反所以 0xC0 恰好是 0x3F 取反。如果硬件上把 Q0 接到了 dp 而不是 a 段段码表就要做位反转这是设计接线时第一个要确认的约定。建议在 Proteus 里画完连线后先发一个 0x01 验证 Q0 连到哪个引脚再决定段码表方向。3. Proteus 原理图搭建AT89C51、74LS164 与共阴数码管的接线步骤3.1 元件查找关键字与新建原理图流程Proteus 8 系列里元件库关键字写法比较稳定主控直接搜 AT89C51寄存器搜 74LS164 或 74HC164数码管搜 7SEG 后从候选列表里选共阴或共阳型号。共阴可以选名字带 COM-CATHODE 的共阳选 COM-ANODE注意不要只看外形必须看属性里的 Common Cathode 标识。晶振用 CRYSTAL复位电路用 CAP-ELEC 和 RES限流电阻用 220Ω 到 470Ω。Proteus 元件搜索关键字数量用途AT89C51AT89C511主控制器74LS16474LS1641~4串转并扩展七段数码管7SEG-COM-CATHODE1~4显示单元晶振CRYSTAL112MHz 时钟电解/瓷片电容CAP, CAP-ELEC各 2复位与晶振电阻RES8限流与上拉新建工程时选 Default 模板即可芯片不需要额外添加仿真模型。Proteus 与 Keil 联调时要把编译生成的 .hex 文件在芯片属性里指定路径但单独仿真时可以直接在源文件里写完整 C 程序用 Proteus 自带的 VSM 源文件编译运行两种方式任选。工程命名建议用英文避免 Proteus 的旧版编译器解析中文路径出错。3.2 最小系统与 74LS164 的连线规范AT89C51 的最小系统在 Proteus 里可以简化但晶振和复位不能省。XTAL1 与 XTAL2 之间接 12MHz 晶振两只 33pF 瓷片电容分别接地。RST 引脚接 10kΩ 下拉电阻到地并通过 10μF 电解电容接到 VCC上电瞬间产生一个高电平复位脉冲。在 Proteus 8 中不接复位电路也能跑起来但换一块真实板子就会异常所以仿真时就按真实规范画。74LS164 的连接顺序建议从芯片左侧开始MR 接 VCCDS 接 P1.0CP 接 P1.1。注意 MR 不是悬空脚在仿真里悬空会被看成高阻显示随机闪烁。Q0 到 Q7 分别经 220Ω 电阻接到共阴数码管的 a 到 dp公共端接地。如果手头只有共阳数码管公共端要接 VCC并且段码表换成第 2 章里的共阳列。P0 口在这里不要用。AT89C51 的 P0 是开漏输出内部没有上拉电阻必须外接 10kΩ 排阻才能输出高电平。P1 口内部有上拉直接驱动 DS 和 CP 就足够这也是项目一律把 P1.0 和 P1.1 作为串行口的原因。P2 口留作位选扫描四位显示时占用 P2.0 到 P2.3。3.3 仿真前的自检程序与常见连线坑连线完成后先别急着写完整显示逻辑用一个自检函数确认三件事时钟能产生上升沿、DS 电平能进入芯片、Q0~Q7 与数码管的位序一致。void segment_selftest(void) { unsigned char pattern[3] {0x01, 0x80, 0xFF}; unsigned char i; for(i 0; i 3; i) { send_byte(pattern[i]); delay_ms(500); } }0x01 让 Q0 输出高0x80 让 Q7 输出高0xFF 让所有段全亮。运行后看数码管只有 a 段亮说明 Q0 接 a亮的是 dp 说明接反了接线或段码方向。如果发 0x01 时数码管反而全灭而发 0x80 时亮一个段说明数据位序没问题只是段码表或连线位序对应关系需要反转。Proteus 里数字元件默认不接 VCC 和 GND 也能仿真这个特性会掩盖供电错误所以从仿真转硬件时一定要回到原理图查一遍电源网络。另一个常见坑是电阻阻值过小220Ω 在 5V 下每段电流约 13mA数码管点亮度已经偏高如果仿真里看到段发白说明限流电阻没接或者接成了直通。0.36 寸小尺寸数码管可以换 470Ω 到 1kΩ显示亮度仍然足够但驱动芯片的发热明显下降。4. C 程序源码逐段拆解段码表、发送函数与多位数码管显示4.1 完整工程结构与头文件定义一块四位数码管显示 2024 的完整程序中引脚定义、段码表、延时函数、发送函数和显示函数是五个必备模块。Keil C51 工程里必须包含 reg52.h它定义了 AT89C51 的寄存器地址比如 P1 和 P2 的专用功能寄存器。#include reg52.h #define MAX_DIGITS 4 sbit SEG_DIN P1^0; sbit SEG_CLK P1^1; sbit DIG1 P2^0; sbit DIG2 P2^1; sbit DIG3 P2^2; sbit DIG4 P2^3; unsigned char code seg_table[10] { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F };sbit 是 Keil 的扩展关键字把 P1^0 这种位地址重新命名为易读的管脚名。code 关键字表示段码表放程序存储区AT89C51 只有 128 字节片内 RAM如果做 16 位显示或需要保存大量常量不写 code 会很快耗尽数据空间。段码表的索引正好对应数字 0 到 9显示数字 5 时直接取 seg_table[5]。4.2 发送函数与位选扫描的协作顺序动态扫描时位选和段码的配合顺序直接决定会不会有拖影。修正后的 display_digit 函数先熄灭所有位选再发送段码最后点亮目标位。这样 74LS164 的输出在无位选状态下完成更新不会把上一位置的显示内容带到新位置。void delay_ms(unsigned int ms) { unsigned int i, j; for(i 0; i ms; i) for(j 0; j 120; j); } void send_byte(unsigned char dat) { unsigned char i; for(i 0; i 8; i) { SEG_DIN (dat 0x80) ? 1 : 0; SEG_CLK 0; SEG_CLK 1; dat 1; } } void display_digit(unsigned char pos, unsigned char num) { DIG1 DIG2 DIG3 DIG4 0; // 先关所有位选 send_byte(seg_table[num]); // 段码进入 74LS164 switch(pos) { case 0: DIG1 1; break; case 1: DIG2 1; break; case 2: DIG3 1; break; case 3: DIG4 1; break; default: break; } delay_ms(2); // 单位置停留 2ms }关键在第一个操作先把四个位选全部置 0再发段码。如果没有这一步上一遍循环点亮的是第 3 位而 send_byte 执行到一半时 74LS164 输出已经变成第 4 位的笔画第 3 位会在极短时间内以错误笔画亮一下表现为数字边缘发虚。把顺序改成「熄灭、发送、点亮」后发送过程对显示无感。主程序里只要循环调用 display_digit 即可。void main(void) { unsigned char digit[4] {2, 0, 2, 4}; while(1) { display_digit(0, digit[0]); display_digit(1, digit[1]); display_digit(2, digit[2]); display_digit(3, digit[3]); } }每位停留 2ms一轮扫描 8ms刷新率约 120Hz肉眼看不出闪烁。如果把 delay 改成 20ms刷新率降到 12Hz数码管的亮度会严重下降目测可以捕捉到跳动。4.3 多片 74LS164 级联与发送顺序倒置四位数码管还有一种接法不单独给每位一组数据线而是把四片 74LS164 串起来CLK 全部并联到 P1.1第一片 DS 接 P1.0前一片的 Q7 接后一片的 DS。这样两根线仍能控制四位段码但发送顺序是反的。移位寄存器一级一级往后传最先进入第一片的 8 位数据最终会停在离单片机最远的那片所以要先发最低位数字的段码。void send_multiple_bytes(unsigned char *buf, unsigned char len) { signed char i; unsigned char j; for(i len - 1; i 0; i--) { for(j 0; j 8; j) { SEG_DIN (buf[i] 0x80) ? 1 : 0; SEG_CLK 0; SEG_CLK 1; buf[i] 1; } } }参数里的 buf 是四个段码组成的数组len 是 4。循环从 len-1 开始先把最右边那位的段码推进链路最后发最左边那位。全部 32 个脉冲结束后四片 74LS164 的输出一次性更新。这个接法的缺点是段码不能单独更新任何一位变化都会重发全部数据优点是 IO 占用保持两根线适合显示位数继续扩展到 6 位或 8 位。级联后应把每位停留时间从 2ms 提到 5ms因为发送 32 位数据本身要占用几十微秒且四片输出同时切换瞬间对电源有冲击Proteus 里不明显硬件上要加 100nF 去耦电容。5. 仿真排错手表逻辑分析仪查时序与级联多位数码管的收尾技巧5.1 逻辑分析仪验证 8 个时钟脉冲与数据对齐送数不显示时先不要怀疑程序先查波形。Proteus 左侧工具栏选择 LOGIC ANALYSER把它接到 P1.0 和 P1.1 上运行后暂停放大波形。正常的发送行为应能看到两路信号DS 在 CLK 每个上升沿之前已经变成目标电平每个字节出现 8 个完整的 CLK 脉冲。如果 CLK 上升沿数量不是 8 的整数倍多半是 send_byte 里多了一次时钟翻转比如先置高再置低等于每个 bit 产生两个沿74LS164 在上升沿和下降沿之间来回触发。如果 DS 变化发生在 CLK 上升沿之后几十纳秒说明单片机先给了时钟才摆数据把函数里两行交换即可。现象直接原因检查顺序数字整体错位段码表位序反转或接线错位先发 0x01、0x80 确认 Q0/Q7每隔一个数字闪一次时钟频率过高或位选延迟太小增大每位停留到 5ms全亮或全灭共阴共阳段码表选错换另一套段码表只有一位能点亮位选引脚连错或数码管公共端接错检查 P2.0~P2.3 与公共极波形检查通过后把发送次数减到每 500ms 一次肉眼可以直接确认段码表是否正确。比如发送 0x06 应显示数字 1发送 0x5B 应显示数字 2。5.2 从 Proteus 到硬件板下拉电阻、去耦电容与刷新率选择仿真通过后转真实硬件要补三个 Proteus 里看不到的细节。第一CP 引脚上并联一只 10kΩ 下拉电阻到地避免单片机复位瞬间 CP 悬空被噪声触发上升沿否则上电时数码管会突然闪一个随机笔画。第二每片 74LS164 的 VCC 和 GND 之间放 100nF 瓷片电容位置越靠近芯片越好级联多片时还要再加 10μF 电解电容平滑电流冲击。第三刷新率不要盲目调高AT89C51 工作在 12MHz 时每位停留 2ms 已经接近数字 I/O 翻转能力的上界如果还要处理按键消抖和串口中断把显示刷新放到定时器 0 中断里主循环只更新数据缓冲区这样显示不会因为主程序阻塞而停住。把 0.01μF 电容就近装在 CLK 与 GND 之间再配合上述下拉电阻74LS164 在任何调试环境下都能给出干净的时序。本文还有配套的精品资源点击获取
返回列表