
简介这份PDF实验报告面向电子信息、自动化等专业学习51单片机的学生与初学者围绕基于8255的8LED数显频率计展开解决频率、周期与脉宽测量及数码管显示的综合设计问题。资源包共1个PDF文件大小约627KB内容为完整的实验报告文档涵盖实验要求、实验内容、实验原理、设计思路与流程图等模块。报告详细讲解8255扩展51单片机I/O管脚的方法利用A、B端口实现输入输出介绍T0、T1两个16位定时/计数器的工作方式以及TMOD、TCON寄存器的配置说明外部中断INT0/INT1的电平触发与负边沿触发采样方式并给出中断服务程序示例。显示部分阐述8LED数码管的送数与扫描原理以及8255控制字与端口地址设置。读者可据此掌握频率计从信号产生、计数测量到译码显示的完整实现思路并参考发挥部分的功能键锁定、刷新时间修改、蜂鸣器提示与波形显示等扩展设计。目前已有114人学习下载。1. 从一份“51单片机频率计.pdf”说起为什么频率测量值得单独做一块板很多人第一次接触 51 单片机做测量类项目都是从频率计开始的。原因很直接它同时踩中了定时器、外部中断、数码管动态扫描、信号整形这几个核心知识点而且测量结果对不对一眼就能从数码管上看出来几乎没有“糊弄过去”的空间。一份名为“51单片机频率计.pdf”的资料本质上讲的就是这套东西——用一片 89C51 或 STC89C52配合定时器 T0/T1 和外部中断 INT0/INT1把一路方波或脉冲信号的频率测出来再通过数码管或 LCD 显示。它解决的问题是手头没有几千块的台式频率计但需要知道某个振荡电路、传感器输出、电机转速脉冲到底跑在多少赫兹。适合谁电子类专业做课程设计的学生、刚上手 51 的嵌入式新手、以及需要给产线做个简易脉冲计数工装的人。测频范围通常在 1Hz 到几百 kHz精度取决于晶振和闸门时间这也是后面要反复调参的地方。2. 51单片机频率计的测频原理与两种主流方案选型2.1 测频法、测周法与多周期同步法的取舍频率测量的底层逻辑只有一句话在已知的时间窗口内数一数来了多少个脉冲。围绕这句话工程上有三种常见做法。第一种是测频法计数法固定一个闸门时间 T比如 1 秒用计数器统计这段时间内外部脉冲的个数 N频率 f N / T。它的优点是实现简单高频段精度高缺点是低频段误差大因为 1 秒内可能只数到几个脉冲±1 个数的量化误差占比就很高。第二种是测周法测量被测信号一个完整周期的时间再取倒数。它适合低频因为周期长、计时器计到的数多相对误差小但高频时周期极短计时器分辨率不够误差反而变大。第三种是多周期同步法用两个计数器分别对标准时钟和被测信号同步计数能兼顾高低频但 51 的硬件资源有限实现起来代码复杂一般课程设计不要求。对于“51单片机频率计”这个标题下的典型需求测频法 1 秒闸门是最常见、最可靠的落地方案。如果被测信号频率低于 10Hz再补一个测周法分支即可。2.2 定时器与外部中断的资源分配51 有两个定时器 T0、T1正好一个用来定闸门一个用来计数。常见分配如下资源用途工作模式说明T0闸门定时模式116位定时产生 1s 或 0.5s 闸门T1脉冲计数模式116位计数对 T1 引脚输入脉冲计数INT0/INT1辅助测周边沿触发低频时测周期用P0/P2数码管段选/位选普通 IO动态扫描显示这里有个容易踩的坑T1 作为计数器时计数脉冲从 P3.5T1引脚输入不是随便哪个 IO 都行。很多人接线时把信号接到 P3.4结果数一直是 0排查半天才发现引脚选错了。2.3 信号整形为什么方波前还要加一级比较器单片机 IO 认的是干净的逻辑电平但实际被测信号可能是正弦波、三角波或者幅度只有几百毫伏。直接接进去要么不触发要么在过零点附近反复抖动计数值乱跳。常见做法是在前端加一级LM393 或 LM339 电压比较器把任意波形整形成同频率的方波再送进 T1 引脚。比较器的参考电压用一个电位器分压得到调到信号幅度的中间值附近。这一步在 Proteus 仿真里可以用ANALOG库里的比较器模型或者干脆用信号源直接给方波先跑通逻辑再补前端。提示如果被测信号幅度超过 5V比较器输出端要加限幅或分压别直接怼进单片机引脚否则容易打坏 IO。3. 在Proteus里搭出可仿真的51单片机频率计电路3.1 元件清单与Proteus元件库检索名在 Proteus 里画频率计电路关键是找对元件库里的名字。下面这张表是我常用的检索关键词直接照着搜就行元件Proteus 检索名备注51单片机AT89C51或AT89C52仿真够用数码管7SEG-MPX4-CC四位共阴比较器LM393信号整形信号源DCLOCK或PULSE产生测试脉冲晶振CRYSTAL11.0592M 或 12M复位电容CAP-ELEC10uF排阻RESPACK-8P0 上拉如果搜不到某个元件多半是元件库没加载全检查一下Library菜单里的Library Manager把Analog、Microprocessor这几个库勾上。3.2 最小系统接线与T1计数引脚最小系统部分晶振接 XTAL1、XTAL2两个 30pF 电容对地复位电路用 10uF 电容加 10k 电阻EA 引脚接高电平表示使用内部程序存储器。计数信号从DCLOCK信号源出来经过 LM393 整形后接到P3.5T1。数码管段选接 P0位选接 P2.0~P2.3P0 记得挂一个RESPACK-8排阻做上拉否则共阴数码管亮度会很低甚至不亮。# Proteus 仿真前的检查清单 1. 双击 AT89C51Program File 指向编译好的 .hex 2. Clock Frequency 填 11.0592M和代码里的晶振频率一致 3. DCLOCK 频率先设 1000Hz方便验证 4. 确认 T1 引脚接的是 P3.5不是 P3.4这几步里最容易出错的是第 2 步Proteus 里单片机属性中的时钟频率必须和 Keil 代码里计算定时初值时用的晶振频率一致否则闸门时间就不是 1 秒测出来的频率会整体偏大或偏小。3.3 数码管动态扫描的显示逻辑四位共阴数码管段选 P0 输出字形码位选 P2 输出低电平选中某一位。动态扫描的核心是每次只点亮一位快速轮流利用视觉暂留形成稳定显示。// 数码管动态扫描显示频率值 void display(unsigned int freq) { unsigned char i; unsigned char buf[4]; buf[0] freq / 1000; // 千位 buf[1] freq / 100 % 10; // 百位 buf[2] freq / 10 % 10; // 十位 buf[3] freq % 10; // 个位 for (i 0; i 4; i) { P0 seg_code[buf[i]]; // 段选查表得到字形码 P2 ~(1 i); // 位选低电平选中第 i 位 delay_ms(2); // 每位点亮 2ms P0 0x00; // 消影防止拖尾 } }逻辑说明seg_code是一个共阴字形码数组把 0~9 对应的段码存好。P2 ~(1 i)每次只让一位为低其余为高。delay_ms(2)决定刷新速度4 位一轮 8ms刷新率约 125Hz人眼看不到闪烁。最后的P0 0x00是消影不加的话上一位的残影会串到下一位显示会发虚。参数说明delay_ms的时长不能太长超过 5ms 单次就会看出闪烁也不能太短太短亮度不够。2ms 是个经验值。4. Keil里写测频代码定时器初值、计数溢出与误差处理4.1 1秒闸门的定时器初值计算用 T0 定时 50ms中断 20 次凑够 1 秒。12M 晶振下机器周期 1us50ms 需要 50000 个机器周期。16 位定时器最大 65536初值 65536 - 50000 15536 0x3CB0。// T0 定时 50ms12M 晶振 void timer0_init() { TMOD 0xF0; TMOD | 0x01; // T0 模式116位定时 TH0 0x3C; // 初值高字节 TL0 0xB0; // 初值低字节 ET0 1; // 开 T0 中断 EA 1; // 开总中断 TR0 1; // 启动 T0 } // T1 计数模式116位 void timer1_init() { TMOD 0x0F; TMOD | 0x50; // T1 模式1计数器模式 TH1 0x00; TL1 0x00; TR1 1; // 启动计数 }逻辑说明TMOD | 0x50中高四位控制 T10101表示模式 1 且为计数模式C/T 位为 1。T1 的计数脉冲来自 P3.5 引脚的外部下降沿。参数说明如果换成 11.0592M 晶振机器周期变成 1.085us50ms 需要约 46080 个周期初值 65536 - 46080 19456 0x4C00。晶振频率变了初值必须重算这是新手最常忽略的一点。4.2 计数溢出的处理与频率换算T1 是 16 位计数器最大计到 65535。如果 1 秒内脉冲超过 65535 个就会溢出回零测出来反而变成小数字。解决办法是开 T1 中断每次溢出给一个溢出变量加 1。unsigned char overflow 0; // T1 溢出次数 unsigned int count 0; // 总脉冲数 unsigned int freq 0; // 频率值 void timer1_isr() interrupt 3 { overflow; // 每次溢出累加 } void timer0_isr() interrupt 1 { static unsigned char n 0; TH0 0x3C; // 重装初值 TL0 0xB0; n; if (n 20) { // 20 * 50ms 1s n 0; TR1 0; // 停止计数 count overflow * 65536 (TH1 * 256 TL1); freq count; // 闸门1s频率数值等于计数 overflow 0; // 清零准备下一轮 TH1 0x00; TL1 0x00; TR1 1; // 重新开始计数 } }逻辑说明overflow * 65536 (TH1 * 256 TL1)是完整的脉冲总数。因为闸门正好是 1 秒所以频率数值直接等于计数。如果闸门改成 0.5 秒freq count * 2。参数说明n 20对应 1 秒闸门改成n 10就是 0.5 秒测量速度更快但低频精度下降。溢出变量用unsigned char最多支持 255 次溢出即约 16.7MHz对 51 来说绰绰有余。4.3 测量误差来源与软件补偿误差主要来自三块晶振本身的精度、闸门时间的量化误差、以及 ±1 个计数的固有误差。晶振用普通的 12M 陶瓷谐振器误差可能到几千 ppm测 100kHz 时偏差几十赫兹很正常。要准就换石英晶振或者用 11.0592M 这种通信常用频率。±1 计数误差在低频时最明显。测 10Hz 信号1 秒闸门数到 10 个误差 ±1 就是 ±10%。这时候可以延长闸门到 10 秒或者切换到测周法。// 低频时用测周法测一个周期的时间 // INT0 下降沿触发记录两次中断间的 T0 计数值 unsigned int period 0; void int0_isr() interrupt 0 { static unsigned int last 0; unsigned int now TH0 * 256 TL0; period now - last; // 一个周期的机器周期数 last now; } // 频率 晶振频率 / (12 * period)逻辑说明测周法用外部中断捕获信号边沿记录两次边沿之间 T0 走过的计数值即周期。频率是周期的倒数。参数说明period单位是机器周期12M 晶振下 1 机器周期 1us所以f 1000000 / period。5. 频率计跑起来之后的调试技巧与量程切换5.1 用Proteus信号源验证不同频段的读数仿真阶段把DCLOCK频率依次设成 10Hz、1kHz、50kHz、200kHz观察数码管读数。如果 10Hz 时读数在 9~11 之间跳说明 ±1 误差正常如果 1kHz 时读数稳定在 1000说明闸门和计数逻辑没问题如果 200kHz 时读数明显偏小检查 T1 溢出中断有没有正常触发。注意Proteus 仿真速度受电脑性能影响高频信号下仿真可能变慢读数刷新会卡顿这是仿真器的问题不是代码的问题。可以先把闸门缩短到 0.1 秒加快刷新。5.2 量程自动切换的判断逻辑单一闸门无法兼顾高低频。常见做法是先固定 1 秒闸门测一次如果读数小于 100说明信号频率低改用测周法如果读数大于 50000说明接近溢出缩短闸门到 0.1 秒再测最后把结果乘 10。// 量程切换伪代码 if (freq 100) { mode PERIOD; // 切测周法 } else if (freq 50000) { gate 0.1; // 缩短闸门 freq count * 10; // 换算回实际频率 } else { mode FREQ; // 保持测频法 }逻辑说明判断阈值 100 和 50000 是经验值可以根据实际精度要求调整。切换时要注意先停计数器、清溢出变量再改模式否则会读到半截数据。5.3 显示刷新与测量节拍的解耦一个容易被忽略的细节测量和显示如果放在同一个循环里显示刷新会拖慢测量节拍。更好的做法是测量在中断里完成主循环只负责把freq变量刷到数码管上。这样即使显示有 8ms 的扫描开销也不影响闸门计时的准确性。void main() { timer0_init(); timer1_init(); while (1) { display(freq); // 主循环只管显示 } }逻辑说明freq在 T0 中断里更新主循环读取时可能读到更新中的值但因为freq是 16 位变量51 上读写不是原子的极端情况下会读到高低字节不一致的值。严谨做法是在读取前关中断读完再开。EA 0; unsigned int f freq; EA 1; display(f);参数说明关中断的时间只有几条指令不会影响 T0 的定时精度但能保证显示值的一致性。这个技巧在 51 上做任何多字节共享变量时都适用。本文还有配套的精品资源点击获取