
简介面向51单片机学习者和电子竞赛选手这是一套完整的16路彩灯控制系统设计方案以8155芯片扩展I/O实现按键输入与LED控制包含至少4种灯光效果切换、暂停全亮等典型功能。资料共34个文件压缩包2.22MB涵盖Keil工程源代码c/a51/hex、Protues仿真DSN/DBK、Altium Designer原理图SchDoc/PDF、流程图、元件清单Excel及实际演示视频mp4并附有lst/obj/m51等中间文件便于核对编译链接过程。文件分类清晰可对照原理图、流程图与源码逐步理解8155的应用方法。已有178人学习下载适合需要快速上手51单片机外设扩展、学习8155芯片使用或完成彩灯控制类课程设计/毕业设计的读者作为完整工程参考模板。1. 16路彩灯为什么非要8155不可51单片机做16路彩灯第一反应是IO不够。STC89C52虽然有32个IO但P0要兼做数据地址总线P3还要留着接中断、串口和定时器真正能空出来点灯的IO数一数也就十个出头。这时候8155的价值就出来了它是一片自带IO扩展能力的可编程接口芯片用片选和两条地址线就能换出22个IO口、256字节RAM和一个14位定时器。16路灯正好拆成PA口管8路、PB口管8路剩下的PC口还能接按键切花样不需要再加74HC595做串转并。这篇按一条能直接落地的工程线来讲原理图怎么连、流程图怎么画、物料清单怎么开、Proteus怎么仿、源代码怎么改花样五部分对得上照着做就能跑。2. 8155扩展16路IO的原理与接线选型2.1 51单片机的IO瓶颈与8155的22位IO资源51单片机直接驱动16路LED最典型的做法是用P1和P2各接8个LED。但现实情况是P2口往往还要接锁存器做地址总线P0口在扩展外部RAM或8155之后也必须让出来。如果项目里还要加按键、数码管、蜂鸣器IO马上就捉襟见肘。8155的方案不是省IO而是把IO“买”过来它内部有PA、PB、PC三个8位口PA和PB默认可以作为两个8位输出口PC可以拆成高低4位独立配置。这样16路LED只占用8155的PA和PB51单片机那边只需要P0口做数据总线、P2.7做片选、P2.0做IO/M选择总共用掉不到12个引脚剩下的IO全释放给其他外设。8155内部还有一块256字节的静态RAM地址范围从0x00到0xFF。这点RAM在彩灯系统里最实用的场景是存花样表把16个LED的状态逐帧存进去需要切花样时直接查表刷新PA和PB不用每次重新计算。另外8155内置一个14位定时器可以输出方波给LED做亮度调节不过彩灯系统里延时一般用51自己的定时器8155的定时器可以闲置。2.2 原理图设计8155与STC89C52的接线要点原理图的连接方式决定了后面所有地址和控制字这里给一套最常见的教材接法Proteus里照抄就能仿真。8155的AD0到AD7接51单片机的P0口注意P0口是开漏输出必须接一个10k欧排阻上拉到VCC否则高电平写不进去。51的ALE引脚接74LS373的锁存允许端74LS373的输入接P0口输出接8155的AD0到AD7这样地址和数据分时复用就能正确分离。8155的CE#接P2.7IO/M接P2.0RESET接51的复位电路输出也就是和51的RST连在一起保证上电时8155和51同时复位。A0和A1这两根地址线接74LS373的输出端也就是锁存后的低两位地址用来选择8155内部的不同端口。VCC接5VVSS接地。PA0到PA7接前8路LEDPB0到PB7接后8路LEDLED的阴极通过限流电阻接地阳极接8155的IO口。这里要特别强调8155推LED的能力有限每个IO最多灌入约10mA电流直接驱动普通发光二极管完全可以但如果是1W以上的大功率LED灯珠必须加ULN2803或三极管扩流后面第5章会单独说。原理图里还有一个容易漏的细节8155的TIMER IN和TIMER OUT如果不用TIMER IN要接地TIMER OUT悬空。这两个引脚在Proteus仿真里不接也不会报错但实物焊接时如果TIMER IN悬空内部定时器电路可能产生不定态导致整个芯片工作异常。所以画原理图的时候要么把定时器接成一个固定计数源要么明确标注“TIMER IN接地”。2.3 物料清单BOM表与选型注意事项编号元件名称型号/规格数量说明1单片机STC89C52RC140脚DIP51内核带8KB Flash2可编程接口芯片8155140脚DIP22位IO256B RAM定时器3地址锁存器74LS373120脚DIP8D锁存器用于分时复用4晶振12MHz1配合两个30pF电容5瓷片电容30pF2晶振负载电容6电解电容10uF/16V1复位电路用7电阻10k欧91个复位上拉8个P0上拉排阻8电阻排10k欧×81或者用8个独立电阻代替9电阻220欧16LED限流电阻10发光二极管红色φ5mm16颜色可任选仿真时用红色直观11轻触按键6mm×6mm2一个切花样一个复零12电源5V/1A1稳压模块或USB供电P0口上拉排阻的阻值选10k还是4.7k取决于LED数量和工作频率。16路LED同时刷新时P0口的负载电流变大10k电阻在高电平驱动时压降会比4.7k大所以实物建议改用4.7k排阻仿真无所谓。74LS373的VCC必须接5V它的输入高电平阈值是2.0V51的P0口经上拉后能稳定输出高电平兼容性没问题。3. 软件流程从花样表到8155控制字的映射3.1 8155命令字与端口地址译码8155的端口地址不是固定的它取决于CE#和IO/M的接法。以本设计的接法为准CE#接P2.7IO/M接P2.0A1A0由P0.1和P0.0经锁存器提供。访问内部寄存器时51单片机通过MOVX指令把地址送到P0和P2口。寄存器IO/MA1 A0地址示例命令/状态寄存器00 00x7F00PA口10 00x7F01PB口10 10x7F02PC口11 00x7F03定时器低8位01 00x7F04定时器高8位01 10x7F05地址0x7F00的推导过程P2.7必须为0选中8155P2.0必须为0表示访问命令寄存器P2.6到P2.1取1所以P2口输出0x7FP0口输出0x00其中P0.1和P0.0为00其余位为0。操作PA口时P2.0置1所以P2口为0x7FP0口低两位为00地址是0x7F01。理解了这个推导过程换一种接线也能自己算出地址不用死记。8155的命令字共8位定义如下位0控制PA口0为输入、1为输出位1控制PB口位2和位3控制PC口的工作方式位4和位5分别使能PA和PB的中断位6和位7控制定时器。对本系统来说PC口要接按键所以命令字设置为0x03即二进制00000011表示PA和PB输出、PC输入、定时器停止。3.2 流程图设计的三个关键分支16路彩灯的控制流程可以拆成三个分支画流程图时这三块是核心。第一个分支是系统初始化上电后先关总中断写8155命令字把PA和PB都清零然后初始化51的定时器T0设置20毫秒定时中断最后打开总中断进入主循环。这里为什么不用延时函数而用定时器中断后面代码部分会解释流程图里要把“定时器中断服务程序”单独画成一个模块。第二个分支是花样切换判定主循环持续读PC口的按键状态如果检测到按键按下花样索引加1同时做软件消抖。消抖不能只延时一次正确做法是检测到低电平后延时10毫秒再读一次确认还是低电平才算有效。流程图里这个分支要用一个菱形判断框加一个延时处理块否则实物按一次键会跳好几个花样。第三个分支是花样执行根据当前花样索引从花样表里取一帧数据拆成高8位和低8位分别写到PA和PB口然后等待下一次定时器中断。这一帧数据保持时间到了之后指针指向下一帧。16路LED的每一帧用一个unsigned int变量表示高字节对应PA口低字节对应PB口数据更新时先刷新PA再刷新PB避免中间态闪烁。3.3 源代码16路跑马灯的最小驱动框架#include reg52.h #include absacc.h #define CMD XBYTE[0x7F00] // 8155命令字寄存器 #define PA XBYTE[0x7F01] // PA口地址 #define PB XBYTE[0x7F02] // PB口地址 #define PC XBYTE[0x7F03] // PC口地址 // 花样表流水灯每帧16位高8位PA、低8位PB unsigned int code pattern[] { 0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080, 0x0100, 0x0200, 0x0400, 0x0800, 0x1000, 0x2000, 0x4000, 0x8000, }; unsigned char pattern_index 0; // 当前花样帧索引 unsigned char frame_counter 0; // 帧停留计数 unsigned char pattern_mode 0; // 当前花样编号 void timer0_init(void) { TMOD 0x01; // T0工作在模式116位定时器 TH0 0x4C; // 12MHz晶振20ms定时初值 TL0 0x00; ET0 1; // 开T0中断 EA 1; // 开总中断 TR0 1; // 启动T0 } void delay_10ms(void) { unsigned char i; for (i 0; i 20; i); } void main(void) { CMD 0x03; // PA输出、PB输出、PC输入 PA 0x00; // 初始全灭 PB 0x00; timer0_init(); while (1) { if ((PC 0x01) 0) { // PCO口接按键低电平有效 delay_10ms(); // 消抖 if ((PC 0x01) 0) { pattern_mode; if (pattern_mode 3) pattern_mode 0; while ((PC 0x01) 0); // 等待按键释放 } } } } void timer0_isr(void) interrupt 1 { TH0 0x4C; // 重装初值 TL0 0x00; frame_counter; if (frame_counter 10) { // 每帧停留200ms frame_counter 0; if (pattern_mode 0) { pattern_index; if (pattern_index 16) pattern_index 0; PA pattern[pattern_index] 8; PB pattern[pattern_index] 0xFF; } // pattern_mode为其他值时这里扩展其他花样 } }代码逻辑说明XBYTE是Keil C51提供的绝对地址访问宏定义在absacc.h里作用是把一个整形常量当成地址直接读写。CMD、PA、PB、PC这四个宏展开后就是对8155内部寄存器的读写操作。pattern数组是花样表每帧数据用一个16位整数表示高8位对应PA输出低8位对应PB输出所以某一帧写PA时用pattern[pattern_index] 8取出高字节写PB时用 0xFF取出低字节。定时器初值0x4C00对应20ms12MHz晶振下机器周期是1us模式1是16位计数最大计数65536要计20000个周期得到20ms初值就是65536减20000等于0x4C00。中断服务程序每次重装初值保证定时准确。主循环中的按键检测用了两次读取加延时第一次读到低电平后等10ms再确认确认有效后还要等按键释放否则循环会反复触发花样切换。4. Proteus仿真原理图到仿真图的落地验证4.1 仿真元件清单与连线检查在Proteus里搭这个系统元件清单和实物基本一致从库中搜索添加以下器件AT89C52、8155、74LS373、LED-RED、RES、RESPACK-8、BUTTON、CAP、CRYSTAL。注意Proteus的8155元件库中可能叫“8155”或“8155H”两者功能一样仿真结果没有差别。AT89C52的双列直插封装在原理图里要手动接电源和地VCC接5VEA#接高电平这样单片机才从内部Flash执行程序。连线时最容易被忽略的是74LS373的使能端。74LS373的G端要接51的ALE信号OE#接地这样P0口输出的地址才能在ALE下降沿被锁存。如果G端接了高电平373一直透明传输P0口的地址和数据互相干扰8155收到的地址会乱。仿真里出现LED随机亮、按键无响应第一个查的就是这根线。4.2 仿真常见故障8155无输出、LED微亮、乱码仿真里8155完全无输出优先检查三处第一8155的RESET是否和51的RST连在一起如果没连上电时8155处于不确定状态命令字写不进去第二CE#引脚是否被拉低CE#必须为低电平8155才响应总线操作仿真里可以用一个电压探针直接看P2.7的电平第三命令字是否写错如果CMD只写了0x01PA输出但PB是输入PB口就会呈现高阻态LED不亮或亮度极低。LED微亮是仿真里特有的现象IO口配置成输入模式时内部是高阻LED两端有微弱的电位差Proteus会把它显示成淡淡的点亮。解决办法是把命令字从0x03改成0x07让PC口也变成输出把不用的PC口引脚全部给低电平。乱码则多发于74LS373的连线错误或者P0口上拉排阻没接导致高电平读成低电平。仿真里可以暂时不接排阻但实物必须接仿真器对P0口的高电平判定比实物宽容。4.3 用逻辑探针验证PB口时序仿真图搭好之后验证不能只看LED闪不闪要确认PB口的输出时序和花样表一致。方法是在Proteus工具栏中调出Logic Probe分别挂到PB0和PB1上然后运行仿真。正常流水灯花样下PB0的电平变化应该是高电平保持200ms然后变低接着PB1变为高依次移位。如果两个探针同时出现高电平或长时间不变说明代码里的帧索引或者位操作有问题。还有一种验证方式是把PB口的输出接到虚拟示波器上观察波形占空比。每个LED的点亮时间由frame_counter控制20ms中断一次10次就是200ms示波器上应该看到200ms高、1800ms低的方波频率约0.5Hz。这个频率和视觉效果匹配人眼能清楚看到流水效果。如果示波器显示频率过高或波形乱优先检查TH0和TL0的初值是否被中断服务程序正确重装常见错误是重装时只写了TH0忘写TL0。5. 让16路灯能跑的四个实用技巧第一个技巧是按键切换花样时加松手检测。很多教材代码里只有消抖没有松手检测导致按下一次按键会连续触发多次切换。正确做法是确认按键按下后用while循环等按键释放再返回主循环这样每次按下只切换一次。这个技巧不仅适用于8155写任何矩阵键盘都能用。第二个技巧是用一个二维数组扩展多种花样。pattern[16]只存了一种流水灯实际可以定义成pattern[4][16]第一维控制花样编号第二维是每一帧的16位数据。切换时只改pattern_mode中断服务程序里用pattern[mode][index]取数据这样代码结构不用变花样从1种扩展到4种只改数组定义。第三个技巧是给LED加灌电流驱动。8155输出高电平驱动LED电流路径是IO口经LED到地这是拉电流方式驱动能力弱。改成灌电流接法后LED阳极接VCC阴极经电阻接8155的IO口IO输出低电平时LED点亮这样能充分发挥8155的灌入电流能力实物亮度更均匀。第四个技巧是主循环里加一个软件看门狗。彩灯系统长时间运行时如果程序跑飞LED会停止变化或者保持常亮。用一个变量在定时器中断里累加主循环里清零如果变量超过某个阈值说明主循环卡死直接软复位到初始化函数。这个做法对课程设计答辩演示特别实用避免演示中途死机的尴尬。本文还有配套的精品资源点击获取