commit df8dd7d08487333cc3116638640e34714faa43b6 Author: cfif Date: Tue May 19 16:47:01 2026 +0300 Init diff --git a/Inc/SerialPortLinArtery.h b/Inc/SerialPortLinArtery.h new file mode 100644 index 0000000..c8231b5 --- /dev/null +++ b/Inc/SerialPortLinArtery.h @@ -0,0 +1,51 @@ +// +// Created by cfif on 16.09.22. +// + +#ifndef SERIALPORT_SERIALPORTLIN_ARTERY_H +#define SERIALPORT_SERIALPORTLIN_ARTERY_H + +#include "SerialPort.h" +#include "at32f435_437.h" +#include "cmsis_os2.h" +#include "stdbool.h" +#include "LinIO.h" + +typedef struct __attribute__((packed)) { + uint8_t id; // ID команды (0-63) + uint8_t dataLen; // Длина данных (0-8) + uint8_t data[8]; // Массив данных + uint8_t checksum; // Контрольная сумма + lin_event_id_t event; // Событие/статус приема +} lin_frame_t; + +typedef struct { + usart_type *uart; + + bool linFrameStarted; + uint8_t linByteCount; + uint32_t linLastByteTime; + uint32_t linFrameTimeoutMs; + uint8_t linBuffer[12]; + lin_frame_t rxFrame; + osMessageQueueId_t rxDataQueue; + osMessageQueueId_t rxDataSnifferQueue; +} tSerialPortLinArtery; + + +void vSerialPortLinInit( + tSerialPortLinArtery *env, + usart_type *uart, + bool swap, + uint32_t BoundRate, + IRQn_Type irq, + crm_periph_clock_type uartClock, + uint8_t irqPriority, + uint32_t rxBufferLength, + uint32_t rxSnifferLength +); + +void SerialPort_IrqProcessing_UartLin(tSerialPortLinArtery *env); +bool vSerialPortSendLinFrame(tSerialPortLinArtery *env, const lin_frame_t *pFrame, uint32_t timeout); + +#endif //SERIALPORT_SERIALPORTLIN_ARTERY_H diff --git a/Src/SerialPortLinArtery.c b/Src/SerialPortLinArtery.c new file mode 100644 index 0000000..12cd3b7 --- /dev/null +++ b/Src/SerialPortLinArtery.c @@ -0,0 +1,307 @@ +// +// Created by cfif on 16.09.22. +// +#include +#include "SerialPortLinArtery.h" +#include "string.h" + + +void vSerialPortLinInit( + tSerialPortLinArtery *env, + usart_type *uart, + bool swap, + uint32_t BoundRate, + IRQn_Type irq, + crm_periph_clock_type uartClock, + uint8_t irqPriority, + uint32_t rxBufferLength, + uint32_t rxSnifferLength +) { + env->uart = uart; + + env->linLastByteTime = 20; + env->linFrameTimeoutMs = 20; + + usart_reset(uart); + + crm_periph_clock_enable(uartClock, TRUE); + + usart_init(uart, BoundRate, USART_DATA_8BITS, USART_STOP_1_BIT); + + // ВКЛЮЧАЕМ LIN РЕЖИМ!!! + usart_lin_mode_enable(uart, TRUE); + // Настраиваем длину break field (LIN требует 13 бит, выбираем 11) + usart_break_bit_num_set(uart, USART_BREAK_11BITS); + + usart_transmitter_enable(uart, TRUE); + usart_receiver_enable(uart, TRUE); + + if (swap) { + usart_transmit_receive_pin_swap(uart, TRUE); + } + + usart_enable(uart, TRUE); + + // Включаем прерывание по break frame + usart_interrupt_enable(uart, USART_BF_INT, TRUE); + usart_interrupt_enable(uart, USART_RDBF_INT, TRUE); +// usart_interrupt_enable(uart, USART_IDLE_INT, TRUE); + + NVIC_EnableIRQ(irq); + NVIC_SetPriority(irq, irqPriority); + + env->rxDataQueue = osMessageQueueNew(rxBufferLength, sizeof(lin_frame_t), NULL); + + if (rxSnifferLength) { + env->rxDataSnifferQueue = osMessageQueueNew(rxSnifferLength, sizeof(lin_frame_t), NULL); + } else { + env->rxDataSnifferQueue = 0; + } + +} + + +static uint16_t +vSerialPortLinTransmitOverCore(tSerialPortLinArtery *env, uint8_t *data, uint16_t size, uint32_t timeout) { + uint16_t sent = 0; + uint32_t endMs = SystemGetMs() + timeout; + + while (size && ((timeout == SystemWaitForever) || (endMs > SystemGetMs()))) { + + if (usart_flag_get(env->uart, USART_TDBE_FLAG)) { + usart_data_transmit(env->uart, *data); + --size; + ++data; + ++sent; + } + + } + + while ((timeout == SystemWaitForever) || (endMs > SystemGetMs())) { + if (usart_flag_get(env->uart, USART_TDC_FLAG)) + break; + } + + return sent; +} + +static uint8_t LIN_DrvMakeCheckSum(uint8_t *pBuf, uint8_t u8Size, uint8_t u8Pid) { + uint16_t u16Checksum = 0U; + uint8_t u8Length = 0U; + + // For PID is 0x3C (ID 0x3C) or 0x7D (ID 0x3D) or 0xFE (ID 0x3E) or 0xBF (ID 0x3F) + if ((0x3CU == u8Pid) || (0x7DU == u8Pid) || (0xFEU == u8Pid) || (0xBFU == u8Pid)) + { + u8Pid = 0U; + } + + u16Checksum += u8Pid; + + for (u8Length = 0U; u8Length < u8Size; u8Length++) + { + u16Checksum += *pBuf; + pBuf++; + if (u16Checksum > 0xFFU) + { + u16Checksum -= 0xFFU; + } + } + + return ~(uint8_t)(u16Checksum); +} + +static uint8_t LIN_CalcParity(uint8_t id) { + uint8_t p0 = 0; + uint8_t p1 = 0; + + // P0 = ID0 ^ ID1 ^ ID2 ^ ID4 + p0 = ((id >> 0) & 0x01) ^ ((id >> 1) & 0x01) ^ + ((id >> 2) & 0x01) ^ ((id >> 4) & 0x01); + + // P1 = ^(ID1 ^ ID3 ^ ID4 ^ ID5) + p1 = 1 ^ ((id >> 1) & 0x01) ^ ((id >> 3) & 0x01) ^ + ((id >> 4) & 0x01) ^ ((id >> 5) & 0x01); + + return (p1 << 7) | (p0 << 6); +} + +// Измененный обработчик прерываний (без IDLE): +void SerialPort_IrqProcessing_UartLin(tSerialPortLinArtery *env) { + + // 1. Break frame detection + if (usart_flag_get(env->uart, USART_BFF_FLAG)) { + usart_flag_clear(env->uart, USART_BFF_FLAG); + usart_data_receive(env->uart); + + env->linFrameStarted = true; + env->linByteCount = 0; + env->linLastByteTime = SystemGetMs(); // Запоминаем время + env->rxFrame.event = LIN_RECV_BREAK_FIELD_OK; + return; + } + + // 2. Receive data (без IDLE) + if (usart_flag_get(env->uart, USART_RDBF_FLAG)) { + if (env->linFrameStarted && (env->linByteCount < sizeof(env->linBuffer))) { + uint8_t data = usart_data_receive(env->uart); + env->linBuffer[env->linByteCount] = data; + env->linByteCount++; + env->linLastByteTime = SystemGetMs(); // Обновляем время + } else { + usart_data_receive(env->uart); + if (env->linFrameStarted) { + env->rxFrame.event = LIN_RX_OVERRUN; + osMessageQueuePut(env->rxDataQueue, &env->rxFrame, 0, 0); + env->linFrameStarted = false; + } + } + } + + // 3. Error handling + if (usart_flag_get(env->uart, USART_FERR_FLAG)) { + usart_flag_clear(env->uart, USART_FERR_FLAG); + usart_data_receive(env->uart); + + if (env->linFrameStarted) { + env->rxFrame.event = LIN_FRAME_ERROR; + osMessageQueuePut(env->rxDataQueue, &env->rxFrame, 0, 0); + env->linFrameStarted = false; + } + } + + if (usart_flag_get(env->uart, USART_ROERR_FLAG)) { + usart_flag_clear(env->uart, USART_ROERR_FLAG); + usart_data_receive(env->uart); + + if (env->linFrameStarted) { + env->rxFrame.event = LIN_RX_OVERRUN; + osMessageQueuePut(env->rxDataQueue, &env->rxFrame, 0, 0); + env->linFrameStarted = false; + } + } +} + + +// Выделенная функция обработки кадра +static void LIN_ProcessReceivedFrame(tSerialPortLinArtery *env) { + // Проверка sync field + if (env->linBuffer[0] != 0x55) { + env->rxFrame.event = LIN_SYNC_ERROR; + osMessageQueuePut(env->rxDataQueue, &env->rxFrame, 0, 0); + return; + } + + // Проверка PID + uint8_t receivedPid = env->linBuffer[1]; + uint8_t id = receivedPid & 0x3F; + uint8_t expectedPid = id | LIN_CalcParity(id); + + if (receivedPid != expectedPid) { + env->rxFrame.event = LIN_PID_ERROR; + osMessageQueuePut(env->rxDataQueue, &env->rxFrame, 0, 0); + return; + } + + env->rxFrame.id = id; + env->rxFrame.dataLen = env->linByteCount - 3; + + if (env->rxFrame.dataLen > 8) { + env->rxFrame.event = LIN_FRAME_ERROR; + osMessageQueuePut(env->rxDataQueue, &env->rxFrame, 0, 0); + return; + } + + // Копируем данные + for (uint8_t i = 0; i < env->rxFrame.dataLen; i++) { + env->rxFrame.data[i] = env->linBuffer[2 + i]; + } + + // Проверка checksum + env->rxFrame.checksum = env->linBuffer[env->linByteCount - 1]; + uint8_t calculatedChecksum = LIN_DrvMakeCheckSum( + env->rxFrame.data, + env->rxFrame.dataLen, + receivedPid + ); + + if (env->rxFrame.checksum != calculatedChecksum) { + env->rxFrame.event = LIN_CHECKSUM_ERROR; + } else { + env->rxFrame.event = LIN_RX_COMPLETED; + } + + osMessageQueuePut(env->rxDataQueue, &env->rxFrame, 0, 0); +} + + +// Функция проверки таймаута (вызывать из таймера, например, каждые 1-5 мс) +void LIN_CheckTimeout(tSerialPortLinArtery *env) { + + if (!env->linFrameStarted) { + return; + } + + uint32_t now = SystemGetMs(); + uint32_t elapsed = now - env->linLastByteTime; + + if (elapsed >= env->linFrameTimeoutMs) { + // Таймаут - кадр должен быть завершен + if (env->linByteCount >= 3) { + // Пытаемся обработать полученные данные + LIN_ProcessReceivedFrame(env); + } else { + // Недостаточно данных + env->rxFrame.event = LIN_TIMEOUT; + osMessageQueuePut(env->rxDataQueue, &env->rxFrame, 0, 0); + } + + env->linFrameStarted = false; + } +} + + +bool vSerialPortSendLinFrame(tSerialPortLinArtery *env, const lin_frame_t *pFrame, uint32_t timeout) { + uint8_t txBuffer[11]; // Максимальный размер LIN кадра (без break) + uint8_t idx = 0; + + if ((!env) || (!pFrame)) { + return false; + } + + // Проверка валидности данных + if (pFrame->dataLen > 8) { + return false; + } + + // 1. Формирование LIN кадра + // Sync field + txBuffer[idx++] = 0x55; + + // PID (ID + parity) + uint8_t pid = pFrame->id | LIN_CalcParity(pFrame->id); + txBuffer[idx++] = pid; + + // Data bytes + for (uint8_t i = 0; i < pFrame->dataLen; i++) { + txBuffer[idx++] = pFrame->data[i]; + } + + // Checksum + txBuffer[idx++] = pFrame->checksum; + + // 2. Отправка break field + usart_break_send(env->uart); + + // Ожидание завершения отправки break + uint32_t breakTimeout = SystemGetMs() + 10; // 10мс максимум на break + while (usart_flag_get(env->uart, USART_TDBE_FLAG) == RESET) { + if (SystemGetMs() > breakTimeout) { + return false; + } + } + + // 3. Отправка данных (возвращает количество отправленных байт) + uint16_t sent = vSerialPortLinTransmitOverCore(env, txBuffer, idx, timeout); + + return (sent == idx); // Успех, если все байты отправлены +} diff --git a/modular.json b/modular.json new file mode 100644 index 0000000..57e69db --- /dev/null +++ b/modular.json @@ -0,0 +1,22 @@ +{ + "dep": [ + { + "type": "git", + "provider": "HVAC_DEV", + "repo": "SerialPort" + }, + { + "type": "git", + "provider": "HVAC_DEV", + "repo": "SystemDelayInterface" + } + ], + "cmake": { + "inc_dirs": [ + "Inc" + ], + "srcs": [ + "Src/**.c" + ] + } +} \ No newline at end of file