#include "pos_vtk/vtk_frame.h" #include "publicdata/public_define.h" #include #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 */