一般而言,我们使用POU都是为了将特定的功能,做成一个函数,在需要重复使用这个功能的时候,只需要调用这个函数,分配给他对应的寄存器进行数据运算即可。免去重复写很多相同的程序。POU分为FC与FB两种,在使用POU进行封装的时候,我们要考虑以下几件事情:1、这个“功能”是在不同地方重复使用的么?这关系到封装是否有意义。2、这个“功能”是一个小功能么?3、如果不是那么可以继续拆分成几个小功能么?。思考完这几个问题后,我们可以得出一个基本的结构,最小的功能单元由FC实现,最大的整体的由FB来实现。因为FC自己不占用数据空间,可以打个比方,我们有一个数学函数f(x),给x一个值,就可以得出另一个值。而FB需要为其分配数据区,就像一个函数集合,写在一张纸上,想要得出一个最终结果,输入值会在若干个函数中来回传递,中间产生的每一段临时结果会被写在空白的地方辅助下一步计算。所以下面我会举例说明。
我们在程序开发中有时会遇到设备累计运行时间,某机构累计运行时间,如定时提示更换、维保。针对这种场景,如果开发一个POU,那么在不同设备上,或者一个设备的不同工位上,就可以重复调用,不用写完一遍来回复制。对于这个功能我们可以先进行开发分析,累计时间我们可以使用系统内置的时钟脉冲来累计,然后这里面涉及到很多的运算,数据需要存储所以整体要用FB,内部可以拆出一个采集时钟脉冲上升沿的FC。然后我们进行开发,首先开发上升沿采集函数。
首先定义一个边沿采集函数结构体
Struct
{
Bool Ex;//采集对象
Bool Status;//状态锁存
Bool Q;//边沿输出
}Edge_Trige_Struct;
然后定义上升沿函数FC,
声明区
VAR_INOUT Edge Edge_Trige_Struct
函数区
void R_Trige(Edge_Trige_Struct* Edge)//定义一个名为Edge的Edge_Trige_Struct类型结构体指针
{
#define SysRegAddr_HD_D_HM_M
if(Edge->Ex == 1 && Edge->Status == 0)//当此刻为1锁存为0,则认为0~1
{
Edge->Q = 1;//输出1
}
else
{
Edge->Q = 0;
}
Edge->Status = Edge->Ex;//锁存此刻状态
}
上升沿函数开发完毕后继续开发主体函数。
声明区
VAR_INPUT Start Bool//启动计时器
VAR Second Real//中间计算量-秒
VAR MinuteReal//中间计算量-秒
VAR HourReal//中间计算量-秒
VAR Day Real//中间计算量-秒
VAR U_M Real//累计分钟
VAR U_H Real//累计小时
VAR U_D Real//累计天数
VAR R_Trige0 Edge_Trige_Struct//边沿采集函数参数结构体传递
VAR_OUTPUT Minutes//输出累计分钟
VAR_OUTPUT Hours//输出累计小时
VAR_OUTPUT Days//输出累计天数
函数区
void TMR_S_BODY(TMR_S* self)//定义一个名为self的TMR_S类型结构体指针
{
#define SysRegAddr_HD_D_HM_M_SM
if ( self->Start )
{
self->R_Trige0.Ex = SM[13];//上升沿函数结构体采集对象赋值
R_Trige ( &self->R_Trige0 );//函数内部参数是结构体指针形式,所以外部要以取地址形式来写
if ( self->R_Trige0.Q )
{
self->Second = self->Second + 1;
if ( self->Second >= 60 )
{
self->Minute = self->Minute + 1;
self->Second = 0;
}
if ( self->Minute >= 60 )
{
self->Hour = self->Hour + 1;
self->Minute = 0;
}
if ( self->Hour >= 24 )
{
self->Day = self->Day + 1;
self->Hour = 0;
}
}
}
self->U_M = self->Day * 24 * 60 + self->Hour * 60 + self->Second / 60 + self->Minute;
self->U_H = ( self->Day * 24 ) + ( self->Hour ) + ( self->Minute / 60 ) + ( self->Second / 3600 );
self->U_D = ( self->Day ) + ( self->Hour / 24 ) + ( self->Minute / 1440 ) + ( self->Second / 86400 );
self->Minutes = self->U_M;
self->Hours = self->U_H;
self->Days = self->U_D;
}
其实写到这里,如果看明白了,你会发现我这似乎是把FC写成了FB,因为都变成了结构体指针传递的形式。其实我一开始的边沿函数是三个参数直接写在外面的,然后FB内部定义静态变量,再放给函数参数。这样可以少一个结构体,相对来说灵活,但不够简洁。写这篇文章的时候,我发现如把边沿函数的参数改为结构体形式,可能会使看到这里的你恍然大悟,对于FB与FC理解的更透彻,所以直接修改了一下。一般我们希望FC实现的是y=kx这样给一个x立刻出一个y不需要上面那种锁存的功能。所以也可以理解为瞬时的动作我们用FC,连续的处理用FB。实际上在IEC标准下,例如TIAPORTAL或者CODESYS里,边沿函数都是FB的形式。
本篇就到这里,受本人水平所限,错漏之处请多包涵。