拓冰建站拓冰建站
首页 / 资讯中心 / 正文

51单片机用74HC595级联驱动8位数码管:原理与代码详解

简介一份面向51单片机初学者的开发板实验资源演示如何通过HC595移位寄存器驱动8个数码管解决单片机IO口不足的问题覆盖串行通信、位操作、定时器中断及数码管动态扫描等嵌入式基础知识点。压缩包共13个文件约343KB包含Keil工程文件uvproj/uvopt、C源码与头文件、可烧录的hex文件以及接口电路图jpg等项目辅助文件除主体程序外还提供config.h与STC15Fxxxx.H等头文件便于对照硬件配置深入学习。目前已有470人学习下载适合正在学习51单片机IO扩展或数码管显示驱动的开发者参考。通过该工程读者可快速理解HC595的“串入并出”工作原理掌握多位LED数码管的动态显示控制方法并能将相关代码灵活迁移到其他显示或IO扩展场景是课内实验与课后练手的高性价比素材。 用51单片机驱动数码管几乎是每个电子爱好者的必修课。单个数码管很简单IO口送一组段码就行但是当实验要求从1个变成8个数码管时问题马上就来了——8个数码管需要64个段选信号和8个位选信号而51单片机真正能用的IO口就那么多直接接根本不够。我最早做这个实验时被IO分配卡了好几天后来在开发板原理图上看到用74HC595工程里习惯写成HC595级联驱动8个数码管的方案才彻底想通串转并这条路怎么走。这篇文章就把整套方案拆开讲清楚为什么必须用这颗芯片、两片595怎么级联、源代码怎么组织、实测会踩哪些坑。内容适合刚入门51单片机、正在做课程设计或者想彻底搞懂数码管动态扫描原理的朋友。1. 为什么8个数码管会让IO口瞬间不够用1.1 数码管显示的最小单位段选与位选一枚七段数码管内部其实就是8个LED七段加一个小数点。要让某个数字亮起来本质是让对应的那几段LED通电发光。比如显示数字“1”要亮起b、c两段显示“8”则a到g七段全亮。这8个LED靠一个公共端分组共阴极数码管把8个LED的阴极接在一起作为公共端共阳极则把阳极接在一起。这里就引出了两个基本概念段选a~g、dp八段决定“亮哪几个笔段”位选公共端决定“哪一位数码管被激活”。驱动一个数码管至少需要8根段选线加1根位选线。这还没考虑电流驱动能力的问题——单片机IO口直接推数码管本来就勉强一旦位数变多问题会更明显。1.2 一笔IO账8个数码管需要多少信号线8个独立数码管无论共阴还是共阳如果把每个数码管的段选都单独接到单片机IO口那么8个×8段64根段选线再加上8根位选线一共72个IO信号。而一颗标准51单片机比如STC89C52P0、P1、P2、P3四个端口加起来才32个IO就算用P0做段选、剩余端口做位选也远远不够。做实验又不是做产品总不能为显示几个数字就换一颗几百引脚的芯片。所以问题的本质是单片机IO资源是稀缺的而8位数码管这种外设需要的信号数量又太大中间必须加一层“扩展”电路把单片机的少量IO变成能控制多个输出的信号。1.3 三条常见出路谁更适合实验面对IO不够的问题行业里通常有三类解法。第一类是用锁存器比如74HC373或74HC573。锁存器只能把8路输入锁存到8路输出本质上是“增加输出保持能力”但不能减少需要占用的IO数量。如果想用锁存器实现多位显示往往要配译码器或额外的位选控制电路复杂度并不低。第二类是用专用数码管驱动芯片比如TM1628、TM1640这类。这类芯片内部集成了扫描电路和显示存储器单片机只需要通过两线或三线接口发数据非常省事。问题在于这类芯片更偏向成品方案对学习数码管原理帮助不大。第三类就是用74HC595做串转并扩展。单片机只需要3根线数据、时钟、锁存就可以控制任意数量的并行输出口而且多片595可以级联想扩展多少位就级联多少片。这也是绝大多数开发板选择595方案的原因——性价比高、原理清晰、代码写起来也直接。8位数码管实验用两片595级联正好是理解这类方案的经典入门题。2. HC595串转并原理与关键时序锁存是灵魂2.1 内部两级寄存器移位寄存器与存储寄存器74HC595的核心是两个寄存器。第一个叫移位寄存器它的工作方式是在每个时钟上升沿把DS脚上的电平“塞进”第一位同时把之前的第一位移到第二位、第二位移到第三位依次往后推。这就像一个排队通道新来的人从入口进队伍里的每个人依次往前挪一位。第二个叫存储寄存器它负责把移位寄存器当前的内容“拍个快照”保存下来然后输出到QA~QH这8个并行口上。这个快照动作由STCP脚控制。为什么要分两级因为如果只有一级寄存器串行移位过程中输出端会跟着不断变化外设就会看到一堆乱七八糟的中间状态。有了存储寄存器就可以等8位数据全部移到位之后再一次性更新输出——这就是“锁存”的意义。2.2 五个关键引脚和一个必须吃透的时序74HC595常用引脚有5个需要重点理解。DS是串行数据输入也就是数据从哪个口“塞”进来。SHCP是移位时钟上升沿触发移位。STCP是锁存时钟上升沿把移位寄存器送到存储寄存器。MR是复位脚低电平有效拉低就会把所有寄存器清零。OE是输出使能低电平有效接低时输出正常接高时QA~QH全部变高阻态。还有一个容易被忽略的Q7S也叫Q7它是移位寄存器的第9位输出。当第8个时钟把数据推到顶之后再推一位最早进来的数据就会从Q7S冒出去。多片595级联靠的就是这个脚——把第一片的Q7S接到第二片的DS两片就串成了16位移位寄存器。发送一字节数据的时序是先把数据最高位放到DS脚上然后给SHCP一个上升沿让这一位被移入接着放下一位数据再给一个上升沿如此循环8次一个字节就全部进入移位寄存器了。最后给STCP一个上升沿8位数据才真正出现在QA~QH上。注意STCP给完上升沿之后要保持低电平否则再移下一位数据时会干扰输出。2.3 为什么锁存能“同时亮起8个段”理解了时序之后你会发现一个很关键的点段码在移位过程中输出端其实一直维持旧数据。只有当STCP上升沿到来的一瞬间QA~QH才整体切换到新数据。这意味着8个段是同步点亮或熄灭的不存在某个段先亮、某个段后亮的错位现象。这跟直接用IO口一位一位送数据完全不同。直接操作IO时从第一位到最后一位之间有时间差如果外设响应速度够快肉眼虽然看不出但逻辑上确实存在中间状态。595的锁存机制天然避免了这个问题这也是它适合驱动数码管这类“多位同步输出”外设的原因。3. 两片级联硬件连接方案与工程细节3.1 一片管段一片管位级联结构怎么搭驱动8个数码管核心思想是把“段选”和“位选”分开控制。一片595负责段选它的8个并行输出QA~QH分别接到8个数码管的a、b、c、d、e、f、g、dp引脚8个数码管同一段的引脚并联。另一片595负责位选它的8个并行输出分别接到8个数码管的公共端通过控制公共端的电平来选中当前要显示的那一位。两片595的连接方式是级联先让主控单片机的3根控制线DS、SHCP、STCP接到第一片595然后把第一片的Q7S接到第二片的DS两片595的SHCP和STCP并联在一起由单片机同一根线控制。这样单片机发送数据时第一片移满8位后继续移数据就会“挤出”到第二片最终实现一次锁存同时更新16个输出。3.2 完整接线对照表下面这张表是我在实验板上验证过的标准接法不同开发板引脚可能不同但逻辑完全一致。单片机的P1.0、P1.1、P1.2作为控制脚你也可以换成其他任意IO。74HC595引脚名称/别名作用接法14脚DS / SER串行数据输入接单片机P1.011脚SHCP / SRCLK移位时钟接单片机P1.112脚STCP / RCLK锁存时钟接单片机P1.213脚OE输出使能低有效接地10脚MR复位低有效接VCC9脚Q7S / Q7级联输出接第二片DS15,1~7脚QA~QH并行输出第一片接段选第二片接位选3.3 段码表极性、限流电阻与共阴共阳的关系数码管有共阴和共阳之分这会直接改变段码表和位选信号的极性。共阴数码管公共端接GND段选端给高电平才能点亮共阳则相反公共端接VCC段选端要拉低才能点亮。我用共阴数码管做示例段码表写成“亮哪段哪段为1”。如果你的板子是共阳最省事的办法是把共阴段码表按位取反编译后效果一样不用改电路。位选信号同理共阴时公共端接GND位选输出高电平才能选中某一位共阳则位选输出低电平有效。这部分调试时最容易出错因为“看起来一样”的数码管极性可能完全不同。限流电阻的计算也要跟上。数码管每段正常工作电流5~10mA段压降约1.8~2V供电5V那么限流电阻R(5-2)/0.01≈300Ω实际用220~330Ω都没问题。由于动态扫描时每个数码管只有1/8的占空比平均电流会比静态时低所以阻值可以适当取小一点保证亮度足够。开发板上常见做法是每段串联一个330Ω电阻或者直接用数码管模块自带的排阻。3.4 动态扫描人眼的视觉暂留是怎么帮忙的如果你想着“8个数码管同时亮”那就钻进死胡同了。595方案实际用的是动态扫描任意时刻只点亮一位数码管8位轮流点亮循环速度足够快之后人眼会因为视觉暂留效应觉得它们是同时在显示。就像小时候玩的手摇动画书每一页画一个动作快速翻页时看起来就是连贯动画。数码管扫描也一样循环周期越短画面越稳定。实践上整轮8位扫描周期控制在10~20毫秒内也就是每位停留1~2毫秒刷新率大概50~100Hz肉眼就基本看不到闪烁了。4. 源代码逐段拆解从底层时序到动态扫描4.1 引脚宏定义与底层时序函数先定义3个控制脚也就是前面硬件表里的P1.0、P1.1、P1.2。加上一个_nop_()空操作让时序更稳定需要包含intrins.h头文件。#include reg52.h #include intrins.h sbit DS P1^0; // 串行数据输入 sbit SHCP P1^1; // 移位时钟 sbit STCP P1^2; // 锁存时钟Keil C51环境下这种sbit定义是最常用的。注意不要让SHCP和STCP悬空实验板上这两个脚一旦没接好后续调试会非常痛苦。4.2 向595发送一字节8个时钟边沿送走一位发送函数是整套代码的地基。每次把dat的最高位移到DS脚然后SHCP产生一个上升沿让这一位进入移位寄存器接着把dat左移一位循环8次。void HC595_SendByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { DS (dat 0x80) ? 1 : 0; // 最高位先发送 dat 1; SHCP 0; _nop_(); SHCP 1; _nop_(); } }这里有一个常见误区很多人会忘记把SHCP先拉低再拉高或者反复只给高电平。移位是边沿触发只有产生上升沿的那一下数据才会被移入所以SHCP必须先置0再置1这个顺序不能省。4.3 段码表与显示缓冲区让主循环变简洁段码表是显示内容的“翻译字典”。按共阴接法数字0~9对应的段码如下unsigned char code seg_table[] { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x00 };下标0对应0x3F显示0下标1对应0x06显示1下标16是0x00对应全灭。点亮小数点只需要在当前段码上加0x80因为共阴接法dp段是高电平点亮。显示缓冲区用一个8字节数组主循环只负责轮询它unsigned char data disp_buf[8];主程序初始化时往disp_buf里填要显示的内容扫描循环每次都从这个缓冲区取段码改动显示内容只改缓冲区刷新逻辑完全不用动。4.4 主循环选位、送段、锁存、消影这是整段代码的核心逻辑。注意发送顺序先发送位选数据再发送段选数据。因为级联时第一片的数据会流向第二片先发的位选会被“挤”到第二片后发的段选留在第一片最后一起锁存。void main(void) { unsigned char i, j; disp_buf[0] seg_table[1]; disp_buf[1] seg_table[2]; disp_buf[2] seg_table[3]; disp_buf[3] seg_table[4]; disp_buf[4] seg_table[5]; disp_buf[5] seg_table[6]; disp_buf[6] seg_table[7]; disp_buf[7] seg_table[8]; while (1) { for (i 0; i 8; i) { HC595_SendByte(0x01 i); // 位选选中第i位 HC595_SendByte(disp_buf[i]); // 段选第i位显示内容 STCP 0; _nop_(); STCP 1; // 锁存输出变成最新数据 _nop_(); STCP 0; for (j 0; j 100; j); // 延时约1ms HC595_SendByte(0x00); // 消影段码清零 STCP 0; _nop_(); STCP 1; _nop_(); STCP 0; } } }锁存之后别忘了把STCP拉回低电平否则下一次移位过程中输出端可能被意外更新。消影动作的意义在下一章我会专门讲它在多位数码管显示里是必不可少的。4.5 改个花样显示小数点和倒计时有了上面这个框架扩展玩法很简单。比如要让第3位数码管的小数点点亮初始化时就这么写disp_buf[2] seg_table[5] | 0x80; // 显示5. dp段亮倒计时的话只需在主循环外用一个定时器或者软件计数来更新disp_buf数组。比如每秒把数组内容减一更新时做一个从整数到各数位的拆分disp_buf[0] seg_table[(num / 10000000) % 10]; disp_buf[1] seg_table[(num / 1000000) % 10]; // ... 依次类推缓冲区设计的好处就在这显示逻辑和刷新逻辑完全解耦你只关心“这一位要显示什么”刷新循环永远不用改。5. 实测中踩过的三个坑和进阶方向5.1 坑一STCP上升沿没处理好显示完全随机第一次上电时我遇到的故障很典型——数码管显示的不是想要的数字而是一堆乱码。当时我怀疑是芯片坏了换了一片还是一样。用示波器看STCP脚才发现代码里锁存之后没有把STCP拉低导致下一轮移位的过程中输出端一直被中间数据干扰看起来就像乱码在跳。排查思路很简单确认发送函数的SHCP有没有产生规范的上升沿确认锁存函数的STCP是不是严格“低-高-低”三个状态。另外注意原理图上STCP也有叫RCLK的不同厂家数据手册命名不完全一样看芯片手册时别被别名绕晕。5.2 坑二刷新频率太低数码管肉眼可见地闪软件延时太大会让每位数码管停留过久整轮扫描周期过长就会看到明显的闪烁。我试过把延时从100改成1000结果8个数码管就跟跑马灯一样一格格地亮灭完全不是同时显示的效果。经验值是每位停留1~2ms整轮扫描8~16ms刷新率60Hz以上人眼才觉得稳。如果用的是12MHz晶振上面的for循环j100大概是1ms级别效果正常。换晶振或者换编译器优化等级后建议用示波器实测一下刷新周期别凭感觉调。5.3 坑三没消影数字边缘出现残影消影这个问题很多教程一笔带过但实际显示效果差距很大。如果不做消影切换到下一位时上一位的段码还残留在595输出端而新的位选已经打开这一瞬间这位数码管会错误地把上一位的内容亮一下视觉上就是数字旁边拖着淡淡的残影。解决方法是每次更新显示前先发送全0段码并锁存一次把所有数码管灭掉然后再送新的位选和段码。虽然这会多占用几十微秒但对显示稳定性帮助极大。真正到了8位甚至16位数码管时消影就是决定成败的细节。5.4 进阶方向定时器中断刷新与更多级联软件延时做扫描有个致命缺点——主循环被延时卡住干不了别的事。想一边显示一边处理按键或者串口数据就得把扫描搬到定时器中断里。定时器每1ms触发一次中断中断里只做“选位-送段-锁存-消影”主循环则专心更新disp_buf里的数据。这样显示和业务逻辑互不干扰是正经产品里最常见的做法。级联扩展方面595支持无限级联。如果你要驱动16个数码管就再加一片595发送顺序变成先发最远端的数据再逐步发到最近端。唯一要注意的是每增加一片刷新一帧需要多移8个时钟周期只要总帧周期仍然小于20ms显示就稳。这套架构将来扩展到点阵屏、甚至小型LED矩阵思路都是完全一样的。最后再分享一个调试小技巧如果接上电显示乱码先别急着换芯片用万用表量一下STCP和SHCP脚有没有信号再检查两片595之间的Q7S到DS连线是否接对。我做过很多次实验这种电路九成问题出在接线上其次才是代码逻辑。先解决硬件连接再回头看代码能省下大量排查时间。本文还有配套的精品资源点击获取
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门