状态机模块

裸机项目开发过程中,比较主张大轮询+状态转换。

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