嵌入式通信协议框架的四层架构设计与实现

文章目录

  • 一、硬件抽象层:数据收发的基石
    • 1.1 设计要点
    • 1.2 代码示例
  • 二、协议管理层:智能路由中枢
    • 2.1 设计要点
    • 2.2 代码示例
  • 三、协议处理层:协议具体实现
    • 3.1 设计要求
    • 3.2代码示例
      • 3.2.1 协议公共定义
      • 3.2.2 协议一设计
      • 3.2.3 协议二设计
  • 四、应用层:业务逻辑实现
    • 4.1 设计要点
    • 4.2 代码示例
      • 4.2.1 协议一处理
      • 4.2.2 协议二处理
  • 五、四层协作流程
    • 5.1 收发流程
    • 5.2 代码示例
  • 总结


在嵌入式系统开发中,高效可靠的通信协议栈设计对系统稳定性至关重要。本文将深入剖析一种四层通信协议架构设计,从硬件抽象到应用处理逐层分解,帮助开发者构建灵活、可扩展的嵌入式通信系统。

下面我将完整展示每个层次的示例代码实现,并附上详细说明。这个架构已被多个工业级项目验证,能够处理复杂的通信需求。

一、硬件抽象层:数据收发的基石

硬件抽象层是与具体硬件打交道的底层,需要处理最原始的数据收发和缓冲管理。

1.1 设计要点

  1. 双缓冲设计:独立的发送和接收缓冲区避免数据竞争

  2. 环形队列:高效利用缓冲区空间,避免频繁内存分配

  3. 硬件封装:drv_custom_can_开头的函数是硬件驱动接口

  4. 状态管理:通过can_t结构体维护通信状态

  5. 错误隔离:硬件错误不会直接影响上层逻辑

1.2 代码示例

/* 缓冲区大小配置 */
#define CUSTOM_CAN_TX_BUFFER_SIZE 1024
#define CUSTOM_CAN_RX_BUFFER_SIZE 512/* 静态缓冲区分配 */
static uint8_t custom_can_tx_buf[CUSTOM_CAN_TX_BUFFER_SIZE];
static uint8_t custom_can_rx_buf[CUSTOM_CAN_RX_BUFFER_SIZE];/* CAN硬件信息结构体 */
typedef struct {uint8_t* tx_buf;        // 发送缓冲区指针uint8_t* rx_buf;        // 接收缓冲区指针uint16_t tx_buf_size;   // 发送缓冲区大小uint16_t rx_buf_size;   // 接收缓冲区大小bool tx_running;        // 发送状态标志bool can_error;         // 错误状态标志RingQueue rx_queue;     // 接收环形队列RingQueue tx_queue;     // 发送环形队列
} can_t;/* CAN硬件信息实例 */
static can_t custom_can_info = {.tx_buf = custom_can_tx_buf,.rx_buf = custom_can_rx_buf,.tx_buf_size = CUSTOM_CAN_TX_BUFFER_SIZE,.rx_buf_size = CUSTOM_CAN_RX_BUFFER_SIZE,.tx_running = false,.can_error = false,
};/* 获取CAN信息结构体指针 */
can_t* get_custom_can_info(void)
{return &custom_can_info;
}/* 硬件初始化函数 */
void custom_can_init(void)
{can_t* can_info = get_custom_can_info();/* 初始化收发环形队列 */init_queue(&can_info->rx_queue, can_info->rx_buf, can_info->rx_buf_size);init_queue(&can_info->tx_queue, can_info->tx_buf, can_info->tx_buf_size);/* 硬件特定初始化 */drv_custom_can_hw_init();/* 上层协议初始化 */protocol_manager_init();
}/* 清空接收队列 */
void custom_can_rx_queue_clear(void)
{can_t* can_info = get_custom_can_info();clear_queue(&can_info->rx_queue);
}/* 获取接收队列数据长度 */
uint16_t custom_can_rx_queue_len(void)
{can_t* can_info = get_custom_can_info();return queue_length(&can_info->rx_queue);
}/* 从接收队列读取数据 */
uint16_t custom_can_read(uint8_t* buf, uint16_t read_len)
{can_t* can_info = get_custom_can_info();return read_block_queue(buf, &can_info->rx_queue, read_len);
}/* 发送数据接口 */
uint16_t custom_can_puts(const uint8_t *src, uint16_t len)
{/* 调用硬件驱动发送 */return drv_custom_can_puts(src, len);
}

二、协议管理层:智能路由中枢

协议管理层是框架的核心枢纽,负责协议的识别和路由。

2.1 设计要点

  1. 插件架构:通过描述符动态注册协议

  2. 自动识别:基于特征值快速识别协议类型

  3. 统一接口:为上层提供一致的解析和构建接口

  4. 类型安全:使用强类型避免错误

  5. 可扩展性:易于添加新协议支持

2.2 代码示例

/* 最大支持的协议数量 */
#define MAX_PROTOCOLS 5/* 自动解析协议数据 */
ProtocolStatus parse_protocol_auto(const uint8_t* data, uint16_t length, ProtocolType* type, void* frame, size_t frame_size
);/* 协议1发送接口 */
ProtocolStatus send_protocol1(uint16_t src_id,uint16_t dest_id,uint8_t cmd_set,uint8_t cmd_id,const uint8_t* data,uint16_t data_len
);/* 协议2发送接口 */ 
ProtocolStatus send_protocol2(uint8_t src_id,uint8_t dest_id,uint8_t cmd_id,const uint8_t* data,uint16_t data_len
);/* 已注册协议数组 */
static const ProtocolDescriptor* registered_protocols[MAX_PROTOCOLS];
static int protocol_count = 0;/* 协议管理器初始化 */
void protocol_manager_init(void) 
{if (protocol_count == 0) {/* 注册协议1 */registered_protocols[protocol_count++] = &protocol1_descriptor;/* 注册协议2 */registered_protocols[protocol_count++] = &protocol2_descriptor;/* 可扩展注册更多协议... */}
}/* 协议自动识别 */
static ProtocolType detect_protocol(const uint8_t* data, uint16_t length) 
{if (length < 1) return PROTOCOL_UNKNOWN;/* 检查协议1特征 */if (data[0] == PROTOCOL1_HEADER) {return PROTOCOL_TYPE_1;} /* 检查协议2特征 */else if (length >= 2 && data[0] == PROTOCOL2_HEADER_1 && data[1] == PROTOCOL2_HEADER_2) {return PROTOCOL_TYPE_2;}return PROTOCOL_UNKNOWN;
}/* 获取协议描述符 */
static const ProtocolDescriptor* get_protocol(ProtocolType type) 
{for (int i = 0; i < protocol_count; i++) {if (registered_protocols[i]->type == type) {return registered_protocols[i];}}return NULL;
}/* 通用协议解析入口 */
ProtocolStatus parse_protocol_auto(const uint8_t* data, uint16_t length, ProtocolType* type, void* frame, size_t frame_size) 
{/* 1. 协议识别 */ProtocolType detected_type = detect_protocol(data, length);if (detected_type == PROTOCOL_UNKNOWN) {return PROTOCOL_ERR_TYPE;}/* 2. 获取协议描述符 */const ProtocolDescriptor* proto = get_protocol(detected_type);if (!proto || frame_size < proto->frame_size) {return PROTOCOL_ERR_TYPE;}/* 3. 调用具体协议解析 */*type = detected_type;return proto->parse(data, length, frame);
}/* 构建协议数据 */
static ProtocolStatus build_protocol(ProtocolType type, const void* frame, uint8_t* buffer, uint16_t buffer_size) 
{const ProtocolDescriptor* proto = get_protocol(type);if (!proto) return PROTOCOL_ERR_TYPE;return proto->build(frame, buffer, buffer_size);
}/* 协议解包处理 */
void custom_can_run_unpack(uint8_t *pdata, uint16_t pdata_len)
{ProtocolType type;union {Protocol1Frame frame1;Protocol2Frame frame2;} frame;/* 1. 自动解析协议 */ProtocolStatus status = parse_protocol_auto(pdata, pdata_len, &type, &frame, sizeof(frame));/* 2. 根据协议类型路由处理 */if (status == PROTOCOL_OK) {switch(type) {case PROTOCOL_TYPE_1:handle_protocol1_data(&frame.frame1);break;case PROTOCOL_TYPE_2:handle_protocol2_data(&frame.frame2);break;default:DEBUG_MSG("Unknown protocol type");break;}} else {DEBUG_MSG("Parse error: %d", status);}
}/* 协议1数据发送实现 */
ProtocolStatus send_protocol1(uint16_t src_id,uint16_t dest_id,uint8_t cmd_set,uint8_t cmd_id,const uint8_t* data,uint16_t data_len) 
{uint8_t send_buf[MAX_PROTOCOL_LEN];/* 构造协议1帧 */Protocol1Frame frame = {.src_id = src_id,.dest_id = dest_id,.cmd_set = cmd_set,.cmd_id = cmd_id,.data_len = data_len,.data = (uint8_t*)data};/* 构建协议1原始数据 */ProtocolStatus status = build_protocol(PROTOCOL_TYPE_1, &frame, send_buf, sizeof(send_buf));/* 通过硬件层发送 */if (status == PROTOCOL_OK) {uint16_t total_len = protocol1_descriptor.min_frame_len + data_len;custom_can_puts(send_buf, total_len);}return status;
}/* 协议2数据发送实现 */
ProtocolStatus send_protocol2(uint8_t src_id,uint8_t dest_id,uint8_t cmd_id,const uint8_t* data,uint16_t data_len) 
{uint8_t send_buf[MAX_PROTOCOL_LEN];/* 构造协议2帧 */Protocol2Frame frame = {.src_id = src_id,.dest_id = dest_id,.cmd_id = cmd_id,.data_len = data_len,.data = (uint8_t*)data};/* 构建协议2原始数据 */ProtocolStatus status = build_protocol(PROTOCOL_TYPE_2, &frame, send_buf, sizeof(send_buf));/* 通过硬件层发送 */if (status == PROTOCOL_OK) {uint16_t total_len = protocol2_descriptor.min_frame_len + data_len;custom_can_puts(send_buf, total_len);}return status;
}

三、协议处理层:协议具体实现

协议处理层包含各具体协议的实现细节(帧结构定义、解析函数、构建函数、校验机制)。

3.1 设计要求

  1. 协议独立性:每个协议完全独立实现
  2. 内存安全:严格检查缓冲区边界
  3. 校验机制:支持多种校验方式
  4. 零拷贝:直接引用原始数据减少拷贝
  5. 标准接口:统一通过描述符暴露功能

3.2代码示例

3.2.1 协议公共定义

/* 协议类型枚举 */
typedef enum {PROTOCOL_TYPE_1 = 1,   // 协议类型1PROTOCOL_TYPE_2 = 2,   // 协议类型2PROTOCOL_UNKNOWN = 0xFF
} ProtocolType;/* 协议处理状态 */
typedef enum {PROTOCOL_OK,            // 处理成功PROTOCOL_ERR_HEADER,    // 头部错误PROTOCOL_ERR_LENGTH,    // 长度错误PROTOCOL_ERR_CHECKSUM,  // 校验错误PROTOCOL_ERR_TYPE,      // 类型错误PROTOCOL_ERR_BUFFER     // 缓冲区不足
} ProtocolStatus;/* 协议解析函数指针 */
typedef ProtocolStatus (*ParseFunc)(const uint8_t*, uint16_t, void* frame);/* 协议构建函数指针 */
typedef ProtocolStatus (*BuildFunc)(const void* frame, uint8_t*, uint16_t);/* 协议描述符结构 */
typedef struct {ProtocolType type;       // 协议类型ParseFunc parse;         // 解析函数BuildFunc build;         // 构建函数uint16_t min_frame_len;  // 最小帧长度size_t frame_size;       // 帧结构体大小
} ProtocolDescriptor;#endif

3.2.2 协议一设计

/* 协议1定义 */
#define PROTOCOL1_HEADER 0x11
#define PROTOCOL1_MIN_LEN 11  // 最小帧长度/* 协议1帧结构 */
typedef struct {uint16_t src_id;       // 源地址uint16_t dest_id;      // 目的地址uint8_t cmd_set;       // 命令集uint8_t cmd_id;        // 命令IDuint16_t data_len;     // 数据长度uint8_t* data;         // 数据指针
} Protocol1Frame;/* 协议1描述符声明 */
extern const ProtocolDescriptor protocol1_descriptor;/* 计算校验和 */
static uint16_t calculate_checksum(const uint8_t* data, uint16_t len) {uint16_t sum = 0;for (uint16_t i = 0; i < len; i++) {sum += data[i];}return sum;
}/* 协议1解析实现 */
static ProtocolStatus parse_protocol1(const uint8_t* data, uint16_t length, void* frame) 
{Protocol1Frame* p1frame = (Protocol1Frame*)frame;/* 检查最小长度 */if (length < PROTOCOL1_MIN_LEN) {return PROTOCOL_ERR_LENGTH;}/* 检查帧头 */if (data[0] != PROTOCOL1_HEADER) {return PROTOCOL_ERR_HEADER;}/* 解析数据长度 */uint16_t data_len = (data[1] << 8) | data[2];/* 检查完整帧长度 */if (length < (PROTOCOL1_MIN_LEN + data_len)) {return PROTOCOL_ERR_LENGTH;}/* 填充帧结构 */p1frame->src_id = (data[3] << 8) | data[4];p1frame->dest_id = (data[5] << 8) | data[6];p1frame->cmd_set = data[7];p1frame->cmd_id = data[8];p1frame->data_len = data_len;p1frame->data = (uint8_t*)&data[9]; // 指向原始数据/* 校验和检查 */uint16_t checksum = (data[9+data_len] << 8) | data[10+data_len];if (checksum != calculate_checksum(&data[1], 8 + data_len)) {return PROTOCOL_ERR_CHECKSUM;}return PROTOCOL_OK;
}/* 协议1构建实现 */
static ProtocolStatus build_protocol1(const void* frame, uint8_t* buffer, uint16_t buffer_size) 
{const Protocol1Frame* p1frame = (const Protocol1Frame*)frame;uint16_t required_len = PROTOCOL1_MIN_LEN + p1frame->data_len;if (buffer_size < required_len) {return PROTOCOL_ERR_BUFFER;}/* 填充固定字段 */buffer[0] = PROTOCOL1_HEADER;buffer[1] = (p1frame->data_len >> 8) & 0xFF;buffer[2] = p1frame->data_len & 0xFF;buffer[3] = (p1frame->src_id >> 8) & 0xFF;buffer[4] = p1frame->src_id & 0xFF;buffer[5] = (p1frame->dest_id >> 8) & 0xFF;buffer[6] = p1frame->dest_id & 0xFF;buffer[7] = p1frame->cmd_set;buffer[8] = p1frame->cmd_id;/* 填充数据 */if (p1frame->data_len > 0) {memcpy(&buffer[9], p1frame->data, p1frame->data_len);}/* 计算校验和 */uint16_t checksum = calculate_checksum(&buffer[1], 8 + p1frame->data_len);buffer[9+p1frame->data_len] = (checksum >> 8) & 0xFF;buffer[10+p1frame->data_len] = checksum & 0xFF;return PROTOCOL_OK;
}/* 协议1描述符定义 */
const ProtocolDescriptor protocol1_descriptor = {PROTOCOL_TYPE_1,parse_protocol1,build_protocol1,PROTOCOL1_MIN_LEN,sizeof(Protocol1Frame)
};

3.2.3 协议二设计

/* 协议2定义 */
#define PROTOCOL2_HEADER_1 0x22
#define PROTOCOL2_HEADER_2 0x33
#define PROTOCOL2_MIN_LEN 9  // 最小帧长度/* 协议2帧结构 */
typedef struct {uint8_t src_id;        // 源地址uint8_t dest_id;       // 目的地址uint8_t cmd_id;        // 命令IDuint16_t data_len;     // 数据长度uint8_t* data;         // 数据指针
} Protocol2Frame;/* 协议2描述符声明 */
extern const ProtocolDescriptor protocol2_descriptor;
/* CRC16计算函数 */
static uint16_t calculate_crc16(const uint8_t* data, uint16_t len) {uint16_t crc = 0xFFFF;for (uint16_t i = 0; i < len; i++) {crc ^= data[i];for (uint8_t j = 0; j < 8; j++) {if (crc & 0x0001) {crc = (crc >> 1) ^ 0xA001;} else {crc >>= 1;}}}return crc;
}/* 协议2解析实现 */
static ProtocolStatus parse_protocol2(const uint8_t* data, uint16_t length, void* frame) 
{Protocol2Frame* p2frame = (Protocol2Frame*)frame;/* 检查最小长度 */if (length < PROTOCOL2_MIN_LEN) {return PROTOCOL_ERR_LENGTH;}/* 检查帧头 */if (data[0] != PROTOCOL2_HEADER_1 || data[1] != PROTOCOL2_HEADER_2) {return PROTOCOL_ERR_HEADER;}/* 解析数据长度 */uint16_t data_len = (data[2] << 8) | data[3];/* 检查完整帧长度 */if (length < (PROTOCOL2_MIN_LEN + data_len)) {return PROTOCOL_ERR_LENGTH;}/* 填充帧结构 */p2frame->src_id = data[4];p2frame->dest_id = data[5];p2frame->cmd_id = data[6];p2frame->data_len = data_len;p2frame->data = (uint8_t*)&data[7]; // 指向原始数据/* CRC校验检查 */uint16_t received_crc = (data[7+data_len] << 8) | data[8+data_len];uint16_t calculated_crc = calculate_crc16(&data[2], 5 + data_len);if (received_crc != calculated_crc) {return PROTOCOL_ERR_CHECKSUM;}return PROTOCOL_OK;
}/* 协议2构建实现 */
static ProtocolStatus build_protocol2(const void* frame, uint8_t* buffer, uint16_t buffer_size) 
{const Protocol2Frame* p2frame = (const Protocol2Frame*)frame;uint16_t required_len = PROTOCOL2_MIN_LEN + p2frame->data_len;if (buffer_size < required_len) {return PROTOCOL_ERR_BUFFER;}/* 填充固定字段 */buffer[0] = PROTOCOL2_HEADER_1;buffer[1] = PROTOCOL2_HEADER_2;buffer[2] = (p2frame->data_len >> 8) & 0xFF;buffer[3] = p2frame->data_len & 0xFF;buffer[4] = p2frame->src_id;buffer[5] = p2frame->dest_id;buffer[6] = p2frame->cmd_id;/* 填充数据 */if (p2frame->data_len > 0) {memcpy(&buffer[7], p2frame->data, p2frame->data_len);}/* 计算CRC */uint16_t crc = calculate_crc16(&buffer[2], 5 + p2frame->data_len);buffer[7+p2frame->data_len] = (crc >> 8) & 0xFF;buffer[8+p2frame->data_len] = crc & 0xFF;return PROTOCOL_OK;
}/* 协议2描述符定义 */
const ProtocolDescriptor protocol2_descriptor = {PROTOCOL_TYPE_2,parse_protocol2,build_protocol2,PROTOCOL2_MIN_LEN,sizeof(Protocol2Frame)
};

四、应用层:业务逻辑实现

应用层处理具体的业务逻辑,主要功能包括:命令处理、数据加工、业务决策、状态管理。

4.1 设计要点

  1. 表驱动编程:使用命令表简化路由逻辑
  2. 业务隔离:协议处理与业务逻辑分离
  3. 模块化设计:各命令处理函数独立
  4. 默认处理:提供未识别命令的默认处理
  5. 可测试性:独立于协议栈进行单元测试

4.2 代码示例

4.2.1 协议一处理

/* 命令处理函数类型 */
typedef void (*Protocol1CmdHandler)(const Protocol1Frame* frame);/* 命令集条目 */
typedef struct {uint8_t cmd_set;           // 命令集IDuint8_t cmd_id;            // 命令IDProtocol1CmdHandler handler; // 处理函数
} Protocol1CmdEntry;/**************** 命令处理表 ****************/
static const Protocol1CmdEntry protocol1_cmd_table[] = {// 系统命令集 (0x01){0x01, 0x01, handle_system_cmd_01},{0x01, 0x02, handle_system_cmd_02},// 数据采集命令集 (0x02){0x02, 0x10, handle_acq_cmd_10},{0x02, 0x20, handle_acq_cmd_20},
};/* 默认处理函数 */
static void default_protocol1_handler(const Protocol1Frame* frame) {DEBUG_MSG("Unhandled Protocol1 command: set=0x%02X, cmd=0x%02X", frame->cmd_set, frame->cmd_id);
}/* 协议1数据处理入口 */
void handle_protocol1_data(const Protocol1Frame* frame)
{const Protocol1CmdEntry* entry = NULL;size_t table_size = sizeof(protocol1_cmd_table) / sizeof(Protocol1CmdEntry);/* 查找匹配的命令处理函数 */for (size_t i = 0; i < table_size; i++) {if (protocol1_cmd_table[i].cmd_set == frame->cmd_set && protocol1_cmd_table[i].cmd_id == frame->cmd_id) {entry = &protocol1_cmd_table[i];break;}}/* 调用处理函数 */if (entry) {entry->handler(frame);} else {default_protocol1_handler(frame);}
}/**************** 具体命令处理实现 ****************/
static void handle_system_cmd_01(const Protocol1Frame* frame) {DEBUG_MSG("Processing System Command 01 from 0x%04X", frame->src_id);/* 实际业务处理... */
}static void handle_system_cmd_02(const Protocol1Frame* frame) {DEBUG_MSG("Processing System Command 02 with %d bytes data", frame->data_len);/* 实际业务处理... */
}static void handle_acq_cmd_10(const Protocol1Frame* frame) {DEBUG_MSG("Processing Acquisition Command 10");/* 实际业务处理... */
}static void handle_acq_cmd_20(const Protocol1Frame* frame) {DEBUG_MSG("Processing Acquisition Command 20");/* 实际业务处理... */
}

4.2.2 协议二处理

/* 命令处理函数类型 */
typedef void (*Protocol2CmdHandler)(const Protocol2Frame* frame);/* 命令集条目 */
typedef struct {uint8_t cmd_id;            // 命令IDProtocol2CmdHandler handler; // 处理函数
} Protocol2CmdEntry;/**************** 命令处理表 ****************/
static const Protocol2CmdEntry protocol2_cmd_table[] = {{0x10, handle_status_query},{0x20, handle_parameter_set},{0x30, handle_other_command},
};/* 默认处理函数 */
static void default_protocol2_handler(const Protocol2Frame* frame) {DEBUG_MSG("Unhandled Protocol2 command: 0x%02X", frame->cmd_id);
}/* 协议2数据处理入口 */
void handle_protocol2_data(const Protocol2Frame* frame)
{const Protocol2CmdEntry* entry = NULL;size_t table_size = sizeof(protocol2_cmd_table) / sizeof(Protocol2CmdEntry);/* 查找匹配的命令处理函数 */for (size_t i = 0; i < table_size; i++) {if (protocol2_cmd_table[i].cmd_id == frame->cmd_id) {entry = &protocol2_cmd_table[i];break;}}/* 调用处理函数 */if (entry) {entry->handler(frame);} else {default_protocol2_handler(frame);}
}/**************** 具体命令处理实现 ****************/
static void handle_status_query(const Protocol2Frame* frame) {DEBUG_MSG("Status query from device %d", frame->src_id);/* 构造响应数据并发送... */
}static void handle_parameter_set(const Protocol2Frame* frame) {DEBUG_MSG("Parameter set from device %d", frame->src_id);/* 参数处理逻辑... */
}static void handle_other_command(const Protocol2Frame* frame) {DEBUG_MSG("Processing command 0x%02X", frame->cmd_id);/* 其他命令处理... */
}

五、四层协作流程

5.1 收发流程

接收流程:

  • 硬件抽象层接收原始数据存入环形缓冲区

  • 协议管理层从缓冲区取出数据并识别协议类型

  • 协议处理层解析数据为结构化信息

  • 应用层处理具体业务逻辑

发送流程:

  • 应用层准备业务数据

  • 协议处理层打包为协议帧

  • 协议管理层选择适当发送接口

  • 硬件抽象层将数据发送至物理介质

在这里插入图片描述

5.2 代码示例

/* 系统初始化 */
void system_init(void)
{// 1. 硬件抽象层初始化custom_can_init();// 2. 应用层初始化app_init();
}/* 主任务循环 */
void main_task(void)
{uint8_t raw_data[256];while(1) {// 1. 从硬件层读取数据uint16_t len = custom_can_read(raw_data, sizeof(raw_data));if(len > 0) {// 2. 协议管理层处理custom_can_run_unpack(raw_data, len);}// 3. 应用层处理app_process();// 4. 休眠或等待事件delay_ms(10);}
}/* 应用层发送示例 */
void send_heartbeat(void)
{uint8_t hb_data[4] = {0x01, 0x02, 0x03, 0x04};// 使用协议1发送心跳ProtocolStatus status = send_protocol1(LOCAL_DEVICE_ID, MASTER_DEVICE_ID,CMD_SET_SYSTEM,CMD_HEARTBEAT,hb_data,sizeof(hb_data));if(status != PROTOCOL_OK) {handle_send_error(status);}
}

总结

这个四层通信协议架构具有以下优势:

  1. 清晰的层次划分:各层职责明确,便于维护

  2. 高度可扩展:轻松添加新协议和新命令

  3. 强类型安全:减少运行时错误

  4. 高效处理:优化数据流转路径

  5. 平台无关:硬件抽象层隔离硬件差异

实际项目中可根据需求调整各层实现,如增加加密层、优化缓冲区管理等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.tpcf.cn/web/84939.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RA信号处理

ra_snr_gui.m 作用&#xff1a;统计不同信噪比下&#xff0c;五种信号的峰值旁瓣比RA和低高频均值比RM&#xff0c;绘制结果&#xff0c;参考图3.11和3.12 DFCW_RA_SNR.m 作用&#xff1a;产生正交离散频率编码信号&#xff0c;并计算峰值旁瓣比RA和低高频均值比 RM LFM_RA_S…

【go的测试】单测之gomock包与gomonkey包

目录 使用gomock包 1. 安装mockgen 2. 定义接口 3. 生成mock文件 4. 在单测中使用mock的函数 5. gomock 包的使用问题 使用gomonkey包 1. mock 一个包函数 2. mock 一个公有成员函数 3. mock 一个私有成员函数 使用gomock包 1. 安装mockgen go get -u github.com/go…

html实现登录与注册功能案例(不写死且只使用js)

目录 案例需求 实现思路 代码参考 login.html register.html 运行效果 升级思路 案例需求 需要一个登录界面和注册页面实现一个较为完整的登录注册功能 1.登录界面没有登录限制需求&#xff08;降低难度&#xff09;&#xff0c;实现基本的登录判断需求&#xff0c;弹窗…

PHP is the best language.

PHP很好写。 众所周知Python很好写&#xff0c;Python 也能开发 Web 应用&#xff0c;但和 PHP 相比&#xff0c;在“直接处理网页”这件事上&#xff0c;PHP 更加贴近底层和原生。 想快速搭建原型或者 B 端后台工具&#xff0c;不妨用 PHP Laravel 来搞&#xff0c;真的很香…

Mybatis-Plus 在 getOne() 的时候要加上 .last(“limit 1“)

1.先写结论: 1.为了确保 SQL 查询只返回一条记录&#xff08;当查询返回多条时会报错->多为代码本身问题&#xff09;。 2.防止数据库执行全表扫描 3.参考网址&#xff1a;问题记录&#xff1a;MyBatis-Plus 中 ServiceImpl 类的 getOne_mybatis_无他&唯手熟尔-2048…

C语言:二分搜索函数

一、二分搜索基本概念 二分搜索&#xff08;Binary Search&#xff09;是一种在有序数组中查找特定元素的高效算法&#xff0c;时间复杂度为O(log n)。 基本特点&#xff1a; 仅适用于有序数组&#xff08;升序或降序&#xff09; 每次比较将搜索范围减半 比线性搜索(O(n))…

[前端AI]LangChain.js 和 Next.js LLM构建——协助博客撰写和总结助手

LangChain.js 和 Next.js LLM 后端应用于协助博客撰写和总结领域是一个非常实用的方向&#xff01;这涉及到理解和处理文本内容&#xff0c;并生成新的、有结构的信息。 根据您之前提供的代码和需求&#xff0c;我们可以在此基础上进行更具针对性的功能规划和技术实现。 博客…

用 GitHub Issues 做任务管理和任务 List,简单好用!

说实话&#xff0c;我平时也是一个人写代码&#xff0c;每次开完会整理任务最麻烦&#xff1a; 一堆事项堆在聊天里、文档里&#xff0c;或者散落在邮件里…… 为了理清这些&#xff0c;我通常会做一份 List&#xff0c;标好优先级&#xff0c;再安排到每日的工作里 虽然这个…

每日算法刷题Day35 6.22:leetcode枚举技巧枚举中间2道题,用时1h

枚举中间 对于三个或者四个变量的问题&#xff0c;枚举中间的变量往往更好算。 为什么&#xff1f;比如问题有三个下标&#xff0c;需要满足 0≤i<j<k<n&#xff0c;对比一下&#xff1a; 枚举 i&#xff0c;后续计算中还需保证 j<k。 枚举 j&#xff0c;那么 i 和…

【教学类-18-06】20250623蒙德里安黑白七款合并WORD(500张、无学号)

背景需要 客户买了蒙德里安黑白格子7种尺寸,但是不需要学号方块,并指定要WORD 设计思路 【教学类-18-05】20241118正方形手工纸(蒙德里安-风格派-红黄蓝黑白)-CSDN博客文章浏览阅读1.3k次,点赞29次,收藏18次。【教学类-18-05】20241118正方形手工纸(蒙德里安-风格派-红…

langchain--(4)

7 Embedding文本向量化 Embedding文本向量化是一种将非结构化文本转化为低维、连续数值向量的技术,旨在通过数学方式捕捉文本的语义、语法或特征信息,从而让机器更高效地处理语言任务。其核心思想源于流形假设(Manifold Hypothesis),即认为高维原始数据(如文本)实际隐含…

DMDRS部署实施手册(ORACLE=》DM)

DMDRS部署实施手册&#xff08;ORACLE》DM&#xff09; 1 同步说明2 DMDRS安装3 数据库准备3.1 源端准备3.1.1 开启归档日志和附加日志3.1.2 关闭回收站3.1.3 创建同步用户 3.2 目标准备3.2.1 创建同步用户 4 DMDRS配置4.1 源端配置4.2 目标配置 5 DMDRS启动5.1 启动源端服务5.…

十(1)作业:sqli-labs重点关卡

参考文章&#xff1a;详细sqli-labs&#xff08;1-65&#xff09;通关讲解-CSDN博客 第1关&#xff1a; 输入 &#xff1a; ?id3 输入 &#xff1a; ?id2 当输入的数字不同&#xff0c;页面的响应也不同&#xff0c;说明&#xff0c;输入的内容被带入到数据库里查询了 输…

Python 爬虫入门 Day 7 - 复盘 + 实战挑战日

Python 第二阶段 - 爬虫入门 &#x1f3af; 本周知识回顾 网络请求与网页结构基础 HTML解析入门&#xff08;使用 BeautifulSoup&#xff09; 实现爬虫多页抓取与翻页逻辑 模拟登录爬虫与 Session 维持 使用 XPath 进行网页解析&#xff08;lxml XPath&#xff09; 反爬虫应对…

WebRTC(七):媒体能力协商

目的 在 WebRTC 中&#xff0c;每个浏览器或终端支持的音视频编解码器、分辨率、码率、帧率等可能不同。媒体能力协商的目的就是&#xff1a; 确保双方能“听得懂”对方发的媒体流&#xff1b;明确谁发送、谁接收、怎么发送&#xff1b;保障连接的互操作性和兼容性。 P2P的基…

可信启动方案设计

安全之安全(security)博客目录导读 目录 一、引言 二、关键数据(Critical Data) 三、度量槽(Measurement Slot) 四、可信启动后端 1、事件日志(Event Log) 2、离散型 TPM(Discrete TPM) 3、RSE(运行时安全引擎) 五、平台接口 平台接口的职责: 1、函数:b…

✨通义万相2.1深度解析:AI视频生成引擎FLF2V-14B全流程指南(命令行参数+模型架构+数据流)

&#x1f31f; 从零详解&#xff1a;如何用AI模型生成视频&#xff1f;命令行、模型结构、数据流全解析&#xff01; 本文通过一个实际案例&#xff0c;详细解析使用AI模型生成视频的整个流程。从命令行参数解读到模型结构&#xff0c;再到数据在模型间的流动&#xff0c;一步步…

在 TypeScript 前端中使用 Umi-Request 调用 Java 接口的完整指南

下面我将详细介绍如何在基于 TypeScript 的前端项目中使用 umi-request 调用 IntelliJ IDEA 中开发的 Java 接口&#xff0c;包括完整的实现方案和代码示例。 整体方案设计 一、Java 后端接口准备 1. 创建 Spring Boot 控制器 // src/main/java/com/example/demo/controller…

GO Gin Web框架面试题及参考答案

目录 Gin 与 net/http 有哪些主要区别?为什么选择 Gin? 如何使用 Gin 启动一个 HTTP 服务并设置默认路由? Gin 的默认路由和自定义路由器组是如何工作的? 如何在 Gin 中绑定请求参数(Query、Form、JSON、XML)? 如何在 Gin 中使用中间件?中间件执行顺序是怎样的? …

asp.net core Razor动态语言编程代替asp.net .aspx更高级吗?

For Each item In products<tr><td>item.Id</td><td>item.Name</td><td>item.Price.ToString("C")</td></tr>Next为什么要用<tr> ? 在Blazor的Razor语法中&#xff0c;使用<tr>是为了在VB.NET代码块中…