STM32并网同步控制:从过零检测到软件锁相环的完整落地指南
第一次把逆变器的输出接到电网之前我盯着示波器上两个波形看了很久。一个是电网电压互感器引下来的正弦波一个是我板子上SPWM滤波之后的输出频闪看起来都在50Hz附近幅值也调得差不多了但我心里清楚直接合闸大概率要出问题。因为做的是STM32并网同步控制相位没有锁住一切都是白搭。那次的教训挺深刻后来我把“并网同步”这件事从头到尾梳理了一遍从过零检测到软件锁相环从捕获中断到SPWM相位映射一步步在STM32上落地。这篇就把整个链路掰开讲清楚电网同步到底在同步什么、过零检测为什么不够用、软件PLL怎么和硬件配合、代码怎么组织、实测又会踩到什么坑。想做并网逆变器、UPS、或者任何需要和电网“对齐相位”的电源类项目这份内容可以直接拿来当参考。1. 为什么要“同步”才算并网那笔冲击电流的账很多刚接触并网的人以为逆变器输出电压做到和电网一样的有效值频率调到50Hz就可以合闸了。这个想法很危险。交流系统里“一样”不是指有效值一样而是指任意时刻的瞬时电压要尽可能一致。也就是说电压波形不但要频率相同、幅值相同相位也必须相同。1.1 幅值、频率、相位三个条件差一个都不行假设电网电压是Va 311sin(ωt)逆变器输出电压是Vb 311sin(ωtθ)。合闸瞬间两个电源被硬接在一起开关回路里流过的是Va - Vb/ ZZ是线路、开关、变压器漏感加起来的回路阻抗。如果相位差θ是10度峰值电压差大约等于2311sin(5°)≈54V如果相位差拉到90度峰值电压差接近440V。这两个电压源的压差直接跨在很小的回路阻抗上冲击电流有多大不用我算你也知道。幅值差同样不能忽略。相位一致但幅值差10V相当于两个电压源之间串了一个微弱压差同样会形成环流。所以并网前输出侧的电压幅值、频率、相位都要往电网靠拢三者缺一不可。硬件上输出端一般会加预充回路或者软启动电路先把母线电容和输出滤波电容充到接近电网电压频率和相位的对齐就要靠我们今天说的同步控制来完成。1.2 频率偏差不是静态问题是持续积累的问题有人会说差0.1Hz能怎么样肉眼都看不出来。但0.1Hz的频率偏差每个周期会让相位多跑0.72度360*0.1/5010个周期就是7.2度相位差会一直累积下去。只要合闸瞬间不是恰好落在过零点附近总会在某个时刻撞上一个不小的相位差。所以并网同步的本质是让逆变器的输出相位“跟着电网走”而不是“差不多就行”。跟着走意味着每个周期都要测量电网频率每个周期都要修正自己的相位直到两者锁住。这也正是过零检测和软件锁相环要解决的核心问题。频率测量本身不算难难的是相位持续跟踪和抗干扰。2. 过零检测能测频率但撑不起严格并网最早的方案当然是过零检测。电网电压每周期有两次过零点检测到过零沿MCU就算出周期进而算出频率和相位起点。这个方案实现简单在很多产品里也确实够用但它有很明显的天花板。2.1 硬件回路怎么搭迟滞往哪加常规做法是电阻分压把220V降下来经过比较器整形成方波再隔离进MCU的外部中断引脚。比较器可以选普通的比较器芯片阈值设置在0V附近为了抗干扰必须加正反馈迟滞否则过零附近电网噪声稍微抖一下输出就会反复翻转MCU一次过零能误触发好几次。迟滞也不能太大。比如比较器迟滞设置成±5V折算到初级那实际触发点并不是电压等于0的点而是电压穿越-5V或者5V的时刻会带来一个固定的相位偏移。这个偏移可以通过软件校准掉但每次硬件参数变化都要重新标定挺麻烦。比较器之后建议加一级数字隔离或者高速光耦把强电侧和MCU侧彻底隔开别让一次雷击浪涌顺着引脚打进单片机。MCU侧我习惯用带有输入捕获的定时器而不是普通外部中断加软件计时。输入捕获能记录精确到定时器时钟周期的边沿时刻比外部中断里读SysTick或者定时器计数值靠谱得多也省CPU。2.2 误差主要来自哪里过零抖动比计数器误差大得多如果用84MHz定时器做输入捕获电网50Hz对应一个周期计数值大约是1.68M计数误差只有±1对应的频率分辨率非常理想。但实测下来频率测量不会那么干净误差大头全在过零抖动电网谐波、整流负载、比较器迟滞、光耦传输延迟都会让捕获到的边沿时刻偏离真实过零点。严重的时候过零时刻抖动个几百微秒甚至一两毫秒都正常。1毫秒在50Hz里是什么概念一个周期20ms1ms就是18度相位误差。带着18度的误差去合闸冲击电流已经很难看了。所以过零检测适合做“粗略同步”和“频率测量”不适合直接在工业现场那种谐波复杂的环境里当严格并网的相位依据。2.3 它适合干什么不适合干什么过零检测的定位是给系统一个绝对相位参考同时提供一个廉价的频率测量通道。配合软件锁相环之后过零信号的作用是校正累积误差而不是直接决定输出相位。开个表格看得更清楚项目过零检测软件锁相环相位精度受硬件抖动影响大依靠连续累加精度由采样率决定抗谐波能力差过零毛刺多好误差经过滤波和PI收敛频率跟踪每个周期更新一次每个采样点都在微调实现复杂度低中典型定位粗同步、频率测量、参考基准精细相位锁定、并网核心可以看到过零检测不能省但只靠它也不行。真正让相位稳定锁住的是软件锁相环。3. 软件锁相环把相位从“粗略对齐”做到“稳定锁定”软件锁相环的思路是用一个相位累加器模拟自己的“虚拟正弦波相位”每个采样周期累加一个固定步长步长由电网频率决定。同时用过零信号作为绝对参考点不断比较虚拟相位和真实电网相位的偏差用PI调节器修正步长。这样既不依赖单次过零的精度又能持续跟踪频率变化。3.1 相位累加器与过零参考怎么配合我用一个32位变量phase_acc表示相位2^32对应360度。这样相位累加天然具备回绕能力加减误差也不用担心溢出。假设ADC每个工频周期采样256点采样频率就是50*25612.8kHz那每个采样点相位应该增加2^32/25616777216。如果测得电网频率是50.2Hz相位步长就需要相应上浮否则虚拟相位就会慢慢跑偏。过零信号在这里扮演“校表”的角色。每检测到一次过零理论上电网相位是0度对应phase_acc应该是0。如果实际phase_acc是500万说明虚拟相位走快了如果是-300万说明走慢了。这个差值就是相位误差喂给PI调节器让它去微调phase_step。3.2 相位误差的PI修正跟车不是急刹车刚开始我做这个PLL的时候想过一个偷懒做法过零来了直接把phase_acc清零相位不就对齐了实测发现这个思路不对。直接清零相当于在每个周期过零点把相位硬掰回0但两个过零之间相位仍然可能漂移而且过零本身有抖动清零策略等于把抖动直接引入相位输出PWM相位会一颤一颤的。正确做法像开车跟车前车刹车灯亮了你不该一脚刹车踩死而是轻收油门、观察车距变化让车距慢慢恢复正常。也就是说相位误差要通过PI调节器换算成频率修正量叠加到50Hz基础频率上。误差大就多修正一点误差小就少修正一点误差被逐步收敛而不是被生硬打断。比例项负责快速响应积分项负责消除稳态误差积分项要做限幅防止长时间失锁时积分饱和等电网恢复后又震荡半天。具体计算里还有一个坑就是相位误差要处理回绕。phase_acc和期望值0的差值可能接近2^31如果不把误差映射到-180度到180度范围内PI调节器会被一个大数吓到输出瞬间饱和。所以每次取误差要判断它是否超过2^31超过就减去2^32确保误差永远在一个周期内。3.3 单相并网的两种PLL实现路线查表法与SOGI法单相系统没有天然的两相正交信号要用锁相环一般走两条路。一条是查表法就是我上面说的相位累加器加过零修正相位直接作为正弦表索引生成SPWM。优点是直观、运算量小适合在STM32F103这种资源一般的芯片上跑缺点是电压畸变严重时过零参考本身带着误差锁相精度会受影响。另一条是SOGI法用一个二阶广义积分器构造出和电网电压正交的虚拟信号然后做dq变换在同步旋转坐标系里锁相。这种方法抗谐波能力强是工业变频器和并网逆变器里非常常见的方案但计算量明显更大每个采样周期要做几次浮点乘加在Cortex-M3上跑到十几kHz的采样率会有点吃力换成带FPU的M4或者M7芯片才合适。做产品选型时我的建议是验证原理、做样机用查表法就够了如果目标是复杂电网环境下的产品直接上SOGI-PLL别在方案上省事。4. STM32侧的外设分工与同步采样链路搭建软件锁相环再漂亮最后也要落到STM32的外设资源上。这里最忌讳的是“一个定时器干所有事”。我见过有人把PWM、输入捕获、软件计时全塞进一个定时器最后中断优先级和资源共享把自己坑惨了。合理分工是第一步。4.1 哪些外设各干什么是先决条件外设职责说明TIM1/HRTIM生成SPWM中心对齐模式互补输出加死区适合全桥逆变TIM2触发ADC采样TRGO事件同步ADC注入通道时机固定ADC1DMA采集电网电压/输出电压由TIM2触发DMA搬运不占CPUTIM5输入捕获电网过零沿高精度记录边沿时间计算电网频率EXTI过零边沿中断可复用比较器输出或者由捕获事件统一处理GPIO继电器/接触器控制并网合闸输出芯片选型上F103系列能跑通整个方案但要注意ADC采样率做不高建议用G431这类带内置比较器、多路高级定时器的型号。G系列的内置比较器可以直接配合DAC阈值做电网过零检测省掉外部比较器芯片硬件精简很多。当然如果手里只有F103也可以做只是模拟部分的坑要多花时间填。4.2 将PWM中心对齐、ADC同步触发、DMA搬运串成一条链这里有个细节值得单独说ADC采样必须和PWM同步不能想采就采。全桥逆变输出经过滤波之后电压相对干净但PWM开关纹波还是会对采样造成干扰。如果在PWM边沿附近触发ADC采到的值会带着很大的开关噪声如果在中心对齐的计数零点触发正好是开关纹波的谷底采样值最稳定。所以用TIM1做主PWM把它的TRGO事件配置成更新事件再触发TIM2产生ADC启动信号ADC采样完成之后DMA自动把数据搬到内存整个过程CPU只在中途做一次PLL运算效率非常高。这个链路一旦搭好后面调PLL的时候你会感谢这个决定——采样点固定了相位计算的一致性才能保证。否则每个周期采样时刻漂移PLL算出来的相位误差里混进了采样延迟误差怎么调PI都不干净。5. 核心代码落地从过零捕获到SPWM相位映射代码部分我按实际项目里的组织方式拆成三块频率测量、相位累加和SPWM映射、过零误差修正与并网判定。只保留核心逻辑工程化封装略去。5.1 定时器输入捕获测量电网频率用TIM5的通道1做输入捕获捕获上升沿。两个上升沿之间的时间就是电网周期。// TIM5_CH1 输入捕获初始化HAL库 static void zero_cross_tim5_init(void) { TIM_IC_InitTypeDef sConfigIC {0}; sConfigIC.ICPolarity TIM_INPUTCHANNELPOLARITY_RISING; sConfigIC.ICSelection TIM_ICSELECTION_DIRECTTI; sConfigIC.ICPrescaler TIM_ICPSC_DIV1; sConfigIC.ICFilter 0x02; // 轻微数字滤波 HAL_TIM_IC_ConfigChannel(htim5, sConfigIC, TIM_CHANNEL_1); HAL_TIM_IC_Start_IT(htim5, TIM_CHANNEL_1); }捕获回调里连续取两次捕获值相减得到一个周期对应的计数值。用无符号减法不需要手动处理回绕C语言自然会按模运算处理static uint32_t cap_old 0; static float grid_freq_hz 50.0f; void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim ! htim5) return; uint32_t cap_new HAL_TIM_ReadCapturedValue(htim5, TIM_CHANNEL_1); uint32_t diff cap_new - cap_old; // 自动处理32位回绕 cap_old cap_new; if (diff 0u) { grid_freq_hz (float)TIM5_CLK_HZ / (float)diff; pll_set_freq(grid_freq_hz); } }要注意输入捕获的计数器频率需要特别配置好分频系数别乱加。比如定时器挂在84MHz上如果预分频器设置成84计数值会缩小84倍频率分辨率变差过零抖动的比例反而更大。我一般设置预分频为1让计数分辨率达到最大。5.2 相位累加器与正弦表查表的实现相位累加器用32位无符号变量2^32对应360度。假如ADC固定每工频周期采样256点每个采样点相位步长为2^32/25616777216乘以频率相对值即可。#define PHASE_2PI (1u 32) #define SIN_TABLE_N 256u static uint32_t phase_acc 0; static float phase_step 0.0f; // 根据电网频率更新每采样点的相位步长 void pll_set_freq(float freq_hz) { phase_step freq_hz * (float)PHASE_2PI / ADC_SAMPLE_HZ; } // 在ADC采样完成中断里更新相位并查表生成PWM比较值 const uint16_t sine_table[SIN_TABLE_N] { /* 预生成值域0~1024sin表达式 */ }; void on_adc_sample_complete(void) { phase_acc (uint32_t)phase_step; uint8_t idx (uint8_t)(phase_acc 24); // 高8位正好对应256点表 uint16_t duty_x ((uint32_t)sine_table[idx] * (uint32_t)modulation) 10u; uint16_t ocr (uint16_t)((duty_x * (uint32_t)PWM_ARR) 10u); __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, ocr); }sine_table可以按单极性调制的形式生成把0~1024映射到一个周期内的半波。实际项目里我一般把正弦表生成函数放上位机里算好直接以const数组拷进工程运行时不做三角函数运算。modulation是调制度0到1024之间可调。这样修改调制度只需要改一个变量不必动PWM。如果觉得256点查表相位分辨率不够每点1.4度可以把采样点数提高到512同时把表换成512点用phase_acc的高9位做索引。实测下来逆变并网对相位分辨率要求不算变态256点足够但如果你后面要做高次谐波补偿或者有源滤波建议直接512点起步。5.3 过零误差修正与并网允许判断相位累加器负责“大步走”过零信号负责“纠偏”。每次过零到来读取phase_acc期望值接近0差值即相位误差映射到±180度范围后交给PI修正void zero_cross_isr(void) { int32_t err (int32_t)phase_acc; if (err (int32_t)(PHASE_2PI / 2)) err - (int32_t)PHASE_2PI; else if (err (int32_t)(-(int32_t)PHASE_2PI / 2)) err (int32_t)PHASE_2PI; // PI修正比例项直接调整步长积分项累积频率误差并限幅 pll_pi_update((float)err); // 并网允许判断频率和相位误差同时满足条件 static uint8_t sync_count 0; if (freq_within_range() abs(pll_err_norm()) 0.001f) { if (sync_count 20) { HAL_GPIO_WritePin(RELAY_PORT, RELAY_PIN, GPIO_PIN_SET); sync_count 0; } } }PI调节器的输出是频率修正量加在基础50Hz上再重新计算phase_step。积分项需要限幅否则长时间频率偏差较大时积分会累积到很大等相位锁住之后容易产生过冲。我习惯把积分项限幅到±0.5Hz既能应对电网频率波动又不会让系统震荡。并网允许条件一定要有连续满足的计数不能让单次相位误差过小就立刻合闸。过零抖动哪怕被滤波单次读取仍有偶然性连续20个周期满足条件基本说明输出相位和电网相位确实稳定锁定了这时候再给继电器信号才稳妥。6. 实测阶段才暴露的坑逐个说清楚代码写完只是开始真正让人头疼的是上电实测。这些坑我在调样机时基本都踩过按频率从高到低给你过一遍。6.1 过零毛刺把一次过零变成三次过零电网带整流性负载的时候电压波形在过零点附近经常出现畸变。比较器逻辑稍不干净一次过零会触发出三个沿频率测量结果变成一堆乱数PLL跟着乱跳。我的处理是三层硬件上加施密特迟滞MCU输入捕获的ICFilter数字滤波开到合适档位软件里再做相邻沿间隔合理性判断——两个沿间隔小于最小周期的一半直接丢弃当作毛刺。三层下来过零计数基本干净了。6.2 固定相位延迟光耦和比较器的“迟到”误差光耦、比较器、数字隔离器全都有传输延迟。这些延迟看着只有几微秒到几十微秒但在50Hz系统里50微秒对应0.9度相位误差多个器件串起来误差不小。而且这个延迟是固定的不会自己消失。解决方法是加一个相位补偿参数先在示波器上对比电网电压波形和PWM输出波形测出实际滞后角度然后在相位累加器里人为加一个偏置让输出提前对应角度。这个参数一次标定之后长时间稳定。6.3 谐波、频率突变与相位跳变的处理策略谐波对过零检测的影响前面说过对PLL的影响则主要体现为相位误差里混入噪声。PI调节器带宽不能太高否则谐波分量会被放大进相位步长PWM频率跟着抖带宽也不能太低否则电网频率突变时PLL反应太慢。我常用的办法是把过零测量到的频率先做滑动平均利用PLL的积分项平滑修正别直接用最新值。这样谐波引起的相位误差会被平均掉而真实的频率偏移又不会被挡在外面。相位跳变则是另一类问题。电网大负载投切瞬间电压相位可能会跳几度甚至十几度。如果PLL还是按小误差慢慢收敛可能要好几个周期才能拉回来中间这段时间逆变器输出和电网相位差偏大存在安全隐患。我的处理策略是把相位误差设置一个阈值比如超过30度就认为发生了相位跳变此时不再继续生成SPWM而是进入重新同步状态等PLL误差收敛、连续N个周期满足条件后再次允许并网。宁可多等两个周期也不冒险在一个大相位差下合闸。6.4 打样调试建议第一次上电千万不要直接挂220V强电并网。我当时的做法是用一台可调交流源先从低压比如30V开始模拟电网电压把PLL跑稳了再逐步升压到220V并网。低压调试阶段可以把过零检测、频率测量、PLL锁定、继电器合闸全流程跑通而且安全得多。示波器上主要看三路电网电压、逆变器输出、继电器驱动信号。当输出波形和电网波形完全重合继电器驱动信号在重合稳定后拉高说明同步链路是对的。然后故意把电网频率调偏0.2Hz观察PLL能不能在几个周期内追回来这个测试能快速验证PI参数是否合理。产品化之前还要注意继电器动作时间。继电器从驱动到触点闭合通常有5到10毫秒延迟所以在相位误差满足条件之后要提前一个固定时间输出合闸信号让继电器闭合瞬间恰好落在相位误差接近零的时刻。这个提前量可以通过示波器测出来写进程序里作为常数别忽略。最后再说一句我个人做这类项目的体会并网同步控制看着是个算法问题实际是模拟电路、MCU外设、控制算法三者的配合。任何一个环节抖一下锁相质量都会打折。不要迷信某一种花哨算法先把过零链路的硬件底子打好让PLL在一个“干净”的输入上工作后面一切都顺了。如果样机预算允许直接选带比较器的高端型号省掉的PCB面积和自己的调试时间算下来比省那几块钱芯片值得多。