分享一个水泥余热发电PCS7(DCS)项目,利用SCL编程实现CFC(Continuous Function Chart,连续功能图)自定义跳闸首出(确定第一个引起汽轮机跳闸的首要条件)的程序。该功能块有20个DI输入信号(跳闸条件)、1个输出信号(YTripCH,跳闸首出DI通道号)、1个复位信号(Reset,Bool类型),可确定20个输入信号当中哪个是引起汽轮机跳闸的首要条件。首要条件确定后,必须通过复位信号且故障信号消失,才能将输出信号清零。
控制系统选择西门子S7-417H冗余DCS系统,通过PCS7 V7.0 SP2进行编程,详细开发过程请见附录(1.硬件组态;2.网络组态;3.定义DI点;4.SCL编程;5.将SCL源文件编译为CFC可调用的功能块;6.CFC编程;7.总结)。
附录:
附录-SCL编程案例征集活动示例程序.pdf
1. 硬件组态
该系统CPU选择417-4H冗余系统,主备CPU切换时间100ms至300ms之间,可实现余热发电系统控制系统无扰切换,确保控制不中断,过程状态不丢失。CPU主站有四个分布式EM200子站,分别是APL汽机子站、AQC子站、SP1&SP2子站、SP3&SP4&SP5子站。主站与子站通过Profibus-DP通讯协议连接,由于通讯距离较长,使用OLM模块进行光电转换以实现长距离通讯,如图1所示。

图1 硬件组态
2. 网络组态
该系统有1个ES(工程师站)、5个OS(操作员站)。PCS7系统可以通过ES站将上位机程序便捷的下载到指定的OS站,避免了通过U盘或设置共享硬盘拷贝程序的繁杂操作,如图2所示。

图2 网络组态
3. 定义DI点
利用导入/导出功能,快速将IO清单导入到DB块中,并设置变量在PCS系统是可见的(S7_m_c:='true' ),如图3所示。

图3 定义DI变量
4. SCL编程
通过PCS7 SCL编辑器根据应用场景及业务逻辑进行编程,如图4所示。

图4 SCL编程
主要源程序如下;
FUNCTION_BLOCK "TripFirst"
TITLE = 'TripFirst'
AUTHOR : YangHong
FAMILY : SBS_BLK
NAME : FirstOut
KNOW_HOW_PROTECT
VERSION: '2.00'
VAR_INPUT
IN1{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN2{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN3{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN4{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN5{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN6{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN7{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN8{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN9{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN10{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN11{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN12{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN13{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN14{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN15{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN16{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN17{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN18{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN19{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
IN20{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
Reset:BOOL:=FALSE; //Reset Sign:Pulse
END_VAR
VAR_OUTPUT
OUT1{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT2{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT3{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT4{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT5{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT6{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT7{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT8{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT9{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT10{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT11{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT12{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT13{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT14{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT15{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT16{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT17{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT18{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT19{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
OUT20{S7_dynamic:='true';S7_m_c:='true'}:BOOL:=FALSE;
YAlarm:BOOL:=FALSE; //First:Alarm
YTripCH:INT:=0; //First:Channel Number
YStep:INT:=0;
END_VAR
LABEL
YAlarmFirst;//Jump Lable
END_LABEL
VAR_TEMP
//Here Define Temp Variable
END_VAR
BEGIN
YStep:=8;
IF IN1 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=1;OUT1:=TRUE;RETURN;END_IF;
IF IN2 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=2;OUT2:=TRUE;RETURN;END_IF;
IF IN3 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=3;OUT3:=TRUE;RETURN;END_IF;
IF IN4 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=4;OUT4:=TRUE;RETURN;END_IF;
IF IN5 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=5;OUT5:=TRUE;RETURN;END_IF;
IF IN6 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=6;OUT6:=TRUE;RETURN;END_IF;
IF IN7 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=7;OUT7:=TRUE;RETURN;END_IF;
IF IN8 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=8;OUT8:=TRUE;RETURN;END_IF;
IF IN9 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=9;OUT9:=TRUE;RETURN;END_IF;
IF IN10 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=10;OUT10:=TRUE;RETURN;END_IF;
IF IN11 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=11;OUT11:=TRUE;RETURN;END_IF;
IF IN12 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=12;OUT12:=TRUE;RETURN;END_IF;
IF IN13 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=13;OUT13:=TRUE;RETURN;END_IF;
IF IN14 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=14;OUT14:=TRUE;RETURN;END_IF;
IF IN15 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=15;OUT15:=TRUE;RETURN;END_IF;
IF IN16 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=16;OUT16:=TRUE;RETURN;END_IF;
IF IN17 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=17;OUT17:=TRUE;RETURN;END_IF;
IF IN18 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=18;OUT18:=TRUE;RETURN;END_IF;
IF IN19 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=19;OUT19:=TRUE;RETURN;END_IF;
IF IN20 = 1 AND Reset = 1 THEN YAlarm:=TRUE;YTripCH:=20;OUT20:=TRUE;RETURN;END_IF;
IF YAlarm = 0 AND Reset = 1 THEN
YAlarm :=FALSE;YTripCH :=0;YStep:=0;
OUT1:=FALSE;OUT2:=FALSE;OUT3:=FALSE;OUT4:=FALSE;OUT5:=FALSE;OUT6:=FALSE;OUT7:=FALSE;OUT8:=FALSE;OUT9:=FALSE;OUT10:=FALSE;
OUT11:=FALSE;OUT12:=FALSE;OUT13:=FALSE;OUT14:=FALSE;OUT15:=FALSE;OUT16:=FALSE;OUT17:=FALSE;OUT18:=FALSE;OUT19:=FALSE;OUT20:=FALSE;
END_IF;
RETURN;
END_FUNCTION_BLOCK
5. 将SCL源文件编译为CFC可调用的功能块
SCL源文件编写完成后,将源文件编译,如图5所示。

图5 编译SCL源文件
编译后可生成CFC跳闸首出功能块,可右键【属性】查看管脚的说明,如图6所示。

图6 生成跳闸首出功能块及管脚说明
6. CFC编程
在CFC编辑环境中,建立跳闸首出模块I1_1输入与DI.TPS1300_A点位的连接;I1_2输入与DI.TPS1300_B点位的连接;I1_3输入与DI.TPS1300_C点位的连接;I1_4输入与DI.TPS1300_D点位的连接;如图7所示。

图7 CFC编程
7. 总结

图8 水泥余热发电工艺流程
基于SCL编程构建的核心控制系统自2010年8月正式投运至今,已连续安全稳定运行近15年。该系统依托SCL语言严谨的结构化特性、强数据类型校验及模块化设计优势,在长期高负荷运行中始终保持零重大事故记录,充分验证了SCL在工业关键控制场景中的超高可靠性与鲁棒性。其代码执行的确定性、异常处理机制的完备性,以及冗余架构下的无缝故障切换能力,为装置的长周期安全运行提供了坚实的技术保障,是工业自动化领域经得起时间考验的编程典范。