Skip to content

Instantly share code, notes, and snippets.

@francisrstokes
Created July 31, 2023 17:56
Show Gist options
  • Save francisrstokes/53525e83954b1561ee000d2cf8a2c8be to your computer and use it in GitHub Desktop.
Save francisrstokes/53525e83954b1561ee000d2cf8a2c8be to your computer and use it in GitHub Desktop.
Simple State Machine Base
#include "machine.h"
void state_machine_step(state_machine_t* descriptor) {
state_transition_t* transition;
uint32_t current_state = *descriptor->state_index;
for (uint32_t i = 0; i < descriptor->num_transitions; i++) {
transition = &descriptor->transitions[i];
if ((transition->from_state == current_state) && (transition->condition(current_state))) {
descriptor->on_state_change(current_state, transition->to_state);
*descriptor->state_index = transition->to_state;
return;
}
}
}
#ifndef INC_MACHINE_H
#define INC_MACHINE_H
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
typedef void (*state_change_fn)(uint32_t, uint32_t);
typedef bool (*state_condition_fn)(uint32_t);
typedef struct state_transition_t {
const uint32_t from_state;
const uint32_t to_state;
const state_condition_fn condition;
} state_transition_t;
typedef struct state_machine_t {
state_transition_t* transitions;
uint32_t num_transitions;
state_change_fn on_state_change;
uint32_t* state_index;
} state_machine_t;
void state_machine_step(state_machine_t* descriptor);
#endif // INC_MACHINE_H
#include <stdio.h>
#include "machine.h"
#define HIGH (1)
#define LOW (0)
// Imaginary API for bit-banging uart
uint8_t prev_level = HIGH;
void drive_tx(uint8_t level) {
if (level == LOW && prev_level == HIGH) {
printf("\\___");
} else if (level == HIGH && prev_level == LOW) {
printf("/¯¯¯");
} else if (level == LOW) {
printf("____");
} else {
printf("¯¯¯¯");
}
prev_level = level;
}
typedef struct timer_t {
uint32_t current;
uint32_t max;
} timer_t;
typedef enum uart_state_t {
State_Idle,
State_StartBit,
State_Bit0,
State_Bit1,
State_Bit2,
State_Bit3,
State_Bit4,
State_Bit5,
State_Bit6,
State_Bit7,
State_StopBit,
State_Max,
} uart_state_t;
static uart_state_t state = State_Idle;
static uint8_t byte_to_send = 0;
static bool byte_valid = false;
static timer_t bit_timer = { .current = 0, .max = 10 }; // Imaginary units
static bool uart_send_byte(uint8_t byte) {
// Public function used to transmit a byte. Returns false when "buffer" is full
if (state != State_Idle) {
return false;
}
byte_to_send = byte;
byte_valid = true;
return true;
}
// Conditions
static bool byte_available(uint32_t state) {
return byte_valid;
}
static bool bit_time_elapsed(uint32_t state) {
return bit_timer.current >= bit_timer.max;
}
// Entrys
static void on_idle_entry(uint32_t from_state, uint32_t to_state) {
drive_tx(HIGH);
}
static void on_start_entry(uint32_t from_state, uint32_t to_state) {
drive_tx(LOW);
}
static void on_stop_entry(uint32_t from_state, uint32_t to_state) {
drive_tx(HIGH);
}
static void on_bit_entry(uint32_t from_state, uint32_t to_state) {
uint8_t bit_index = to_state - 2;
drive_tx((byte_to_send >> bit_index) & 1);
}
static const state_change_fn entries[State_Max] = {
on_idle_entry, // State_Idle,
on_start_entry, // State_StartBit,
on_bit_entry, // State_Bit0,
on_bit_entry, // State_Bit1,
on_bit_entry, // State_Bit2,
on_bit_entry, // State_Bit3,
on_bit_entry, // State_Bit4,
on_bit_entry, // State_Bit5,
on_bit_entry, // State_Bit6,
on_bit_entry, // State_Bit7,
on_stop_entry, // State_StopBit,
};
// Exits
static void on_idle_exit(uint32_t from_state, uint32_t to_state) {
byte_valid = false;
}
static void on_state_exit(uint32_t from_state, uint32_t to_state) {
bit_timer.current = 0;
}
static const state_change_fn exits[State_Max] = {
on_idle_exit, // State_Idle,
on_state_exit, // State_StartBit,
on_state_exit, // State_Bit0,
on_state_exit, // State_Bit1,
on_state_exit, // State_Bit2,
on_state_exit, // State_Bit3,
on_state_exit, // State_Bit4,
on_state_exit, // State_Bit5,
on_state_exit, // State_Bit6,
on_state_exit, // State_Bit7,
on_state_exit, // State_StopBit,
};
static state_transition_t transitions[] = {
{ .from_state = State_Idle, .to_state = State_StartBit, .condition = byte_available },
{ .from_state = State_StartBit, .to_state = State_Bit0, .condition = bit_time_elapsed },
{ .from_state = State_Bit0, .to_state = State_Bit1, .condition = bit_time_elapsed },
{ .from_state = State_Bit1, .to_state = State_Bit2, .condition = bit_time_elapsed },
{ .from_state = State_Bit2, .to_state = State_Bit3, .condition = bit_time_elapsed },
{ .from_state = State_Bit3, .to_state = State_Bit4, .condition = bit_time_elapsed },
{ .from_state = State_Bit4, .to_state = State_Bit5, .condition = bit_time_elapsed },
{ .from_state = State_Bit5, .to_state = State_Bit6, .condition = bit_time_elapsed },
{ .from_state = State_Bit6, .to_state = State_Bit7, .condition = bit_time_elapsed },
{ .from_state = State_Bit7, .to_state = State_StopBit, .condition = bit_time_elapsed },
{ .from_state = State_StopBit, .to_state = State_Idle, .condition = bit_time_elapsed },
};
static void on_state_change(uint32_t from_state, uint32_t to_state) {
if (exits[from_state]) {
exits[from_state](from_state, to_state);
}
// If we had transition specific logic, we could do it here
if (entries[to_state]) {
entries[to_state](from_state, to_state);
}
}
static state_machine_t uart_state_machine = {
.num_transitions = 11,
.transitions = transitions,
.on_state_change = on_state_change,
.state_index = &state
};
int main() {
uart_send_byte(0x4A); // 01001010
on_idle_entry(0, 0);
for (uint32_t i = 0; i < 200; i++) {
state_machine_step(&uart_state_machine);
bit_timer.current++;
uart_send_byte(0xe7); // 11100111
}
printf("\n");
return 0;
}
// Produces:
// ¯¯¯¯\_______/¯¯¯\___/¯¯¯\_______/¯¯¯\___/¯¯¯¯¯¯¯\___/¯¯¯¯¯¯¯¯¯¯¯\_______/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// I s 0 1 0 1 0 0 1 0 S I 0 1 1 1 0 0 1 1 S I
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment