 的PLC-HMI工程實戰記錄(一)創建PLC工程模板項目)
一個PLC-HMI實戰工程的過程記錄。一、新建TIA工程組態PLC時可以直接指定PLC型號也可以不指定CPU使用unspecified CPU這樣做的好處是保持模板的通用性并且在硬件確定之后不用手動逐個輸入組態硬件連接到已通電的CPU使用“在線”、“硬件檢測”就可以直接把實際的硬件讀取到項目中。使用這種讀取在線PLC的方式組態有一個坑點是必須給在線硬件分配PROFINET 設備名稱并與程序中的名稱一致否則會組態失敗。二、新建程序分組命名為“系統OB”添加OB30、OB82、OB100、OB86、OB122等必要和有可能用到的OB系統塊。三、新建PLC數據類型這個命名為“AreaVar”的數據類型包含了數據分區I/QM/DB)、DB編號、數據起點、數據字節長度等信息可用來進行各種以字節為單位的塊間操作。這個數據類型是本工程與上位機通信的基礎類型它指定了數據報文的位置和長度。四、導入之前導出保存的CRC校驗表DB文件“添加新的外部文件”、“從源生成塊”這樣就把外部的db文件轉換為項目中的DB塊。新建分組“背景類DB”并將生成的DB塊拖入五、創建TCP連接并組態和編程連接創建連接之前需要將CPU指定為具有網絡通信能力的型號這里指定為1513-1PN。1、新建程序塊分組功能塊2、在分組中新建FB命名為“上位通信”此功能塊負責與上位機通信。3、打開上位通信FB開始編程。4、從指令庫中將TCON拖入FB的網絡會自動創建一個DB塊TCON_DB。5、點擊“開始組態”按鈕對連接進行組態在打開的組態窗口中伙伴未指定伙伴主動建立連接連接數據新建作為一個模板無需為PLC特意指定IP地址當在實際的項目中改變了CPU的IP地址后連接參數中的地址會自動隨之改變為新的。這樣就將PLC創建為TCP主站。可以看到在系統塊的程序資源中自動生成了2個數據塊PLC_1_Connection_DB用于存儲連接參數。TCON_DB則是TCON指令的背景數據塊這些都是自動生成和分配的無需人工參與只要知道其用途即可。6、編譯一下確認生成無誤7、為上位通信FB塊創建一個數據塊上位通信_工作DB并在DB中定義一個Struct作為TCON指令的參數容器8、在上位通信FB中為TCON指令分配參數9、在OB1中調用上位通信FB調用時會自動創建一個背景DB將其命名為上位通信_背景DB并拖入“背景類DB”分組中。這個分組中的DB全部為背景類的DB無需人工干預和編輯。10、在上位通信_工作DB中補充發送和接收的參數容器11、在上位通信FB中繼續編程拖入發送和接收指令增加接收和發送功能12、在創建兩個相同的DB塊用來存放上行和下行報文13、繼續完善上位通信FB把發送和接收指令的DATA端口指向新建的兩個報文DB14、創建FC塊命名為“_build_AreaVal”功能是由Area、DBnum、offset、byteCount這幾個參數來生成一個自定義的區域變量AreaVal接口Input 數據類型Area ByteDBNum UIntOffset UIntbyteCount UIntInOutdestAreaVar AreaVarscl:#destAreaVar.Area : #Area; #destAreaVar.DBNum : #DBNum; #destAreaVar.Offset : #Offset; #destAreaVar.byteCount : #byteCount;15、創建FC塊命名為“組態上行報文”用來組態上行報文接口InputframeHeader1 Byte 幀頭字節1frameHeader2 Byte 幀頭字節2frameEnd1 Byte 幀尾字節1frameEnd2 Byte 幀尾字節2srcAreaVars Array[*] of AreaVar 源數據區域支持多區域輸入DB_CRC_TABLE Array[0..255] of Word CRC查表運算的表格OutputInOutdestArea AreaVar 目標區域存放上行報文的字節TempTemp_1 AreaVarwCRC Wordi DIntpos DInt 字節臨時指針bTemp BytewTemp WordnIndex DIntvarTemp AreaVartmp_DI1 DInttmp_DI2 DIntConstantReturn組態上行報文 Voidscl////////////////////////////組態幀頭/////////////////////////// POKE(area : #destArea.Area, dbNumber : #destArea.DBNum, byteOffset : 0, value : #frameHeader1); //幀頭1 POKE(area : #destArea.Area, dbNumber : #destArea.DBNum, byteOffset : 1, value : #frameHeader2); //幀頭2 #pos : 2; //#tmp_DI1 : LOWER_BOUND(ARR : #srcAreaVars, DIM : 1); //#tmp_DI2 : UPPER_BOUND(ARR : #srcAreaVars, DIM : 1); FOR #i : LOWER_BOUND(ARR : #srcAreaVars, DIM : 1) TO UPPER_BOUND(ARR : #srcAreaVars, DIM : 1) DO #varTemp : #srcAreaVars[#i]; ////////組態區域變量的信息本幀數據的區域和起點、長度////// POKE(area : #destArea.Area, //本幀數據的區域 dbNumber : #destArea.DBNum, byteOffset : #pos, value : #varTemp.Area); #pos : #pos 1; POKE(area : #destArea.Area, //字節占位 dbNumber : #destArea.DBNum, byteOffset : #pos, value : #varTemp.Area); #pos : #pos 1; POKE(area : #destArea.Area, //本幀數據的DB號 dbNumber : #destArea.DBNum, byteOffset : #pos, value : #varTemp.DBNum); //value : DINT_TO_BYTE(IN:#varTemp.DBNum)); #pos : #pos 4; POKE(area : #destArea.Area, //本幀數據的起始字節偏移 dbNumber : #destArea.DBNum, byteOffset : #pos, value : #varTemp.Offset); #pos : #pos 4; POKE(area : #destArea.Area, //本幀數據的字節數 dbNumber : #destArea.DBNum, byteOffset : #pos, value : #varTemp.byteCount); #pos : #pos 4; ///////////////////////////////組態幀數據內容///////////////////////// POKE_BLK(area_src : #varTemp.Area, dbNumber_src : #varTemp.DBNum, byteOffset_src : #varTemp.Offset, area_dest : #destArea.Area, dbNumber_dest : #destArea.DBNum, byteOffset_dest : #pos, count : #varTemp.byteCount); #pos : #pos #varTemp.byteCount; END_FOR; ///////////////////////////////組態幀尾//////////////////////////// POKE(area : #destArea.Area, //幀尾1 dbNumber : #destArea.DBNum, byteOffset : #pos, value : #frameEnd1); #pos : #pos 1; POKE(area : #destArea.Area, //幀尾2 dbNumber : #destArea.DBNum, byteOffset : #pos, value : #frameEnd2); #pos : #pos 1; /////////////////////////////////CRC16校驗和////////////////////////////// #destArea.byteCount : #pos; #wCRC : 16#FFFF; FOR #i : 0 TO #destArea.byteCount - 1 DO #bTemp : PEEK(area : #destArea.Area, byteOffset : #i, dbNumber : #destArea.DBNum); #wTemp : #wCRC XOR BYTE_TO_WORD(IN : #bTemp); #wTemp : #wTemp AND 16#00FF; #nIndex : WORD_TO_INT(IN : #wTemp); #wCRC : SHR(IN : #wCRC, N : 8) XOR #DB_CRC_TABLE[#nIndex]; END_FOR; //寫入CRC字 POKE(area : #destArea.Area, dbNumber : #destArea.DBNum, byteOffset : #pos, value : #wCRC); #pos : #pos 2; #destArea.byteCount : #pos;16、在“上位通信_工作DB”中增加幀頭和幀尾的定義17、在“上位通信”FB中編程組態上行報文上行報文的區域暫時不填具體到實際的PLC再填。18、創建FC塊命名為“解析下行數據”接口InputframeHeader1 Byte 幀頭字節1frameHeader2 Byte 幀頭字節2frameEnd1 Byte 幀尾字節1frameEnd2 Byte 幀尾字節2rsv_area_var AreaVar 接收下行報文的DB塊DB_CRC_TABLE Array[0..255] of WordOutputDONE BoolInOutTempi DInt 0.0pos DInt 4.0 字節臨時指針tmp_head Byte 8.0tmp_end Byte 9.0tmp_areaVar AreaVar 10.0bTemp Byte 28.0wTemp Word 30.0wCRC Word 32.0nIndex Int 34.0ConstantReturn解析下行數據 Voidscl:#DONE : FALSE; ////////////////////////////解析幀頭/////////////////////////// #pos : 0; // 字節指針 #tmp_head : PEEK(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos); //幀頭1 IF #frameHeader1 #tmp_head THEN #DONE : FALSE; RETURN; END_IF; #pos : #pos 1; #tmp_head : PEEK(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos); //幀頭2 IF #frameHeader1 #tmp_head THEN #DONE : FALSE; RETURN; END_IF; #pos : #pos 1; IF PEEK(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos) #frameEnd1 THEN IF PEEK(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos 1) #frameEnd2 THEN #DONE : TRUE; RETURN; END_IF; END_IF; /////////////////////////解析區域變量的參數//////////////////// #tmp_areaVar.Area : PEEK(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos); //幀區域 #pos : #pos 2; #tmp_areaVar.DBNum : DWORD_TO_DINT(IN : PEEK_DWORD(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos)); //幀DB號 #pos : #pos 4; #tmp_areaVar.Offset : DWORD_TO_DINT(PEEK_DWORD(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos)); //幀字節偏移 #pos : #pos 4; #tmp_areaVar.byteCount : DWORD_TO_DINT(PEEK_DWORD(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos)); //幀字節長度 #pos : #pos 4; #tmp_areaVar.bit : DWORD_TO_DINT(PEEK_DWORD(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos)); //幀位bit #pos : #pos 4; //pos目前位于數據區之前 ////////////////////////////解析數據內容和CRC////////////////////////// //解析CRC #wCRC : 16#FFFF; FOR #i : 0 TO #pos #tmp_areaVar.byteCount 1 DO //讀取接收DB從0到數據長度再多讀4位幀尾 #bTemp : PEEK(area : #rsv_area_var.Area, byteOffset : #i, dbNumber : #rsv_area_var.DBNum); #wTemp : #wCRC XOR BYTE_TO_WORD(IN : #bTemp); #wTemp : #wTemp AND 16#00FF; #nIndex : WORD_TO_INT(IN : #wTemp); #wCRC : SHR(IN : #wCRC, N : 8) XOR #DB_CRC_TABLE[#nIndex]; END_FOR; #wCRC : #wCRC; #pos : #pos #tmp_areaVar.byteCount; //pos目前位于數據區之后幀尾之前 IF #wCRC PEEK_WORD(area : #rsv_area_var.Area, byteOffset : #pos 2, dbNumber : #rsv_area_var.DBNum) THEN //CRC失敗 #DONE : FALSE; RETURN; END_IF; ////////////////////////////解析幀尾/////////////////////////////////// #tmp_end : PEEK(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos); //幀尾1 IF #frameEnd1 #tmp_end THEN #DONE : FALSE; RETURN; END_IF; #pos : #pos 1; #tmp_end : PEEK(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos); //幀尾2 IF #frameEnd2 #tmp_end THEN #DONE : FALSE; RETURN; END_IF; #pos : #pos 1; //此時的pos位于幀尾后CRC前 #pos : #pos - #tmp_areaVar.byteCount - 2; //把pos移回到數據區的起始 /////////////////////////////////如果是bool////////////////////////////// IF #tmp_areaVar.bit 16#FFFFFFFF THEN POKE_BOOL(area : #tmp_areaVar.Area, ////寫一個bool位 dbNumber : #tmp_areaVar.DBNum, byteOffset : #tmp_areaVar.Offset, bitOffset : DINT_TO_INT(IN : #tmp_areaVar.bit), value : PEEK_BOOL(area : #rsv_area_var.Area, dbNumber : #rsv_area_var.DBNum, byteOffset : #pos, bitOffset : #tmp_areaVar.bit)); /////////////////////////////////如果不是bool////////////////////////////// ELSE //寫塊數據 POKE_BLK(area_src : #rsv_area_var.Area, dbNumber_src : #rsv_area_var.DBNum, byteOffset_src : #pos, area_dest : #tmp_areaVar.Area, dbNumber_dest : #tmp_areaVar.DBNum, byteOffset_dest : #tmp_areaVar.Offset, count : #tmp_areaVar.byteCount); END_IF; #DONE : TRUE;