// Example statemachine implementation and usage. // /////////////////////////////////////////////////////////////////////////////// // INCLUDES /////////////////////////////////////////////////////////////////////////////// #include #include #include /////////////////////////////////////////////////////////////////////////////// // LOCAL MACROS /////////////////////////////////////////////////////////////////////////////// #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) #define DEFAULT_ON_LEVEL 128u #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) #define CLIP(value, min, max) MIN(MAX((value), (min)), (max)) #define INVALID 0xFFu #define STATE( s ) { INVALID, INVALID, INVALID, s } #define NONE INVALID #define ELSE INVALID /////////////////////////////////////////////////////////////////////////////// // LOCAL TYPES /////////////////////////////////////////////////////////////////////////////// typedef uint8_t trigger_t; typedef uint8_t state_t; typedef bool (*guardHandler_t)(uint8_t guard); typedef void (*effectHandler_t)(uint8_t effect); typedef struct statemachineRule_t { uint8_t trigger; uint8_t guard; uint8_t effect; uint8_t state; } statemachineRule_t; typedef struct statemachine_t { guardHandler_t guardHandler; effectHandler_t effectHandler; const statemachineRule_t *pRules; const uint8_t nrOfRules; uint8_t currentState; } statemachine_t; enum states { sOff , sOn , sDimming }; enum { tPressOn , tPressOff , tPressUp , tPressDown , tReleaseUp , tReleaseDown }; enum { gLevelIsZero }; enum { eSetDefaultOnLevel, eTurnOff , eDimUp , eDimDown , eStopDimming }; /////////////////////////////////////////////////////////////////////////////// // LOCAL FUNCTION PROTOTYPES /////////////////////////////////////////////////////////////////////////////// // guards static bool levelIsZero(void); // effects static void setDefaultOnLevel(void); static void turnOff(void); static void dimUp(void); static void dimDown(void); static void stopDimming(void); static void statemachine_ApplyTrigger( statemachine_t *pstatemachine , const trigger_t trigger ); static void applyTrigger(const trigger_t trigger); /////////////////////////////////////////////////////////////////////////////// // LOCAL CONSTANTS AND VARIABLES /////////////////////////////////////////////////////////////////////////////// static uint8_t s_dimLevel = DEFAULT_ON_LEVEL; static int8_t s_dimDelta = 0u; /////////////////////////////////////////////////////////////////////////////// // LOCAL FUNCTIONS /////////////////////////////////////////////////////////////////////////////// static bool guardHandler(uint8_t guard) { switch( guard ) { case gLevelIsZero : return levelIsZero(); default : return true; } } static void effectHandler(uint8_t effect) { switch( effect ) { case eSetDefaultOnLevel : return setDefaultOnLevel(); break; case eTurnOff : return turnOff() ; break; case eDimUp : return dimUp() ; break; case eDimDown : return dimDown() ; break; case eStopDimming : return stopDimming() ; break; default : break; } } // guards static bool levelIsZero(void) { return (s_dimLevel == 0u); } // effects static void setDefaultOnLevel(void) { s_dimLevel = DEFAULT_ON_LEVEL; printf("Set dimlevel to default on level.\n"); } static void turnOff(void) { s_dimLevel = 0u; printf("Turn lights off...\n"); } static void dimUp(void) { s_dimDelta = 5; printf("Start dimming up.\n"); } static void dimDown(void) { s_dimDelta = -3; printf("Start dimming down.\n"); } static void stopDimming(void) { s_dimDelta = 0; printf("Stop dimming.\n"); } static void statemachine_ApplyTrigger( statemachine_t *pStatemachine, trigger_t trigger ) { uint8_t internalState = INVALID; for ( size_t i = 0u; i < pStatemachine->nrOfRules; ++i ) { statemachineRule_t rule = pStatemachine->pRules[i]; if ( rule.trigger == INVALID ) { internalState = rule.state; continue; } if ( (internalState == pStatemachine->currentState) && (trigger == rule.trigger) && ( (pStatemachine->guardHandler == NULL) || pStatemachine->guardHandler(rule.guard)) ) { if ( pStatemachine->effectHandler ) { pStatemachine->effectHandler( rule.effect ); } pStatemachine->currentState = rule.state; break; } } } static void applyTrigger(const trigger_t trigger) { // states enum { sOff , sOn , sDimming }; static const statemachineRule_t rules[] = { // trigger , guard , effect , next state STATE( sOff ), { tPressOn , NONE , eSetDefaultOnLevel, sOn }, STATE( sOn ), { tPressOff , NONE , eTurnOff , sOff }, { tPressUp , NONE , eDimUp , sDimming }, { tPressDown , NONE , eDimDown , sDimming }, STATE( sDimming ), { tPressOff , NONE , eTurnOff , sOff }, { tReleaseUp , NONE , eStopDimming , sOn }, { tReleaseDown, gLevelIsZero, eTurnOff , sOff }, { tReleaseDown, ELSE , eStopDimming , sOn }, }; static statemachine_t statemachine = { guardHandler , effectHandler , rules , ARRAY_SIZE(rules), sOff }; statemachine_ApplyTrigger(&statemachine, trigger); } /////////////////////////////////////////////////////////////////////////////// // EXPORTED CONSTANTS AND VARIABLES /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // EXPORTED FUNCTIONS /////////////////////////////////////////////////////////////////////////////// int main(void) { printf( "usage :\n" " 1) Press On\n" " 0) Press Off\n" " +) Press/Release Up (toggle)\n" " -) Press/Release Down (toggle)\n" "\n" " x) exit\n"); bool upState = false; bool downState = false; for(;;) { int c = getchar(); if ( c < 0 ) break; if ( c == '\n' ) continue; switch( c ) { case '1' : applyTrigger(tPressOn) ; break; case '0' : applyTrigger(tPressOff); break; case '+' : applyTrigger( upState ? tReleaseUp : tPressUp ); upState = !upState; break; case '-' : applyTrigger( downState ? tReleaseDown : tPressDown ); downState = !downState; break; case 'x' : return 0; default : break; } // simulate dimming s_dimLevel = (uint8_t)CLIP((int)s_dimLevel + (int)s_dimDelta, 0, 255); printf("current dim level is %u\n", s_dimLevel); } return 0; }