Files
CCSModuleSW30Web/Core/Src/serial_control.c
T

280 lines
9.8 KiB
C

#include "serial_control.h"
#include "usart.h"
#include "board.h"
#include "serial.h"
#include "debug.h"
// Приватные функции
static uint32_t calculate_crc32(const uint8_t* data, uint16_t length);
static uint16_t encode_packet(const uint8_t* payload, uint16_t payload_len, uint8_t* output, uint8_t response_code);
static uint8_t parse_packet(const uint8_t* packet_data, uint16_t packet_len, ReceivedCommand_t* out_cmd);
static uint8_t process_received_packet(SerialControl_t *ctx, const uint8_t* packet_data, uint16_t packet_len);
static void SC_ArmUart2Rx(void);
static void SC_ArmUart5Rx(void);
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;
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));
}
void SC_Task() {
// Запуск приема в режиме прерывания с ожиданием idle
SC_ArmUart2Rx();
SC_ArmUart5Rx();
// Проверка таймаута отправки пакета (больше 100 мс)
if (huart2.gState == HAL_UART_STATE_BUSY_TX && serial_control.tx_tick != 0) {
if ((HAL_GetTick() - serial_control.tx_tick) > 100) {
// Таймаут: принудительно сбрасываем передачу
HAL_UART_Abort_IT(&huart2);
// Выключаем DIR при сбросе передачи
HAL_GPIO_WritePin(USART2_DIR_GPIO_Port, USART2_DIR_Pin, GPIO_PIN_RESET);
serial_control.tx_tick = 0; // Сбрасываем tick
}
}
// Проверка наличия принятой команды для обработки
if (serial_control.command_ready && (huart2.gState != HAL_UART_STATE_BUSY_TX)) {
g_sc_command_source = SC_SOURCE_UART2;
SC_CommandHandler(&serial_control.received_command);
serial_control.command_ready = 0; // Сбрасываем флаг
SC_ArmUart2Rx();
}
if (serial_control.response_pending && (huart2.gState != HAL_UART_STATE_BUSY_TX)) {
SC_SendPacket(NULL, 0, serial_control.response_code);
serial_control.response_pending = 0;
}
if (serial_iso.command_ready) {
g_sc_command_source = SC_SOURCE_UART5;
SC_CommandHandler((ReceivedCommand_t*)&serial_iso.received_command);
serial_iso.command_ready = 0;
SC_ArmUart5Rx();
}
}
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
if (huart->Instance == huart2.Instance) {
if (!process_received_packet(&serial_control, serial_control.rx_buffer, Size)) {
serial_control.response_pending = 1;
serial_control.response_code = RESP_INVALID;
SC_ArmUart2Rx();
}
} else if (huart->Instance == huart5.Instance) {
if (!process_received_packet(&serial_iso, serial_iso.rx_buffer, Size)) {
SC_ArmUart5Rx();
}
} else if (huart->Instance == huart3.Instance) {
CCS_RxEventCallback(huart, Size);
}
}
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == huart2.Instance) {
uint32_t uart_error = HAL_UART_GetError(huart);
log_printf(LOG_WARN, "USART2 rx error: 0x%08lx\n", uart_error);
SC_ArmUart2Rx();
} else if (huart->Instance == huart5.Instance) {
uint32_t uart_error = HAL_UART_GetError(huart);
log_printf(LOG_WARN, "UART5 rx error: 0x%08lx\n", uart_error);
SC_ArmUart5Rx();
} else if (huart->Instance == huart3.Instance) {
uint32_t uart_error = HAL_UART_GetError(huart);
log_printf(LOG_WARN, "USART3 rx error: 0x%08lx\n", uart_error);
CCS_RxArm();
}
}
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == huart2.Instance) {
HAL_GPIO_WritePin(USART2_DIR_GPIO_Port, USART2_DIR_Pin, GPIO_PIN_RESET);
serial_control.tx_tick = 0;
}
}
static void SC_ArmUart2Rx(void) {
if ((&huart2)->RxState == HAL_UART_STATE_READY && serial_control.command_ready == 0) {
(void)HAL_UARTEx_ReceiveToIdle_IT(&huart2, serial_control.rx_buffer, MAX_RX_BUFFER_SIZE - 1);
}
}
static void SC_ArmUart5Rx(void) {
if ((&huart5)->RxState == HAL_UART_STATE_READY && serial_iso.command_ready == 0) {
(void)HAL_UARTEx_ReceiveToIdle_IT(&huart5, serial_iso.rx_buffer, MAX_RX_BUFFER_SIZE - 1);
}
}
// Приватные функции реализации
// Полностью программная реализация CRC-32 (полином CRC32_POLYNOMIAL, порядок little-endian)
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;
}
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;
}
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_BUSY_TX) {
HAL_UART_Abort_IT(&huart2);
HAL_GPIO_WritePin(USART2_DIR_GPIO_Port, USART2_DIR_Pin, GPIO_PIN_RESET);
}
HAL_GPIO_WritePin(USART2_DIR_GPIO_Port, USART2_DIR_Pin, GPIO_PIN_SET);
HAL_UART_Transmit_IT(&huart2, serial_control.tx_buffer, packet_len);
serial_control.tx_tick = HAL_GetTick();
}
}
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;
}
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;
}