/*================================================================================================== * Project : S32K3_miniEvb * Platform : CORTEXM * Peripheral : S32K3XX * Dependencies : spi_protocol.h * * Autosar Version : 4.7.0 * Autosar Revision : ASR_REL_4_7_REV_0000 * Autosar Conf.Variant : * SW Version : 0.1.0 * Build Version : * * Copyright 2026 - 20xx HuaXu * * HuaXu Confidential. This software is owned or controlled by HuaXu and may only be * used strictly in accordance with the applicable license terms. By expressly * accepting such terms or by downloading, installing, activating and/or otherwise * using the software, you are agreeing that you have read, and that you agree to * comply with and are bound by, such license terms. If you do not agree to be * bound by the applicable license terms, then you may not retain, install, * activate or otherwise use the software. ==================================================================================================*/ /*================================================================================================== * File version history: * 0.1.0 initial version ==================================================================================================*/ /** * @file spi_protocol.c * @version 0.1.0 * @brief SPI Protocol v2.0 — 组包/解包实现 * * 帧结构 (Big Endian): * ┌──────┬──────┬──────┬────────┬──────────┬─────┬────────┬─────~──────┬───────┬──────┬──────────┐ * │ SOF │ Ver │FType │ Seq │Timestamp │PktCnt│TotalLen│ TLVs... │ CRC16 │ EOF │ 0x00... │ * │ 1B │ 1B │ 1B │ 2B │ 4B │ 1B │ 2B │ variable │ 2B │ 1B │ pad │ * └──────┴──────┴──────┴────────┴──────────┴─────┴────────┴─────~──────┴───────┴──────┴──────────┘ * ↑ ↑ * └──── CRC 计算范围 ───────────────┘ * (HEAD + TLVs, 不含 SOF/EOF/Padding) * * @addtogroup SPI * @{ */ /*================================================================================================== INCLUDE FILES ==================================================================================================*/ #include "spi_protocol.hpp" #include /*================================================================================================== LOCAL FUNCTION PROTOTYPES ==================================================================================================*/ static void SpiProtocol_WriteBe16(uint8_t *buf, uint16_t val); static void SpiProtocol_WriteBe32(uint8_t *buf, uint32_t val); static uint16_t SpiProtocol_ReadBe16(const uint8_t *buf); static uint32_t SpiProtocol_ReadBe32(const uint8_t *buf); /*================================================================================================== LOCAL FUNCTION DEFINITIONS ==================================================================================================*/ /** * @brief 写入 16 位大端值 */ static void SpiProtocol_WriteBe16(uint8_t *buf, uint16_t val) { buf[0] = (uint8_t)((val >> 8) & 0xFFu); buf[1] = (uint8_t)(val & 0xFFu); } /** * @brief 写入 32 位大端值 */ static void SpiProtocol_WriteBe32(uint8_t *buf, uint32_t val) { buf[0] = (uint8_t)((val >> 24) & 0xFFu); buf[1] = (uint8_t)((val >> 16) & 0xFFu); buf[2] = (uint8_t)((val >> 8) & 0xFFu); buf[3] = (uint8_t)(val & 0xFFu); } /** * @brief 读取 16 位大端值 */ static uint16_t SpiProtocol_ReadBe16(const uint8_t *buf) { return (uint16_t)(((uint16_t)buf[0] << 8) | (uint16_t)buf[1]); } /** * @brief 读取 32 位大端值 */ static uint32_t SpiProtocol_ReadBe32(const uint8_t *buf) { return ((uint32_t)buf[0] << 24) | ((uint32_t)buf[1] << 16) | ((uint32_t)buf[2] << 8) | (uint32_t)buf[3]; } /*================================================================================================== GLOBAL FUNCTION DEFINITIONS ==================================================================================================*/ /** * @brief CRC-16/XMODEM 查表(多项式 0x1021,初始值 0x0000,不取反) * @note 如需 CRC-16/CCITT(初始 0xFFFF),修改 crc 初值即可 */ uint16_t SpiProtocol_CalcCrc16(const uint8_t *data, uint16_t len) { static const uint16_t crc_table[256] = { 0x0000u, 0x1021u, 0x2042u, 0x3063u, 0x4084u, 0x50A5u, 0x60C6u, 0x70E7u, 0x8108u, 0x9129u, 0xA14Au, 0xB16Bu, 0xC18Cu, 0xD1ADu, 0xE1CEu, 0xF1EFu, 0x1231u, 0x0210u, 0x3273u, 0x2252u, 0x52B5u, 0x4294u, 0x72F7u, 0x62D6u, 0x9339u, 0x8318u, 0xB37Bu, 0xA35Au, 0xD3BDu, 0xC39Cu, 0xF3FFu, 0xE3DEu, 0x2462u, 0x3443u, 0x0420u, 0x1401u, 0x64E6u, 0x74C7u, 0x44A4u, 0x5485u, 0xA56Au, 0xB54Bu, 0x8528u, 0x9509u, 0xE5EEu, 0xF5CFu, 0xC5ACu, 0xD58Du, 0x3653u, 0x2672u, 0x1611u, 0x0630u, 0x76D7u, 0x66F6u, 0x5695u, 0x46B4u, 0xB75Bu, 0xA77Au, 0x9719u, 0x8738u, 0xF7DFu, 0xE7FEu, 0xD79Du, 0xC7BCu, 0x48C4u, 0x58E5u, 0x6886u, 0x78A7u, 0x0840u, 0x1861u, 0x2802u, 0x3823u, 0xC9CCu, 0xD9EDu, 0xE98Eu, 0xF9AFu, 0x8948u, 0x9969u, 0xA90Au, 0xB92Bu, 0x5AF5u, 0x4AD4u, 0x7AB7u, 0x6A96u, 0x1A71u, 0x0A50u, 0x3A33u, 0x2A12u, 0xDBFDu, 0xCBDCu, 0xFBBFu, 0xEB9Eu, 0x9B79u, 0x8B58u, 0xBB3Bu, 0xAB1Au, 0x6CA6u, 0x7C87u, 0x4CE4u, 0x5CC5u, 0x2C22u, 0x3C03u, 0x0C60u, 0x1C41u, 0xEDAEu, 0xFD8Fu, 0xCDECu, 0xDDCDu, 0xAD2Au, 0xBD0Bu, 0x8D68u, 0x9D49u, 0x7E97u, 0x6EB6u, 0x5ED5u, 0x4EF4u, 0x3E13u, 0x2E32u, 0x1E51u, 0x0E70u, 0xFF9Fu, 0xEFBEu, 0xDFDDu, 0xCFFCu, 0xBF1Bu, 0xAF3Au, 0x9F59u, 0x8F78u, 0x9188u, 0x81A9u, 0xB1CAu, 0xA1EBu, 0xD10Cu, 0xC12Du, 0xF14Eu, 0xE16Fu, 0x1080u, 0x00A1u, 0x30C2u, 0x20E3u, 0x5004u, 0x4025u, 0x7046u, 0x6067u, 0x83B9u, 0x9398u, 0xA3FBu, 0xB3DAu, 0xC33Du, 0xD31Cu, 0xE37Fu, 0xF35Eu, 0x02B1u, 0x1290u, 0x22F3u, 0x32D2u, 0x4235u, 0x5214u, 0x6277u, 0x7256u, 0xB5EAu, 0xA5CBu, 0x95A8u, 0x8589u, 0xF56Eu, 0xE54Fu, 0xD52Cu, 0xC50Du, 0x34E2u, 0x24C3u, 0x14A0u, 0x0481u, 0x7466u, 0x6447u, 0x5424u, 0x4405u, 0xA7DBu, 0xB7FAu, 0x8799u, 0x97B8u, 0xE75Fu, 0xF77Eu, 0xC71Du, 0xD73Cu, 0x26D3u, 0x36F2u, 0x0691u, 0x16B0u, 0x6657u, 0x7676u, 0x4615u, 0x5634u, 0xD94Cu, 0xC96Du, 0xF90Eu, 0xE92Fu, 0x99C8u, 0x89E9u, 0xB98Au, 0xA9ABu, 0x5844u, 0x4865u, 0x7806u, 0x6827u, 0x18C0u, 0x08E1u, 0x3882u, 0x28A3u, 0xCB7Du, 0xDB5Cu, 0xEB3Fu, 0xFB1Eu, 0x8BF9u, 0x9BD8u, 0xABBBu, 0xBB9Au, 0x4A75u, 0x5A54u, 0x6A37u, 0x7A16u, 0x0AF1u, 0x1AD0u, 0x2AB3u, 0x3A92u, 0xFD2Eu, 0xED0Fu, 0xDD6Cu, 0xCD4Du, 0xBDAAu, 0xAD8Bu, 0x9DE8u, 0x8DC9u, 0x7C26u, 0x6C07u, 0x5C64u, 0x4C45u, 0x3CA2u, 0x2C83u, 0x1CE0u, 0x0CC1u, 0xEF1Fu, 0xFF3Eu, 0xCF5Du, 0xDF7Cu, 0xAF9Bu, 0xBFBAu, 0x8FD9u, 0x9FF8u, 0x6E17u, 0x7E36u, 0x4E55u, 0x5E74u, 0x2E93u, 0x3EB2u, 0x0ED1u, 0x1EF0u }; uint16_t crc = 0x0000u; uint16_t i; for (i = 0u; i < len; i++) { crc = (uint16_t)((crc << 8) ^ crc_table[(uint8_t)((crc >> 8) ^ data[i])]); } return crc; } /*================================================================================================== * SpiProtocol_Pack — 组包 *==================================================================================================*/ int32_t SpiProtocol_Pack(uint8_t *frame, uint16_t frame_size, SpiProtocol_FrameType frame_type, uint16_t seq, uint32_t timestamp, const SpiProtocol_TlvType *tlvs, uint8_t tlv_count) { uint16_t total_len = 0u; uint16_t payload_bytes; uint16_t crc_offset; uint16_t crc_val; uint8_t i; uint16_t offset; /*———— 参数校验 ————*/ if ((frame == NULL) || ((tlvs == NULL) && (tlv_count > 0u))) { return SPI_PROTO_ERR_PARAM; } if ((frame_size != SPI_PROTO_FRAME_64) && (frame_size != SPI_PROTO_FRAME_128) && (frame_size != SPI_PROTO_FRAME_256) && (frame_size != SPI_PROTO_FRAME_512) && (frame_size != SPI_PROTO_FRAME_1024)) { return SPI_PROTO_ERR_SIZE; } if (tlv_count > SPI_PROTO_MAX_TLV_COUNT) { return SPI_PROTO_ERR_PKTCOUNT_OVF; } /* 计算 TotalLen(所有 TLV value 长度之和) */ for (i = 0u; i < tlv_count; i++) { total_len = (uint16_t)(total_len + tlvs[i].length); } /* Payload 总字节数 = PacketCount * 4 + TotalLen */ payload_bytes = (uint16_t)((uint16_t)tlv_count * SPI_PROTO_TLV_HEAD_LEN) + total_len; /* 检查是否超出帧容量 */ if (payload_bytes > (uint16_t)(frame_size - SPI_PROTO_OVERHEAD)) { return SPI_PROTO_ERR_TOTALLEN_OVF; } /*———— 写入帧 ————*/ (void)memset(frame, 0x00u, frame_size); /* SOF */ frame[0] = SPI_PROTO_SOF; /* HEAD(从偏移 1 开始) */ frame[1] = SPI_PROTO_VERSION; /* Version */ frame[2] = frame_type; /* FrameType */ SpiProtocol_WriteBe16(&frame[3], seq); /* Seq */ SpiProtocol_WriteBe32(&frame[5], timestamp); /* Timestamp */ frame[9] = tlv_count; /* PacketCount */ SpiProtocol_WriteBe16(&frame[10], total_len); /* TotalLen */ /* Payload — 逐 TLV 写入 */ offset = SPI_PROTO_OVERHEAD - 1u; /* 11 + 4 - 1 = offset of first TLV after SOF+HEAD = offset 11 */ /* 实际上 SOF(1) + HEAD(11) = 12 字节在 SOF+HEAD 区域,Payload 从 offset 11(0-based)开始 */ /* offset 11 = SOF(1) + HEAD(11) */ offset = 1u + SPI_PROTO_HEAD_LEN; /* = 12 */ for (i = 0u; i < tlv_count; i++) { frame[offset] = tlvs[i].service_id; frame[offset + 1u] = tlvs[i].method_id; SpiProtocol_WriteBe16(&frame[offset + 2u], tlvs[i].length); offset = (uint16_t)(offset + SPI_PROTO_TLV_HEAD_LEN); if (tlvs[i].length > 0u) { (void)memcpy(&frame[offset], tlvs[i].value, tlvs[i].length); offset = (uint16_t)(offset + tlvs[i].length); } } /* CRC16 — 计算 HEAD + Payload */ crc_offset = offset; /* offset 当前指向 CRC 写入位置 */ crc_val = SpiProtocol_CalcCrc16(&frame[1], (uint16_t)(crc_offset - 1u)); SpiProtocol_WriteBe16(&frame[crc_offset], crc_val); offset = (uint16_t)(crc_offset + 2u); /* EOF */ frame[offset] = SPI_PROTO_EOF; /* offset = (uint16_t)(offset + 1u); — padding 已在 memset 中填充 */ /* 返回帧长(= frame_size,已 memset 填充 0x00 至末尾) */ return (int32_t)frame_size; } /*================================================================================================== * SpiProtocol_Unpack — 解包 *==================================================================================================*/ int32_t SpiProtocol_Unpack(const uint8_t *frame, uint16_t frame_len, uint16_t frame_size, SpiProtocol_RxResultType *result) { uint16_t calc_crc; uint16_t recv_crc; uint16_t total_len; uint8_t packet_count; SpiProtocol_FrameType frame_type; uint16_t seq; uint32_t timestamp; uint16_t offset; uint16_t min_frame_len; uint16_t payload_bytes; uint16_t crc_pos; uint16_t eof_pos; uint8_t i; uint16_t t; /*———— 参数校验 ————*/ if ((frame == NULL) || (result == NULL)) { return SPI_PROTO_ERR_PARAM; } /* 帧至少要有 SOF + HEAD + CRC + EOF = 15 字节 */ min_frame_len = SPI_PROTO_OVERHEAD; if (frame_len < min_frame_len) { return SPI_PROTO_ERR_FRAME_SHORT; } /* SOF 校验 */ if (frame[0] != SPI_PROTO_SOF) { return SPI_PROTO_ERR_SOF_MISMATCH; } /* Version 校验 */ if (frame[1] != SPI_PROTO_VERSION) { return SPI_PROTO_ERR_VERSION_MISMATCH; } /* 解析 HEAD 字段 */ frame_type = (SpiProtocol_FrameType)frame[2]; seq = SpiProtocol_ReadBe16(&frame[3]); timestamp = SpiProtocol_ReadBe32(&frame[5]); packet_count = frame[9]; total_len = SpiProtocol_ReadBe16(&frame[10]); /* PacketCount 校验 */ if (packet_count > SPI_PROTO_MAX_TLV_COUNT) { return SPI_PROTO_ERR_PKTCOUNT_OVF; } /* 计算 Payload 字节数 */ payload_bytes = (uint16_t)((uint16_t)packet_count * SPI_PROTO_TLV_HEAD_LEN) + total_len; /* 计算 CRC / EOF 位置 */ crc_pos = (uint16_t)(1u + SPI_PROTO_HEAD_LEN + payload_bytes); eof_pos = (uint16_t)(crc_pos + 2u); /* 检查帧长度是否足够 */ if ((uint16_t)(eof_pos + 1u) > frame_len) { return SPI_PROTO_ERR_FRAME_SHORT; } /* EOF 校验 */ if (frame[eof_pos] != SPI_PROTO_EOF) { return SPI_PROTO_ERR_EOF_MISMATCH; } /* CRC 校验 */ recv_crc = SpiProtocol_ReadBe16(&frame[crc_pos]); calc_crc = SpiProtocol_CalcCrc16(&frame[1], crc_pos - 1u); if (recv_crc != calc_crc) { return SPI_PROTO_ERR_CRC_MISMATCH; } /* Padding 校验(EOF 之后到 frame_len 或 frame_size 必须是 0x00) */ { uint16_t pad_end = (frame_size > 0u) ? frame_size : frame_len; if (pad_end > (uint16_t)(eof_pos + 1u)) { for (t = (uint16_t)(eof_pos + 1u); t < pad_end; t++) { if (t < frame_len) { if (frame[t] != 0x00u) { return SPI_PROTO_ERR_PADDING_NZ; } } } } } /*———— 填充结果 ————*/ result->frame_type = frame_type; result->seq = seq; result->timestamp = timestamp; result->tlv_count = packet_count; result->nack_error_code = 0u; /* 解析 TLV */ offset = 1u + SPI_PROTO_HEAD_LEN; /* 跳过 SOF + HEAD */ for (i = 0u; i < packet_count; i++) { result->tlvs[i].service_id = frame[offset]; result->tlvs[i].method_id = frame[offset + 1u]; result->tlvs[i].length = SpiProtocol_ReadBe16(&frame[offset + 2u]); result->tlvs[i].value = &frame[offset + SPI_PROTO_TLV_HEAD_LEN]; offset = (uint16_t)(offset + SPI_PROTO_TLV_HEAD_LEN + result->tlvs[i].length); } /* 如果是 NACK 帧,提取错误码 */ if ((frame_type == SPI_PROTO_FRAME_NACK) && (packet_count == 1u)) { if ((result->tlvs[0].service_id == SPI_PROTO_SID_ERROR) && (result->tlvs[0].length >= 1u)) { result->nack_error_code = result->tlvs[0].value[0]; } } return (int32_t)packet_count; } /*================================================================================================== * SpiProtocol_PackAck — 构造 ACK 帧 *==================================================================================================*/ int32_t SpiProtocol_PackAck(uint8_t *frame, uint16_t frame_size, uint16_t ack_seq) { /* ACK: FrameType=ACK, PacketCount=0, TotalLen=0, 无 TLV */ return SpiProtocol_Pack(frame, frame_size, SPI_PROTO_FRAME_ACK, ack_seq, 0u, NULL, 0u); } /*================================================================================================== * SpiProtocol_PackNack — 构造 NACK 帧 *==================================================================================================*/ int32_t SpiProtocol_PackNack(uint8_t *frame, uint16_t frame_size, uint16_t ack_seq, uint8_t error_code) { SpiProtocol_TlvType tlv; tlv.service_id = SPI_PROTO_SID_ERROR; tlv.method_id = 0x00u; tlv.length = 1u; tlv.value = &error_code; return SpiProtocol_Pack(frame, frame_size, SPI_PROTO_FRAME_NACK, ack_seq, 0u, &tlv, 1u); } /*================================================================================================== * SpiProtocol_PackHeartbeat — 构造心跳帧 *==================================================================================================*/ int32_t SpiProtocol_PackHeartbeat(uint8_t *frame, uint16_t frame_size, uint16_t seq) { return SpiProtocol_Pack(frame, frame_size, SPI_PROTO_FRAME_HEARTBEAT, seq, 0u, NULL, 0u); } /** @} */