state variable

Thread Starter

Dadu@

Joined Feb 4, 2022
155
Hello,

When I was reading about State Machine I found the following paragraph:

The internal state variable are the smallest possible subset of system variables that can represent the entire state of the system at any given time
I guess a system continuously waits for some external or internal event to occur such as a button press, a time tick, when the event occurs the system takes action.

I don't have any idea what does it means a state variable in the context of a state machine.
 

nsaspook

Joined Aug 27, 2009
16,322
Here the volatile variables disp_tick and adc_tick are state variables that are set in interrupt routines to signal 'events'; The true/false state of these variables determine which functional path the 'main' program takes as the state machine loops 'forever'.

C:
#pragma warning disable 520
#pragma warning disable 1498

#include "ps_test.h"

volatile adc_result_t ana[MAX_ADC_CHAN];
volatile bool disp_tick = false, adc_tick = false;
volatile uint8_t adc_chan = 0;
char buff1[128];

void display_led(DISPLAY_TYPES led);

/*
* 1/4 second trigger interrupt
* TMR6
*
* Blink LED and set flag
*/
void Led_Blink(void)
{
    LED_RESET_Toggle();
    /*
     * xmit serial data flag
     */
    disp_tick = true;
}

/*
* ADC conversion complete interrupt routine
* conversion start triggered by TMR5 interrupt
*
* move conversion data into program data array
* set flag
*/
void Adc_Isr(void)
{
    ana[adc_chan] = ADCC_GetConversionResult();
    if (adc_chan++ >= MAX_ADC_CHAN) {
        adc_chan = 0;
    }
    ADPCH = adc_chan;
    /*
     * adc value valid flag
     */
    adc_tick = true;
}

/*
             Main application
*/
void main(void)
{
    uint8_t dac_v = 1;
    // Initialize the device
    SYSTEM_Initialize();

    LED_MODE_SetLow();
    LED_DB_SetLow();
    TMR6_SetInterruptHandler(Led_Blink);
    TMR5_SetInterruptHandler(Timers_Isr);
    ADCC_SetADIInterruptHandler(Adc_Isr);
    ADPCH = adc_chan;

    // Enable high priority global interrupts
    INTERRUPT_GlobalInterruptHighEnable();

    DAC1_SetOutput(dac_v);

/* state machine for data updates */
    while (true) {
        if (adc_tick) {
            if (ana[0] < 900) {
                display_led(oo00_off);
                display_led(oo30_off);
            } else {
                if (ana[0] > 2500) {
                    display_led(oo00_g);
                    display_led(oo30_g);
                } else {
                    display_led(oo00_r);
                    display_led(oo30_r);
                }
            }

            if (ana[1] < 900) {
                display_led(oo10_off);
                display_led(oo20_off);
            } else {
                if (ana[1] > 2500) {
                    display_led(oo10_g);
                    display_led(oo20_g);
                } else {
                    display_led(oo10_r);
                    display_led(oo20_r);
                }
            }
            adc_tick = false;

            if (disp_tick) {
                sprintf(buff1, "PS: V=%4u I=%4u \r\n", ana[0], ana[1]);
                puts(buff1);
                DAC1_SetOutput(++dac_v);
                disp_tick = false;
            }
        }
    }
}

/**
End of File
*/

/* fragment from timers.c to show Timer #5 ISR code. The interrupt flag triggers the ADC conversion. */
/*
* update the software timers
* TMR5 interrupt
*/
void Timers_Isr(void)
{
    //Decrement each software timer
    for (int i = 0; i < TMR_COUNT; i++) {
        if (tickCount[i] != 0) {
            tickCount[i]--;
        }
    }
}
 

Papabravo

Joined Feb 24, 2006
22,082
The representation of a state variable is completely arbitrary. The only requirement is that the states are distinguishable. You can use bits, numbers, letters, symbols, coins, or rocks.
 

nsaspook

Joined Aug 27, 2009
16,322
A more complex example of state variables and their uses.

State variable header file. State are mainly defined by using C enum typedef's.
C:
#ifndef VCONFIG_H
#define    VCONFIG_H

#include "mcc_generated_files/adcc.h"

#ifdef    __cplusplus
extern "C" {
#endif

#include <xc.h>
#include "mcc_generated_files/spi1.h"
#include "mcc_generated_files/pin_manager.h"
#include "ringbufs.h"

#define VER    "1.64G"
    /*
     * 1.13G wafer load-lock control
     * 1.15 add specific equipment types V.e_types
     * 1.22 message sequencing in secs_II_message
     */
    //#define TESTING
    //#define DISPLAY_SLOW

    //#define DB1
    //#define DB2
    //#define DB3
    //#define DB4
    //#define RERROR    // generate 'random' checksum/link errors to simulate rs-232 bit errors
    //#define DISP_TRIG
#define SEQ_TEST    false    // testing message template transfers

#define SLED    LED0_LAT
#define DLED    DEBUG2_LAT

#define EADOGM_CMD_CLR        1
#define EADOGM_CMD_CURSOR_ON     0b00001111
#define EADOGM_CMD_CURSOR_OFF    0b00001100
#define EADOGM_CMD_DISPLAY_ON    0b00001100
#define EADOGM_CMD_DISPLAY_OFF   0b00001000
#define EADOGM_CMD_DDRAM_ADDR    0b10000000
#define EADOGM_CMD_CGRAM_ADDR    0b01000000
#define EADOGM_CMD_SELECT_R0     0b00011000
#define EADOGM_CMD_SELECT_R1     0b00010000
#define EADOGM_CMD_SET_TABLE2    0b00101010
#define EADOGM_COLSPAN        16

#define T1    500
#define T2    3000
#define T3    5000
#define T4    5000
#define HBTS    5000 // short ping delay
#define HBTL    30000 // ping delay
#define    RTY    3
#define ERROR_CHECKSUM    31500
#define ERROR_COMM    31500

#define DEFAULT_TID    1
#define TDELAY    3000
#define LDELAY    1000
#define SDELAY    500
#define BDELAY    300
#define DDELAY    100 // display update spacing
#define DFLIP    1500 // display info flipping spacing

#define Y2KTIME

    /*
     * offsets in bytes
     */

#define TX_RESERVE    59

    /*
     * Host message ACK CEIDs
     */
#define V_OSCREEN    93
#define V_SSCREEN    94
#define E_OSCREEN    81
#define E_OSCREEN    81

#define S10F3_STR_POS    135
#define S10F3_TID_POS    138

#define MAX_LINE    16

    struct spi_link_type { // internal SPI state table
        uint8_t SPI_LCD : 1;
        uint8_t SPI_AUX : 1;
        uint8_t LCD_TIMER : 1;
        volatile uint8_t LCD_DATA : 1;
        uint16_t delay;
        uint8_t config;
        struct ringBufS_t *tx1b, *tx1a;
        volatile int32_t int_count;
    };

    typedef enum {
        CODE_TS = 0,
        CODE_TM = 1,
        CODE_ONLOCAL = 2, // CEID codes
        CODE_ONREMOTE = 3,
        CODE_OFFLINE = 4,
        CODE_DEBUG,
        CODE_LOG,
        CODE_LOAD,
        CODE_UNLOAD,
        CODE_PUMP,
        CODE_HELP,
        CODE_SEQUENCE,
        CODE_ERR,
    } P_CODES;

    typedef enum {
        DIS_STR = 0,
        DIS_TERM,
        DIS_LOG,
        DIS_LOAD,
        DIS_UNLOAD,
        DIS_PUMP,
        DIS_HELP,
        DIS_SEQUENCE,
        DIS_SEQUENCE_M,
        DIS_ERR,
        DIS_CLEAR,
    } D_CODES;

    typedef struct terminal_type {
        uint8_t ack[32], mesgid;
        uint8_t TID, mcode, mparm, cmdlen, log_seq;
        uint8_t host_display_ack : 1;
        D_CODES info, help_temp;
        uint16_t ceid;
        uint16_t log_num;
    } terminal_type;

    typedef enum {
        SEQ_STATE_INIT = 0,
        SEQ_STATE_RX,
        SEQ_STATE_TX,
        SEQ_STATE_TRIGGER,
        SEQ_STATE_QUEUE,
        SEQ_STATE_DONE,
        SEQ_STATE_ERROR
    } SEQ_STATES;

    typedef enum {
        UI_STATE_INIT = 0,
        UI_STATE_HOST,
        UI_STATE_DEBUG,
        UI_STATE_LOG,
        UI_STATE_ERROR
    } UI_STATES;

    typedef enum {
        GEM_STATE_DISABLE = 0,
        GEM_STATE_COMM,
        GEM_STATE_OFFLINE,
        GEM_STATE_ONLINE,
        GEM_STATE_REMOTE,
        GEM_STATE_ERROR
    } GEM_STATES;

    typedef enum {
        GEM_GENERIC = 0,
        GEM_VII80,
        GEM_E220,
        GEM_ERROR
    } GEM_EQUIP;

    typedef enum {
        LINK_STATE_IDLE = 0,
        LINK_STATE_ENQ,
        LINK_STATE_EOT,
        LINK_STATE_ACK,
        LINK_STATE_DONE,
        LINK_STATE_NAK,
        LINK_STATE_ERROR
    } LINK_STATES;

    typedef enum {
        LINK_ERROR_NONE = 10,
        LINK_ERROR_T1,
        LINK_ERROR_T2,
        LINK_ERROR_T3,
        LINK_ERROR_T4,
        LINK_ERROR_CHECKSUM,
        LINK_ERROR_NAK,
        LINK_ERROR_ABORT,
        LINK_ERROR_SEND
    } LINK_ERRORS;

    typedef enum {
        MSG_ERROR_NONE = 0,
        MSG_ERROR_ID = 1,
        MSG_ERROR_STREAM = 3,
        MSG_ERROR_FUNCTION = 5,
        MSG_ERROR_DATA = 7,
        MSG_ERROR_TIMEOUT = 9,
        MSG_ERROR_DATASIZE = 11,
        MSG_ERROR_RESET = 20
    } MSG_ERRORS;

    typedef struct V_data { // control data structure
        SEQ_STATES s_state;
        UI_STATES ui_state;
        GEM_STATES g_state;
        GEM_EQUIP e_types;
        LINK_STATES m_l_state;
        LINK_STATES r_l_state;
        LINK_STATES t_l_state;
        char buf[64], terminal[160], info[64];
        uint32_t ticks, systemb;
        int32_t testing;
        uint8_t stream, function, error, abort, msg_error, msg_ret, alarm;
        UI_STATES ui_sw;
        uint16_t r_checksum, t_checksum, checksum_error, timer_error, ping, mode_pwm, equip_timeout, sequences, all_errors;
        uint8_t rbit : 1, wbit : 1, ebit : 1, set_sequ : 1,
        failed_send : 4, failed_receive : 4,
        queue : 1, debug : 1, help : 1, stack : 3, help_id : 2;
        terminal_type response;
        uint8_t uart, llid, sid, ping_count;
        volatile uint8_t ticker;
        bool flipper;
    } V_data;

    typedef struct V_help {
        const char message[32], display[32];
    } V_help;

#ifdef    __cplusplus
}
#endif

#endif    /* VCONFIG_H */
Protocol state machine code fragment using C switch-case structure.
C:
LINK_STATES m_protocol(LINK_STATES *m_link)
{
    uint8_t rxData;
    static uint8_t rxData_l = 0, *b_block = (uint8_t*) & H254[0];

    switch (*m_link) {
    case LINK_STATE_IDLE:
#ifdef DB1
        WaitMs(50);
#endif
        if (UART1_is_rx_ready()) {
            rxData = UART1_Read();
            if (rxData == ENQ) {
                //                IO_RB4_SetHigh();
                V.uart = 1;
                StartTimer(TMR_T2, T2);
                V.error = LINK_ERROR_NONE; // reset error status
                *m_link = LINK_STATE_ENQ;
            }
        }
        if (UART2_is_rx_ready()) {
            rxData = UART2_Read();
            if (rxData == ENQ) {
                //                IO_RB5_SetHigh();
                V.uart = 2;
                StartTimer(TMR_T2, T2);
                V.error = LINK_ERROR_NONE; // reset error status
                *m_link = LINK_STATE_ENQ;
            }
        }
        break;
    case LINK_STATE_ENQ:
        rxData_l = 0;
        if (TimerDone(TMR_T2)) {
            V.error = LINK_ERROR_T2;
            V.all_errors++;
            V.timer_error++;
            V.failed_receive = 2;
            *m_link = LINK_STATE_NAK;
        } else {
#ifdef DB2
            WaitMs(1);
            if (V.uart == 1)
#ifdef RERROR
                if (rand() < ERROR_COMM)
#endif
                    secs_send((uint8_t*) & H27[0], sizeof(header27), true, V.uart);
            if (V.uart == 2)
#ifdef RERROR
                if (rand() < ERROR_COMM)
#endif
                    secs_send((uint8_t*) & H10[0], sizeof(header10), true, V.uart);
            V.error = LINK_ERROR_NONE; // reset error status
            *m_link = LINK_STATE_EOT;
            StartTimer(TMR_T2, T2);
#else
            if (V.uart == 2 && UART1_is_rx_ready()) {
                rxData = UART1_Read();
                if (rxData == EOT) {
                    StartTimer(TMR_T2, T2);
                    V.error = LINK_ERROR_NONE; // reset error status
                    *m_link = LINK_STATE_EOT;
                }
            }
            if (V.uart == 1 && UART2_is_rx_ready()) {
                rxData = UART2_Read();
                if (rxData == EOT) {
                    StartTimer(TMR_T2, T2);
                    V.error = LINK_ERROR_NONE; // reset error status
                    *m_link = LINK_STATE_EOT;
                }
            }
#endif
        }
        break;
    case LINK_STATE_EOT:
        if (TimerDone(TMR_T2)) {
            V.error = LINK_ERROR_T2;
            V.timer_error++;
            V.all_errors++;
            V.failed_receive = 2;
            *m_link = LINK_STATE_NAK;
        } else {
            if (V.uart == 1 && UART1_is_rx_ready()) {
                rxData = UART1_Read();
                if (rxData_l == 0) { // start header reads
                    r_block.length = rxData; // header+message bytes
                    run_checksum(0, true);
                    rxData_l++;
                    b_block[sizeof(header254) - rxData_l] = rxData; // buffer the message
                } else {
                    /*
                     * skip possible message data
                     */
                    if (rxData_l <= sizeof(block10)) // save header only
                        H10[1].block.b[sizeof(block10) - rxData_l] = rxData;
                    if (rxData_l <= r_block.length) // generate checksum from data stream
                        V.r_checksum = run_checksum(rxData, false);

                    if (rxData_l == r_block.length + 1) // checksum high byte
                        H10[1].checksum = (uint16_t) rxData << 8;
                    if (rxData_l == r_block.length + 2) // checksum low byte
                        H10[1].checksum += rxData;

                    rxData_l++;
                    b_block[sizeof(header254) - rxData_l] = rxData;
                    if (rxData_l > (r_block.length + 2)) { // end of total data stream
                        if (V.r_checksum == H10[1].checksum) {
                            *m_link = LINK_STATE_ACK;
                        } else { // bad checksum
                            while (UART1_is_rx_ready()) // dump receive buffer of bad data
                                rxData = UART1_Read();
                            WaitMs(T1); // inter-character timeout
                            V.error = LINK_ERROR_CHECKSUM;
                            V.checksum_error++;
                            V.all_errors++;
                            V.failed_receive = 3;
                            *m_link = LINK_STATE_NAK;
                        }
                    }
                }
            }

            if (V.uart == 2 && UART2_is_rx_ready()) {
                rxData = UART2_Read();
                if (rxData_l == 0) { // start header reads
                    r_block.length = rxData; // header+message bytes
                    run_checksum(0, true);
                    rxData_l++;
                    b_block[sizeof(header254) - rxData_l] = rxData; // buffer the message
                } else {
                    /*
                     * skip possible message data
                     */
                    if (rxData_l <= sizeof(block10)) // save header only
                        H10[1].block.b[sizeof(block10) - rxData_l] = rxData;
                    if (rxData_l <= r_block.length) // generate checksum from data stream
                        V.r_checksum = run_checksum(rxData, false);

                    if (rxData_l == r_block.length + 1) // checksum high byte
                        H10[1].checksum = (uint16_t) rxData << 8;
                    if (rxData_l == r_block.length + 2) // checksum low byte
                        H10[1].checksum += rxData;

                    rxData_l++;
                    b_block[sizeof(header254) - rxData_l] = rxData;
                    if (rxData_l > (r_block.length + 2)) { // end of total data stream
                        if (V.r_checksum == H10[1].checksum) {
                            *m_link = LINK_STATE_ACK;
                        } else { // bad checksum
                            while (UART2_is_rx_ready()) // dump receive buffer of bad data
                                rxData = UART2_Read();
                            WaitMs(T1); // inter-character timeout
                            V.error = LINK_ERROR_CHECKSUM;
                            V.checksum_error++;
                            V.all_errors++;
                            V.failed_receive = 4;
                            *m_link = LINK_STATE_NAK;
                        }
                    }
                }
            }
        }
        break;
    case LINK_STATE_ACK:
#ifdef DB1
        WaitMs(1);
#endif
        V.stream = H10[1].block.block.stream;
        V.function = H10[1].block.block.function;
        V.systemb = H10[1].block.block.systemb;
        V.rbit = H10[1].block.block.rbit;
        V.wbit = H10[1].block.block.wbit;
        V.ebit = H10[1].block.block.ebit;
        V.failed_receive = false;
        secs_II_monitor_message(V.stream, V.function, LDELAY); // log selected messages
        V.g_state = secs_gem_state(V.stream, V.function);
        *m_link = LINK_STATE_DONE;
        //        IO_RB4_SetLow();
        //        IO_RB5_SetLow();
        break;
    case LINK_STATE_NAK:
        *m_link = LINK_STATE_ERROR;
        V.all_errors++;
        while (UART1_DataReady) { // dump the receive buffer
            UART1_Read();
        }
        while (UART2_DataReady) { // dump the receive buffer
            UART2_Read();
        }
        break;
    case LINK_STATE_ERROR:
        break;
    case LINK_STATE_DONE: // normally we don't execute this code state
        V.failed_receive = false;
        //        IO_RB4_Toggle();
        //        IO_RB5_Toggle();
    default:
        *m_link = LINK_STATE_IDLE;
        break;
    }

    return *m_link;
}
 

Thread Starter

Dadu@

Joined Feb 4, 2022
155
The representation of a state variable is completely arbitrary. The only requirement is that the states are distinguishable. You can use bits, numbers, letters, symbols, coins, or rocks.
Can the system tick be an example of a state variable?
 

ErnieM

Joined Apr 24, 2011
8,415
Can the system tick be an example of a state variable?
No, that's just a variable. Think of something concrete, like a car.

The states of a car may be things like OFF, STARTING, IDLE, FORWARD, REVERSE, and BRAKING. A computer overseeing things may do different reactions depending on the state.

Say the driver enters "moar gas" by pressing the gas pedal. If the state is either OFF or BREAKING that input may be ignored.

A formal state machine not only as defined states but define transitions between states. I just used a huge state machine with well over 50 states, but that was due to an inappropriate set of user visual widgets that would only update when "my" code was done. So my code was triggered by a timer and the code would do the next thing (or usually nothing) defined by the state, a tick timer, and some external inputs. It would do something quick, then return control to the user interface stuff so the screen would keep active. As this was running a test program in synch with a temperature chamber running over 24 hours yeah, keeping the display current was importaint.
 
Last edited:
Top