API接口
void fsm_init(fsm_t* me , state_handle_t init);
void fsm_polling(void);
/*evt function*/
void fsm_set_timeout_evt(fsm_t* me , uint32_t timeout,uint8_t evt);
void fsm_stop_timeout_evt(fsm_t* me,uint8_t evt);
void fsm_set_irq_evt(fsm_t* me , uint32_t timeout , uint8_t evt);
void fsm_stop_irq_evt(fsm_t* me);
int fsm_evt_post(fsm_t* me ,uint8_t evt);
使用示例
实现在自动入网重连,周期上报数据,串口接收超时检测功能
/*定义状态机*/
fsm_t lw_fsm;
/*声明所有系统状态*/
static R_state_t lw_idle_state(fsm_t* me , uint8_t evt);
static R_state_t lw_join_state(fsm_t* me , uint8_t evt);
static R_state_t lw_normal_state(fsm_t* me , uint8_t evt);
/*定义系统存在的事件*/
enum
{
UART_RECV_PACKET_EVT = USER_EVT,
UART_LINK_TIMEOUT_EVT,
JOIN_ACCEPT_EVT,
JOIN_EXIT_EVT,
};
/*初始化状态机*/
void lw_fsm_init(void)
{
fsm_init(&lw_fsm , lw_idle_state);
}
/*******************************************************************************
* --------<->---------
* / \
* idle ------->join --------->normal
* systerm lw fsm
*******************************************************************************/
/*各个状态的实现*/
static R_state_t lw_idle_state(fsm_t* me , uint8_t evt)
{
switch (evt)
{
case ENTER_EVT:
{
fsm_set_timeout_evt(me , 50 ,TIMEOUT_EVT);
LOG("enter idle state");
return S_HANDLE();
}
case EXIT_EVT:
{
fsm_stop_timeout_evt(me ,TIMEOUT_EVT);
return S_HANDLE();
}
case TIMEOUT_EVT:
{
return S_TRAN(lw_join_state);
}
default :
{
return S_IGNORE();
}
}
}
static R_state_t lw_join_state(fsm_t* me , uint8_t evt)
{
switch (evt)
{
case ENTER_EVT:
{
LOG("enter join state");
fsm_set_timeout_evt(me ,10000 , TIMEOUT_EVT);
LOG("join init: %d", 10000);
return S_HANDLE();
}
case EXIT_EVT:
{
/*when exit clear timer evt*/
fsm_stop_timeout_evt(me,TIMEOUT_EVT);
return S_HANDLE();
}
case TIMEOUT_EVT:
{
lw_join();
fsm_set_timeout_evt(me , 15000,TIMEOUT_EVT);
LOG("join next: %d", 15000);
return S_HANDLE();
}
case JOIN_ACCEPT_EVT:
{
return S_TRAN(lw_normal_state);
return S_HANDLE();
}
default :
{
return S_IGNORE();
}
}
}
static R_state_t lw_normal_state(fsm_t* me , uint8_t evt)
{
switch (evt)
{
case ENTER_EVT:
{
LOG("enter normal state");
fsm_set_timeout_evt(me, 1000,START_UPLINK_PACKET_EVT);
return S_HANDLE();
}
case EXIT_EVT:/*notify ul_fsm goto idle*/
{
fsm_stop_timeout_evt(me,START_UPLINK_PACKET_EVT);
fsm_stop_timeout_evt(me ,SENSOR_LINK_TIMEOUT_EVT);
return S_HANDLE();
}
case JOIN_EXIT_EVT:
{
return S_TRAN(ut_idle_state);
}
case SENSOR_RECV_PACKET_EVT:
{
/*timeout = 30s*/
fsm_stop_timeout_evt(me ,SENSOR_LINK_TIMEOUT_EVT);
fsm_set_timeout_evt(me,30000,SENSOR_LINK_TIMEOUT_EVT);
return S_HANDLE();
}
case SENSOR_LINK_TIMEOUT_EVT:
{
/*maybe periodic uplink uart link error there*/
fsm_set_timeout_evt(me,30000,SENSOR_LINK_TIMEOUT_EVT);
return S_HANDLE();
}
case START_UPLINK_PACKET_EVT: /*10s period uplink*/
{
lorawan_send(uplink.buf , uplink.len);
fsm_set_timeout_evt(me, 10000,START_UPLINK_PACKET_EVT);
return S_HANDLE();
}
default :
{
return S_IGNORE();
}
}
}
/*事件的产生*/
void lorawan_callback(...)
{
...
case LW_STA_JOINED:
fsm_evt_post(&lw_fsm , JOIN_ACCEPT_EVT);
...
}
void uart_rx_callback(uint8_t byte)
{
...
if(validate == OK)
{
fsm_evt_post(&lw_fsm , SENSOR_RECV_PACKET_EVT);
}
...
}
/*
JOIN_EXIT 事件的产生:
每次上行数据进行计数(unconfirm),当计数数值为8的倍数,发送confirm , 当计数数值到达24 ,产生JOIN_EXIT事件,有任何下行都会清除计数数值
*/
实现
事件分为3种类型:
-
直接post事件 ,事件被加入指定状态机的事件队列进行,系统轮询获取
-
定时post事件,事件在到达定时时间后,会触发调度,但也是轮询调度
-
中断事件 ,
与定时事件类似,但中断事件是在中断中进行事件处理,拥有更高的实时性。
实现要点:
每个状态机具备一个状态函数指针,当状态发生切换,直接改变状态指针指向,不同状态机链接在一起。
typedef R_state_t (*state_handle_t)(fsm_t* me, uint8_t evt);
struct fsm
{
struct fsm* next; //指向下一个状态机
state_handle_t cur_state;
state_handle_t last_state;
};
事件派发:
static void fsm_evt_dispatch(fsm_t* me,uint8_t evt)
{
state_handle_t last = me->cur_state; //记录当前状态
R_state_t ret = me->cur_state(me , evt); //执行事件
if(ret == STATE_RET_TRAN) //发生状态转变
{
me->last_state = last;
me->last_state(me,EXIT_EVT); //执行上一个状态的退出事件
me->cur_state (me,ENTER_EVT);//执行下一个状态的进入事件
}
}
事件获取:
/*轮询获取*/
void fsm_polling(void)
{
fsm_t* cur_fsm = &active_fsm;
uint8_t evt;
while (cur_fsm->next != NULL)
{
cur_fsm = cur_fsm->next;
/*get all evt and dispatch*/
while(fsm_evt_get(cur_fsm,&evt) == 0)
{
fsm_evt_dispatch(cur_fsm,evt);
}
/*get all timeout evt and dispatch*/
for(uint8_t i=0;i<FSM_MAX_TIMER_EVT;i++)
{
if(cur_fsm->timer_evt[i].status == true)
{
if(TimerGetFlag(&cur_fsm->timer_evt[i].timer))
{
cur_fsm->timer_evt[i].status = false;
evt = cur_fsm->timer_evt[i].evt;
cur_fsm->timer_evt[i].evt = 0;
fsm_evt_dispatch(cur_fsm , evt);
}
}
}
}
}
/*中断获取*/
static void fsm_timer_irq_callback(void)
{
uint8_t evt;
fsm_t* cur_fsm = &active_fsm;
while (cur_fsm->next != NULL)
{
cur_fsm = cur_fsm->next;
if(cur_fsm->irq_evt.status == true)
{
if(TimerGetFlag(&cur_fsm->irq_evt.timer))
{
/*weather need to dispatch the fifo evt*/
cur_fsm->irq_evt.status = false;
evt = cur_fsm->irq_evt.evt;
cur_fsm->irq_evt.evt = 0;
fsm_evt_dispatch(cur_fsm , evt);
/*Only one will happen at any one time*/
return;
}
}
}
}
使用注意事项
- 需要实现定时器接口才能移植
- 在一个状态中启动了定时事件,需要在退出事件中清除对应的事件,否则定时事件会已经存在,最终被派发到切换后的状态,若事件为公共事件,将会出问题。
- 中断事件的使用
- 一个状态只支持一个中断事件,可进行扩展。
- 增加中断事件是为了达到精确延时的目的,但是会引入新的问题。首先,中断事件处理是属于临界段,代码执行时间需要严格把控。
- 另一个问题在于:若在中断事件中发生了状态切换,而此时事件队列中仍然有事件剩余,那么事件是应该丢弃还是保留?这个难以抉择,对于只存在本状态的事件,在中断事件造成状态切换后,事件丢不丢弃无所谓,其他状态不会理会;但有的公共事件不好抉择,如系统的TIMEOUT事件可能很多状态会使用,所以需要丢弃 ,但JOIN_EXIT公共事件 却不能丢弃。因此,需要特别注意中断事件发生状态切换的情况。
完整代码:
链接:https://pan.baidu.com/s/1LZwGaiU59UKvhVXlJ0ZQ2A
提取码:jxjh