Initial commit: CCU621_M firmware project with BLE debug link support.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-08 17:28:36 +08:00
commit 9ceb218f80
1597 changed files with 724159 additions and 0 deletions
+241
View File
@@ -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 */