The FSM Library is a lightweight, extensible Finite State Machine (FSM) implementation in C, designed for embedded systems like AUTOSAR. It provides a macro-based interface for defining states, transitions, and event handlers, making it ideal for real-time applications with periodic task scheduling.
- Macro-Based Configuration: Use
DECLARE_SWC_FSM_CONTEXT
,DECLARE_SWC_FSM_STATE
, andDECLARE_SWC_FSM_TRANSITION
to define FSM contexts, states, and transitions declaratively. - State-Specific Event Handling: Each state handles its own entry, routine, and exit actions.
- Flexible Transitions: Supports deterministic transitions via a transition table (
fsmTransTable
) with optional common and state-specific checks. - Fast Lookup Option: Enable
FSM_STATE_FF
for O(1) state access using array indexing. - AUTOSAR Compatibility: No dynamic memory allocation, minimal dependencies (only
<stdint.h>
), and C++ compatibility withextern "C"
. - Single Header Design: All functionality in
SWC_Fsm.h
, inspired by single-header libraries likestb
andminiz
. - Optional Implementation: No need to define
FSM_IMPLEMENTATION
for header-only usage; defining it allows separating implementation into a single source file for better modularity.
- A C compiler (e.g.,
gcc
,clang
, IAR, or Keil for AUTOSAR). - Standard C library:
<stdint.h>
(no other dependencies required).
-
Clone the repository:
git clone https://github.com/TreeNeeBee/fsm.git cd fsm
-
Include
SWC_Fsm.h
in your project:- Copy
SWC_Fsm.h
to your project directory. - Header-Only Usage: Simply include
SWC_Fsm.h
without definingFSM_IMPLEMENTATION
. All functions are defined inline in the header, suitable for small projects or quick integration. - Separated Implementation: Define
FSM_IMPLEMENTATION
in one source file to include function implementations, reducing code duplication in larger projects:#define FSM_IMPLEMENTATION #include "SWC_Fsm.h"
- Copy
-
Do I Need
FSM_IMPLEMENTATION
?- No: If you include
SWC_Fsm.h
without definingFSM_IMPLEMENTATION
, all function implementations are included inline in the header. This is simpler for small projects or rapid prototyping, as it requires no additional setup. - Yes: Defining
FSM_IMPLEMENTATION
in one source file moves function implementations to that file, reducing binary size and compilation time in projects with multiple source files includingSWC_Fsm.h
.
- No: If you include
-
Advantages of Defining
FSM_IMPLEMENTATION
:- Reduced Binary Size: Prevents multiple inclusions of function implementations across translation units.
- Improved Compilation Speed: Compiles function implementations only once in the designated source file.
- Better Modularity: Separates interface (header) from implementation, making it easier to maintain large projects.
- Linker Optimization: Avoids duplicate symbol errors when linking multiple object files.
-
Steps to Use
FSM_IMPLEMENTATION
:- Create a source file (e.g.,
fsm_impl.c
) with:#define FSM_IMPLEMENTATION #include "SWC_Fsm.h"
- Compile the implementation file separately:
gcc -c fsm_impl.c -o fsm_impl.o
- Include
SWC_Fsm.h
in other source files without definingFSM_IMPLEMENTATION
. - Link all object files:
gcc -o my_app main.o fsm_impl.o
- Create a source file (e.g.,
To build and run the example (exampleFsm.c
):
cd examples
gcc -I.. -o fsmd exampleFsm.c
./fsmd
For fast state lookup (O(1)):
gcc -DFSM_STATE_FF -I.. -o fsmd exampleFsm.c
./fsmd
For separated implementation:
gcc -c fsm_impl.c -o fsm_impl.o
gcc -I.. -c exampleFsm.c -o exampleFsm.o
gcc -o fsmd exampleFsm.o fsm_impl.o
./fsmd
Define states and events in your application:
typedef enum {
FSM_STATE_STANDBY,
FSM_STATE_A,
FSM_STATE_B,
FSM_STATE_C,
FSM_STATE_COUNT
} fsm_state_t;
typedef enum {
FSM_EVENT_KEY_A,
FSM_EVENT_KEY_B,
FSM_EVENT_KEY_C,
FSM_EVENT_KEY_S,
FSM_EVENT_NONE
} FSM_Event;
The library provides comprehensive error handling with detailed error codes and optional callbacks:
typedef enum {
FSM_OK = 0, // 成功
FSM_ERR_NULL_CONTEXT = -1, // 空上下文
FSM_ERR_INVALID_STATE = -2, // 无效状态
FSM_ERR_NO_TRANSITION = -3, // 无转换路径
FSM_ERR_CHECK_FAILED = -4, // 转换检查失败
FSM_ERR_INIT_FAILED = -5, // 初始化失败
FSM_ERR_ROUTINE_FAILED = -6, // 周期性任务失败
FSM_ERR_STATE_ROUTINE_FAILED = -7, // 状态例程失败
FSM_ERR_TOO_MANY_STATES = -8, // 状态数超限
FSM_ERR_ENTRY_FAILED = -9, // 进入动作失败
FSM_ERR_EXIT_FAILED = -10, // 退出动作失败
FSM_ERR_UNKNOWN = -11 // 未知错误
} fsm_error_t;
typedef void (*ptrSWCFsmErrorHandler)(fsm_error_t error, fsm_state_t curState, fsm_state_t nextState, void *context);
void myErrorHandler(fsm_error_t error, fsm_state_t curState, fsm_state_t nextState, SWCFsmContext *context) {
const char *errorMsg;
switch (error) {
case FSM_ERR_NULL_CONTEXT: errorMsg = "Null context"; break;
case FSM_ERR_INVALID_STATE: errorMsg = "Invalid state"; break;
case FSM_ERR_NO_TRANSITION: errorMsg = "No transition path"; break;
case FSM_ERR_CHECK_FAILED: errorMsg = "Transition check failed"; break;
case FSM_ERR_INIT_FAILED: errorMsg = "Initialization failed"; break;
case FSM_ERR_ROUTINE_FAILED: errorMsg = "Routine failed"; break;
case FSM_ERR_TOO_MANY_STATES: errorMsg = "Too many states"; break;
case FSM_ERR_ENTRY_FAILED: errorMsg = "Entry action failed"; break;
case FSM_ERR_EXIT_FAILED: errorMsg = "Exit action failed"; break;
default: errorMsg = "Unknown error"; break;
}
printf("FSM Error: %s (code=%d, cur=%u, next=%u)\n", errorMsg, (int)error, (unsigned)curState, (unsigned)nextState);
}
Use the DECLARE_SWC_FSM_CONTEXT
macro to define the FSM context, including states, transitions, and callbacks:
// Common transition check
fsm_bool_t commonCheck(fsm_state_t from, fsm_state_t to, void *fsm) {
return DEF_FSM_TRUE;
}
// State-specific transition check
fsm_bool_t transCheckAtoB(fsm_state_t from, fsm_state_t to, void *fsm) {
return DEF_FSM_TRUE;
}
// State action callbacks
int32_t stateAEntry(void *fsm) { printf("Enter State A\n"); return 0; }
int32_t stateARoutine(void *fsm) { return 0; }
int32_t stateAExit(void *fsm) { printf("Exit State A\n"); return 0; }
#define FSM_STATE_LIST \
DECLARE_SWC_FSM_STATE(FSM_STATE_A, stateAEntry, stateARoutine, stateAExit, 1000), \
DECLARE_SWC_FSM_STATE(FSM_STATE_STANDBY, NULL, NULL, NULL, 0), \
DECLARE_SWC_FSM_STATE(FSM_STATE_B, NULL, NULL, NULL, 0), \
DECLARE_SWC_FSM_STATE(FSM_STATE_C, NULL, NULL, NULL, 0)
#define FSM_TRANS_TABLE \
DECLARE_SWC_FSM_TRANSITION(FSM_STATE_STANDBY, FSM_STATE_A, NULL), \
DECLARE_SWC_FSM_TRANSITION(FSM_STATE_A, FSM_STATE_B, transCheckAtoB), \
DECLARE_SWC_FSM_TRANSITION(FSM_STATE_A, FSM_STATE_C, NULL), \
DECLARE_SWC_FSM_TRANSITION(FSM_STATE_B, FSM_STATE_C, NULL), \
DECLARE_SWC_FSM_TRANSITION(FSM_STATE_C, FSM_STATE_STANDBY, NULL)
// FSM context
DECLARE_SWC_FSM_CONTEXT(MyFSM, 1, DECLARE_SWC_FSM_STATES(FSM_STATE_LIST), DECLARE_SWC_FSM_TRANSITIONS(FSM_TRANS_TABLE), commonCheck, FSM_STATE_STANDBY, 1000, NULL, NULL, NULL)
Below is an example (exampleFsm.c
) demonstrating FSM initialization and event-driven transitions:
#include "SWC_Fsm.h"
#include <stdio.h>
// Common transition check
fsm_bool_t commonCheck(fsm_state_t from, fsm_state_t to, void *fsm) {
return DEF_FSM_TRUE;
}
// State-specific transition check
fsm_bool_t transCheckAtoB(fsm_state_t from, fsm_state_t to, void *fsm) {
return DEF_FSM_TRUE;
}
// State action callbacks
fsm_error_t stateAEntry(void *fsm) { printf("Enter State A\n"); return FSM_OK; }
fsm_error_t stateARoutine(void *fsm) { return FSM_OK; }
fsm_error_t stateAExit(void *fsm) { printf("Exit State A\n"); return FSM_OK; }
// Define states and transitions
#define FSM_STATE_LIST \
DECLARE_SWC_FSM_STATE(FSM_STATE_A, stateAEntry, stateARoutine, stateAExit, 1000), \
DECLARE_SWC_FSM_STATE(FSM_STATE_STANDBY, NULL, NULL, NULL, 0), \
DECLARE_SWC_FSM_STATE(FSM_STATE_B, NULL, NULL, NULL, 0), \
DECLARE_SWC_FSM_STATE(FSM_STATE_C, NULL, NULL, NULL, 0)
#define FSM_TRANS_TABLE \
DECLARE_SWC_FSM_TRANSITION(FSM_STATE_STANDBY, FSM_STATE_A, NULL), \
DECLARE_SWC_FSM_TRANSITION(FSM_STATE_A, FSM_STATE_B, transCheckAtoB), \
DECLARE_SWC_FSM_TRANSITION(FSM_STATE_A, FSM_STATE_C, NULL), \
DECLARE_SWC_FSM_TRANSITION(FSM_STATE_B, FSM_STATE_C, NULL), \
DECLARE_SWC_FSM_TRANSITION(FSM_STATE_C, FSM_STATE_STANDBY, NULL)
// Declare FSM context
DECLARE_SWC_FSM_CONTEXT(MyFSM, 1, DECLARE_SWC_FSM_STATES(FSM_STATE_LIST), DECLARE_SWC_FSM_TRANSITIONS(FSM_TRANS_TABLE), commonCheck, NULL, FSM_STATE_STANDBY, 1000, NULL, NULL, NULL)
int main(void) {
SWCFsmContext *context = DECLARE_SWC_FSM_CONTEXT_REF(MyFSM);
if (!swcFsmInit(context)) {
printf("FSM 初始化失败\n");
return 1;
}
printf("Transition to A\n");
swcFsmTransTo(FSM_STATE_A, DEF_FSM_FALSE, context);
printf("Transition to B\n");
swcFsmTransTo(FSM_STATE_B, DEF_FSM_FALSE, context);
printf("Transition to C\n");
swcFsmTransTo(FSM_STATE_C, DEF_FSM_FALSE, context);
printf("Transition to Standby\n");
swcFsmTransTo(FSM_STATE_STANDBY, DEF_FSM_FALSE, context);
swcFsmExit(context);
return 0;
}
The example implements the following transitions:
- Standby → A: Triggered by
swcFsmTransTo(FSM_STATE_A, ...)
. - A → B: Triggered by
swcFsmTransTo(FSM_STATE_B, ...)
withtransCheckAtoB
. - A → C: Triggered by
swcFsmTransTo(FSM_STATE_C, ...)
. - B → C: Triggered by
swcFsmTransTo(FSM_STATE_C, ...)
. - C → Standby: Triggered by
swcFsmTransTo(FSM_STATE_STANDBY, ...)
.
Each transition can include a common check (fsmCommonCheck
) and state-specific checks (transCheck
).
- No Dynamic Memory: Uses static state and transition tables.
- Minimal Dependencies: Only requires
<stdint.h>
; definesNULL
andfsm_bool_t
internally. - C++ Support: Wrapped in
extern "C"
for C++ compatibility. - Configurable: Supports
FSM_STATE_FF
for O(1) lookup and custom intervals for periodic tasks.
No, you can include SWC_Fsm.h
without defining FSM_IMPLEMENTATION
for header-only usage, where all functions are inline. Define FSM_IMPLEMENTATION
in one source file for larger projects to reduce binary size and improve modularity.
Define FSM_STATE_FF
during compilation for O(1) state access. Ensure fsmStateList
is an array indexed by fsm_state_t
:
static SWCFsmStateItem stateList[FSM_STATE_COUNT] = { ... };
Yes, the library is designed for AUTOSAR:
- No dynamic allocation.
- Minimal dependencies.
- Supports custom state and transition definitions via macros.
Use DECLARE_SWC_FSM_STATE
and DECLARE_SWC_FSM_TRANSITION
in the FSM_STATE_LIST
and FSM_TRANS_TABLE
macros, respectively.
Contributions are welcome! Please:
- Fork the repository.
- Create a feature branch (
git checkout -b feature/your-feature
). - Commit changes (
git commit -m 'Add your feature'
). - Push to the branch (
git push origin feature/your-feature
). - Open a pull request.
This project is licensed under the MIT License. See the LICENSE file for details.