BeckHoff <---> Keyence (LJ-8000) 2D相机 Profinet 通讯
目录
编辑
一、 设备介绍
1、产品特点
2、控制器选择
3、应用领域
二、PLC通讯接口配置
1、PLC添加GSDML文件
2、定义输入3、变量实例化
3、定义输出变量实例化
三、设备通讯接口数据类型定义
1、定义全局结构体数据
2、定义 INput Decode结构体数据
四、通讯功能块变量声明
五、通讯功能块程序
六、通讯功能块程序分析
6.1 代码功能分析
6.2整体架构解析
6.3 详细功能说明
初始化与全局变量
工作模式与命令请求处理
状态机主逻辑(CASE 结构)
错误处理与诊断
6.4 关键数据结构与接口
6.5 总结
七、通讯功能块功能描述及可能的改进空间
7.1 功能块描述
7.2 改进空间
八、附录
1、recive_data 子程序
2、send_data 子程序
一、 设备介绍
以下是基恩士 LJ-X8000 系列中的 2D 产品介绍:
1、产品特点
- 高精度测量:采用 3200points/profile 超高精度测量,能够极其精确地绘制目标物形状,实现精确的尺寸测量和外观检测,测量精度可达以往产品的 4 倍1。
- 高速采样:2D 模式下,采样周期最高可达 1kHz(1ms),可快速获取测量数据,满足在线检测等高速测量需求3。
- 易于设定:只需三步即可完成测量、检测所需的设定,操作轻松简单,兼顾了高精度和易操作性2。
- 丰富的工具与功能:每个设定有 100 个工具,其中 20 个为错位修正用,可通过识别工件的错位及倾斜,并自动修正,实现不受定位影响的在线全检3。
- 多种数据接口:具备 RS-232C、PLC 链接、USB、EtherNet/IP™、PROFINET、EtherCAT® 等丰富的接口,可方便地与其他设备进行连接和数据传输,实现系统集成和自动化控制3。
- 稳定可靠:采用圆柱形透镜防止反射光散射,配备大孔径接收器透镜增加接收光强度,新开发的 CMOS 在不同反射率的目标上也能提供更好的成像能力,确保测量的稳定性和可靠性2。
2、控制器选择
- LJ-X8000:适用于追求简单快速地实现在线检测场合,可连接最多 2 台感测头,支持 LJ-X8000 系列感测头、LJ-V7000 系列感测头。
- LJ-X8000E:提供用户高自由度编程的环境以及自行开发的选择,是追求以专有程序实现难度高、应用复杂检测的理想选择。
3、应用领域
- 电子制造:可对精密电子元件进行细微检测,如芯片封装、印刷电路板(PCB)的尺寸测量、焊点检测、元器件高度检测等,保证电子元件的质量和性能。
- 汽车制造:用于汽车零部件的尺寸控制、形状检测、装配精度检测等,如汽车发动机缸体、缸盖、曲轴、轮毂等零部件的测量和检测,确保零部件的质量和装配精度。
- 机械加工:用于机械加工零件的尺寸精度测量、形位公差检测、表面粗糙度测量等,帮助机械加工企业提高产品质量和加工精度。
- 其他领域:还可应用于建筑材料的均匀性检查、电缆的凹凸检测、端子的间距和高度差检测等多个领域
二、PLC通讯接口配置
1、PLC添加GSDML文件
2、定义2D相机的全局变量
2、定义输入3、变量实例化
3、定义输出变量实例化
三、设备通讯接口数据类型定义
1、定义全局结构体数据
2、定义 INput Decode结构体数据
TYPE ST_Laser_Profiler_Input_Decode : //Result_Data_128Byte + 32 x 5 Byte = 288 Bytes
STRUCT
ErrorCode :INT; // Status INT12
Reserved14 :INT; // Status INT14
Total_Count :DINT; // Status DINT16
CommandResult :DINT; // Status DINT20
CommandData :ARRAY [1..29] OF DINT; // Vision Check Command Datas DINT 20 - DINT136 (相机命令应答自己设置为 116 ) == Result_Data_128Byte
arrResult_Data :ARRAY [1..8] OF DINT; // Vision Check Result Datas DINT 140 - DINT168 == Result_Data_32Byte1 END_STRUCT
END_TYPE
(*
DINT140 = Result Data1
......
......
......
......
DINT168 = Result Data8
*)
3、定义OUTPUT Decode结构体数据
TYPE ST_Laser_Profiler_Output_Decode : //Parameter_128Byte
STRUCT
ExecCondNo :DINT; // Control DINT12
CommandNumber :DINT; // Control DINT16
CmdParameter :ARRAY [1..30] OF DINT; // Control Parameter DINT20 - DINT 136
END_STRUCT
END_TYPE
(*
DINT20 = CmdParameter1
......
......
......
......
DINT136 = CmdParameter30*)
四、通讯功能块变量声明
FUNCTION_BLOCK FB_TYP_162_Laser_Profiler_LJ_X_V1_0_1(*---------------------------------------------*)
(*IN variables*)
(*---------------------------------------------*)
VAR_INPUTiIn_idxElement : INT; (* !!!UNIQUE!!! Element identification*)ist_HWIn : ST_Laser_Profiler_Input; (* Profinet-interface: Inputs *)ibExt_CMD :BOOL;ibImage_StorageEnable :BOOL; // 照片存储使能 istPart_ID : STRING(30); (* Part Barcode *) // Carmera Software Need Config Command Parmeters Length
END_VAR(*---------------------------------------------*)
(*IN/OUT variables*)
(*---------------------------------------------*)
VAR_IN_OUTstINOUT_SYS_CELL :ST_SYS_CELL;
END_VAR(*---------------------------------------------*)
(*OUT variables*)
(*---------------------------------------------*)
VAR_OUTPUTost_HWOut : ST_Laser_Profiler_Output; (* Profinet-interface: Outputs *)oCurrent_Prog_No : INT; oCurrent_Exe_No : INT; oCmd_Idle : BOOL;
END_VAR(*---------------------------------------------*)
(*Local variables*)
(*---------------------------------------------*)
VARstLaser_Profiler_Data_Decode :ST_Laser_Profiler_Decode; (* Laser Profiler Interface Datas *)Dly_Ton : TON;Dly_Ton1 : TON; Dly_Ton2 : TON; Execution_Timeout : TON; Step :INT;Checked_Count :DINT; Start_Condition :BOOL;Cmd_No :INT; // 24 =Switch Program Numer,25=Read Prog No,41=Entry Execute No,42=Read Current Execute No,60=Entry String,61=Read String Cmd_Type :BYTE; // (*0= Switch Program Number,1=Switch Excution Condition Number,2=Trigger Check *) Count_No :INT;index :INT:=0;index1 :INT:=0;strTemp :T_MAXSTRING; Data_Barcode :T_MAXSTRING; arrData :ARRAY [0..255]OF BYTE;UnpackDWord :MEM.UnpackDWord;
END_VAR(*---------------------------------------------*)
(*Local temp variables*)
(*---------------------------------------------*)
VARstTemp_CELL : ST_SYS_CELL; (* Cell structure for element*)stTemp_ELEMENT : ST_SYS_ELEMENTS; (* Data structure of element *)tTemp_T_Ticker :TIME;(* actual CPU-Tick *)bVar_REQ_HP : BOOL; // Execute FunctionbVar_REQ_WP_1 : BOOL; // Load Job NumberbVar_REQ_WP : BOOL; // Cision Check StartwTemp_Diagnose : WORD;(*Error MessagesBIT.0 Robot: Collective ErrorBIT.1 Robot: Task number invalidBIT.2 Robot: Missing release for task start BIT.3 Robot: Emergengy Stop from robot BIT.4 Robot: Error - Bus StateBIT.5 Robot: Error - Program not startedBIT.6 Robot: Error - Stop by programBIT.7 Robot: Error - Stopped by userBIT.8 Robot: ReserveBIT.9 Robot: Teach Mode activeBIT.10 Robot: resBIT.11 Robot: resBIT.12 Robot: No Connection to Robot TCP_IP-ServerBIT.13 Robot: No answer from robot for Read errorBIT.14 Robot: User Error by RobotBIT.15 Robot: Reserve*)
END_VARVAR idxRotaryCylinder :UINT := 0; (* Element Connection to 'Cylinder V021 ' - set to "0" if not used *)idxLaser_Profiler :UINT := 0; (* Element Connection to 'Camera 3D lJ-X8000 ' - set to "0" if not used *)idxVision_AxisX :UINT := 0; (* Element Connection to 'Axis X ' - set to "0" if not used *)idxVision_AxisY :UINT := 0; (* Element Connection to 'Axis Y ' - set to "0" if not used *) Step_Fun_Master : UINT; MeasureDatas_3D : ARRAY [1..9] OF ST_Laser_Profiler_Result; bMeasterExecuteEnd : BOOL; // 执行完成bMeasureExecuteEnd : BOOL; // 执行完成 CylPos_Define : ARRAY [1..9] OF USINT; Index_AxisPos : USINT; MeasureNo : USINT; // 测量次数 Step_Fun_Measure : UINT;
END_VAR
五、通讯功能块程序
(* Initialisation of FB internal structures - for reducing length of expressions *)
acstartCopyData();(*============================================================================================================================*)
ac_Mapping_Receive_Data();GV_Global_Variables .stGV_MasterMeasure.Height_BaseValue :=2.1531;GV_Global_Variables .stGV_MasterMeasure.Width_BaseValue :=3.1486;
IF stTemp_CELL.stMODE.bMANUAL THEN_acMaster_Function(); // _aMeasure_Funciton();
END_IF(* --- INIT of element feedback --------------------------------------------------- *)
//stTemp_ELEMENT.stFeedBack.stSTATE.bIN_WP := FALSE;
stTemp_ELEMENT.stFeedBack.stSTATE.bIN_WP_1 := FALSE;
stTemp_ELEMENT.stFeedBack.stSTATE.bIN_HP := FALSE;
stTemp_ELEMENT.stFeedBack.stSTATE.bOUT_HP := FALSE;
stTemp_ELEMENT.stFeedBack.stSTATE.bOUT_WP := FALSE;
stTemp_ELEMENT.stFeedBack.stSTATE.bTO_WP := FALSE;(* Request HP: Start of Switch Execution Number *) // 执行条件切换
bVar_REQ_HP := (stTemp_CELL.stMODE.bMANUAL AND stTemp_ELEMENT.stCMD.bMAN_HP )OR((stTemp_CELL.stMODE.bINIT_RUNNING OR stTemp_CELL.stMODE.bAUTO_RUNNING) AND stTemp_ELEMENT.stCMD.bAUTO_HP);
(* Request WP_1: Start loadinG of Program Number *)
bVar_REQ_WP_1 := ((stTemp_CELL.stMODE.bMANUAL AND stTemp_ELEMENT.stCMD.bMAN_WP_1 )OR((stTemp_CELL.stMODE.bINIT_RUNNING OR stTemp_CELL.stMODE.bAUTO_RUNNING) AND stTemp_ELEMENT.stCMD.bAUTO_WP_1));
(* Request WP: start Vision *)
bVar_REQ_WP := ((stTemp_CELL.stMODE.bMANUAL AND stTemp_ELEMENT.stCMD.bMAN_WP)OR((stTemp_CELL.stMODE.bINIT_RUNNING OR stTemp_CELL.stMODE.bAUTO_RUNNING) AND stTemp_ELEMENT.stCMD.bAUTO_WP))AND NOT bVar_REQ_WP_1;(* Reset Edit mode if user forgot it *)
IF stTemp_CELL.stMODE.bAUTOOR stTemp_CELL.stMODE.bAUTO_RUNNINGOR stTemp_CELL.stMODE.bINITIALOR stTemp_CELL.stMODE.bINIT_RUNNING THENstTemp_ELEMENT.stCMD.bEditMode := FALSE;
END_IF;(* ================================================================================ *)(* --- Reset of element commands -------------------------------------------------- *)
stTemp_ELEMENT.stCMD.bMAN_HP := FALSE;
stTemp_ELEMENT.stCMD.bMAN_WP := FALSE;
stTemp_ELEMENT.stCMD.bMAN_WP_1 := FALSE;
stTemp_ELEMENT.stCMD.bAUTO_WP := FALSE;
stTemp_ELEMENT.stCMD.bAUTO_WP_1 := FALSE;
stTemp_ELEMENT.stCMD.bAUTO_HP := FALSE;
(* ================================================================================ *)
// Parameter Define
//stTemp_ELEMENT.arstValue[1].Result = 目标程序号
//stTemp_ELEMENT.arstValue[2].Result = 目标执行条件号
//stTemp_ELEMENT.arstValue[9].Result = 当前执行条件号
//stTemp_ELEMENT.arstValue[10].Result = 当前程序号(* === Diagnostic and monitoring ================================================== *)IF stTemp_CELL.stMODE.bQUITT_ERRORS AND wTemp_Diagnose <> 0 (*OR stTemp_ELEMENT.stCMD.bSIMULATE*) THENwTemp_Diagnose := 0; (* Reset Error-word *)Step :=0;ost_HWOut.Cmd_Request:=FALSE;ost_HWOut.Error_Reset_Request:=TRUE; Dly_Ton.IN := FALSE;stTemp_ELEMENT.stFeedBack.stSTATE.bERR_HP := FALSE;stTemp_ELEMENT.stFeedBack.stSTATE.bERR_WP := FALSE;stTemp_ELEMENT.stFeedBack.stSTATE.bERR_WP_1 := FALSE;
ELSEost_HWOut.Error_Reset_Request:=FALSE;
END_IF(* === Execute Function ================================================== *)
Start_Condition:=ist_HWIn.Run AND NOT ist_HWIn.Error AND NOT ist_HWIn.Cmd_Err AND NOT ist_HWIn.Busy AND ist_HWIn.Cmd_Ready AND ist_HWIn.Ready AND Step =0;
Dly_Ton(IN:=Dly_Ton.IN , PT:=Dly_Ton.PT);
Dly_Ton1(IN:=Dly_Ton1.IN , PT:=Dly_Ton1.PT);
Dly_Ton2(IN:=Dly_Ton2.IN , PT:=Dly_Ton2.PT);IF bVar_REQ_WP_1 AND Start_Condition THEN // 切换程序号Cmd_Type :=0 ;Step :=10;
ELSIF bVar_REQ_HP AND Start_Condition THEN // 切换执行条件Cmd_Type :=1 ;Step :=10;
ELSIF bVar_REQ_WP AND Start_Condition THEN // 特殊需求 运行中执行拍照 直接跳至 200Count_No:=0; Cmd_Type :=2 ;Step :=200;
END_IFCASE Step OF (* Step10-Step100 Exeture Funciton *)10: Dly_Ton.IN := FALSE; ost_HWOut.Cmd_Request:=FALSE;Count_No := 0; stTemp_ELEMENT.stFeedBack.stSTATE.bIN_WP:=FALSE; IF NOT Dly_Ton.Q AND NOT ost_HWOut.Cmd_Request AND Count_No = 0 THENStep :=20;END_IF 20: Dly_Ton.PT := T#30MS;Dly_Ton.IN := TRUE; IF (Cmd_Type = 0 OR Cmd_Type = 2) THEN Cmd_No := UINT_TO_INT( stTemp_ELEMENT.arstValue[1].Result ) ; // (* Target Program Number Entry + 添加功能自动处理程序号写入*)stLaser_Profiler_Data_Decode.Output.CommandNumber:=25; IF Dly_Ton.Q THEN Step :=25;END_IFELSIF (Cmd_Type = 1 OR Cmd_Type = 3) THENCmd_No :=UINT_TO_INT( stTemp_ELEMENT.arstValue[2].Result ) ; // (* Target Execution Number Entry + 添加功能自动处理执行条件号写入*)stLaser_Profiler_Data_Decode.Output.CommandNumber:=42; IF Dly_Ton.Q THEN Step :=25;END_IF END_IF 25:Dly_Ton.IN := FALSE; IF NOT Dly_Ton.Q THEN Step :=30;END_IF 30:Dly_Ton.PT := T#1S;Dly_Ton.IN := TRUE; ost_HWOut.Cmd_Request:=TRUE; IF NOT ist_HWIn.Cmd_Ready THENStep :=35;ELSIF Dly_Ton.Q THEN // Exeture Failed, Camera No ResponsewTemp_Diagnose.0 := TRUE;Step :=999; END_IF 35: Dly_Ton.IN := FALSE; ost_HWOut.Cmd_Request:=FALSE;IF ist_HWIn.Cmd_Ready AND NOT ost_HWOut.Cmd_Request AND NOT Dly_Ton.Q THEN STEP:=40;END_IF 40: Dly_Ton.PT := T#30MS;Dly_Ton.IN := TRUE;IF Dly_Ton.Q THENIF Cmd_Type = 0 AND Cmd_No = stLaser_Profiler_Data_Decode.Input.CommandData[2] THEN // 执行切换程序号OK == 程序结束oCurrent_Prog_No := DINT_TO_INT (stLaser_Profiler_Data_Decode.Input.CommandData[2]);Step :=150; ELSIF Cmd_Type = 1 AND Cmd_No = stLaser_Profiler_Data_Decode.Input.CommandData[1] THEN // 检测执行条件选择OK == 程序结束oCurrent_Exe_No := DINT_TO_INT (stLaser_Profiler_Data_Decode.Input.CommandData[1]);Step :=160; ELSIF Cmd_Type = 2 AND Cmd_No = stLaser_Profiler_Data_Decode.Input.CommandData[2] THEN // 检测程序号OK,查看执行条件是否OKoCurrent_Prog_No := DINT_TO_INT (stLaser_Profiler_Data_Decode.Input.CommandData[2]);Step :=45; ELSIF Cmd_Type = 3 AND Cmd_No = stLaser_Profiler_Data_Decode.Input.CommandData[1] THEN // 检测执行条件选择OK == 跳转执行开始检测oCurrent_Exe_No := DINT_TO_INT (stLaser_Profiler_Data_Decode.Input.CommandData[1]);Step :=200; ELSIF (Cmd_Type = 0 OR Cmd_Type = 2) AND Cmd_No <> stLaser_Profiler_Data_Decode.Input.CommandData[2] THEN // 检测程序号NOK,重新切换Step :=50; ELSIF (Cmd_Type = 1 OR Cmd_Type = 3) AND Cmd_No <> stLaser_Profiler_Data_Decode.Input.CommandData[1] THEN // 检测执行条件号NOK,重新切换Step :=50;END_IF END_IF45:Dly_Ton.IN := FALSE;Cmd_Type := 3; IF NOT Dly_Ton.Q AND Cmd_Type = 3 THEN // 查看执行条件是否正确Step :=10;END_IF 50:Dly_Ton.IN := FALSE;ost_HWOut.Cmd_Request:=FALSE; IF NOT Dly_Ton.Q AND NOT ost_HWOut.Cmd_Request THEN Step :=55;END_IF 55: Dly_Ton.PT := T#30MS;Dly_Ton.IN := TRUE;IF (Cmd_Type = 0 OR Cmd_Type = 2) THEN // 切换程序号stLaser_Profiler_Data_Decode.Output.CommandNumber:=24; stLaser_Profiler_Data_Decode.Output.CmdParameter[1]:=1; // SD Card SlotstLaser_Profiler_Data_Decode.Output.CmdParameter[2]:=Cmd_No; // Program Number IF Dly_Ton.Q THEN Step :=60; END_IFELSIF (Cmd_Type = 1 OR Cmd_Type = 3) THENstLaser_Profiler_Data_Decode.Output.CommandNumber:=41; stLaser_Profiler_Data_Decode.Output.CmdParameter[1]:=Cmd_No; // Execute Number IF Dly_Ton.Q THEN Step :=60; END_IFEND_IF 60: Dly_Ton.IN := FALSE;IF NOT Dly_Ton.Q THEN Step :=65; END_IF 65:Dly_Ton.PT := T#1S;Dly_Ton.IN := TRUE; ost_HWOut.Cmd_Request:=TRUE; IF NOT ist_HWIn.Cmd_Ready THENStep :=70;ELSIF Dly_Ton.Q THEN // Exeture Failed, Camera No ResponsewTemp_Diagnose.0 := TRUE;Step :=999; END_IF 70: Dly_Ton.IN := FALSE; ost_HWOut.Cmd_Request:=FALSE;IF ist_HWIn.Cmd_Ready AND NOT ost_HWOut.Cmd_Request AND NOT Dly_Ton.Q THEN STEP:=75;END_IF 75: // 读取程序号或者执行条件是否正确Dly_Ton.PT := T#30MS;Dly_Ton.IN := TRUE; IF (Cmd_Type = 0 OR Cmd_Type = 2) THEN stLaser_Profiler_Data_Decode.Output.CommandNumber:=25; IF Dly_Ton.Q THEN Step :=80;END_IF ELSIF (Cmd_Type = 1 OR Cmd_Type = 3) THENstLaser_Profiler_Data_Decode.Output.CommandNumber:=42; IF Dly_Ton.Q THEN Step :=80;END_IF END_IF 80: // Reset TimerDly_Ton.IN := FALSE;IF NOT Dly_Ton.Q THEN Step :=85; END_IF 85:Dly_Ton.PT := T#1S;Dly_Ton.IN := TRUE; ost_HWOut.Cmd_Request:=TRUE; IF NOT ist_HWIn.Cmd_Ready THENStep :=90;ELSIF Dly_Ton.Q THEN // Exeture Failed, Camera No ResponsewTemp_Diagnose.0 := TRUE;Step :=999; END_IF 90: Dly_Ton.IN := FALSE;ost_HWOut.Cmd_Request:=FALSE; IF ist_HWIn.Cmd_Ready AND NOT Dly_Ton.Q AND NOT ost_HWOut.Cmd_Request THEN STEP:=95;END_IF 95: Dly_Ton.IN := FALSE; IF Cmd_Type = 0 AND Cmd_No = stLaser_Profiler_Data_Decode.Input.CommandData[2] THENoCurrent_Prog_No := DINT_TO_INT (stLaser_Profiler_Data_Decode.Input.CommandData[2]);Step :=150; ELSIF Cmd_Type = 1 AND Cmd_No = stLaser_Profiler_Data_Decode.Input.CommandData[1] THENoCurrent_Exe_No := DINT_TO_INT (stLaser_Profiler_Data_Decode.Input.CommandData[1]); Step :=160; ELSIF Cmd_Type = 2 AND Cmd_No = stLaser_Profiler_Data_Decode.Input.CommandData[2] THENoCurrent_Prog_No := DINT_TO_INT (stLaser_Profiler_Data_Decode.Input.CommandData[2]);Step :=200; ELSIF Cmd_Type = 3 AND Cmd_No = stLaser_Profiler_Data_Decode.Input.CommandData[1] THENoCurrent_Exe_No := DINT_TO_INT (stLaser_Profiler_Data_Decode.Input.CommandData[1]); Step :=200; ELSIF (Cmd_Type = 0 OR Cmd_Type = 2) AND Cmd_No <> stLaser_Profiler_Data_Decode.Input.CommandData[2] THENStep :=100; ELSIF (Cmd_Type = 1 OR Cmd_Type = 3) AND Cmd_No <> stLaser_Profiler_Data_Decode.Input.CommandData[1] THENStep :=100; END_IF 100:Count_No := Count_No + 1;Step :=105;105:Dly_Ton.IN := FALSE; IF Count_No >=3 THEN // Execute Command Function End,But Result NokwTemp_Diagnose.1 := TRUE;Step :=999; ELSIF Count_No < 3 THEN Step :=50; END_IF 150: ost_HWOut.Cmd_Request:=FALSE; Dly_Ton.IN := FALSE;Cmd_No := 0 ;stTemp_ELEMENT.stFeedBack.stSTATE.bIN_WP_1:=TRUE; IF NOT Dly_Ton.Q AND NOT ost_HWOut.Cmd_Request AND NOT bVar_REQ_WP_1 THEN // 执行程序号切换Step :=0; END_IF 160: ost_HWOut.Cmd_Request:=FALSE; Dly_Ton.IN := FALSE;Cmd_No := 0 ; stTemp_ELEMENT.stFeedBack.stSTATE.bIN_HP:=TRUE;IF NOT Dly_Ton.Q AND NOT ost_HWOut.Cmd_Request AND NOT bVar_REQ_HP THEN // 执行执行条件号切换Step :=0; END_IF 200: //检测是否要存储照片ost_HWOut.Cmd_Request:=FALSE; Dly_Ton.IN := FALSE;FOR Index := 1 TO 30 DO // 清除命令参数stLaser_Profiler_Data_Decode.Output.CmdParameter[Index]:= 0 ; END_FOR IF NOT Dly_Ton.Q AND NOT ost_HWOut.Cmd_Request AND ibImage_StorageEnable THEN Step :=205; ELSIF NOT Dly_Ton.Q AND NOT ost_HWOut.Cmd_Request AND NOT ibImage_StorageEnable THEN Step :=300; END_IF 205: FOR index := 0 TO 127 BY 1 DO ;ost_HWOut.Parameter_128Byte[index]:=0; END_FOR Data_Barcode:=''; strTemp :=''; Step :=210; 210:Dly_Ton.PT := T#30MS;Dly_Ton.IN := TRUE; stLaser_Profiler_Data_Decode.Output.CommandNumber:=60; stLaser_Profiler_Data_Decode.Output.CmdParameter[1]:=0; // 指定外部字符串 (0-9) ,此功能块只支持设置为 0arrData := MAXSTRING_TO_BYTEARR (istPart_ID);FOR Index := 1 TO len (istPart_ID) DO // 最大支持30个字符stLaser_Profiler_Data_Decode.Output.CmdParameter[Index + 1]:= arrData[Index - 1 ] ; END_FORIF Dly_Ton.Q THEN Step :=215; END_IF 215: Dly_Ton.IN := FALSE;IF NOT Dly_Ton.Q THEN Step :=220; END_IF 220:Dly_Ton.PT := T#1S;Dly_Ton.IN := TRUE; ost_HWOut.Cmd_Request:=TRUE; IF NOT ist_HWIn.Cmd_Ready THENStep :=225;ELSIF Dly_Ton.Q THEN // Exeture Failed, Camera No ResponsewTemp_Diagnose.0 := TRUE;Step :=999; END_IF 225: Dly_Ton.IN := FALSE; ost_HWOut.Cmd_Request:=FALSE;IF ist_HWIn.Cmd_Ready AND NOT ost_HWOut.Cmd_Request AND NOT Dly_Ton.Q THEN STEP:=230;END_IF 230: Dly_Ton.PT := T#30MS;Dly_Ton.IN := TRUE; stLaser_Profiler_Data_Decode.Output.CommandNumber:=61; IF Dly_Ton.Q THEN Step :=235;END_IF 235: // Reset TimerDly_Ton.IN := FALSE;IF NOT Dly_Ton.Q THEN Step :=240; END_IF 240:Dly_Ton.PT := T#1S;Dly_Ton.IN := TRUE; ost_HWOut.Cmd_Request:=TRUE; IF NOT ist_HWIn.Cmd_Ready THENStep :=245;ELSIF Dly_Ton.Q THEN // Exeture Failed, Camera No ResponsewTemp_Diagnose.0 := TRUE;Step :=999; END_IF 245: Dly_Ton.IN := FALSE;ost_HWOut.Cmd_Request:=FALSE; IF ist_HWIn.Cmd_Ready AND NOT Dly_Ton.Q AND NOT ost_HWOut.Cmd_Request THEN Step:=250;END_IF 250: FOR index := 1 TO len (istPart_ID) BY 1 DO ;strTemp := F_ToCHR(c:=ist_HWIn .Result_Data_128Byte[4* (index + 2) + 3 ]);Data_Barcode :=CONCAT (Data_Barcode ,strTemp);IF index = len (istPart_ID) THENEXIT;END_IFEND_FOR Step:=255;255: IF Data_Barcode <> istPart_ID THENStep :=260; ELSIF Data_Barcode = istPart_ID THENStep :=300; END_IF 260:Data_Barcode:='';Count_No := Count_No + 1;Step :=265;265:Dly_Ton.IN := FALSE; IF Count_No >=3 THEN // Execute Rewrite Name FailurewTemp_Diagnose.7 := TRUE;Step :=999; ELSIF Count_No < 3 THEN Step :=200; END_IF 300:// 清除结果状态Dly_Ton.IN := FALSE;ost_HWOut.Cmd_Request:=FALSE;ost_HWOut.Trg:=FALSE;ost_HWOut.Result_Ack:=TRUE; stTemp_ELEMENT.stText.str[1]:= ''; Checked_Count := stLaser_Profiler_Data_Decode.Input.Total_Count; IF NOT Dly_Ton.IN AND NOT ost_HWOut.Trg AND NOT ist_HWIn.Result_Ready THEN Step :=305; END_IF 305:ost_HWOut.Result_Ack:=FALSE; stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte4:=0;stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte5:=0;stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte6:=0;stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte7:=0; stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte8:=0;stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte9:=0; stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte10:=0;stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte11:=0; FOR index := 1 TO 8 BY 1 DOstLaser_Profiler_Data_Decode.Result_Bits.arrResult_Data[index]:=0; END_FOR Step :=310;310: Dly_Ton.PT := T#5S;Dly_Ton.IN := TRUE;ost_HWOut.Trg:=TRUE; IF NOT ist_HWIn.Error AND ( ist_HWIn.Busy OR NOT ist_HWIn.Ready ) THEN Step :=315; ELSIF Dly_Ton.Q THEN // Exeture Failed, Camera No ResponsewTemp_Diagnose.2 := TRUE;Step :=999; END_IF 315: //等待结果处理完成 Dly_Ton.IN := FALSE;ost_HWOut.Trg:=FALSE; IF NOT ist_HWIn.Busy AND ist_HWIn.Result_Ready AND ist_HWIn.Ready AND NOT ost_HWOut.Trg AND NOT Dly_Ton.Q AND( Checked_Count +1 = stLaser_Profiler_Data_Decode.Input.Total_Count )THEN Step :=320; END_IF320: Dly_Ton.PT := T#100MS;Dly_Ton.IN := TRUE;stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte4:=ist_HWIn.Tool_Judge_Value[0];stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte5:=ist_HWIn.Tool_Judge_Value[1]; stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte6:=ist_HWIn.Tool_Judge_Value[2];stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte7:=ist_HWIn.Tool_Judge_Value[3];stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte8:=ist_HWIn.Tool_Judge_Value[4]; stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte9:=ist_HWIn.Tool_Judge_Value[5];stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte10:=ist_HWIn.Tool_Judge_Value[6];stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte11:=ist_HWIn.Tool_Judge_Value[7]; FOR index := 1 TO 8 BY 1 DOstLaser_Profiler_Data_Decode.Result_Bits.arrResult_Data[index]:=stLaser_Profiler_Data_Decode.Input.arrResult_Data[index]; END_FOR IF Dly_Ton.Q THEN Step :=330; END_IF330: // Result BulidIF stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte4.0 AND NOT stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte4.1 THEN stTemp_ELEMENT.stText.str[1]:= 'OK'; Step :=340; ELSIF stLaser_Profiler_Data_Decode.Result_Bits.JudgeByte4.1 THEN stTemp_ELEMENT.stText.str[1]:= 'NG'; Step :=340; END_IF 340: // Reset TimerDly_Ton.IN := FALSE;ost_HWOut.Trg:=FALSE;ost_HWOut.Result_Ack:=FALSE; stTemp_ELEMENT.stFeedBack.stSTATE.bIN_WP:=TRUE;IF NOT Dly_Ton.Q AND NOT ost_HWOut.Trg AND NOT ost_HWOut.Result_Ack AND NOT bVar_REQ_WP THEN Step :=0; END_IF 999:Dly_Ton.IN := FALSE;ost_HWOut.Cmd_Request:=FALSE;ost_HWOut.Trg:=FALSE;Cmd_Type :=0;IF NOT Dly_Ton.IN AND NOT ost_HWOut.Trg THEN Step :=0; END_IF END_CASE (* States *)stTemp_ELEMENT.stFeedBack.stSTATE.bERR := wTemp_Diagnose <>0;stTemp_ELEMENT.stFeedBack.stSTATE.bIN_HP := NOT stTemp_ELEMENT.stFeedBack.stSTATE.bIN_WP AND NOT stTemp_ELEMENT.stFeedBack.stSTATE.bIN_WP_1AND NOT stTemp_ELEMENT.stFeedBack.stSTATE.bERR;// Alarm BitswTemp_Diagnose.3 := ist_HWIn.Cmd_Err;wTemp_Diagnose.4 := ist_HWIn.Error;wTemp_Diagnose.5 := stLaser_Profiler_Data_Decode.Input.ErrorCode <> 0;wTemp_Diagnose.6 := NOT ist_HWIn.Run;
Execution_Timeout(IN:=Step >0 , PT:=T#20S);IF Execution_Timeout.Q THENwTemp_Diagnose.15 := TRUE;
END_IF
(*============================================================================================================================*)
(* ...finally write back FB internal structures *)ost_HWOut.EXT:=ibExt_CMD;oCmd_Idle :=ist_HWIn.Run AND NOT ist_HWIn.Error AND NOT ist_HWIn.Cmd_Err AND NOT ist_HWIn.Busy AND ist_HWIn.Cmd_Ready AND ist_HWIn.Ready ;
(* Write loaded program number *)
stTemp_ELEMENT.arstValue[10].Result := oCurrent_Prog_No;
stTemp_ELEMENT.arstValue[9].Result := oCurrent_Exe_No;
ac_Mapping_Send_Data();
acFinalCopyData();
六、通讯功能块程序分析
6.1 代码功能分析
这段代码是基于 PLC(可编程逻辑控制器)的结构化文本 (ST) 语言编写的,主要用于控制基恩士激光测量设备的自动化检测流程。从整体结构来看,它实现了设备初始化、模式切换、命令发送、数据接收及结果判断等功能。
6.2整体架构解析
代码主要分为以下几个功能模块:
- 初始化与全局变量设置
- 工作模式判断与命令请求生成
- 状态反馈与错误处理
- 主执行逻辑(基于状态机的分步处理)
- 结果处理与数据输出
6.3 详细功能说明
初始化与全局变量
acstartCopyData();
ac_Mapping_Receive_Data();GV_Global_Variables .stGV_MasterMeasure.Height_BaseValue :=2.1531;
GV_Global_Variables .stGV_MasterMeasure.Width_BaseValue :=3.1486;
- 首先调用了数据复制和映射函数,可能用于初始化通信缓冲区或数据结构
- 设置了全局测量基准值(高度 2.1531,宽度 3.1486),作为测量判断的参考标准
工作模式与命令请求处理
IF stTemp_CELL.stMODE.bMANUAL THEN_acMaster_Function();
END_IFstTemp_ELEMENT.stFeedBack.stSTATE.bIN_WP_1 := FALSE;
stTemp_ELEMENT.stFeedBack.stSTATE.bIN_HP := FALSE;
stTemp_ELEMENT.stFeedBack.stSTATE.bOUT_HP := FALSE;
stTemp_ELEMENT.stFeedBack.stSTATE.bOUT_WP := FALSE;
stTemp_ELEMENT.stFeedBack.stSTATE.bTO_WP := FALSE;bVar_REQ_HP := (stTemp_CELL.stMODE.bMANUAL AND stTemp_ELEMENT.stCMD.bMAN_HP )OR((stTemp_CELL.stMODE.bINIT_RUNNING OR stTemp_CELL.stMODE.bAUTO_RUNNING) AND stTemp_ELEMENT.stCMD.bAUTO_HP);
- 检测手动模式并执行相应函数
- 初始化多个状态反馈变量
- 通过逻辑运算生成不同的命令请求信号(HP、WP_1、WP),支持手动和自动模式下的命令触发
状态机主逻辑(CASE 结构)
代码的核心是一个基于Step
变量的状态机,通过 CASE 语句实现不同阶段的操作:
CASE Step OF 10: Dly_Ton.IN := FALSE; ost_HWOut.Cmd_Request:=FALSE;Count_No := 0; stTemp_ELEMENT.stFeedBack.stSTATE.bIN_WP:=FALSE; IF NOT Dly_Ton.Q AND NOT ost_HWOut.Cmd_Request AND Count_No = 0 THENStep :=20;END_IF 20: Dly_Ton.PT := T#30MS;Dly_Ton.IN := TRUE; IF (Cmd_Type = 0 OR Cmd_Type = 2) THEN Cmd_No := UINT_TO_INT( stTemp_ELEMENT.arstValue[1].Result ) ; // 目标程序号stLaser_Profiler_Data_Decode.Output.CommandNumber:=25; IF Dly_Ton.Q THEN Step :=25;END_IFELSIF (Cmd_Type = 1 OR Cmd_Type = 3) THENCmd_No :=UINT_TO_INT( stTemp_ELEMENT.arstValue[2].Result ) ; // 目标执行条件号stLaser_Profiler_Data_Decode.Output.CommandNumber:=42; IF Dly_Ton.Q THEN Step :=25;END_IF END_IF ...(中间步骤省略)...300:// 清除结果状态Dly_Ton.IN := FALSE;ost_HWOut.Cmd_Request:=FALSE;ost_HWOut.Trg:=FALSE;ost_HWOut.Result_Ack:=TRUE; stTemp_ELEMENT.stText.str[1]:= ''; Checked_Count := stLaser_Profiler_Data_Decode.Input.Total_Count; IF NOT Dly_Ton.IN AND NOT ost_HWOut.Trg AND NOT ist_HWIn.Result_Ready THEN Step :=305; END_IF ...(后续步骤省略)...
END_CASE
状态机主要处理以下任务:
-
步骤 10-100:程序号和执行条件号的切换流程
- 设置延时定时器
- 构建并发送命令到激光测量仪
- 检查命令执行结果
- 处理执行失败的重试逻辑(最多 3 次)
-
步骤 150-160:模式切换完成后的状态更新
- 设置相应的反馈状态标志
- 重置命令参数
-
步骤 200-265:照片存储功能(可选)
- 处理条形码或工件 ID 的传输
- 验证数据传输的正确性
-
步骤 300-340:测量结果处理
- 触发测量操作
- 接收并解析测量结果
- 根据判断结果设置 "OK" 或 "NG" 状态
- 重置系统状态准备下一次测量
错误处理与诊断
st
IF stTemp_CELL.stMODE.bQUITT_ERRORS AND wTemp_Diagnose <> 0 THENwTemp_Diagnose := 0; (* Reset Error-word *)Step :=0;ost_HWOut.Cmd_Request:=FALSE;ost_HWOut.Error_Reset_Request:=TRUE; Dly_Ton.IN := FALSE;stTemp_ELEMENT.stFeedBack.stSTATE.bERR_HP := FALSE;stTemp_ELEMENT.stFeedBack.stSTATE.bERR_WP := FALSE;stTemp_ELEMENT.stFeedBack.stSTATE.bERR_WP_1 := FALSE;
ELSEost_HWOut.Error_Reset_Request:=FALSE;
END_IFwTemp_Diagnose.3 := ist_HWIn.Cmd_Err;
wTemp_Diagnose.4 := ist_HWIn.Error;
wTemp_Diagnose.5 := stLaser_Profiler_Data_Decode.Input.ErrorCode <> 0;
wTemp_Diagnose.6 := NOT ist_HWIn.Run;
Execution_Timeout(IN:=Step >0 , PT:=T#20S);IF Execution_Timeout.Q THENwTemp_Diagnose.15 := TRUE;
END_IF
- 检测到错误时重置错误状态并清除命令请求
- 多个诊断位记录不同类型的错误(命令错误、设备错误、解码错误等)
- 实现执行超时检测(20 秒),防止程序卡死
6.4 关键数据结构与接口
// Parameter Define
//stTemp_ELEMENT.arstValue[1].Result = 目标程序号
//stTemp_ELEMENT.arstValue[2].Result = 目标执行条件号
//stTemp_ELEMENT.arstValue[9].Result = 当前执行条件号
//stTemp_ELEMENT.arstValue[10].Result = 当前程序号Start_Condition:=ist_HWIn.Run AND NOT ist_HWIn.Error AND NOT ist_HWIn.Cmd_Err AND NOT ist_HWIn.Busy AND ist_HWIn.Cmd_Ready AND ist_HWIn.Ready AND Step =0;ost_HWOut.Cmd_Request:=TRUE;
ost_HWOut.Trg:=TRUE;
ost_HWOut.Result_Ack:=TRUE;stLaser_Profiler_Data_Decode.Output.CommandNumber:=25;
stLaser_Profiler_Data_Decode.Output.CmdParameter[1]:=1;
- 定义了与激光测量仪交互的关键参数和接口
Start_Condition
定义了系统启动的必要条件(设备运行、无错误、准备就绪等)- 通过
ost_HWOut
结构体发送命令请求和触发信号 - 通过
stLaser_Profiler_Data_Decode
结构体处理测量仪返回的数据
6.5 总结
这段代码实现了一个完整的激光测量自动化检测系统,主要功能包括:
- 支持手动和自动两种工作模式
- 实现程序号和执行条件号的切换
- 控制激光测量仪进行数据采集
- 处理和解析测量结果,判断是否合格
- 完善的错误处理和诊断机制
- 支持工件 ID 与测量数据的关联存储
该系统采用状态机设计,将复杂的测量流程分解为多个步骤,确保了操作的有序性和可靠性,适用于需要高精度重复检测的工业自动化场景。
七、通讯功能块功能描述及可能的改进空间
7.1 功能块描述
这段代码实现了一个完整的基恩士激光测量设备控制功能块,主要用于自动化生产线上的精密测量和检测任务。它能够:
- 模式切换与控制:支持手动、自动和初始化三种工作模式,根据不同模式执行相应的控制逻辑
- 程序与条件管理:实现测量程序号和执行条件号的切换,支持从 SD 卡加载不同的测量程序
- 测量执行:触发激光测量仪进行高精度测量,并获取测量结果
- 数据处理:接收和解析测量数据,判断测量结果是否合格(OK/NG)
- 结果存储:可选地将测量结果与工件 ID 关联存储,便于追溯和分析
- 错误处理:全面检测系统状态,处理各类异常情况,包括超时、命令错误、设备错误等
- 状态反馈:提供丰富的状态指示,包括当前工作模式、测量状态、错误信息等
这个功能块适用于需要高精度尺寸检测、表面缺陷检测或形状分析的工业自动化场景,如汽车零部件制造、电子元件生产、精密机械加工等领域。
7.2 改进空间
虽然代码已经实现了完整的功能,但仍有一些可以改进的地方:
-
代码结构优化
- 可以将一些重复的代码提取成独立的子程序,如延时控制、命令发送、结果解析等,提高代码的可读性和可维护性
- 目前状态机的步骤较多,可以考虑将一些功能相关的步骤封装成独立的功能模块
-
错误处理增强
- 增加更详细的错误日志记录,便于故障排查
- 针对不同类型的错误,实现更细致的处理策略,而不是简单地重置
- 增加错误恢复机制,某些错误发生后可以尝试自动恢复,而不是直接终止
-
性能优化
- 优化延时等待策略,避免不必要的延时,提高系统响应速度
- 增加并行处理能力,例如在等待测量结果时可以同时处理其他任务
-
用户交互与配置
- 增加配置参数,使功能块更加灵活,例如允许用户自定义测量基准值、超时时间等
- 提供更友好的状态反馈接口,便于上位机系统监控和操作
-
安全机制
- 增加权限控制,防止未授权的操作
- 实现紧急停止功能,确保在异常情况下可以迅速终止系统运行
-
数据管理
- 优化数据存储策略,增加数据备份和恢复功能
- 提供更丰富的数据统计和分析功能
-
代码健壮性
- 增加输入参数的有效性检查,防止非法输入导致系统异常
- 优化内存管理,避免内存泄漏或溢出
以下是一个简单的代码优化示例,将部分重复代码提取成子程序:
// 原代码中的重复部分
Dly_Ton.PT := T#30MS;
Dly_Ton.IN := TRUE;
// ... 等待延时完成 ...
Dly_Ton.IN := FALSE;// 可以提取成一个通用的延时函数
FUNCTION DelayMS : BOOLVAR_INPUTTimeMS : TIME; // 延时时间END_VARDly_Timer.PT := TimeMS;Dly_Timer.IN := TRUE;// 等待延时完成WHILE NOT Dly_Timer.Q DO// 可以添加退出条件,避免无限循环END_WHILE;Dly_Timer.IN := FALSE;DelayMS := TRUE; // 延时完成
END_FUNCTION
通过类似的优化,可以使代码更加简洁、清晰,同时提高可维护性和可扩展性。
八、附录
1、recive_data 子程序
stLaser_Profiler_Data_Decode.Input.ErrorCode :=MEM.PackBytesToWord(byhighbyte:=ist_HWIn.Result_Data_128Byte[0] ,byLowByte:=ist_HWIn .Result_Data_128Byte[1]);
stLaser_Profiler_Data_Decode.Input.Total_Count :=DWORD_TO_DINT (MEM.PackBytesToDWord(byHHbyte:=ist_HWIn .Result_Data_128Byte[4] ,byHLbyte:=ist_HWIn .Result_Data_128Byte[5], byLHbyte:=ist_HWIn .Result_Data_128Byte[6] ,byLLbyte:=ist_HWIn .Result_Data_128Byte[7]));
stLaser_Profiler_Data_Decode.Input.CommandResult :=DWORD_TO_DINT (MEM.PackBytesToDWord(byHHbyte:=ist_HWIn .Result_Data_128Byte[8] ,byHLbyte:=ist_HWIn .Result_Data_128Byte[9], byLHbyte:=ist_HWIn .Result_Data_128Byte[10] ,byLLbyte:=ist_HWIn .Result_Data_128Byte[11])); FOR index1 := 1 TO 29 BY 1 DO // Byte12 -byte 127 = 29 Dint == Result_Data_128Byte stLaser_Profiler_Data_Decode.Input.CommandData[index1] :=DWORD_TO_DINT (MEM.PackBytesToDWord(byHHbyte:=ist_HWIn .Result_Data_128Byte[4*(index1-1)+12] ,byHLbyte:=ist_HWIn .Result_Data_128Byte[4*(index1-1)+13],byLHbyte:=ist_HWIn .Result_Data_128Byte[4*(index1-1)+14] ,byLLbyte:=ist_HWIn .Result_Data_128Byte[4*(index1-1)+15]));
END_FORFOR index := 1 TO 8 BY 1 DO // Byte0 -byte 31 = 8 Dint == Result_Data_32Byte1 stLaser_Profiler_Data_Decode.Input.arrResult_Data[index] :=DWORD_TO_DINT (MEM.PackBytesToDWord(byHHbyte:=ist_HWIn .Result_Data_32Byte1[4*(index-1)+0] ,byHLbyte:=ist_HWIn .Result_Data_32Byte1[4*(index-1)+1],byLHbyte:=ist_HWIn .Result_Data_32Byte1[4*(index-1)+2] ,byLLbyte:=ist_HWIn.Result_Data_32Byte1[4*(index-1)+3]));
END_FOR
2、send_data 子程序
UnpackDWord(dwValue:= stLaser_Profiler_Data_Decode.Output.ExecCondNo, byLLByte=>ost_HWOut.Parameter_128Byte[3], byLHByte=>ost_HWOut.Parameter_128Byte[2],byHLByte=>ost_HWOut.Parameter_128Byte[1],byHHByte=>ost_HWOut.Parameter_128Byte[0]);
UnpackDWord(dwValue:= stLaser_Profiler_Data_Decode.Output.CommandNumber, byLLByte=>ost_HWOut.Parameter_128Byte[7], byLHByte=>ost_HWOut.Parameter_128Byte[6],byHLByte=>ost_HWOut.Parameter_128Byte[5],byHHByte=>ost_HWOut.Parameter_128Byte[4]);FOR index := 1 TO 30 BY 1 DO
UnpackDWord(dwValue:= stLaser_Profiler_Data_Decode.Output.CmdParameter[index], byLLByte=>ost_HWOut.Parameter_128Byte[4*(index-1)+ 11], byLHByte=>ost_HWOut.Parameter_128Byte[4*(index-1)+ 10],byHLByte=>ost_HWOut.Parameter_128Byte[4*(index-1)+9],byHHByte=>ost_HWOut.Parameter_128Byte[4*(index-1)+8]);
END_FOR