forked from achamaikin/CCSModuleSW30Web
244 lines
8.1 KiB
C
244 lines
8.1 KiB
C
#include "serial_control.h"
|
||
#include "crc.h"
|
||
#include "usart.h"
|
||
#include "board.h"
|
||
|
||
// Приватные функции
|
||
uint32_t revbit(uint32_t uData);
|
||
uint32_t CRC32_ForBytes(uint8_t *pData, uint32_t uLen);
|
||
uint32_t calculate_crc32(const uint8_t* data, uint16_t length);
|
||
uint16_t encode_packet(const uint8_t* payload, uint16_t payload_len, uint8_t* output, uint8_t response_code);
|
||
uint8_t process_received_packet(const uint8_t* packet_data, uint16_t packet_len);
|
||
|
||
uint8_t test_crc_invalid = 0;
|
||
|
||
SerialControl_t serial_control;
|
||
|
||
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));
|
||
}
|
||
|
||
void SC_Task() {
|
||
// Запуск приема в режиме прерывания с ожиданием idle
|
||
if((huart2.RxState == HAL_UART_STATE_READY) && (serial_control.command_ready == 0)) HAL_UARTEx_ReceiveToIdle_IT(&huart2, serial_control.rx_buffer, MAX_RX_BUFFER_SIZE - 1);
|
||
|
||
// Проверка таймаута отправки пакета (больше 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)) {
|
||
// HAL_Delay(2);
|
||
SC_CommandHandler(&serial_control.received_command);
|
||
HAL_UARTEx_ReceiveToIdle_IT(&huart2, serial_control.rx_buffer, MAX_RX_BUFFER_SIZE - 1);
|
||
serial_control.command_ready = 0; // Сбрасываем флаг
|
||
}
|
||
}
|
||
|
||
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
|
||
if (huart->Instance == huart2.Instance) {
|
||
if(!process_received_packet(serial_control.rx_buffer, Size)){
|
||
SC_SendPacket(NULL, 0, RESP_INVALID);
|
||
}
|
||
}
|
||
}
|
||
|
||
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;
|
||
}
|
||
}
|
||
|
||
// Приватные функции реализации
|
||
uint32_t revbit(uint32_t uData) {
|
||
uint32_t uRevData = 0, uIndex = 0;
|
||
uRevData |= ((uData >> uIndex) & 0x01);
|
||
for(uIndex = 1; uIndex < 32; uIndex++) {
|
||
uRevData <<= 1;
|
||
uRevData |= ((uData >> uIndex) & 0x01);
|
||
}
|
||
return uRevData;
|
||
}
|
||
|
||
uint32_t CRC32_ForBytes(uint8_t *pData, uint32_t uLen) {
|
||
uint32_t uIndex = 0, uData = 0, i;
|
||
uIndex = uLen >> 2;
|
||
|
||
SERIAL_PROTOCOL_CRC_CLK_ENABLE();
|
||
|
||
__HAL_CRC_DR_RESET(&hcrc);
|
||
|
||
while(uIndex--) {
|
||
((uint8_t *) & uData)[0] = pData[0];
|
||
((uint8_t *) & uData)[1] = pData[1];
|
||
((uint8_t *) & uData)[2] = pData[2];
|
||
((uint8_t *) & uData)[3] = pData[3];
|
||
pData += 4;
|
||
uData = revbit(uData);
|
||
hcrc.Instance->DR = uData;
|
||
}
|
||
uData = revbit(hcrc.Instance->DR);
|
||
uIndex = uLen & 0x03;
|
||
while(uIndex--) {
|
||
uData ^= (uint32_t) * pData++;
|
||
for(i = 0; i < 8; i++)
|
||
if (uData & 0x1)
|
||
uData = (uData >> 1) ^ CRC32_POLYNOMIAL;
|
||
else
|
||
uData >>= 1;
|
||
}
|
||
|
||
SERIAL_PROTOCOL_CRC_CLK_DISABLE();
|
||
|
||
return uData ^ 0xFFFFFFFF;
|
||
}
|
||
|
||
uint32_t calculate_crc32(const uint8_t* data, uint16_t length) {
|
||
return CRC32_ForBytes((uint8_t*)data, (uint32_t)length);
|
||
}
|
||
|
||
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();
|
||
}
|
||
}
|
||
|
||
uint8_t process_received_packet(const uint8_t* packet_data, uint16_t packet_len) {
|
||
// 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 не совпадает
|
||
|
||
serial_control.received_command.argument = &packet_data[1];
|
||
serial_control.received_command.command = packet_data[0];
|
||
serial_control.received_command.argument_length = payload_length - 1;
|
||
serial_control.command_ready = 1;
|
||
return 1;
|
||
}
|
||
|