ตัวอย่าง project เพื่อการทำงาน Co-operative multitasking ด้วย State machine ในแบบ simple

  • 58 Replies
  • 15905 Views
   สำหรับน้องๆ เรื่องนี้อาจจะยังไหม่และไม่ใช่เรื่องพื้นฐานแน่ๆ ถ้ายังมือใหม่แนะนำขอให้ข้ามหัวข้อนี้ไปก่อนครับ หากสนใจก็ยินดีครับ
   ข้อดีของการใช้ Co-operative multitasking ด้วย state machine คือใช้กับ cpu/mcu ขนาดเล็ก ที่มี resource น้อย ข้อจำกัดมากได้ดี ทำงานซับซ้อนเกือบเทียบเท่าการใช้ RTOS เลย
   ในการเขียนโปรแกรมแบบ Co-operative multitasking ในเรื่องหนึ่งที่สำคัญคือการใช้ state machine เพื่อใช้การทำการ code
แต่เนื่องด้วย code ที่เป็นพื้นฐานด้านการเขียนในแนว state machine ค่อนข้างจะน้อย หนังสือก็น้อย อาจเป็นเพราะเข้าใจยากและไม่ใช่เรื่องพื้นฐานนัก จึงอยากจะขอแนะนำ code ที่เป็น state machine แบบเบื้องต้น ทำให้ดูง่าย(เท่าที่จะทำได้) และหลายๆท่านในนี้ อาจจะชำนาญแล้วด้วยซ้ำ แต่เห็นว่าน่าจะยังมีอีกส่วนหนึ่งที่ยังไม่มีโอกาศได้รู้จึงนำมาเสนอกัน และผมได้ทำการประยุกต์ปรับปรุงให้ดูง่ายพอที่จะนำเสนอได้ ส่วนถ้าเป็นรายละเอียดทั้งหมดของเรื่องนี้ ขอแนะนำให้ดูจากหนังสือที่ชื่อ Embededded Multitasking โดย Keith E. Curtis จะเหมาะสมกว่าครับ (หา Download ได้จาก web บางแห่ง หรือหาซื้อมาอ่านก็จะเป็นการดีครับ)
   เนื่องจากกระทู้ก่อนหน้านี้ ทำให้ผมพยายามหาตัวอย่างการเขียนในแนวนี้ ซึ่งเคยทำไว้นานมากแล้ว ซึ่งเกิดโดยอาศัยข้อมูลจากนิตยสาร Embedded system programming เพื่อมาเป็นแนวทาง แต่คงหายากมากแล้ว และเ็ป็นบทย่อยๆ ต้องประติดประต่อเอามาก แต่หากสนใจให้อ่านทีุ่่ี่ัั็หนังสือ Embededded Multitasking จะอ่านได้เป็นเรื่องเป็นราวกว่าครับ
  ผมเองได้ทำตัวอย่างไว้กับ MCS-51 แต่อาจนำไปปรับแก้้ให้เป็น CPU หรือ MPU อื่นๆได้เช่น PIC , AVR เพราะใช้แค่ความสามารถของภาษา C ในการทำงาน หากถนัด C อยู่แล้วก็จะง่ายขึ้น แต่ท่านสามารถนำหลักของ state machine ไปประยุกต์กับภาษาือื่นๆก็น่าจะได้ ตามที่ตนถนัดเพียงขอให้เข้าใจในหลักการครับ (แต่พวกหนังสือก็อาจจะอ้างอิงกับ C มากกว่านะครับ)
   ตัวอย่าง Code สาธิตนี้ เพื่อให้ง่ายต่อความเข้่าใจ ผมจึงทำเป็นพวก ไฟกระพริบ กับ Switch กด และใช้ Uart ในการทำงานนะครับ โดย
ผมให้มีการทำงานดังนี้
   1. LED1 กระพริบแบบ Standby mode ( แบบไฟของโทรศัพท์มือถือ )
   2. LED2 กระพริบแบบ Cycle เป็นจังหวะ
   3. LED3 กระพริบแบบ Standby mode หรือ Cycle ตาม Switch กด (กดครั้งที่ 1 เป็น Standby ครั้งที่ 2 เป็น Cycle และสลับไปมา)
   4. Uart ให้รับค่าจาก RS-232 ของ PC (ที่ 9600 BPS) แล้วส่งค่านั้นกลับไปยัง PC อีกครั้ง เหมือน Echo แต่ให้สามารถรับข้อมูลได้ต่อเนื่องได้

Hardware ใช้ MPU กลุ่ม MCS-51 เช่น 89C51 89C2051 อะไรทำนองนี้ จึงขออธิบายเป็นการต่อตามแบบนี้ครับ
(ผมไม่ได้วาดเป็นวงจรไว้่นะครับ คิดว่าน่าจะพอเข้าใจ)

ให้ P1.0 เป็นการควบคุม LED1 , ให้ P1.1 เป็นการควบคุม LED2 , ให้ P1.2 เป็นการควบคุม LED3 โดยการต่อทั้ง 3 ดวงเป็นแบบ กระแส Sink นะครับ  และให้ P1.7 ต่อกับเป็น Switch ส่วนช่อง Uart RXD และ TXD ต่อไปสื่อสารกับ PC ที่ช่อง Comport

เอาละครับ มาดู code กันเลยดีกว่า สำหรับใน Code นี้ผมแก้แล้วให้ใช้ Keil เป็นตัว compile นะครับ (ใครที่ติดเป็น Demo version ก็ใช้ได้เพราะได้ hex file ที่ไม่ใหญ่เลย) หรือท่านอื่นๆอาจจะใช้ compiler ตัวอื่นๆได้ ก็คงแก้ไม่มากนัก
ใน code ผมมีการใช้ Interrupt timer (ช่อง 0) เพียงอย่างเดียวเพื่อทำเรื่องของ state timer แต่ก็มีวิธีที่ไม่ใช้ Interrupt timer ได้ ก็ลองปรับแก้ิูได้ แต่ที่ทำไว้เพื่อความสะดวก และผมคิดว่า้จะมีแง่มุมบางอย่างด้วยครับ ส่วน Uart ไม่ได้ใช้ interrupt เลย อีกอย่าง Code นี้ไม่ได้ทำการ optimize ใดๆ เป็นตัวอย่างเพื่อสร้างความเข้าใจเท่านั้น หากนำไปใช้จริง คงต้องจัดการต่อเองครับ

ผมจะแสดงเน้นเฉพาะ file สำคัญนะครับ

อันนี้เป็น main.c
Code: [Select]
/*************************************************************************
 * File name     : main.c
 * Created by    : AppleIIe
 * Date created  : 2000/6/26
 * Description   : main , io , section
 * Notes         : -
 *************************************************************************/
/* Include section
 * Add all #includes here
 *************************************************************************/
#include <REG52.H>              /* special function register declarations   */
                                  /* for the intended 8051 derivative         */
#include "state.h"
#include "main.h"
#include "co_state.h"

/*************************************************************************
 * Defines section
 * Add all #defines here
 *************************************************************************/
#define     XTAL_BASE   110592000
#define     F_CPU       ( XTAL_BASE / 12 )

// timer overflow with 1ms.
#define     T1MS        -( F_CPU / 1000 )

// calculate baud to TH1
#define     UART_BAUD   9600
#define     _BAUD       ( 256 - ( ( F_CPU / 32 ) / UART_BAUD ) )

/*************************************************************************
 * Function Prototype Section
 * Add prototypes for all functions called by this
 * module, with the exception of runtime routines.
 *************************************************************************/

/*************************************************************************
                           initialize device
 *************************************************************************/
static void init_port(void)
{
    P1 = 0xff;
    P3 = 0xff;
}

//------------------------------------------------------------------------
static void init_uart(void)
{
    SCON = 0x50;                /* SCON: mode 1, 8-bit UART, enable rcvr      */
    TMOD = 0x21;                /* TMOD: timer 1, mode 2, 8-bit reload        */
    TH1 = _BAUD;                /* TH1:  reload value for 9600 baud           */
    TR1 = 1;                    /* TR1:  timer 1 run                          */
    TI = 0;                     // CLEAR TI
    ES = 0;                     // uart interrupt
}

//------------------------------------------------------------------------
static void init_timer(void)
{
    // load T1MS
    TMOD = 0x21;                /* TMOD: timer 1, mode 2, 8-bit reload        */
    TL0 = T1MS & 0xff;
    TH0 = T1MS >> 8;
    TR0 = 1;
    ET0 = 1;
}

/*************************************************************************
                         Timer interrupt
 *************************************************************************/
static timer_pack _state_time;

void isr_timer0(void) interrupt 1
{
    // load T1MS
    TR0 = 0;
    TL0 = T1MS & 0xff;
    TH0 = T1MS >> 8;
    TR0 = 1;
    _state_time++;
}

timer_pack get_state_timer(void)
{
    timer_pack temp;

    DI;                         // critical
    temp = _state_time;
    EI;                         // end of critical
    return temp;
}

/*************************************************************************
 * Function name : void main( void )
 * Description   : main loop process
 * Notes         : -
 *************************************************************************/
void main(void)
{
    timer_pack _time;

    init_port();                // port
    init_uart();                // uart
    init_timer();               // timer with interrupt
    EI;                         // enable interrupt

    // begin of time
    _time = get_state_timer();
    for (;;) {

        // co-operative task without timer
        co_state_task_uart();
        // ... other co-state at here

        // check point of state timer
        {
            if (_time == get_state_timer())
                continue;
            _time++;
        }

        // engine of co-operative task with timer
        co_state_timer_task_led1();
        co_state_timer_task_led2();
        co_state_timer_task_led3();
        co_state_timer_task_button_switch();
        // ... other co-state timer at here
    }
}

file นี้เป็นการทำงานของแต่ละ state machine ครับ co_state.c
Code: [Select]
/*************************************************************************
 * File name     : co_state.c
 * Created by    : AppleIIe
 * Date created  : 2000/6/26
 * Description   : operate with co-state
 * Notes         : -
 *************************************************************************/
/* Include section
 * Add all #includes here
 *************************************************************************/
#include <REG52.H>              /* special function register declarations   */
                                  /* for the intended 8051 derivative         */
#include "state.h"
#include "main.h"
#include "co_state.h"

/*************************************************************************
 * Defines section
 * Add all #defines here
 *************************************************************************/
sbit LED1 = P1 ^ 0;
sbit LED2 = P1 ^ 1;
sbit LED3 = P1 ^ 2;

sbit BTN_SW = P1 ^ 7;

#define     ON_LED1     { LED1 = 0; }
#define     OFF_LED1    { LED1 = 1; }

#define     ON_LED2     { LED2 = 0; }
#define     OFF_LED2    { LED2 = 1; }

#define     ON_LED3     { LED3 = 0; }
#define     OFF_LED3    { LED3 = 1; }

/*************************************************************************
 * Function Prototype Section
 * Add prototypes for all functions called by this
 * module, with the exception of runtime routines.
 *************************************************************************/

/*************************************************************************
                           Co-state task LED1
 *************************************************************************/
void co_state_timer_task_led1(void)
{
    enum { LED_STATE_BEGIN, LED_STATE_1, LED_STATE_2 };
    state_declare;
    state_timer_declare;

    state_timer_process();
    state_switch {
    case LED_STATE_BEGIN:
        // initial co-state at here
        state_jump(LED_STATE_1);        // jump to next state
        return;
    case LED_STATE_1:
        ON_LED1;
        state_delay(T1SEC * 1 / 10, LED_STATE_2);       // delay 100 mS.
        return;
    case LED_STATE_2:
        OFF_LED1;
        state_delay(T1SEC * 1, LED_STATE_1);    // delay 1 Sec.
        return;
    }
}

/*************************************************************************
                           Co-state task LED2
 *************************************************************************/
void co_state_timer_task_led2(void)
{
    enum { LED_STATE_BEGIN, LED_STATE_1, LED_STATE_2 };
    state_declare;
    state_timer_declare;

    state_timer_process();
    state_switch {
    case LED_STATE_BEGIN:
        // initial co-state at here
        state_jump(LED_STATE_1);        // jump to next state
        return;
    case LED_STATE_1:
        ON_LED2;
        state_delay(T1SEC * 3 / 10, LED_STATE_2);       // delay 300 mS.
        return;
    case LED_STATE_2:
        OFF_LED2;
        state_delay(T1SEC * 3 / 10, LED_STATE_1);       // delay 300 mS.
        return;
    }
}

/*************************************************************************
                           Co-state task LED3
 *************************************************************************/
static bit _led_busy;
void co_state_timer_task_led3(void)
{
    enum { LED_STATE_BEGIN, LED_STATE_MON,
        LED_STATE_STB1, LED_STATE_STB2,
        LED_STATE_BUSY1, LED_STATE_BUSY2
    };
    state_declare;
    state_timer_declare;

    state_timer_process();
    state_switch {
    case LED_STATE_BEGIN:
        // initial co-state at here
        state_jump(LED_STATE_MON);      // jump to next state
        return;
    case LED_STATE_MON:
        if (!_led_busy) {
            state_jump(LED_STATE_STB1);
            return;
        }
        state_jump(LED_STATE_BUSY1);
        return;
    case LED_STATE_STB1:
        ON_LED3;
        state_delay(T1SEC * 1 / 10, LED_STATE_STB2);    // delay 100 mS.
        return;
    case LED_STATE_STB2:
        OFF_LED3;
        state_delay(T1SEC * 1, LED_STATE_MON);  // delay 1 Sec.
        return;
    case LED_STATE_BUSY1:
        ON_LED3;
        state_delay(T1SEC * 3 / 10, LED_STATE_BUSY2);   // delay 300 mS.
        return;
    case LED_STATE_BUSY2:
        OFF_LED3;
        state_delay(T1SEC * 3 / 10, LED_STATE_MON);     // delay 300 mS.
        return;
    }
}

/*************************************************************************
                      co-state task button switch
                    This state will toggle _lcd_busy
 *************************************************************************/
static bit get_btn_switch(void);

void co_state_timer_task_button_switch(void)
{
    enum { SW_STATE_BEGIN, SW_STATE_1 };
    state_declare;
    state_timer_declare;

    state_timer_process();
    state_switch {
    case SW_STATE_BEGIN:
        state_jump(SW_STATE_1); // jump to next state
        return;
    case SW_STATE_1:
        if (get_btn_switch())
            _led_busy = !_led_busy;
        state_delay(10, SW_STATE_1);    // debounce 10 ms.
        return;
    }
}

//------------------------------------------------------------------------
// get switch press
static bit get_btn_switch(void)
{
    static bit sw_flg;

    if (BTN_SW) {
        sw_flg = false;
        return false;
    }
    if (sw_flg)
        return false;
    sw_flg = true;
    return true;
}

/*************************************************************************
                           Co-state task uart
 *************************************************************************/
static void co_state_task_uart_rx(void);
static void co_state_task_uart_tx(void);

static unsigned char _rx_buff;
static bit _rx_flag;

void co_state_task_uart(void)
{
    co_state_task_uart_rx();
    co_state_task_uart_tx();
}

//------------------------------------------------------------------------
// co-state uart rx
static void co_state_task_uart_rx(void)
{
    enum { RX_STATE_BEGIN, RX_STATE_1 };
    state_declare;

    state_switch {
    case RX_STATE_BEGIN:
        // initial co-state at here
        state_jump(RX_STATE_1); // jump to next state
        return;
    case RX_STATE_1:
        if (RI) {
            RI = 0;
            _rx_buff = SBUF;
            _rx_flag = true;
        }
        return;
    }
}

//------------------------------------------------------------------------
// co-state uart tx
static void co_state_task_uart_tx(void)
{
    enum { TX_STATE_BEGIN, TX_STATE_1, TX_STATE_2 };
    state_declare;

    state_switch {
    case TX_STATE_BEGIN:
        // initial co-state at here
        TI = 0;
        state_jump(TX_STATE_1); // jump to next state
        return;
    case TX_STATE_1:               // wait rx buffer
        if (_rx_flag) {
            SBUF = _rx_buff;
            _rx_flag = false;
            state_jump(TX_STATE_2);     // jump to next state
            return;
        }
        return;
    case TX_STATE_2:               // wait ti flag
        if (TI) {
            TI = 0;
            state_jump(TX_STATE_1);     // jump to next state
            return;
        }
        return;
    }
}

สุดท้าย Header file ที่ชื่อ state.h
Code: [Select]
#ifndef         STATE_PROCESS_H
#define         STATE_PROCESS_H

/*************************************************************************
 * File name     : STATE.H
 * Created by    : AppleIIe
 * Date created  : 2000/6/26
 * Description   : Header of State machine file
 * Notes         : define macro for state machine control.
 *************************************************************************/
// #include "state_cfg.h"
#define USE_SIMPLE_STATE

#ifdef  USE_SIMPLE_STATE
/*************************************************************************
                Macro for simple state machine control.
 *************************************************************************/
typedef unsigned timer_pack;
typedef unsigned char state_pack;

// declare state variable
#define state_declare           static state_pack _state

// jump to other state
#define state_jump( target )  { _state = target; return; }

/*  use this macro in state timer only */
// declare state timer variable
#define state_timer_declare     static timer_pack _state_timer

// process timer of state
#define state_timer_process()  { if ( _state_timer ) { _state_timer--; return; } }
// delay with state timer , alway tmr > 0
#define state_delay(tmr,target) { _state_timer = (tmr)-1; _state = target; return; }
// way of state
#define state_switch    switch ( _state )

/*************************************************************************
                       End of simple state macro
 *************************************************************************/
#endif

#endif                          // STATE_PROCESS_H
   
ลองนำไปศึกษาดูก่อนครับ น่าจะไม่ยากนัก
หากท่านใดทดสอบแล้วผลเป็นอย่างไร ก็แนะนำกันได้นะครับ ติชมกันแบบตรงไปตรงมาได้ แต่ขอให้อย่างเป็นประโยชน์และสร้างสรรค์
เพื่อจะได้นำไปเป็นข้อมูลไปทำการปรับปรุงต่อให้สำหรับท่านอื่นๆให้ได้ประโยชน์มากขึ้น และขออภัยหากอธิบายรายละเอียดได้ไม่ดี

Download file ทั้งหมดได้ครับ จากข้างล่างนี้ครับ
"Stay Hungry, Stay Foolish"
จงกระหาย และ ทำตัวโง่ให้ตลอดเวลา
"Innovation distinguishes between a leader and a follower."
นวัตกรรมแยกผู้นำกับผู้ตามออกจากกัน

คนฉลาด...ต้องโง่เป็น คนโง่ไม่เป็น...จะไม่มีทางฉลาด

*

Offline JENG

  • *****
  • 808
  • รู้ทุกเรื่องเว้นเรื่องจริง
    • View Profile
สามารถติดตาม electoday ได้ที่

Facebook
www.facebook.com/groups/coopmicro

Google+
https://plus.google.com/communities/103482067769375459277

☺☺☺ความรู้ และความฉลาด ไม่ใช่สิ่งเดียวกัน จะมีประโยชน์อะไร ถ้าฉลาดแต่อยู่ในกะลา☺☺☺

ท่าน Jeng ครับ
จากตัวอย่างนี้ จุดสำคัญของเรื่องที่ท่านสงสัยมาตลอด คือ state ที่ชื่อว่า co_state_task_uart ครับ
ซึ่งจาก Link http://wara.com/article-578.html ที่ท่านเคยแนะนำมาจะทำไม่ได้ดีนักในเรื่องนี้ครับ
ส่วนเรื่อง macro คงไม่มีอะไรใหม่นะครับ เพียงแต่ macro สามารถช่วยให้อธิบายอะไรต่างๆใน code
และนี่คือเสน่ห์ของมัน ซึ่งท่านก็คงจะทราบอยู่แล้ว ถ้าท่านสนใจลองศึกษา macro แบบทำนองนี้ได้จากพวก
Source code จาก Salvo ของ pumpkin  (http://www.pumpkininc.com/) หรือ
OSA ( http://www.pic24.ru/doku.php/en/osa/ref/download/intro ) ซึ่งมีอยู่เยอะครับ และขอบคุณที่ชอบครับ  :)

ใน Code ตัวอย่าง (โชคดีที่ยังเก็บไว้และหาเจอเพราะลืมไปแล้ว แล้วนำมาถกกับท่านได้) ผมได้แฝงมุมมองการเขียน Code เอาไว้ ตามที่เคยถกกันนะครับ เช่น การใช้ Global variable / function โดยใ้ช้เท่าที่จำเป็น , การไม่ได้เรียกว่า task เป็นหลัก และอื่นๆ ลองอ่านดู  เป็นตัวอย่างไว้ในมุมของผมครับ ( อาจไม่ดีที่สุดในทุกเรื่อง แต่ว่าทำให้จัดการอะไรมันง่ายขึ้นครับ และคิดว่าไม่อยากจะอ้างไปที่หลักการใดๆมาก เพราะแนวความคิดที่อาจแตกต่างกันอยู่บ้าง )
"Stay Hungry, Stay Foolish"
จงกระหาย และ ทำตัวโง่ให้ตลอดเวลา
"Innovation distinguishes between a leader and a follower."
นวัตกรรมแยกผู้นำกับผู้ตามออกจากกัน

คนฉลาด...ต้องโง่เป็น คนโง่ไม่เป็น...จะไม่มีทางฉลาด

*

Offline boe

  • ***
  • 104
    • View Profile
เยี่ยมครับ ขอศึกษาด้วยคน ;)
ไม่มีความยากจน ในหมู่คนขยัน

เยี่ยมครับ ขอศึกษาด้วยคน ;)

เชิญศึกษาได้เลยครับ ต้องขออภัยท่านด้วยที่อาจจะใช้ mpu เก่าไปหน่อย แต่เห็นว่าไม่น่าจะเป็นปัญหา เพราะใช้ภาษา C ที่ไม่ได้อ้างอิงกับ Hardware แบบใด ด้วยหลักการของ State machine นี้ครับ แต่จะให้ดีก็อาจต้องใช้กับพวก Ansi C จะดีกว่า

ความจริงเรื่อง state machine ยังมีแง่มุมในความสามารถทที่ำอะไรได้มากกว่านี้ครับ เช่น การ Call ระหว่าง state  , state ที่มีการ synchronize กันระหว่าง state และอื่นๆ เรียกว่ามี function การทำงานคล้ายๆ RTOS เลยทีเดียว แต่มันจะทำให้เข้าใจได้ยากขึ้น สลับซับซ้อนขึ้น จนอาจทำให้ท้อเสียก่อนครับ เอาเป็นว่าการเริ่มต้นแล้ว เอาเพียงแค่นี้จะดีกว่า

ดังนั้นการเขียน code แบบใช้ state machine จะทำให้เหนื่อยกว่าพวก RTOS มากครับ แต่คุ้มตรงที่ใช้ mcu ตัวเล็กๆได้ดี และในงานที่เหมาะสมครับ ส่วน mcu ระดับกลางๆ เราสามารถใช้ RTOS ร่วมกันกับ state machine ได้ครับ อย่าง FreeRTOS ก็มี state machine นี้แฝงอยู่เรียกว่า Co-routines (http://www.freertos.org/croutine.html) ครับ ทำให้ช่วยในการใช้ Resource ได้น้อยลง (แต่เหนื่อยเพิ่มขึ้นอีกนิดหน่อยครับ)
"Stay Hungry, Stay Foolish"
จงกระหาย และ ทำตัวโง่ให้ตลอดเวลา
"Innovation distinguishes between a leader and a follower."
นวัตกรรมแยกผู้นำกับผู้ตามออกจากกัน

คนฉลาด...ต้องโง่เป็น คนโง่ไม่เป็น...จะไม่มีทางฉลาด

ขอบคุณ มากเลยครับ ที่สละเวลามาแบ่งปันความรู้ดีๆ
ผมเองก็กำลังจะเริ่มแล้วเหมือนกันครับ เพราะคิดว่าคงอาจจะมีโอกาศได้ใช้บ้างในเร็ววัน

*

Offline ROM

  • ***
  • 167
    • View Profile
เป็นการใช้ define ที่แปลกดี ไม่เคยเห็นเหมือนกัน  แต่ดูดีจัง คิดได้ไง
ทำให้ใช้ง่ายดี ทำไว้ตั้งแต่ปี 2000 อีกด้วย (เมื่อ 13 ปีก่อนเลย)
เข้าใจแล้วว่าทำไมมันไม่ใช่เรื่องพื้นฐาน แค่ define ก็ยอดเยี่ยมไปเลย
ขอรับไปศึกษาบ้าง ขอบคุณมากครับ

ขอบคุณทุกท่านมากครับ ที่ให้ข้อมูลมา และติดตามกัน
ผมจะว่าต่อในเรื่องของการเขียนแบบ state machine ในแบบ simple ตา่มหัวข้อเลยนะครับ
หากท่านได้อ่านจาก code พอเข้าใจแล้วนะครับว่า ใน main จะไปทำ co-state อยู่ 2 แบบ
    1. co-state ที่ไม่ใ้ช้ Timer ใช้กับ co-state task ที่ำทำงานตลอดเวลาไม่อิงกับ Timer ใดๆ
    2. co-state แบบมี Timer เป็นลักษณะของการทำงานแบบ Timer event ใช้สำหรับ co-state task ที่ใช้ีการ Delay มาประกอบกัน
    ส่วนการเขียนใน co-state ต่างๆนั้น เราก็ต้องจัดสรร ให้ถูกว่า จะใช้ co-state แบบไหนให้ตรงกันตามตัวอย่าง
    ส่วนใน function ที่เป็น co_state นั้นท่านก็ใช้ state_jump(next_state) และ state_delay(time,next_state)
ในการกำหนดการทำงานไปสู่ state ต่างๆของ co_state เช่น (ใช้ case ประกอบนะครับ) ในตัวอย่างของ co_state_timer_task_led1


    // ประกาศ ค่าของ state ภายใน co_state ใช้เป็นการภายในเท่านั้น
    enum { LED_STATE_BEGIN, LED_STATE_1, LED_STATE_2 };

    // การทำงานของ state
    case LED_STATE_BEGIN:
        // initial co-state at here
        state_jump(LED_STATE_1);        // jump to next state
        return;
    case LED_STATE_1:
        ON_LED1;
        state_delay(T1SEC * 1 / 10, LED_STATE_2);       // delay 100 mS.
        return;
    case LED_STATE_2:
        OFF_LED1;
        state_delay(T1SEC * 1, LED_STATE_1);    // delay 1 Sec.
        return;


     เริ่มต้นของ co_state_timer_task_led1 นี้ การทำงานจะมาที่ LED_STATE_BEGIN
และจะไปที่ LED_STATE_1 ในรอบต่อไป เมื่อมาถึง LED_STATE_1 ก็ทำการ ON_LED1 และเตรียมตัวไป LED_STATE_2
โดยใช้ state_delay ให้ชลอเวลาไว้จนครบ 100 ms. แล้วไปที่ LED_STATE_2 เมื่อมาถึง LED_STATE_2 ก็ทำการ OFF_LED1
และเตรียมตัวไป LED_STATE_1 โดยใช้ state_delay ให้ชลอเวลาไว้จนครบ 1 Sec. และวนเวียน ใน LED_STATE_1 และ LED_STATE_2 นี้ หวังว่าคงเข้าใจกันนะครับ

    เอาละเดี๋ยวเราเริ่มมาลัดสั้นกันนะครับจะได้ใช้กันอย่างง่ายๆ
    ใน state.h ให้เพิ่ม #define เข้าไปครับ

#define co_state        state_declare;\
                        state_switch

#define co_state_timer  state_declare;\
                        state_timer_declare;\
                        state_timer_process();\
                        state_switch


    ก็จะได้ดังนี้ state.h ใหม่ดังนี้

Code: [Select]
#ifndef         STATE_PROCESS_H
#define         STATE_PROCESS_H

/*************************************************************************
 * File name     : STATE.H
 * Version       : 1.001
 * Created by    : AppleIIe
 * Date created  : 2000/6/26
 * Description   : Header of State machine file
 * Notes         : define macro for state machine control.
 *************************************************************************/
// #include "state_cfg.h"
#define USE_SIMPLE_STATE

#ifdef  USE_SIMPLE_STATE
/*************************************************************************
                Macro for simple state machine control.
 *************************************************************************/
typedef unsigned timer_pack;
typedef unsigned char state_pack;

// declare state variable
#define state_declare           static state_pack _state

// jump to other state
#define state_jump( target )  { _state = target; return; }

/*  use this macro in state timer only */
// declare state timer variable
#define state_timer_declare     static timer_pack _state_timer

// process timer of state
#define state_timer_process()  { if ( _state_timer ) { _state_timer--; return; } }
// delay with state timer , alway tmr > 0
#define state_delay(tmr,target) { _state_timer = (tmr)-1; _state = target; return; }
// way of state
#define state_switch    switch ( _state )
// shortcut of co_state/co_state_timer
#define co_state        state_declare;\
                        state_switch

#define co_state_timer  state_declare;\
                        state_timer_declare;\
                        state_timer_process();\
                        state_switch

/*************************************************************************
                       End of simple state macro
 *************************************************************************/
#endif

#endif                          // STATE_PROCESS_H

เวลานำไปใช้ใน co-state ก็เพียงแค่ เรียก macro ที่ชื่อ co_state ( ไม่ใช้ Timer event co-state )
หรือ co_state_timer (ใช้ Timer event co-state ) เช่น จากตัวอย่างแก้ code ในส่วน
co_state_timer_task_led1 ได้เป็น

Code: [Select]
/*************************************************************************
                           Co-state task LED1
 *************************************************************************/
void co_state_timer_task_led1(void)
{
    enum { LED_STATE_BEGIN, LED_STATE_1, LED_STATE_2 };

    co_state_timer {
    case LED_STATE_BEGIN:
        // initial co-state at here
        state_jump(LED_STATE_1);        // jump to next state
        return;
    case LED_STATE_1:
        ON_LED1;
        state_delay(T1SEC * 1 / 10, LED_STATE_2);       // delay 100 mS.
        return;
    case LED_STATE_2:
        OFF_LED1;
        state_delay(T1SEC * 1, LED_STATE_1);    // delay 1 Sec.
        return;
    }
}

ส่วน co_state_task_uart_rx ก็จะได้เป็น
Code: [Select]
//------------------------------------------------------------------------
// co-state uart rx
static void co_state_task_uart_rx(void)
{
    enum { RX_STATE_BEGIN, RX_STATE_1 };

    co_state {
    case RX_STATE_BEGIN:
        // initial co-state at here
        state_jump(RX_STATE_1); // jump to next state
        return;
    case RX_STATE_1:
        if (RI) {
            RI = 0;
            _rx_buff = SBUF;
            _rx_flag = true;
        }
        return;
    }
}

ชีวิตก็จะง่ายขึ้นเยอะเลยครับ

สุดท้ายนี้คงต้องขออภัยหากอธิบายในเรื่องนี้ได้อย่างไม่ดีนัก

Download ตัวอย่างแบบ ลัดสั้นได้เลยครับ
"Stay Hungry, Stay Foolish"
จงกระหาย และ ทำตัวโง่ให้ตลอดเวลา
"Innovation distinguishes between a leader and a follower."
นวัตกรรมแยกผู้นำกับผู้ตามออกจากกัน

คนฉลาด...ต้องโง่เป็น คนโง่ไม่เป็น...จะไม่มีทางฉลาด