SerialPortLin_ARTERY_AT32/Src/SerialPortLinArtery.c

308 lines
9.0 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 = 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); // Успех, если все байты отправлены
}