#include "serial_control.h" #include "board.h" #include "debug.h" #include "isr_opt.h" #include "serial.h" #include "usart.h" // Приватные функции ISR_FAST static uint32_t calculate_crc32(const uint8_t* data, uint16_t length); ISR_FAST static uint16_t encode_packet(const uint8_t* payload, uint16_t payload_len, uint8_t* output, uint8_t response_code); ISR_FAST static uint8_t parse_packet(const uint8_t* packet_data, uint16_t packet_len, ReceivedCommand_t* out_cmd); ISR_FAST static uint8_t process_received_packet(SerialControl_t *ctx, const uint8_t* packet_data, uint16_t packet_len); static void SC_UART2_Watchdog(void); static void SC_ArmUart2RxDma(void); static void SC_ArmUart5RxDma(void); static void SC_LogUartError(const char *tag, UART_HandleTypeDef *huart); uint8_t test_crc_invalid = 0; SerialControl_t serial_control; // Контекст для приема пакетов по UART5 (однонаправленный UART) static SerialControl_t serial_iso; volatile SC_Source_t g_sc_command_source = SC_SOURCE_UART2; static volatile uint8_t sc_uart2_timed_out = 0; static uint32_t sc_uart2_last_packet_tick = 0; static uint32_t sc_uart2_last_recover_tick = 0; #define SC_UART2_RECOVER_GUARD_MS 200u #define SC_UART2_PACKET_TIMEOUT_MS 5000u StatusPacket_t statusPacket = { .SOC = 0, .Energy = 0, .RequestedVoltage = 0, .RequestedCurrent = 0, .MeasuredVoltage = 0, .MeasuredCurrent = 0, .outputEnabled = 0, .chargingError = 0, .connState = 0, .chargingElapsedTimeMin = 0, .chargingElapsedTimeSec = 0, .estimatedRemainingChargingTime = 0, .relayAC = 0, .relayDC = 0, .relayAUX = 0, .lockState = 0, .evInfoAvailable = 0, .psuOnline = 0, .tempConnector0 = 0, .tempConnector1 = 0, .tempAmbient = 0, .tempBatteryMax = 0, .tempBatteryMin = 0, .highestVoltageOfBatteryCell = 0, .batteryStatus = 0, .phaseVoltageAB = 0, .phaseVoltageBC = 0, .phaseVoltageCA = 0, }; InfoPacket_t infoPacket = { .serialNumber = 0, .boardVersion = 0, .stationType = 0, .fw_version_major = 0, .fw_version_minor = 0, .fw_version_patch = 0, }; void ReadVersion(){ infoPacket.serialNumber = InfoBlock->serialNumber; infoPacket.boardVersion = InfoBlock->boardVersion; infoPacket.stationType = InfoBlock->stationType; infoPacket.fw_version_major = FW_VERSION_MAJOR; infoPacket.fw_version_minor = FW_VERSION_MINOR; infoPacket.fw_version_patch = FW_VERSION_PATCH; } // Внешняя функция обработки команд (определена в serial_handler.c) extern void SC_CommandHandler(ReceivedCommand_t* cmd); void SC_Init() { // Обнуляем структуру memset(&serial_control, 0, sizeof(SerialControl_t)); memset(&serial_iso, 0, sizeof(serial_iso)); sc_uart2_timed_out = 0; sc_uart2_last_packet_tick = HAL_GetTick(); sc_uart2_last_recover_tick = sc_uart2_last_packet_tick; SC_ArmUart2RxDma(); SC_ArmUart5RxDma(); } void SC_Task() { static uint32_t tick; if ((int32_t)(HAL_GetTick() - tick) < 1) return; tick = HAL_GetTick(); SC_UART2_Watchdog(); // Запуск приема в режиме DMA + idle SC_ArmUart2RxDma(); SC_ArmUart5RxDma(); // Проверка таймаута отправки пакета (больше 100 мс) if (huart2.gState == HAL_UART_STATE_BUSY_TX && serial_control.tx_tick != 0) { if ((int32_t)(HAL_GetTick() - serial_control.tx_tick) > 100) { // Таймаут: принудительно сбрасываем передачу (void)HAL_UART_AbortTransmit(&huart2); serial_control.tx_tick = 0; // Сбрасываем tick } } // Проверка наличия принятой команды для обработки if (serial_control.command_ready && (huart2.gState != HAL_UART_STATE_BUSY_TX)) { // HAL_Delay(2); SC_CommandHandler((ReceivedCommand_t*)&serial_control.received_command); serial_control.command_ready = 0; // Сбрасываем флаг SC_ArmUart2RxDma(); } } ISR_FAST void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { if (huart->Instance == huart2.Instance) { if (Size == 0u) { log_printf(LOG_WARN, "UART2 RX idle event with zero size\n"); } sc_uart2_last_packet_tick = HAL_GetTick(); sc_uart2_last_recover_tick = sc_uart2_last_packet_tick; sc_uart2_timed_out = 0; if(!process_received_packet(&serial_control, serial_control.rx_buffer, Size)){ log_printf(LOG_WARN, "UART2 RX invalid packet len=%u\n", (unsigned)Size); SC_SendPacket(NULL, 0, RESP_INVALID); } g_sc_command_source = SC_SOURCE_UART2; SC_ArmUart2RxDma(); } else if (huart->Instance == huart5.Instance) { if (Size == 0u) { log_printf(LOG_WARN, "UART5 RX idle event with zero size\n"); } if (process_received_packet(&serial_iso, serial_iso.rx_buffer, Size)) { g_sc_command_source = SC_SOURCE_UART5; SC_CommandHandler((ReceivedCommand_t*)&serial_iso.received_command); } else { log_printf(LOG_WARN, "UART5 RX invalid packet len=%u\n", (unsigned)Size); } SC_ArmUart5RxDma(); } else if (huart->Instance == huart3.Instance) { CCS_RxEventCallback(huart, Size); } } ISR_FAST void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == huart2.Instance) { serial_control.tx_tick = 0; } else if (huart->Instance == huart3.Instance) { CCS_TxCpltCallback(huart); } } // Приватные функции реализации // Полностью программная реализация CRC-32 (полином CRC32_POLYNOMIAL, порядок little-endian) ISR_FAST static uint32_t calculate_crc32(const uint8_t* data, uint16_t length) { uint32_t crc = 0xFFFFFFFFu; for (uint16_t i = 0; i < length; i++) { crc ^= data[i]; for (uint8_t bit = 0; bit < 8; bit++) { if (crc & 0x1u) { crc = (crc >> 1) ^ CRC32_POLYNOMIAL; } else { crc >>= 1; } } } return crc ^ 0xFFFFFFFFu; } ISR_FAST static uint16_t encode_packet(const uint8_t* payload, uint16_t payload_len, uint8_t* output, uint8_t response_code) { uint16_t out_index = 0; output[out_index++] = response_code; if (payload != NULL) { // Просто копируем полезную нагрузку без какого‑либо экранирования for (uint16_t i = 0; i < payload_len; i++) { output[out_index++] = payload[i]; // Проверка переполнения if (out_index >= MAX_TX_BUFFER_SIZE - 5) { // 4 байта CRC + END_BYTE return 0; } } } // Вычисляем CRC для всего содержимого (код ответа + полезная нагрузка) uint32_t crc = calculate_crc32(output, out_index); uint8_t* crc_bytes = (uint8_t*)&crc; // Добавляем CRC без экранирования for (int i = 0; i < 4; i++) { output[out_index++] = crc_bytes[i]; if (out_index >= MAX_TX_BUFFER_SIZE - 1) { // место для END_BYTE return 0; } } return out_index; } ISR_FAST void SC_SendPacket(const uint8_t* payload, uint16_t payload_len, uint8_t response_code) { uint16_t packet_len = encode_packet(payload, payload_len, serial_control.tx_buffer, response_code); if (packet_len > 0) { if (huart2.gState != HAL_UART_STATE_READY) { (void)HAL_UART_AbortTransmit(&huart2); log_printf(LOG_WARN, "UART2 TX busy, abort transmit before resend\n"); } if (HAL_UART_Transmit_DMA(&huart2, serial_control.tx_buffer, packet_len) != HAL_OK) { SC_LogUartError("UART2 TX DMA start failed", &huart2); return; } serial_control.tx_tick = HAL_GetTick(); } } ISR_FAST static uint8_t parse_packet(const uint8_t* packet_data, uint16_t packet_len, ReceivedCommand_t* out_cmd) { // if (test_crc_invalid && (packet_data[1] != CMD_GET_STATUS)) { // test_crc_invalid--; // return 0; // }else{ // test_crc_invalid = 5; // } // Минимальный размер: 1 байт команды + 4 байта CRC if (packet_len < 5) return 0; if (packet_len > MAX_RX_BUFFER_SIZE) return 0; uint16_t payload_length = packet_len - 4; // Извлекаем принятую CRC (последние 4 байта, little-endian) uint32_t received_checksum = ((uint32_t)packet_data[payload_length] << 0) | ((uint32_t)packet_data[payload_length + 1] << 8) | ((uint32_t)packet_data[payload_length + 2] << 16) | ((uint32_t)packet_data[payload_length + 3] << 24); // Вычисляем CRC для полезной нагрузки uint32_t calculated_checksum = calculate_crc32(packet_data, payload_length); if (received_checksum != calculated_checksum) return 0; // CRC не совпадает out_cmd->argument = (void *)&packet_data[1]; out_cmd->command = packet_data[0]; out_cmd->argument_length = (uint8_t)(payload_length - 1); return 1; } ISR_FAST static uint8_t process_received_packet(SerialControl_t *ctx, const uint8_t* packet_data, uint16_t packet_len) { if (!parse_packet(packet_data, packet_len, (ReceivedCommand_t *)&ctx->received_command)) { return 0; } ctx->command_ready = 1; return 1; } static void SC_UART2_Watchdog(void) { const uint32_t now = HAL_GetTick(); const int32_t since_last_packet = (int32_t)(now - sc_uart2_last_packet_tick); if (since_last_packet >= (int32_t)SC_UART2_PACKET_TIMEOUT_MS) { if (sc_uart2_timed_out == 0u) { serial_control.command_ready = 0; log_printf(LOG_WARN, "UART2 RX packet timeout (%u ms)\n", (unsigned)SC_UART2_PACKET_TIMEOUT_MS); } sc_uart2_timed_out = 1; } else { sc_uart2_timed_out = 0; } if ((huart2.RxState == HAL_UART_STATE_READY) && ((int32_t)(now - sc_uart2_last_recover_tick) >= (int32_t)SC_UART2_RECOVER_GUARD_MS)) { SC_ArmUart2RxDma(); sc_uart2_last_recover_tick = now; } } static void SC_ArmUart2RxDma(void) { if ((huart2.RxState == HAL_UART_STATE_READY) && (serial_control.command_ready == 0)) { if (HAL_UARTEx_ReceiveToIdle_DMA(&huart2, serial_control.rx_buffer, MAX_RX_BUFFER_SIZE - 1) != HAL_OK) { SC_LogUartError("UART2 RX DMA arm failed", &huart2); } } } static void SC_ArmUart5RxDma(void) { if (huart5.RxState == HAL_UART_STATE_READY) { if (HAL_UARTEx_ReceiveToIdle_IT(&huart5, serial_iso.rx_buffer, MAX_RX_BUFFER_SIZE - 1) == HAL_OK) { return; } SC_LogUartError("UART5 RX IT arm failed", &huart5); } } void SC_RecoverUartDma(UART_HandleTypeDef *huart) { if (huart == &huart2) { SC_LogUartError("UART2 recover start", &huart2); (void)HAL_UART_AbortReceive(&huart2); (void)HAL_UART_AbortTransmit(&huart2); serial_control.tx_tick = 0; SC_ArmUart2RxDma(); sc_uart2_last_recover_tick = HAL_GetTick(); } else if (huart == &huart5) { SC_LogUartError("UART5 recover start", &huart5); (void)HAL_UART_AbortReceive(&huart5); SC_ArmUart5RxDma(); } } static void SC_LogUartError(const char *tag, UART_HandleTypeDef *huart) { if (tag == NULL || huart == NULL) { return; } log_printf(LOG_ERR, "%s: instance=0x%08lx err=0x%08lx g=%lu rx=%lu\n", tag, (unsigned long)huart->Instance, (unsigned long)HAL_UART_GetError(huart), (unsigned long)huart->gState, (unsigned long)huart->RxState); }