9ceb218f80
Co-authored-by: Cursor <cursoragent@cursor.com>
242 lines
7.0 KiB
C
242 lines
7.0 KiB
C
#include "pos_vtk/vtk_frame.h"
|
|
#include "publicdata/public_define.h"
|
|
#include <string.h>
|
|
|
|
#if (POS_VTK_EN)
|
|
|
|
/******************************************************************************
|
|
* 模块说明:VTK 串口帧封装与解析
|
|
*
|
|
* 本文件负责:
|
|
* - 按 VTK 协议格式,将应用层 TLV 数据封装为串口帧(起始字节、Length、协议号、CRC16)。
|
|
* - 从串口接收的字节流中拼出完整帧,并做 CRC 校验,输出应用层数据指针与长度。
|
|
*
|
|
* 对外主要函数:
|
|
* - vtk_build_frame() :打包一帧 VTK 串口报文。
|
|
* - vtk_parse_frame() :在拿到完整帧后,做 CRC 校验并解析协议号和 App 区段。
|
|
* - vtk_frame_parser_init() :初始化流式解析器。
|
|
* - vtk_frame_parser_input() :逐字节喂入数据,自动拼出完整帧。
|
|
*
|
|
* 串口接收侧通常的用法是:
|
|
* 1. 在系统初始化时调用 vtk_frame_parser_init() 初始化解析器实例。
|
|
* 2. 在 UART 接收中断或接收任务中对每个字节调用 vtk_frame_parser_input()。
|
|
* 3. 当返回值为 1 时,说明解析器内部缓冲中已经形成一帧完整报文。
|
|
******************************************************************************/
|
|
|
|
/* CRC16-CCITT (0xFFFF) 计算 */
|
|
static U16_T vtk_crc16_ccitt(const U8_T *pData, U16_T len)
|
|
{
|
|
U16_T crc = 0xFFFF;
|
|
U16_T i;
|
|
|
|
while (len--)
|
|
{
|
|
crc ^= (U16_T)(*pData++) << 8;
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
if (crc & 0x8000)
|
|
crc = (U16_T)((crc << 1) ^ 0x1021);
|
|
else
|
|
crc <<= 1;
|
|
}
|
|
}
|
|
return crc;
|
|
}
|
|
|
|
U16_T vtk_build_frame(const U8_T *pApp, U16_T appLen, U16_T protoDisc,
|
|
U8_T *pOut, U16_T outMax)
|
|
{
|
|
U16_T lengthField;
|
|
U16_T totalLen;
|
|
U16_T crc;
|
|
|
|
if (!pOut || !pApp || appLen == 0)
|
|
return 0;
|
|
|
|
/* Length 字段 = Protocol Disc(2) + App 数据长度 */
|
|
lengthField = (U16_T)(2U + appLen);
|
|
totalLen = (U16_T)(1U + 2U + lengthField + 2U); /* Start + Len + Body + CRC */
|
|
|
|
if (totalLen > outMax || totalLen > VTK_MAX_FRAME_LEN)
|
|
return 0;
|
|
|
|
/* 组帧 */
|
|
pOut[0] = VTK_FRAME_START_BYTE;
|
|
pOut[1] = (U8_T)(lengthField >> 8);
|
|
pOut[2] = (U8_T)(lengthField & 0xFFU);
|
|
pOut[3] = (U8_T)(protoDisc >> 8);
|
|
pOut[4] = (U8_T)(protoDisc & 0xFFU);
|
|
|
|
(void)memcpy(&pOut[5], pApp, appLen);
|
|
|
|
/* CRC 从起始字节到应用数据最后一字节 */
|
|
crc = vtk_crc16_ccitt(pOut, (U16_T)(1U + 2U + 2U + appLen));
|
|
pOut[5U + appLen] = (U8_T)(crc >> 8);
|
|
pOut[6U + appLen] = (U8_T)(crc & 0xFFU);
|
|
|
|
return totalLen;
|
|
}
|
|
|
|
U8_T vtk_parse_frame(const U8_T *pFrame, U16_T frameLen, VTK_FRAME_INFO_T *pInfo)
|
|
{
|
|
U16_T lengthField;
|
|
U16_T expectedTotalLen;
|
|
U16_T crcCalc;
|
|
U16_T crcInFrame;
|
|
|
|
if (!pFrame || !pInfo || frameLen < 1U + 2U + 2U + 2U)
|
|
return 0;
|
|
|
|
if (pFrame[0] != VTK_FRAME_START_BYTE)
|
|
return 0;
|
|
|
|
lengthField = (U16_T)(((U16_T)pFrame[1] << 8) | pFrame[2]);
|
|
expectedTotalLen = (U16_T)(1U + 2U + lengthField + 2U);
|
|
|
|
if (frameLen != expectedTotalLen || frameLen > VTK_MAX_FRAME_LEN)
|
|
return 0;
|
|
|
|
/* CRC 校验 */
|
|
crcCalc = vtk_crc16_ccitt(pFrame, (U16_T)(frameLen - 2U));
|
|
crcInFrame = (U16_T)(((U16_T)pFrame[frameLen - 2U] << 8) |
|
|
pFrame[frameLen - 1U]);
|
|
if (crcCalc != crcInFrame)
|
|
return 0;
|
|
|
|
/* Protocol Disc 紧跟在 Length 字段后面 */
|
|
pInfo->protoDisc = (U16_T)(((U16_T)pFrame[3] << 8) | pFrame[4]);
|
|
pInfo->pApp = &pFrame[5];
|
|
pInfo->appLen = (U16_T)(lengthField - 2U); /* 去掉协议号 2 字节 */
|
|
|
|
return 1;
|
|
}
|
|
|
|
void vtk_frame_parser_init(VTK_FRAME_PARSER_T *parser)
|
|
{
|
|
if (!parser)
|
|
return;
|
|
|
|
parser->state = VTK_PARSE_WAIT_START;
|
|
parser->lengthField = 0;
|
|
parser->index = 0;
|
|
parser->totalLen = 0;
|
|
}
|
|
|
|
U8_T vtk_frame_parser_input(VTK_FRAME_PARSER_T *parser, U8_T byte,
|
|
const U8_T **ppFrame, U16_T *pLen)
|
|
{
|
|
if (!parser || !ppFrame || !pLen)
|
|
return 0;
|
|
|
|
*ppFrame = NULL;
|
|
*pLen = 0;
|
|
|
|
switch (parser->state)
|
|
{
|
|
case VTK_PARSE_WAIT_START:
|
|
if (byte == VTK_FRAME_START_BYTE)
|
|
{
|
|
parser->buf[0] = byte;
|
|
parser->index = 1U;
|
|
parser->state = VTK_PARSE_LEN_H;
|
|
}
|
|
break;
|
|
|
|
case VTK_PARSE_LEN_H:
|
|
parser->buf[parser->index++] = byte;
|
|
parser->state = VTK_PARSE_LEN_L;
|
|
break;
|
|
|
|
case VTK_PARSE_LEN_L:
|
|
parser->buf[parser->index++] = byte;
|
|
parser->lengthField = (U16_T)(((U16_T)parser->buf[1] << 8) |
|
|
parser->buf[2]);
|
|
parser->totalLen = (U16_T)(1U + 2U + parser->lengthField + 2U);
|
|
if (parser->totalLen > VTK_MAX_FRAME_LEN)
|
|
{
|
|
/* 长度异常,重新等待起始字节 */
|
|
parser->state = VTK_PARSE_WAIT_START;
|
|
parser->index = 0;
|
|
}
|
|
else
|
|
{
|
|
parser->state = VTK_PARSE_BODY;
|
|
}
|
|
break;
|
|
|
|
case VTK_PARSE_BODY:
|
|
parser->buf[parser->index++] = byte;
|
|
if (parser->index >= parser->totalLen)
|
|
{
|
|
VTK_FRAME_INFO_T info;
|
|
if (vtk_parse_frame(parser->buf, parser->totalLen, &info))
|
|
{
|
|
*ppFrame = parser->buf;
|
|
*pLen = parser->totalLen;
|
|
/* 完成一帧后重置状态等待下一帧 */
|
|
parser->state = VTK_PARSE_WAIT_START;
|
|
parser->index = 0;
|
|
return 1;
|
|
}
|
|
/* CRC 或其他错误,丢弃本帧,重新等待起始字节 */
|
|
parser->state = VTK_PARSE_WAIT_START;
|
|
parser->index = 0;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
parser->state = VTK_PARSE_WAIT_START;
|
|
parser->index = 0;
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* 外部使用示例:
|
|
*
|
|
* 1)发送一帧带 TLV 的 VTK 报文
|
|
*
|
|
* U8_T appBuf[VTK_MAX_APP_LEN];
|
|
* U16_T appLen = 0;
|
|
* U8_T frameBuf[VTK_MAX_FRAME_LEN];
|
|
* U16_T frameLen;
|
|
*
|
|
* // 此处省略 TLV 追加逻辑(建议使用 vtk_tlv_xxx 接口)。
|
|
* // appBuf/appLen 中存放 TLV 数据,例如 MessageName/OperationNo/Amount 等。
|
|
*
|
|
* frameLen = vtk_build_frame(appBuf, appLen, VTK_PD_VMC_TO_POS,
|
|
* frameBuf, sizeof(frameBuf));
|
|
* if (frameLen > 0U)
|
|
* {
|
|
* // 通过上层定义的 POS 串口发送宏发送出去
|
|
* POS_VTK_UART_SEND(frameBuf, frameLen);
|
|
* }
|
|
*
|
|
* 2)接收侧使用流式解析器:
|
|
*
|
|
* static VTK_FRAME_PARSER_T s_parser;
|
|
*
|
|
* void POS_Uart_Init(void)
|
|
* {
|
|
* vtk_frame_parser_init(&s_parser);
|
|
* }
|
|
*
|
|
* // 在 UART 接收中断 / 任务里调用
|
|
* void POS_Uart_Rx_Byte_Handler(U8_T rxByte)
|
|
* {
|
|
* const U8_T *pFrame;
|
|
* U16_T frameLen;
|
|
*
|
|
* if (vtk_frame_parser_input(&s_parser, rxByte, &pFrame, &frameLen))
|
|
* {
|
|
* // 这里获得完整帧 pFrame[0..frameLen-1],可继续调用 vtk_parse_frame
|
|
* // 或 vtk_parse_payment_response 进行业务解析。
|
|
* }
|
|
* }
|
|
*/
|
|
|
|
#endif /* POS_VTK_EN */
|
|
|