并网逆变器的clark、park变换的C语言实现技巧
坐标变换,及c语言实现的讲解多如牛毛。
在此也做点整理,加入一根牛毛。偏重用什么样的C语言语法技巧实现这些变换来做双环控制的。
此处用到了C语言在嵌入式领域非常常用的一个语法:函数指针。
指针是个变量,那么函数指针也是个变量。函数指针变量装的内容是一个函数的首地址。
函数指针的定义为:函数返回值类型 (* 指针变量名) (函数参数列表);
函数指针放入结构体,提供了C++的面向对象的思想,便于模块的封装和层次化。
坐标变换过程中有许多变量,同时有数个函数,如果在C++中,我们可以定义一个类,但是在C语言中我们只能定义一个结构体组织这些紧密联系的成员和算法实现。
以三相电网电压为例
typedef struct{
float32 a,b,c;//存放电网电压当前值
float32 alpha,beta;//存放电网电压变换到alpha,beta轴上的值
float32 d,q;//存放电网电压变换到d,q轴上的值
float32 theta;//存放电网电压当前相位角度
float32 sin_sita,cos_sita;//存放根据theta计算出来的正弦余弦值
void (*Clarke)();//坐标变换有四个函数,每个函数在内存都有一块区域,定义四个函数指针变量,指向这四个函数的首地址
void (*Park)(); //注意函数指针的定义的第二个括号里面是最好不要写东西的。因为函数指针的意思是这个指针变量指向任意 void (*InvPark)(); //函数,我们并不知道指向的函数的参数情况,所以在定义函数指针的时候,不能指定函数参数。
void (*InvClarke)();
}CORDI_TRANSFER; //Coordinate transformation
上面的数据类型中,有四个函数指针,那么我们还要定义四个坐标变换函数。
// Clarke
void clarke(CORDI_TRANSFER *p)
{
p->alpha = COE_2DIVID3*(p->a - 0.5*p->b - 0.5*p->c);
p->beta = COE_2DIVID3*(HALF_SQRT_3*p->b - HALF_SQRT_3*p->c);
}
// park
void park(CORDI_TRANSFER *p)
{
p->d = p->alpha * p->sin_sita - p->beta * p->cos_sita;
p->q = -p->alpha * p->cos_sita - p->beta * p->sin_sita;
}
// inv-clarke
void inv_clarke(CORDI_TRANSFER *p)
{
p->a = p->alpha;
p->b = - 0.5*p->alpha + HALF_SQRT_3*p->beta;
p->c = - 0.5*p->alpha - HALF_SQRT_3*p->beta;
}
// inv-park
void inv_park(CORDI_TRANSFER *p)
{
p->alpha = p->d*p->sin_sita - p->q*p->cos_sita;
p->beta = - p->d*p->cos_sita - p->q*p->sin_sita;
}
上面四个函数的定义最好写在CORDI_TRANSFER结构体定义之后,因为函数参数的类型是CORDI_TRANSFER,不然可能会报错,我没有试过。
上面定义了数据类型,把数据和算法实现给内存做了恰当的指示,但是编译器目前还不会按照上面的数据类型分配内存你空间,而是要根据这个数据类型定义变量,之后在给这个变量根据这种数据类型分配内存你空间。并且还有把这个变量初始化
CORDI_TRANSFER ClarkeParkSwitch = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
clarke,//这四个函数名代表函数的首地址,而定义CORDI_TRANSFER 的时候,
inv_park,//定义了四个函数指针,那么初始化,自然要把函数的首地址赋值给这四个
inv_clarke//变量
};
基本的数据和算法实现准备就做好了。
下面开始坐标变换啦,以clark变换为例。
把三相电压变换成两相电压;
1、输入采样滤波后的三相电EUa,Ub,Uc;压存放在ClarkeParkSwitch.a, ClarkeParkSwitch.b, ClarkeParkSwitch.c :
ClarkeParkSwitch.a=Ua, ClarkeParkSwitch.b=Ub, ClarkeParkSwitch.c
2、调用void clarke(CORDI_TRANSFER *p)函数,
进行clark变换:ClarkeParkSwitch.Clarke(&ClarkeParkSwitch)。这里需要解释,clark变换函数的调用有点特殊,是利用函数指针变量调用的,因为初始化变量ClarkeParkSwitch时,四个函数的指针都初始化给了结构体里面的四个函数指针。
ClarkeParkSwitch.Clarke(&ClarkeParkSwitch)这句话完成的的功能和clarke(&ClarkeParkSwitch)是等价的。执行滞后,
计算出Ualpha和Ubelta的值,存放于ClarkeParkSwitch. alpha, ClarkeParkSwitch.beta中。
后面的几个变换按照上面的类推就行。