Created
July 31, 2023 17:56
-
-
Save francisrstokes/53525e83954b1561ee000d2cf8a2c8be to your computer and use it in GitHub Desktop.
Simple State Machine Base
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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