2026-03-15 16:22:19 +08:00

157 lines
5.4 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @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;
}