カスタム シリアル ポート プロトコル


序文

この記事では、有限ステート マシン循環キューを組み合わせて、安定した信頼性の高いカスタム シリアル ポート プロトコルを構築します. コードが少しわかりにくい場合は、直接移植して使用することができます. デモを使用するだけで、パッケージ化されています.それ。

Gitee リンクはこちら:
シリアル ポート ベースの有限ステート マシン

実装されている主な機能を簡単に説明します。

1.: コマンド モード。入力文字とバッファの内容を比較し、真または偽の値を返します。

2.: パラメータ調整モード、入力文字とバッファの内容を比較し、入力数値の値を返します。これは、パラメータのデバッグにさらに役立ちます. たとえば、PID のデバッグでは、PID_P=0.111 などのコマンドを直接入力すると、オンライン パラメータ調整に使用される入力パラメータの 1000 倍が返されます。


1.有限ステートマシン

有限状態マシンの場合、ここでは 4 つの単純な状態のみが使用され、状態間の遷移は下の図に従って行うことができます。受信したさまざまな文字タイプに応じて状態を切り替えます。

このステート マシンでは、ソース ステートはバッファ領域が受け入れを完了した空の状態であり、トリガー イベントは文字の受信であり (したがって、通常は判定のために割り込みに配置されます)、ガーディアン コンディションは次のタイプです。受信した文字とアクションの切り替え条件とバッファへの格納です。

下の図で、矢印は状態の切り替えを示し、矢印の横の文字は状態を切り替える条件です。図中、IDEL はデータ受信可能状態、HEAD はヘッドステータス受信済み、DATA はデータ受信ステータス(常にデータ受信可能)、TAIL はテールデータ受信済み、END は受信データが不正で受信エラーであることを示します。エラーデータはクリアです。

設計されたカスタム シリアル ポート プロトコルでは、フレーム ヘッダーとフレーム テールのみがあり、フレーム ヘッダーとフレーム テールの両方が初期化関数を介して独自に設計できます。
ここに画像の説明を挿入

有限ステート マシン コード

上の図によると、列挙を使用して 4 つの状態を設計すると、次のコードを設計できます。

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

受信データバッファ

データをより効率的に処理するために、受信した正しいフレームをバッファに格納するためにバッファが使用され、バッファは n 個の有効なフレームを格納できます。

バッファ要件

設計されたバッファは、先入れ先出しの形式にする必要があります。つまり、古い命令が最初に処理されます。キューをバッファとして使用することは要件に沿っています. キューはキューイングのようなものです. 最初に並んだ人が最初に処理され、後で参加した人が後で処理されます.

循環キュー

限られた MCU リソースでは、誰かがキューに参加して退出するときに、できるだけ操作が少ないことを願っています。ポジションを維持する。これにより、キューが移動し、全員のエネルギー (CPU リソース) が浪費されます。したがって、循環キューを使用することを選択し、キューをサイクルに形成し、現在キューに入れられている個人を示すカーソルを使用します。前の人が離れるとカーソルが次の人に移動し、後ろの人は移動する必要がありません。このように、キュー全体を移動することなく、カーソルを移動することで次の人が誰であるかを知ることができるため、CPU リソースの消費が削減されます。

1.循環キュー

循環キューはもっと厄介です. 具体的な実装原理を理解したい場合は、Baidu を参照するか、教科書を見つけて学習してください! これはオブジェクト指向アプローチに基づく循環キューです. 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;
}

2.有限状態マシンとデコード

有限状態マシン コアの実装コード

有限状態マシンの主なアイデアは、以下が実装コードであり、これは上記の写真のコード実装であり、写真と一緒に使用することをお勧めします。

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

}

有限状態マシンとデコード

デコードは主に、個人が開発しやすい 2 つの単純な小さな関数であり、コアの実装は、効果的な判断と処理のためにバッファーの内容を比較することです。

プロセスは次のとおりです。

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

3.小さな例を使用する

小さなコード例は次のとおりです

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