0%

串口通信时自定义协议的数据包设计与解析方法

前言:

前面写了篇使用STM32的USB虚拟串口的笔记,既然通信口通了下面就要有数据交互,数据交互就要用到自定的协议,用于约束设备间的控制。比如:PC发一个数据包到STM32,STM32就要解析数据包,解析后根据协议约定执行相应的动作。直接贴代码就不作说明了,因为太简单。

代码如下:

   #include <string.h>
#include <stdio.h>
#include<stdint.h>

#pragma pack(1)

void app_get_version(int* idata, int ilen, int* odata, int* olen);
void app_start_read(int* idata, int ilen, int* odata, int* olen);
void app_handle_err(int* idata, int ilen, int* odata, int* olen);

typedef    enum {
    BOOT_CODE_OK        = 0x40,
    BOOT_CODE_RETURN    = 0xF0,
    BOOT_CODE_ERROR        = 0xF4
}packet_boot_code_e;

typedef    enum {
    NONE            =    0x00,
    CMD_GET_VER        =    0x02,
    CMD_START_READ    =    0x06
}packte_cmd_e;

typedef    struct {
    packet_boot_code_e    boot_code;
    int                    len;
    packte_cmd_e        cmd;
    int                    data[64];
    int                    check;
}packet_t;

typedef struct {
    packte_cmd_e    cmd;
    void(*pcallback)(int* idata, int ilen, int* odata, int* olen);
}packet_cmd_list_t;

packet_cmd_list_t app_cmds_list[] =
{
    {CMD_GET_VER,        app_get_version    },
    {CMD_START_READ,    app_start_read    },

    {NONE,                app_handle_err    }
};

void packet_cmds_parse(int* idata, int ilen, int* odata, int* olen)
{
    packet_cmd_list_t* pcmd_list = app_cmds_list;
    packet_t* pcmd = (packet_t*)idata;

    while ((pcmd_list->cmd!= pcmd->cmd)&& (pcmd_list->cmd!=0))
    {
        pcmd_list++;
    }
    // 查表结束,执行表中对应回调函数
    pcmd_list->pcallback( idata,  ilen,  odata,  olen);
}

//packet格式={boot len cmd data[] check}
void main(void)
{
    int txbuff[5] = { 0x40,0x03,0x02,0xBC,0xBD };
    int rxbuff[64];
    int len = 0;
    packet_cmds_parse(txbuff, 5, rxbuff, &len);
    printf("main\n");
}

void app_get_version(int* idata, int ilen, int* odata, int* olen)
{
    printf("ver\n");
}
void app_start_read(int* idata, int ilen, int* odata, int* olen)
{
    printf("read\n");
}
void app_handle_err(int* idata, int ilen, int* odata, int* olen)
{
    printf("err\n");
}

结束:

上面和下面的代码都在vs2019环境测试通过。

使用了查表方式–其实就是结构体数组,这样的方式比传统方式就是逻辑更简单,是不是看不到if…else和switch…case这样的语句?

补充:

贴下传统方式的方便与查表方式作对比:

#include <string.h>
#include <stdio.h>
#include<stdint.h>

#pragma pack(1)

typedef    enum {
    BOOT_CODE = 0x40,
    RETI_CODE = 0xF0,
    ERRO_CODE = 0xF4
}packet_boot_code_t;

typedef    enum {    
    VERSION    =    0x02,
    READ    =    0x06
}packte_cmd_t;

typedef    struct {
    packet_boot_code_t    boot_code;
    int    len;
    packte_cmd_t    cmd;
    int    data[64];
    int    check;
}packet_t;

void packet_parse(int *indata, int inlen,int *outdata, int  *outlen)
{
    packet_t* ppacket = (packet_t*)indata; 

    switch (ppacket->boot_code)
    {
        case BOOT_CODE:
            if (VERSION == ppacket->cmd)
            {
                //根据命令 do  something...
                ppacket->boot_code = BOOT_CODE;
                memcpy(outdata, indata, inlen*4);
                *outlen = inlen;
            }
        default:
            break;
    }
}
//packet格式={boot len cmd data[] check}
void main(void)
{
    int txbuff[5] = { 0x40,0x03,0x02,0xBC,0xbd };
    int rxbuff[64];
    int len=0;
    packet_parse(txbuff, 5, rxbuff, &len);        
}