基于UML状态机开发嵌入式系统
本文探讨了如何使用UML状态机向导开发和模拟跨平台嵌入式系统。
图 1: PowerUpDown 应用程序在运行模式下的状态树
引言
本文探讨了如何使用UML状态机向导开发和模拟跨平台嵌入式系统。
背景
嵌入式系统是一些在设备内部使用的专用计算机。嵌入式系统通常使用微控制器,微控制器在一个设备上包含计算机的许多功能。
嵌入式系统必须与专用硬件紧密协作。它们必须在预定的时间内响应外部交互。通常,嵌入式系统是实时的。
许多嵌入式系统应用程序很自然地适合被组织为状态机。一个必须按顺序执行一系列操作,或者根据其状态以不同方式处理输入的程序,通常最好实现为状态机。
因此,大多数嵌入式系统都支持基于状态机的设计,在这种设计中,可以在单个状态中接收多个事件。下一个状态由接收到的事件的内容决定。状态机提供了一种非常灵活的机制来处理异步事件交互。灵活性伴随着自身的复杂性。
Visual C++ 是一种强大的软件开发工具。但是,它的目标是开发特定于 Windows 的应用程序。UML状态机向导充当 Visual C++ 插件,它在便携式标准 C 中提供 UML(统一建模语言)状态机编程机制,用于在 Visual C++ 开发人员工作室中开发和模拟嵌入式系统。在模拟和调试之后,开发人员可以以很少或不需要额外的投入或努力将程序移动到目标工作环境。
使用代码
有很多方法可以显示状态机,从简单的表格到图形动画插图。状态机向导以两种方式显示和组织状态机:状态树和状态图。状态机向导定义了一组特定的宏作为状态机映射数据。这些宏映射到状态枚举、事件处理程序函数声明、状态的事件处理程序表、状态树定义和应用程序变量定义。在下面的源文件 PowerUpDown.c 中,状态机向导生成以下宏来定义 PowerUpDown 应用程序
SME_BEGIN_STATE_DEF(PowerUpDown,PowerUpDown) /*{{SME_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_STATE_DEF*/ SME_END_STATE_DEF SME_BEGIN_STATE_DEF(PowerUpDown,PowerDown) /*{{SME_STATE_DEF(PowerUpDown,PowerDown)*/ SME_STATE_ENTRY_FUNC(PowerDownEntry) SME_STATE_EXIT_FUNC(PowerDownExit) SME_ON_EVENT(EVENT_POWER_UP,NULL,PowerUp) /*}}SME_STATE_DEF*/ SME_END_STATE_DEF SME_BEGIN_STATE_DEF(PowerUpDown,PowerUp) /*{{SME_STATE_DEF(PowerUpDown,PowerUp)*/ SME_STATE_ENTRY_FUNC(PowerUpEntry) SME_STATE_EXIT_FUNC(PowerUpExit) SME_ON_EVENT(EVENT_KEY_POWER,NULL,PowerDown) /*}}SME_STATE_DEF*/ SME_END_STATE_DEF SME_BEGIN_STATE_DEF(PowerUpDown,Idle) /*{{SME_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_STATE_DEF*/ SME_END_STATE_DEF SME_BEGIN_STATE_DEF(PowerUpDown,MenuSurf) /*{{SME_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_STATE_DEF*/ SME_END_STATE_DEF SME_BEGIN_STATE_DEF(PowerUpDown,Function) /*{{SME_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_STATE_DEF*/ SME_END_STATE_DEF SME_BEGIN_STATE_DEF(PowerUpDown,DigitInput) /*{{SME_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_STATE_DEF*/ SME_END_STATE_DEF SME_BEGIN_STATE_DEF(PowerUpDown,ImageSurf) /*{{SME_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_STATE_DEF*/ SME_END_STATE_DEF /*{{SME_STATE_STATETREE_SEPARATOR}}*/ SME_BEGIN_STATE_TREE_DEF(PowerUpDown) /*{{SME_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_STATE_TREE_DEF*/ SME_END_STATE_TREE_DEF /*{{SME_DEC_IMP_SEPARATOR}}*/ SME_APPLICATION_DEF(PowerUpDown, "PowerUpDown") /* The follows are macro definitions for state machine data mappings in header file. */ #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}; /* Get application variable name. */ #define SME_GET_APP_VAR(_app) _app##App /* Declare application variable that has external linkage. */ #define SME_DEC_EXT_APP_VAR(_app) extern SME_APP_T _app##App; //
图 1 显示了运行模式下的状态树选项卡窗口。App 项目由 DialogCtrl、MenuCtrl 和 PowerUpDown 应用程序组成。PowerUpDown 应用程序由 (PowerUp (Idle, MenuSurf, Function), PowerDown) 状态组成。
状态图让您可以“鸟瞰”应用程序的逻辑结构和流程。它以图形方式构建状态层次结构,子状态嵌套在其父状态中。状态图优于状态树的另一个主要优点是,它以从一个状态到另一个状态的箭头形式显示每个状态的转换。
图 2: PowerUpDown 应用程序的状态图
在开发嵌入式系统应用程序中有两个线程,一个线程是应用程序开发。另一个线程是模拟器开发。在每个开发线程上,我们可以将其划分为以下三个阶段
- 建模阶段
- 开发/编码阶段
- 模拟/调试阶段
图 3: 开发阶段
在 Windows 平台上进行模拟和调试后,开发人员可以以很少或不需要额外的投入或努力将程序移动到目标工作环境。