switch case with enum (LED state machine)

Thread Starter

Kittu20

Joined Oct 12, 2022
431
Hello everyone

I am trying to write program using the PIC18F45K80 microcontroller that controls a set of LEDs connected to PORTB pins. The LEDs should exhibit three states: OFF, ON, and BLINKING. The program should , turning off the LEDs in the OFF state, turning them on in the ON state, and making them blink five times in the BLINKING state.

I have written simple program on tested on PC

PC program
C:
#include<stdio.h>

enum LightState {OFF, ON, BLINKING};
enum LightState state;

int main()
{
    switch ( state = ON)
    {
      
        case OFF:
                 printf("ALL Lights OFF \n");
                 break;
        case ON:
                 printf("ALL Lights ON \n");
                 break;
        case BLINKING:
                 printf("ALL Lights Blinking \n"); 
                 break;
    }

  return 0; 
}
Output
ALL Lights ON

This program should turning ON all LEDs using a switch-case structure and an enum but I don't understand why LEDs are not turning on the state "ON" ?. I am sure there is no problem in connection.

Embedded C Program
C:
// MPLAB XC8 PIC19F45K80
#include <xc.h>
#include <stdio.h>
#include "config.h"

// Define LED states using an enum
enum LEDState {
    OFF,
    ON,
    BLINKING
};

void main(void) {
    TRISB = 0x00;  // Configure all pins of PORTB as outputs
    LATB = 0x00;   // Initialize PORTB to low

    while (1) {
        enum LEDState state = ON;  // Initial LED state
      
        switch (state) {
            case OFF:
                LATB = 0x00;  // Turn off LEDs
                break;
            case ON:
                LATB = 0xFF;  // Turn on LEDs
                break;
            case BLINKING:
                for (int i = 0; i < 5; i++) {
                    LATB = 0xFF;  // Turn on LEDs
                    __delay_ms(500); // Delay for 500 ms
                    LATB = 0x00;  // Turn off LEDs
                    __delay_ms(500); // Delay for 500 ms
                }
                break;
        }
    }

    return;
}
configuartion file
Code:
config.h
#define _XTAL_FREQ 20000000

config.c

// PIC18F45K80 Configuration Bit Settings
// CONFIG1L
#pragma config RETEN = ON       // VREG Sleep Enable bit (Ultra low-power regulator is Enabled (Controlled by SRETEN bit))
#pragma config INTOSCSEL = LOW  // LF-INTOSC Low-power Enable bit (LF-INTOSC in Low-power mode during Sleep)
// SOSCSEL = No Setting
#pragma config XINST = OFF      // Extended Instruction Set (Disabled)
// CONFIG1H
#pragma config FOSC = HS2       // HS oscillator (high power, 16 MHz-25 MHz
#pragma config PLLCFG = OFF     // PLL x4 Enable bit (Disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor (Disabled)
#pragma config IESO = OFF       // Internal External Oscillator Switch Over Mode (Disabled)
// CONFIG2L
#pragma config PWRTEN = ON      // Power Up Timer (Enabled)
#pragma config BOREN = OFF      // Brown Out Detect (Disabled in hardware, SBOREN disabled)
#pragma config BORV = 0         // Brown-out Reset Voltage bits (3.0V)
#pragma config BORPWR = LOW     // BORMV Power level (BORMV set to low power level)
// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer (WDT disabled in hardware; SWDTEN bit disabled)
#pragma config WDTPS = 1        // Watchdog Postscaler (1:1)
// CONFIG3H
#pragma config CANMX = PORTC    // ECAN Mux bit (ECAN TX and RX pins are located on RC6 and RC7, respectively)
#pragma config MSSPMSK = MSK5   // MSSP address masking (5 bit address masking mode)
#pragma config MCLRE = ON      // Master Clear Enable (MCLR Enabled, RE3 Disabled)
// CONFIG4L
#pragma config STVREN = OFF     // Stack Overflow Reset (Disabled)
#pragma config BBSIZ = BB1K     // Boot Block Size (1K word Boot Block size)
// CONFIG5L
#pragma config CP0 = ON         // Code Protect 00800-01FFF (Enabled)
#pragma config CP1 = ON         // Code Protect 02000-03FFF (Enabled)
#pragma config CP2 = ON         // Code Protect 04000-05FFF (Enabled)
#pragma config CP3 = ON         // Code Protect 06000-07FFF (Enabled)
// CONFIG5H
#pragma config CPB = ON         // Code Protect Boot (Enabled)
#pragma config CPD = ON         // Data EE Read Protect (Enabled)
// CONFIG6L
#pragma config WRT0 = ON        // Table Write Protect 00800-01FFF (Enabled)
#pragma config WRT1 = ON        // Table Write Protect 02000-03FFF (Enabled)
#pragma config WRT2 = ON        // Table Write Protect 04000-05FFF (Enabled)
#pragma config WRT3 = ON        // Table Write Protect 06000-07FFF (Enabled)
// CONFIG6H
#pragma config WRTC = ON        // Config. Write Protect (Enabled)
#pragma config WRTB = ON        // Table Write Protect Boot (Enabled)
#pragma config WRTD = ON        // Data EE Write Protect (Enabled)
// CONFIG7L
#pragma config EBTR0 = ON       // Table Read Protect 00800-01FFF (Enabled)
#pragma config EBTR1 = ON       // Table Read Protect 02000-03FFF (Enabled)
#pragma config EBTR2 = ON       // Table Read Protect 04000-05FFF (Enabled)
#pragma config EBTR3 = ON       // Table Read Protect 06000-07FFF (Enabled)
// CONFIG7H
#pragma config EBTRB = ON       // Table Read Protect Boot (Enabled)
// #pragma config statements should precede project file includes.
 

BobTPH

Joined Jun 5, 2013
8,671
The code should indeed turn the LEDs on.

My guess is that the processor is not running. You have many settings in your config that I would not be touching typically. Have you verified that these config settings are working?

You have the oscillator set to high frequency crystal. Do you have a 16-25MHz crystal installed on osc1 and osc2?
 
Last edited:

Thread Starter

Kittu20

Joined Oct 12, 2022
431
The code should indeed turn the LEDs on.

My guess is that the processor is not running. You have many settings in your config that I would not be touching typically. Have you verified that these config settings are working?
I wanted to provide an update on the issue I was facing. Problem was related to the configuration settings of my microcontroller. After some troubleshooting, I decided to use the internal oscillator instead of an external one. I'm pleased to report that this change has resolved my issue, and everything is working as expected now.
Code:
#define _XTAL_FREQ 8000000
#include <xc.h>
// PIC18F45K80 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1L
#pragma config RETEN = OFF      // VREG Sleep Enable bit (Ultra low-power regulator is Disabled (Controlled by REGSLP bit))
#pragma config INTOSCSEL = HIGH // LF-INTOSC Low-power Enable bit (LF-INTOSC in High-power mode during Sleep)
#pragma config SOSCSEL = HIGH   // SOSC Power Selection and mode Configuration bits (High Power SOSC circuit selected)
#pragma config XINST = OFF       // Extended Instruction Set (Enabled)
// CONFIG1H
#pragma config FOSC = INTIO2    // Oscillator (Internal RC oscillator)
#pragma config PLLCFG = OFF     // PLL x4 Enable bit (Disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor (Disabled)
#pragma config IESO = OFF       // Internal External Oscillator Switch Over Mode (Disabled)
// CONFIG2L
#pragma config PWRTEN = OFF     // Power Up Timer (Disabled)
#pragma config BOREN = SBORDIS  // Brown Out Detect (Enabled in hardware, SBOREN disabled)
#pragma config BORV = 3         // Brown-out Reset Voltage bits (1.8V)
#pragma config BORPWR = ZPBORMV // BORMV Power level (ZPBORMV instead of BORMV is selected)
// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer (WDT disabled in hardware; SWDTEN bit disabled)
#pragma config WDTPS = 1048576  // Watchdog Postscaler (1:1048576)
// CONFIG3H
#pragma config CANMX = PORTB    // ECAN Mux bit (ECAN TX and RX pins are located on RB2 and RB3, respectively)
#pragma config MSSPMSK = MSK7   // MSSP address masking (7 Bit address masking mode)
#pragma config MCLRE = ON       // Master Clear Enable (MCLR Enabled, RE3 Disabled)
// CONFIG4L
#pragma config STVREN = ON      // Stack Overflow Reset (Enabled)
#pragma config BBSIZ = BB2K     // Boot Block Size (2K word Boot Block size)
// CONFIG5L
#pragma config CP0 = OFF        // Code Protect 00800-01FFF (Disabled)
#pragma config CP1 = OFF        // Code Protect 02000-03FFF (Disabled)
#pragma config CP2 = OFF        // Code Protect 04000-05FFF (Disabled)
#pragma config CP3 = OFF        // Code Protect 06000-07FFF (Disabled)
// CONFIG5H
#pragma config CPB = OFF        // Code Protect Boot (Disabled)
#pragma config CPD = OFF        // Data EE Read Protect (Disabled)
// CONFIG6L
#pragma config WRT0 = OFF       // Table Write Protect 00800-01FFF (Disabled)
#pragma config WRT1 = OFF       // Table Write Protect 02000-03FFF (Disabled)
#pragma config WRT2 = OFF       // Table Write Protect 04000-05FFF (Disabled)
#pragma config WRT3 = OFF       // Table Write Protect 06000-07FFF (Disabled)
// CONFIG6H
#pragma config WRTC = OFF       // Config. Write Protect (Disabled)
#pragma config WRTB = OFF       // Table Write Protect Boot (Disabled)
#pragma config WRTD = OFF       // Data EE Write Protect (Disabled)
// CONFIG7L
#pragma config EBTR0 = OFF      // Table Read Protect 00800-01FFF (Disabled)
#pragma config EBTR1 = OFF      // Table Read Protect 02000-03FFF (Disabled)
#pragma config EBTR2 = OFF      // Table Read Protect 04000-05FFF (Disabled)
#pragma config EBTR3 = OFF      // Table Read Protect 06000-07FFF (Disabled)
// CONFIG7H
#pragma config EBTRB = OFF      // Table Read Protect Boot (Disabled)
Thank you for your help in guiding me through this challenge.
 

xox

Joined Sep 8, 2017
838
Hello everyone

I am trying to write program using the PIC18F45K80 microcontroller that controls a set of LEDs connected to PORTB pins. The LEDs should exhibit three states: OFF, ON, and BLINKING. The program should , turning off the LEDs in the OFF state, turning them on in the ON state, and making them blink five times in the BLINKING state.

I have written simple program on tested on PC

PC program
C:
#include<stdio.h>

enum LightState {OFF, ON, BLINKING};
enum LightState state;

int main()
{
    switch ( state = ON)
    {
     
        case OFF:
                 printf("ALL Lights OFF \n");
                 break;
        case ON:
                 printf("ALL Lights ON \n");
                 break;
        case BLINKING:
                 printf("ALL Lights Blinking \n");
                 break;
    }

  return 0;
}
Output
ALL Lights ON

This program should turning ON all LEDs using a switch-case structure and an enum but I don't understand why LEDs are not turning on the state "ON" ?. I am sure there is no problem in connection.

Embedded C Program
C:
// MPLAB XC8 PIC19F45K80
#include <xc.h>
#include <stdio.h>
#include "config.h"

// Define LED states using an enum
enum LEDState {
    OFF,
    ON,
    BLINKING
};

void main(void) {
    TRISB = 0x00;  // Configure all pins of PORTB as outputs
    LATB = 0x00;   // Initialize PORTB to low

    while (1) {
        enum LEDState state = ON;  // Initial LED state
     
        switch (state) {
            case OFF:
                LATB = 0x00;  // Turn off LEDs
                break;
            case ON:
                LATB = 0xFF;  // Turn on LEDs
                break;
            case BLINKING:
                for (int i = 0; i < 5; i++) {
                    LATB = 0xFF;  // Turn on LEDs
                    __delay_ms(500); // Delay for 500 ms
                    LATB = 0x00;  // Turn off LEDs
                    __delay_ms(500); // Delay for 500 ms
                }
                break;
        }
    }

    return;
}
configuartion file
Code:
config.h
#define _XTAL_FREQ 20000000

config.c

// PIC18F45K80 Configuration Bit Settings
// CONFIG1L
#pragma config RETEN = ON       // VREG Sleep Enable bit (Ultra low-power regulator is Enabled (Controlled by SRETEN bit))
#pragma config INTOSCSEL = LOW  // LF-INTOSC Low-power Enable bit (LF-INTOSC in Low-power mode during Sleep)
// SOSCSEL = No Setting
#pragma config XINST = OFF      // Extended Instruction Set (Disabled)
// CONFIG1H
#pragma config FOSC = HS2       // HS oscillator (high power, 16 MHz-25 MHz
#pragma config PLLCFG = OFF     // PLL x4 Enable bit (Disabled)
#pragma config FCMEN = OFF      // Fail-Safe Clock Monitor (Disabled)
#pragma config IESO = OFF       // Internal External Oscillator Switch Over Mode (Disabled)
// CONFIG2L
#pragma config PWRTEN = ON      // Power Up Timer (Enabled)
#pragma config BOREN = OFF      // Brown Out Detect (Disabled in hardware, SBOREN disabled)
#pragma config BORV = 0         // Brown-out Reset Voltage bits (3.0V)
#pragma config BORPWR = LOW     // BORMV Power level (BORMV set to low power level)
// CONFIG2H
#pragma config WDTEN = OFF      // Watchdog Timer (WDT disabled in hardware; SWDTEN bit disabled)
#pragma config WDTPS = 1        // Watchdog Postscaler (1:1)
// CONFIG3H
#pragma config CANMX = PORTC    // ECAN Mux bit (ECAN TX and RX pins are located on RC6 and RC7, respectively)
#pragma config MSSPMSK = MSK5   // MSSP address masking (5 bit address masking mode)
#pragma config MCLRE = ON      // Master Clear Enable (MCLR Enabled, RE3 Disabled)
// CONFIG4L
#pragma config STVREN = OFF     // Stack Overflow Reset (Disabled)
#pragma config BBSIZ = BB1K     // Boot Block Size (1K word Boot Block size)
// CONFIG5L
#pragma config CP0 = ON         // Code Protect 00800-01FFF (Enabled)
#pragma config CP1 = ON         // Code Protect 02000-03FFF (Enabled)
#pragma config CP2 = ON         // Code Protect 04000-05FFF (Enabled)
#pragma config CP3 = ON         // Code Protect 06000-07FFF (Enabled)
// CONFIG5H
#pragma config CPB = ON         // Code Protect Boot (Enabled)
#pragma config CPD = ON         // Data EE Read Protect (Enabled)
// CONFIG6L
#pragma config WRT0 = ON        // Table Write Protect 00800-01FFF (Enabled)
#pragma config WRT1 = ON        // Table Write Protect 02000-03FFF (Enabled)
#pragma config WRT2 = ON        // Table Write Protect 04000-05FFF (Enabled)
#pragma config WRT3 = ON        // Table Write Protect 06000-07FFF (Enabled)
// CONFIG6H
#pragma config WRTC = ON        // Config. Write Protect (Enabled)
#pragma config WRTB = ON        // Table Write Protect Boot (Enabled)
#pragma config WRTD = ON        // Data EE Write Protect (Enabled)
// CONFIG7L
#pragma config EBTR0 = ON       // Table Read Protect 00800-01FFF (Enabled)
#pragma config EBTR1 = ON       // Table Read Protect 02000-03FFF (Enabled)
#pragma config EBTR2 = ON       // Table Read Protect 04000-05FFF (Enabled)
#pragma config EBTR3 = ON       // Table Read Protect 06000-07FFF (Enabled)
// CONFIG7H
#pragma config EBTRB = ON       // Table Read Protect Boot (Enabled)
// #pragma config statements should precede project file includes.
Your finite state machine seems incomplete. Where are the states being set in the program? Generally the transition is driven by some input. In your case, the program just seems to initialize the state to ON and there is no way for it to go into the OFF or BLINKING states. There should be a mapping from a one state to another given a particular input.

Here is a C++ program I wrote some years back which demonstrates the basic functioning of standard FSM using a high-level interface:

Code:
#include <map>

template <typename State, typename Transition = State>
class finite_state_machine {
 protected:

  State current;
  std::map<State, std::map<Transition, State>> database;

 public:

  finite_state_machine() { set(State()); }

  void set(State const& state) { current = state; }

  State get() const { return current; }

  void clear() { database.clear(); }

  void add(State const& state,
           Transition const& transition,
           State const& next) {
    database[state][transition] = next;
  }

  /*
          Add a state which is also it's own transition (and thus a link in a
     chain of sequences)
  */
  void add(State const& state_and_transition, State const& next) {
    add(state_and_transition, state_and_transition, next);
  }

  bool process(Transition const& transition) {
    auto const &transitions = database[current],
               found = transitions.find(transition);
    if (found == transitions.end())
      return false;
    auto const& next = found->second;
    set(next);
    return true;
  }

  /*
          Process so-called "automatic transitions" (ie: sequencing)
  */
  bool process() { return process(get()); }

  /*
          A set of utility functions that may be helpful for displaying valid
     transitions to the user, etc...
  */
  template <typename PushBackContainer>
  bool get_valid_transitions(State const& state, PushBackContainer& container) {
    container.clear();
    auto const& found = database.find(state);
    if (found == database.end())
      return false;
    auto const& transitions = found->second;
    if (transitions.size() == 0)
      return false;
    for (auto const& iterator : transitions) {
      auto const& transition = iterator.first;
      container.push_back(transition);
    }
    return true;
  }

  template <typename Container>
  bool get_valid_transitions(Container& container) {
    return get_valid_transitions(get(), container);
  }
};

/*
        Example usage: a simple vending machine
*/

#include <iostream>
#include <string>
#include <vector>

using namespace std;

void print(string const& message) {
  cout << message << endl;
}

int main() {
  finite_state_machine<string> machine;
  machine.add("ready", "quit", "exit");
  machine.add("ready", "deposit", "waiting");
  machine.add("waiting", "select", "dispense");
  machine.add("waiting", "refund", "refunding");
  machine.add("dispense", "remove", "ready");
  machine.add("refunding", "ready");
  machine.set("ready");
  for (;;) {
    string state = machine.get();
    if (state == "ready")
      print("Please deposit coins.");
    else if (state == "waiting")
      print("Please select a product.");
    else if (state == "dispense")
      print("Dispensed...please remove product from tray.");
    else if (state == "refunding")
      print("Refunding money...");
    else if (state == "exit")
      break;
    else
      print("Internal error: unaccounted state '" + state + "'!");
    /*
            Handle "automatic" transitions
    */
    if (machine.process())
      continue;
    vector<string> transitions;
    machine.get_valid_transitions(transitions);
    string options;
    for (auto const& transition : transitions) {
      if (!options.empty())
        options += ", ";
      options += transition;
    }
    print("[" + state + "] Input the next transition (" + options + "): ");
    string transition;
    cout << " > ";
    cin >> transition;
    if (!machine.process(transition))
      print("Error: invalid transition!");
  }
}
And yes, I do understand you are currently using C, but hopefully it will still be clear enough to help you understand the main idea behind FSM's.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
431
Your finite state machine seems incomplete. Where are the states being set in the program? Generally the transition is driven by some input. In your case, the program just seems to initialize the state to ON and there is no way for it to go into the OFF or BLINKING states. There should be a mapping from a one state to another given a particular input.
After considering the suggestions, I've decided to use a timer to automatically transition states based on time intervals. This approach will allow me to drive the transitions without the need for direct user input. Specifically, I'm planning to configure a timer to generate interrupts at predefined intervals. These interrupts will serve as the triggers for transitioning between different states.

For instance, I'm aiming to have the LEDs ON for a certain duration, then OFF for another duration, and finally enter a blinking state. By adjusting the timer settings, I'll be able to achieve the desired time intervals for each state transition.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
431
Meanwhile I'm particularly interested in understanding how to use function pointers in the context of a state machine. Any insights or examples on this topic would be greatly appreciated!

Thanks in advance!
 

BobTPH

Joined Jun 5, 2013
8,671
I think you are completely misunderstanding the point of a state machine. The power comes from the ability of each state to:

1 perform an action
2 make a decision often based on input
3 choose the next state

You seem to be doing only 1.
 
Last edited:

geekoftheweek

Joined Oct 6, 2013
1,183
Meanwhile I'm particularly interested in understanding how to use function pointers in the context of a state machine. Any insights or examples on this topic would be greatly appreciated!

Thanks in advance!
I am not sure how function pointers would be beneficial in a state machine, but this is what I imagine:

Code:
// pointer declarations
int (*current_state_function)();
int (*current_state_check_for_change)();

current_state_function = &default_state_function;
current_state_check_for_change = &default_state_check_for_change;

loop() {
  while(1) {
    // perform current state operations
    current_state_function();
    current_state_check_for_change();
  }
}

int default_state_function() {
  // perform IO functions
}

int default_state_check_for_change() {
  // check if to change state
  if (some matching input) {
    current_state_function = &new_state_function;
    current_state_check_for_change = &new_state_check_for_change;
  }
}
You could obviously have your checks for new state in the current state function... I just wrote it out that way.

Personally instead of pointers I would do...

Code:
// MPLAB XC8 PIC19F45K80
#include <xc.h>
#include <stdio.h>
#include "config.h"

// Define LED states using an enum
enum LEDState {
    OFF,
    ON,
    BLINKING
};

void main(void) {
    TRISB = 0x00;  // Configure all pins of PORTB as outputs
    LATB = 0x00;   // Initialize PORTB to low

    while (1) {
        switch (state) {
            case OFF:
                function_and_checks_for_LED_OFF();
                break;
            case ON:
                function_and_checks_for_LED_ON();
                break;
            case BLINKING:
                function_and_checks_for_LED_BLINKING();
                break;
        }
    }

    return;
}
function_and_checks_for_LED_????? would then perform necessary IO for each state and perform checks to determine if the state needs to change for the next time through the loop
 

BobTPH

Joined Jun 5, 2013
8,671
Forget function pointers, that is an implementation detail. First learn what a state machine is used for and how. Your switch case method is fine for that.
 

nsaspook

Joined Aug 27, 2009
12,821
Meanwhile I'm particularly interested in understanding how to use function pointers in the context of a state machine. Any insights or examples on this topic would be greatly appreciated!

Thanks in advance!
Your're likely getting ahead of yourself but here it is:

This is one way function pointers in a switch based state machine can be configured.

A common use is a communications message processor where you can have several possible errors, waits, timeouts or state event transactions per data transfer. It's a useful abstraction to code a clean, readable section of C code for a complex state machine in the switch.
C:
        switch (state) {
        case state_init:
            send_mx_cmd(cmd_id);
            rec_mx_cmd(state_init_cb, REC_LEN);
            break;
        case state_status:
            send_mx_cmd(cmd_status);
            rec_mx_cmd(state_status_cb, REC_LEN);
            break;
        case state_panel:
            send_mx_cmd(cmd_panelv);
            rec_mx_cmd(state_panelv_cb, REC_LEN);
            break;
        case state_batteryv:
            send_mx_cmd(cmd_batteryv);
            rec_mx_cmd(state_batteryv_cb, REC_LEN);
            break;
        case state_batterya:
            send_mx_cmd(cmd_batterya);
            rec_mx_cmd(state_batterya_cb, REC_LEN);
            break;
        case state_watts:
            send_mx_cmd(cmd_watts);
            rec_mx_cmd(state_watts_cb, REC_LEN);
            break;
        case state_mx_status: // wait for ten second flag in this state for logging
            send_mx_cmd(cmd_mx_status);
            rec_mx_cmd(state_mx_status_cb, REC_STATUS_LEN);
            break;
        case state_misc:
            send_mx_cmd(cmd_misc);
            rec_mx_cmd(state_misc_cb, REC_LEN);
            break;
        default:
            send_mx_cmd(cmd_id);
            rec_mx_cmd(state_init_cb, REC_LEN);
            break;
        }
//
void state_init_cb(void)
{
    float Soc;

    if (FMxx_ID == FM80_ID) {
        printf("\r\n\r\n%5d %3x %3x %3x %3x %3x   INIT: FM80 Online\r\n", B.rx_count++, abuf[0], abuf[1], abuf[2], abuf[3], abuf[4]);
        if (!B.FM80_online) { // try to guess battery energy by looking at battery voltage
            Soc = ((float) Volts_to_SOC(vw, vf) * 0.01f);
            EBD.bat_energy = BAT_ENERGY*Soc;
        }
        B.FM80_online = true;
        snprintf(buffer, MAX_B_BUF, "FM80 Online         ");
        eaDogM_WriteStringAtPos(3, 0, buffer);
    } else {
        snprintf(buffer, MAX_B_BUF, "FM80 Offline        ");
        eaDogM_WriteStringAtPos(3, 0, buffer);
        B.FM80_online = false;
        cc_mode = STATUS_LAST;
    }
    state = state_status;
}

void state_status_cb(void)
{
#ifdef debug_data
    printf("%5d: %3x %3x %3x %3x %3x STATUS: FM80 %s mode\r\n", rx_count++, abuf[0], abuf[1], abuf[2], abuf[3], abuf[4], state_name[abuf[2]]);
#endif
    if (FMxx_STATE != STATUS_SLEEPING) {
        state = state_watts;
    } else {
        state = state_watts;
    }
    cc_mode = abuf[2];
}
The switch 'goto' variable state (state_init value is assumed here) is the command sequencer that's updated in the 'rec_mx_cmd' function that is passed a function pointer 'state_init_cb' to a program to process the received data. The code in 'state_init_cb' does the required processing for the function and setts the next command sequence to be processed "state_status". The use of function pointers allows one function to have many processing functions in the same word-space in C using Object Oriented Programming polymorphism. OOP with function pointers is a design method (implementation detail :)).

https://www.state-machine.com/doc/AN_Simple_OOP_in_C.pdf

C:
            /*
             * check received response data for size and format for each command sent
             */
            switch (client->modbus_command) {
            case G_LIGHT: // check for controller back-light codes
                modbus_write_check(client, &client->light_ok, sizeof(em_light));
                break;
            case G_PASSWD: // check for controller password codes
                modbus_write_check(client, &client->passwd_ok, sizeof(em_passwd));
                break;
            case G_CONFIG: // check for controller configuration codes
                modbus_write_check(client, &client->config_ok, sizeof(em_config));
                break;
            case G_DATA1: // check for controller data1 codes
                modbus_read_check(client, &client->data_ok, sizeof(em_data1), em_data_handler); <<-- function passed
                break;
            case G_DATA2: // check for controller data2 codes
                modbus_read_check(client, &client->data_ok, sizeof(em_data2), emt_data_handler); <<-- function passed
                break;
            case G_ID: // check for client module type
            default:
                modbus_read_id_check(client, &client->id_ok, sizeof(em_id));
                break;
            }

//
bool modbus_read_check(C_data * client, bool* cstate, uint16_t rec_length, void (* DataHandler)(void))
{
// code fragment
            /*
             * move from receive buffer to data structure and munge the data into the correct local 32-bit format from MODBUS client
             */
            DataHandler();
}

void em_data_handler(void)
{
    /*
     * move from receive buffer to data structure and munge the data into the correct local formats from MODBUS client
     */
    memcpy((void*) &em, (void*) &cc_buffer[3], sizeof(em));
    em.vl1l2 = mb32_swap(em.vl1l2);
    em.vl2l3 = mb32_swap(em.vl2l3);
    em.vl3l1 = mb32_swap(em.vl3l1);
// more code
}

void emt_data_handler(void)
{
    /*
     * move from receive buffer to data structure and munge the data into the correct local formats from MODBUS client
     */
    memcpy((void*) &emt, (void*) &cc_buffer[3], sizeof(emt));
    emt.hz = mb32_swap(emt.hz);
}
 
Last edited:

xox

Joined Sep 8, 2017
838
After considering the suggestions, I've decided to use a timer to automatically transition states based on time intervals. This approach will allow me to drive the transitions without the need for direct user input. Specifically, I'm planning to configure a timer to generate interrupts at predefined intervals. These interrupts will serve as the triggers for transitioning between different states.


For instance, I'm aiming to have the LEDs ON for a certain duration, then OFF for another duration, and finally enter a blinking state. By adjusting the timer settings, I'll be able to achieve the desired time intervals for each state transition.

Sounds like a good approach! Is this some kind of homework/lab project?

Meanwhile I'm particularly interested in understanding how to use function pointers in the context of a state machine. Any insights or examples on this topic would be greatly appreciated!
Function pointers are mostly useful inside of "lookup tables", but even then only when there are a large number of things to process. (As it basically gives you constant-time access, and thus in general faster than switch/case or if-statements).

That said, it can be incredibly nifty way simplify the logic of a program too. Here is a toy example which implements an FSM with the aid of function pointers in C.

Code:
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

typedef enum {
  turn_off,
  turn_on,
  blink_error,
  number_of_transitions,
  invalid = number_of_transitions
} transition;

typedef enum { off, on, blinking } state;

static state status = off;

#define REJECT(message)                      \
  do {                                       \
    fprintf(stderr, "error: %s\n", message); \
    return status;                           \
  } while (0)

#define REJECT_IF(that)                \
  if (status == that) {                \
    REJECT("current state is " #that); \
  }

#define REJECT_UNLESS(that)                 \
  if (status != that) {                     \
    REJECT("current state must be " #that); \
  }

#define SKIP(this)                                           \
  do {                                                       \
    if (status == this) {                                    \
      fprintf(stderr, "note: light is already %s\n", #this); \
      return status;                                         \
    }                                                        \
  } while (0)

int do_turn_off() {
  puts("turning light off...");
  SKIP(off);
  puts("light is off");
  return off;
}

int do_turn_on() {
  puts("attempting to turn on...");
  SKIP(on);
  REJECT_IF(blinking);
  puts("light is now on");
  return on;
}

int do_alert_error() {
  puts("triggering error conditon...");
  SKIP(blinking);
  REJECT_UNLESS(on);
  puts("blinking mode engaged");
  return blinking;
}

typedef int (*handler)();

static handler handlers[number_of_transitions] = {
  do_turn_off, 
  do_turn_on,
  do_alert_error
};

int input(void) {
  static int init = true;
  if (init) {
    init = false;
    srand(time(NULL));
  }
  return rand() % number_of_transitions;
}

int main(void) {
  for (;;) {
    int mapping = input();
    if (mapping >= number_of_transitions) {
      fprintf(stderr, "FATAL: invalid transition %d\n", mapping);
      exit(1);
    }
    status = handlers[mapping]();
  }
}
 

BobTPH

Joined Jun 5, 2013
8,671
Meanwhile I'm particularly interested in understanding how to use function pointers in the context of a state machine. Any insights or examples on this topic would be greatly appreciated!

Thanks in advance!
Which would be trivially implemented by straight line code or a series of function calls. There is no reason to use a state machine.
 

Thread Starter

Kittu20

Joined Oct 12, 2022
431
Sounds like a good approach! Is this some kind of homework/lab project?
I appreciate your curiosity, but this isn't a homework or lab project. I've been exploring function pointers in C language on my own. I've come across some discussions mentioning that using function pointers in a state machine can be a smart approach. I was curious to know benefits of using function pointers in state machine
 

geekoftheweek

Joined Oct 6, 2013
1,183
I was curious to know benefits of using function pointers in state machine
One thing to keep in mind is every pointer is going to use extra RAM to store it. In the case of a PIC18 it will take three bytes to store the address. It doesn't sound like much, and in all reality is not much, but depending on which micro and what you are doing that can be the difference between the program working or not. Using a 18F45K80 you will probably be able to get away more than a few before it becomes a problem, but with say a 16F1504 it won't take long before you have allocation errors.
 

BobTPH

Joined Jun 5, 2013
8,671
But it demonstrates none of the power and flexibility of a state machine, so what has he learned? That a state machine us just an obscure way if doing things that can be done in a simple and straightforward way?
 

nsaspook

Joined Aug 27, 2009
12,821
But it demonstrates none of the power and flexibility of a state machine, so what has he learned? That a state machine us just an obscure way if doing things that can be done in a simple and straightforward way?
He's learning to crawl (GROK the basic software template and develop a future mental model of how to handle software problems in a structured way) before running a 100M dash like most beginners, The OP curiosity is IMO very well directed.

There is absolutely nothing obscure IMO about state machines on 8-bit controllers running fairly deterministic, even simple software if you write lots of embedded code that needs to parse and execute commands that can interact with several devices. I use them (starting from a mental model of the functionally needed) for even simple interrupt driven blink LED routines all the time if some event driven variation is needed in the blinking.

State machines are a fundamental programming method/pattern that are a hell of lot more simple, straightforward and predictable to build (that can start trivial and grow) than programmatic logic (that can quickly turn to obscure pretzel logic) as event, data and interaction complexity increases. The sooner you can easily design them and use them, the better.

https://www.ni.com/en/support/docum...ple-state-machine-template-documentation.html
Why Use a State Machine?

State Machines are used in applications where distinguishable states exist. Each state can lead to one or multiple states and can also end the process flow. A State Machine relies on user input or in-state calculation to determine which state to go to next. Many applications require an “initialize” state, followed by a default state where many different actions can be performed. The actions performed can depend on previous and current inputs as well as states. A “shutdown” state can then be used to perform clean up actions.

Besides its powerful ability to implement decision-making algorithms, state machines are also functional forms of application planning. As the complexity of applications grow, so does the need for adequate design. State diagrams and flowcharts are useful and sometimes essential for the design process. Not only are State Machines advantageous in application planning, they are also easy to create.
1692566613618.png

https://barrgroup.com/embedded-systems/how-to/coding-state-machines
And here is where state machines come in. When used correctly, state machines become powerful "spaghetti reducers" that drastically reduce the number of execution paths through the code, simplify the conditions tested at each branching point, and simplify transitions between different modes of execution. All these benefits hinge on the concept of "state." As it turns out, the behavior of most reactive systems can be divided into a relatively small number of non-overlapping chunks (states), where event responses within each individual chunk depend only on the current event, but no longer on the sequence of past events. In this model, change of behavior (that is, change in response to any event) corresponds to change of state (state transition). Thus, the concept of state becomes a succinct representation of the relevant system history.
In small embedded controllers "The computer is state" at the bare-metal level so it's useful to design at that level too when interfacing directly to hardware.
 
Last edited:

xox

Joined Sep 8, 2017
838
But it demonstrates none of the power and flexibility of a state machine, so what has he learned? That a state machine us just an obscure way if doing things that can be done in a simple and straightforward way?

Except. Managing state is pretty fundamental in programming. We encounter it just about every time we start writing some code. In fact, so many programs fall into an invalid state (ever had to force-kill an app?) that we almost forget that 99% of those kinds of issues are preventable. And that's where FSM's come in. They provide a framework that allows one to turn a difficult problem into a much easier one. So in the long run at least, I would argue that they are a most definitely an important topic of study. Especially when it comes to greener developers. How much buggy spaghetti code would have been avoided had more programmers simply learned the basic concepts of state management?
 
Top