Initial commit: CCU621_M firmware project with BLE debug link support.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
#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 */
|
||||
|
||||
Reference in New Issue
Block a user