Custom serial port protocol


foreword

This article will combine the finite state machine and the circular queue to build a stable and reliable custom serial port protocol. If the code is a bit difficult to understand, you can directly transplant it and use it. Just use the demo, I have packaged it.

Gitee link here:
serial port based finite state machine

Briefly mention the main functions implemented:

1.: Command mode, compare the input characters with the contents of the buffer, and return a true or false value.

2.: Parameter adjustment mode, compare the input characters with the contents of the buffer, and return the value of the input number. This is more useful for debugging parameters . For example, when debugging PID, you can directly enter a command such as PID_P=0.111, and it will return 1000 times the input parameter, which is used for online parameter adjustment.


1. Finite state machine

For the finite state machine, only 4 simple states are used here, and the transition between states can be according to the figure below. Switch between states according to the different character types received.

In this state machine, the source state is an empty state in which the buffer area completes an acceptance; the trigger event is the receipt of a character (so it is generally placed in the interrupt for judgment); the guardian condition is the type of the character received, and the action It is the switching condition and storing in the buffer.

In the figure below: the arrows indicate the switching between states, and the letters next to the arrows are the conditions for switching states. In the figure, IDEL indicates that it is ready to receive data; HEAD has received the head status; DATA has received data status (can always receive data); TAIL has received tail data, and END indicates that the received data is incorrect and the error data is clear.

In the designed custom serial port protocol: there are only frame header and frame tail, and both frame header and frame tail can be designed by themselves through the initialization function.
insert image description here

Finite State Machine Code

According to the above figure, the following code can be designed, using enumeration to design 4 states.

/*协议有限状态机枚举*/
typedef enum
{
    
    
    STATUS_IDEL=(uint8_t) 0,
    STATUS_HEAD,
    STATUS_DATA,
    STATUS_END,
}COM_STATUS;

receive data buffer

In order to process data more efficiently, a buffer is used to store the received correct frames into the buffer, and the buffer can store n valid frames.

buffer requirements

The designed buffer should be in the form of first-in first-out , that is, the old instructions are processed first. Using the queue as a buffer is in line with the requirements. The queue is like our queuing. Those who line up first are processed first, and those who join later are processed later.

circular queue

In the limited MCU resources, we hope that when someone joins and exits the queue, there will be as few operations as possible, that is, we don't want a person in the front to walk in the queue, and the people behind must keep up with a position. This will move the queue and waste everyone's energy (CPU resources). Therefore, choose to use a circular queue, form the queue into a cycle, and use a cursor, which indicates the currently queued individual. When the person in front leaves, the cursor moves to the next one, and the people behind do not need to move. In this way, you can know who the next person is by moving the cursor without moving the entire queue, which reduces CPU resource consumption.

1. Circular Queue

Circular queue is more troublesome. If you want to understand the specific implementation principle, please Baidu or find a textbook to learn! Here is a circular queue based on an object-oriented approach. If you want to learn how to use C language to implement object-oriented, you can read my previous articles –> C language implements object-oriented

If you don’t know much about void * pointers, you can read my previous article –> C language value Void * pointer

Code

The code is very easy to understand (the premise is to understand the queue and the object-oriented way of C language), so don't write too many comments~

Circular queue header file:

#ifndef _QUE_OOP_H
#define _QUE_OOP_H

#define FALSE 0
#define TRUE 1

typedef unsigned char cbool;
typedef unsigned char uint8_t;

#define QUE_MAX_LEN 100

typedef char QUEUE_TYPE;
struct Que_vtable;

/*创建循环队列类*/

/*循环队列实际上只有K-1个空间能用*/
/*队列为空:头=尾 */
/*队列满:头=尾+1%最大长度,留出一个空位作为满的条件,不然头=尾的情况和空的情况重复*/

/*循环队列结构体*/
typedef struct Cir_queue
{
    
    
    struct Que_vtable *c_vptr;

    QUEUE_TYPE  Queue_Buffer[QUE_MAX_LEN];//缓冲数组
    int head,tail,max_len,lenth;
}Cir_queue;

/*队列虚表*/
struct Que_vtable
{
    
    
    // void (*create_queue)(void * Me);
    void (*delete_queue)(void * const Me);

    cbool (*empty)(void const * const Me);
    cbool (*full)(void const * const Me);

    cbool (*pop)(void * const Me,QUEUE_TYPE *Get); 
    cbool (*push)(void * const Me,QUEUE_TYPE value);

    cbool (*head_push)(void * const Me,QUEUE_TYPE value);
    cbool (*tail_pop)(void * const Me,QUEUE_TYPE *Get); 

    cbool (*back)(void const * const Me,QUEUE_TYPE *Get);
    cbool (*front)(void const * const Me,QUEUE_TYPE *Get);  

    int   (*lenth)(void const * const Me);

}Que_vtable;

/*多态类*/
// void Qcreate_queue(void * const Me);
void Qdreate_queue(void * const Me);
cbool Qempty(void const * const Me);
cbool Qfull(void const * const Me);
cbool Qtail_pop(void * const Me,QUEUE_TYPE *Get);
cbool Qhead_push(void * const Me,QUEUE_TYPE value);
cbool Qpop(void * const Me,QUEUE_TYPE *Get);
cbool Qpush(void * const Me,QUEUE_TYPE value);
cbool Qback(void const * const Me,QUEUE_TYPE *Get);
cbool Qfront(void const * const Me,QUEUE_TYPE *Get);
int Q_lenth(void const * const Me);

/*循环队列*/
void Cir__Qcreate_queue(Cir_queue * const Me);
void Cir__Qdreate_queue(Cir_queue * const Me);
cbool Cir__Qempty(Cir_queue const * const Me);
cbool Cir__Qfull(Cir_queue const * const Me);
cbool Cir_Qtail_pop(Cir_queue * const Me,QUEUE_TYPE *Get);
cbool Cir_Qhead_push(Cir_queue * const Me,QUEUE_TYPE value);
cbool Cir__Qpush(Cir_queue * const Me,QUEUE_TYPE value);
cbool Cir__Qpop(Cir_queue * const Me,QUEUE_TYPE *Get);
cbool Cir__Qback(Cir_queue const * const Me,QUEUE_TYPE *Get);
cbool Cir__Qfront(Cir_queue const * const Me,QUEUE_TYPE *Get);
int Cir__Q_lenth(Cir_queue const * const Me);

Source File:

void * my_memset(void *source,int dest,int n)
{
    
    
    char *c_s=(char *)source;
    while(n--) *c_s++=dest;
    return source;
}

/*多态实现*/
inline void Qdreate_queue(void * const Me)
{
    
    
    Cir_queue const * const _Me=(Cir_queue * const)Me;
    
    _Me->c_vptr->delete_queue(Me);
}

inline cbool Qempty(void const * const Me)
{
    
    
    Cir_queue const * const _Me=(Cir_queue * const)Me;
    
    return _Me->c_vptr->empty(Me);
}


inline cbool Qfull(void const * const Me)
{
    
    
    Cir_queue const * const _Me=(Cir_queue const * const)Me;
    
    return _Me->c_vptr->full(Me);
}

inline cbool Qpop(void * const Me,QUEUE_TYPE *Get)
{
    
    
    Cir_queue const * const _Me=(Cir_queue * const)Me;
    
    return _Me->c_vptr->pop(Me,Get);
}

inline cbool Qpush(void * const Me,QUEUE_TYPE value)
{
    
    
    Cir_queue const * const _Me=(Cir_queue * const)Me;
    
    return _Me->c_vptr->push(Me,value);    
}

inline cbool Qtail_pop(void * const Me,QUEUE_TYPE *Get)
{
    
    
    Cir_queue const * const _Me=(Cir_queue * const)Me;
    
    return _Me->c_vptr->pop(Me,Get);
}

inline cbool Qhead_push(void * const Me,QUEUE_TYPE value)
{
    
    
    Cir_queue const * const _Me=(Cir_queue * const)Me;
    
    return _Me->c_vptr->push(Me,value); 
}

inline cbool Qback(void const * const Me,QUEUE_TYPE *Get)
{
    
    
    Cir_queue const * const _Me=(Cir_queue * const)Me;
    
    return _Me->c_vptr->back(Me,Get);    
}

inline cbool Qfront(void const * const Me,QUEUE_TYPE *Get)
{
    
    
    Cir_queue const * const _Me=(Cir_queue * const)Me;
    
    return _Me->c_vptr->front(Me,Get);    
}

inline int Q_lenth(void const * const Me)
{
    
    
    Cir_queue const * const _Me=(Cir_queue * const)Me;
    
    return _Me->c_vptr->lenth(Me);      
}

void Cir__Qcreate_queue(Cir_queue * const Me)
{
    
    
    /*清零*/
    my_memset(Me,0,sizeof(Cir_queue));
    /*函数表绑定*/
    static struct Que_vtable  table;
    // table.create_queue=(void (*)(void *))(void(*)(Cir_queue *))Cir__Qcreate_queue;
    table.delete_queue=(void (*)(void *))(void(*)(Cir_queue *))Cir__Qdreate_queue;
    table.empty=(cbool (*)(void const *const ))(cbool(*)(Cir_queue  const* const))Cir__Qempty;
    table.full=(cbool (*)(void const * const ))(cbool(*)(Cir_queue const *const))Cir__Qfull;
    table.pop=(cbool (*)(void *const ,QUEUE_TYPE *Get))(cbool(*)(Cir_queue *const,QUEUE_TYPE *Get))Cir__Qpop;
    table.push=(cbool (*)(void *const,QUEUE_TYPE value))(cbool(*)(Cir_queue *const,QUEUE_TYPE value))Cir__Qpush;
    table.tail_pop=(cbool (*)(void *const,QUEUE_TYPE *Get))(cbool(*)(Cir_queue *const,QUEUE_TYPE *Get))Cir_Qtail_pop;
    table.head_push=(cbool (*)(void *const,QUEUE_TYPE value))(cbool(*)(Cir_queue *const,QUEUE_TYPE value))Cir_Qhead_push;
    table.back=(cbool (*)(void  const* const,QUEUE_TYPE *Get))(cbool(*)(Cir_queue  const*const ,QUEUE_TYPE *Get))Cir__Qback;
    table.front=(cbool (*)(void  const* const,QUEUE_TYPE *Get))(cbool(*)(Cir_queue  const*const ,QUEUE_TYPE *Get))Cir__Qfront;
    table.lenth=(int (*)(void  const* const))(int (*)(Cir_queue  const*const))Cir__Q_lenth;
    Me->c_vptr=&table;

    Me->max_len=QUE_MAX_LEN;
}

void Cir__Qdreate_queue(Cir_queue * const Me)
{
    
    
    return ;
}
cbool Cir__Qempty(Cir_queue const * const Me)
{
    
    
    if(Me->head==Me->tail) return TRUE;
    else return FALSE;
}
cbool Cir__Qfull(Cir_queue const * const Me)
{
    
    
    if(Me->head==(Me->tail+1)%QUE_MAX_LEN) return TRUE;
    else return FALSE;
}

cbool Cir__Qpop(Cir_queue * const Me,QUEUE_TYPE *Get)
{
    
    
    if(Cir__Qempty(Me)) return FALSE;
       
    else
    {
    
    
        *Get=Me->Queue_Buffer[Me->head];
        Me->head=(Me->head+1)%QUE_MAX_LEN;
        Me->lenth--;
        return TRUE;
    }     

}

cbool Cir__Qpush(Cir_queue * const Me,QUEUE_TYPE value)
{
    
    
    if(Cir__Qfull(Me)) return FALSE;
       
    else
    {
    
    
        Me->Queue_Buffer[Me->tail]=value;
        Me->tail=(Me->tail+1)%QUE_MAX_LEN;
        Me->lenth++;
        return TRUE;
    }    
}

cbool Cir_Qtail_pop(Cir_queue * const Me,QUEUE_TYPE *Get)
{
    
    
    if(Cir__Qempty(Me)) return FALSE;
       
    else
    {
    
    
        Me->tail=((Me->tail-1+QUE_MAX_LEN)%QUE_MAX_LEN);
        *Get=Me->Queue_Buffer[(Me->tail)];
        Me->lenth--;
        return TRUE;
    }        
}

cbool Cir_Qhead_push(Cir_queue * const Me,QUEUE_TYPE value)
{
    
    
    if(Cir__Qfull(Me)) return FALSE;
       
    else
    {
    
    
        Me->head=((Me->head-1+QUE_MAX_LEN)%QUE_MAX_LEN);
        Me->Queue_Buffer[Me->head]=value;
        Me->lenth++;
        return TRUE;
    }      
}

cbool Cir__Qback(Cir_queue const * const Me,QUEUE_TYPE *Get)
{
    
    
    if(Cir__Qempty(Me)) return FALSE;
    else
    {
    
    
        *Get=Me->Queue_Buffer[(Me->tail-1)%QUE_MAX_LEN];
        return TRUE;
    }

}

cbool Cir__Qfront(Cir_queue const * const Me,QUEUE_TYPE *Get)
{
    
    
    if(Cir__Qempty(Me)) return FALSE;
    else
    {
    
    
        *Get=Me->Queue_Buffer[Me->head%QUE_MAX_LEN];
        return TRUE;
    }
}

int Cir__Q_lenth(Cir_queue const * const Me)
{
    
    
    return Me->lenth;
}

2. Finite state machine and decoding

Finite state machine core implementation code

The main idea of ​​the finite state machine is that the following is the implementation code, which is the code implementation of the above-mentioned picture, and it is better to use it with the picture.

/*有限状态机读入缓存函数*/
void Com_rxUsart_data(Cir_queue * const QMe,Usart_Trm *const Me,uint8_t bydata)
{
    
    

    switch (Me->usart_status)
    {
    
    
        case STATUS_IDEL:
        if(bydata==Me->head)
        {
    
    
            Me->usart_status=STATUS_HEAD;
            Me->last_pos++;
        }
        
        else Me->usart_status=STATUS_END;
        break;
        
        case STATUS_HEAD:    
        if(bydata!=Me->teal) 
        {
    
    
            Me->usart_status=STATUS_DATA;
            Me->last_pos++;
        }

        else Me->usart_status=STATUS_END;
        break;
        
        case STATUS_DATA:
        if(bydata!=Me->teal&&!QMe->c_vptr->full(QMe)) 
        {
    
    
            Me->usart_status=STATUS_DATA;
            Me->last_pos++;
        }
        else if(bydata==Me->teal) 
        {
    
    
            Me->last_pos=0;/*成功接收到头和尾,没有错误数据*/
            Me->Uartx_frame++; /*队列中有效帧+1*/
            Me->usart_status=STATUS_IDEL;
        }

        else Me->usart_status=STATUS_END;
        break;

        case STATUS_END: break;

        default:Me->usart_status=STATUS_END;break;
    }

    if(Me->usart_status==STATUS_END)
    {
    
    
        QUEUE_TYPE temp;
        /*把出错数据取出,从尾取出*/
        while(Me->last_pos) 
        {
    
    
            QMe->c_vptr->tail_pop(QMe,&temp);
            Me->last_pos--;
        }
        Me->usart_status=STATUS_IDEL;
    }

    else
    {
    
    
        QMe->c_vptr->push(QMe,bydata);/*数据入队列*/
    }

}

Finite state machine and decoding

Decoding is mainly two simple small functions that are easy for individuals to develop. The core implementation is to compare the contents of the buffer for effective judgment and processing.

The process is as follows:

  1. The serial port receives valid data and enters the queue buffer
  2. The main function calls Frame_deal to compare the incoming instructions, and if there is no corresponding instruction in the buffer, take out the most incoming frame of data. If there is a corresponding instruction, it will be processed.

head File

The header file contains the header file for the circular queue

#ifndef __STATUS__U
#define __STATUS__U
/*协议有限状态机枚举*/
typedef enum
{
    
    
    STATUS_IDEL=(uint8_t) 0,
    STATUS_HEAD,
    STATUS_DATA,
    STATUS_END,
}COM_STATUS;

#define COMMAND_LIST_MAX 10 /*命令列表长度*/
#define COMMAND_LEN_MAX 20  /*命令最大长度*/

struct Usart_vtable;
/*协议有限状态机结构体*/
typedef struct Usart_Trm
{
    
    
    /*有效帧格式: (data),不支持中文输入*/
	uint8_t  Uartx_frame;   /*有效数据帧数*/
	uint8_t head;           /*头帧标志*/
	uint8_t teal;            /*尾帧标志*/
    COM_STATUS usart_status;  /*状态机*/
    int last_pos;           /*错误数据计数*/
    
    struct Usart_vtable *c_vptr;/*函数表,提供接口接口使用*/
}Usart_Trm;

struct Usart_vtable
{
    
    
    // void (*create_ComUsart)(Usart_Trm *Me,uint8_t set_h,uint8_t set_t);
    void (*rx_buff)(Cir_queue * const QMe,Usart_Trm *const Me,uint8_t bydata);

    int (*fram_num)(Usart_Trm *Me);
    long (*fram_deal)(Cir_queue *QMe,Usart_Trm *Me,const char *sdata);

}Usart_vtable;

void Create_ComUsart(Usart_Trm *Me,uint8_t set_h,uint8_t set_t);
void Com_rxUsart_data(Cir_queue * const QMe,Usart_Trm *const Me,uint8_t bydata);

int Get_fram_num(Usart_Trm *Me);
long Deal_com(Cir_queue *QMe,Usart_Trm *Me,const char *sdata);

void Com_tailpush(Cir_queue *QMe,Usart_Trm *Me,char const *get_str);

#endif

Source File

/*有限状态机初始化*/
void Create_ComUsart(Usart_Trm *Me,uint8_t set_h,uint8_t set_t)
{
    
       
    static struct Usart_vtable vtable;

    my_memset(Me,0,sizeof(Usart_Trm));

    vtable.rx_buff=&Com_rxUsart_data;
    vtable.fram_deal=&Deal_com;
    vtable.fram_num=&Get_fram_num;   
    Me->c_vptr=&vtable;

    Me->head=set_h;
    Me->teal=set_t;
    Me->usart_status=STATUS_IDEL;
}

/*有限状态机读入缓存函数*/
void Com_rxUsart_data(Cir_queue * const QMe,Usart_Trm *const Me,uint8_t bydata)
{
    
    

    switch (Me->usart_status)
    {
    
    
        case STATUS_IDEL:
        if(bydata==Me->head)
        {
    
    
            Me->usart_status=STATUS_HEAD;
            Me->last_pos++;
        }
        
        else Me->usart_status=STATUS_END;
        break;
        
        case STATUS_HEAD:    
        if(bydata!=Me->teal) 
        {
    
    
            Me->usart_status=STATUS_DATA;
            Me->last_pos++;
        }

        else Me->usart_status=STATUS_END;
        break;
        
        case STATUS_DATA:
        if(bydata!=Me->teal&&!QMe->c_vptr->full(QMe)) 
        {
    
    
            Me->usart_status=STATUS_DATA;
            Me->last_pos++;
        }
        else if(bydata==Me->teal) 
        {
    
    
            Me->last_pos=0;/*成功接收到头和尾,没有错误数据*/
            Me->Uartx_frame++; /*队列中有效帧+1*/
            Me->usart_status=STATUS_IDEL;
        }

        else Me->usart_status=STATUS_END;
        break;

        case STATUS_END: break;

        default:Me->usart_status=STATUS_END;break;
    }

    if(Me->usart_status==STATUS_END)
    {
    
    
        QUEUE_TYPE temp;
        /*把出错数据取出,从尾取出*/
        while(Me->last_pos) 
        {
    
    
            QMe->c_vptr->tail_pop(QMe,&temp);
            Me->last_pos--;
        }
        Me->usart_status=STATUS_IDEL;
    }

    else
    {
    
    
        QMe->c_vptr->push(QMe,bydata);/*数据入队列*/
    }

}

/*实现两种命令方式,自定义根据有没有'='号自行判断命令类型*/

/*判断命令类型*/
static int Com_type(const char * const str)
{
    
    
    char *buf_ptr=(char *)str;
	int count = 0;
    
    /*内容为空*/
    if(*(buf_ptr+count)=='\0') return 0;	

	while (*(buf_ptr+count)!='='&&*(buf_ptr+count)!='\0')//'='是判断的位置,也是命令停止标志
	{
    
    
		count++;
	}

    if(*(buf_ptr+count)=='=') 
    {
    
    
       return count;
    }

	else 
    {
    
    
        return 0;
    }
}

/*读取取出1帧的内容,保存帧头和帧尾,返回读取到的内容*/
static char * Get_fram_data(Cir_queue *QMe,Usart_Trm *Me)
{
    
    
    static char buff[50],get_char;
    
    int i=0;
    my_memset(buff,0,sizeof(buff));

    while(Me->Uartx_frame!=0&&QMe->c_vptr->pop(QMe,&get_char)) 
    {
    
    
        if(get_char==')') 
        {
    
    
            Me->Uartx_frame--;
            break;
        }
        buff[i++]=get_char;
    }
    buff[i++]=')';
    return buff;
}

/*将命令放在尾部*/
void Com_tailpush(Cir_queue *QMe,Usart_Trm *Me,char const *get_str)
{
    
    

    Me->usart_status==STATUS_END;
    /*解决传入命令不完成的问题,比如原来数据:(111,还未接收完成(标记为错误,重新接收),插入就会变成(111(112)*/
    if(Me->last_pos!=0) 
    {
    
    
        QUEUE_TYPE temp;
        for(int i=Me->last_pos;i>0;i--) QMe->c_vptr->tail_pop(QMe,&temp);
        Me->usart_status=STATUS_IDEL;
        Me->last_pos=0;
    }
    /*把当前数据的帧头和帧尾放进列表尾部*/
    while(*get_str!='\0')
    {
    
    
        Me->c_vptr->rx_buff(QMe,Me,*get_str);/*存入数据进入缓冲区*/
        get_str++;
    }

    Me->c_vptr->rx_buff(QMe,Me,*get_str);/*将帧尾存入*/
}

/*返回缓存区的帧数*/
int Get_fram_num(Usart_Trm *Me)
{
    
    
    return Me->Uartx_frame;
}

/*返回0则为命令假,返回其他则为真,数据默认扩大1000倍,返回1则为命令模式*/
long Deal_com(Cir_queue *QMe,Usart_Trm *Me,const char *sdata)
{
    
    
	char com_buf[COMMAND_LEN_MAX]={
    
    0};//临时保存内容数据
    int eque_pos=0,back_data;

    float temp=0;
	char *get_string;

    int i=0;
    /*遍历队列中所有命令,找到符合命令的*/
    while (i<=Me->c_vptr->fram_num(Me))
    {
    
    
        i++;
        get_string=Get_fram_data(QMe,Me);    /*获取帧内容*/
        #ifdef CIRDEBUG
        printf("Get string is %s \n",get_string);
        #endif 
        /*遍历一遍找到了,则返回*/
        if(strncmp((const char *)get_string+1,sdata,strlen(sdata))==0)  break;
    
        Com_tailpush(QMe,Me,get_string);
    }
    
    /*遍历完都没有找到,丢弃当前帧,当前为最先插入的帧*/
    if(i==Me->c_vptr->fram_num(Me)+1) return 0;

    eque_pos=Com_type(++get_string);

    /*返回输入命令中的数字*/
	if(eque_pos!=0&&*get_string!='\0')
	{
    
    	
        /*把尾帧去掉*/
        for(int i=0;i<strlen(get_string)-(eque_pos+1);i++)
        {
    
    
             com_buf[i]=*(get_string+eque_pos+i+1);/*把等于号后面的全部数字保存*/
        }
		temp=atof(com_buf);/*字符串转换成为浮点型数字*/
        back_data=temp*1000;/*扩大100倍*/
        return back_data;
	}   
    /*符合命令,不反回参数*/
	else if(eque_pos==0&&*get_string!='\0')   return 1;
    /*其他情况*/
    else   return 0;
}

3. Use a small example

The small example code is as follows

int main()
{
    
    
    long x;
    Usart_Trm usart_trm;
    Cir_queue que;
    int cout=0;

    /*假设实际接收到的数据*/
    char AS[20]="(PID_P=1.11)";
    char BS[20]="(PID_I=2.22)";
    char CS[20]="(PID_D=3.33)";

    char COM[20]="(SET)";

    Create_ComUsart(&usart_trm,'(',')');
    Cir__Qcreate_queue(&que);


    /*模拟串口存入数据*/
    for(int j=0;j<5;j++)
    {
    
    
        for(int i=0;i<strlen(AS);i++)
        {
    
    
            /*逐个数据存入*/
            if(j==0) usart_trm.c_vptr->rx_buff(&que,&usart_trm,AS[i]);/*PID_P*/
            if(j==1) usart_trm.c_vptr->rx_buff(&que,&usart_trm,BS[i]);/*PID_I*/
            if(j==2) usart_trm.c_vptr->rx_buff(&que,&usart_trm,CS[i]);/*PID_D*/
            if(j==3) usart_trm.c_vptr->rx_buff(&que,&usart_trm,AS[i]);/*PID_P*/
            if(j==4) usart_trm.c_vptr->rx_buff(&que,&usart_trm,COM[i]);/*SET*/
        }
    }

    if(Get_fram_num(&usart_trm))
    {
    
    
        /*调参模式*/
        /*传入指令*/
        x=usart_trm.c_vptr->fram_deal(&que,&usart_trm,"PID_D=");
        printf("x=%d\n",x);                                     
        x=usart_trm.c_vptr->fram_deal(&que,&usart_trm,"PID_D=");
        printf("x=%d\n",x);                                     
        x=0;
        x=usart_trm.c_vptr->fram_deal(&que,&usart_trm,"PID_I=");
        printf("x=%d\n",x);                                     
        x=0;    
        x=usart_trm.c_vptr->fram_deal(&que,&usart_trm,"PID_P=");
        printf("x=%d\n",x);                                     
        x=0;
        /*命令模式*/
        x=usart_trm.c_vptr->fram_deal(&que,&usart_trm,"SET");
        printf("x=%d\n",x);                                     
                     
    }
    system("pause");
}


operation result:

x=3330
x=0
x=2220
x=1110
x=1

Summarize

It is relatively convenient to use, and can also store relevant data, which can be accessed through function pointers.

Gitee link here:
serial port based finite state machine

I also wrote a similar article before, which introduced how to transplant it in STM32. This article is based on the custom serial port protocol of STM32 . This was written half a year ago, the code is a bit horrible, just look at how to transplant it.
Reference article:

State machine based on serial port design

Game Development State Machine Use

C language implements state machine

Guess you like

Origin blog.csdn.net/weixin_46185705/article/details/123961557