本文是CSDN上看到的一篇質(zhì)量比較高的文章,自己仔細(xì)讀了幾遍收獲很大,值得一起學(xué)習(xí)。我在作者的基礎(chǔ)上重新排版添加了一些備注和修改了一些錯誤的地方。由于字?jǐn)?shù)比較多,因此分成了上下篇。
一、前言
本文將會講解高度抽象的設(shè)計模式,設(shè)計模式與案例無關(guān),并不以案例講題。反正學(xué)校按這種方式教了那么久也沒人聽得懂。我將主要以理論為主,避免與具體案例綁定,以防止讀者先入為主,以為某一設(shè)計模式只能適用于某案例,或某一設(shè)計模式只能單獨使用,不能結(jié)合其它設(shè)計模式,又或者某一案例只適合某一設(shè)計模式。
快捷鍵:
- Shift + Alt + F4 轉(zhuǎn)換和編譯所有程序
- Alt + O + W PLC寫入
簡單程序
Y0 := TRUE;
K1Y1 := HF;//指從Y1開始的4個連續(xù)端口設(shè)置為二進制1111,也就是四路導(dǎo)通。
為什么要用K開頭呢? K在PLC符號中代表后面的文本將作為十進制數(shù)字使用,然而數(shù)字不能用于標(biāo)識符的第一個字母,這是所有編程語言的通例。 K1是指4個連續(xù)端口。為什么是4個? 1位16進制數(shù)代表4個端口。這個K1是指“1位16進制數(shù)”的意思,也就是4個開關(guān)量。如果改為K2,就是“2位16進制數(shù)”,代表8個開關(guān)量。Y1表示從Y1端口開始計數(shù),實際上就是C語言里的起始指針,數(shù)組指針之類的含義。
三菱ST編程相對西門子更簡單,因為它可以直接使用X、Y、M、D這幾個系統(tǒng)標(biāo)識,而西門子變量和端口全部都要聲明,然后定義到指定地址才能使用。
二、結(jié)構(gòu)化文本入門
PLC的經(jīng)典用法就是串并聯(lián)邏輯電路,ST語言的串聯(lián)用AND操作符,并聯(lián)用OR操作符,動斷觸點(常閉)用NOT操作符,此外還支持奇校驗XOR操作符。
2.1 單按鈕
M1 := X0 AND NOT M0;(* 上升沿檢測,如果X0是通,M0是斷,則M1是通 *)
Y0 := Y0 XOR M1;(* 上升沿的時候轉(zhuǎn)換狀態(tài),如果Y0和M1中通的個數(shù)為單數(shù),則Y0為通,否則為斷 *)
M0 := X0;(* 記憶X0的上一個狀態(tài) *)
2.2 起保停
X0為通時Y0設(shè)置為通,X1為通時Y0設(shè)置為斷,自鎖電路。
Y0 := NOT X1 AND (X0 OR Y0);
在梯形圖中,有一種專業(yè)的自鎖電路的替代方法,就是SET和RST指令。它們在ST中被封裝成系統(tǒng)宏函數(shù)。
SET(X0, Y0);(* 按下X0時,Y0置位 *)
RST(X1, Y0);(* 按下X1時,Y0復(fù)位 *)
這是推薦使用的方法,簡單易懂。ST宏函數(shù)和系統(tǒng)功能塊非常多,我們能夠記住一些指令就很不容易了,何況它們還有不同的參數(shù)。GX Work2提供了一個快捷鍵,Ctrl + F1,把插入點定位在寫好的函數(shù)名上,按Ctrl + F1,IDE會自動插入?yún)?shù)提示。如果連函數(shù)名也不記得了,可以在右邊的窗口雙擊插入函數(shù)名或功能塊名。西門子Portal則是從左邊的自定義程序和右邊的系統(tǒng)程序中拖拽到工作區(qū)。系統(tǒng)函數(shù)的第一個形參都是使能標(biāo)志,相當(dāng)于每一個系統(tǒng)函數(shù)內(nèi)置了一個條件表達(dá)式,只有當(dāng)?shù)谝粋€形參傳入TRUE(通)的時候,才執(zhí)行指令的功能。
2.3 XOR高電平奇校驗
Y0 := X0 XOR X1 XOR X2;//做一個三聯(lián)開關(guān),X0,X1,X2的狀態(tài)變化都會引起Y0翻轉(zhuǎn)。
這個程序在X0,X1,X2之中任意一個改變狀態(tài)時,都會使Y0狀態(tài)變化,也就是任意一個開關(guān)都可以自由地操作燈的亮滅。這個表達(dá)式如果要用標(biāo)準(zhǔn)的串并聯(lián)梯形圖來做,會非常麻煩。
- 連續(xù)XOR表達(dá)式也叫“ 高電平奇校驗 ”就是輸入點中TRUE的個數(shù)為單數(shù)的時候,結(jié)果為TRUE,雙數(shù)的時候結(jié)果為FALSE。
- 連續(xù)WOR表達(dá)式( 低電平奇校驗 )的效果是輸入點中的FALSE個數(shù)為單數(shù)時,結(jié)果為FALSE,雙數(shù)的時候結(jié)果為TRUE。
我們做邏輯運算多用的是高電平奇校驗公式,很少用到低電平奇校驗公式。當(dāng)奇校驗公式中有奇數(shù)個常量目標(biāo)電平時,根據(jù)奇校驗的算法,可以證明它等價于偶校驗,其實也等于在中間的某個位置加入一個NOT運算符或?qū)ψ罱K結(jié)果取反。
了解了奇校驗的特點后,我們也可以用一個計數(shù)器來代替奇校驗公式。
2.4 上升沿和下降沿
ST中的上升沿檢測使用LDP,下降沿使用LDF。升級版的單按鈕控制:
Y0 := Y0 XOR LDP(TRUE, X0);
LDP的第一個形參還是使能的意思,幾乎所有的指令都有很大概率在第一個形參寫TRUE,因為三菱PLC所有的指令都需要使能,而多數(shù)情況下,這些指令是需要永久使能的。LDP的第二個形參是上升沿檢測的變量,這里檢測的是X0的上升沿。實際上對應(yīng)單片機內(nèi)部電路,X0的引腳可能是收到了下降沿,這個LDP是指的從斷到通的狀態(tài),與單片機電平方向無關(guān)。當(dāng)收到上升沿的時候,LDP返回一個掃描周期的TRUE,TRUE與XOR組合,就等價于一個NOT,就將Y0翻轉(zhuǎn)了,而LDP只返回一個掃描周期的TRUE,所以也只有這一個掃描周期的XOR等價于NOT,其余掃描周期則為XOR FALSE,也就是不起作用。這樣就實現(xiàn)了X0在從斷到通時Y0會發(fā)生狀態(tài)翻轉(zhuǎn),也就是記憶按鈕的效果。
2.5 定時器和計數(shù)器
定時器
輸出自變量,ENO:執(zhí)行結(jié)果:位。定時器觸點:TS普通,STS累計。定時器線圈:TC普通,STC累計。定時器當(dāng)前值:TN普通,STN累計。
計數(shù)器
輸出自變量,ENO:執(zhí)行結(jié)果:位。計數(shù)器觸點:CS。計數(shù)器線圈:CC。計數(shù)器當(dāng)前值:CN
擴展AB相編碼器的ST寫法
擴展閱讀https://blog.csdn.net/rediculous/article/details/102656817 注意: ST語言不能直接寫C251
- C和T元件的線圈加第二個字符C,如CC0, TC1等,C就是
Coin
線圈。 - 觸點加S,如C0的觸點就是CS0,S就是
Switch
開關(guān)的意思。 - 數(shù)值加N,如C0的數(shù)值就是CN0,N是
Number
數(shù)字的意思。
ST編寫AB相編碼器
OUT_C_32(TRUE,CC251,0);
第一個參數(shù)是使能C251,當(dāng)它為TRUE時,X0和X1被指定用于C251,這是PLC內(nèi)部系統(tǒng)的設(shè)計,無法更改。第二個參數(shù)如果是CC251,表示使用C251,如果是CC252就使用C252。
C252比C251多使用一個X2作為復(fù)位。C254比C252多一個X6做外部使能。C253和C252功能一樣,但綁定的是X3,X4,X5。C255和C254功能一樣,但綁定的是X3,X4,X5,X7。
第三個參數(shù)是CS251的閥值。即CS251 := CN251 >= [第三個參數(shù)]
。與普通計數(shù)器不同,第三個參數(shù)不會限制計數(shù)器的增長。如果不需要使用CS251,則第三個參數(shù)可以隨便寫。計數(shù)器復(fù)位當(dāng)然,C251也可以使用RST(X2, CN251);來復(fù)位,C252也可以用OUT_C_32(X6, CC252, 0);RST(X2, CN252);達(dá)到與C254一樣的效果。但指令表的響應(yīng)時間為0.3ms,比直接使用內(nèi)置的功能慢。
梯形圖剩下的功能就只有定時器和計數(shù)器了。下面演示一下定時器和計數(shù)器的用法。這個程序功能為按下X0兩次后,Y0輸出1秒的通,然后變回斷。
OUT_C(X0, CC0, 2);(* 對X0計數(shù),它是邊沿有效的,計數(shù)器C0,CC0是C0的線圈,第2個C代表"Coin" *)
SET(CS0, M0);(* 達(dá)到2的時候,CS0變?yōu)門RUE,對M0置位,將M0也變?yōu)門RUE,CS0是C0的觸點,第2個S代表"Switch"開關(guān)的意思 *)
RST(M0, CN0);(* M0為TRUE時,復(fù)位計數(shù)器C0,M0為TRUE時,按X0不會計數(shù),因為它一直被復(fù)位,CN0是C0的數(shù)值,第2個N是"Number"數(shù)字的意思 *)
OUT_T(M0, TC0, 10);(* M0為TRUE時,定時器T0開始計時,三菱FX3U定時器單位是100ms,第3個參數(shù)10表示1秒 *)
RST(TS0, M0);(* 時間到的時候TS0為TRUE,這一步就會復(fù)位M0,將系統(tǒng)狀態(tài)變回起始值 *)
Y0 := M0;(* 將M0的值從Y0輸出 *)
三、語句
3.1 條件分支IF-THEN…ELSE_IF-THEN…ELSE…END_IF;
我們再來試試自鎖電路的一種標(biāo)準(zhǔn)化寫法,它同樣具有很好的可讀性。
IF X0 THEN
Y0 := TRUE;
ELSE_IF X1 THEN
Y0 := FALSE;
END_IF;(* 注意:這個END_IF是要加分號結(jié)尾的,ST語言坑死人,多少新手被坑在這里好久才發(fā)現(xiàn)突然需要一個分號 *)
比起SET和RST,使用了條件分支使程序看起來更像是說明文檔。它的大意是:“如果X0通了,則讓Y0也通,如果X0沒通,但是X1通了,就讓Y0斷開,如果以上條件都不滿足,就什么也不做,保持之前的狀態(tài)。” 再來一個自鎖(X0,X1)+強制點動(X2)+翻轉(zhuǎn)(X3)的綜合型開關(guān)電路,動力輸出設(shè)計為Y4,并且每當(dāng)按下按鈕時,會有對應(yīng)的指示燈亮起,指示燈設(shè)計Yn對應(yīng)Xn:
K1Y0 := K1X0;(* 4路指示燈直接賦值 *)
IF X0 THEN
Y4 := TRUE;(* 啟動 *)
ELSE_IF X1 THEN
Y4 := FALSE;(* 停止 *)
ELSE_IF LDP(TRUE, X3) THEN
Y4 := NOT Y4;(* 翻轉(zhuǎn) *)
ELSE_IF LDP(TRUE, X2) OR LDF(TRUE, X2) THEN
Y4 := X2;(* X2動作時,取消記憶,強制變?yōu)槭謩?*)
END_IF;(* 再次強調(diào):這個END_IF是要加分號結(jié)尾的,ST語言坑死人,多少新手被坑在這里好久才發(fā)現(xiàn)突然需要一個分號 *)
讀者可以自己試試用梯形圖串并聯(lián)方式實現(xiàn)這個電路,一個簡單的功能做成梯形圖也會非常復(fù)雜。條件分支的完整模板為:
IF conditions THEN
tasks;(* 語法上IF語句任何一段分支的tasks都可以是空的。如果是空的,分號也可以不要 *)
ELSE_IF conditions THEN (* 如果沒有其它條件,ELSE_IF必須省略,如果有多個,ELSE_IF應(yīng)當(dāng)有很多,語法上沒有上限,可以寫到程序存儲器不夠了為止 *)
tasks;
ELSE_IF conditions THEN
tasks;
...
ELSE (* 如果沒有默認(rèn)操作,ELSE可以省略,也可以不省略 *)
tasks;
END_IF;(* 這個地方破天荒地有一個分號 *)
3.2 狀態(tài)分支CASE-OF 1: … 2: … 3: … n: … ELSE … END_CASE;
這種分支結(jié)構(gòu)是專門用于狀態(tài)變量的,需要使用數(shù)字作為狀態(tài)變量,但代碼中不一定要按大小順序排版,也不一定要連續(xù)。未顯式定義的數(shù)字會轉(zhuǎn)接到ELSE段中。做一個演示用的紅綠燈,有紅亮(Y0)、黃亮(Y1)、綠亮(Y2)三種狀態(tài),用一個按鈕X0來控制。這個程序并不十分適合這一場景,只是做為介紹語法之用,具體工程中只需要5~6行代碼就搞定了。
IF LDP(TRUE, X0) THEN
D0 := D0 + 1;(* 狀態(tài)變量如果無止境地加上去會怎么樣?事實上不會,因為在CASE里有做限制 *)
END_IF;
CASE D0 OF
0:(* 狀態(tài)0的時候Y0通。不一定要換行,只是為了寫注釋方便我在冒號后面直接換行了*)
Y0 := TRUE;
Y1 := FALSE;
Y2 := FALSE;
1:(* 狀態(tài)1時Y1通 *)
Y0 := FALSE;
Y1 := TRUE;
Y2 := FALSE;
2:(* 狀態(tài)2時Y2通*)
Y0 := FALSE;
Y1 := FALSE;
Y2 := TRUE;
ELSE(* 其它狀態(tài)時,跳到狀態(tài)0 *)
D0 := 0;
END_CASE;
與C語言家族不同,結(jié)構(gòu)化文本不需要break,它不會從一個狀態(tài)直接就執(zhí)行到下一個狀態(tài)。case語句的結(jié)構(gòu)很簡單,開頭CASE和OF中間只能寫一個數(shù)值變量,后面是常量標(biāo)簽加個冒號,一個ELSE代表未定義的數(shù)值,最后以END_CASE;結(jié)尾,注意那個分號。簡化的寫法,通過條件判斷來賦值:
IF LDP(TRUE, X0) THEN
D0 := (D0 + 1) MOD 3;
END_IF;
Y0 := D0 = 0;
Y1 := D0 = 1;
Y2 := D0 = 2;
還有依次傳遞狀態(tài) (不推薦)
IF LDP(TRUE, X0) THEN
Y2 := Y1;
Y1 := Y0;
END_IF;
Y0 := NOT (Y1 OR Y2);
個人比較推薦6行寫法,比較正規(guī),5行寫法太作了,可讀性不好。目前看來CASE語句好像沒什么用。事實上CASE語句是IF語句的定制化版本,適合范圍更窄,但能少寫一些代碼。接下來的設(shè)計模式中如果條件允許,用CASE會有更好的可讀性,因為不重要的部分(標(biāo)簽數(shù)字)不會太顯眼。
3.3 循環(huán)
FOR temp := 起始值 TO 完成值 BY 增量 DO
業(yè)務(wù)代碼
END_FOR;
while x0 then
y0 := 1;
end_while;
REPEAT
D0 := D0 + 1;
UNTIL D0 == 0 END_REPEAT;
3.4 跳轉(zhuǎn)指令
- EXIT
提前終止當(dāng)前循環(huán),進入尾部的代碼,相當(dāng)于C的 break。
- CONTINUE
提前跳過本次循環(huán),進入首部的代碼,與C的continue類似。但三菱GX Works2中沒有這個關(guān)鍵字。
- RETURN
返回,相當(dāng)于X86匯編的RET指令,RETURN是無操作數(shù)指令。ST的函數(shù)返回值直接函數(shù)名接受值。三菱FX3U的函數(shù)是一次性的,建議不要將自定義函數(shù)用于FX3U。造成三菱FX3U函數(shù)為一次性的原因是FX3U沒有動態(tài)棧。能夠函數(shù)復(fù)用的X86軟件的編譯器會給每一個CALL指令前面加上硬件壓棧指令PUSH,在RET之后會加上硬件彈棧指令POP。硬件棧是由CPU直接操作的連續(xù)內(nèi)存空間,不是軟件模擬的數(shù)據(jù)結(jié)構(gòu)。它需要CPU內(nèi)置有棧指針寄存器。而FX3U的PLC程序是虛擬的處理器,沒有設(shè)計棧指針的功能。雖然可以用軟件棧代替,但他們也沒有這么做。這就導(dǎo)致了所有CALL命令都綁定在一塊固定的寄存器上,無法嵌套。而且寄存器綁定函數(shù)后就會導(dǎo)致數(shù)據(jù)耦合,產(chǎn)生難以預(yù)料的后果。
三菱FX3U的功能塊可以產(chǎn)生多個實例,每一個實例綁定獨立的寄存器,就可以安全地做到代碼復(fù)用。對于純計算,即沒有狀態(tài)存儲功能的函數(shù)和功能塊,可以放心地復(fù)用,對于有狀態(tài)存儲功能的功能塊需要創(chuàng)建多個實例。所有支持梯形圖的PLC都難以實現(xiàn)動態(tài)棧,因為動態(tài)棧和梯形圖的程序理念是相背離的,動態(tài)棧希望程序盡可能使用動態(tài)變量,而梯形圖依賴于靜態(tài)變量。動與靜的矛盾之下,PLC設(shè)計者必須進行取舍。FX3U的內(nèi)存量是足夠動靜共存的,問題在于動態(tài)棧很難兼容梯形圖,而梯形圖才是PLC的主要賣點。
- JMP
跳轉(zhuǎn)到指定標(biāo)簽位置,和X86指令同名,可以用于代碼混淆,不建議手寫。但三菱GX Works2中沒有這個關(guān)鍵字。不確定是不是我的資料的問題。三菱Q系列有JMP指令(比FX系列可以節(jié)省一個LD M8000,其實沒什么很大的意義),但不能作為ST的函數(shù)或關(guān)鍵字。
-
plc
+關(guān)注
關(guān)注
5011文章
13297瀏覽量
463342 -
設(shè)計模式
+關(guān)注
關(guān)注
0文章
53瀏覽量
8634 -
csdn
+關(guān)注
關(guān)注
2文章
16瀏覽量
6845
發(fā)布評論請先 登錄
相關(guān)推薦
評論