Figure 1: The State Tree of PowerUpDown Application in Running Mode
Introduction
This article discovers how to develop and simulate cross-platform embedded systems using UML State Machine Wizard.
Background
Embedded systems are some special purpose computers that are used inside of devices. Embedded systems generally use micro controllers that contain many functions of a computer on a single device.
Embedded systems have to tightly work together with special hardware. They have to respond to external interactions in a predetermined amount of time. Usually embedded systems are real time.
Many embedded system applications are natural candidates for being organized as a state machine. A program that must sequence a series of actions, or handle inputs differently depending on what state it's in, is often best implemented as a state machine.
Thus most embedded systems support state machine based design where multiple events can be received in a single state. The next state is determined by the contents of the received event. State machines provide a very flexible mechanism to handle asynchronous event interactions. The flexibility comes with its own complexities.
Visual C++ is a powerful software developing tool. However it aims at developing Windows-specific applications. UML State Machine Wizard acts as a Visual C++ add-in, which provides a UML (Unified Modeling Language) state machine programming mechanism in portable standard C for embedded systems developing and simulating in Visual C++ developer studio. After simulating and debugging, developer can move the program to a destination working environment with little or no extra investment or effort.
Using the code
There are a number of ways to show state machines, from simple tables through graphically animated illustrations. State Machine Wizard shows and organizes state machines in two ways: state tree and state chart. State Machine Wizard defines a set of specific macros as state machine mapping data. These macros map to state enumerations, event handler function declarations, event handler table for a state, state tree definition and application variable definition. In the following source file PowerUpDown.c, State Machine Wizard generates the following macros to define the PowerUpDown application:
SME_BEGIN_STATE_DEF(PowerUpDown,PowerUpDown)
SME_STATE_ENTRY_FUNC(PowerUpDownEntry)
SME_STATE_EXIT_FUNC(PowerUpDownExit)
SME_ON_EVENT(EVENT_KEY_LEFTSOFT,
OnPowerUpDownEVENT_KEY_LEFTSOFT,SME_INTERNAL_TRAN)
SME_ON_EVENT(EVENT_KEY_RIGHTSOFT,
OnPowerUpDownEVENT_KEY_RIGHTSOFT,SME_INTERNAL_TRAN)
SME_END_STATE_DEF
SME_BEGIN_STATE_DEF(PowerUpDown,PowerDown)
SME_STATE_ENTRY_FUNC(PowerDownEntry)
SME_STATE_EXIT_FUNC(PowerDownExit)
SME_ON_EVENT(EVENT_POWER_UP,NULL,PowerUp)
SME_END_STATE_DEF
SME_BEGIN_STATE_DEF(PowerUpDown,PowerUp)
SME_STATE_ENTRY_FUNC(PowerUpEntry)
SME_STATE_EXIT_FUNC(PowerUpExit)
SME_ON_EVENT(EVENT_KEY_POWER,NULL,PowerDown)
SME_END_STATE_DEF
SME_BEGIN_STATE_DEF(PowerUpDown,Idle)
SME_STATE_ENTRY_FUNC(IdleEntry)
SME_STATE_EXIT_FUNC(IdleExit)
SME_ON_EVENT(EVENT_OPEN_MENU_MAIN,OnIdleEVENT_OPEN_MENU_MAIN,ImageSurf)
SME_ON_EVENT(EVENT_OPEN_MENU_LANG,OnIdleEVENT_OPEN_MENU_LANG,MenuSurf)
SME_ON_EVENT(EVENT_KEY_0,OnIdleEVENT_KEY_0,DigitInput)
SME_ON_EVENT(EVENT_KEY_1,OnIdleEVENT_KEY_1,DigitInput)
SME_ON_EVENT(EVENT_KEY_2,OnIdleEVENT_KEY_2,DigitInput)
SME_ON_EVENT(EVENT_KEY_3,OnIdleEVENT_KEY_3,DigitInput)
SME_ON_EVENT(EVENT_KEY_4,OnIdleEVENT_KEY_4,DigitInput)
SME_ON_EVENT(EVENT_KEY_5,OnIdleEVENT_KEY_5,DigitInput)
SME_ON_EVENT(EVENT_KEY_6,OnIdleEVENT_KEY_6,DigitInput)
SME_ON_EVENT(EVENT_KEY_7,OnIdleEVENT_KEY_7,DigitInput)
SME_ON_EVENT(EVENT_KEY_8,OnIdleEVENT_KEY_8,DigitInput)
SME_ON_EVENT(EVENT_KEY_9,OnIdleEVENT_KEY_9,DigitInput)
SME_ON_EVENT(EVENT_ON_TIMER,OnIdleEVENT_ON_TIMER,SME_INTERNAL_TRAN)
SME_END_STATE_DEF
SME_BEGIN_STATE_DEF(PowerUpDown,MenuSurf)
SME_STATE_ENTRY_FUNC(MenuSurfEntry)
SME_STATE_EXIT_FUNC(MenuSurfExit)
SME_ON_EVENT(EVENT_MENU_EXIT,NULL,Idle)
SME_ON_EVENT(EVENT_MENU_SEL,OnMenuSurfEVENT_MENU_SEL,Function)
SME_END_STATE_DEF
SME_BEGIN_STATE_DEF(PowerUpDown,Function)
SME_STATE_ENTRY_FUNC(FunctionEntry)
SME_STATE_EXIT_FUNC(FunctionExit)
SME_ON_EVENT(EVENT_DLG_OK,OnFunctionEVENT_DLG_OK,Idle)
SME_ON_EVENT(EVENT_DLG_CANCEL,NULL,Idle)
SME_END_STATE_DEF
SME_BEGIN_STATE_DEF(PowerUpDown,DigitInput)
SME_STATE_ENTRY_FUNC(DigitInputEntry)
SME_STATE_EXIT_FUNC(DigitInputExit)
SME_ON_EVENT(EVENT_EDIT_OK,OnDigitInputEVENT_EDIT_OK,Function)
SME_END_STATE_DEF
SME_BEGIN_STATE_DEF(PowerUpDown,ImageSurf)
SME_STATE_ENTRY_FUNC(ImageSurfEntry)
SME_STATE_EXIT_FUNC(ImageSurfExit)
SME_ON_EVENT(EVENT_IMAGE_EXIT,NULL,Idle)
SME_ON_EVENT(EVENT_IMAGE_FUNC,OnImageSurfEVENT_IMAGE_FUNC,Function)
SME_ON_EVENT(EVENT_IMAGE_OPEN,OnImageSurfEVENT_IMAGE_OPEN,MenuSurf)
SME_END_STATE_DEF
SME_BEGIN_STATE_TREE_DEF(PowerUpDown)
SME_STATE(PowerUpDown,PowerUpDown,SME_INVALID_STATE,PowerDown)
SME_STATE(PowerUpDown,PowerDown,0,-1)
SME_STATE(PowerUpDown,PowerUp,0,Idle)
SME_STATE(PowerUpDown,Idle,PowerUp,-1)
SME_STATE(PowerUpDown,MenuSurf,PowerUp,-1)
SME_STATE(PowerUpDown,Function,PowerUp,-1)
SME_STATE(PowerUpDown,DigitInput,PowerUp,-1)
SME_STATE(PowerUpDown,ImageSurf,PowerUp,-1)
SME_END_STATE_TREE_DEF
SME_APPLICATION_DEF(PowerUpDown, "PowerUpDown")
#define SME_BEGIN_STATE_DECLARE(_app) \
enum _app##_state_enum_t \
{
#define SME_STATE_DECLARE(_state) _state,
#define SME_MAX_STATE(_app) _app##_max_state
#define SME_END_STATE_DECLARE };
#define SME_ENTRY_FUNC_IDX 0
#define SME_EXIT_FUNC_IDX 1
#define SME_EVENT_HANDLER_FUNC_IDX 2
#define SME_BEGIN_STATE_DEF(_app,_state) \
static const SME_EVENT_TABLE_T _app##_state##_event_hdl_tbl[] \
={
#define SME_STATE_ENTRY_FUNC( _EntryFunc) \
{ SME_INVALID_EVENT_ID, _EntryFunc, 0},
#define SME_STATE_EXIT_FUNC( _ExitFunc) \
{ SME_INVALID_EVENT_ID, _ExitFunc, 0},
#define SME_ON_EVENT(_EventID, _Handler, _NewState) \
{ _EventID, _Handler, _NewState},
#define SME_END_STATE_DEF { SME_INVALID_EVENT_ID, 0, SME_INVALID_STATE}};
#define SME_BEGIN_STATE_TREE_DEF(_app) \
extern const SME_STATE_TREE_TABLE_T _app##_state_tree[] = \
{
#define SME_STATE(_app,_state,_state_parent,_def_substate) \
{(SME_EVENT_TABLE_T *)_app##_state##_event_hdl_tbl,
_state,_state_parent,_def_substate},
#define SME_END_STATE_TREE_DEF };
#define SME_APPLICATION_DEF(_app,_app_name) \
struct SME_APP_T _app##App = { \
_app_name, NULL, NULL, 0, NULL, NULL,
_app##_state_tree, SME_INVALID_STATE};
#define SME_GET_APP_VAR(_app) _app##App
#define SME_DEC_EXT_APP_VAR(_app) extern SME_APP_T _app##App;
The figure 1 shows the State Tree tab window in running mode. App project consists of the DialogCtrl, MenuCtrl and PowerUpDown applications. PowerUpDown application is comprised of (PowerUp (Idle, MenuSurf, Function), PowerDown) states.
The State Chart gives you a "birds'-eye view" of your application's logical structure and flow. It graphically constructs the state hierarchy, with child states nested within their parent state. Another of State Chart's major advantage over the State Tree is that it displays every state's transitions, in the form of arrows from one state to another state.
Figure 2:The State Chart of PowerUpDown Application
There are two threads in developing embedded system applications, one thread is application developing. The other thread is simulator developing. On each developing thread, we can divide it to the following three stages:
- Modeling stage
- Developing/coding stage
- Simulating/debugging stage
Figure 3: The Developing Stages
After simulating and debugging on the Windows platform, the developer can move the program to a destination working environment with little or no extra investment or effort.