Copyright: ISaGRAF V3.4


 ST (結構化文字) 語言

ST (Structured Text)  是一種為自動化流程所設計的高階結構化語言。此語言主要是用來執行無法用繪圖式語言簡單表示出來的複雜程序。在SFC語言中，步驟的行為描述和轉移條件的判斷條件是以ST語言為內定語言。

ST主要語法

表示式和括弧

函式或功能方塊呼叫

ST的特定布林運算符號

ST基本敘述

ST延伸函數


ST主要語法

ST程式是一連串的ST敘述，每一個敘述以一個分號 ( ";" ) 做為結尾。在ST程式原始碼中所使用的名稱 (變數識別字、常數、語言的關鍵字...) 是以無作用分隔符號 (空白字元、列結束或跳位符號) 或是以已定義好含意的有作用分隔符號 (例如，">" 分隔符號是做 "大於" 比較的意思) 來分隔開。文字中可以自由的加上註解，而註解必須啟始於 "(*"，且終止於 "*)"。每一個敘述終結於分號 ( ";" ) 字元。ST敘述的基本形式如下：

-	指定敘述 (變數 := 表示式;)

-	副程式或函數呼叫

-	功能方塊呼叫

-	選擇敘述 (IF，THEN，ELSE，CASE...)

-	遞迴敘述 (FOR，WHILE，REPEAT...)

-	控制敘述 (RETURN，EXIT...)

-	與其他語言 (例如SFC) 連結的特別敘述

無作用分隔符號可以自由的用在有作用分隔符號、常數型態和識別字之間。ST的無作用分隔符號是：空白 (space) 字元、跳位 (tabs) 符號和列結束字元。不像每一列有固定格式的語言 (例如IL)，列結束可以在程式的任何地方使用。當使用無作用分隔符號來增加ST程式的可讀性時，應遵循下面的規則：

-	一列只寫一個敘述

-	使用跳位符號做為複雜敘述的縮排方式

-	加上註解來增加每一行和每一個段落的可讀性


表示式和括弧

ST表示式是由ST運算符號和變數或常數運算子結合起來的。對於每一個單一的表示式 (用ST運算符號將運算子結合起來)，所有運算子的型態必須相同。單一表示式的型態會與它的運算子型態相同，而且可以被用在更複雜的表示式中。例如：

(boo_var1 AND boo_var2)	具有布林型態
not (boo_var1)	具有布林型態
(sin (3.14) + 0.72)	具有實數類比型態
(t#1s23 + 1.78)	是一個無效的表示式

括弧可以用來隔離表示式的子部分，並且明確的整理出它們操作的優先順序。當一個複雜的表示式中沒有任何括弧時，操作順序是以ST運算符號之間的內定優先順序為準。例如：

2 + 3 * 6	等於2+18=20	因為相乘運算符號有較高的優先順序
(2+3) * 6	等於5*6=30	括弧有較高的優先順序

警告：在一個表示式中，槽狀括弧的最大數目是8階層的括弧。


函式或功能方塊呼叫

標準的ST函式呼叫可以使用在下面的每一個物件中：

-	副程式

-	程式庫函式和用IEC語言撰寫的功能方塊

-	"C" 函式和功能方塊

-	型態轉換函式

呼叫副程式或函數

呼叫功能方塊


呼叫副程式或函數

名稱：	被呼叫的副程式的名稱

或是用IEC語言或 "C" 語言撰寫的程式庫函數

含意：	呼叫一個ST、IL、LD或FBD副程式或函數或一個 "C" 函數，並且得到它的回傳值

語法：	<變數> := <副程式> (<參數1>, ... <參數N> );

運算子：	回傳值的型態和呼叫參數必須遵循副程式的定義

回傳值：	由副程式傳回來的值

副程式呼叫可以使用在任何的表示式中，也可以使用在SFC的轉移條件中。

範例一：副程式呼叫

(* ST主程式 *)

(* 得到一個類比數值並且將它轉換成一個有限制的時間數值 *)

ana_timeprog := SPlimit ( tprog_cmd );

appl_timer := tmr (ana_timeprog * 100);

(* 被呼叫的FBD程式 'SPlimit' *)

範例二：函數呼叫

(* 在複雜的表示式中使用標準的 "C" 函數: min, max, right, mlen和left *)

limited_value := min (16, max (0, input_value) );

rol_msg := right (message, mlen (message) - 1) + left (message, 1);


呼叫功能方塊

名稱：	功能方塊樣例的名稱

含意：	從ISaGRAF程式庫或從使用者的程式庫呼叫一個功能方塊，並且使用它的回傳參數

語法：	(* 呼叫功能方塊 *)

<方塊名稱> ( <參數1>, <參數2> ... );

(* 得到它的回傳參數 *)

<結果> := <方塊名稱>. <回傳參數1>;

...

<結果> := <方塊名稱>. <回傳參數N>;

運算子：	參數是配合功能方塊指明的參數型態的表示式

回傳值：	參考語法部份以得到回傳參數

請查閱ISaGRAF library，以便瞭解功能方塊中每一個參數的含意和型態。功能方塊樣例 (功能方塊的複製名稱) 必須在字典中宣告。

範例：

(* 呼叫功能方塊的ST程式 *)

(* 在字典宣告方塊樣例 : *)

(* trigb1 : block R_TRIG - 上升邊界偵測 *)

(* 用ST語言執行功能方塊 *)

trigb1 (b1);

(* 使用回傳參數的 *)

If (trigb1.Q) Then nb_edge := nb_edge + 1; End_if;


ST的特定布林運算符號

下面是ST語言的特定布林運算符號：

REDGE	上升邊緣偵測

FEDGE	下降邊緣偵測

ST語言可以使用其他的標準布林運算符號，如下：

NOT	布林反相

AND (&)	邏輯和

OR	邏輯或

XOR	邏輯互斥或

這些運算符號的相關說明，請參考 '標準運算符號，功能方塊和函式' 章節。


"REDGE" 運算符號

名稱：	REDGE

含意：	求得完整布林表示式的上升邊緣

語法：	<邊緣> := REDGE ( <布林表示式>,<記憶體變數> );

運算子：	第一個運算子是任何的布林變數或複雜的表示式

第二個運算子是一個用來儲存表示式最後狀態的內部布林變數

回傳值：	當表示式從假 (FALSE) 變成真 (TRUE) 時，回傳值為真 (TRUE)。而其他所有的狀況，回傳值皆為假 (FALSE)。

使用REDGE運算符號時，在相同的執行週期中，一個表示式的上升邊緣只能偵測到一次。REDGE運算符號可以用來描述SFC轉移條件的判斷條件。

警告：用來儲存表示式最後狀態的 "記憶體" 布林變數不可以用來做為不同表示式的邊緣觸發器。

假如想在REDGE表示式中使用名稱為 "xxx" 的布林變數，必須先宣告一個名稱為 "EDGE_xxx" 的獨特內部變數，然後在REDGE表示式中使用EDGE_xxx變數。這個方式可以使得REDGE在估值期間，不會覆蓋掉其它的記憶體變數。

範例：

(* 使用REDGE運算符號的ST程式 *)

(* 這個程式是用來計數布林輸入的上升邊緣 *)

(* Bi120是一個輸入布林變數 *)

(* Edge_Bi120是用來儲存Bi120變數的狀態 *)

If REDGE (Bi120, Edge_Bi120) Then

Counter := Counter + 1;

End_if;

注意：IEC1131-1標準沒有包括REDGE運算符號。使用者可以使用R_TRIG標準方塊。REDGE運算符號是因為相容性的理由而被保留下來的。


"FEDGE" 運算符號

名稱：	FEDGE

含意：	求得布林表示式的下降邊緣

語法：	<邊緣> := FEDGE ( <布林表示式>, <記憶體變數> );

運算子：	第一個運算子是任何的布林變數或複雜的表示式

第二個運算子是一個用來儲存表示式最後狀態的內部布林變數

回傳值：	當表示式從真 (TRUE) 變成假 (FALSE) 時，回傳值為真 (TRUE)。而其他所有的狀況，回傳值皆為假 (FALSE)。

使用FEDGE運算符號時，在相同的執行週期中，一個表示式的下降邊緣只能偵測到一次。FEDGE運算符號可以用來描述SFC轉移條件的判斷條件。

警告：用來儲存表示式最後狀態的 "記憶體" 布林變數不可以用來做為不同表示式的邊緣觸發器。

假如想在FEDGE表示式中使用名稱為 "xxx" 的布林變數，必須先宣告一個名稱為 "EDGE_xxx" 的獨特內部變數，然後在FEDGE表示式中使用EDGE_xxx變數。這個方式可以使得REDGE在估值期間，不會覆蓋掉其它的記憶體變數。

範例：

(* 使用FEDGE運算符號的ST程式 *)

(* 這個程式是用來計數一個布林輸入的下降邊緣 *)

(* Bi120是一個輸入布林變數 *)

(* Edge_Bi120是用來儲存Bi120變數的狀態 *)

If FEDGE (Bi120, Edge_Bi120) Then

Counter := Counter + 1;

End_if;

注意：IEC1131-1標準沒有包括FEDGE運算符號，使用者可以使用F_TRIG標準方塊。FEDGE運算符號是因為相容性的理由而被保留下來的。


ST基本敘述

下面是ST語言的基本敘述：

指定

RETURN敘述

IF-THEN-ELSIF-ELSE結構

CASE敘述

WHILE反覆敘述

REPEAT反覆敘述

FOR反覆敘述

EXIT敘述


指定

名稱：	:=

含意：	將表示式結果指定給變數

語法：	<變數> := <任何表示式> ;

運算子：	變數必須是內部變數或輸出變數

變數和表示式必須具有相同的型態

表示式可以是副程式呼叫或是從ISaGRAF程式庫中呼叫函式的敘述。

範例：

(* 使用指定敘述的ST程式 *)

(* 變數 <<= 變數 *)

bo23 := bo10;

(* 變數 <<= 表示式 *)

bo56 := bx34 OR alrm100 & (level >= over_value);

result := (100 * input_value) / scale;

(* 指定為副程式的回傳值 *)

rc := PSelect ( );

(* 指定為函式呼叫 *)

limited_value := min (16, max (0, input_value) );


跳回 (RETURN) 敘述

名稱：	RETURN

含意：	終止目前程式執行

語法：	RETURN ;

運算子：	無

在SFC行為方塊中，RETURN敘述僅表示此方塊執行結束。

範例：

(* 以FBD規格撰寫的程式：可程式計數器 *)

(* 使用ST的RETURN敘述來撰寫此程式 *)

If not (CU) then

Q := false;

CV := 0;

RETURN;   (* 終止程式 *)

end_if;

if R then

CV := 0;

else

if (CV < PV) then

CV := CV + 1;

end_if;

end_if;

Q := (CV >= PV);


IF-THEN-ELSIF-ELSE敘述

名稱：	IF ... THEN ... ELSIF ... THEN ... ELSE ... END_IF

含意：	選擇其中一組ST敘述來執行

選擇是根據布林表示式的數值決定的

語法：	IF <布林表示式n> THEN

   <敘述> ;

   <敘述> ;

   ...

ELSIF <布林表示式> THEN

   <敘述> ;

   <敘述> ;

   ...

ELSE

   <敘述> ;

   <敘述> ;

   ...

END_IF;

ELSE和ELSIF敘述是選擇性的。假如程式中沒有ELSE敘述，則當判斷條件是假 (FALSE) 的時候，不會執行任何指令。

範例：

(* 使用IF敘述的ST程式 *)

IF manual AND not (alarm) THEN

level := manual_level;

bx126 := bi12 OR bi45;

ELSIF over_mode THEN

level := max_level;

ELSE

level := (lv16 * 100) / scale;

END_IF;

(* 沒有ELSE的IF結構 *)

If overflow THEN

alarm_level := true;

END_IF;


CASE敘述

名稱：	CASE ... OF ... ELSE ... END_CASE

含意：	選擇其中一組ST敘述來執行

選擇是根據整數表示式決定的

語法：	CASE <整數表示式> OF

   <數值> : <敘述> ;

   <數值> , <數值> : <敘述> ;

   ...

ELSE

   <敘述> ;

END_CASE;

CASE的數值必須是整數常數形態。以逗點分開的一些數值可以使用相同的一組敘述。ELSE敘述是選擇性的。

範例：

(* 使用CASE敘述的ST程式 *)

CASE error_code OF

255:	err_msg := 'Division by zero';

fatal_error := TRUE;

1:	err_msg := 'Overflow';

2, 3:	err_msg := 'Bad sign';

ELSE

err_msg := 'Unknown error';

END_CASE;


WHILE敘述

名稱：	WHILE ... DO ... END_WHILE

含意：	一組ST敘述的遞迴結構

做任何遞迴動作之前，會先評估是否 "繼續" 遞迴的判斷條件

語法：	WHILE <布林表示式> DO

   <敘述> ;

   <敘述> ;

   ...

END_WHILE ;

警告：因為ISaGRAF是一個同步系統，所以在WHILE的反覆執行期間，輸入變數不會被更新。輸入變數狀態的改變不可用來描述一個WHILE敘述的判斷條件。

範例：

(* 使用WHILE敘述的ST程式 *)

(* 這個程式使用了特殊的 "C" 函式在一個序列埠上讀取字串 *)

string := ''; (* 空字串 *)

nbchar := 0;

WHILE ((nbchar < 16) & ComIsReady ( )) DO

string := string + ComGetChar ( );

nbchar := nbchar + 1;

END_WHILE;


REPEAT敘述

名稱：	REPEAT ... UNTIL ... END_REPEAT

含意：	一組ST敘述的遞迴結構

做任何遞迴動作之後，會評估是否 "繼續" 遞迴的判斷條件

語法：	REPEAT

   <敘述> ;

   <敘述> ;

  ...

UNTIL <布林判斷條件>

END_REPEAT ;

警告：因為ISaGRAF是一個同步系統，所以在REPEAT的反覆執行期間，輸入變數不會被更新。輸入變數狀態的改變不可用來描述一個REPEAT敘述的結束判斷條件。

範例：

(* 使用REPEAT敘述的ST程式 *)

(* 這個程式使用了特殊的 "C" 函式在一個序列埠上讀取字串 *)

string := ''; (* 空字串 *)

nbchar := 0;

IF ComIsReady ( ) THEN

REPEAT

string := string + ComGetChar ( );

nbchar := nbchar + 1;

UNTIL ( (nbchar >= 16) OR NOT (ComIsReady ( )) )

END_REPEAT;

END_IF;


FOR敘述

名稱：	FOR ... TO ... BY ... DO ... END_FOR

含意：	使用整數類比索引變數來執行一個限制數目的遞迴動作

語法：	FOR <索引> := <最小值> TO <最大值> BY <增量> DO

   <敘述> ;

   <敘述> ;

END_FOR;

運算子：	索引：	在任何迴路中遞增的內部類比變數

最小值：	索引的初始值 (第一個迴路之前)

最大值：	索引的最大允許值

增量：	每一個迴路的索引增量

[ BY 增量 ] 敘述是選擇性的，假如沒有特別指定，增量為1。

警告：因為ISaGRAF是一個同步系統，所以在FOR的遞迴執行期間，輸入變數不會被更新。

下面是用 "while" 語法來表達FOR敘述：

索引 := 最小值;

while (索引 <= 最大值) do

   <敘述> ;

   <敘述> ;

   索引 := 索引 + 增量;

end_while;

範例：

(* 使用FOR敘述的ST程式 *)

(* 這個程式用來抽取字串的數位字元 *)

length := mlen (message);

target := ''; (* 空字串 *)

FOR index := 1 TO length BY 1 DO

code := ascii (message, index);

IF (code >= 48) & (code <= 57) THEN

target := target + char (code);

END_IF;

END_FOR;


離開 (EXIT) 敘述

名稱：	EXIT

含意：	跳離FOR、WHILE或REPEAT的遞迴敘述

語法：	EXIT;

運算子：	無

EXIT通常用在IF敘述中，或是一個FOR、WHILE或REPEAT區塊中。

範例：

(* 使用EXIT敘述的ST程式 *)

(* 這個程式會從字串中尋找一個字元 *)

length := mlen (message);

found := NO;

FOR index := 1 TO length BY 1 DO

code := ascii (message, index);

IF (code  = searched_char) THEN

found := YES;

EXIT;

END_IF;

END_FOR;


ST延伸函數

下面是ST語言的延伸函式：

TSTART - TSTOP：計時器控制

下面的敘述和函式可以用來控制SFC子程式的執行。它們可以用在SFC步驟的ACTION(): _ END_ACTION;區塊中。

GSTART	開始SFC程式

GKILL	刪除SFC程式

GFREEZE	冰凍SFC程式

GRST	重新開始被冰凍的SFC程式

GSTATUS	得到SFC程式的目前執行狀態

警告：IEC 1131-3標準不包括以上這些函式。

撰寫SFC步驟時，使用者可以發現GSTART和GKILL的等價語法，如下所示：

child_name(S); (* 與GSTART(child_name);意思等價 *)

child_name(R); (* 與GKILL(child_name);意思等價 *)

下面的欄位可以用來存取SFC步驟的狀態：

GSnnn.x	用來表示步驟動作狀態的布林值

GSnnn.t	從步驟最後活動到目前的時間

( "nnn" 是SFC步驟的參考號碼)

藉著使用下面的語法，使用者也有可能測試另一個SFC程式中的步驟動作情形：

GSnnn(progname).x

警告：參考另一個程式的步驟的語法並沒有包括在IEC 1131-3標準裡。說到IEC規則，一個簡單方式可以達到相同效果，就是在字典裡宣告一個全域的布林變數來表示被測試的步驟的活動情形 (例如ref_step_X)。然後使用者在步驟中插入這個變數的N修飾字 (譬如：ref_step_X(N); )。則在測試步驟動作狀態的程式中，使用者就可以使用這個變數。


TSTART敘述

名稱：	TSTART

含意：	開始計時器變數計時

TSTART指令不會修改時間數值，換言之，計時會從時間目前數值開始。

語法：	TSTART ( <計時器變數> );

運算子：	任何未動作的計時器變數

回傳值：	無

範例：

(* 使用TSTART和TSTOP敘述的SFC程式 *)

假如bi100總是假 (FALSE) 時，時間的變化圖如下：

在一個週期期間，時間一直保持相同的數值。


TSTOP敘述

名稱：	TSTOP

含意：	停止更新一個計時器變數

TSTOP指令不會修改時間數值

語法：	TSTOP ( <計時器變數> );

運算子：	任何正動作中的計時器變數

回傳值：	無

範例：請參考TSTART範例。


GSTART敘述

名稱：	GSTART

含意：	開始一個SFC子程式，並且在每一個初始步驟放進一個權杖符號

語法：	GSTART ( <子程式> );

運算子：	敘述中的子程式必須是這個程式的子程式

回傳值：	無

GSTART敘述不會自動啟始子程式的子程式。

注意：因為IEC 1131-3標準並不包括GSTART，所以可以使用S修飾字，以下面的語法啟始一個SFC子程式：

Child_name(S);

範例：使用GSTART和GKILL


GKILL敘述

名稱：	GKILL

含意：	藉著移除存在於一個SFC子程式之目前步驟的權杖符號來終止此SFC子程式

語法：	GKILL ( <子程式> );

運算子：	敘述中的子程式必須是這個程式的子程式

回傳值：	無

GKILL敘述會自動終止子程式的子程式。

注意：因為IEC 1131-3標準並不包括GKILL，所以可以使用R修飾字，以下面的語法終止一個SFC子程式：

Child_name(R);

範例：請參考GSTART範例。


GFREEZE敘述

名稱：	GFREEZE

含意：	冰凍SFC子程式執行。被冰凍的程式可以用GRST敘述來重新啟始。

語法：	GFREEZE ( <子程式> );

運算子：	敘述中的子程式必須是這個程式的子程式

回傳值：	無

GKILL敘述會自動暫停子程式的子程式。

注意：IEC 1131-3標準不包括GFREEZE。

範例：


GRST敘述

名稱：	GRST

含意：	重新啟始被GFREEZE敘述冰凍的SFC子程式

語法：	GRST ( <子程式> );

運算子：	敘述中的子程式必須是這個程式的子程式

回傳值：	無

GRST敘述會自動重新啟始子程式的子程式。

注意：IEC 1131-3標準不包括GRST。

範例：請參考GFREEZE範例。


GSTATUS敘述

名稱：	GSTATUS

含意：	傳回SFC程式目前的執行狀態

語法：	<ana_var> := GSTATUS ( <子程式> );

運算子：	敘述中的子程式必須是這個程式的子程式

回傳值：	0 = 程式是非執行的狀態 (終止的)

1 = 程式是正在執行的狀態 (啟始的)

2 = 程式是被冰凍的

注意：IEC 1131-3標準不包括GSTATUS。

範例：