/******************************************************************************* * File Name : PythonLink.c * Description : Python通信链路模块实现 * Created on : 2025-11-13 * Author : Auto-generated *******************************************************************************/ /******************************************************************************* * Include Files * *******************************************************************************/ #include "PythonLink.h" #include "Uart.h" #include "HIDO_Debug.h" #include #include /******************************************************************************* * Global Variables * *******************************************************************************/ // UART 缓冲区 static HIDO_UINT8 g_au8PythonLinkTxFrameBuf[PYTHONLINK_TX_BUF_SIZE]; // 用于组帧 static HIDO_UINT8 g_au8PythonLinkTxQueueBuf[PYTHONLINK_TX_BUF_SIZE]; // 提供给UART FIFO static HIDO_UINT8 g_au8PythonLinkRxBuf[PYTHONLINK_RX_BUF_SIZE]; typedef struct { HIDO_UINT32 m_u32LastPollTick; HIDO_UINT32 m_u32LastDataTick; HIDO_UINT32 m_u32TotalBytes; HIDO_UINT32 m_u32ParsedFrames; HIDO_UINT32 m_u32HandleFailCount; HIDO_UINT32 m_u32DmaNullCount; HIDO_UINT32 m_u32NoDataWarningTick; HIDO_UINT32 m_u32LastLogTick; } ST_PythonLink_RxDebug; static ST_PythonLink_RxDebug g_stPythonLinkRxDebug; // 统计计数 static HIDO_UINT32 g_u32GPSPacketCount = 0; static HIDO_UINT32 g_u32IMUPacketCount = 0; static HIDO_UINT32 g_u32ErrorCount = 0; static HIDO_UINT32 g_u32ControlRxCount = 0; // 控制命令缓存 static volatile HIDO_UINT16 g_u16LatestSteeringPWM = 1500; // 中位值 static volatile HIDO_UINT16 g_u16LatestThrottlePWM = 1500; // 中位值 static volatile HIDO_UINT32 g_u32ControlTimestamp = 0; // 命令时间戳 static volatile HIDO_BOOL g_bControlValid = HIDO_FALSE; // 命令有效标志 // DMA接收位置跟踪 static HIDO_UINT16 g_u16LastDMAPos = 0; /******************************************************************************* * Local Function * *******************************************************************************/ /** * @brief 计算16位累加校验和 (Checksum_u16) * @param _pu8Data: 数据指针 * @param _u32Len: 数据长度 * @return 16位校验和 */ static HIDO_UINT16 PythonLink_CalcChecksum(const HIDO_UINT8 *_pu8Data, HIDO_UINT32 _u32Len) { HIDO_UINT32 sum = 0; HIDO_UINT32 i; // 16位累加和 for (i = 0; i < _u32Len; i++) { sum += _pu8Data[i]; } // 返回低16位 return (HIDO_UINT16)(sum & 0xFFFF); } #if (PYTHONLINK_FORCE_FIXED_PAYLOAD != 0) /** * @brief 调试: 生成固定内容的负载数据,便于观测串口是否乱码 * @param _pu8Dst 负载存放位置 * @param _u16Len 负载长度 * @param _u8Type 数据类型, 用于区分不同包 */ static HIDO_VOID PythonLink_DebugFillFixedPayload(HIDO_UINT8 *_pu8Dst, HIDO_UINT16 _u16Len, HIDO_UINT8 _u8Type) { static const HIDO_UINT8 s_au8TagGPS[] = "GPSDBG"; static const HIDO_UINT8 s_au8TagIMU[] = "IMUDBG"; static const HIDO_UINT8 s_au8TagCTRL[] = "CTRDBG"; const HIDO_UINT8 *pTag = HIDO_NULL; HIDO_UINT8 tagLen = 0; HIDO_UINT16 i; switch (_u8Type) { case PYTHONLINK_TYPE_GPS: pTag = s_au8TagGPS; tagLen = sizeof(s_au8TagGPS) - 1; break; case PYTHONLINK_TYPE_IMU: pTag = s_au8TagIMU; tagLen = sizeof(s_au8TagIMU) - 1; break; case PYTHONLINK_TYPE_CONTROL: pTag = s_au8TagCTRL; tagLen = sizeof(s_au8TagCTRL) - 1; break; default: tagLen = 0; break; } for (i = 0; i < _u16Len; i++) { if (i < tagLen && pTag != HIDO_NULL) { _pu8Dst[i] = pTag[i]; } else { _pu8Dst[i] = (HIDO_UINT8)((_u8Type << 4) + (i & 0x0F)); } } } #endif /** * @brief 解析并处理接收到的控制命令帧 * @param _pu8Data: 帧数据指针 (从帧头开始) * @param _u16Len: 数据长度 * @return HIDO_OK: 解析成功, HIDO_ERR: 失败 */ static HIDO_INT32 PythonLink_ParseControlFrame(const HIDO_UINT8 *_pu8Data, HIDO_UINT16 _u16Len) { ST_PythonLink_FrameHeader *pHeader; ST_PythonLink_Control *pControl; HIDO_UINT16 checksum_received, checksum_calc; HIDO_UINT16 payload_len; // 检查最小长度: 帧头(5) + 负载(4) + 校验和(2) + 帧尾(2) = 13字节 if (_u16Len < 13) { return HIDO_ERR; } // 解析帧头 pHeader = (ST_PythonLink_FrameHeader *)_pu8Data; // 检查帧头 if (pHeader->m_u8Header1 != PYTHONLINK_FRAME_HEADER1 || pHeader->m_u8Header2 != PYTHONLINK_FRAME_HEADER2) { return HIDO_ERR; } // 检查类型 if (pHeader->m_u8Type != PYTHONLINK_TYPE_CONTROL) { return HIDO_ERR; } // 检查负载长度 payload_len = pHeader->m_u16Length; if (payload_len != sizeof(ST_PythonLink_Control)) { return HIDO_ERR; } // 检查总长度 HIDO_UINT16 expected_len = sizeof(ST_PythonLink_FrameHeader) + payload_len + sizeof(ST_PythonLink_FrameFooter); if (_u16Len < expected_len) { return HIDO_ERR; } // 校验Checksum (从类型字节开始,跳过帧头AA 55) // 校验范围: Type + Length + Payload checksum_calc = PythonLink_CalcChecksum(_pu8Data + 2, sizeof(ST_PythonLink_FrameHeader) - 2 + payload_len); checksum_received = *((HIDO_UINT16 *)(_pu8Data + sizeof(ST_PythonLink_FrameHeader) + payload_len)); if (checksum_received != checksum_calc) { g_u32ErrorCount++; return HIDO_ERR; } // 检查帧尾 HIDO_UINT8 footer1 = _pu8Data[sizeof(ST_PythonLink_FrameHeader) + payload_len + 2]; HIDO_UINT8 footer2 = _pu8Data[sizeof(ST_PythonLink_FrameHeader) + payload_len + 3]; if (footer1 != PYTHONLINK_FRAME_FOOTER1 || footer2 != PYTHONLINK_FRAME_FOOTER2) { g_u32ErrorCount++; return HIDO_ERR; } // 解析控制命令 pControl = (ST_PythonLink_Control *)(_pu8Data + sizeof(ST_PythonLink_FrameHeader)); // 限制PWM范围并更新全局变量 g_u16LatestSteeringPWM = (pControl->m_u16SteeringPWM < 1000) ? 1000 : (pControl->m_u16SteeringPWM > 2000) ? 2000 : pControl->m_u16SteeringPWM; g_u16LatestThrottlePWM = (pControl->m_u16ThrottlePWM < 1000) ? 1000 : (pControl->m_u16ThrottlePWM > 2000) ? 2000 : pControl->m_u16ThrottlePWM; g_u32ControlTimestamp = HAL_GetTick(); g_bControlValid = HIDO_TRUE; g_u32ControlRxCount++; return HIDO_OK; } /** * @brief 在DMA接收缓冲区中查找并解析控制命令帧 * @return 解析成功的帧数量 */ static HIDO_UINT32 PythonLink_ProcessRxBuffer(HIDO_VOID) { UART_HandleTypeDef *pUart = NULL; HIDO_UINT16 dma_pos, available_bytes; HIDO_UINT32 parsed_count = 0; HIDO_INT32 ret; // 获取UART句柄 HIDO_UINT32 tick_now = HAL_GetTick(); g_stPythonLinkRxDebug.m_u32LastPollTick = tick_now; ret = Uart_GetHandle(UART_ID_PYTHON, (HIDO_VOID **)&pUart); if (ret != HIDO_OK || pUart == NULL) { g_stPythonLinkRxDebug.m_u32HandleFailCount++; return 0; } if (pUart->hdmarx == NULL) { g_stPythonLinkRxDebug.m_u32DmaNullCount++; return 0; } // 获取DMA当前写入位置 dma_pos = PYTHONLINK_RX_BUF_SIZE - __HAL_DMA_GET_COUNTER(pUart->hdmarx); // 计算可读字节数 if (dma_pos >= g_u16LastDMAPos) { available_bytes = dma_pos - g_u16LastDMAPos; } else { // DMA循环缓冲区回绕 available_bytes = PYTHONLINK_RX_BUF_SIZE - g_u16LastDMAPos + dma_pos; } // 如果没有新数据,直接返回 if (available_bytes == 0) { if ((tick_now - g_stPythonLinkRxDebug.m_u32LastDataTick) > 1000 && (tick_now - g_stPythonLinkRxDebug.m_u32NoDataWarningTick) > 1000) { g_stPythonLinkRxDebug.m_u32NoDataWarningTick = tick_now; } return 0; } g_stPythonLinkRxDebug.m_u32LastDataTick = tick_now; g_stPythonLinkRxDebug.m_u32TotalBytes += available_bytes; // 查找帧头并解析 HIDO_UINT16 search_pos = g_u16LastDMAPos; while (available_bytes >= 13) // 最小帧长度 { // 查找帧头 AA 55 HIDO_UINT8 byte1 = g_au8PythonLinkRxBuf[search_pos]; HIDO_UINT8 byte2 = g_au8PythonLinkRxBuf[(search_pos + 1) % PYTHONLINK_RX_BUF_SIZE]; if (byte1 == PYTHONLINK_FRAME_HEADER1 && byte2 == PYTHONLINK_FRAME_HEADER2) { // 找到帧头,尝试解析 HIDO_UINT8 temp_buf[64]; // 临时缓冲区 HIDO_UINT16 copy_len = (available_bytes > 64) ? 64 : available_bytes; // 从循环缓冲区拷贝数据到临时缓冲区 for (HIDO_UINT16 i = 0; i < copy_len; i++) { temp_buf[i] = g_au8PythonLinkRxBuf[(search_pos + i) % PYTHONLINK_RX_BUF_SIZE]; } // 尝试解析 if (PythonLink_ParseControlFrame(temp_buf, copy_len) == HIDO_OK) { // 解析成功,跳过这个帧 HIDO_UINT16 frame_len = 13; // 控制帧固定长度 search_pos = (search_pos + frame_len) % PYTHONLINK_RX_BUF_SIZE; available_bytes -= frame_len; parsed_count++; g_stPythonLinkRxDebug.m_u32ParsedFrames++; } else { // 解析失败,跳过一个字节继续搜索 search_pos = (search_pos + 1) % PYTHONLINK_RX_BUF_SIZE; available_bytes--; } } else { // 不是帧头,继续搜索 search_pos = (search_pos + 1) % PYTHONLINK_RX_BUF_SIZE; available_bytes--; } } // 更新读取位置 g_u16LastDMAPos = search_pos; return parsed_count; } /** * @brief 发送数据帧 * @param _u8Type: 数据类型 (GPS/IMU) * @param _pu8Payload: 数据负载指针 * @param _u16PayloadLen: 负载长度 * @return HIDO_OK: 成功, HIDO_ERR: 失败 */ static HIDO_INT32 PythonLink_SendFrame(HIDO_UINT8 _u8Type, const HIDO_UINT8 *_pu8Payload, HIDO_UINT16 _u16PayloadLen) { HIDO_UINT32 frameLen = 0; HIDO_UINT16 crc = 0; ST_PythonLink_FrameHeader header; ST_PythonLink_FrameFooter footer; // 检查参数 if (_pu8Payload == NULL || _u16PayloadLen == 0) { g_u32ErrorCount++; return HIDO_ERR; } // 检查缓冲区空间 if ((sizeof(header) + _u16PayloadLen + sizeof(footer)) > PYTHONLINK_TX_BUF_SIZE) { g_u32ErrorCount++; return HIDO_ERR; } // 构造帧头 header.m_u8Header1 = PYTHONLINK_FRAME_HEADER1; header.m_u8Header2 = PYTHONLINK_FRAME_HEADER2; header.m_u8Type = _u8Type; header.m_u16Length = _u16PayloadLen; // 拷贝帧头到发送缓冲区 memcpy(&g_au8PythonLinkTxFrameBuf[frameLen], &header, sizeof(header)); frameLen += sizeof(header); // 拷贝数据负载 #if (PYTHONLINK_FORCE_FIXED_PAYLOAD != 0) PythonLink_DebugFillFixedPayload(&g_au8PythonLinkTxFrameBuf[frameLen], _u16PayloadLen, _u8Type); #else memcpy(&g_au8PythonLinkTxFrameBuf[frameLen], _pu8Payload, _u16PayloadLen); #endif frameLen += _u16PayloadLen; // 计算Checksum (从类型字节开始,跳过帧头AA 55) // 校验范围: Type + Length + Payload crc = PythonLink_CalcChecksum(&g_au8PythonLinkTxFrameBuf[2], frameLen - 2); // 构造帧尾 footer.m_u16CRC = crc; footer.m_u8Footer1 = PYTHONLINK_FRAME_FOOTER1; footer.m_u8Footer2 = PYTHONLINK_FRAME_FOOTER2; // 拷贝帧尾 memcpy(&g_au8PythonLinkTxFrameBuf[frameLen], &footer, sizeof(footer)); frameLen += sizeof(footer); // 通过UART发送 if (Uart_Send(UART_ID_PYTHON, g_au8PythonLinkTxFrameBuf, frameLen) != HIDO_OK) { g_u32ErrorCount++; return HIDO_ERR; } return HIDO_OK; } /******************************************************************************* * Global Function * *******************************************************************************/ /** * @brief 初始化 PythonLink 模块 */ HIDO_INT32 PythonLink_Init(HIDO_VOID) { ST_UartInit stUartInit; UART_HandleTypeDef *pUart = NULL; HIDO_INT32 ret; // 检查 UART 是否已注册 ret = Uart_GetHandle(UART_ID_PYTHON, (HIDO_VOID **)&pUart); if (ret != HIDO_OK || pUart == NULL) { HIDO_Debug2("[PythonLink] UART_ID_PYTHON not registered!\r\n"); return HIDO_ERR; } // 清空缓冲区 memset(g_au8PythonLinkTxFrameBuf, 0, sizeof(g_au8PythonLinkTxFrameBuf)); memset(g_au8PythonLinkTxQueueBuf, 0, sizeof(g_au8PythonLinkTxQueueBuf)); memset(g_au8PythonLinkRxBuf, 0, sizeof(g_au8PythonLinkRxBuf)); // 配置 UART (DMA 模式) stUartInit.m_eTxMode = UART_TX_MODE_DMA; stUartInit.m_pu8TxBuf = g_au8PythonLinkTxQueueBuf; stUartInit.m_u32TxBufSize = PYTHONLINK_TX_BUF_SIZE; stUartInit.m_u32TxQueueMemberCnt = 16; // 发送队列深度 (增大以缓冲更多 DMA 事务) stUartInit.m_eRxMode = UART_RX_MODE_DMA; stUartInit.m_pu8RxBuf = g_au8PythonLinkRxBuf; stUartInit.m_u32RxBufSize = PYTHONLINK_RX_BUF_SIZE; stUartInit.m_fnRxISR = NULL; ret = Uart_Init(UART_ID_PYTHON, &stUartInit); if (ret != HIDO_OK) { HIDO_Debug2("[PythonLink] UART init failed!\r\n"); return HIDO_ERR; } // 重置统计计数 g_u32GPSPacketCount = 0; g_u32IMUPacketCount = 0; g_u32ErrorCount = 0; HIDO_Debug2("[PythonLink] Init success (UART5, 921600 bps, DMA)\r\n"); return HIDO_OK; } /** * @brief PythonLink 轮询函数 */ HIDO_VOID PythonLink_Poll(HIDO_VOID) { // 处理接收缓冲区中的控制命令 PythonLink_ProcessRxBuffer(); // 检查控制命令超时 (1秒无新命令则标记为无效) if (g_bControlValid == HIDO_TRUE) { HIDO_UINT32 elapsed = HAL_GetTick() - g_u32ControlTimestamp; if (elapsed > 1000) { g_bControlValid = HIDO_FALSE; // 超时后恢复到中位值 g_u16LatestSteeringPWM = 1500; g_u16LatestThrottlePWM = 1500; } } } /** * @brief 发送GPS数据包 */ HIDO_INT32 PythonLink_SendGPSData(const ST_GPRMI *_pstGPRMI) { ST_PythonLink_GPS gpsPacket; HIDO_INT32 ret; // 检查参数 if (_pstGPRMI == NULL || _pstGPRMI->m_bValid == HIDO_FALSE) { return HIDO_ERR; } // 填充GPS数据包 memset(&gpsPacket, 0, sizeof(gpsPacket)); gpsPacket.m_dLatitude = _pstGPRMI->m_dLatitude; gpsPacket.m_dLongitude = _pstGPRMI->m_dLongitude; // 默认使用GPRMI提供的航向角, 若不可用则回退到速度计算 HIDO_FLOAT heading = _pstGPRMI->m_fHeadingAngle; if (!isfinite(heading)) { heading = 0.0f; } if ((!isfinite(heading) || heading == 0.0f) && (_pstGPRMI->m_fEastVelocity != 0.0f || _pstGPRMI->m_fNorthVelocity != 0.0f)) { // atan2(东,北) 得到相对正北方向的角度 heading = atan2f(_pstGPRMI->m_fEastVelocity, _pstGPRMI->m_fNorthVelocity) * 180.0f / 3.14159265f; if (heading < 0.0f) { heading += 360.0f; } } gpsPacket.m_fHeadingAngle = heading; gpsPacket.m_fPitchAngle = _pstGPRMI->m_fPitchAngle; gpsPacket.m_fRollAngle = _pstGPRMI->m_fRollAngle; if (!isfinite(gpsPacket.m_fPitchAngle)) { gpsPacket.m_fPitchAngle = 0.0f; } if (!isfinite(gpsPacket.m_fRollAngle)) { gpsPacket.m_fRollAngle = 0.0f; } gpsPacket.m_fEastVelocity = _pstGPRMI->m_fEastVelocity; gpsPacket.m_fNorthVelocity = _pstGPRMI->m_fNorthVelocity; gpsPacket.m_fUpVelocity = _pstGPRMI->m_fUpVelocity; gpsPacket.m_fAltitude = _pstGPRMI->m_fAltitude; gpsPacket.m_u32UTCTime = _pstGPRMI->m_u32UTCTime; gpsPacket.m_u8PositionQuality = _pstGPRMI->m_u8PositionQuality; gpsPacket.m_u8SatelliteCount = _pstGPRMI->m_u8SatelliteCount; // 发送数据帧 ret = PythonLink_SendFrame(PYTHONLINK_TYPE_GPS, (const HIDO_UINT8 *)&gpsPacket, sizeof(gpsPacket)); if (ret == HIDO_OK) { g_u32GPSPacketCount++; } return ret; } /** * @brief 发送IMU数据包 */ HIDO_INT32 PythonLink_SendIMUData(const ST_GPIMU *_pstGPIMU) { ST_PythonLink_IMU imuPacket; HIDO_INT32 ret; // 检查参数 if (_pstGPIMU == NULL || _pstGPIMU->m_bValid == HIDO_FALSE) { return HIDO_ERR; } // 填充IMU数据包 memset(&imuPacket, 0, sizeof(imuPacket)); imuPacket.m_fAccelX = _pstGPIMU->m_fAccelX; imuPacket.m_fAccelY = _pstGPIMU->m_fAccelY; imuPacket.m_fAccelZ = _pstGPIMU->m_fAccelZ; imuPacket.m_fGyroX = _pstGPIMU->m_fGyroX; imuPacket.m_fGyroY = _pstGPIMU->m_fGyroY; imuPacket.m_fGyroZ = _pstGPIMU->m_fGyroZ; imuPacket.m_fTemperature = _pstGPIMU->m_fTemperature; imuPacket.m_u32UTCTime = _pstGPIMU->m_u32UTCTime; // 发送数据帧 ret = PythonLink_SendFrame(PYTHONLINK_TYPE_IMU, (const HIDO_UINT8 *)&imuPacket, sizeof(imuPacket)); if (ret == HIDO_OK) { g_u32IMUPacketCount++; } return ret; } /** * @brief 获取统计信息 */ HIDO_VOID PythonLink_GetStats(HIDO_UINT32 *_pu32GPSCount, HIDO_UINT32 *_pu32IMUCount, HIDO_UINT32 *_pu32ErrorCount) { if (_pu32GPSCount != NULL) { *_pu32GPSCount = g_u32GPSPacketCount; } if (_pu32IMUCount != NULL) { *_pu32IMUCount = g_u32IMUPacketCount; } if (_pu32ErrorCount != NULL) { *_pu32ErrorCount = g_u32ErrorCount; } } /** * @brief 打印调试信息 */ HIDO_VOID PythonLink_PrintDebugInfo(HIDO_VOID) { HIDO_Debug2("\r\n========== PythonLink Debug Info ==========\r\n"); HIDO_Debug2("GPS packets sent: %u\r\n", g_u32GPSPacketCount); HIDO_Debug2("IMU packets sent: %u\r\n", g_u32IMUPacketCount); HIDO_Debug2("Control RX count: %u\r\n", g_u32ControlRxCount); HIDO_Debug2("Error count: %u\r\n", g_u32ErrorCount); HIDO_Debug2("RX total bytes: %u\r\n", g_stPythonLinkRxDebug.m_u32TotalBytes); HIDO_Debug2("RX parsed frames: %u\r\n", g_stPythonLinkRxDebug.m_u32ParsedFrames); HIDO_Debug2("RX last data age: %u ms\r\n", HAL_GetTick() - g_stPythonLinkRxDebug.m_u32LastDataTick); HIDO_Debug2("RX handle fails: %u\r\n", g_stPythonLinkRxDebug.m_u32HandleFailCount); HIDO_Debug2("RX DMA null count: %u\r\n", g_stPythonLinkRxDebug.m_u32DmaNullCount); HIDO_Debug2("Control valid: %s\r\n", g_bControlValid ? "YES" : "NO"); if (g_bControlValid) { HIDO_Debug2(" Steering PWM: %u us\r\n", g_u16LatestSteeringPWM); HIDO_Debug2(" Throttle PWM: %u us\r\n", g_u16LatestThrottlePWM); HIDO_Debug2(" Age: %u ms\r\n", HAL_GetTick() - g_u32ControlTimestamp); } HIDO_Debug2("===========================================\r\n\r\n"); } HIDO_VOID PythonLink_PrintRxDebug(HIDO_VOID) { HIDO_Debug2("\r\n----- PythonLink RX Debug Dump -----\r\n"); HIDO_Debug2("Last poll tick: %u\r\n", g_stPythonLinkRxDebug.m_u32LastPollTick); HIDO_Debug2("Last data tick: %u\r\n", g_stPythonLinkRxDebug.m_u32LastDataTick); HIDO_Debug2("Total bytes: %u\r\n", g_stPythonLinkRxDebug.m_u32TotalBytes); HIDO_Debug2("Parsed frames: %u\r\n", g_stPythonLinkRxDebug.m_u32ParsedFrames); HIDO_Debug2("Handle fail count: %u\r\n", g_stPythonLinkRxDebug.m_u32HandleFailCount); HIDO_Debug2("DMA null count: %u\r\n", g_stPythonLinkRxDebug.m_u32DmaNullCount); HIDO_Debug2("Last log tick: %u\r\n", g_stPythonLinkRxDebug.m_u32LastLogTick); HIDO_Debug2("---------------------------------------\r\n\r\n"); } /** * @brief 获取最新的控制命令 */ HIDO_BOOL PythonLink_GetControl(HIDO_UINT16 *_pu16SteeringPWM, HIDO_UINT16 *_pu16ThrottlePWM, HIDO_UINT32 *_pu32Timestamp) { if (g_bControlValid == HIDO_FALSE) { return HIDO_FALSE; } if (_pu16SteeringPWM != NULL) { *_pu16SteeringPWM = g_u16LatestSteeringPWM; } if (_pu16ThrottlePWM != NULL) { *_pu16ThrottlePWM = g_u16LatestThrottlePWM; } if (_pu32Timestamp != NULL) { *_pu32Timestamp = g_u32ControlTimestamp; } return HIDO_TRUE; }