526 lines
15 KiB
C
526 lines
15 KiB
C
//
|
||
// 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 = 2;
|
||
env->linFrameTimeoutMs = 2;
|
||
|
||
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);
|
||
}
|
||
|
||
extern const uint8_t DRS_SENSON_ADR_Stat[8];
|
||
|
||
|
||
// Выделенная функция обработки кадра
|
||
static void LIN_ProcessReceivedFrame(tSerialPortLinArtery *env, bool isHeader) {
|
||
// Проверка 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;
|
||
}
|
||
|
||
if (isHeader) {
|
||
env->rxFrame.id = id;
|
||
env->rxFrame.dataLen = 0;
|
||
env->rxFrame.event = LIN_RX_COMPLETED;
|
||
osMessageQueuePut(env->rxDataQueue, &env->rxFrame, 0, 0);
|
||
}
|
||
|
||
|
||
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);
|
||
}
|
||
|
||
// Измененный обработчик прерываний (без IDLE):
|
||
void SerialPort_IrqProcessing_UartLin(tSerialPortLinArtery *env) {
|
||
|
||
// ИГНОРИРУЕМ ВСЕ ПРЕРЫВАНИЯ ВО ВРЕМЯ ПЕРЕДАЧИ
|
||
if (env->linTxInProgress) {
|
||
// Просто очищаем флаги, но не обрабатываем
|
||
if (usart_flag_get(env->uart, USART_BFF_FLAG)) {
|
||
usart_flag_clear(env->uart, USART_BFF_FLAG);
|
||
usart_data_receive(env->uart);
|
||
}
|
||
if (usart_flag_get(env->uart, USART_RDBF_FLAG)) {
|
||
usart_data_receive(env->uart);
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 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;
|
||
}
|
||
|
||
|
||
if (usart_flag_get(env->uart, USART_IDLEF_FLAG)) {
|
||
usart_flag_clear(env->uart, USART_IDLEF_FLAG);
|
||
|
||
uint8_t data = usart_data_receive(env->uart);
|
||
|
||
if (!env->linFrameStarted) {
|
||
return;
|
||
}
|
||
|
||
// Таймаут - кадр должен быть завершен
|
||
if (env->linByteCount >= 2) {
|
||
|
||
if (env->linBuffer[1] == 0x97) {
|
||
return;
|
||
}
|
||
|
||
// Пытаемся обработать полученные данные
|
||
if (env->linByteCount == 2) {
|
||
LIN_ProcessReceivedFrame(env, true);
|
||
} else {
|
||
LIN_ProcessReceivedFrame(env, false);
|
||
}
|
||
|
||
} else {
|
||
// Недостаточно данных
|
||
env->rxFrame.event = LIN_TIMEOUT;
|
||
osMessageQueuePut(env->rxDataQueue, &env->rxFrame, 0, 0);
|
||
}
|
||
|
||
env->linFrameStarted = false;
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
// 2. Receive data (без IDLE)
|
||
if (usart_flag_get(env->uart, USART_RDBF_FLAG)) {
|
||
|
||
uint8_t data = usart_data_receive(env->uart);
|
||
|
||
if (env->linFrameStarted && (env->linByteCount < sizeof(env->linBuffer))) {
|
||
env->linBuffer[env->linByteCount] = data;
|
||
env->linByteCount++;
|
||
env->linLastByteTime = SystemGetMs(); // Обновляем время
|
||
} else {
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
// Функция проверки таймаута (вызывать из таймера, например, каждые 1-5 мс)
|
||
void LIN_CheckTimeout(tSerialPortLinArtery *env) {
|
||
|
||
return;
|
||
|
||
if (!env->linFrameStarted) {
|
||
return;
|
||
}
|
||
|
||
uint32_t now = SystemGetMs();
|
||
uint32_t elapsed = now - env->linLastByteTime;
|
||
|
||
if (elapsed >= env->linFrameTimeoutMs) {
|
||
// Таймаут - кадр должен быть завершен
|
||
if (env->linByteCount >= 2) {
|
||
|
||
if (env->linBuffer[1] == 0x97) {
|
||
return;
|
||
}
|
||
|
||
// Пытаемся обработать полученные данные
|
||
if (env->linByteCount == 2) {
|
||
LIN_ProcessReceivedFrame(env, true);
|
||
} else {
|
||
LIN_ProcessReceivedFrame(env, false);
|
||
}
|
||
|
||
|
||
} else {
|
||
// Недостаточно данных
|
||
env->rxFrame.event = LIN_TIMEOUT;
|
||
osMessageQueuePut(env->rxDataQueue, &env->rxFrame, 0, 0);
|
||
}
|
||
|
||
env->linFrameStarted = false;
|
||
}
|
||
}
|
||
|
||
/*
|
||
static bool vSerialPortSendLinFrame(tSerialPortLinArtery *env, const lin_frame_t *pFrame, uint32_t timeout) {
|
||
uint8_t txBuffer[11]; // Максимальный размер LIN кадра (без break)
|
||
uint8_t idx = 0;
|
||
|
||
if (!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;
|
||
|
||
env->linTxInProgress = true;
|
||
|
||
// 1. Отправка break field
|
||
usart_break_send(env->uart);
|
||
|
||
// Ожидаем завершения break (но не через TDC!)
|
||
uint32_t breakTimeout = SystemGetMs() + 2; // 2мс достаточно для break
|
||
while (usart_flag_get(env->uart, USART_TDBE_FLAG) == RESET) {
|
||
if (SystemGetMs() > breakTimeout) {
|
||
env->linTxInProgress = false;
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// 2. Отправка данных (используем TDBE, не TDC!)
|
||
for (uint8_t i = 0; i < idx; i++) {
|
||
// Ждем пока буфер передачи освободится
|
||
while (usart_flag_get(env->uart, USART_TDBE_FLAG) == RESET);
|
||
|
||
// Отправляем байт
|
||
usart_data_transmit(env->uart, txBuffer[i]);
|
||
|
||
// НЕ ждем TDC! Сразу переходим к следующему байту
|
||
}
|
||
|
||
// 3. Ждем завершения передачи последнего байта
|
||
while (usart_flag_get(env->uart, USART_TDC_FLAG) == RESET);
|
||
|
||
env->linTxInProgress = false;
|
||
|
||
// 4. Очищаем эхо (важно!)
|
||
while (usart_flag_get(env->uart, USART_RDBF_FLAG)) {
|
||
(void)usart_data_receive(env->uart);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
*/
|
||
|
||
// Функция отправки RESPONSE от Slave (после получения HEADER)
|
||
static bool vSerialPortSendLinResponse(tSerialPortLinArtery *env, const lin_frame_t *pFrame, uint32_t timeout) {
|
||
uint8_t txBuffer[10]; // Только данные + checksum (без break и sync!)
|
||
uint8_t idx = 0;
|
||
|
||
if (!pFrame || pFrame->dataLen > 8) return false;
|
||
|
||
// Формируем ТОЛЬКО RESPONSE (без Header!)
|
||
for (uint8_t i = 0; i < pFrame->dataLen; i++) {
|
||
txBuffer[idx++] = pFrame->data[i];
|
||
}
|
||
|
||
txBuffer[idx++] = pFrame->checksum;
|
||
|
||
env->linTxInProgress = true;
|
||
|
||
// Отправка response (без break и sync!)
|
||
for (uint8_t i = 0; i < idx; i++) {
|
||
while (usart_flag_get(env->uart, USART_TDBE_FLAG) == RESET);
|
||
usart_data_transmit(env->uart, txBuffer[i]);
|
||
}
|
||
|
||
// Ждем завершения
|
||
while (usart_flag_get(env->uart, USART_TDC_FLAG) == RESET);
|
||
|
||
env->linTxInProgress = false;
|
||
return true;
|
||
}
|
||
|
||
static uint16_t vSerialPortLinReceiveQueue(tSerialPortLinArtery *env, uint8_t *data, uint16_t size, uint32_t timeout,
|
||
osMessageQueueId_t queueId) {
|
||
uint16_t received = 0;
|
||
|
||
|
||
if (timeout) {
|
||
uint32_t endMs = SystemGetMs() + timeout;
|
||
uint32_t leftMs;
|
||
|
||
while (size && ((timeout == SystemWaitForever) || (endMs > SystemGetMs()))) {
|
||
leftMs = endMs - SystemGetMs();
|
||
if (osMessageQueueGet(queueId, data, NULL, leftMs) == osOK) {
|
||
--size;
|
||
++received;
|
||
++data;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
while (size) {
|
||
if (osMessageQueueGet(queueId, data, NULL, 0) == osOK) {
|
||
--size;
|
||
++received;
|
||
++data;
|
||
} else {
|
||
return received;
|
||
}
|
||
}
|
||
}
|
||
|
||
return received;
|
||
|
||
}
|
||
|
||
static uint8_t vLinTransmitCommand(tSerialPortLinArtery *env, tLinData *linData, uint8_t ADR_COM, uint32_t timeout) {
|
||
|
||
lin_frame_t txFrame;
|
||
|
||
txFrame.dataLen = linData->g_aTxBufferLen;
|
||
for (uint8_t i = 0; i < linData->g_aTxBufferLen; ++i) {
|
||
txFrame.data[i] = linData->g_aTxBuffer[i];
|
||
}
|
||
|
||
uint8_t receivedPid = ADR_COM;
|
||
uint8_t id = receivedPid & 0x3F;
|
||
uint8_t expectedPid = id | LIN_CalcParity(id);
|
||
|
||
txFrame.id = expectedPid;
|
||
txFrame.checksum = LIN_DrvMakeCheckSum(
|
||
txFrame.data,
|
||
txFrame.dataLen,
|
||
txFrame.id
|
||
);
|
||
|
||
bool result = vSerialPortSendLinResponse(env, &txFrame, timeout);
|
||
|
||
if (result) {
|
||
return LIN_TX_COMPLETED;
|
||
}
|
||
|
||
return LIN_TIMEOUT;
|
||
}
|
||
|
||
static uint8_t vLinReceivedCommand(tSerialPortLinArtery *env, tLinData *linData, uint32_t timeout) {
|
||
lin_frame_t rxFrame;
|
||
|
||
// uint16_t len = vSerialPortLinReceiveQueue(env, (void *) &rxFrame, sizeof(lin_frame_t), osWaitForever, env->rxDataQueue);
|
||
osStatus_t result = osMessageQueueGet(env->rxDataQueue, (void *) &rxFrame, NULL, osWaitForever);
|
||
if (result == osOK) {
|
||
|
||
memset(linData->g_aRxBuffer, 0, sizeof(linData->g_aRxBuffer));
|
||
|
||
if (rxFrame.event == LIN_RX_COMPLETED) {
|
||
linData->g_aRxBufferLen = rxFrame.dataLen + 1;
|
||
linData->g_aRxBuffer[0] = rxFrame.id;
|
||
for (uint8_t i = 0; i < rxFrame.dataLen; ++i) {
|
||
linData->g_aRxBuffer[i + 1] = rxFrame.data[i];
|
||
}
|
||
}
|
||
|
||
return rxFrame.event;
|
||
}
|
||
|
||
return LIN_NO_EVENT;
|
||
}
|
||
|
||
tSerialPortLinIO vSerialPorLinGetIo(tSerialPortLinArtery *env) {
|
||
tSerialPortLinIO io = {
|
||
.env = env,
|
||
.transmitCommand = (LinIOTransactionTransmit) vLinTransmitCommand,
|
||
.receivedCommand = (LinIOTransactionReceived) vLinReceivedCommand
|
||
};
|
||
return io;
|
||
} |