ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

基于Proteus仿真的低频数字式相位测量仪设计

基于Proteus仿真的低频数字式相位测量仪设计 简介一套完整的基于单片机与Proteus仿真的低频数字式相位测量仪设计方案适合电子设计竞赛、单片机课程设计及毕业设计参考。系统以51单片机为控制核心覆盖100Hz1kHz方波信号的频率测量与相位差测量支持3.35V输入幅度频率测量相对误差≤1%相位差测量绝对误差≤5°显示范围0°359.9°、分辨力0.1°指标明确便于逐项验证。压缩包共19个文件、约279KB包含Proteus仿真工程、C语言源程序、HEX烧录文件、赛题PDF、仿真截图和文本说明等仿真工程可直接打开查看电路与运行效果源程序与HEX文件便于阅读、修改及烧录截图和PDF用于核对结果、理解题目要求。已有405人学习浏览覆盖从需求分析、代码编写到Proteus仿真验证的完整流程可帮助读者掌握频率测量、相位差计算及显示模块配合等要点适合课程设计、竞赛训练与二次开发。1. 低频数字式相位测量仪先把Proteus仿真图当调试台拿到基于单片机Protues仿真的低频数字式相位测量仪仿真图、源代码这类题目多数人会先纠结硬件电路但我的建议是先把仿真图跑通。低频段1Hz100kHz的相位差测量核心只有一件事把两路同频信号的相位差转成时间差用单片机定时器量成计数值。Proteus的VSINE信号源、虚拟示波器和数码管外设足以支撑完整闭环验证适合51单片机入门者、毕业设计选题以及想在买仪器之前验证算法的工程师。过零检测原理、仿真图搭建、C源码、调试排错这条路从顶层算到底层电路值得完整走一遍。2. 过零检测原理与Proteus仿真图上的信号调理电路2.1 相位差测量原理把时间差换算成角度两路同频正弦波之间如果有相位差反映在时间轴上就是过零点的位置错开了。设周期为T两路过零时刻的差为Δt相位差φ (Δt / T) × 360°。所以低频段测量相位差的关键只有两点一是准确找到过零点二是把Δt量出来并除以周期T。前者靠比较器把正弦波整形成方波后者靠单片机定时器计数。这里有个容易忽略的细节标准51单片机的INT0和INT1只支持下降沿触发或低电平触发不支持上升沿触发所以代码里统一用下降沿。LM393反相端接地、同相端接信号信号由正变负时输出从高跳低这个下降沿对应正弦波的负向过零点。两路信号都用同一边沿相位差计算完全不受影响但如果你在Proteus里用示波器观察比较器输出心里要清楚这个边沿和过零点的对应关系。2.2 VSINE加LM393比较器前级整形电路的Proteus连接Proteus仿真图里的前级调理常见做法是放两个VSINE信号源模拟被测信号一个相位设0°另一个设30°或60°分别经过两片LM393比较器整形成方波输出端接上拉电阻到VCC再送入单片机的外部中断引脚。电路连接一句话VSINE1 → U2:A输出 → INT0VSINE2 → U3:A输出 → INT1两个比较器输出端各接一只10kΩ上拉。LM393是开集电极输出高电平靠外部上拉电阻拉起来仿真图里漏掉上拉输出高电平会直接悬空接进单片机中断引脚的信号就是一片混乱。另一个常见问题是电源引脚Proteus里LM393的VCC和GND默认是隐藏引脚很多人不知道怎么添加引脚其实右键元件选Edit Properties勾选Display Hidden Pins就能显示出来否则只能靠全局电源网络自动连接一旦网络名对不上比较器输出就永远低电平查起来非常费时间。2.2.1 比较器输入端的限幅与参考点选择比较器输入端建议各串一个1kΩ电阻再进同相端反相端直接接地。这样正弦波负半周不会对输入级产生过大负压Proteus模型对负压容忍度比真实芯片宽松但真实硬件上这个电阻能保护比较器输入养成习惯没有坏处。信号源内阻默认是零串入限流电阻不会改变过零判断的阈值因为反相端参考电压固定在0V输入信号过零时比较器翻转的时刻不会因串联电阻而偏移。2.3 Proteus元件对照表与51单片机引脚分配AT89C51或AT89C52是这类仿真项目的默认选择。STC系列在Proteus里缺少完整仿真模型硬用STC型号反而容易在加载HEX时报错STM32在Proteus 8.9以上版本能跑部分F103外设仿真但定时器捕获加外部中断的组合远没有51模型成熟做相位测量这种依赖精确计时的项目AT89C51仍然是最稳的选择。资源占用和引脚分配如下表。功能模块引脚/资源说明方波输入AP3.2 (INT0)外部中断0下降沿触发方波输入BP3.3 (INT1)外部中断1下降沿触发时间基准定时器T016位方式对机器周期计数周期测量T0溢出计数记录相邻边沿的完整周期显示P0/P2段选和位选接共阴数码管按键P3.4/P3.5量程切换或测量模式选择两个外部中断的触发方式必须一致一个下降沿一个上升沿会引入固定90°偏差。T0的计数频率取系统时钟的12分频Proteus里把晶振设为12MHz计数分辨率就是1μs对100kHz以内的信号足够。AT89C51的P0口是开漏输出做数码管段选时必须接上拉排阻仿真图里漏掉这组排阻显示会明显偏暗。提示双击单片机设置Program File和Clock Frequency时确认频率真的改成了12MHz。Proteus默认值有时是1MHz定时器计数间隔变成12μs程序里所有除法结果都会偏大。3. 相位测量仪源代码双中断加定时器算出相位差3.1 程序初始化T0定时器与INT0/INT1下降沿触发源码整体分成初始化、中断服务、主循环三块。初始化里把T0配成16位定时器并启动INT0和INT1配成下降沿触发中断服务只负责记录时间戳把计算量留在主循环避免在中断里做除法拉长响应时间。#include reg51.h unsigned int t1_count; // INT0到达时T0的计数值 unsigned int t2_count; // INT1到达时T0的计数值 unsigned int last_t1; // 上次INT0的计数值用于算周期 unsigned int period_count; // 一个完整周期的计数值 unsigned char phase_flag 0; // 一次测量完成的标志 void timer0_init(void) { TMOD 0x01; // T0方式116位定时器软件启停 TH0 0x00; TL0 0x00; ET0 1; // 允许T0溢出中断 EA 1; TR0 1; // 启动T0 } void ext_int_init(void) { IT0 1; // INT0下降沿触发 IT1 1; // INT1下降沿触发 EX0 1; EX1 1; }TMOD 0x01的含义是低四位0001GATE为0定时器只由TR0控制启停C/T为0选择定时功能M1M0为0116位不自动重装。T0从0开始加1计满65536产生溢出中断。TR0在初始化里直接置1让T0全速运行两个中断到达时各自读取当前计数值差值就是时间间隔。这里不推荐用GATE门控方式让硬件自动启停定时器因为INT0同时承担了触发读取的任务两者混用会引入额外的边沿判断歧义。3.2 中断服务函数记录时间戳并处理16位回绕16位寄存器读取有个经典坑TL0先加满进位到TH0如果主程序先读TH0再读TL0恰好赶上进位瞬间读出的值会错得离谱。标准做法是先读TH0再读TL0然后重新读TH0比较两次不一致说明进位发生了重读一遍。void ext0_isr(void) interrupt 0 { unsigned char th1, th0, th2; do { th1 TH0; th0 TL0; th2 TH0; } while (th1 ! th2); t1_count ((unsigned int)th1 8) | th0; } void ext1_isr(void) interrupt 2 { unsigned char th1, th0, th2; do { th1 TH0; th0 TL0; th2 TH0; } while (th1 ! th2); t2_count ((unsigned int)th1 8) | th0; phase_flag 1; // 通知主循环计算 }do-while重读逻辑在这段代码里的作用是保证时间戳读取的原子性。中断服务里只做寄存器读取和标志位置位不做乘除和浮点运算这样INT1中断到达的时间戳读取延迟被压到几个机器周期以内相位差测量的一致性才能保证。phase_flag置1后主循环才允许读t1_count和t2_count做计算避免主循环在下一次中断写入一半时读到脏数据。3.3 主循环里的相位差与频率计算公式拿到两个时间戳后相位差的原始表达式是Δt t2_count - t1_count。如果t2_count小于t1_count说明T0已经溢出回绕差值要加上65536修正。周期T同样靠计数得到在INT0的两次相邻中断里记录间隔period_count就是T对应的计数值。最后φ (Δt / period_count) × 360°。signed int calc_phase_diff(void) { long delta; delta (long)t2_count - t1_count; if (delta 0) { delta 65536L; // 溢出回绕修正 } return (signed int)((delta * 360L) / period_count); }这里用long做中间运算是必需的。delta最大接近65535乘360后约2360万超出16位int范围直接相乘会溢出成负数算出来的相位角完全不可信。C51对long的运算支持完整在这个量级上性能损耗可以忽略。period_count在INT0中断里通过last_t1与当前t1_count的差值获得注意也要做同样的回绕修正频率则直接用12MHz除以12再除以period_count单位是Hz。3.4 数码管动态扫描与显示缓冲区显示部分用动态扫描实现每隔2ms切换一个位选循环点亮四位数码管。显示缓冲区定义为数组每一位存09的段码段码表用code关键字放进程序存储区节省内部RAM。unsigned char code seg_table[10] { 0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F }; void display_refresh(void) { unsigned char i; for (i 0; i 4; i) { P0 seg_table[disp_buf[i]]; P2 (unsigned char)(0x01 i); delay_ms(2); } }delay_ms用简单的嵌套循环实现即可Proteus仿真的时间基准本身受主机性能影响不需要精确延时。如果显示刷新放在主循环里而主循环同时在算相位差会出现明显闪烁解决办法是把显示刷新挪到T0溢出中断里用定时中断驱动扫描保证刷新率恒定。disp_buf在每次计算完成后更新低位存小数位高位存整数位。4. Proteus仿真调试信号源参数、波形观察与发散规避4.1 信号发生器VSINE的相位和频率设置在Proteus左侧工具栏选择Generator模式放置两个VSINE双击进入属性设置。两个信号源的Frequency设为相同值比如1kHzAmplitude设为5V。第二路信号的相位偏移直接在Phase一栏填度数比如30Time Offset保持0。需要说明的是Phase和Time Offset同时设置时两者会叠加所以习惯上只用其中一种。低频测量建议把频率设在1kHz到10kHz之间仿真速度最快频率太低时T0计数周期拉长仿真步数暴增Proteus跑起来明显卡顿频率太高则要担心比较器模型带宽跟不跟得上。4.1.1 相位正负与边沿顺序的确认方法Phase填正值时信号超前还是滞后取决于比较器接法和中断沿的选择文字上绕不清楚。稳妥做法是先不接单片机用虚拟示波器同时观察这两路输出确认下降沿的先后顺序与预期一致再接进INT0和INT1。仿真图调试阶段多花这一步能省下后面排查中断顺序的时间。4.2 虚拟示波器看整形波形先确认边沿再进单片机虚拟示波器在右侧虚拟仪器面板里拖入后把通道A、B分别接到两个比较器输出端。触发方式选自动1kHz信号用0.5ms/格可以看见两个完整周期。输出端如果看到的是缓慢变化的三角波而不是陡峭方波多半是上拉电阻阻值过大10kΩ在低频下没问题换成100kΩ边沿明显变缓这会影响中断触发的时刻一致性。比较器输出恒为低电平的排查顺序是这样的先看电源引脚是否真的连接到VCC和GND再看上拉电阻是否接在输出端和VCC之间最后确认输入信号幅度是否超过了比较器的共模范围。Proteus模型里这几个环节任何一个出错表现都是输出不动查起来最容易漏掉的是隐藏电源引脚的网络归属。4.3 HEX文件加载与时钟频率配置双击单片机在Program File一栏选择Keil编译生成的HEX文件这是仿真能否运行的第一步。Clock Frequency默认值往往不是12MHz改成12MHz后T0每1μs计一个数程序里的频率计算才和现实一致。提示运行后单片机完全没反应先检查Program File是否真选到了HEX再检查VCC电压是否为5V。这两项在Proteus的51模型里优先级最高。4.4 仿真发散与输出恒低的常见诱因仿真发散在Proteus里的表现是波形上出现高频毛刺振荡或者直接报不收敛错误。对相位测量仪这类带比较器的电路常见诱因有三个比较器正反馈环路里没有滞回电阻过零点附近反复翻转信号源频率设太高仿真步长跟不上翻转速度电路里存在没有初值的电容。规避方法对应是加滞回电阻、调低信号频率、给电容设初始条件。仿真发散基本都指向电路模型问题而不是仿真器性能不要一上来就改求解器参数。仿真现象常见原因处理方波边沿毛刺抖动比较器无滞回输出端接1MΩ正反馈到同相端报错不收敛步长过小或无初值调大步长电容设初始电压输出恒低电平上拉缺失或电源悬空查10kΩ上拉和隐藏电源引脚波形边沿变缓上拉电阻过大降到10kΩ以下4.5 用虚拟终端监控中间变量程序逻辑错误在仿真里比硬件问题更难定位。我一般会在关键计算完成后把t1_count、t2_count和计算出的相位值通过串口发出来Proteus的Virtual Terminal直接挂在P3.0和P3.1上观察。串口需要T1产生波特率初始化里TMOD就要从0x01改成0x21这两个定时器的配置在同一个寄存器里改的时候别把T0的方式位覆盖掉。printf输出重定向到串口只需重写putchar函数char putchar(char c) { SBUF c; while (!TI); TI 0; return c; }波特率在Proteus虚拟终端里不用太较真12MHz晶振配TH1 0xFD得到的约9600bps有少量误差虚拟终端一般也能正确接收。如果后续要移植到真实硬件换11.0592MHz晶振同样TH1 0xFD就是标准9600bps。5. 相位测量仪的零点校准与误差修正技巧5.1 自检流程消除系统零偏把两路输入端短接到同一个信号源理论上相位差应该显示0°实际会读出几度偏差。来源有两个INT0和INT1的中断响应延迟不完全一致两路比较器传输延迟有差异。Proteus仿真里第一个来源占主导。上电后做一次自检两路短接测得一个offset值后续每次测量结果都减去这个offset。实测相位 原始计算值 - 自检offset这个流程在校验阶段比任何滤波都有效。5.2 提高分辨率的均值滤波与1T定时器第一种手段是多次测量取平均连续采样16次去掉最大最小值再平均。Proteus信号源稳定无噪声取平均对纯正弦波意义不大但代码结构对后续移植真实硬件很有价值。第二种是提高定时器分辨率12MHz标准51的1μs分辨率对1kHz信号相当于0.36°对10kHz信号则是3.6°误差随频率线性放大。改成1T模式分辨率提升12倍代价是定时器溢出更频繁溢出中断里要做32位计数扩展代码复杂度明显上升。低频量程内标准12T模式够用改1T前先确认测量上限频率真有必要。5.3 滞回比较器消除过零抖动开环比较器在正弦波过零附近会有输出抖动这是高增益放大器的固有特性。在输出端和同相输入端之间接一只1MΩ到2MΩ的正反馈电阻形成滞回窗口。窗口大小由反馈电阻和信号源内阻决定低频下这几毫伏的迟钝对过零时刻的偏移可以忽略但换来的边沿稳定性是实打实的。仿真里如果方波边沿出现大量毛刺这个电阻是最直接的解法。最后给一个验证用的收尾技巧把第二路VSINE的Phase分别设成0°、45°、90°、135°跑完记录显示值和设置值的偏差画一条偏差曲线。偏差在低频段呈线性趋势可以直接用软件线性拟合作修正如果偏差在中频段出现拐点优先怀疑比较器带宽或中断触发沿配置。这条偏差曲线存档之后每次改频率或换量程直接对照它判断是算法问题还是前端电路问题不用重新盲调。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进