Software Phase Locked Loop Design——锁相环软件设计

Software Phase Locked Loop Design——锁相环软件设计

1 Introduction——介绍

电网的相角对于像光伏逆变器这样向电网输送能量的设备的运行是十分重要的信息。 锁相环(PLL)是一种闭环系统,它使用反馈控制环使内部振荡器产生与外部周期信号相位同步的正弦波。PLL是一个结构精简的伺服系统,它控制其输出信号的相位,使它的输出相位和参考相位之间的误差保持最小。 锁相环的性能直接影响逆变器的并网性能。 线路陷波,网压不平衡、线路电压骤降,相位损耗和频率变化是电力设备面临的常见问题,PLL需要克服这些问题以保持对电网电压的稳定跟踪。

Figure 1PLL的结构框图,它包含鉴相器,低通滤波器和压控振荡器(VCO)三部分。
Software Phase Locked Loop Design——锁相环软件设计_第1张图片
电网电压可以表示为:
v = v g r i d   s i n ( θ i n ) = v g r i d   s i n ( ω g r i d t + Θ g r i d ) (1) v = v_{grid}\ sin(\theta_{in}) = v_{grid}\ sin(\omega_{grid}t+\Theta_{grid} )\tag{1} v=vgrid sin(θin)=vgrid sin(ωgridt+Θgrid)(1)

假设压控振荡器产生了与电网频率接近的正弦波,其输出可以表示为:

v ‘ = cos ⁡ ( θ o u t ) = cos ⁡ ( ω P L L t + θ P L L ) (2) v^`=\cos(\theta_{out}) = \cos( \omega_{PLL}t +\theta_{PLL})\tag{2} v=cos(θout)=cos(ωPLLt+θPLL)(2)

鉴相器的作用是比较输入参考电压与压控振荡器产生的正弦波形的相角,以此来生成与相角误差成比例的误差信号,这可以通过将VCO的输出与已测到的输入电压值相乘得到:

v d = K d v g r i d 2 [ sin ⁡ ( ( ω g r i d − ω P L L ) t + ( θ g r i d − θ P L L ) ) + sin ⁡ ( ( ω g r i d + ω P L L ) t + ( θ g r i d + θ P L L ) ) ] (3) v_d = \frac{K_dv_{grid}}{2}\left[\sin((\omega_{grid}-\omega_{PLL})t+(\theta_{grid}-\theta_{PLL}))+\sin((\omega_{grid}+\omega_{PLL})t+(\theta_{grid}+\theta_{PLL}))\right] \tag{3} vd=2Kdvgrid[sin((ωgridωPLL)t+(θgridθPLL))+sin((ωgrid+ωPLL)t+(θgrid+θPLL))](3)

Equation3可知,鉴相器的输出包含有锁相误差的信息。然而,这种误差信息并不是线性的,并且包含有两倍电网频率的成分。为了得到精确的锁相误差,需要滤除这个两倍频成分。

在滤除两倍频成分后,锁相误差就可以表示为:

v d ‾ = K d v g r i d 2 sin ⁡ ( ( ω g r i d − ω P L L ) t + ( θ g r i d − θ P L L ) ) (4) \overline{v_{d}} = \frac{K_dv_{grid}}{2}\sin\left((\omega_{grid}-\omega_{PLL})t+(\theta_{grid}-\theta_{PLL})\right)\tag{4} vd=2Kdvgridsin((ωgridωPLL)t+(θgridθPLL))(4)

稳态运行时, ω g r i d − ω P L L \omega_{grid}-\omega_{PLL} ωgridωPLL可被忽略,对于小角度 θ \theta θ来说, sin ⁡ ( θ ) ≈ θ \sin(\theta)\approx\theta sin(θ)θ。因此,可以得到一个线性的误差表达式:

e r r = v g r i d ( θ g r i d − θ P L L ) 2 (5) err = \frac{v_{grid}(\theta_{grid}-\theta_{PLL})}{2}\tag{5} err=2vgrid(θgridθPLL)(5)

环路滤波器可以同时作为PI控制器,可以在稳态时将锁相误差控制在0。对锁相环进行小信号分析,可以得到开环传递函数和闭环传递函数:

C l o s e d L o o p T F = O p e n L o o p T F / ( 1 + O p e n L o o p T F ) Closed Loop TF = OpenLoopTF / (1+ OpenLoopTF) ClosedLoopTF=OpenLoopTF/(1+OpenLoopTF)

PLL的闭环传递函数可以表示为: H o ( s ) = θ o u t ( s ) θ i n = L F ( s ) s + L F ( s ) = v g r i d ( k p s + k p T i ) s 2 + V g r i d k p s + v g r i d k p T i H_o(s) = \frac{\theta_{out}(s)}{\theta_{in}} = \frac{LF(s)}{s+LF(s)} = \frac{v_{grid}(k_ps+\frac{k_p}{T_i})}{s^2+V_{grid}k_ps+v_{grid}\frac{k_p}{T_i}} Ho(s)=θinθout(s)=s+LF(s)LF(s)=s2+Vgridkps+vgridTikpvgrid(kps+Tikp)

闭环误差传递函数可以表示为: E o ( s ) = V d ( s ) θ i n ( s ) = 1 − H o ( s ) = s s + L F ( s ) = s 2 s 2 + k p s + k p T i E_o(s) = \frac{V_d(s)}{\theta_{in}(s)} = 1 - H_o(s) = \frac{s}{s+LF(s)} = \frac{s^2}{s^2+k_ps+\frac{k_p}{T_i}} Eo(s)=θin(s)Vd(s)=1Ho(s)=s+LF(s)s=s2+kps+Tikps2

将闭环传递函数与普通二阶系统的传递函数进行对比:

H ( s ) = 2 ζ ω n s + ω n 2 s 2 + 2 ζ ω n s + ω n 2 (6) H(s) = \frac{2\zeta\omega_ns+\omega^2_n}{s^2+2\zeta\omega_ns+\omega^2_n}\tag{6} H(s)=s2+2ζωns+ωn22ζωns+ωn2(6)

可以得到系统的固有频率和阻尼比:

ω n = v g r i d K p T i (7) \omega_n =\sqrt{\frac{v_{grid}K_p}{T_i}}\tag{7} ωn=TivgridKp (7)

ζ = v g r i d T i K p 4 (8) \zeta = \sqrt{\frac{v_{grid}T_iK_p}{4}}\tag{8} ζ=4vgridTiKp (8)

可以看到,在锁相环中,PI控制器具有双重作用:

  • 滤除锁相误差中的两倍频成分
  • 调节PLL的阶跃响应性能

环路滤波器具有低通特性,可以滤除鉴相器输出的高频成分,当被锁信号的频率较高时,PI控制器的低通特性可以滤除掉存在的两倍频成分,但是,电网的频率很低(50Hz-60Hz),PI控制器还无法达到理想的滤波效果,环路滤波器输出的高频成分将会影响最终PLL的性能。

从以上的讨论可知,PI控制器的低通特性还无法完全满足滤除鉴相器输出的两倍电网频率的成分的要求,因此,必须找到将鉴相器的输出进行线性化的方法,以下是可行的两种方法:

  • 使用陷波器滤除鉴相器输出的电网两倍频成分
  • 使用正交信号发生器生成固定参考系

2 PLL With Notch Filter——带陷波器的锁相环

陷波器可以用来衰减鉴相器输出的网压两倍频成分。一个可调节的陷波器还可以在网压有波动时精确地去除相对应频率的成分。本章 Section 2.1 描述了PI系数的设计流程。同时展示了一个可调节陷波器的参数设计方法。

Software Phase Locked Loop Design——锁相环软件设计_第2张图片
Section1讨论到,引入陷波器后,PI控制器的设计只与锁相环的动态和稳态性能相关。Section 2.1 描述了PI控制器的数字化实现方法和PI系数的选择方法。

2.1 Discrete Implementation of PI Controller——PI控制器的离散化实现

环路滤波器或者说是PI控制器的数学表达式如 Equation 9所示:

y l f [ n ] = y l f [ n − 1 ] ∗ A 1 + y n o t c h [ n ] ∗ B 0 + y n o t c h [ n − 1 ] ∗ B 1 (9) ylf[n] = ylf[n-1]*A1 + ynotch[n]*B0 + ynotch[n-1]*B1\tag{9} ylf[n]=ylf[n1]A1+ynotch[n]B0+ynotch[n1]B1(9)

进行z变换后,Equation 9可被重写为:

y l f ( z ) y n o t c h ( z ) = B 0 + B 1 ∗ z − 1 1 − z − 1 (10) \frac{ylf(z)}{ynotch(z)} = \frac{B0+B1*z^{-1}}{1-z^{-1}}\tag{10} ynotch(z)ylf(z)=1z1B0+B1z1(10)

PI控制器的拉普拉斯变换表达式为:

y l f ( s ) y n o t c h ( s ) = K p + K i s (11) \frac{ylf(s)}{ynotch(s)} = K_p+\frac{K_i}{s}\tag{11} ynotch(s)ylf(s)=Kp+sKi(11)

使用双线性变换 s = 2 T ( z − 1 z + 1 ) s = \frac{2}{T}(\frac{z-1}{z+1}) s=T2(z+1z1),其中T为采样时间,可以得到:

y l f ( z ) y n o t c h ( z ) = ( 2 ∗ K p + K i ∗ T 2 ) − ( 2 ∗ K p − K i ∗ T 2 ) z − 1 1 − z − 1 (12) \frac{ylf(z)}{ynotch(z)} = \frac{(\frac{2*K_p+K_i*T}{2})-(\frac{2*K_p-K_i*T}{2})z^{-1}}{1-z^{-1}}\tag{12} ynotch(z)ylf(z)=1z1(22Kp+KiT)(22KpKiT)z1(12)

Equation 10Equation 12可映射到PI控制器在数字域的比例和积分增益,选择合适的比例和积分系数是PI控制器设计最具挑战性的部分。

二阶系统的阶跃响应

H ( s ) = ω n 2 s 2 + 2 ζ ω n s + ω n 2 (13) H(s) = \frac{\omega^2_n}{s^2+2\zeta\omega_ns+\omega^2_n}\tag{13} H(s)=s2+2ζωns+ωn2ωn2(13)

时域下可表示为:

y ( t ) = 1 − c e − σ t sin ⁡ ( ω d t + φ ) (14) y(t)=1-ce^{-\sigma{t}}\sin(\omega_dt+\varphi)\tag{14} y(t)=1ceσtsin(ωdt+φ)(14)

忽略Equation 14中存在的左半平面极点,将调整时间规定为阶跃响应在一定范围内进行调整的时间,假定这个调整范围为 ∂ \partial ,可以得到:

1 − ∂ = 1 − c e − σ t s = > ∂ = c e − σ t s = > t s = 1 σ ∗ ln ⁡ c σ (15) 1-\partial=1-ce^{-\sigma{t_s}}=>\partial=ce^{-\sigma{t_s}}=>t_s=\frac{1}{\sigma}*\ln{\frac{c}{\sigma}}\tag{15} 1=1ceσts=>=ceσts=>ts=σ1lnσc(15)
其中,
σ = ζ ω n (16) \sigma=\zeta\omega_n\tag{16} σ=ζωn(16)
c = w n w d (16) c = \frac{w_n}{w_d}\tag{16} c=wdwn(16)
ω d = 1 − ζ 2 ω n (16) \omega_d=\sqrt{1-\zeta^2}\omega_n\tag{16} ωd=1ζ2 ωn(16)

设定调整时间为30ms,误差范围为5%,阻尼比为0.7,可以得到固有频率为158.6859,返回代入Equation 7Equation 8可以得到 k p k_p kp=222.1603,Ki=25181.22.

将上述值代入数字环路滤波器的系数表达式:

B 0 = ( 2 ∗ K p + K i ∗ T 2 ) B0=(\frac{2*K_p+K_i*T}{2}) B0=(22Kp+KiT) B 1 = − ( 2 ∗ K p − K i ∗ T 2 ) (17) B1 = -(\frac{2*K_p-K_i*T}{2})\tag{17} B1=(22KpKiT)(17)

对于50Hz的运行频率,可以得到 B 0 B0 B0= 223.4194 B 1 B1 B1=-220.901.

2.2 Adaptive Notch Filter Design——可调整陷波滤波器设计

Figure 2 所示的陷波滤波器可用来滤除鉴相器输出的两倍频成分,但是电网频率存在一定的波动,为了精确滤除电网两倍频成分,需要使用可调节陷波滤波器。一个典型的陷波滤波器s域表达式如Equation 18所示:

H n f ( s ) = s 2 + 2 ζ 2 ω n s + ω n 2 s 2 + 2 ζ 1 ω n s + ω n 2 , ζ 2 < < ζ 1 (18) H_{nf}(s) = \frac{s^2+2\zeta_2\omega_ns+\omega^2_n}{s^2+2\zeta_1\omega_ns+\omega^2_n} , \zeta_2<<\zeta_1\tag{18} Hnf(s)=s2+2ζ1ωns+ωn2s2+2ζ2ωns+ωn2,ζ2<<ζ1(18)

使用零阶保持法 s = z − 1 T s=\frac{z-1}{T} s=Tz1Equation 18离散化:

H n f ( z ) = z 2 + ( 2 ζ 2 ω n T − 2 ) z + ( − 2 ζ 2 ω n T + ω n 2 T 2 + 1 ) z 2 + ( 2 ζ 1 ω n T − 2 ) z + ( − 2 ζ 1 ω n T + ω n 2 T 2 + 1 ) = B 0 + B 1 z − 1 + B 2 z − 2 A 0 + A 1 z − 1 + A 2 z − 2 (19) H_{nf}(z) = \frac{z^2+(2\zeta_2\omega_nT-2)z+(-2\zeta_2\omega_nT+\omega^2_nT^2+1)}{z^2+(2\zeta_1\omega_nT-2)z+(-2\zeta_1\omega_nT+\omega^2_nT^2+1)}=\frac{B_0+B_1z^{-1}+B_2z^{-2}}{A_0+A_1z^{-1}+A_2z^{-2}}\tag{19} Hnf(z)=z2+(2ζ1ωnT2)z+(2ζ1ωnT+ωn2T2+1)z2+(2ζ2ωnT2)z+(2ζ2ωnT+ωn2T2+1)=A0+A1z1+A2z2B0+B1z1+B2z2(19)

Equation 19存在两个极点和两个零点,陷波器的系数可以跟随电网频率的摆动进行调整。

例如,设 ζ 2 \zeta_2 ζ2 = 0.00001 , ζ 1 \zeta_1 ζ1 = 0.1 ( ζ 2 \zeta_2 ζ2 << ζ 1 \zeta_1 ζ1),陷波器的响应如**Figure 3 **所示,陷波器的系数根据电网频率进行自动更新。
Software Phase Locked Loop Design——锁相环软件设计_第3张图片

2.3 Sine and Cosine Generation——正弦和余弦发生器

PLL的设计需要进行大量 sin ⁡ \sin sin cos ⁡ \cos cos运算,这些运算消耗了大量CPU运行时间。为了解决这个问题,采用积分的方法产生正弦或余弦值的方法被提出。

y ( t + Δ t ) = y ( t ) + d y ( t ) d t ∗ Δ t (20) y(t+\Delta{t})=y(t)+\frac{dy(t)}{dt}*\Delta{t}\tag{20} y(t+Δt)=y(t)+dtdy(t)Δt(20)

对于正弦或余弦信号,这个表达式转化为:

sin ⁡ ( t + Δ t ) = sin ⁡ ( t ) + d sin ⁡ ( t ) d t ∗ Δ t = sin ⁡ ( t ) + cos ⁡ ( t ) ∗ Δ t (21) \sin(t+\Delta{t})=\sin(t)+\frac{d\sin(t)}{dt}*\Delta{t}=\sin(t)+\cos(t)*\Delta{t}\tag{21} sin(t+Δt)=sin(t)+dtdsin(t)Δt=sin(t)+cos(t)Δt(21)
cos ⁡ ( t + Δ t ) = cos ⁡ ( t ) + d cos ⁡ ( t ) d t ∗ Δ t = cos ⁡ ( t ) − sin ⁡ ( t ) ∗ Δ t (21) \cos(t+\Delta{t})=\cos(t)+\frac{d\cos(t)}{dt}*\Delta{t}=\cos(t)-\sin(t)*\Delta{t}\tag{21} cos(t+Δt)=cos(t)+dtdcos(t)Δt=cos(t)sin(t)Δt(21)

2.4 Simulating the Phase Locked Loop for Varying Conditions——锁相环仿真

在编写SPLL程序之前有必要对PLL在不同电网环境下的行为进行仿真,很多并网变换器使用的都是定点处理器,IQ Math可以很方便地使用十进制数表示定点数,C2000 IQ Math库提供的内置函数可以帮助程序员简化十进制定点数的处理。然而,使用定点数编程存在动态范围小,精度低的问题,因此有必要在仿真环境下模拟定点处理器的行为,以下是使用matlab定点工具箱测试锁相环在动态电网条件下的运行性能的脚本。

%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%TI C2000
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%Select numeric type, let's choose Q21
T=numerictype('WordLength',32,'FractionLength',21);
%Specify math attributes to the fimath object
F=fimath('RoundMode','floor','OverflowMode','wrap');
F.ProductMode='SpecifyPrecision';
F.ProductWordLength=32;
F.ProductFractionLength=21;
F.SumMode='SpecifyPrecision';
F.SumWordLength=32;
F.SumFractionLength=21;
%specify fipref object, to display warning in cases of overflow and
%underflow
P=fipref;
P.LoggingMode='on';
P.NumericTypeDisplay='none';
P.FimathDisplay='none';
%PLL Modelling starts from here
Fs=50000; %Sampling frequency = 50Khz
GridFreq=50; %Nominal Grid Frequency in Hz
Tfinal=0.2; %Time the simulation is run for = 0.5 seconds
Ts=1/Fs; %Sampling Time = 1/Fs
t=0:Ts:Tfinal; %Simulation Time vector
wn=2*pi*GridFreq; %Nominal Grid Frequency in radians
%generate input signal and create a fi object of it
%input wave with a phase jump at the mid point of simulation
% CASE 1 : Phase Jump at the Mid Point
L=length(t);
for n=1:floor(L)
u(n)=sin(2*pi*GridFreq*Ts*n);
end
for n=1:floor(L)
u1(n)=sin(2*pi*GridFreq*Ts*n);
end
for n=floor(L/2):L
u(n)=sin(2*pi*GridFreq*Ts*n+pi/2);
end
%CASE 2 : Harmonics
% L=length(t);
% for n=1:floor(L)
% u(n)=0.9*sin(2*pi*GridFreq*Ts*n)+0.1*sin(2*pi*5*GridFreq*Ts*n);
% end
% for n=1:floor(L)
% u1(n)=sin(2*pi*GridFreq*Ts*n);
% end
%CASE 3 : Frequency Shift
% L=length(t);
% for n=1:floor(L)
% u(n)=sin(2*pi*GridFreq*Ts*n);
% end
% for n=1:floor(L)
% u1(n)=sin(2*pi*GridFreq*Ts*n);
% end
% for n=floor(L/2):L
% u(n)=sin(2*pi*GridFreq*1.1*Ts*n);
% end
%CASE 4: Amplitude Variations
% L=length(t);
% for n=1:floor(L)
% u(n)=sin(2*pi*GridFreq*Ts*n);
% end
% for n=1:floor(L)
% u1(n)=sin(2*pi*GridFreq*Ts*n);
% end
% for n=floor(L/2):L
% u(n)=0.8*sin(2*pi*GridFreq*Ts*n);
% end;
u=fi(u,T,F);
u1=fi(u1,T,F);
%declare arrays used by the PLL process
Upd=fi([0,0,0],T,F);
ynotch=fi([0,0,0],T,F);
ynotch_buff=fi([0,0,0],T,F);
ylf=fi([0,0],T,F);
SinGen=fi([0,0],T,F);
Plot_Var=fi([0,0],T,F);
Mysin=fi([0,0],T,F);
Mycos=fi([fi(1.0,T,F),fi(1.0,T,F)],T,F);
theta=fi([0,0],T,F);
werror=fi([0,0],T,F);
%notch filter design
c1=0.1;
c2=0.00001;
X=2*c2*wn*2*Ts;
Y=2*c1*wn*2*Ts;
Z=wn*2*wn*2*Ts*Ts;
B_notch=[1 (X-2) (-X+Z+1)];
A_notch=[1 (Y-2) (-Y+Z+1)];
B_notch=fi(B_notch,T,F);
A_notch=fi(A_notch,T,F);
% simulate the PLL process
for n=2:Tfinal/Ts % No of iteration of the PLL process in the simulation time
% Phase Detect
Upd(1)= u(n)*Mycos(2);
%Notch Filter
ynotch(1)=-A_notch(2)*ynotch(2)-
A_notch(3)*ynotch(3)+B_notch(1)*Upd(1)+B_notch(2)*Upd(2)+B_notch(3)*Upd(3);
%update the Upd array for future sample
Upd(3)=Upd(2);
Upd(2)=Upd(1);
% PI Loop Filter
%ts=30ms, damping ration = 0.7
% we get natural frequency = 110, Kp=166.6 and Ki=27755.55
% B0=166.877556 & B1=-166.322444
ylf(1)= fi(1.0,T,F)*ylf(2)+fi(166.877556,T,F)*ynotch(1)+fi(-166.322444,T,F)*ynotch(2);
%update Ynotch for future use
ynotch(3)=ynotch(2);
ynotch(2)=ynotch(1);
ynotch_buff(n+1)=ynotch(1);
ylf(1)=min([ylf(1) fi(200.0,T,F)]);
ylf(2)=ylf(1);
wo=fi(wn,T,F)+ylf(1);
werror(n+1)=(wo-wn)*fi(0.00318309886,T,F);
%integration process
Mysin(1)=Mysin(2)+wo*fi(Ts,T,F)*(Mycos(2));
Mycos(1)=Mycos(2)-wo*fi(Ts,T,F)*(Mysin(2));
%limit the oscillator integrators
Mysin(1)=max([Mysin(1) fi(-1.0,T,F)]);
Mysin(1)=min([Mysin(1) fi(1.0,T,F)]);
Mycos(1)=max([Mycos(1) fi(-1.0,T,F)]);
Mycos(1)=min([Mycos(1) fi(1.0,T,F)]);
Mysin(2)=Mysin(1);
Mycos(2)=Mycos(1);
%update the output phase
theta(1)=theta(2)+wo*Ts;
%output phase reset condition
if(Mysin(1)>0 && Mysin(2) <=0)
theta(1)=-fi(pi,T,F);
end
SinGen(n+1)=Mycos(1);
Plot_Var(n+1)=Mysin(1);
end
% CASE 1 : Phase Jump at the Mid Point
error=Plot_Var-u;
%CASE 2 : Harmonics
%error=Plot_Var-u1;
%CASE 3: Frequency Variations
%error=Plot_Var-u;
%CASE 4: Amplitude Variations
%error=Plot_Var-u1;
figure;
subplot(3,1,1),plot(t,Plot_Var,'r',t,u,'b'),title('SPLL(red) & Ideal Grid(blue)');
subplot(3,1,2),plot(t,error,'r'),title('Error');
subplot(3,1,3),plot(t,u1,'r',t,Plot_Var,'b'),title('SPLL Out(Blue) & Ideal Grid(Red)');

SPLL_1ph.m

Figure 4展示了PLL在动态电网环境下的仿真结果。
Software Phase Locked Loop Design——锁相环软件设计_第4张图片

2.5 Implementing PLL on C2000 Controller Using IQ Math——使用IQ Math在C2000控制器上实现锁相环

一般为逆变器编写的代码都使用IQ24格式,但在Section 2.4中选择的格式是IQ21,因此当前PLL的代码为:

#define _SPLL_1ph_H_
#define SPLL_Q _IQ21
#define SPLL_Qmpy _IQ21mpy
typedef struct{
int32 B2_notch;
int32 B1_notch;
int32 B0_notch;
int32 A2_notch;
int32 1_notch;
}SPLL_NOTCH_COEFF;
typedef struct{
int32 B1_lf;
int32 B0_lf;
int32 A1_lf;
}SPLL_LPF_COEFF;
typedef struct{
int32 AC_input;
int32 theta[2];
int32 cos[2];
int32 sin[2];
int32 wo;
int32 wn;
SPLL_NOTCH_COEFF notch_coeff;
SPLL_LPF_COEFF lpf_coeff;
int32 Upd[3];
int32 ynotch[3];
int32 ylf[2];
int32 delta_t;
}SPLL_1ph;
void SPLL_1ph_init(int Grid_freq, long DELTA_T, SPLL_1ph *spll, SPLL_LPF_COEFF lpf_coeff);
void SPLL_1ph_notch_coeff_update(float delta_T, float wn,float c2, float c1, SPLL_1ph *spll_obj);
inline void SPLL_1ph_run_FUNC(SPLL_1ph *spll1);
void SPLL_1ph_init(int Grid_freq, long DELTA_T, SPLL_1ph *spll_obj, SPLL_LPF_COEFF lpf_coeff)
{
spll_obj->Upd[0]=SPLL_Q(0.0);
spll_obj->Upd[1]=SPLL_Q(0.0);
spll_obj->Upd[2]=SPLL_Q(0.0);
spll_obj->ynotch[0]=SPLL_Q(0.0);
spll_obj->ynotch[1]=SPLL_Q(0.0);
spll_obj->ynotch[2]=SPLL_Q(0.0);
spll_obj->ylf[0]=SPLL_Q(0.0);
spll_obj->ylf[1]=SPLL_Q(0.0);
spll_obj->sin[0]=SPLL_Q(0.0);
spll_obj->sin[1]=SPLL_Q(0.0);
spll_obj->cos[0]=SPLL_Q(0.999);
spll_obj->cos[1]=SPLL_Q(0.999);
spll_obj->theta[0]=SPLL_Q(0.0);
spll_obj->theta[1]=SPLL_Q(0.0);
spll_obj->wn=SPLL_Q(2*3.14*Grid_freq);
//coefficients for the loop filter
spll_obj->lpf_coeff.B1_lf=lpf_coeff.B1_lf;
spll_obj->lpf_coeff.B0_lf=lpf_coeff.B0_lf;
spll_obj->lpf_coeff.A1_lf=lpf_coeff.A1_lf;
spll_obj->delta_t=DELTA_T;
}
void SPLL_1ph_notch_coeff_update(float delta_T, float wn,float c2, float c1, SPLL_1ph *spll_obj)
{
// Note c2<
float x,y,z;
x=(float)(2.0*c2*wn*delta_T);
y=(float)(2.0*c1*wn*delta_T);
z=(float)(wn*delta_T*wn*delta_T);
spll_obj->notch_coeff.A1_notch=SPLL_Q(y-2);
spll_obj->notch_coeff.A2_notch=SPLL_Q(z-y+1);
spll_obj->notch_coeff.B0_notch=SPLL_Q(1.0);
spll_obj->notch_coeff.B1_notch=SPLL_Q(x-2);
spll_obj->notch_coeff.B2_notch=SPLL_Q(z-x+1);
}
inline void SPLL_1ph_run_FUNC(SPLL_1ph *spll_obj)
{
//-------------------//
// Phase Detect //
//-------------------//
spll_obj->Upd[0]=SPLL_Qmpy(spll_obj->AC_input,spll_obj->cos[1]);
//-------------------//
//Notch filter structure//
//-------------------//
spll_obj->ynotch[0]=-SPLL_Qmpy(spll_obj->notch_coeff.A1_notch,spll_obj->ynotch[1])-
SPLL_Qmpy(spll_obj->notch_coeff.A2_notch,spll_obj->ynotch[2])+SPLL_Qmpy(spll_obj-
>notch_coeff.B0_notch,spll_obj->Upd[0])+SPLL_Qmpy(spll_obj->notch_coeff.B1_notch,spll_obj-
>Upd[1])+SPLL_Qmpy(spll_obj->notch_coeff.B2_notch,spll_obj->Upd[2]);
// update the Upd array for future
spll_obj->Upd[2]=spll_obj->Upd[1];
spll_obj->Upd[1]=spll_obj->Upd[0];
//---------------------------//
// PI loop filter //
//---------------------------//
spll_obj->ylf[0]=-SPLL_Qmpy(spll_obj->lpf_coeff.A1_lf,spll_obj->ylf[1])+SPLL_Qmpy(spll_obj-
>lpf_coeff.B0_lf,spll_obj->ynotch[0])+SPLL_Qmpy(spll_obj->lpf_coeff.B1_lf,spll_obj->ynotch[1]);
//update array for future use
spll_obj->ynotch[2]=spll_obj->ynotch[1];
spll_obj->ynotch[1]=spll_obj->ynotch[0];
spll_obj->ylf[1]=spll_obj->ylf[0];
//------------------//
// VCO //
//------------------//
spll_obj->wo=spll_obj->wn+spll_obj->ylf[0];
//integration process to compute sine and cosine
spll_obj->sin[0]=spll_obj->sin[1]+SPLL_Qmpy((SPLL_Qmpy(spll_obj->delta_t,spll_obj-
>wo)),spll_obj->cos[1]);
spll_obj->cos[0]=spll_obj->cos[1]-SPLL_Qmpy((SPLL_Qmpy(spll_obj->delta_t,spll_obj-
>wo)),spll_obj->sin[1]);
if(spll_obj->sin[0]>SPLL_Q(0.99))
spll_obj->sin[0]=SPLL_Q(0.99);
else if(spll_obj->sin[0]<SPLL_Q(-0.99))
spll_obj->sin[0]=SPLL_Q(-0.99);
if(spll_obj->cos[0]>SPLL_Q(0.99))
spll_obj->cos[0]=SPLL_Q(0.99);
else if(spll_obj->cos[0]<SPLL_Q(-0.99))
spll_obj->cos[0]=SPLL_Q(-0.99);
//compute theta value
spll_obj->theta[0]=spll_obj->theta[1]+SPLL_Qmpy(SPLL_Qmpy(spll_obj-
>wo,SPLL_Q(0.159154943)),spll_obj->delta_t);
if(spll_obj->sin[0]>SPLL_Q(0.0) && spll_obj->sin[1] <=SPLL_Q (0.0) )
{
spll_obj->theta[0]=SPLL_Q(0.0);
spll_obj->theta[1]=spll_obj->theta[0];
spll_obj->sin[1]=spll_obj->sin[0];
spll_obj->cos[1]=spll_obj->cos[0];
}
#endif

SPLL_1ph.h
Software Phase Locked Loop Design——锁相环软件设计_第5张图片
如果要在最终程序中使用上述代码,需要包含以下头文件并对SPLL对象和环路滤波器系数进行声明。

#include "SPLL_1ph.h"
// ------------- Software PLL for Grid Tie Applications ----------
SPLL_1ph spll1;
SPLL_LPF_COEFF spll_lpf_coef1;

从先前的环路滤波器系数的分析可知,环路滤波器的系数可组织为:

#define B0_LPF SPLL_Q(166.877556) //需要根据调试情况自行调整
#define B1_LPF SPLL_Q(-166.322444)
#define A1_LPF SPLL_Q(-1.0)
spll_lpf_coef1.B0_lf=B0_LPF;
spll_lpf_coef1.B1_lf=B1_LPF;
spll_lpf_coef1.A1_lf=A1_LPF;

在中断服务程序中调用***SPLL_1ph_init()***,接着调用***SPLL_1ph_notch_coeff_update()***。

SPLL_1ph_init(GRID_FREQ,_IQ21((float)(1.0/ISR_FREQUENCY)) &spll1,spll_lpf_coef1);
c1=0.1;
c2=0.00001;
SPLL_1ph_notch_coeff_update(((float)(1.0/ISR_FREQUENCY)),(float)(2*PI*GRID_FREQ*2),(float)c2,(floa
t)c1, &spll1);

ISR中,从ADC读取正弦输入电压值并将其传入SPLL结构体,并将当前网压相角的正弦值传给invsine

inv_meas_vol_inst =((long)((long)VAC_FB<<12))-offset_165)<<1;
spll1.AC_input=(long)InvSine>>3; // Q24 to Q21
SPLL_1ph_run_FUNC(&spll1);
InvSine=spll2.sin<<3; // shift from Q21 to Q24

2.6 Results of Notch SPLL——陷波器的效果

Figure 5展示了在F28035x上实现的SPLL稳态表现和带有相位突变时的动态表现。
Software Phase Locked Loop Design——锁相环软件设计_第6张图片

3 Orthogonal Signal Generator PLL——正交信号发生锁相环

前面提到,单相锁相环的设计由于鉴相器输出的电网两倍频成分而变得十分棘手,前面用到陷波器消除了这个两倍频成分并得到了令人满意的结果。另外一个可以线性化鉴相器输出的方法是使用正交信号发生器和Park变换,Figure 6展示了正交信号发生PLL的结构框图,它包含带有正交信号发生和Park变换功能的鉴相器,环路滤波器和压控振荡器。

Software Phase Locked Loop Design——锁相环软件设计_第7张图片
输入电压的正交信号可以通过多种方法实现,比如延时,希尔伯特变换等,比较常见的是‘A New Single Phase PLL Structure Based on Second Order Generalized Integrator’, Mihai Ciobotaru, et al, PESC’06.中提出的二阶积分法,这个方法的优势在于它可以选择性地调谐正交信号的频率以抑制除电网频率外的其他频率成分。

Software Phase Locked Loop Design——锁相环软件设计_第8张图片
二阶广义积分器的闭环传递函数为:

H d ( s ) = v ‘ v ( s ) = k ω n s s 2 + k ω n s + ω n 2 (22) H_d(s)=\frac{v^`}{v}(s)=\frac{k\omega_n{s}}{s^2+k\omega_ns+\omega^2_n}\tag{22} Hd(s)=vv(s)=s2+kωns+ωn2kωns(22)

H q ( s ) = q v ‘ v ( s ) = k ω n 2 s 2 + k ω n s + ω n 2 (22) H_q(s)=\frac{qv^`}{v}(s)=\frac{k\omega^2_n}{s^2+k\omega_ns+\omega^2_n}\tag{22} Hq(s)=vqv(s)=s2+kωns+ωn2kωn2(22)

Section 2.6提到,电网频率存在波动,因此正交信号发生器需要能够调整其传递函数的系数以跟踪电网频率的变化。使用梯形近似法得到传递函数的离散形式:

H d ( z ) = k ω n 2 T s z − 1 z + 1 ( 2 T s z − 1 z + 1 ) 2 + k ω n 2 T s z − 1 z + 1 + ω n 2 = ( z k w n T s ) ( z 2 − 1 ) 4 ( z − 1 ) 2 + ( 2 k ω n T s ) ( z 2 − 1 ) + ( ω n T s ) 2 ( z + 1 ) 2 (23) H_d(z)=\frac{k\omega_n{\frac{2}{T_s}\frac{z-1}{z+1}}}{({\frac{2}{T_s}\frac{z-1}{z+1}})^2+k\omega_n{\frac{2}{T_s}\frac{z-1}{z+1}}+\omega^2_n}=\frac{(zkw_nT_s)(z^2-1)}{4(z-1)^2+(2k\omega_nT_s)(z^2-1)+(\omega_nT_s)^2(z+1)^2}\tag{23} Hd(z)=(Ts2z+1z1)2+kωnTs2z+1z1+ωn2kωnTs2z+1z1=4(z1)2+(2kωnTs)(z21)+(ωnTs)2(z+1)2(zkwnTs)(z21)(23)

x = 2 k ω n T s x=2k\omega_nT_s x=2kωnTs y = ( ω T s ) 2 y=(\omega{T_s})^2 y=(ωTs)2:

H d ( z ) = ( x x + y + 4 ) + ( − x x + y + 4 ) z − 2 1 − ( 2 ( 4 − y ) x + y + 4 ) z − 1 − ( x − y − 4 x + y + 4 ) z − 2 = b 0 + b 2 z − 2 1 − a 1 z − 1 − a 2 z − 2 (24) H_d(z)=\frac{(\frac{x}{x+y+4})+(\frac{-x}{x+y+4})z^{-2}}{1-(\frac{2(4-y)}{x+y+4})z^{-1}-(\frac{x-y-4}{x+y+4})z^{-2}}=\frac{b_0+b_2z^{-2}}{1-a_1z^{-1}-a_2z^{-2}}\tag{24} Hd(z)=1(x+y+42(4y))z1(x+y+4xy4)z2(x+y+4x)+(x+y+4x)z2=1a1z1a2z2b0+b2z2(24)

同样的:

H q ( z ) = ( k . y x + y + 4 ) + 2 ( k . y x + y + 4 ) z − 1 + ( k . y x + y + 4 ) z − 2 1 − ( 2 ( 4 − y ) x + y + 4 ) z − 1 − ( x − y − 4 x + y + 4 ) z − 2 = q b 0 + q b 1 z − 1 + q b 2 z − 2 1 − a 1 z − 1 − a 2 z − 2 (25) H_q(z)=\frac{(\frac{k.y}{x+y+4})+2(\frac{k.y}{x+y+4})z^{-1}+(\frac{k.y}{x+y+4})z^{-2}}{1-(\frac{2(4-y)}{x+y+4})z^{-1}-(\frac{x-y-4}{x+y+4})z^{-2}}=\frac{qb_0+qb_1z^{-1}+qb_2z^{-2}}{1-a_1z^{-1}-a_2z^{-2}}\tag{25} Hq(z)=1(x+y+42(4y))z1(x+y+4xy4)z2(x+y+4k.y)+2(x+y+4k.y)z1+(x+y+4k.y)z2=1a1z1a2z2qb0+qb1z1+qb2z2(25)

正交信号产生后,经过Park变换得到旋转参考系下的Q轴和D轴分量,经过环路滤波器后控制压控振荡器产生正弦波。环路滤波器的调整与前面Section 2.1所描述的类似。同理,正交信号发生器的系数可以根据电网频率和采样频率的变化进行动态调整。变量k用来调整正交信号发生器的输出信号频率。二阶广义积分器的使用还可以去除输入电压中的谐波成分,这可以通过采用相对较小的k值实现,但是这同时会削弱动态响应性能。

Figure 8展示了使用二阶广义积分器滤除五次谐波的效果。接下来讨论二阶广义积分器的实现:
Software Phase Locked Loop Design——锁相环软件设计_第9张图片
电网电压的有效值可以通过Equation 26得到:
v R M S = 1 2 v ‘ 2 + q v ‘ 2 (26) v_{RMS}=\frac{1}{\sqrt{2}}\sqrt{v^{`2}+qv^{`2}}\tag{26} vRMS=2 1v2+qv2 (26)

3.1 Simulating the Phase Locked Loop for Varying Conditions——锁相环动态仿真

在编写SPLL程序之前有必要对PLL在不同电网环境下的行为进行仿真,很多的并网变换器使用的都是定点处理器,IQ Math可以很方便地使用十进制数字表示定点数,C2000 IQ Math库提供的内置函数可以帮助程序员简化十进制定点数的处理。然而,使用定点数编程存在动态范围小,精度低的问题,因此有必要在仿真环境下模拟定点处理器的行为,以下是使用matlab定点工具箱测试锁相环在动态电网条件下的运行性能的脚本。

clear all;
close all;
clc;
% define the math type being used on the controller using objects from the
% fixed-point tool box in matlab
%Select numeric type, let's choose Q23
T=numerictype('WordLength',32,'FractionLength',23);
%Specify math attributes to the fimath object
F=fimath('RoundMode','floor','OverflowMode','wrap');
F.ProductMode='SpecifyPrecision';
F.ProductWordLength=32;
F.ProductFractionLength=23;
F.SumMode='SpecifyPrecision';
F.SumWordLength=32;
F.SumFractionLength=23;
%specify fipref object, to display warning in cases of overflow and
%underflow
P=fipref;
P.LoggingMode='on';
P.NumericTypeDisplay='none';
P.FimathDisplay='none';
%PLL Modelling starts from here
Fs=50000; %Sampling frequency = 50Khz
GridFreq=50; %Nominal Grid Frequency in Hz
Tfinal=0.2; %Time the simulation is run for = 0.5 seconds
Ts=1/Fs; %Sampling Time = 1/Fs
t=0:Ts:Tfinal; %Simulation Time vector
wn=2*pi*GridFreq; %Nominal Grid Frequency in radians
%declare arrays used by the PLL process
err=fi([0,0,0,0,0],T,F);
ylf=fi([0,0,0,0,0],T,F);
Mysin=fi([0,0,0,0,0],T,F);
Mycos=fi([1,1,1,1,1],T,F);
theta=fi([0,0,0,0,0],T,F);
dc_err=fi([0,0,0,0,0],T,F);
wo=fi(0,T,F);
% used for plotting
Plot_Var=fi([0,0,0,0],T,F);
Plot_theta=fi([0,0,0,0],T,F);
Plot_osgu=fi([0,0,0,0],T,F);
Plot_osgqu=fi([0,0,0,0],T,F);
Plot_D=fi([0,0,0,0],T,F);
Plot_Q=fi([0,0,0,0],T,F);
Plot_dc_err=fi([0,0,0,0,0],T,F);
%orthogonal signal generator
%using trapezoidal approximation
osg_k=0.5;
osg_x=2*osg_k*wn*Ts;
osg_y=(wn*wn*Ts*Ts);
osg_b0=osg_x/(osg_x+osg_y+4);
osg_b2=-1*osg_b0;
osg_a1=(2*(4-osg_y))/(osg_x+osg_y+4);
osg_a2=(osg_x-osg_y-4)/(osg_x+osg_y+4);
osg_qb0=(osg_k*osg_y)/(osg_x+osg_y+4);
osg_qb1=2*osg_qb0;
osg_qb2=osg_qb0;
osg_k=fi(osg_k,T,F);
osg_x=fi(osg_x,T,F);
osg_y=fi(osg_y,T,F);
osg_b0=fi(osg_b0,T,F);
osg_b2=fi(osg_b2,T,F);
osg_a1=fi(osg_a1,T,F);
osg_a2=fi(osg_a2,T,F);
osg_qb0=fi(osg_qb0,T,F);
osg_qb1=fi(osg_qb1,T,F);
osg_qb2=fi(osg_qb2,T,F);
osg_u=fi([0,0,0,0,0,0],T,F);
osg_qu=fi([0,0,0,0,0,0],T,F);
u_Q=fi([0,0,0],T,F);
u_D=fi([0,0,0],T,F);
%generate input signal
% CASE 1 : Phase Jump at the Mid Point
L=length(t);
for n=1:floor(L)
u(n)=sin(2*pi*GridFreq*Ts*n);
end
for n=1:floor(L)
u1(n)=sin(2*pi*GridFreq*Ts*n);
end
for n=floor(L/2):L
u(n)=sin(2*pi*GridFreq*Ts*n+pi/2);
end
u=fi(u,T,F);
% simulate the PLL process
for n=3:Tfinal/Ts % No of iteration of the PLL process in the simulation time
%Orthogonal Signal Generator
osg_u(1)=(osg_b0*(u(n)-u(n-2)))+osg_a1*osg_u(2)+osg_a2*osg_u(3);
osg_u(3)=osg_u(2);
osg_u(2)=osg_u(1);
osg_qu(1)=(osg_qb0*u(n)+osg_qb1*u(n-1)+osg_qb2*u(n-2))+osg_a1*osg_qu(2)+osg_a2*osg_qu(3);
osg_qu(3)=osg_qu(2);
osg_qu(2)=osg_qu(1);
%park trasnform from alpha beta to d-q axis
u_Q(1)=Mycos(2)*osg_u(1)+Mysin(2)*osg_qu(1);
u_D(1)=-Mysin(2)*osg_u(1)+Mycos(2)*osg_qu(1);
%Loop Filter
ylf(1)=fi(1,T,F)*ylf(2)+fi(166.877556,T,F)*u_Q(1)+fi(-166.322444,T,F)*u_Q(2);
u_Q(2)=u_Q(1);
u_D(2)=u_D(1);
%Limit LF according to its Q? size pipeline
ylf(1)=max([ylf(1) fi(-128,T,F)]);
ylf(1)=min([ylf(1) fi(128,T,F)]);
ylf(2)=ylf(1);
%update output frequency
wo=GridFreq+ylf(1);
%update the output phase
theta(1)=theta(2)+wo*fi(Ts,T,F);
if(theta(1)>fi(1.0,T,F))
theta(1)=fi(0,T,F);
end
theta(2)=theta(1);
Mysin(1)=sin(theta(1)*fi(2*pi,T,F));
Mycos(1)=cos(theta(1)*fi(2*pi,T,F));
Mysin(2)=Mysin(1);
Mycos(2)=Mycos(1);
Plot_theta(n+1)=theta(1);
Plot_osgu(n+1)=osg_u(1);
Plot_osgqu(n+1)=osg_qu(1);
Plot_Var(n+1)=Mysin(1);
Plot_D(n+1)=u_D(1);
Plot_Q(n+1)=u_Q(1);
end
% CASE 1 : Phase Jump at the Mid Point
error=Plot_Var-u;
%CASE 2 : Harmonics
%error=Plot_Var-u1;
%CASE 3: Frequency Variations
%error=Plot_Var-u;
%CASE 4: Amplitude Variations
%error=Plot_Var-u1;
subplot(3,1,1),plot(t,Plot_Var,'r',t,u,'b'),title('SPLL(red) & Ideal Grid(blue)');
subplot(3,1,2),plot(t,error,'r'),title('Error');
subplot(3,1,3),plot(t,u1,'r',t,Plot_Var,'b'),title('SPLL Out(Blue) & Ideal Grid(Red)');

SPLL_1ph_SOGI.m

Figure 9 展示了锁相环在动态条件下的运行结果。
Software Phase Locked Loop Design——锁相环软件设计_第10张图片

3.2 Implementing PLL on C2000 Controller Using IQ Math——运用IQ Math在C2000控制器上实现锁相环

一般逆变器都使用IQ24格式,但在 Section 3.1中使用了IQ23,因此PLL的代码可以写为:

#ifndef SPLL_1ph_SOGI_H_
#define SPLL_1ph_SOGI_H_
#define SPLL_SOGI_Q _IQ23
#define SPLL_SOGI_Qmpy _IQ23mpy
#define SPLL_SOGI_SINE _IQ23sin
#define SPLL_SOGI_COS _IQ23cos
//*********** Structure Definition ********//
typedef struct{
int32 osg_k;
int32 osg_x;
int32 osg_y;
int32 osg_b0;
int32 osg_b2;
int32 osg_a1;
int32 osg_a2;
int32 osg_qb0;
int32 osg_qb1;
int32 osg_qb2;
}SPLL_SOGI_OSG_COEFF;
typedef struct{
int32 B1_lf;
int32 B0_lf;
int32 A1_lf;
}SPLL_SOGI_LPF_COEFF;
typedef struct{
int32 u[3]; // Ac Input
int32 osg_u[3];
int32 osg_qu[3];
int32 u_Q[2];
int32 u_D[2];
int32 ylf[2];
int32 fo; // output frequency of PLL
int32 fn; //nominal frequency
int32 theta[2];
int32 cos;
int32 sin;
int32 delta_T;
SPLL_SOGI_OSG_COEFF osg_coeff;
SPLL_SOGI_LPF_COEFF lpf_coeff;
}SPLL_1ph_SOGI;
//*********** Function Declarations *******//
inline void SPLL_1ph_SOGI_init(int Grid_freq, long DELTA_T, volatile SPLL_1ph_SOGI *spll,
volatile SPLL_SOGI_LPF_COEFF lpf_coeff);
inline void SPLL_1ph_SOGI_coeff_update(float delta_T, float wn, volatile SPLL_1ph_SOGI *spll);
inline void SPLL_1ph_SOGI_run_FUNC(SPLL_1ph_SOGI *spll1);
//*********** Structure Init Function ****//
inline void SPLL_1ph_SOGI_init(int Grid_freq, long DELTA_T, volatile SPLL_1ph_SOGI *spll_obj,
volatile SPLL_SOGI_LPF_COEFF lpf_coeff)
{
spll_obj->u[0]=SPLL_SOGI_Q(0.0);
spll_obj->u[1]=SPLL_SOGI_Q(0.0);
spll_obj->u[2]=SPLL_SOGI_Q(0.0);
spll_obj->osg_u[0]=SPLL_SOGI_Q(0.0);
spll_obj->osg_u[1]=SPLL_SOGI_Q(0.0);
spll_obj->osg_u[2]=SPLL_SOGI_Q(0.0);
spll_obj->osg_qu[0]=SPLL_SOGI_Q(0.0);
spll_obj->osg_qu[1]=SPLL_SOGI_Q(0.0);
spll_obj->osg_qu[2]=SPLL_SOGI_Q(0.0);
spll_obj->u_Q[0]=SPLL_SOGI_Q(0.0);
spll_obj->u_Q[1]=SPLL_SOGI_Q(0.0);
spll_obj->u_D[0]=SPLL_SOGI_Q(0.0);
spll_obj->u_D[1]=SPLL_SOGI_Q(0.0);
spll_obj->ylf[0]=SPLL_SOGI_Q(0.0);
spll_obj->ylf[1]=SPLL_SOGI_Q(0.0);
spll_obj->fo=SPLL_SOGI_Q(0.0);
spll_obj->fn=SPLL_SOGI_Q(Grid_freq);
spll_obj->theta[0]=SPLL_SOGI_Q(0.0);
spll_obj->theta[1]=SPLL_SOGI_Q(0.0);
spll_obj->sin=SPLL_SOGI_Q(0.0);
spll_obj->cos=SPLL_SOGI_Q(0.0);
//coefficients for the loop filter
spll_obj->lpf_coeff.B1_lf=lpf_coeff.B1_lf;
spll_obj->lpf_coeff.B0_lf=lpf_coeff.B0_lf;
spll_obj->lpf_coeff.A1_lf=lpf_coeff.A1_lf;
spll_obj->delta_T=DELTA_T;
}
//*********** Structure Coeff Update *****//
inline void SPLL_1ph_SOGI_coeff_update(float delta_T, float wn, volatile SPLL_1ph_SOGI *spll)
{
float osgx,osgy,temp;
spll->osg_coeff.osg_k=SPLL_SOGI_Q(0.5);
osgx=(float)(2.0*0.5*wn*delta_T);
spll->osg_coeff.osg_x=SPLL_SOGI_Q(osgx);
osgy=(float)(wn*delta_T*wn*delta_T);
spll->osg_coeff.osg_y=SPLL_SOGI_Q(osgy);
temp=(float)1.0/(osgx+osgy+4.0);
spll->osg_coeff.osg_b0=SPLL_SOGI_Q((float)osgx*temp);
spll->osg_coeff.osg_b2=SPLL_SOGI_Qmpy(SPLL_SOGI_Q(-1.0),spll->osg_coeff.osg_b0);
spll->osg_coeff.osg_a1=SPLL_SOGI_Q((float)(2.0*(4.0-osgy))*temp);
spll->osg_coeff.osg_a2=SPLL_SOGI_Q((float)(osgx-osgy-4)*temp);
spll->osg_coeff.osg_qb0=SPLL_SOGI_Q((float)(0.5*osgy)*temp);
spll->osg_coeff.osg_qb1=SPLL_SOGI_Qmpy(spll-
>osg_coeff.osg_qb0,SPLL_SOGI_Q(2.0));
spll->osg_coeff.osg_qb2=spll->osg_coeff.osg_qb0;
}
//*********** Function Definition ********//
inline void SPLL_1ph_SOGI_run_FUNC(SPLL_1ph_SOGI *spll_obj)
{
// Update the spll_obj->u[0] with the grid value before calling this routine
//-------------------------------//
// Orthogonal Signal Generator //
//-------------------------------//
spll_obj->osg_u[0]=SPLL_SOGI_Qmpy(spll_obj->osg_coeff.osg_b0,(spll_obj->u[0]-
spll_obj->u[2]))+SPLL_SOGI_Qmpy(spll_obj->osg_coeff.osg_a1,spll_obj-
>osg_u[1])+SPLL_SOGI_Qmpy(spll_obj->osg_coeff.osg_a2,spll_obj->osg_u[2]);
spll_obj->osg_u[2]=spll_obj->osg_u[1];
spll_obj->osg_u[1]=spll_obj->osg_u[0];
spll_obj->osg_qu[0]=SPLL_SOGI_Qmpy(spll_obj->osg_coeff.osg_qb0,spll_obj-
>u[0])+SPLL_SOGI_Qmpy(spll_obj->osg_coeff.osg_qb1,spll_obj->u[1])+SPLL_SOGI_Qmpy(spll_obj-
>osg_coeff.osg_qb2,spll_obj->u[2])+SPLL_SOGI_Qmpy(spll_obj-
>osg_coeff.osg_a1,spll_obj->osg_qu[1])+SPLL_SOGI_Qmpy(spll_obj-
>osg_coeff.osg_a2,spll_obj->osg_qu[2]);
spll_obj->osg_qu[2]=spll_obj->osg_qu[1];
spll_obj->osg_qu[1]=spll_obj->osg_qu[0];
spll_obj->u[2]=spll_obj->u[1];
spll_obj->u[1]=spll_obj->u[0];
//-------------------------------------------------------//
// Park Transform from alpha beta to d-q axis //
//-------------------------------------------------------//
spll_obj->u_Q[0]=SPLL_SOGI_Qmpy(spll_obj->cos,spll_obj-
>osg_u[0])+SPLL_SOGI_Qmpy(spll_obj->sin,spll_obj->osg_qu[0]);
spll_obj->u_D[0]=SPLL_SOGI_Qmpy(spll_obj->cos,spll_obj->osg_qu[0])-
SPLL_SOGI_Qmpy(spll_obj->sin,spll_obj->osg_u[0]);
//---------------------------------//
// Loop Filter //
/---------------------------------//
spll_obj->ylf[0]=spll_obj->ylf[1]+SPLL_SOGI_Qmpy(spll_obj-
>lpf_coeff.B0_lf,spll_obj->u_Q[0])+SPLL_SOGI_Qmpy(spll_obj->lpf_coeff.B1_lf,spll_obj->u_Q[1]);
//spll_obj->ylf[0]=(spll_obj->ylf[0]>SPLL_SOGI_Q(20.0))?SPLL_SOGI_Q(20.0):spll_obj-
>ylf[0];
//spll_obj->ylf[0]=(spll_obj->ylf[0] < SPLL_SOGI_Q(-20.0))?SPLL_SOGI_Q(-
20.0):spll_obj->ylf[0];
spll_obj->ylf[1]=spll_obj->ylf[0];
spll_obj->u_Q[1]=spll_obj->u_Q[0];
//spll_obj->u_D[1]=spll_obj->u_D[0];
//---------------------------------//
// VCO //
//---------------------------------//
spll_obj->fo=spll_obj->fn+spll_obj->ylf[0];
spll_obj->theta[0]=spll_obj->theta[1]+SPLL_SOGI_Qmpy(SPLL_SOGI_Qmpy(spll_obj-
>fo,spll_obj->delta_T),SPLL_SOGI_Q(2*3.1415926));
if(spll_obj->theta[0]>SPLL_SOGI_Q(2*3.1415926))
spll_obj->theta[0]=spll_obj->theta[0]-SPLL_SOGI_Q(2*3.1415926);
spll_obj->theta[1]=spll_obj->theta[0];
spll_obj->sin=SPLL_SOGI_SINE(spll_obj->theta[0]);
spll_obj->cos=SPLL_SOGI_COS(spll_obj->theta[0]);
}
//*********** Macro Definition ***********//
#define SPLL_1ph_SOGI_run_MACRO(v) \
v.osg_u[0]=SPLL_SOGI_Qmpy(v.osg_coeff.osg_b0,(v.u[0]-
v.u[2]))+SPLL_SOGI_Qmpy(v.osg_coeff.osg_a1,v.osg_u[1])+SPLL_SOGI_Qmpy(v.osg_coeff.osg_a2,v.osg_u[2
]);\
v.osg_u[2]=v.osg_u[1];\
v.osg_u[1]=v.osg_u[10];\
v.osg_qu[0]=SPLL_SOGI_Qmpy(v.osg_coeff.osg_qb0,v.u[0])+SPLL_SOGI_Qmpyu(v.osg_coeff.osg_qb1,v.u[1])
+SPLL_SOGI_Qmpy(v.osg_coeff.osg_qb2,v.u[2])+SPLL_SOGI_Qmpy(v.osg_coeff.osg_a1,v.osg_qu[1])+
SPLL_SOGI_Qmpy(v.osg_coeff.osg_a2.v.osg_qu[2]);
\
v.osg_qu[2]=v.osg_qu[1]; \
v.osg_qu[1]=v.osg_qu[0]; \
v.u[2]=v.u[1]; \
v.u[1]=v.u[0]; \
v.u_Q[0]=SPLL_SOGI_Qmpy(v.cos,v.osg_u[0])+SPLL_SOGI_Qmpy(v.sin,v.osg_qu[0]); \
v.u_D[0]=SPLL_SOGI_Qmpy(v.cos,v.osg_qu[0])-SPLL_SOGI_Qmpy(v.sin,v.osg_u[0]); \
v.ylf[0]=v.ylf[1]+SPLL_SOGI_Qmpy(v.lpf_coeff.B0_lf,v.u_Q[0])+SPLL_SOGI_Qmpy(v.lpf_coeff.B1_lf,v.u_
Q[1]); \
v.ylf[1]=v.ylf[0]; \
v.u_Q[1]=v.u_Q[0]; \
v.fo=v.fn+v.ylf[0]; \
v.theta[0]=v.theta[1]+SPLL_SOGI_Qmpy(SPLL_SOGI_Qmpy(v.fov.v.delta_T),SPLL_Q(2*3.1415926)); \
if(v.theta[0]>SPLL_SOGI_Q(2*3.1415926)) \
v.theta[0]=SPLL_SOGI_Q(0.0); \
v.theta[1]=v.theta[0]; \
v.sin=SPLL_SOGI_SINE(v.theta[0]); \
v.cos=SPLL_SOGI_COS(v.theta[0]);
#endif/* SPLL_1ph_SOGI_H_ */

SPLL_1ph_SOGI.h
Software Phase Locked Loop Design——锁相环软件设计_第11张图片

要在最终应用中使用以上代码,需要包含以下头文件并声明SPLL结构体和环路滤波器系数:

#include "SPLL_1ph_SOGI.h"
SPLL_1ph_SOGI spll1;
SPLL_SOGI_LPF_COEFF spll_lpf_coef1;

Section 2.1可知,环路滤波器的系数是已知的,将其写入spll结构体的lpf变量中。

#define B0_LPF SPLL_SOGI_Q(166.877556)
#define B1_LPF SPLL_SOGI_Q(-166.322444)
#define A1_LPF SPLL_SOGI_Q(-1.0)
spll_lpf_coef1.B0_lf=B0_LPF;
spll_lpf_coef1.B1_lf=B1_LPF;
spll_lpf_coef1.A1_lf=A1_LPF;

在中断服务程序中调用***SPLL_1ph_SOGI_init()***,SPLL将会被作为参数与电网频率一起代入***SPLL_1ph_SOGI_coeff_update()***中执行。

SPLL_1ph_SOGI_init(GRID_FREQ,_IQ23((float)(1.0/ISR_FREQUENCY)),&spll2,spll_lpf_coef2);
SPLL_1ph_SOGI_coeff_update(((float)(1.0/ISR_FREQUENCY)),(float)(2*PI*GRID_FREQ),&spll2);

在中断服务程序中,从ADC中读取正弦输入电压值并将其传入SPLL结构体中,将当前电网相角的正弦值赋值给invsine变量,这将被用在后续的控制中。

inv_meas_vol_inst =((long)((long)VAC_FB<<12))-offset_165)<<1;
spll1.AC_input=(long)InvSine>>1; // Q24 to Q23
SPLL_1ph_SOGI_run_FUNC(&spll1);
InvSine=spll2.sin<<1; // shift from Q23 to Q24

3.3 Result of SOGI PLL——二阶广义积分锁相环的运行结果

Figure 10可见SOGI锁相环动态响应速度很快。

Software Phase Locked Loop Design——锁相环软件设计_第12张图片

5 References——参考

  • Francisco D. Freijedo et al, “Robust Phase Locked Loops Optimized for DSP implementation in Power Quality Applications”, IECON 2008, 3052-3057
  • TMS320F28030, TMS320F28031, TMS320F28032, TMS320F28033, TMS320F28034, TMS320F28035 Piccolo Microcontrollers Data Manual (SPRS584)
  • C2000 controlSUITE
  • Marco Liserre, Pedro Rodriguez Remus Teodrescu, Grid Converters for Phototvoltaic and Wind Power Systems: John Wiley & Sons Ltd., 2011.
  • P. Rodriguez et al, “Double Synchronous Reference Frame PLL for Power Converters Control,” vol. 22, no. 2, 2007.
  • http://www.ti.com.cn/cn/lit/an/sprabt3a/sprabt3a.pdf?ts=1588461365101

你可能感兴趣的:(经验分享)