资讯动态

基于AT89C52的10MHz数字频率计:分频、自动量程与误差控制

发布时间:2026/9/17 17:33:22 来源:尧图企业网站定制
简介这是一份面向电子信息技术、近代电子学实验等课程设计场景的单片机应用设计报告围绕AT89C52实现1Hz10MHz自动量程数字频率计覆盖频率测量、放大整形、分频、脉宽测量、显示锁存等完整设计流程适合电气/电子类专业学生做课程设计、毕业设计或实验报告参考。资源共1个文件为doc格式设计说明压缩包约1.06MB内容以设计任务、测量原理、模块电路分析、仿真截图和程序控制思路为主文件集中便于直接查阅。文档详细展示了74LS90分频、74LS273锁存显示、74LS153量程切换、GATE位脉宽测量以及放大整形电路的计算与调试方法并配有分频、整形等电路仿真截图可帮助读者理解单片机测量系统从硬件到控制的完整实现路径。目前已有99人学习浏览对有同类课程设计或单片机测控项目需求的学习者具有直接参考价值。1. 单片机数字频率计的关键坎AT89C52 的计数上限与 74LS90 分频单片机数字频率计看着只是数脉冲实际上一旦把 1MHz 方波直接送到 AT89C52 的 T1 脚一秒内 16 位计数器会溢出十几次显示值完全不可信。课程设计题面写的是 1Hz 到 1MHz这套方案却用两片 74LS90 把上限扩到了 10MHz核心思路不是让单片机硬数高频而是先把信号分频到它能稳定处理的区间。对电子信息类专业的学生来说它把信号放大整形、门控定时、自动量程、锁存显示串成一条完整链路对做信号源校准的工程师来说里面 74LS153 换挡和回差处理也有可参考的地方。下面按原理、硬件、固件和误差四个方向拆开讲。2. 数字频率计的测频原理与 AT89C52 的计数上限频率测量最直接的定义是“单位时间内的脉冲数”。常见的做法是固定 1 秒闸门对输入信号的上升沿计数计数值在数值上就等于频率值。这个思路简单但放到 AT89C52 上会碰到两个限制第一引脚能识别的最高信号频率约为晶振频率的 1/2412MHz 晶振下就是 500kHz第二16 位计数器在 1 秒内最多记 65535 个脉冲不做软件扩展的话量程连 100kHz 都撑不住。所以必须同时在硬件上加分频在软件里扩展计数值。2.1 自动量程三档的计数关系方案把测量范围分成三档核心是让进入 T1 引脚的频率始终落在 100kHz 以内。这个值比 500kHz 物理上限低很多能留出余量也让 1 秒闸门内的最大计数值稳定在 100000 附近。分频比不同最终频率换算系数也不同。档位P1.1 P1.0分频比例输入频率范围1 秒闸门内 T1 最大计数值000不处理1Hz – 100kHz10000010110 分频100kHz – 1MHz100000210100 分频1MHz – 10MHz100000注意第二档和第三档在分频之后进入 T1 的引脚频率仍然在 100kHz 以下。第一档虽然允许直接输入到 100kHz但 16 位计数器最大只有 65535依然会溢出。所以下一节要做的软件扩展是必选项不是锦上添花。2.2 T1 溢出中断扩展把 16 位计数器当成 24 位用AT89C52 的两个定时/计数器 T0、T1 都支持外部脉冲计数但寄存器只有 16 位。要在 1 秒闸门内安全地数到 100000就得在 T1 每次溢出时进中断把软件计数器加一。读取最终频率时用溢出次数乘以 65536再加上 TH1/TL1 的当前值就得到完整计数值。// 频率测量模式T0定时10msT1外部计数 void freq_mode_init(void) { TMOD 0x51; // T1:方式1外部计数T0:方式1定时 TH0 0xD8; // 10ms定时初值12MHz晶振 TL0 0xF0; // 65536 - 10000 0xD8F0 TH1 0x00; // T1计数清零 TL1 0x00; ET0 1; // 开T0中断 ET1 1; // 开T1溢出中断 EA 1; // 开总中断 TR0 1; // 启动定时 TR1 1; // 启动外部计数 } unsigned int ovf_cnt; void timer1_isr(void) interrupt 3 { ovf_cnt; // 每次溢出代表又多计了65536个脉冲 }TMOD 的 0x51 换算成二进制是 0101 0001高四位对应 T1低四位对应 T0。低四位 0001 表示 T0 工作在方式 1 的 16 位定时器高四位 0101 表示 T1 工作在方式 1 的 16 位计数器。T1 在计数模式时持续采样外部引脚跳变每检测到一个上升沿TH1/TL1 加 1。溢出中断只负责把软件高 16 位补上最终得到类似 24 位的计数值。实际工程里读取 T1 时要先停 TR1 再读避免计数器一边跳一边读这个问题在 4.2 节会专门说。2.3 GATE 位复用用同一颗单片机测脉宽测频之外还要测脉宽方案利用的是 TMOD 的 GATE 位。GATE 置 1 后定时器的启动条件从“TRx1”变成“TRx1 且 INTx 引脚为高”。这样一来INT0 引脚持续为高的时间就是定时器的实际开门时间。配置语句很少。// 脉宽测量模式T0方式1GATE1 void pulse_mode_init(void) { TMOD 0x09; // 0000 1001T0方式1且门控打开 TH0 0x00; // 从0开始计数 TL0 0x00; TR0 1; // 允许计数但需INT0为高才真正计数 }12MHz 晶振下机器周期是 1us定时器每个计数就是 1us。因此测量结束后读出的 TH0/TL0 直接就是脉冲宽度的微秒数。需要留意的是方式 1 最大只有 65535us约 65.5ms如果被测脉宽更长同样要用溢出中断扩展。这个 GATE 位的用法是很多 51 单片机教材里容易略过的地方实际做脉宽测量时非常顺手。提示频率测量模式和脉宽测量模式共用同一组定时器寄存器切换模式前先 TR0/TR1 清零避免中断里读到半个闸门的数据。3. 放大整形、74LS90 分频与 74LS273 锁存显示硬件链路被测信号不一定是方波题目要求里写了输入可以是 10mV 到 3V 有效值的正弦波。单片机不认识正弦波只认 TTL 电平的上升沿和下降沿所以前端必须完成两件事把信号幅度抬到门限以上再把波形变成方波。之后才能接分频和显示电路。3.1 反相比例放大和与门整形参数要按最小输入来定放大电路由集成运放组成反相比例放大增益 Au R4 / R3默认取 10 倍。后面接一个与门与门一端拉高另一端接放大后的信号。输入幅度高于与门阈值时输出高低于阈值时输出低于是正弦波被切成方波。如果输入最大到 3V 有效值放大 10 倍后会超过运放输出范围波形顶部会削顶。这不是不能工作削顶后过零跳变仍然有效但最好按最小输入幅度调整增益。R3 取 10k、R4 取 100k 时增益 10适合几十 mV 的小信号输入到几百 mV 以上时我会直接把 R4 改成 22k增益压到 2.2 倍避免运放进入饱和区。输入幅度R3R4增益说明10mV – 100mV10k100k10小信号输入时用100mV – 1V10k22k2.2中幅度常用1V – 3V10k10k1接近直接整形有一点必须提醒与门不是施密特触发器它只有一个固定阈值电压输入边沿有噪声时输出会产生抖动。课程设计里用与门是低成本做法如果后续想提高稳定性应换成 74HC14 施密特门整形效果会好很多。3.2 74LS90 双级分频与 74LS153 选通切换74LS90 是异步十进制计数器可以配置成 10 分频。第一片从输入 CLK 进、QD 出就是十分频第二片再接第一片的 QD从第二片的 QD 出就是一百分频。分频输出不能同时直接进单片机否则两路信号会互相影响所以要用 74LS153 四选一选择器。AT89C52 的 P1.0 和 P1.1 接到 74LS153 的 A、B 选择端三个状态分别选通直通、10 分频、100 分频。对应关系如下。P1.1P1.074LS153 选择进入 T1 的频率量程上限00输入直通原信号100kHz01通道 10输入 / 101MHz10通道 100输入 / 10010MHz切换选择端要用代码操作两个 IO不能靠硬件自动跳。也就是说量程判断必须在软件里完成分频器只是执行机构。切挡时注意等当前 1 秒闸门结束再改 P1.0/P1.1不然闸门中段混入不同分频比计数值既不是原频率也不是分频后的频率。#define DIV_BYPASS 0 #define DIV_10 1 #define DIV_100 2 sbit SEL_A P1^0; sbit SEL_B P1^1; void set_prescaler(unsigned char div) { SEL_A div 0x01; // 74LS153的A选择位 SEL_B (div 1) 0x01; // 74LS153的B选择位 }这样写的好处是上层逻辑只需要传DIV_BYPASS、DIV_10、DIV_100不需要关心引脚电平组合。如果后面换成 STC 或其他 51 单片机只要把 P1.0/P1.1 的 sbit 定义改掉即可。3.3 74LS273 锁存显示与小数点控制显示部分用 6 片 74LS273 配 6 个数码管。AT89C52 的 P0 口分时送出段码P2 口送出锁存信号让每一片 74LS273 在上升沿锁存当前 P0 数据。锁存之后即使 P0 去传送下一位数码管也不会闪这就是文档里强调的“锁存输出”比动态扫描稳定的原因。显示刷新可以按位写先送段码到 P0再选中对应锁存器并给一个上升沿。工程里常用一个数组保存当前 6 位数字。unsigned char code seg_tab[10] { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F }; void update_display(unsigned char *digits) { unsigned char i; for (i 0; i 6; i) { P0 seg_tab[digits[i]]; // 数字转段码 P2 (P2 0xF8) | i; // 选中第i个锁存器 P2 (P2 0xF8) | (i | 0x08); // CLK上升沿锁存 P2 (P2 0xF8) | i; // 拉低CLK准备下一个 } }这里的思路是用 P2 低三位选锁存器编号P2.3 当 CLK 使用。先给数据再给一个上升沿数据就被锁住。段码表对应共阴数码管 a-g-dp如果换共阳数码管需要把表中每个值按位取反。小数点的位置由 P1.2、P1.3、P1.4 控制和分频器选择端的原理一样不放进刷新函数里处理。如果改用 LCD锁存逻辑就不需要了改为把频率值按位拆成字符直接写 LCD 模块。4. 频率计固件实现1s 闸门、自动量程与脉宽测量固件最需要注意的是状态切换。测频和测脉宽用的是同一个定时器模块如果不先把中断关掉或者把寄存器恢复很容易出现上一档遗留的初值污染下一档的问题。下面按主循环、闸门计数、自动量程、脉宽测量四段展开。4.1 主循环的按键入口freq 和 pwide 怎么分配两个按键分别对应测频和测脉宽。按键按下时引脚为低释放时为高。为了保证按键只在按下瞬间触发一次通常在主循环里做一次电平判断加一次延时确认。#define KEY_FREQ P3_2 #define KEY_PWIDE P3_3 unsigned char mode; void key_scan(void) { if (KEY_FREQ 0) { delay_10ms(); if (KEY_FREQ 0) { mode MODE_FREQ; // 进入频率测量 } } if (KEY_PWIDE 0) { delay_10ms(); if (KEY_PWIDE 0) { mode MODE_PWIDTH; // 进入脉宽测量 } } }延时确认是简单的软件消抖10ms 已经能滤掉大部分机械抖动。如果按键接触电阻偏大可以改成 20ms。进入对应模式后主循环分别调用freq_measure_once()和pulse_width_measure_once()两个函数不要同时运行。4.2 10ms 定时中断拼出 1 秒闸门文档里写的闸门时间是 1 秒刷新时间也是 1 秒。一种标准做法是让 T0 每 10ms 中断一次软件计数到 100 次后认为 1 秒闸门结束。这样比直接定时 1 秒更稳中断里的固定开销也会被平均掉。unsigned int t0_cnt; bit gate_done; void timer0_isr(void) interrupt 1 { TH0 0xD8; // 重新装入10ms初值 TL0 0xF0; if (t0_cnt 100) { t0_cnt 0; gate_done 1; // 1秒闸门到 } } unsigned long get_count(void) { unsigned long n; TR1 0; // 先停T1避免读取过程中变化 n (unsigned long)ovf_cnt * 65536UL ((unsigned int)TH1 8 | TL1); TR1 1; return n; }读取 T1 计数时先停TR1这是很多人容易漏掉的一步。如果不先停TH1/TL1 可能在读取期间继续加 1导致高字节和低字节不是同一个时刻的值。更稳的顺序是“停计数器、读 TH1、读 TL1、恢复计数器”这一点在 51 单片机定时器相关课程里经常被忽略。4.3 自动量程先低挡试测超阈值再升挡测频的主循环先从旁路挡开始计数 1 秒。如果raw超过 100000就升到 10 分频挡再重新计一个 1 秒闸门。判断阈值用的不是最终频率而是进入 T1 引脚的原始计数值。当前挡位判断条件动作旁路raw 100000切 10 分频重新计数10 分频raw 100000切 100 分频重新计数10 分频raw 90000降回旁路带 10% 回差unsigned long raw, freq; unsigned char range DIV_BYPASS; while (1) { set_prescaler(range); // start_gate()清ovf_cnt、TH1/TL1置gate_done0开TR0/TR1 start_gate(); wait_gate_done(); raw get_count(); if (range DIV_BYPASS raw 100000UL) { range DIV_10; // 输入超过100kHz升到10分频 continue; // 重新开始一次完整闸门 } if (range DIV_10 raw 100000UL) { range DIV_100; // 超过1MHz升到100分频 continue; } break; // 当前挡位计数合适 } switch (range) { case DIV_BYPASS: freq raw; break; case DIV_10: freq raw * 10UL; break; case DIV_100: freq raw * 100UL; break; }这里的升挡逻辑在高频时会占用两个以上的 1 秒周期。比如一个 5MHz 输入信号第一秒在旁路挡计数发现超限第二秒在 10 分频挡计数可能还是超限第三秒在 100 分频挡才读到正确值。也就是说自动量程的刷新时间不总是 1 秒这是“自动量程”和“1 秒刷新”之间必须做的取舍。如果课程设计要求严格 1 秒刷新可以把第一秒当成预扫描不显示第二秒直接显示结果。4.4 脉宽测量利用 INT0 的电平门控脉宽测量用 T0 的门控方式GATE1 时只有 INT0 为高电平T0 才计数。要测一个高脉冲宽度流程是等待信号变低清空计数器开 TR0然后等上升沿开始计数等下降沿停止。unsigned long measure_pulse_us(void) { unsigned long cnt; TMOD 0xF0; // 只动T0保留T1原来的配置 TMOD | 0x09; // T0方式1GATE1 TH0 0x00; // 计数值清零 TL0 0x00; while (INT0 1); // 如果当前是高电平先等低 TR0 1; // 允许T0计数但INT0为低时不会动作 while (INT0 0); // 等待上升沿计数自动开始 while (INT0 1); // 等待下降沿计数自动停止 TR0 0; cnt ((unsigned int)TH0 8) | TL0; return cnt; // 12MHz晶振下单位是us }这段代码里while (INT0 1)的作用很重要。如果测频刚结束INT0 上还留着高电平直接开门会导致后半段高电平也被计进去。先等低电平才能保证测量的起点是上升沿。返回的cnt直接就是微秒数。测完后必须把 TMOD 恢复到0x51否则下一次测频会用脉宽模式。注意INT0 输入不能直接接高压信号。被测电压超过 5V 时先经过前面 3.1 节的放大整形再进 INT0否则会损坏单片机引脚。5. 频率计误差校验和量程切换回差技巧频率计做完后不能只看信号源上的数字。我一般会把信号源和频率计接到同一个输出端从低频到高频选几个点读出显示值并计算差值。文档里最清晰的实测点是 567.890kHz显示为 567.888kHz相差 2Hz相对误差约 0.00000352%。这个误差主要来自 ±1 计数误差也就是闸门开启时刻和输入脉冲边沿不同步造成的计数差。信号源频率频率计显示绝对误差相对误差567.890kHz567.888kHz2Hz0.00000352%理论 ±1 计数误差--≤ 1 / 567888 ≈ 0.000176%2Hz 是实测误差理论 ±1 误差反而更大说明晶振和整形电路在这个频点没有额外拖后腿。频率越高计数 N 越大相对误差越小这也是高频档位误差百分比反而不高的原因。5.1 加一个回差值避免量程边界反复跳档自动量程最容易出问题的位置是 100kHz 和 1MHz 附近。信号源输出正好在边界上下抖动时频率计会在两个量程之间反复切换显示值来回跳。解决方法是在判断线上加一个迟滞区间升挡还是按 100000但降挡要低于 90000 才执行。if (range DIV_BYPASS raw 100000UL) { range DIV_10; continue; } if (range DIV_10 raw 90000UL) { range DIV_BYPASS; // 不是低于100000立刻降挡 continue; } if (range DIV_10 raw 100000UL) { range DIV_100; continue; } if (range DIV_100 raw 90000UL) { range DIV_10; continue; }90000 这个值按当前挡位的原始计数值判断相当于给量程边界留了 10% 回差。回差大小可以根据稳定度需求调整一般取量程上限的 5% 到 10%。如果现场信号本身有缓慢漂移可以把 90000 改到 85000如果信号很稳则可以改到 95000减少换挡次数。5.2 现场调试时优先检查的四个地方第一确认分频后的信号确实进了 T1 而不是 T0。本方案里 T0 用于定时接错引脚会导致计数永远为零或直接卡死。第二用示波器看 74LS153 的输出端分频比必须和 P1.0/P1.1 的电平组合一致。第三换挡后不要立即读计数值等一个完整的闸门周期再显示。第四测脉宽模式下如果 INT0 浮空GATE 逻辑会被噪声触发要么接上被测信号要么先让 P3.2 输出高电平。把这些检查项走完比反复改软件更容易定位问题。最后一个技巧把挡位、分频系数和回差阈值整理成一个结构体比如{range, divisor, up_th, down_th}。固件升级或换晶振时只改这一组表不需要在中断服务程序里翻逻辑。这样调完一个点剩下的点基本都能一次通过。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价