• AVR Freaks

Hot!State Machine

Author
Tuurbo46
Super Member
  • Total Posts : 225
  • Reward points : 0
  • Joined: 2004/09/12 05:05:45
  • Location: UK
  • Status: offline
2018/11/05 08:36:05 (permalink)
0

State Machine

Hi All,
 
I am just about to create my first state machine program.  I have read up and it looks like using a switch case is not recommend, and to use a table driven machine is the preferred route.  However after looking for examples, I have got a bit lost.  Could anybody recommend a good tutorial or example i could work though.
 
Look forward to your reply.
 
Thanks,
 
Tuurbo46
#1

11 Replies Related Threads

    mbrowning
    Just a Member
    • Total Posts : 1423
    • Reward points : 0
    • Joined: 2005/03/16 14:32:56
    • Location: Melbourne, FL
    • Status: online
    Re: State Machine 2018/11/05 08:46:47 (permalink)
    +1 (1)
    I never create a state machine other than switch/case. Nothing else makes sense to me. 
    Real example from my code:

    enum i2c_machine{i2c_next, i2c_scan1, i2c_scan2, i2c_scan3,
        i2c_wstart, i2c_wr1, i2c_wr2,
        i2c_rstart, i2c_rd, i2c_rdata,
        i2c_error} i2c1_st = i2c_next, i2c2_st = i2c_next;


    void i2c1_mach (uint8_t init) {
        if (init) i2c1_st = i2c_next;

        switch (i2c1_st) {
        case i2c_next:
            if (i2c1_scan_cmd) {
                i2c1_st        = i2c_scan1;
            } else if (i2c1_rhd != i2c1_rtl) {
                i2c1addr    = (uint8_t)(i2c1_rcbfr[i2c1_rtl].addr << 1u);
                i2c1_reg    = i2c1_rcbfr[i2c1_rtl].reg;
                i2c1_cnt    = i2c1_rcbfr[i2c1_rtl].cnt;
                i2c1_rval_ptr    = i2c1_rcbfr[i2c1_rtl].vptr;
                i2c1_rtl    = (i2c1_rtl + 1u) MOD_RCBFR_SIZE;
                i2c1_st        = i2c_rstart;
            } else if (i2c1_whd != i2c1_wtl) {
                i2c1addr    = (uint8_t)(i2c1_wcbfr[i2c1_wtl].addr << 1u);
                i2c1_reg    = i2c1_wcbfr[i2c1_wtl].reg;
                i2c1_wval    = i2c1_wcbfr[i2c1_wtl].val;
                i2c1_wtl    = (i2c1_wtl + 1u) MOD_WCBFR_SIZE;
                i2c1_st        = i2c_wstart;
            }
            break;

        case i2c_scan1:
            i2c1addr    = 0;
            i2c1_cnt    = 0;
            i2c1_st        = i2c_scan2;
            printf("i2c1 scan ");
            break;
        case i2c_scan2:
            if (I2C1STAT0bits.MMA == 0) {    // if I2C not busy
                I2C1ADB1    = i2c1addr;        // write operation
                I2C1CNT        = 0;            // eeadr (0)
                I2C1CON0bits.S    = 1;        // start the transfer
                CNT1IF        = 0;
                i2c1_st        = i2c_scan3;
            }
            break;
        case i2c_scan3:
            if (I2C1STAT0bits.MMA == 0) {            // if I2C done shifting
                if (I2C1CON1bits.ACKSTAT == 1) {    // no ack received
                    I2C1STAT1bits.CLRBF    = 1;        // clear buffer since no ack
                } else {
                    printf("0x%02x ", (uint16_t)(i2c1addr>>1));
                    i2c1_cnt++;
                }
                if (i2c1addr>= 0xfe) {
                    if (i2c1_cnt) {
                        printf("%u found\r\n", (uint16_t)i2c1_cnt);
                    } else {
                        printf("none found\r\n");
                    }
                    i2c1_scan_cmd = 0;
                    i2c1_st = i2c_next;
                } else {
                    i2c1_st = i2c_scan2;
                    i2c1addr += 2;
                }
            }
            break;

    etc.
            
        case i2c_error:
            if (i2c1fault) {
                printf("i2c1 0x%02x fault\r\n", (uint16_t)(i2c1addr>>1));
                i2c1fault = 0;
            }
            i2c1_st = i2c_next;
            break;

        default:
            i2c1_st    = i2c_next;
            break;
        }
    }


    Oh well - there's always next year
    #2
    jack@kksound
    code tags!
    • Total Posts : 3198
    • Reward points : 0
    • Joined: 2014/05/14 10:03:19
    • Location: 0
    • Status: offline
    Re: State Machine 2018/11/05 09:03:11 (permalink)
    +1 (1)
    In my experience state machines operated from tables are easier more suitable if each state must establish/change a great number of vars/parameters. If not then the switch approach seems to work well and is fairly intuitive to read. Also the compilers seem to do a good job of optimization on this approach. I also use the switch approach in most of my designs, simpler and quicker to code.
    #3
    NKurzman
    A Guy on the Net
    • Total Posts : 17499
    • Reward points : 0
    • Joined: 2008/01/16 19:33:48
    • Location: 0
    • Status: offline
    Re: State Machine 2018/11/05 09:09:52 (permalink)
    +2 (2)
    Either method is valid. Wich is more suited to a task depends on the details of the project.
    On an 8 bit PIC with XC8 a switch may have advantages. It is also easier to code.
    #4
    Tuurbo46
    Super Member
    • Total Posts : 225
    • Reward points : 0
    • Joined: 2004/09/12 05:05:45
    • Location: UK
    • Status: offline
    Re: State Machine 2018/11/05 09:28:44 (permalink)
    0
    Hi,
     
    Thanks for your reply's.  I will be reading a number of 0-5V ADC channels.  Below is a basic example of moving an arm reading one ADC channel. Is the below example best suited to a switch case method?
     
    state 1:
    if(adc1 < 2.3V)
    {
         Move arm left
    {
     
    state 2:
    if(adc1 > 2.7V)
    {
         Move arm right
    {
     
    state 3:
    if((adc1 > 2.3V) && (adc1 < 2.7V))
    {
         Stop arm movement
    {
     
    Thanks,
     
    Tuurbo46
     
     
    #5
    KTrenholm
    Super Member
    • Total Posts : 709
    • Reward points : 0
    • Joined: 2012/08/08 14:04:23
    • Location: Connecticut, USA
    • Status: offline
    Re: State Machine 2018/11/05 09:32:52 (permalink)
    0
    I don't think I'd even use a case statement. 
    Why not just do this?
     
     
    if (adc < 2.3){
     /*Left*/
    } else if ( adc > 2.7 ){
     /*Right*/
    } else {
     /*Stop*/
    }

     
    Turn it into a function you can call when you need to get a direction from a passed in ADC read:

    #define ADC_2_3V   /*ADC Value for 2.3V goes here*/
    #define ADC_2_7V   /*ADC Value for 2.7V goes here*/
    typedef enum{
        ARM_STOP = 0,
        ARM_LEFT = 1,
        ARM_RIGHT = 2
    }    ArmState;

    ArmState GetStateFromADC(uint16_t adc_val){
    ArmState state = ARM_STOP;
    if (adc_val < ADC_2_3V){
        /*Left*/
        state = ARM_LEFT;
    } else if ( adc_val > ADC_2_7V){
        /*Right*/
        state = ARM_RIGHT;
    } else {
        /*Stop*/
        state = ARM_STOP;
    }
    return state;
    }

    post edited by KTrenholm - 2018/11/05 09:46:01
    #6
    Tuurbo46
    Super Member
    • Total Posts : 225
    • Reward points : 0
    • Joined: 2004/09/12 05:05:45
    • Location: UK
    • Status: offline
    Re: State Machine 2018/11/05 09:51:09 (permalink)
    0
    KTrenholm,
     
    Thanks for your input.  That example looks very straight forward and easy to understand.
     
    At what point would you cross over from a regular if-else/ switch to a state machine.  I understand a coffee and a food vending machines are suitable for a state machine process, but i don't really understand how to determine if other applications are suitable for state machine processes.
     
    Look forward to your reply.
     
    Thanks,
     
    Tuurbo46
    #7
    jack@kksound
    code tags!
    • Total Posts : 3198
    • Reward points : 0
    • Joined: 2014/05/14 10:03:19
    • Location: 0
    • Status: offline
    Re: State Machine 2018/11/05 10:05:42 (permalink)
    +1 (1)
    A state machine approach, in my experience, is usually used when a program has many possible responses to external inputs based upon a sequence of events, like following a protocol or deciding which possible sequence is best to follow next (poor description - sorry). Simple reading an A/D value and deciding a direction to go can certainly be done in a state machine but sounds like over-kill to me.
    An example is reading a PS2 keyboard (old example possibly but I know this one): the keyboard can send different sequences of bytes depending on the keys pressed/released so a state machine helps sort it out because you need to recognize the end of a sequence that may be different lengths and there are no terminating characters, only acceptable sequences and incorrect sequences. Hope that helps, a bit wordy, sorry.
    #8
    Jim Nickerson
    User 452
    • Total Posts : 6022
    • Reward points : 0
    • Joined: 2003/11/07 12:35:10
    • Location: San Diego, CA
    • Status: offline
    Re: State Machine 2018/11/05 10:10:12 (permalink)
    +2 (2)
    I tend to use a state machine when the if/ if else gets too complex and convoluted or I am avoiding blocking code and must test a bit before advancing from one state to the next.
    #9
    KTrenholm
    Super Member
    • Total Posts : 709
    • Reward points : 0
    • Joined: 2012/08/08 14:04:23
    • Location: Connecticut, USA
    • Status: offline
    Re: State Machine 2018/11/05 10:12:40 (permalink)
    +2 (2)
    jack@kksound
    A state machine approach, in my experience, is usually used when a program has many possible responses to external inputs based upon a sequence of events, like following a protocol or deciding which possible sequence is best to follow next (poor description - sorry). Simple reading an A/D value and deciding a direction to go can certainly be done in a state machine but sounds like over-kill to me.
    An example is reading a PS2 keyboard (old example possibly but I know this one): the keyboard can send different sequences of bytes depending on the keys pressed/released so a state machine helps sort it out because you need to recognize the end of a sequence that may be different lengths and there are no terminating characters, only acceptable sequences and incorrect sequences. Hope that helps, a bit wordy, sorry.




    Even old examples are good, I had to write a bit-banged PS/2 protocol handler just last year.
    Good example though, since in PS/2 you need to differentiate between make/break codes for your pressed key raw/when shift is held/when num lock is on/when caps lock is on, etc.  Then you have weird fringe cases like Pause/Break which has it's whole own set of rules.
    USB Keyboards are much easier to handle lol.
     
     
    I personally use a state machine to handle "power modes" in the firmware I do.  For example I have "Off", "Bootup", "On", and "Suspend".  Each of these modes have different requirements for entry and exit.  I.E. to exit the "Off" state into "Bootup" the power button must be pressed and held a minimum time.  To go from "Powerup" to "On", thermal requirements must be met and self testing must pass.  Going from "On" into "Suspend" requires a specific signal to go low.  There are rules and actions that must be performed for moving between states and rules for what actions may be performed in each state.  For example, user input that is accepted by the system is limited in "Suspend" whereas it wouldn't be limited (or would have different limitations) in the "On" state.
    post edited by KTrenholm - 2018/11/05 10:27:08
    #10
    Gort2015
    Klaatu Barada Nikto
    • Total Posts : 3122
    • Reward points : 0
    • Joined: 2015/04/30 10:49:57
    • Location: 0
    • Status: offline
    Re: State Machine 2018/11/05 13:49:34 (permalink)
    0
    I use an interrupt state machine sequence to home a motor to a sensor.
     
    1. if not in sensor range move left, find sensor end.
    2. move left, find sensor start.
    3. moverel (end - start + adjust) / 2

    MPLab X playing up, bug in your code? Nevermind, Star Trek:Discovery will be with us soon.
    https://www.youtube.com/watch?v=Iu1qa8N2ID0
    + ST:Continues, "What Ships are Made for", Q's back.
    #11
    JPortici
    Super Member
    • Total Posts : 693
    • Reward points : 0
    • Joined: 2012/11/17 06:27:45
    • Location: Grappaland
    • Status: offline
    Re: State Machine 2018/11/05 15:54:28 (permalink)
    +1 (1)
    I code state machines with the switch/case approach. I like it especially for UI and protocols (I2C, CANbus networking,obd and such)
    I tried to understand the table approach but i still don't get it.
    with I2C i created a slightly different FSM machine where also i have an array containing the sequence of operations and each case will then point to the current/next/end element in case of repeated operation like read next byte/next operation/error
     
    One think i like to do with state machine is also to implement a FSM trace where i record every change of state in case the machine freezes i can see what the control flow was
    post edited by JPortici - 2018/11/05 15:55:35
    #12
    Jump to:
    © 2019 APG vNext Commercial Version 4.5