SerialPortLin_ARTERY_AT32/Src/SerialPortLinArtery.c

526 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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, uint8_t *ADR_COM, 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;
*ADR_COM = rxFrame.id & 0x3F;
for (uint8_t i = 0; i < rxFrame.dataLen; ++i) {
linData->g_aRxBuffer[i] = 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;
}