ในฐานะ CODESYS Solutions Architect ยินดีต้อนรับสู่โลกของ IEC 61131-3 ครับ สำหรับ C# Developer การทำความเข้าใจ Function Block (FB) นั้นง่ายมากครับ

ให้มองว่า Function Block คือ “Class” ใน C# ครับ

  1. ก่อนใช้งาน ต้อง Instantiate (ประกาศตัวแปร) ก่อนเสมอ (เหมือน new Timer())
  2. FB มี Memory/State (จำค่าเก่าได้ เช่น เวลาที่นับค้างไว้ หรือสถานะปุ่มรอบที่แล้ว)
  3. การเรียกใช้ (Call) คือการสั่งให้ Object นั้นทำงานในรอบสแกน (Cyclic Scan) นั้นๆ

นี่คือ Best Practices ในการใช้งาน Standard Libraries (TON, R_TRIG) ในภาษา ST ครับ

1. วิธีการประกาศตัวแปร Instance (Declaration)

ใน CODESYS เราไม่ใช้ keyword new ในการสร้าง Object แต่เราจะประกาศในส่วน VAR ครับ การประกาศนี้ระบบจะจอง Memory ให้โดยอัตโนมัติ

PROGRAM PLC_PRG
VAR
    // Inputs
    xButton      : BOOL;

    // Outputs
    xMotor       : BOOL;
    xCmdShoot    : BOOL; // คำสั่งยิงสัญญาณครั้งเดียว

    // --- 1. การประกาศ Instance (เหมือน: Timer fbTimerLongPress = new Timer();) ---
    fbTimerLongPress : TON;     // Timer On-Delay (หน่วงเวลาเปิด)
    fbTrigOneShot    : R_TRIG;  // Rising Edge Trigger (จับขอบขาขึ้น)
END_VAR

2. ตัวอย่าง Code: Long Press Logic (TON)

โจทย์: ต้องกดปุ่มแช่ไว้นาน 3 วินาที มอเตอร์ถึงจะทำงาน (ถ้าปล่อยก่อน เวลาจะ Reset)

หลักการทำงานของ TON (Timer On-Delay):

  • IN: เงื่อนไขขาเข้า (BOOL)
  • PT: เวลาที่ตั้งไว้ (Preset Time)
  • Q: Output จะ True เมื่อ IN ค้างไว้นานครบ PT
  • ET: เวลาที่นับผ่านไป (Elapsed Time)
// ============================================================
// Logic: Long Press 3 Seconds to Start Motor
// ============================================================

// 1. เรียกใช้งาน Instance (Call the Object)
// ส่ง Parameter แบบ Named Parameters (แนะนำวิธีนี้เพื่อความอ่านง่าย)
fbTimerLongPress(IN := xButton, PT := T#3S);

// 2. นำค่า Output (.Q) ไปใช้งาน
// ถ้ากดค้างครบ 3 วินาที fbTimerLongPress.Q จะเป็น TRUE
xMotor := fbTimerLongPress.Q;

// 💡 C# Analogy:
// เหมือนการเรียก fbTimerLongPress.Update(input, timeSet); ใน Game Loop
// แล้วค่อยเช็ค if (fbTimerLongPress.IsFinished) { ... }

3. ตัวอย่าง Code: Rising Edge (One-shot)

โจทย์: เมื่อกดปุ่ม (จังหวะ 0 -> 1) ให้ส่งคำสั่ง xCmdShoot ทำงานแค่ 1 รอบสแกน (Pulse) แม้จะกดปุ่มค้างไว้

หลักการทำงานของ R_TRIG:

  • CLK: สัญญาณ Clock ขาเข้า
  • Q: จะ True แค่ 1 รอบสแกน เมื่อ CLK เปลี่ยนจาก False เป็น True
// ============================================================
// Logic: Rising Edge Detection (One-Shot)
// ============================================================

// 1. เรียกใช้งาน Instance ทุกรอบสแกน
fbTrigOneShot(CLK := xButton);

// 2. เช็คสถานะ Output (.Q)
// Q จะเป็น TRUE แค่ "แวบเดียว" ตอนที่มือกดปุ่มลง
IF fbTrigOneShot.Q THEN
    // Code ในนี้จะทำงานแค่ครั้งเดียว ต่อการกด 1 ครั้ง
    // เหมาะสำหรับสั่ง Toggle ค่า, บวกเลข Counter, หรือส่ง Data communication
    xCmdShoot := TRUE;
ELSE
    xCmdShoot := FALSE;
END_IF

4. ⚠️ Architect Warning: ทำไมต้องเรียก FB ภายนอก IF?

นี่คือ “The #1 Mistake” ของโปรแกรมเมอร์ที่ย้ายมาจาก C# ครับ ใน C# เรามักจะเขียนแบบนี้เพื่อประหยัด Resource:

// ❌ C# Style (ผิดมหันต์ใน PLC)
if (StartProcess) {
    timer.Update(); // เรียก Timer เฉพาะตอนใช้งาน
}

แต่ใน PLC ห้ามทำแบบนั้นเด็ดขาด! เพราะ:

  1. FB ต้องการการรีเซ็ต: TON จะจับเวลาได้ ต้องเห็น IN เปลี่ยนจาก TRUE เป็น FALSE (Falling Edge) เพื่อ Reset ค่า ET (Elapsed Time) กลับเป็น 0
  2. FB ต้องการเห็นการเปลี่ยนแปลง: R_TRIG ต้องจำค่ารอบที่แล้ว (Last Cycle) เพื่อเทียบกับรอบปัจจุบัน ถ้าคุณหยุดเรียกมันใน IF มันจะจำค่าไม่ได้ และ Logic จะเพี้ยน

✅ วิธีที่ถูกต้อง (Best Practice):

ต้องเรียก FB ให้ทำงาน ทุกรอบสแกน (Outside IF) แล้วค่อยเอาผลลัพธ์ (.Q) ไปใช้ใน IF ครับ

// ❌ BAD Code (เสี่ยง Bug รุนแรง)
IF xButton THEN
    // ถ้า xButton เป็น FALSE, fbTimer จะหยุดทำงาน และ "ค้าง" ค่าเดิมไว้
    // ครั้งหน้ากดใหม่ เวลาอาจจะไม่เริ่มนับจาก 0 หรือ Output ค้างเป็น TRUE ตลอดกาล
    fbTimerLongPress(IN := TRUE, PT := T#3S);
END_IF

// ✅ GOOD Code (Industrial Standard)
// เรียก FB เสมอ เพื่อให้มัน update state ภายในของมันเอง
fbTimerLongPress(IN := xButton, PT := T#3S);

IF fbTimerLongPress.Q THEN
    // เอาผลลัพธ์มาใช้ตรงนี้
    xMotor := TRUE;
END_IF

สรุป: ให้มอง Function Block เหมือน “Hardware Component” (เช่น ชิปจับเวลา) ที่ต้องจ่ายไฟเลี้ยงตลอดเวลา (เรียกใช้นอก IF) ส่วน Logic การควบคุมให้ไปจัดการที่ขา Input/Output ของมันแทนครับ


ติดตามต่อใน EP.5: เราจะนำความรู้ทั้งหมด (IF, CASE, FB) มารวมร่างกันเพื่อสร้าง “State Machine” สำหรับควบคุมเครื่องจักรเต็มรูปแบบครับ