简介:一套基于51单片机与Proteus仿真的功率因数矫正电路设计资料包,面向单片机爱好者、电气相关专业学生及需要完成课程设计或毕业设计的开发者。方案实现电网电压电流测量、无功功率因数检测、电容阵列自动投切功能,并支持手动与自动两种模式,通过仿真验证系统可靠性与实用性。包内文件共52个,约3.32MB,主要包括Proteus仿真工程(pdsprj/pdsbak)、单片机C语言源代码(c/h/hex)、AD原理图(SchDoc)、设计论文(doc/pdf)及元件清单等,同时附有程序流程图与功能划分示意图,便于从原理、编程到仿真调试全流程对照学习。压缩包目录结构清晰,源程序、仿真文件及文档分层存放,资料完整度较高。已有40人浏览学习,适合作为功率因数校正类课题设计的完整参考方案。
1. 一个基于51单片机和Proteus仿真搭出来的功率因数校正电路,最终要回答的问题其实很具体
电费单里出现“力调电费”负金额时,你的负载到底超前还是滞后了多少度。电机、电磁炉、开关电源这类负载的功率因数普遍在0.6~0.8之间,控制器要做的不是改变负载本身,而是在电源侧补上可投切的电容,把电流波形往电压波形方向拉。51单片机在其中的任务被很多人简化成“检测相位差,控制继电器”,但实际做起来会发现难点在三个地方:过零检测电路能不能提供干净的跳变沿、相位差换算成功率因数的过程有没有累计误差、电容投切的触发时刻选在哪一拍。Proteus仿真在这里的价值不只是画一张能交差的图,它能让比较器的抖动、触发脉冲的宽度、互感器同名端接反这类问题在焊接前就暴露出来。下面按一个能复现的流程,从检测电路、源代码、仿真图一直讲到AD原理图落地前的改动。
2. 过零检测电路决定相位差测得到底准不准:比较器、光耦和参数表
2.1 电压过零与电流过零的信号采集:比较器方案和互感器方案的取舍
要测量电压超前电流多少度,最直观的参照物就是两个过零点。电压过零用降压后的正弦波过零点作为标志,电流过零则需要在负载回路里串电流互感器或采样电阻,把电流信号变成电压信号,再用比较器把过零点当作边沿跳变送入51的外部中断。采样电阻方案在几十安培回路里损耗明显,电流互感器方案几乎不增加主回路损耗,所以仿真和实物我都会优先选互感器。Proteus里用电流互感器模型时把变比设在1000:1,次级并联一个100Ω采样电阻,额定电流下次级电压峰值在1V左右,给后面比较器留足放大余地。
比较器选型上,LM393是这个题目里最常见的选择,但有两个坑必须避开。第一,LM393是集电极开路输出,输出端必须接上拉电阻,否则输出永远被拉在低电平,INT0收不到任何跳变。第二,它的输入共模范围只到VCC-2V,直接接带负半周的交流信号会把输入级打坏,所以正弦波进入比较器之前必须先经过电容隔直,再叠加一个2.5V直流偏置。典型接法是:降压后的信号经过5.1k与4.7k分压把工作点抬到2.5V,负输入端接一个2.5V参考电压,输出端用10k上拉到5V之后,接到51单片机的INT0引脚。
这里最容易忽略的是比较器本身的抖动。LM393没有内部迟滞,过零附近几十毫伏的噪声就会让输出在零点反复跳变,INT0中断被连续触发多次,最后算出的相位差完全不能用。解决办法是在比较器正输入端和输出端之间加一个1MΩ正反馈电阻,把翻转点做成一个几十毫伏宽的小窗口。窗口不会明显拉大相位误差,但能把抖动彻底滤干净,这是过零检测电路里最值得花时间调的一个电阻。
提示:LM393这类开集电极输出比较器,上拉电阻不能省,也没有内置迟滞,两处都处理好,过零信号才会稳定。
2.2 从过零时间差到功率因数:相位计算公式与一张常用参数表
相位差与时间差的关系是φ = 360° × Δt / T,50Hz工频下T=20ms。单片机里不需要同时既算度数又算cosφ,常见做法是把时间差先换算成相位角,再用查表法得到功率因数。下面这张表是功率因数治理中最常用的折算点,控制器里判断电容投切通常只看这些点,不需要把每个角度都精确展开:
| 功率因数目标 | 相位差角度 | 20ms周期内的时间差Δt | 对应常见设备状态 |
|---|---|---|---|
| 1.000 | 0.00° | 0 μs | 纯阻性负载 |
| 0.985 | 9.89° | 549 μs | 已充分补偿 |
| 0.950 | 18.19° | 1011 μs | 补偿合格的边界 |
| 0.866 | 30.00° | 1667 μs | 未补偿电机负载 |
| 0.707 | 45.00° | 2500 μs | 重感性负载典型值 |
| 0.500 | 60.00° | 3333 μs | 空载变压器附近 |
这张表的另一个用途是反推整个检测链路的延迟容限。如果目标是把功率因数从0.7提到0.95,相位差需要从45°压到18.19°,差值约26.8°。假设过零检测链路有1ms固定延迟,对应18°的相位误差,投切判据会彻底失效。所以在Proteus里验证仿真图时,第一步要用示波器看比较器输出边沿与正弦波过零点的间距,把这个固定延迟测出来,回填到代码里做软件补偿。
多补一句容量计算,论文里也常用:补偿电容容量Q = P(tanφ1 - tanφ2),P是有功功率,φ1和φ2是补偿前后的相位角。比如1kW负载从0.7补偿到0.95,tanφ1约1.02,tanφ2约0.33,需要约690Var补偿容量,对应220V下大约45μF的CBB电容组。
2.3 电容投切触发时刻:为什么选过零触发而不是延时移相
补偿电容投入电网的一瞬间,如果交流电压不在零点附近,电容上会出现很大的电流浪涌,多次投切会把晶闸管直接烧穿。所以电容投切普遍采用过零触发:把晶闸管的导通时刻限制在电压过零点附近,让电流从零开始平滑爬升。51单片机不需要自己做高精度移相,用带过零检测功能的MOC3061光耦晶闸管驱动器,单片机只输出一个“允许投入”的电平,MOC3061会等自身检测到电网过零后再触发晶闸管。
这个选择对51尤其有利。51的中断响应延迟是不确定的,软件移相方案需要在过零后的第N微秒触发晶闸管,任何一条中断嵌套都会让角度偏移;过零触发方案把这层不确定性从软件移到硬件,51只需要维持几百毫秒的使能电平,晶闸管触发的最终时刻由硬件内部电路决定,电网频率漂移也不影响可靠性。
3. 51单片机测相位差的源代码与投切判据怎么组织
3.1 没有输入捕获外设,就用两个外部中断加定时器打时间戳
STM32做输入捕获是硬件完成的,51没有这个外设,但两个外部中断配合一个自由运行的定时器完全可以实现同样的效果。思路是让T0以机器周期为单位自由计数,电压过零信号接INT0,电流过零信号接INT1,每次过零触发中断时,把当前TH0和TL0的组合值保存成时间戳,两个时间戳相减就是Δt。
T0初始化成16位定时器模式,不需要周期中断,只是让它在后台一直跑。12MHz晶振下机器周期是1μs,16位定时器最大量程约65.5ms,一个工频周期20ms在量程内,但电压过零和电流过零可能跨越一次回绕,所以时间戳要扩展成32位。用软件计数器配合TF0中断,每溢出一次记一个数,下面是完整可用的打时间戳代码:
unsigned char timer_overflow; // T0溢出次数,由中断维护 unsigned long timestamp_voltage; // 电压过零时间戳 unsigned long timestamp_current; // 电流过零时间戳 void timer0_isr(void) interrupt 1 // T0溢出中断 { timer_overflow++; // 每65536us加1 } unsigned long get_timestamp(void) { unsigned char ov1, ov2; unsigned int low; ov1 = timer_overflow; low = (TH0 << 8) | TL0; ov2 = timer_overflow; // 防止读取过程中发生溢出 if (ov1 != ov2) // 溢出中断把TH0/TL0归零,需要重读 low = (TH0 << 8) | TL0; return ((unsigned long)ov2 << 16) | low; } void int0_isr(void) interrupt 0 // 电压过零 { timestamp_voltage = get_timestamp(); } void int1_isr(void) interrupt 2 // 电流过零 { timestamp_current = get_timestamp(); }初始化时执行TMOD=0x01、TH0=0、TL0=0、ET0=1、EA=1、TR0=1,外部中断0和1都设为下降沿触发。get_timestamp里先读溢出次数再读TH0/TL0,是为了避免在读低字节时发生硬件溢出导致高低字节不一致。这个“先读高再读低”的顺序在51上有特殊情况,因为读TH0时TL0可能正在计数翻转,所以要靠读溢出次数前后是否变化来判断是否需要重读一遍。
两个中断服务函数里只保存时间戳,不做相位差运算,所有计算都放到主循环。这样中断服务函数耗时控制在几个微秒以内,避免前后两个过零中断互相干扰。
3.2 相位差换算功率因数的C代码与定时器回绕处理
得到两个32位时间戳之后,电流滞后电压是感性负载的典型状态,电流过零时间戳大于电压过零时间戳,差值就是Δt,单位是微秒。下面这段换算代码放在主循环的检测标志位之后执行:
unsigned int calc_phase_delta(void) { unsigned long delta; if (timestamp_current >= timestamp_voltage) delta = timestamp_current - timestamp_voltage; else delta = 0xFFFFFFFFUL - timestamp_voltage + timestamp_current; // 20ms对应360度,1us对应0.018度 // 返回值单位是0.1度,用于查表 return (unsigned int)((unsigned long)delta * 3600UL / 20000UL); }逻辑说明:delta的单位是微秒,20ms是20000μs,对应3600个0.1度。角度 = delta × 3600 / 20000,先乘后除,中间用unsigned long防止溢出。这里把单位定成0.1度,是为了让后面的cosφ查表在整数运算下完成。51的浮点库非常慢,如果直接调用cos()函数,一个周期只能算一到两次,查表法在速度和代码体积上都更合适。
查表可以按每1度存一个cos值乘1000后的整数,表长91字节,放在code区。查表时还要处理方向性:电流滞后电压说明是感性负载,相位差为正;电流超前电压说明已经过补,相位差为负。单片机里通常只取绝对值,超前或者滞后的符号位单独记录下来,投切逻辑靠这个符号区分“需要补容”和“已经过补”。
3.3 电容组投切判据:滞回比较避免晶闸管反复触发
电容投切最忌讳在临界点反复震荡。只用一个阈值判断“低于0.9投入,高于0.9切除”时,微小的噪声会让晶闸管在几秒钟内来回触发,电容反复充放电,寿命很快耗尽。标准做法是设置两条门限构成滞回区间:投入门限取cosφ=0.85,切除门限取cosφ=0.99,两条门限之间留出约0.14的滞回区。下面这段投切逻辑同时处理了防抖和分组控制:
#define CAP_GROUPS 3 sbit TRIAC0 = P1^0; // 电容组1触发 sbit TRIAC1 = P1^1; // 电容组2触发 sbit MOC_EN = P1^2; // MOC3061使能 unsigned char cap_count = 0; // 当前已投入组数 unsigned int confirm_counter = 0; // 越限确认计数器 void update_cap_groups(unsigned int pf_x1000) { if (pf_x1000 < 850 && cap_count < CAP_GROUPS) { if (++confirm_counter >= 15) { cap_count++; trigger_group(cap_count - 1); confirm_counter = 0; } } else if (pf_x1000 > 990 && cap_count > 0) { if (++confirm_counter >= 15) { cap_count--; release_group(cap_count); confirm_counter = 0; } } else { confirm_counter = 0; } }参数说明:850对应cosφ=0.85的投入门限,990对应cosφ=0.99的切除门限,两条门限之间的0.04差值就是最小滞回区宽度。confirm_counter表示越限确认次数,每次主循环里的功率因数测量完成后调用一次本函数,15次约300ms。延时期间如果测量值退回到滞回区内,计数器清0,这是最简单也最有效的防抖逻辑,实际运行中比任何滤波算法都直观。
trigger_group和release_group内部只做引脚置位和清零。需要强调一个容易理解错的点:晶闸管的触发时刻不是由51的这条置位指令直接决定的,MOC3061输入侧收到高电平后,要等自身电路检测到电压过零才输出触发窄脉冲,51输出的只是一个持续多个周期的使能电平,触发脉冲的形状和宽度由硬件完成。所以前面3.1节说的中断延迟抖动,在这个方案里对触发精度几乎不造成影响。
4. Proteus仿真图从元件清单到波形调通的完整路径
Proteus这个软件名经常被写成protues,搜索元件对照表的时候两种拼写都有内容,建议两套关键词都试。真正开始画仿真图之前,先想清楚主回路怎么供电,再动手放元件,能省掉大半返工时间。
4.1 元件对照表:建仿真图前先想清楚主回路怎么供电
Proteus仿真里220V交流可以不用隔离变压器,直接用VSINE组件当作电网,但进入单片机控制部分的信号必须经过降压、隔直、比较器整形,否则交流信号会把单片机引脚电位拉出工作范围。下面这张元件对照表适合直接照着搭:
| 电路位置 | Proteus元件名 | 参数/型号建议 | 绘图时注意点 |
|---|---|---|---|
| 单片机 | AT89C51 / AT89C52 | 12MHz晶振、22pF电容 | 复位电路用10μF+10kΩ |
| 电网电源 | VSINE | 幅值311V,频率50Hz | 幅值栏填峰值,不是有效值 |
| 降压输入 | TRAN-2P2S 或电阻分压 | 220V:5V变压器或2个100kΩ分压 | 分压后信号峰值控制在5V内 |
| 电压比较器 | LM393 | 开集输出,10kΩ上拉 | 输入加1MΩ迟滞电阻 |
| 电流互感器 | CT模型 | 变比1000:1,次级100Ω | 同名端接反会导致相位差符号反 |
| 光耦隔离 | 6N137或4N25 | 6N137延迟更小 | 输出端上拉到VCC |
| 双向晶闸管 | BT137或2N6075 | 2N6075门极触发电流更小 | 门极串220Ω限流电阻 |
| 过零驱动光耦 | MOC3061 | 带过零检测 | 与普通光耦MOC3021别混用 |
| 感性负载 | INDUCTOR+RES | 50mH+30Ω,可再并一组 | 用可变电感验证投切更直观 |
注意:Proteus里的VSINE属性填的是幅值,不是有效值。220V有效值对应311V幅值,直接填220会把输入电压压低约30%,整个仿真的触发点和功率数值都会偏。
P0口如果用来驱动数码管或发光二极管,必须外接排阻上拉。Proteus里用RESPACK-8画排阻,未连接的引脚在电气规则检查时会报warning,这个warning不要忽略,它在后面排查悬空引脚时很有用。
4.2 引脚添加、总线连接和虚拟示波器的三路接法
多路并行数据线建议用总线连接,尤其是P0口分给多个外设时。操作上先用左侧的Bus工具画一根总线,再给每条支路导线上标注相同的网络标号,比如P0.0到P0.7分别标成AD0到AD7,总线上对应引脚用AD[0..7]一次标完。网络标号是Proteus里连线的关键,元件引脚名不必与网络名相同,但同一网络的所有标号必须一字不差。
添加外部中断引脚时,右键元件选择“Edit Properties”,在“Alternate Functions”里确认INT0和INT1已经映射到对应引脚。仿真图上这一步遗漏的情况很多,画完检查时以为连到了P3.2,实际Proteus没有把INT0功能激活,结果中断永远不触发。
波形观察接法是整个仿真图调试的核心。虚拟示波器至少接三路:第一路放在电源输出端测电压,第二路放在电流互感器次级采样电阻上测电流,第三路接比较器输出观察整形后的过零脉冲。三路波形同时展开时,电压和电流过零点的相对位置可以直接目测。如果波形抖动得很厉害,先不要怀疑算法,右键点击示波器把Time/Div调到2ms档,采样点太稀时过零点会显得前后跳动,这个现象和代码无关。
4.3 仿真发散、复位和触发脉冲丢失的处理顺序
Proteus仿真最常见的三个故障按发生频率排序是:仿真发散、单片机不停复位、晶闸管不出触发脉冲。
仿真发散通常表现为运行几毫秒后画面出现数值溢出或波形乱跳。第一反应是调小仿真步长,在Advanced Simulation Settings里把Maximum Step从默认值改到10ns以下,同时把Relative Tolerance调松到0.01。晶闸管导通瞬间的dI/dt非常大,步长不够时数值积分直接发散。这也是为什么很多带可控硅的仿真图原理上没问题但跑不动。
单片机不停复位的排查顺序是:先断开外部中断和晶闸管触发引脚,只留最小系统跑一个闪灯程序,确认最小系统本身没问题;再把过零检测电路接回去,此时重点看比较器输出引脚在无信号输入时是否被上拉电阻稳定在高电平。如果断开输入后该引脚悬空,INT0会收到随机边沿,程序会像被“随机复位”一样反复跑飞。
触发脉冲丢失的排查要看晶闸管门极回路。MOC3061输入侧为高电平时,输出侧应该在电压过零附近出现一串窄脉冲,晶闸管被闩锁导通直到电流过零自然关断。仿真里看不到这串窄脉冲时,先查MOC3061输入侧的限流电阻,电阻太大导致输入侧电流低于触发阈值,输出侧永远不会翻转。
5. 从AD原理图落到实物前要改的几处,以及一个验证手段
5.1 电源和地的划分:仿真里的一根地砖到实物要拆成三组
Proteus里所有地用同一个GND符号,AD原理图也这么画会导致实物工作不稳定。过零检测的信号地、单片机数字地和晶闸管驱动的地应该分别命名成AGND、DGND和PGND,最后用0欧电阻在滤波电容处单点汇合。驱动晶闸管的信号来自MOC3061输出侧,这一部分用PGND,不要与单片机数字地混在一起回流,否则门极触发脉冲的尖峰会全部灌进INT0引脚。
5.2 晶闸管驱动与RC吸收参数在AD原理图里怎么定
AD原理图转PCB时,晶闸管驱动部分的参数要从仿真里的“能触发”升级成“可靠触发”。2N6075门极触发电流只有5mA左右,但门极触发电压要在1.2V以上。MOC3061输出侧的限流电阻要按310V峰值电压来算,而不是按220V有效值,峰值情况下电流会冲到正常值的1.4倍,这个差异直接影响限流电阻会不会发热烧断。
晶闸管两端并联的RC吸收电路,仿真里不画也能工作,但实物电网阻抗有分布电感,晶闸管关断瞬间的di/dt会在主回路感应出尖峰电压。RC吸收的典型接法是100Ω电阻与0.1μF/630V电容串联后,跨接在晶闸管两个主端子之间,R取22~100Ω,C取0.01~0.1μF,具体值要等实物示波器测到关断尖峰后微调。
5.3 验证手段:用Proteus图表模式量有功功率,不用肉眼读波形
很多人在最终验证时只展示电压电流波形图,说“相位差变小了”,这拿不出准数。Proteus的图表模式可以替代肉眼观察:新建一个Analogue Graph,把电源电压节点和负载电流支路分别作为两条迹线加进去,仿真结束后在Graph里用数学表达式P=V×I计算瞬时功率,再取平均值得到有功功率P,用P除以电压有效值与电流有效值的乘积,得到的就是比较准确的功率因数数字结果。
这样验证有两个好处。一是数字结果可以直接写进论文,示波器截图只是图形证据,而图表模式给的是可引用的数据。二是当代码查表结果和仿真测量出现差异时,图表模式能帮助定位误差来源是角度换算还是触发时刻抖动。调试完顺手把负载电感值改成原来的两倍,重新看功率因数能否在判定区间内自动投入一组电容,这是整个仿真里最接近实物运行的一个动作。
还可以做一个具体实验:在电压互感器次级串联一个RC低通,故意引入约30μs的延迟,再用图表模式对比加RC前后的功率因数读数。两次读数的差值就是过零检测链路延迟造成的系统误差,把它作为软件补偿值写进代码,比在理论上反复推算整个链路的延迟要可靠得多。
本文还有配套的精品资源,点击获取