一种简易状态机实现

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

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值