代码:
https://download.csdn.net/download/weixin_37928884/81123943
https://download.csdn.net/download/weixin_37928884/69778708
VS2012创建过程
WinCC调用C++生成的DLL文件
WinCC全局脚本
#include "apdefap.h"
int gscAction( void )
{
// WINCC:TAGNAME_SECTION_START
// syntax: #define TagNameInAction "DMTagName"
// next TagID : 1
// WINCC:TAGNAME_SECTION_END
// WINCC:PICNAME_SECTION_START
// syntax: #define PicNameInAction "PictureName"
// next PicID : 1
// WINCC:PICNAME_SECTION_END
//#pragma code("C:\Users\Test\Desktop\math_boiler\siemens\Debug\siemens_fb41.dll")
#pragma code("C:\Users\Test\Desktop\math_boiler\siemens\Release\siemens_fb41.dll")
float TCONT_C(float PV_IN41,float SP_INT41,float DISV41,float GAIN41,float TI41,float TD41,float DEADB_W41,float MAN41,float LMN_LLM41,float LMN_HLM41,unsigned char MAN_ON41);
#pragma code()
float siemens_lmn1;
float siemens_lmn2;
siemens_lmn1 = TCONT_C(98,100,0,2.0,40,20,0,50,0,100,0);
siemens_lmn2 = TCONT_C(98,100,0,2.0,40,20,0,50,0,100,1);
printf("siemens.FB41.lmn:%4.4f,%4.4f\r\n",siemens_lmn1,siemens_lmn2);
return 0;
}
siemens_fb41.h
#pragma once
extern "C" __declspec(dllexport) float TCONT_C(float PV_IN41,float SP_INT41,float DISV41,float GAIN41,float TI41,float TD41,float DEADB_W41,float MAN41,float LMN_LLM41,float LMN_HLM41,unsigned char MAN_ON41);
siemens_fb41.cpp
// siemens_fb41.cpp : 定义 DLL 应用程序的导出函数。
//
#include "stdafx.h"
// siemens_fb41.cpp : 定义控制台应用程序的入口点。
//
#include "siemens_fb41.h"
#ifndef TRUE
#define TRUE 0x01
#endif
#ifndef FALSE
#define FALSE 0x00
#endif
unsigned char COM_RST = FALSE; //完全重启动
unsigned char MAN_ON = FALSE ;//TRUE; //手动值打开
unsigned char PVPER_ON = FALSE; //外设过程变量打开
unsigned char P_SEL = FALSE; //比例作用打开
unsigned char I_SEL = TRUE; //积分作用打开
unsigned char INT_HOLD = FALSE; //积分作用保持
unsigned char I_ITL_ON = FALSE; //积分作用初始化
unsigned char D_SEL = FALSE; //微分作用打开
//CYCLE : TIME := T#1S; //采样时间
float CYCLE = 1;
float SP_INT = 0.0;//内部设定值
float PV_IN = 0.0; //过程变量输入
//PV_PER : WORD := W#16#0; //外设过程变量
int PV_PER = 0;
float MAN = 0.0; //手动值
float GAIN = 2.0; //比例增益
//TI : TIME := T#20S; //积分复位时间
float TI = 20;
//TD : TIME := T#10S; //微分时间
float TD = 10;
//TM_LAG : TIME := T#2S; //微分作用时间延时
float TM_LAG = 2;
float DEADB_W = 0.0; //死区带宽
float LMN_HLM = 100.0; //积分值上限
float LMN_LLM = 0.0; //积分值下限
float PV_FAC = 1.0; //过程变量因子
float PV_OFF = 0.0; //过程变量偏移量
float LMN_FAC = 1.0; //调节值因子
float LMN_OFF = 0.0; //调节值偏移量
float I_ITLVAL = 0.0; //积分作用的初始化值
float DISV = 0.0; //干扰变量
float LMN = 0.0; //调节值
//LMN_PER : WORD := W#16#0; //外设调节值
float LMN_PER = 0;
unsigned char QLMN_HLM = FALSE; //达到调节值上限
unsigned char QLMN_LLM = FALSE; //达到调节值下限
float LMN_P = 0.0; //比例分量
float LMN_I = 0.0; //积分分量
float LMN_D = 0.0; //微分分量
float PV = 0.0; //
float ER = 0.0; //误差信号
float sInvAlt = 0.0; //上周期比例偏差值
float sIanteilAlt = 0.0; //上周期积分值
float sRestInt = 0.0; //上周期积分偏差量浮点数计算偏差
float sRestDif = 0.0; //上周期微分偏差量浮点数计算偏差
float sRueck = 0.0; //
float sLmn = 0.0; //上周期调节值
unsigned char sbArwHLmOn = FALSE; //上周期达到调节值上限
unsigned char sbArwLLmOn = FALSE; //上周期达到调节值下限
unsigned char sbILimOn = TRUE; //备用-本程序没有使用该变量
float TCONT_C(float PV_IN,float SP_INT,float DISV,float GAIN,float TI,float TD,float DEADB_W,float MAN,float LMN_LLM,float LMN_HLM,unsigned char MAN_ON)
{
float LMN = 0;
//VAR_TEMP
float rCycle; //采样时间浮点值
float Iant; //积分增量
float Diff; //积分量
float Istwert; //过程变量浮点值
float ErKp; //偏差比例值
float rTi; //积分时间浮点值
float rTd; //微分时间浮点值
float rTmLag; //微分作用时间延时浮点值
float Panteil; //比例值
float Ianteil; //积分值
float Danteil; //微分值
float Verstaerk; //
float RueckDiff; //
float RueckAlt; //上周期积分量
float dLmn; //调节量
float gf; //Hilfwert
float rVal; //Real Hilfsvariable
if (COM_RST == TRUE) //PID初始化
{
sIanteilAlt = I_ITLVAL ;
LMN = 0.0 ;
QLMN_HLM = FALSE ;
QLMN_LLM = FALSE ;
LMN_P = 0.0 ;
LMN_I = 0.0 ;
LMN_D = 0.0 ;
LMN_PER = 0 ; //LMN_PER := W#16#0 ;
PV = 0.0 ;
ER = 0.0 ;
sInvAlt = 0.0 ;
sRestInt = 0.0 ;
sRestDif = 0.0 ;
sRueck = 0.0 ;
sLmn = 0.0 ;
sbArwHLmOn = FALSE ;
sbArwLLmOn = FALSE ;
}
else
{
rCycle = CYCLE / 1000.0 ; //采样时间换为浮点数值//rCycle := DINT_TO_REAL( TIME_TO_DINT( CYCLE ) ) / 1000.0 ; //采样时间换为浮点数值
Istwert = PV_PER * 0.0036898 ;//Istwert := DINT_TO_REAL( INT_TO_DINT( WORD_TO_INT ( PV_PER ) ) ) * 0.0036898 ;
Istwert = Istwert * PV_FAC + PV_OFF ; //外设输入转换为浮点数值
if (PVPER_ON == FALSE) //过程变量选择
{
Istwert = PV_IN ;
}
PV = Istwert ;
ErKp = SP_INT - PV ; //计算偏差
if (ErKp < -DEADB_W)
{
ER = ErKp + DEADB_W ;
}
else if(ErKp > DEADB_W)
{
ER = ErKp - DEADB_W ;
}
else
{
ER = 0.0 ;
}
ErKp = ER * GAIN ; //偏差比例增益
rTi = TI / 1000.0 ; //rTi := DINT_TO_REAL( TIME_TO_DINT( TI ) ) / 1000.0 ;
rTd = TD / 1000.0 ;//rTd := DINT_TO_REAL( TIME_TO_DINT( TD ) ) / 1000.0 ;
rTmLag = TM_LAG / 1000.0 ; //rTmLag := DINT_TO_REAL( TIME_TO_DINT( TM_LAG ) ) / 1000.0 ;
if (rTi < rCycle * 0.5) //积分时间必须 >= 采样时间的0.5倍
{
rTi = rCycle * 0.5;
}
if (rTd < rCycle) //微分时间必须 >= 采样时间
{
rTd = rCycle ;
}
if (rTmLag < rCycle * 0.5) //微分作用延时时间必须 >= 采样时间的0.5倍
{
rTmLag = rCycle * 0.5 ;
}
if (P_SEL == TRUE) //比例作用投入
{
Panteil = ErKp ;
}
else
{
Panteil = 0.0 ;
}
if (I_SEL == TRUE) //积分作用投入
{
if (I_ITL_ON == TRUE) //积分初始化
{
Ianteil = I_ITLVAL ;
sRestInt = 0.0 ;
}
else if(MAN_ON == TRUE) //手动值输入时的积分量计算用于用于手动切换自动无扰切换
{
Ianteil = sLmn - Panteil - DISV ;
sRestInt = 0.0 ;
}
else //积分计算
{
Iant = ( rCycle / rTi ) * ( ErKp + sInvAlt ) * 0.5 + sRestInt ;
if (((Iant > 0.0 && sbArwHLmOn ) || INT_HOLD ) || ( Iant < 0.0 && sbArwLLmOn )) //抗积分饱和
{
Iant = 0.0 ;
}
Ianteil = sIanteilAlt + Iant ; //当前积分值 := 上时刻积分值 + 本次积分量
sRestInt = sIanteilAlt - Ianteil + Iant ; //感觉 sRestInt一直等于0.0 ;不知为何,通过运行发现不为0.0可能是浮点数计算误差
}
}
else
{
Ianteil = 0.0 ;
sRestInt = 0.0 ;
}
Diff = ErKp ;
if (MAN_ON == FALSE && D_SEL == TRUE) //微分作用投入
{
Verstaerk = rTd / ( rCycle * 0.5 + rTmLag ) ;
Danteil = ( Diff - sRueck ) * Verstaerk ;
RueckAlt = sRueck ;
RueckDiff = rCycle / rTd * Danteil + sRestDif ;
sRueck = RueckDiff + RueckAlt ;
sRestDif = RueckAlt - sRueck + RueckDiff ; //同积分一样计算微分误差量
}
else
{
Danteil = 0.0 ;
sRestDif = 0.0 ;
sRueck = Diff ;
}
dLmn = Panteil + Ianteil + Danteil + DISV ; //PID输出
if (MAN_ON == TRUE) //PID手动之打开
{
dLmn = MAN ;
}
else
{
if ((I_ITL_ON == FALSE) && I_SEL == TRUE) //干扰量处理
{
if (Ianteil > LMN_HLM - DISV && dLmn > LMN_HLM && dLmn - LMN_D > LMN_HLM)
{
rVal = LMN_HLM - DISV ;
gf = dLmn - LMN_HLM ;
rVal = Ianteil - rVal;
if (rVal > gf)
{
rVal = gf ;
}
Ianteil = Ianteil - rVal ;
}
else if(Ianteil < LMN_LLM - DISV && dLmn < LMN_LLM && dLmn - LMN_D < LMN_LLM)
{
rVal = LMN_LLM - DISV ;
gf = dLmn - LMN_LLM ;
rVal = Ianteil - rVal ;
if (rVal < gf)
{
rVal = gf ;
}
Ianteil = Ianteil - rVal ;
}
}
}
LMN_P = Panteil ;
LMN_I = Ianteil ;
LMN_D = Danteil ;
sInvAlt = ErKp ;
sIanteilAlt = Ianteil ;
sbArwHLmOn = FALSE ;
sbArwLLmOn = FALSE ;
if (dLmn >= LMN_HLM) //调节辆限幅(上限)
{
QLMN_HLM = TRUE ;
QLMN_LLM = FALSE ;
dLmn = LMN_HLM ;
sbArwHLmOn = TRUE ;
}
else
{
QLMN_HLM = FALSE ;
if(dLmn <= LMN_LLM) //调节辆限幅(下限)
{
QLMN_LLM = TRUE ;
dLmn = LMN_LLM ;
sbArwLLmOn = TRUE ;
}
else
{
QLMN_LLM = FALSE ;
}
}
sLmn = dLmn ;
dLmn = dLmn * LMN_FAC + LMN_OFF ;
LMN = dLmn ;
dLmn = dLmn* 276.48 ;
if (dLmn >= 32512.0)
{
dLmn = 32512.0 ;
}
else if(dLmn <= -32512.0)
{
dLmn = -32512.0 ;
}
LMN_PER = dLmn;//LMN_PER := INT_TO_WORD( DINT_TO_INT( REAL_TO_DINT( dLmn) ) ) ;
}
//FB"CONT_C"的控制模式从手动切换到自动是无扰切换,反之不是无扰切换,需要通过编程实现。
//MAN_ON == 0 自动
//MAN_ON == 1 手动
//增加如下程序,来实现自动切手动时的无扰动切换
if(MAN_ON == 0)
{//上升沿 自动转手动
MAN = LMN;
}
return LMN;
}