十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AT89C51用74LS164驱动数码管:Proteus仿真与C编程实现

AT89C51用74LS164驱动数码管:Proteus仿真与C编程实现 简介AT89C51驱动74LS164实现数码管显示的Proteus仿真源文件包面向单片机初学者与数字电路爱好者用于学习串入并出移位寄存器扩展I/O口以及动态扫描显示。其中AT89C51作为主控通过I/O口模拟时钟与数据信号将段码串行送入74LS164后并行输出到数码管实现动态刷新显示对理解移位寄存器工作方式很有帮助。资源共47个文件以C语言源码、Keil工程文件、Proteus仿真设计为主包含hex、lst、uv2、dsn、pwi等类型压缩包仅169KB配置精简便于快速打开验证。已有1286人学习下载适合对照电路与程序理解74LS164的时序控制、段码编码和显示刷新逻辑。通过Proteus仿真可直观观察数码管显示效果降低硬件调试成本是结合单片机编程与数字逻辑设计的实用练习案例。项目中还提供了串口调试、液晶按键等辅助模块可辅助学习扩展功能整个工程组织规范目录层次清楚适合课堂实验或自学入门。1. AT89C51 点亮数码管为什么要把 74LS164 插在中间AT89C51 点亮一个数码管最简单的方式是把 8 个段选直接接到 P 口再留一个 I/O 控制公共端一位就吃掉 9 个引脚四位显示做下来 P0、P1、P2 基本被占完按键、ADC、LCD 都排不上队。74LS164 的作用是把这种并联直驱改成串行扩展AT89C51 只用 P1.0 和 P1.1 两根线送数据和时钟74LS164 将其还原为 8 路并行输出驱动数码管段选。这里要讲的是 AT89C51 加 74LS164 在 Proteus 仿真中的完整落地路径包括引脚接线、C 程序源码、段码表和排错顺序让读者能照着复现一份可运行的 7 段数码管显示工程。2. 74LS164 的移位过程与 AT89C51 的软件时序模拟2.1 74LS164 的 8 位串入并出结构74LS164 是一款典型的 TTL 8 位串入并出移位寄存器内部由 8 个边沿触发的 D 触发器和若干与门构成Q0 到 Q7 在任何时刻都能读到当前寄存器里存着的数据。它与 74HC595 最大的区别是没有锁存脚数据进入 Q0 后每个时钟脉冲到来时整体向后移动一位Q7 的数据直接丢弃整个过程完全透明不能像 595 那样先把 8 位数据全部移进来、最后一次并行输出。这个特性对驱动数码管来说反而是优点输出随数据输入实时变化软件里不需要额外控制锁存信号但也意味着移位过程中输出端会短暂出现中间态。如果显示内容刷新太快数码管上能看到拖影或乱码所以后面讲多位刷新时要专门处理消隐。2.2 DIP-14 引脚功能与常用的接线约定74LS164 的 1 脚 A 和 2 脚 B 是数据输入两者做逻辑与后送入第一位触发器3 脚到 6 脚、10 脚到 13 脚是 Q0 到 Q7 并行输出8 脚 CP 是时钟输入9 脚 MR 是低电平有效的复位输入。驱动数码管时习惯上把 A、B 都接在同一个 I/O 口上因为它们做与运算其中一个悬空会导致输入随机显示出现缺段。引脚号符号功能AT89C51 数码管驱动接法1A串行数据输入与 B 相与P1.0与脚 2 并联2B串行数据输入与 A 相与P1.0与脚 1 并联3Q0并行输出第 0 位数码管 a 段4Q1并行输出第 1 位数码管 b 段5Q2并行输出第 2 位数码管 c 段6Q3并行输出第 3 位数码管 d 段7GND电源地GND8CP时钟输入上升沿移入数据P1.19MR复位输入低电平清零5V 或 P1.210Q4并行输出第 4 位数码管 e 段11Q5并行输出第 5 位数码管 f 段12Q6并行输出第 6 位数码管 g 段13Q7并行输出第 7 位也用于级联数码管 dp 段14VCC电源5VMR 脚接高电平是正常工作状态接 5V 电源即可如果要让显示瞬间全灭也可以把它接到 P1.2由单片机输出低电平清零。剩下的 Q0 到 Q7 按顺序接数码管 a 到 dp 段这里 Q0 对应 a、Q7 对应 dp 的映射关系直接决定了后面 C 程序中段码表怎么取。2.3 AT89C51 没有 SPI用两个 I/O 口模拟时钟上升沿AT89C51 内核没有 SPI 外设串行扩展要么用定时器模拟要么直接软件翻转 I/O。先做最简单的版本定义 DATA 为 P1.0、CLK 为 P1.1发送子函数如下#include reg51.h #include intrins.h sbit DATA P1^0; // 74LS164 数据输入 A/B sbit CLK P1^1; // 74LS164 时钟输入 CP void send_164(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DATA (dat 0x80) ? 1 : 0; // 先输出数据比特 CLK 0; // 时钟拉低 _nop_(); // 等待电平稳定 CLK 1; // 上升沿数据被移入 Q0 dat 1; } }这段代码的关键在于先设置 DATA、后拉高 CLK。74LS164 是上升沿采样器件只有在 CLK 从低跳高的一瞬间A 与 B 的与值才会进入第一位触发器顺序反了移进去的就是上一个时钟周期前的旧数据。每移一位前把 CLK 拉低是为了产生完整的低-高跳变并让数据信号先于时钟建立。_nop_()在 Proteus 仿真里不加也能跑通实际硬件上建议保留避免在高频下数据建立时间不足。2.4 建立时间与保持时间在 12MHz 晶振下够不够用74LS164 数据手册给出的数据建立时间大约 20ns保持时间更短AT89C51 在 12MHz 晶振下机器周期是 1µs一条指令的执行时间已是建立时间的几十倍就算连续执行置位、拉低、拉高语句数据信号也早已稳定时序余量非常充裕。需要留意的是Proteus 仿真中的时序与真实芯片并不完全一致仿真器按理想逻辑处理边沿容易掩盖真实硬件里因线路过长、I/O 口负载过重带来的延迟。所以硬件上仍然要把数据线和时钟线分开走一位数据控制在 1~2µs 的节奏对 AT89C51 和 74LS164 的组合来说最稳妥也不会因为程序中插入了延时函数而导致显示响应变慢。3. Proteus 8 Professional 中搭建 AT89C51 与 74LS164 数码管驱动电路3.1 元件搜索关键词与需要的器件清单打开 Proteus 8 Professional 后在元件选择区的 P 按钮进入 Pick Devices 页面。元器件搜索框输入 AT89C51在 Microprocessor ICs 分类下能找到 AT89C51、AT89C52 等常用 51 内核芯片输入 74LS164 可以找到移位寄存器数码管输入 7SEG-MPX1-CA 代表一位共阳7SEG-MPX1-CC 代表一位共阴做四位显示直接用 7SEG-MPX4-CA 或 7SEG-MPX4-CC。按下面这张 Proteus 元件清单准备器件就可以开始画图元件Proteus 关键词数量说明AT89C51 单片机AT89C511核心控制74LS164 移位寄存器74LS1641串行转并行一位共阳数码管7SEG-MPX1-CA4或四位一体数码管晶振 12MHzCRYSTAL1接 XTAL1、XTAL2瓷片电容 33pFCAP2晶振负载电解电容 10uFCAP-ELEC1复位电路电阻 10kRES1复位下拉按钮BUTTON1手动复位可选3.2 最小系统与 74LS164 的完整连接顺序连线时先布 AT89C51 最小系统EA 引脚接 VCC表示从片内 Flash 启动XTAL1 与 XTAL2 之间接 12MHz 晶振晶振两端各用一个 33pF 电容接地RST 引脚接 10µF 电容到 VCC、10kΩ 电阻到地构成上电复位电路。接着把 74LS164 放好按下面的映射关系连线P1.0 同时连到 74LS164 的 1 脚和 2 脚P1.1 连到 8 脚9 脚 MR 接 VCC14 脚接 VCC7 脚接地。74LS164 的并行输出脚到数码管段的对应关系是关键74LS164 输出脚连接的数码管引脚说明Q0第 3 脚a 段字节 bit0Q1第 4 脚b 段字节 bit1Q2第 5 脚c 段字节 bit2Q3第 6 脚d 段字节 bit3Q4第 10 脚e 段字节 bit4Q5第 11 脚f 段字节 bit5Q6第 12 脚g 段字节 bit6Q7第 13 脚dp 段字节 bit7这里 Q0 到 Q7 对应字节 bit0 到 bit7也就是段 a 到段 dp 的位序。很多 Proteus 仿真源文件里显示乱码都是因为实际连线时把 Q0 接到了 dp 段或者发送子函数从最低位开始移两边对不上。3.3 加载 HEX 文件与设置 AT89C51 时钟频率双击放置好的 AT89C51 元件在弹出的对话框中找到 Program File 一栏点击文件夹图标选择 Keil 编译生成的 .hex 文件Clock Frequency 默认按 12MHz 处理如果你画了晶振可以用 12000000点击 OK 后回到原理图。# Proteus 中加载固件的步骤 1. 双击 U1 打开 Edit Component 2. Program File 指向 Keil 输出目录下的 main.hex 3. Clock Frequency 保持 12000000 4. 点击左下角 Play 按钮运行仿真Proteus 不主动放置晶振也可以仿真因为单片机属性里有内部时钟源但原理图还是要画出晶振和负载电容这样跟实际打板内容一致也方便用示波器观察时钟引脚。加载完 HEX 后点 Play程序才开始执行之前只有静态的电平状态。3.4 共阳极与共阴极数码管的驱动差异74LS164 的输出级是推挽结构高电平输出能力弱于低电平灌电流。因此驱动数码管时优先选共阳极公共端接 VCC段选为低电平时点亮电流从 VCC 经数码管流入 74LS164每段 8mA 左右的灌电流足够驱动常见尺寸的数码管。共阴极数码管要求段选高电平点亮74LS164 高电平输出只有几百微安直接驱动会偏暗仿真中看不出亮度差异因为 Proteus 不模拟亮度但硬件上这样接就不行。所以我的建议是Proteus 仿真阶段用共阳极数码管配合共阳极段码表和 74LS164 的电气特性匹配后面搬去实际电路也少踩一个坑。4. C 程序源码74LS164 发送函数、段码表与一位数码管显示4.1 用 Keil C51 组织源文件的步骤在 Keil 中新建工程时选择 AT89C51 或任何 8051 兼容内核Target 的 Memory Model 选择 SmallCode Rom Size 选择 LargeOutput 页面勾选 Create HEX File编译后生成 .hex 文件供 Proteus 加载。源码只需要一个 main.c不需要引入第三方库用头文件 reg51.h 之后用 sbit 语句单独声明与 74LS164 相连的两个引脚。这样的文件组织方式也方便移植把 sbit 定义换一下就能从 AT89C51 换到 STC89C52、AT89S52 等芯片其余代码基本不用改。4.2 从最高位开始发送的 send_164 函数第 2 章给出的 send_164 函数从 bit7 发送到 bit0也就是每次取 dat 的最高位放到 DATA 线上然后产生时钟上升沿再把 dat 左移一位。这样的发送顺序与 74LS164 输出脚的自然排列正好匹配经过 8 个时钟后最先送入的 bit7 被推到最末一级 Q7bit0 停在 Q0。配合第 3 章 Q0 接 a 段、Q7 接 dp 段的连接方式段码表里 bit0 是 a、bit6 是 g、bit7 是 dp段码数值与数码管图形的对应关系一目了然。如果发送顺序反过来从 bit0 开始Q0 对应的是 dp 段要么重新定义段码表要么把硬件接线全部倒过来。4.3 共阳、共阴七段数码管段码表对照七段数码管段位按 a、b、c、d、e、f、g、dp 顺序分配到字节的 bit0 到 bit7。共阳数码管公共端接 VCC某段熄灭写 1、点亮写 0共阴极相反点亮为 1、熄灭为 0。常用数字的段码表如下数字共阳段码共阴段码亮段00xC00x3Fa b c d e f10xF90x06b c20xA40x5Ba b g e d30xB00x4Fa b g c d40x990x66f g b c50x920x6Da f g c d60x820x7Da f g e c d70xF80x07a b c80x800x7F全部90x900x6Fa b c d f g注意共阳 0xC0 和共阴 0x3F 并不是简单的按位取反关系因为 dp 位在静态数字里始终熄灭共阳写 1、共阴写 0。实际使用中两套表都要保留调试时通过宏或条件编译切换避免拿错表。4.4 让一位共阳数码管循环显示 0 到 9完整的可运行 C 程序源码如下效果是让一位共阳数码管从 0 循环显示到 9每隔一段时间变一次数字#include reg51.h #include intrins.h sbit DATA P1^0; // 74LS164 数据输入 sbit CLK P1^1; // 74LS164 时钟输入 unsigned char code seg_CA[10] { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 }; void send_164(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DATA (dat 0x80) ? 1 : 0; CLK 0; _nop_(); CLK 1; dat 1; } } void delay(unsigned int t) { unsigned int m; while (t--) { for (m 0; m 120; m); } } void main(void) { unsigned char n; while (1) { for (n 0; n 10; n) { send_164(seg_CA[n]); // 把共阳段码串行送出 delay(300); // 约 300ms 换一个数字 } } }主循环里每次先调用 send_164 刷新数码管段选然后延时几百毫秒。因为 74LS164 的输出会持续保持数码管在两次发送之间不会熄灭这和 P 口直驱方式在控制流程上有明显差别。把 delay(300) 改成 delay(20)在 Proteus 里能观察到变字时的拖影这正是下一位刷新需要解决的问题。5. 多位 AT89C51 加 74LS164 数码管显示位选扫描与定时刷新频率设计5.1 一组段选共用 74LS164位选留给 P2 口需要显示四位数字时常见做法是让四位数码管的 a 到 dp 段全部并联接在同一片 74LS164 的 8 个输出脚上公共端分别接到 P2.0 到 P2.3。每次只让一个数码管的公共端导通同时 74LS164 送出对应位的段码轮流点亮四位靠视觉暂留形成完整显示。这样单片机只消耗 P1.0、P1.1 两个串行引脚和 P2.0 到 P2.3 四个位选引脚合计 6 个 I/O 就能驱动四位数字。真实硬件中共阳数码管位选信号要经过 PNP 三极管驱动共阴数码管位选则用 NPN 三极管因为 I/O 口直接驱动公共端会电流不足。Proteus 仿真里直接接 I/O 口就能工作但视觉亮度无法体现。5.2 定时器 0 中断里面做刷新刷新的关键点是每一位必须周期性被点亮否则会闪烁。在 12MHz 晶振下使用定时器 0 方式 1每隔 5ms 产生一次中断中断服务函数里依次选择位并发送段码#include reg51.h #include intrins.h sbit DATA P1^0; sbit CLK P1^1; unsigned char code seg_CA[10] { 0xC0, 0xF9, 0xA4, 0xB0, 0x99, 0x92, 0x82, 0xF8, 0x80, 0x90 }; unsigned char buf[4]; // 待显示的数字 0~9 unsigned char pos; // 当前扫描位 void timer0_init(void) { TMOD 0x01; // 定时器0方式116位计数 TH0 0xEC; // 5ms 定时初值 TL0 0x78; ET0 1; // 允许定时器0中断 EA 1; // 开总中断 TR0 1; // 启动定时器 } void send_164(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DATA (dat 0x80) ? 1 : 0; CLK 0; _nop_(); CLK 1; dat 1; } } void timer0_isr(void) interrupt 1 { TH0 0xEC; // 重新装载初值 TL0 0x78; P2 0xFF; // 关闭全部位选消隐 send_164(seg_CA[buf[pos]]); // 送当前位段码 P2 ~(0x01 pos); // 共阳数码管低电平点亮当前位 pos; if (pos 4) pos 0; } void main(void) { buf[0] 1; buf[1] 9; buf[2] 8; buf[3] 5; pos 0; timer0_init(); while (1); }位选逻辑要特别说明四个共阳数码管公共端接正极位选脚输出低电平时该位才导通所以消隐用 P2 0xFF点亮当前位用 P2 ~(1 pos)。如果换成共阴数码管位选高低电平正好相反消隐写 P2 0x00点亮写 P2 | (0x01 pos)。很多人直接照搬代码、忽略共阴共阳的取反逻辑结果从轮流显示变成某位常亮。5.3 刷新频率怎么算闪烁的临界值在哪里四位扫描一轮需要 4 个 5ms也就是 20ms对应刷新率 50Hz人眼看起来基本稳定把每位缩短到 2ms一轮 8ms刷新率提高到 125Hz动态更顺滑但 CPU 占用更多。刷新率的经验值如下每位置时间一轮 4 位时间刷新率观感2ms8ms125Hz稳定且顺滑5ms20ms50Hz稳定推荐下限10ms40ms25Hz可见闪烁选 3ms 到 5ms 是折中区间。如果主程序中还有按键扫描、ADC 采集等任务记得中断服务函数只负责刷新不要在中断里加软件延时否则会影响主循环响应。5.4 多片 74LS164 级联扩展更多显示位要扩展更多显示位方向一是增加位选口扩展行数方向二用级联扩展段选路径。74LS164 级联非常直接把第一片的 Q7 接到第二片的 A、B 端两片的 CP 并联在一起单片机先发高字节再发低字节连续完成 16 个时钟脉冲即可。发送完高字节后无需停顿第 8 个时钟后高字节停留在第二片第 16 个时钟后低字节停留在第一片。级联后发送函数要循环 16 位段选输出脚仍然是 Q0 到 Q7连线约定不变。6. 跑通 Proteus 仿真后如何验证 74LS164 输出并排查显示故障6.1 数码管全灭时先查 MR、DATA、CLK 三个引脚数码管上电全灭先不要怀疑段码表用 Proteus 的 Voltage Probe 量 74LS164 的 MR、A、B 三个引脚。MR 没有得到高电平芯片就被持续清零一次数据都移不进去这是最容易漏掉的错误检查第 9 脚与 VCC 之间是否真的连线因为多根线叠在一起时网络名容易被覆盖。A、B 两个输入脚如果只有一端接到 P1.0另一端悬空与门输出会随机化每来一个时钟移入 Q0 的值可能变成 0显示内容固定但段位缺失。6.2 显示乱码的两种典型原因与验证方法乱码最常见的原因是共阳共阴选错用共阳段码表驱动共阴数码管时结果通常是多个段同时亮、数字轮廓对不上。把 Proteus 里的数码管从 7SEG-MPX1-CA 换成 7SEG-MPX1-CC再跑一遍即可确认。第二种原因是段位序颠倒Q0 接成 dp、Q7 接成 a显示内容会左右错位。排查时写一个逐段点亮函数让 8 个段按顺序单独亮一遍void test_seg(void) { unsigned char s; for (s 0; s 8; s) { send_164(0xFF ~(0x80 s)); // 逐段点亮 delay(200); } }运行这个测试函数观察数码管上 a 到 dp 的亮灭顺序就能确认硬件接法与段码表定义是否一致。6.3 用 Logic Probe 观察 CLK 与 DATA 波形最后的验证手段是看波形。在 Proteus 左侧工具栏选择 Logic Probe分别挂在 P1.0 和 P1.1 网络上点击运行后右键选择 Digital 波形可以看到 DATA 线上按 bit7 到 bit0 排列的电平序列CLK 线上有 8 个脉冲组成一组。理想情况下DATA 的电平变化应该在 CLK 上升沿之前完成如果 CLK 脉冲数量不是 8 的整数倍说明发送函数被中断打断重点检查中断服务函数里是否对 DATA、CLK 做了意外改动。若波形正常但显示仍是错的就把范围缩到段码表本身用单字节单段的方式逐个验证比反复猜连线要快得多。本文还有配套的精品资源点击获取
返回列表