This commit is contained in:
cfif 2026-05-19 16:47:01 +03:00
commit df8dd7d084
3 changed files with 380 additions and 0 deletions

51
Inc/SerialPortLinArtery.h Normal file
View File

@ -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

307
Src/SerialPortLinArtery.c Normal file
View File

@ -0,0 +1,307 @@
//
// Created by cfif on 16.09.22.
//
#include <SystemDelayInterface.h>
#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); // Успех, если все байты отправлены
}

22
modular.json Normal file
View File

@ -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"
]
}
}