157 lines
5.4 KiB
C
157 lines
5.4 KiB
C
/**
|
||
* @file qdx_protocol.c
|
||
* @brief Communication Protocol 2.0 Utility Implementations
|
||
*/
|
||
|
||
#include "qdx_protocol.h"
|
||
#include <string.h>
|
||
|
||
/* ============================================================
|
||
* Shift-Safe Serialization (Little-Endian)
|
||
* ============================================================ */
|
||
|
||
void qdx_write_u16_le(uint8_t *buf, uint16_t val) {
|
||
buf[0] = (uint8_t)(val & 0xFF);
|
||
buf[1] = (uint8_t)((val >> 8) & 0xFF);
|
||
}
|
||
|
||
void qdx_write_u32_le(uint8_t *buf, uint32_t val) {
|
||
buf[0] = (uint8_t)(val & 0xFF);
|
||
buf[1] = (uint8_t)((val >> 8) & 0xFF);
|
||
buf[2] = (uint8_t)((val >> 16) & 0xFF);
|
||
buf[3] = (uint8_t)((val >> 24) & 0xFF);
|
||
}
|
||
|
||
uint16_t qdx_read_u16_le(const uint8_t *buf) {
|
||
return (uint16_t)(buf[0] | (buf[1] << 8));
|
||
}
|
||
|
||
uint32_t qdx_read_u32_le(const uint8_t *buf) {
|
||
/* 显式转型防止高位字节的符号位扩展 */
|
||
return ((uint32_t)buf[0]) | ((uint32_t)buf[1] << 8) |
|
||
((uint32_t)buf[2] << 16) | ((uint32_t)buf[3] << 24);
|
||
}
|
||
|
||
/* ============================================================
|
||
* CRC16-MODBUS Checksum (Polynomial: 0xA001, Init: 0xFFFF)
|
||
* ============================================================ */
|
||
uint16_t qdx_crc16_modbus(const uint8_t *data, int len) {
|
||
uint16_t crc = 0xFFFF;
|
||
for (int i = 0; i < len; i++) {
|
||
crc ^= data[i];
|
||
for (int j = 0; j < 8; j++) {
|
||
if (crc & 0x0001) {
|
||
crc >>= 1;
|
||
crc ^= 0xA001;
|
||
} else {
|
||
crc >>= 1;
|
||
}
|
||
}
|
||
}
|
||
return crc;
|
||
}
|
||
|
||
/* ============================================================
|
||
* Build Frame (Header + TLV + Payload + CRC16)
|
||
* Uses strict byte shifting to prevent unaligned struct access on MCU.
|
||
* ============================================================ */
|
||
int qdx_build_frame(uint8_t *buf, uint8_t msg_class, uint8_t tlv_type,
|
||
const uint8_t *payload, uint16_t payload_len,
|
||
uint16_t dev_id, uint16_t seq, uint32_t timestamp,
|
||
uint8_t flags) {
|
||
|
||
flags |= FLAG_LAST_FRAGMENT;
|
||
uint16_t total_len = HEADER_SIZE + TLV_HEADER_SIZE + payload_len + CRC_SIZE;
|
||
|
||
/* 1. Fill FrameHeader (16 bytes) safely */
|
||
qdx_write_u16_le(buf + 0, PROTO_MAGIC); /* Magic */
|
||
buf[2] = PROTO_VERSION; /* Version */
|
||
qdx_write_u16_le(buf + 3, total_len); /* Length */
|
||
qdx_write_u16_le(buf + 5, seq); /* Sequence */
|
||
qdx_write_u32_le(buf + 7, timestamp); /* Timestamp */
|
||
buf[11] = 0x01; /* Source = MCU (0x01) */
|
||
qdx_write_u16_le(buf + 12, dev_id); /* DevID */
|
||
buf[14] = msg_class; /* Class */
|
||
buf[15] = flags; /* Flags */
|
||
|
||
/* 2. Fill TLV Header (3 bytes) safely */
|
||
buf[16] = tlv_type; /* TLV Type */
|
||
qdx_write_u16_le(buf + 17, payload_len); /* TLV Length */
|
||
|
||
/* 3. Copy Payload(使用 memmove 防止 payload 与 buf 重叠时的未定义行为) */
|
||
if (payload && payload_len > 0) {
|
||
memmove(buf + HEADER_SIZE + TLV_HEADER_SIZE, payload, payload_len);
|
||
}
|
||
|
||
/* 4. Append CRC16 */
|
||
uint16_t crc = qdx_crc16_modbus(buf, total_len - CRC_SIZE);
|
||
qdx_write_u16_le(buf + total_len - CRC_SIZE, crc);
|
||
|
||
return total_len;
|
||
}
|
||
|
||
/* ============================================================
|
||
* In-Place Frame Build (Header + CRC only, payload already in place)
|
||
* 用于零拷贝场景:payload(含 TLV + 数据)已填充在 buf[HEADER_SIZE] 处,
|
||
* 本函数仅写入 16 字节帧头并在尾部追加 CRC16。
|
||
* ============================================================ */
|
||
int qdx_build_frame_inplace(uint8_t *buf, uint8_t msg_class,
|
||
uint16_t payload_len, uint16_t dev_id, uint16_t seq,
|
||
uint32_t timestamp, uint8_t flags) {
|
||
|
||
flags |= FLAG_LAST_FRAGMENT;
|
||
uint16_t total_len = HEADER_SIZE + payload_len + CRC_SIZE;
|
||
|
||
/* 填写 16 字节帧头 */
|
||
qdx_write_u16_le(buf + 0, PROTO_MAGIC);
|
||
buf[2] = PROTO_VERSION;
|
||
qdx_write_u16_le(buf + 3, total_len);
|
||
qdx_write_u16_le(buf + 5, seq);
|
||
qdx_write_u32_le(buf + 7, timestamp);
|
||
buf[11] = 0x01; /* Source = MCU */
|
||
qdx_write_u16_le(buf + 12, dev_id);
|
||
buf[14] = msg_class;
|
||
buf[15] = flags;
|
||
|
||
/* payload 已在 buf[16..16+payload_len-1],无需拷贝 */
|
||
|
||
/* 追加 CRC16 */
|
||
uint16_t crc = qdx_crc16_modbus(buf, total_len - CRC_SIZE);
|
||
qdx_write_u16_le(buf + total_len - CRC_SIZE, crc);
|
||
|
||
return total_len;
|
||
}
|
||
|
||
/* ============================================================
|
||
* Build Fragmented Frame (Header + Chunk + CRC16, NO TLV)
|
||
* ============================================================ */
|
||
int qdx_build_fragment_frame(uint8_t *buf, uint8_t msg_class,
|
||
const uint8_t *chunk, uint16_t chunk_len,
|
||
uint16_t dev_id, uint16_t seq, uint32_t timestamp,
|
||
uint8_t flags) {
|
||
|
||
uint16_t total_len = HEADER_SIZE + chunk_len + CRC_SIZE;
|
||
|
||
/* 1. Fill FrameHeader (16 bytes) safely */
|
||
qdx_write_u16_le(buf + 0, PROTO_MAGIC);
|
||
buf[2] = PROTO_VERSION;
|
||
qdx_write_u16_le(buf + 3, total_len);
|
||
qdx_write_u16_le(buf + 5, seq);
|
||
qdx_write_u32_le(buf + 7, timestamp);
|
||
buf[11] = 0x01; /* Source MCU */
|
||
qdx_write_u16_le(buf + 12, dev_id);
|
||
buf[14] = msg_class;
|
||
buf[15] = flags;
|
||
|
||
/* 2. Copy Payload Chunk(使用 memmove 应对潜在重叠) */
|
||
if (chunk && chunk_len > 0) {
|
||
memmove(buf + HEADER_SIZE, chunk, chunk_len);
|
||
}
|
||
|
||
/* 3. Append CRC16 */
|
||
uint16_t crc = qdx_crc16_modbus(buf, total_len - CRC_SIZE);
|
||
qdx_write_u16_le(buf + total_len - CRC_SIZE, crc);
|
||
|
||
return total_len;
|
||
}
|