状态机模块
裸机项目开发过程中,比较主张大轮询+状态转换。
BabyOS提供的代码多为功能模块,也一直在考虑使用哪种方式给用户去组织应用部分的代码。目前网上几种典型的:类似小小调度器,基于C的语法,做成多任务调度的样子 ,其他的就是事件驱动的状态机。
个人比较偏向于状态机,对于应用层,主要是实现产品的业务逻辑。编码前整理系统的状态数量、切换条件以及各状态下的执行操作,最后实现即可。
目前网上的状态机代码,初略去看都整的挺繁琐(接口比较多)。于是想着BabyOS先提供一个简洁的状态机软件模块,在使用过程中慢慢完善。(b_mod_state)
先看接口,为了做到简洁,只提供了4个接口:
//注册状态信息,将state_info添加到b_mod_state段里面
#define bSTATE_REG_INSTANCE(state_info)
//状态的转换,state是目标状态
int bStateTransfer(uint32_t state);
//调用事件,每个状态支持携带一个事件表
int bStateInvokeEvent(uint32_t event, void *arg);
//获取当前状态
int bGetCurrentState(void);
关键数据结构:
typedef struct
{
uint32_t state; //状态
pStateEnterHandler_t enter; //进入状态时执行的操作,可以为NULL
pStateExitHandler_t exit; //离开状态时执行的操作,可以为NULL
pStateHandler_t handler; //在此状态期间执行的操作,可以为NULL
bStateEventTable_t event_table; //状态附加的事件表,其内容可以为NULL
} bStateInfo_t;
举例说明(例程仓库/state目录代码):
产品x,STM32F107+WIFI模组,主要功能是通过MQTT定时上报“hello babyos”。
一、规划状态和事件:
1.离线状态
定时ping www.baidu.com 如果ping通则切换到在线状态
2.在线状态
连接MQTT代理,定时发布消息
3.配网状态
进入AP状态,并建立TCP服务。等待配网信息的接收。连接路由器成功后切换到离线状态。
4.事件
每个状态都搭配一个事件表,即配网事件。离线和在线状态时收到配网事件,进入配网模式。
配网状态下收到配网事件则退出进入离线状态
二、实现数据结构并注册
以在线状态为例,其他的可以看仓库代码。
static const bStateEvent_t s_OnlineEventTable[] = {
{EVENT_CFG_NET, _EventHandler},
};
static const bStateInfo_t s_OnlineStateInfo = {
.state = STATE_ONLINE,
.enter = _EnterOnline,
.exit = _ExitOnline,
.handler = _OnlineHandler,
.event_table =
{
.p_event_table = (bStateEvent_t *)&s_OnlineEventTable[0],
.number = (sizeof(s_OnlineEventTable) / sizeof(bStateEvent_t)),
},
};
bSTATE_REG_INSTANCE(s_OnlineStateInfo);
三、实现各个接口里面内容,具体的可见例程仓库代码
四、初始化时,指定起始状态
int main()
{
BoardInit();
SysTick_Config(SystemCoreClock / TICK_FRQ_HZ);
NVIC_SetPriority(SysTick_IRQn, 0x0);
bInit();
bButtonInit(3000, 5000, 8000);
bButtonRegEvent(0, BTN_EVENT_LONG, BtnEventHandler);
bStateTransfer(STATE_OFFLINE); //初始为离线状态
while (1)
{
bExec();
}
}
HW:21.12.12 FW:7.4.10 COMPILE:Oct 23 2022-01:17:43
device number:2
I:state 0 --> offline(0) //初始状态
I:state 0 --> net_cfg(2) //长按按键,进入配网状态
tcp:s:notrynohigh p:11223344 //建立AP和TCP服务后,收到数据
ssid:notrynohigh
passwd:11223344 //解析路由器信息
I:state 2 --> offline(0) //切换到离线状态
I:state 0 --> Online(1) //离线状态下ping百度成功,进入在线状态
result 2
error 0
I:state 1 --> offline(0) //进入在线状态,连接MQTT代理失败则进入离线
I:state 0 --> Online(1) //离线状态下确认网络正常,则再进入在线状态
result 1