自定义串口协议


前言

此篇将结合有限状态机循环队列,构建一个稳定可靠的自定义串口协议,如果代码有点难懂,可以直接移植使用,看使用demo就好啦,我都封装好了。

Gitee链接在这里:
基于串口的有限状态机

简单提一下实现的主要功能:

一·:命令模式,根据输入字符与缓冲区的内容进行比对,返回真假值。

二·:调参模式,根据输入字符与缓冲区的内容进行比对,返回传入数字的值。这个用来调试参数还是比较好用的,比如调试PID就可以直接输入PID_P=0.111之类的命令,就返回输入参数的1000倍,用来在线调参。


一、有限状态机

有限状态机,这里仅仅使用简单的4个状态,状态之间的转换可以根据下图。根据接收到的不同的字符类型进行状态之间的转换。

在此状态机中的,源状态是缓存区完成一次接受的空状态;触发事件便是接收到一个字符(所以一般放在中断里面进行判断);监护条件便是接受到的字符的类型,动作便是切换条件与存入缓冲。

下图中:箭头表示状态之间的切换,箭头旁边的字母便是切换状态的条件。图中IDEL表示准备好接收数据;HEAD接收到头状态;DATA接收数据状态(可以一直接收数据);TAIL接收到尾数据,END表示,接收数据有误,清楚错误数据。

在设计的自定义串口协议中:仅仅只有帧头和帧尾,帧头和帧尾都可以自己通过初始化函数进行设计。
在这里插入图片描述

有限状态机代码

根据上图可以设计如下代码,使用枚举设计4种状态。

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

接收数据缓冲

为了更高效的处理数据,使用缓冲区,将接收到的正确的帧存入缓冲区,缓冲区可以存入n个有效帧。

缓冲要求

设计的缓冲区应该是先进先出的形式,也就是优先处理旧的指令。使用队列作为缓冲区是符合要求的,队列就像是我们排队一样,先排的人先处理,后加入的人后处理。

循环队列

在有限的单片机资源中,我们希望队列有人加入和出的时候,尽可能的少操作,也就是我们排队中不希望前面走了一个人,后面的人要跟上一个位置。这样会移动队列,浪费大家精力(CPU资源)。因此选择使用循环队列,将队列构成一个循环,利用一个游标,游标表示当前排到的个人。前面的人走了,游标便移动到下一个,后面的人不需要移动。这样移动游标就能知道下一个人是谁,不需要移动整个队列,减少了CPU的资源消耗。

一、循环队列

循环队列比较麻烦,想要了解具体的实现原理,请自己百度或者找教材来学习啦!这里就贴上基于面向对象的方式实现的循环队列,想要学习使用C语言实现面向对象,可以看我往期文章–>C语言实现面向对象

如果对void *指针还不是怎么了解的同学,可以看我往前文章–>C语言值Void *指针

代码实现

代码都挺好理解的(前提理解好队列和C语言实现面向对象的方式),就不写过多注释啦~

循环队列头文件:

#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);

源文件:

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;
}

二、有限状态机与解码

有限状态机核心实现代码

有限状态机,主要思想是下面的是实现代码,也就是上面提到的那图的的代码实现,配合图片是用更佳。

/*有限状态机读入缓存函数*/
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);/*数据入队列*/
    }

}

有限状态机与解码

解码主要是个人便于开发的两个简单的小功能,核心实现就是比对缓冲区的内容,进行有效的判断和处理。

处理过程如下:

  1. 串口接收有效数据进入队列缓冲区
  2. 主函数调用Frame_deal对传入的指令进行比对,如果缓冲区内没有相应的指令,则取出最找传入的一帧数据。有相应指令则进行处理。

头文件

头文件包含循环队列的头文件

#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

源文件

/*有限状态机初始化*/
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;
}

三、使用小例子

小例子代码如下

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");
}


运行结果:

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

总结

使用起来还是比较方便的,也能够存入相关数据,可以通过函数指针进行访问。

Gitee链接在这里:
基于串口的有限状态机

之前我也写过一篇类似的文章,里面有介绍大概怎么在STM32移植,这篇是基于STM32的自定义串口协议。这个是半年前写的,代码有点惨不忍睹,看看怎么移植就好了。
参考文章:

基于串口设计的状态机

游戏开发状态机使用

C语言实现状态机

猜你喜欢

转载自blog.csdn.net/weixin_46185705/article/details/123961557