Files
SOMEIP/source/soa_server/spi_protocol.cpp

429 lines
17 KiB
C++
Executable File
Raw 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.
/*==================================================================================================
* 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 <string.h>
/*==================================================================================================
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 110-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);
}
/** @} */