1835 lines
65 KiB
C
1835 lines
65 KiB
C
/**
|
|
* @file module_driver_lin.h
|
|
* @author Flagchip
|
|
* @brief LIN (Local Interconnect Network) protocol driver implementation
|
|
* @version 2.0.0
|
|
* @date 2024-08-23
|
|
*
|
|
* SDK Version: 2.6.0
|
|
*
|
|
|
|
* @copyright Copyright (c) 2020-2024 Flagchip Semiconductors Co., Ltd.
|
|
* @details This file provides low-level driver functions for LIN communication, including initialization,
|
|
* frame transmission/reception, sleep/wakeup management, and interrupt handling. Supports both
|
|
* master and slave node modes.
|
|
*/
|
|
/*********************************************************************************
|
|
* Revision History:
|
|
|
|
* Version Date Initials CR# Descriptions
|
|
* --------- ---------- ------------ ---------- ---------------
|
|
* 2.0.0 2024-08-23 Flagchip122 N/A First version (Initial implementation of LIN protocol driver)
|
|
*********************************************************************************/
|
|
|
|
#include "module_driver_lin.h"
|
|
|
|
#if FCUART_INSTANCE_COUNT > 0U
|
|
|
|
#include "device_header.h"
|
|
#include "module_driver_fcuart.h"
|
|
|
|
/* ################################################################################## */
|
|
/* ####################################### Macro #################################### */
|
|
/**
|
|
* @brief Enable/disable device error reporting feature.
|
|
* @note When set to STD_ON, error reports will be generated using ReportDevError()
|
|
*/
|
|
#ifndef LIN_DEV_ERROR_REPORT
|
|
#define LIN_DEV_ERROR_REPORT STD_OFF
|
|
#endif
|
|
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
/**
|
|
* @brief Helper macro to report device errors.
|
|
* @param func ID of the function where error occurred
|
|
* @param error Specific error code
|
|
*/
|
|
#define LIN_ReportDevError(func, error) ReportDevError(LIN_MODULE_ID, func, error)
|
|
#endif
|
|
|
|
/** UART Instance Number: Total available LIN controller instances */
|
|
#define LIN_INSTANCE_NUM FCUART_INSTANCE_COUNT
|
|
|
|
/* ################################################################################## */
|
|
/* ################################### type define ################################## */
|
|
|
|
/* ################################################################################## */
|
|
/* ################################ Local Variables ################################# */
|
|
|
|
/**
|
|
* @brief Array of FCUART peripheral base addresses.
|
|
* @details Used to access hardware registers of different LIN controller instances.
|
|
*/
|
|
static FCUART_Type *const s_aFCUART_InstanceTable[FCUART_INSTANCE_COUNT] = FCUART_BASE_PTRS;
|
|
|
|
/* ################################################################################## */
|
|
/* ########################### Local Prototype Functions ############################ */
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
LOCAL_INLINE LIN_ReturnType LIN_AssertLinInit(LIN_HandleType *pLinHandle, LIN_ConfigType *pConfig);
|
|
LOCAL_INLINE LIN_ReturnType LIN_AssertLinHandle(LIN_HandleType *pLinHandle);
|
|
LOCAL_INLINE LIN_ReturnType LIN_AssertLinHwIndex(LIN_ConfigType *pConfig);
|
|
LOCAL_INLINE LIN_ReturnType LIN_AssertLinPdu(LIN_PduType *pPdu);
|
|
LOCAL_INLINE LIN_ReturnType LIN_AssertMasterApi(LIN_HandleType *pLinHandle, uint32_t u32ApiId);
|
|
#endif /* LIN_DEV_ERROR_REPORT == STD_ON */
|
|
LOCAL_INLINE void LIN_DelayFunctionClk(LIN_XferStateType *pXfer, uint32 u32Tick);
|
|
static void LIN_ResetLinController(LIN_XferStateType *pXfer, LIN_ConfigType *pCfg);
|
|
|
|
static LIN_ReturnType LIN_DrvCalBaudrate(LIN_HandleType *pLinHandle, LIN_BaudRateCfgType *pCfg);
|
|
LOCAL_INLINE uint8_t BIT(uint8_t A, uint8_t B);
|
|
static uint8_t LIN_DrvMakeCheckSum(LIN_XferStateType *pXfer);
|
|
/* ################################################################################## */
|
|
/* ########################### Global Prototype Functions ########################### */
|
|
|
|
/* ################################################################################## */
|
|
/* ################################ Local Functions ################################# */
|
|
|
|
/**
|
|
* @brief Extract specified bit from a byte.
|
|
* @param A Source byte value
|
|
* @param B Bit position to extract (0-7)
|
|
* @return Value of the specified bit (0 or 1)
|
|
*/
|
|
LOCAL_INLINE uint8_t BIT(uint8_t A, uint8_t B)
|
|
{
|
|
return (uint8_t)((A >> B) & (uint8_t)0x01U);
|
|
}
|
|
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
/**
|
|
* @brief Validate LIN initialization parameters.
|
|
* @param pLinHandle Pointer to LIN handle structure
|
|
* @param pConfig Pointer to LIN configuration structure
|
|
* @return LIN_RET_SUCCESS if parameters are valid, otherwise error code
|
|
* @details Checks for non-null pointers
|
|
*/
|
|
LOCAL_INLINE LIN_ReturnType LIN_AssertLinInit(LIN_HandleType *pLinHandle, LIN_ConfigType *pConfig)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_STATE_ERROR;
|
|
if ((pLinHandle != NULL) && (pConfig != NULL))
|
|
{
|
|
ret = LIN_RET_SUCCESS;
|
|
}
|
|
return ret;
|
|
}
|
|
/**
|
|
* @brief Validate LIN handle structure integrity.
|
|
* @param pLinHandle Pointer to LIN handle structure
|
|
* @return LIN_RET_SUCCESS if handle and its internal pointers are valid, otherwise error code
|
|
* @details Checks if handle itself is non-null, and its internal configuration (pCfg) and transfer state (pXfer) pointers are non-null
|
|
*/
|
|
LOCAL_INLINE LIN_ReturnType LIN_AssertLinHandle(LIN_HandleType *pLinHandle)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_STATE_ERROR;
|
|
if (pLinHandle != NULL)
|
|
{
|
|
if ((pLinHandle->pCfg != NULL) && (pLinHandle->pXfer != NULL))
|
|
{
|
|
ret = LIN_RET_SUCCESS;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
/**
|
|
* @brief Validate LIN hardware index range.
|
|
* @param pConfig Pointer to LIN configuration structure
|
|
* @return LIN_RET_SUCCESS if hardware index is within valid range, otherwise error code
|
|
* @details Checks if the hardware index in configuration (pCfg->u8LinHwIndex) is less than total available instances (LIN_INSTANCE_NUM)
|
|
*/
|
|
LOCAL_INLINE LIN_ReturnType LIN_AssertLinHwIndex(LIN_ConfigType *pConfig)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_STATE_ERROR;
|
|
if (pConfig->u8LinHwIndex < LIN_INSTANCE_NUM)
|
|
{
|
|
ret = LIN_RET_SUCCESS;
|
|
}
|
|
return ret;
|
|
}
|
|
/**
|
|
* @brief Validate LIN PDU (Protocol Data Unit) pointer.
|
|
* @param pPdu Pointer to LIN PDU structure
|
|
* @return LIN_RET_SUCCESS if PDU pointer is non-null, otherwise error code
|
|
*/
|
|
LOCAL_INLINE LIN_ReturnType LIN_AssertLinPdu(LIN_PduType *pPdu)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_STATE_ERROR;
|
|
if (pPdu != NULL)
|
|
{
|
|
ret = LIN_RET_SUCCESS;
|
|
}
|
|
return ret;
|
|
}
|
|
/**
|
|
* @brief Validate master node API call permission.
|
|
* @param pLinHandle Pointer to LIN handle structure
|
|
* @param u32ApiId ID of the API being called
|
|
* @return LIN_RET_SUCCESS if API call is allowed for current node mode, otherwise error code
|
|
* @details For slave nodes, only allows calls to APIs explicitly permitted (e.g., LIN_DRV_GOTO_SLEEP_ID is blocked)
|
|
*/
|
|
LOCAL_INLINE LIN_ReturnType LIN_AssertMasterApi(LIN_HandleType *pLinHandle, uint32_t u32ApiId)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
if (LIN_NODE_SLAVE == pLinHandle->pCfg->nodeMode)
|
|
{
|
|
if (LIN_DRV_GOTO_SLEEP_ID == u32ApiId)
|
|
{
|
|
ret = LIN_RET_STATE_ERROR;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
#endif /* LIN_DEV_ERROR_REPORT == STD_ON */
|
|
|
|
/**
|
|
* @brief Delay execution for specified clock cycles using assembly loop
|
|
* @param pXfer Pointer to LIN transfer state structure (contains delay counter)
|
|
* @param u32Tick Number of clock cycles to delay
|
|
* @details Implements a simple delay using a decrement loop in assembly.
|
|
* The loop count is calculated based on the functional clock frequency ratio.
|
|
* Typical loop execution: 5-6 clock cycles per iteration.
|
|
*/
|
|
LOCAL_INLINE void LIN_DelayFunctionClk(LIN_XferStateType *pXfer, uint32 u32Tick)
|
|
{
|
|
uint32 u32CountLimit = pXfer->xferDlyCnt * u32Tick / 4;
|
|
/*range 4-7 clk, typical 5-6 clk per loop*/
|
|
__asm volatile(
|
|
"1: subs %[cnt], %[cnt], #1 \n\t"
|
|
" nop \n\t"
|
|
" dsb \n\t"
|
|
" bne 1b \n\t"
|
|
: [cnt] "+r"(u32CountLimit)
|
|
:
|
|
: "cc", "memory"
|
|
);
|
|
}
|
|
/**
|
|
* @brief Reset LIN controller to idle operational or sleep state
|
|
* @param pXfer Pointer to LIN transfer state structure
|
|
* @param pCfg Pointer to LIN configuration structure
|
|
* @details
|
|
*/
|
|
static void LIN_ResetLinController(LIN_XferStateType *pXfer, LIN_ConfigType *pCfg)
|
|
{
|
|
uint32_t val;
|
|
|
|
if ((LIN_CH_SLEEP_PENDING == pXfer->curChnState) || (LIN_CH_SLEEP == pXfer->curChnState))
|
|
{
|
|
val = FCUART_CTRL_ORIE(0U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(0U) | \
|
|
FCUART_CTRL_TE(0U) | FCUART_CTRL_RE(0U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, val);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
FCUART_HWA_FlushTxRxFifo(pXfer->base);
|
|
val = FCUART_CTRL_ORIE(0U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(0U) | \
|
|
FCUART_CTRL_TE(1U) | FCUART_CTRL_RE(1U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, val);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
(void)FCUART_HWA_GetData(pXfer->base);
|
|
pXfer->curFrState = LIN_FRAME_SLEEP_STATE;
|
|
pXfer->cntByte = 0U;
|
|
FCUART_HWA_SetReceiveActiveInterrupt(pXfer->base, true);
|
|
}
|
|
else
|
|
{
|
|
val = FCUART_CTRL_ORIE(0U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(0U) | \
|
|
FCUART_CTRL_TE(0U) | FCUART_CTRL_RE(0U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, val);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
FCUART_HWA_FlushTxRxFifo(pXfer->base);
|
|
val = FCUART_CTRL_ORIE(1U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(1U) | \
|
|
FCUART_CTRL_TE(1U) | FCUART_CTRL_RE(1U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, val);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
FCUART_HWA_SetReceiveActiveInterrupt(pXfer->base, false);
|
|
(void)FCUART_HWA_GetData(pXfer->base);
|
|
pXfer->curFrState = LIN_FRAME_IDLE_STATE;
|
|
if (LIN_NODE_SLAVE == pCfg->nodeMode)
|
|
{
|
|
FCUART_HWA_SetLinBreakDetectEnable(pXfer->base, true);
|
|
FCUART_HWA_SetLinBreakDetectInterrupt(pXfer->base, true);
|
|
}
|
|
pXfer->cntByte = 0U;
|
|
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate optimal baud rate configuration parameters
|
|
* @param pLinHandle LIN handle structure containing configuration
|
|
* @param pCfg Output parameter to store calculated baud rate settings
|
|
* @return LIN_RET_SUCCESS on successful calculation
|
|
* @details
|
|
* - Iterates over possible oversampling ratios (16-32)
|
|
* - For each ratio, calculates baud rate divider (SBR)
|
|
* - Selects the combination with minimum deviation from target baud rate
|
|
* - Stores results in pCfg->u16OvrSamp (oversampling ratio) and pCfg->u16SBR (divider)
|
|
*/
|
|
static LIN_ReturnType LIN_DrvCalBaudrate(LIN_HandleType *pLinHandle, LIN_BaudRateCfgType *pCfg)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
uint32_t clockFreq = pLinHandle->pCfg->funcClkFreq;
|
|
uint32_t sbr = 1U;
|
|
uint32_t osr = 1U;
|
|
uint32_t sbrTmp;
|
|
uint32_t index;
|
|
uint32_t minDiff = 0xFFFFFFFFU;
|
|
uint32_t baudDiff;
|
|
uint32_t temp;
|
|
|
|
for (index = 16U; index <= 32U; index++)
|
|
{
|
|
temp = pLinHandle->pCfg->baudRate * index;
|
|
sbrTmp = (clockFreq + (temp >> 1U)) / temp;
|
|
if (sbrTmp == 0U)
|
|
{
|
|
sbrTmp = 1U;
|
|
}
|
|
else if (sbrTmp >= 8192U)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
/* do nothing */
|
|
}
|
|
|
|
temp = temp * sbrTmp;
|
|
if (temp == clockFreq)
|
|
{
|
|
baudDiff = 0U;
|
|
osr = index;
|
|
sbr = sbrTmp;
|
|
break;
|
|
}
|
|
else if (temp > clockFreq)
|
|
{
|
|
baudDiff = temp - clockFreq;
|
|
}
|
|
else
|
|
{
|
|
baudDiff = clockFreq - temp;
|
|
}
|
|
|
|
if (baudDiff < minDiff)
|
|
{
|
|
minDiff = baudDiff;
|
|
osr = index;
|
|
sbr = sbrTmp;
|
|
}
|
|
}
|
|
pCfg->u16OvrSamp = (uint16_t)osr;
|
|
pCfg->u16SBR = (uint16_t)sbr;
|
|
ret = LIN_RET_SUCCESS;
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Calculate LIN frame checksum
|
|
* @param pXfer Transfer state structure containing PDU information
|
|
* @return Calculated checksum byte
|
|
* @details Implements LIN protocol checksum calculation:
|
|
* - Classic checksum: sum of data bytes only
|
|
* - Enhanced checksum: sum of PID + data bytes
|
|
* Uses inverted modulo 256 sum (two's complement)
|
|
*/
|
|
static uint8_t LIN_DrvMakeCheckSum(LIN_XferStateType *pXfer)
|
|
{
|
|
uint16_t u16Checksum = 0U;
|
|
uint8_t index = 0U;
|
|
|
|
if (LIN_ENHANCED_CS == pXfer->xferPdu.Cs)
|
|
{
|
|
u16Checksum = (uint16_t)(u16Checksum + pXfer->xferPdu.Pid);
|
|
}
|
|
|
|
for (index = 0U; index < pXfer->xferPdu.Dl; index++)
|
|
{
|
|
u16Checksum = (uint16_t)(u16Checksum + pXfer->xferPdu.SduPtr[index]);
|
|
if (u16Checksum > 0xFFU)
|
|
{
|
|
u16Checksum = (uint16_t)(u16Checksum - 0xFFU);
|
|
}
|
|
}
|
|
|
|
return (uint8_t)(~u16Checksum);
|
|
}
|
|
|
|
/**
|
|
* @brief Initiate LIN header transmission (Master only)
|
|
* @param pXfer Transfer state structure
|
|
* @return LIN_RET_SUCCESS on success
|
|
* @details
|
|
* - Configures controller for transmission (enables TX/RX, error interrupts)
|
|
* - Clears status register flags
|
|
* - Flushes TX/RX FIFOs
|
|
* - Sends break field to start frame transmission
|
|
* - Enables transmission complete interrupt
|
|
* - Updates state machine to BREAK_SEND_STATE
|
|
* Note: Only valid for master nodes
|
|
*/
|
|
static LIN_ReturnType LIN_DrvSendHeader(LIN_XferStateType *pXfer)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
uint32_t ctrlRegVal = 0U;
|
|
uint32_t statRegVal;
|
|
|
|
ctrlRegVal = FCUART_CTRL_ORIE(1U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(0U) | \
|
|
FCUART_CTRL_TE(0U) | FCUART_CTRL_RE(0U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, ctrlRegVal);
|
|
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
|
|
FCUART_HWA_FlushTxRxFifo(pXfer->base);
|
|
|
|
ctrlRegVal = FCUART_CTRL_ORIE(1U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(0U) | \
|
|
FCUART_CTRL_TE(1U) | FCUART_CTRL_RE(1U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, ctrlRegVal);
|
|
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
/* clear register status flag */
|
|
statRegVal = FCUART_HWA_GetSTAT(pXfer->base);
|
|
FCUART_HWA_WriteClearSTAT(pXfer->base, statRegVal);
|
|
|
|
/* Send a break filed to protocol. */
|
|
FCUART_HWA_SendBreakField(pXfer->base);
|
|
FCUART_HWA_EnableInterrupt(pXfer->base, FCUART_CTRL_TCIE_MASK);
|
|
|
|
/* Update node state. */
|
|
pXfer->curEventId = LIN_NO_EVENT;
|
|
pXfer->curFrState = LIN_FRAME_BREAK_SEND_STATE;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Prepare LIN frame response data for transmission
|
|
* @param pXfer Transfer state structure (contains current transfer context)
|
|
* @param pCfg LIN configuration structure (contains node mode)
|
|
* @param pLinPdu Protocol Data Unit (PDU) containing frame information
|
|
* @return Operation status:
|
|
* - LIN_RET_SUCCESS: Response data prepared successfully
|
|
* - LIN_RET_STATE_ERROR: Invalid node mode or PDU type
|
|
* @details
|
|
* - For master nodes: Copies PDU data to transfer buffer and calculates checksum
|
|
* - Handles TX/RX response types:
|
|
* - LIN_FRAMERESPONSE_TX: Copy payload data and compute checksum
|
|
* - LIN_FRAMERESPONSE_RX: No action (wait for received data)
|
|
* - Other types: Ignored (slave-to-slave communication)
|
|
* - Resets byte counter and clears current event ID
|
|
*/
|
|
static LIN_ReturnType LIN_DrvSendResponse(LIN_XferStateType *pXfer, LIN_ConfigType *pCfg, LIN_PduType *pLinPdu)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
uint8_t index;
|
|
|
|
if (LIN_NODE_MASTER == pCfg->nodeMode)
|
|
{
|
|
pXfer->xferPdu.Cs = pLinPdu->Cs;
|
|
pXfer->xferPdu.Drc = pLinPdu->Drc;
|
|
pXfer->xferPdu.Dl = pLinPdu->Dl;
|
|
if (LIN_FRAMERESPONSE_TX == pXfer->xferPdu.Drc)
|
|
{
|
|
for (index = 0; index < pXfer->xferPdu.Dl; index++)
|
|
{
|
|
pXfer->xferPdu.SduPtr[index] = pLinPdu->SduPtr[index];
|
|
}
|
|
pXfer->checkSum = LIN_DrvMakeCheckSum(pXfer);
|
|
}
|
|
else if (LIN_FRAMERESPONSE_RX == pXfer->xferPdu.Drc)
|
|
{
|
|
/* do nothing */
|
|
}
|
|
else
|
|
{
|
|
/* slave to slave, ignore */
|
|
}
|
|
pXfer->cntByte = 0U;
|
|
pXfer->curEventId = LIN_NO_EVENT;
|
|
|
|
}
|
|
else
|
|
{
|
|
/* salve node do nothing */
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Handle frame transmission completion interrupt.
|
|
* @param pXfer Pointer to transfer state structure
|
|
* @param pCfg Pointer to LIN configuration structure
|
|
* @details Manages state transitions and hardware reset after frame transmission:
|
|
* - For slave nodes: Resets to idle state unless in sleep mode
|
|
* - For master nodes:
|
|
* - If in BREAK_SEND_STATE: Reads data to prevent overrun, delays for delimiter,
|
|
* configures FIFO watermark, sends sync byte (0x55) and PID, updates state to HEADER_SEND_STATE
|
|
* - Otherwise: Resets to idle state
|
|
* Clears transmission complete flag in status register
|
|
*/
|
|
static void LIN_DrvFrameTxHandle(LIN_XferStateType *pXfer, LIN_ConfigType *pCfg)
|
|
{
|
|
if (LIN_NODE_SLAVE == pCfg->nodeMode)
|
|
{
|
|
if (LIN_FRAME_SLEEP_STATE == pXfer->curFrState)
|
|
{
|
|
/* need wakeup first */
|
|
}
|
|
else
|
|
{
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (LIN_FRAME_BREAK_SEND_STATE == pXfer->curFrState)
|
|
{
|
|
/* receive data to avoid overrun error occurred */
|
|
(void)FCUART_HWA_GetData(pXfer->base);
|
|
/* need delay 0.1 delimiter (5~20us) !!!!!!! */
|
|
LIN_DelayFunctionClk(pXfer, 100U);
|
|
FCUART_HWA_SetRxWaterMark(pXfer->base, 1U);
|
|
FCUART_HWA_SetData(pXfer->base, (uint32_t)0x55U);
|
|
FCUART_HWA_SetData(pXfer->base, (uint32_t)pXfer->xferPdu.Pid);
|
|
FCUART_HWA_DisableInterrupt(pXfer->base, (uint32_t)FCUART_CTRL_TCIE_MASK);
|
|
FCUART_HWA_EnableInterrupt(pXfer->base, (uint32_t)FCUART_CTRL_RIE_MASK);
|
|
pXfer->curFrState = LIN_FRAME_HEADER_SEND_STATE;
|
|
pXfer->curChnState = LIN_TX_HEADER_BUSY;
|
|
}
|
|
else
|
|
{
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
}
|
|
FCUART_HWA_WriteClearSTAT(pXfer->base, (uint32_t)FCUART_STAT_TCF_MASK);
|
|
}
|
|
/**
|
|
* @brief Handle LIN break field reception interrupt.
|
|
* @param pXfer Pointer to transfer state structure
|
|
* @param pCfg Pointer to LIN configuration structure
|
|
* @details Processes break field detection on LIN bus:
|
|
* - For slave nodes:
|
|
* - If in sleep mode: Clears event ID (no action)
|
|
* - If in idle mode: Reads data to clear frame error, disables break detection interrupt,
|
|
* enables receive interrupt, updates state to BREAK_RECV_STATE
|
|
* - Otherwise: Resets to idle state
|
|
* - For master nodes: Resets to idle state (break interrupt disabled)
|
|
* Clears break detect flag in status register
|
|
*/
|
|
static void LIN_DrvFrameRxBreakHandle(LIN_XferStateType *pXfer, LIN_ConfigType *pCfg)
|
|
{
|
|
if (LIN_NODE_SLAVE == pCfg->nodeMode)
|
|
{
|
|
if (LIN_FRAME_SLEEP_STATE == pXfer->curFrState)
|
|
{
|
|
/* break field can also be used as wakeup signal */
|
|
FCUART_HWA_SetReceiveDataInverse(pXfer->base, false);
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
pXfer->curEventId = LIN_WAKEUP_SIGNAL;
|
|
pXfer->curChnState = LIN_CH_OPERATIONAL;
|
|
if (NULL != pCfg->callback)
|
|
{
|
|
pCfg->callback(pXfer->u8LinChannel, pXfer->curEventId, &pXfer->xferPdu, &pXfer->checkSum);
|
|
}
|
|
}
|
|
else if ((LIN_FRAME_IDLE_STATE == pXfer->curFrState))
|
|
{
|
|
/* receive break field will generate frame error */
|
|
(void)FCUART_HWA_GetData(pXfer->base);
|
|
FCUART_HWA_WriteClearSTAT(pXfer->base, (uint32_t)FCUART_STAT_FEF_MASK);
|
|
FCUART_HWA_SetLinBreakDetectInterrupt(pXfer->base, false);
|
|
FCUART_HWA_SetLinBreakDetectEnable(pXfer->base, false);
|
|
FCUART_HWA_EnableInterrupt(pXfer->base, FCUART_CTRL_RIE_MASK);
|
|
FCUART_HWA_SetRxWaterMark(pXfer->base, 1U);
|
|
pXfer->curFrState = LIN_FRAME_BREAK_RECV_STATE;
|
|
pXfer->curEventId = LIN_RECV_BREAK_FIELD_OK;
|
|
pXfer->curChnState = LIN_RX_HEADER_BUSY;
|
|
if (NULL != pCfg->callback)
|
|
{
|
|
pCfg->callback(pXfer->u8LinChannel, pXfer->curEventId, &pXfer->xferPdu, &pXfer->checkSum);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* shouldn't enter this branch */
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* in master mode, break interrupt disable */
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
FCUART_HWA_WriteClearSTAT(pXfer->base, (uint32_t)FCUART_STAT_LBKDIF_MASK);
|
|
}
|
|
|
|
/**
|
|
* @brief Handle header reception completion in data receive flow.
|
|
* @param pXfer Pointer to transfer state structure
|
|
* @param pCfg Pointer to LIN configuration structure
|
|
* @details Configures FIFO and updates state after header reception:
|
|
* - For TX response type:
|
|
* - Sends payload data and checksum to FIFO
|
|
* - Sets FIFO watermark and updates state to RESPONSE_SEND_STATE
|
|
* - For RX response type:
|
|
* - Sets FIFO watermark based on payload length
|
|
* - Updates state to RESPONSE_RECV_STATE
|
|
* - Invalid response types: Reset to idle state
|
|
*/
|
|
static void LIN_DrvFrameRxData_HeaderRecvHandle(LIN_XferStateType *pXfer, LIN_ConfigType *pCfg)
|
|
{
|
|
uint8_t index;
|
|
if (LIN_FRAMERESPONSE_TX == pXfer->xferPdu.Drc)
|
|
{
|
|
for (index = 0; index < pXfer->xferPdu.Dl; index++)
|
|
{
|
|
FCUART_HWA_SetData(pXfer->base, (uint32_t)(pXfer->xferPdu.SduPtr[index]));
|
|
}
|
|
FCUART_HWA_SetData(pXfer->base, (uint32_t)pXfer->checkSum);
|
|
FCUART_HWA_SetRxWaterMark(pXfer->base, pXfer->xferPdu.Dl);
|
|
pXfer->curFrState = LIN_FRAME_RESPONSE_SEND_STATE;
|
|
pXfer->curChnState = LIN_TX_RESP_BUSY;
|
|
}
|
|
else if (LIN_FRAMERESPONSE_RX == pXfer->xferPdu.Drc)
|
|
{
|
|
FCUART_HWA_SetRxWaterMark(pXfer->base, pXfer->xferPdu.Dl);
|
|
pXfer->curFrState = LIN_FRAME_RESPONSE_RECV_STATE;
|
|
pXfer->curChnState = LIN_RX_RESP_BUSY;
|
|
}
|
|
else
|
|
{
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Handle data reception during response transmission (TX mode)
|
|
* @param pXfer Pointer to LIN transfer state structure
|
|
* @param pCfg Pointer to LIN configuration structure
|
|
* @param rxCnt Number of received bytes in FIFO
|
|
* @details
|
|
* - Validates received data against expected payload
|
|
* - Updates FIFO watermark and continues transmission if more data is needed
|
|
* - Checks checksum on final data byte
|
|
* - Updates state to OK/error and triggers callback if validation passes
|
|
* - Resets to idle state on validation failure
|
|
*/
|
|
static void LIN_DrvFrameRxData_ResponseSndHandle(LIN_XferStateType *pXfer, LIN_ConfigType *pCfg, uint8_t rxCnt)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
bool bFef;
|
|
uint8_t data;
|
|
while ((pXfer->cntByte < pXfer->xferPdu.Dl) && (rxCnt > 0U))
|
|
{
|
|
bFef = FCUART_HWA_GetDataWithFef(pXfer->base, &data);
|
|
if ((bFef) || (data != pXfer->xferPdu.SduPtr[pXfer->cntByte]))
|
|
{
|
|
ret = LIN_RET_STATE_ERROR;
|
|
break;
|
|
}
|
|
pXfer->cntByte++;
|
|
rxCnt--;
|
|
}
|
|
|
|
if (LIN_RET_SUCCESS == ret)
|
|
{
|
|
if (rxCnt == 0U)
|
|
{
|
|
pXfer->curEventId = LIN_REPONSE_ERROR;
|
|
pXfer->curChnState = LIN_TX_RESP_ERROR;
|
|
}
|
|
else
|
|
{
|
|
bFef = FCUART_HWA_GetDataWithFef(pXfer->base, &data);
|
|
if (bFef)
|
|
{
|
|
pXfer->curEventId = LIN_FRAME_ERROR;
|
|
pXfer->curChnState = LIN_TX_RESP_ERROR;
|
|
}
|
|
else if (pXfer->checkSum == data)
|
|
{
|
|
pXfer->curFrState = LIN_FRAME_RESPONSE_SEND_OK_STATE;
|
|
if (1U == pXfer->sleepSignal)
|
|
{
|
|
pXfer->curChnState = LIN_CH_SLEEP_PENDING;
|
|
}
|
|
else
|
|
{
|
|
pXfer->curChnState = LIN_TX_RESP_OK;
|
|
}
|
|
pXfer->curEventId = LIN_TX_COMPLETED;
|
|
}
|
|
else
|
|
{
|
|
pXfer->curEventId = LIN_CHECKSUM_ERROR;
|
|
pXfer->curChnState = LIN_TX_RESP_ERROR;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bFef)
|
|
{
|
|
pXfer->curEventId = LIN_FRAME_ERROR;
|
|
}
|
|
else
|
|
{
|
|
pXfer->curEventId = LIN_READBACK_ERROR;
|
|
}
|
|
pXfer->curChnState = LIN_TX_RESP_ERROR;
|
|
}
|
|
|
|
if (LIN_TX_RESP_BUSY == pXfer->curChnState)
|
|
{
|
|
/* do nothing */
|
|
}
|
|
else
|
|
{
|
|
if (NULL != pCfg->callback)
|
|
{
|
|
pCfg->callback(pXfer->u8LinChannel, pXfer->curEventId, &pXfer->xferPdu, &pXfer->checkSum);
|
|
}
|
|
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Handle data reception during response reception (RX mode)
|
|
* @param pXfer Pointer to LIN transfer state structure
|
|
* @param pCfg Pointer to LIN configuration structure
|
|
* @param rxCnt Number of received bytes in FIFO
|
|
* @details
|
|
* - Reads received data into payload buffer
|
|
* - Updates FIFO watermark for remaining data
|
|
* - Checks checksum on final data byte
|
|
* - Updates state to OK and triggers callback if reception completes
|
|
* - Resets to idle state on reception failure
|
|
*/
|
|
static void LIN_DrvFrameRxData_ResponseRecvHandle(LIN_XferStateType *pXfer, LIN_ConfigType *pCfg, uint8_t rxCnt)
|
|
{
|
|
bool bFef;
|
|
uint8_t data;
|
|
while ((pXfer->cntByte < pXfer->xferPdu.Dl) && (rxCnt > 0U))
|
|
{
|
|
bFef = FCUART_HWA_GetDataWithFef(pXfer->base, &data);
|
|
if (bFef)
|
|
{
|
|
pXfer->curEventId = LIN_FRAME_ERROR;
|
|
pXfer->curChnState = LIN_RX_RESP_ERROR;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
pXfer->xferPdu.SduPtr[pXfer->cntByte] = data;
|
|
pXfer->cntByte++;
|
|
rxCnt--;
|
|
}
|
|
}
|
|
|
|
|
|
if (LIN_RX_RESP_BUSY == pXfer->curChnState)
|
|
{
|
|
if (rxCnt == 0U)
|
|
{
|
|
pXfer->curEventId = LIN_REPONSE_ERROR;
|
|
pXfer->curChnState = LIN_RX_RESP_ERROR;
|
|
}
|
|
else
|
|
{
|
|
bFef = FCUART_HWA_GetDataWithFef(pXfer->base, &data);
|
|
if (bFef)
|
|
{
|
|
pXfer->curEventId = LIN_FRAME_ERROR;
|
|
pXfer->curChnState = LIN_RX_RESP_ERROR;
|
|
}
|
|
else
|
|
{
|
|
pXfer->checkSum = data;
|
|
pXfer->curFrState = LIN_FRAME_RESPONSE_RECV_OK_STATE;
|
|
pXfer->curChnState = LIN_RX_RESP_OK;
|
|
pXfer->curEventId = LIN_RX_COMPLETED;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (LIN_RX_RESP_BUSY == pXfer->curChnState)
|
|
{
|
|
/* do nothing */
|
|
}
|
|
else
|
|
{
|
|
if (NULL != pCfg->callback)
|
|
{
|
|
pCfg->callback(pXfer->u8LinChannel, pXfer->curEventId, &pXfer->xferPdu, &pXfer->checkSum);
|
|
}
|
|
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Main handler for LIN data reception interrupts
|
|
* @param pXfer Pointer to LIN transfer state structure
|
|
* @param pCfg Pointer to LIN configuration structure
|
|
* @details
|
|
* - Checks current frame state to determine processing flow
|
|
* - Handles break field reception (validates sync byte and PID)
|
|
* - Handles header transmission (validates sync byte and PID)
|
|
* - Delegates response transmission/reception to specific handlers
|
|
* - Resets to idle state for unrecognized frame states
|
|
* - Clears data reception flag in status register
|
|
*/
|
|
static void LIN_DrvFrameRxDataHandle(LIN_XferStateType *pXfer, LIN_ConfigType *pCfg)
|
|
{
|
|
uint8_t rxCnt;
|
|
uint8_t data;
|
|
bool bFef;
|
|
|
|
rxCnt = FCUART_HWA_GetFifoRxCount(pXfer->base);
|
|
if (LIN_FRAME_SLEEP_STATE == pXfer->curFrState)
|
|
{
|
|
/* need wake-up first */
|
|
pXfer->curEventId = LIN_NO_EVENT;
|
|
}
|
|
else if (LIN_FRAME_BREAK_RECV_STATE == pXfer->curFrState)
|
|
{
|
|
if (rxCnt >= 2U)
|
|
{
|
|
bFef = FCUART_HWA_GetDataWithFef(pXfer->base, &data);
|
|
if (bFef)
|
|
{
|
|
pXfer->curEventId = LIN_FRAME_ERROR;
|
|
pXfer->curChnState = LIN_RX_HEADER_ERROR;
|
|
}
|
|
else if (0x55U == data)
|
|
{
|
|
bFef = FCUART_HWA_GetDataWithFef(pXfer->base, &data);
|
|
if (bFef)
|
|
{
|
|
pXfer->curEventId = LIN_FRAME_ERROR;
|
|
pXfer->curChnState = LIN_RX_HEADER_ERROR;
|
|
}
|
|
else
|
|
{
|
|
pXfer->xferPdu.Pid = data;
|
|
pXfer->curFrState = LIN_FRAME_HEADER_RECV_STATE;
|
|
pXfer->curEventId = LIN_HEADER_OK;
|
|
pXfer->curChnState = LIN_RX_HEADER_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pXfer->curEventId = LIN_SYNC_ERROR;
|
|
pXfer->curChnState = LIN_RX_HEADER_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pXfer->curEventId = LIN_HEADER_ERROR;
|
|
pXfer->curChnState = LIN_RX_HEADER_ERROR;
|
|
}
|
|
|
|
if (NULL != pCfg->callback)
|
|
{
|
|
pCfg->callback(pXfer->u8LinChannel, pXfer->curEventId, &pXfer->xferPdu, &pXfer->checkSum);
|
|
}
|
|
else
|
|
{
|
|
pXfer->curChnState = LIN_CALLBACK_ERROR;
|
|
}
|
|
|
|
if (LIN_RX_HEADER_OK == pXfer->curChnState)
|
|
{
|
|
LIN_DrvFrameRxData_HeaderRecvHandle(pXfer, pCfg);
|
|
}
|
|
else
|
|
{
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
|
|
}
|
|
else if (LIN_FRAME_HEADER_SEND_STATE == pXfer->curFrState)
|
|
{
|
|
if (rxCnt >= 2U)
|
|
{
|
|
bFef = FCUART_HWA_GetDataWithFef(pXfer->base, &data);
|
|
if (bFef)
|
|
{
|
|
pXfer->curEventId = LIN_FRAME_ERROR;
|
|
pXfer->curChnState = LIN_TX_HEADER_ERROR;
|
|
}
|
|
else if (0x55U == data)
|
|
{
|
|
bFef = FCUART_HWA_GetDataWithFef(pXfer->base, &data);
|
|
if (bFef)
|
|
{
|
|
pXfer->curEventId = LIN_FRAME_ERROR;
|
|
pXfer->curChnState = LIN_TX_HEADER_ERROR;
|
|
}
|
|
else if (pXfer->xferPdu.Pid == data)
|
|
{
|
|
pXfer->curEventId = LIN_HEADER_OK;
|
|
pXfer->curChnState = LIN_TX_HEADER_OK;
|
|
}
|
|
else
|
|
{
|
|
pXfer->curEventId = LIN_PID_ERROR;
|
|
pXfer->curChnState = LIN_TX_HEADER_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pXfer->curEventId = LIN_SYNC_ERROR;
|
|
pXfer->curChnState = LIN_TX_HEADER_ERROR;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pXfer->curEventId = LIN_HEADER_ERROR;
|
|
pXfer->curChnState = LIN_TX_HEADER_ERROR;
|
|
}
|
|
|
|
if (NULL != pCfg->callback)
|
|
{
|
|
pCfg->callback(pXfer->u8LinChannel, pXfer->curEventId, &pXfer->xferPdu, &pXfer->checkSum);
|
|
}
|
|
|
|
if (LIN_TX_HEADER_OK == pXfer->curChnState)
|
|
{
|
|
LIN_DrvFrameRxData_HeaderRecvHandle(pXfer, pCfg);
|
|
}
|
|
else
|
|
{
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
}
|
|
else if (LIN_FRAME_RESPONSE_SEND_STATE == pXfer->curFrState)
|
|
{
|
|
LIN_DrvFrameRxData_ResponseSndHandle(pXfer, pCfg, rxCnt);
|
|
}
|
|
else if (LIN_FRAME_RESPONSE_RECV_STATE == pXfer->curFrState)
|
|
{
|
|
LIN_DrvFrameRxData_ResponseRecvHandle(pXfer, pCfg, rxCnt);
|
|
}
|
|
else
|
|
{
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
FCUART_HWA_WriteClearSTAT(pXfer->base, (uint32_t)FCUART_STAT_RDRFF_MASK);
|
|
}
|
|
|
|
/**
|
|
* @brief Handle LIN controller error conditions
|
|
* @param pXfer Pointer to LIN transfer state structure (contains current frame/channel state)
|
|
* @param pCfg Pointer to LIN configuration structure (contains callback functions)
|
|
* @details
|
|
* - In sleep state: Reads data register to clear error flags
|
|
* - In operational state:
|
|
* - Detects overrun errors (FCUART_STAT_RORF_MASK) and sets event ID to LIN_RX_OVERRUN
|
|
* - Detects frame errors (when not in break send/recv/idle states) and sets event ID to LIN_FRAME_ERROR
|
|
* - Triggers user callback with error event ID if errors occur
|
|
* - Resets controller to idle state after error handling
|
|
* - Clears error flags in status register
|
|
*/
|
|
static void LIN_DrvErrorHandle(LIN_XferStateType *pXfer, LIN_ConfigType *pCfg)
|
|
{
|
|
uint32_t u32Stat;
|
|
uint8_t err = 0U;
|
|
|
|
if (LIN_FRAME_SLEEP_STATE == pXfer->curFrState)
|
|
{
|
|
/* read data register to clear error */
|
|
(void)FCUART_HWA_GetData(pXfer->base);
|
|
}
|
|
else
|
|
{
|
|
u32Stat = FCUART_HWA_GetSTAT(pXfer->base);
|
|
if (u32Stat & FCUART_STAT_RORF_MASK)
|
|
{
|
|
pXfer->curEventId = LIN_RX_OVERRUN;
|
|
err = 1U;
|
|
}
|
|
else if (u32Stat & FCUART_STAT_FEF_MASK)
|
|
{
|
|
if ((LIN_FRAME_BREAK_SEND_STATE != pXfer->curFrState) &&
|
|
(LIN_FRAME_IDLE_STATE != pXfer->curFrState))
|
|
{
|
|
/* may receive new break field during last frame reception */
|
|
pXfer->curEventId = LIN_FRAME_ERROR;
|
|
err = 1U;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* do nothing */
|
|
}
|
|
|
|
if (err)
|
|
{
|
|
if ((LIN_FRAME_IDLE_STATE == pXfer->curFrState) ||
|
|
(LIN_FRAME_BREAK_RECV_STATE == pXfer->curFrState) ||
|
|
(LIN_FRAME_HEADER_RECV_STATE == pXfer->curFrState) ||
|
|
(LIN_FRAME_RESPONSE_SEND_OK_STATE == pXfer->curFrState) ||
|
|
(LIN_FRAME_RESPONSE_RECV_OK_STATE == pXfer->curFrState))
|
|
{
|
|
pXfer->curChnState = LIN_RX_HEADER_ERROR;
|
|
}
|
|
else if ((LIN_FRAME_BREAK_SEND_STATE == pXfer->curFrState) ||
|
|
(LIN_FRAME_HEADER_SEND_STATE == pXfer->curFrState))
|
|
{
|
|
pXfer->curChnState = LIN_TX_HEADER_ERROR;
|
|
}
|
|
else if (LIN_FRAME_RESPONSE_SEND_STATE == pXfer->curFrState)
|
|
{
|
|
pXfer->curChnState = LIN_TX_RESP_ERROR;
|
|
}
|
|
else if (LIN_FRAME_RESPONSE_RECV_STATE == pXfer->curFrState)
|
|
{
|
|
pXfer->curChnState = LIN_RX_RESP_ERROR;
|
|
}
|
|
else
|
|
{
|
|
/* do nothing */
|
|
}
|
|
|
|
if (NULL != pCfg->callback)
|
|
{
|
|
pCfg->callback(pXfer->u8LinChannel, pXfer->curEventId, &pXfer->xferPdu, &pXfer->checkSum);
|
|
}
|
|
|
|
/* Error occurred, enter IDLE state. */
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
}
|
|
(void)pCfg;
|
|
}
|
|
|
|
|
|
/**
|
|
* @brief Handle LIN wake-up signal detection and timing calculation
|
|
* @param pXfer Pointer to LIN transfer state structure (stores wake-up timestamp)
|
|
* @param pCfg Pointer to LIN configuration structure (contains time callback function)
|
|
* @details
|
|
* - Step 1: On first receive active interrupt (low level on RX pin):
|
|
* - Records timestamp using user-provided time callback
|
|
* - Enables data inversion to detect rising edge
|
|
* - Step 2: On second receive active interrupt (rising edge):
|
|
* - Disables data inversion
|
|
* - Recalculates timestamp and checks if interval > 150us (valid wake-up signal)
|
|
* - If valid: Resets to idle state, sets event ID to LIN_WAKEUP_SIGNAL, and triggers callback
|
|
* - If invalid: Clears stored timestamp
|
|
* - Requires user to provide time callback via pCfg->timeCallback for accurate timing
|
|
*/
|
|
static void LIN_DrvWakeupHandle(LIN_XferStateType *pXfer, LIN_ConfigType *pCfg)
|
|
{
|
|
uint64_t timestamp;
|
|
/* Step 1, record the first receive active time. */
|
|
if (false == FCUART_HWA_GetReceiveDataInverse(pXfer->base))
|
|
{
|
|
/* Call the user time value call function. */
|
|
if (NULL != pCfg->timeCallback)
|
|
{
|
|
pCfg->timeCallback(×tamp);
|
|
pXfer->wakeupTime = timestamp;
|
|
}
|
|
else
|
|
{
|
|
pXfer->wakeupTime = 0U;
|
|
}
|
|
|
|
/* Change the receive data inverse, and receive the next interrupt. */
|
|
FCUART_HWA_SetReceiveDataInverse(pXfer->base, true);
|
|
}
|
|
/* Step 2. calculate the wake-up time. */
|
|
else
|
|
{
|
|
FCUART_HWA_SetReceiveDataInverse(pXfer->base, false);
|
|
|
|
/* Call the user time value call function. */
|
|
if (NULL != pCfg->timeCallback)
|
|
{
|
|
pCfg->timeCallback(×tamp);
|
|
if (timestamp > pXfer->wakeupTime)
|
|
{
|
|
if ((timestamp - pXfer->wakeupTime) > 150U)
|
|
{
|
|
pXfer->curEventId = LIN_WAKEUP_SIGNAL;
|
|
pXfer->curChnState = LIN_CH_OPERATIONAL;
|
|
if (NULL != pCfg->callback)
|
|
{
|
|
pCfg->callback(pXfer->u8LinChannel, pXfer->curEventId, &pXfer->xferPdu, &pXfer->checkSum);
|
|
}
|
|
LIN_ResetLinController(pXfer, pCfg);
|
|
}
|
|
else
|
|
{
|
|
pXfer->wakeupTime = 0U;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pXfer->wakeupTime = 0U;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* do nothing */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* ################################################################################## */
|
|
/* ################################ Global Functions ################################ */
|
|
|
|
/**
|
|
* @brief Generate LIN PID parity bits according to LIN protocol specification
|
|
* @param PID Input raw PID value (6-bit, without parity bits)
|
|
* @return 8-bit PID value with parity bits (bits 6 and 7)
|
|
* @details Calculates two parity bits:
|
|
* - P0 (bit 6): XOR of PID bits 0,1,2,4
|
|
* - P1 (bit 7): XOR of PID bits 1,3,4,5 (inverted)
|
|
* Combines raw PID with parity bits to form final 8-bit identifier
|
|
*/
|
|
uint8_t LIN_DrvParityMake(uint8_t PID)
|
|
{
|
|
uint8_t parity;
|
|
uint8_t retVal;
|
|
|
|
parity = (uint8_t)((((0xFFU & BIT(PID, 0U)) ^ (0xFFU & BIT(PID, 1U)) ^
|
|
(0xFFU & BIT(PID, 2U)) ^ (0xFFU & BIT(PID, 4U))) << 6U) |
|
|
((0xFFU ^ (BIT(PID, 1U)) ^ (0xFFU & BIT(PID, 3U)) ^
|
|
(0xFFU & BIT(PID, 4U)) ^ (0xFFU & BIT(PID, 5U))) << 7U));
|
|
|
|
/* Making parity bits */
|
|
retVal = (uint8_t)(PID | parity);
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/**
|
|
* @brief Validate LIN PID parity bits according to LIN protocol specification
|
|
* @param PID Input 8-bit PID value (with parity bits in bits 6 and 7)
|
|
* @return Validated raw PID value (6-bit) or 0xFFU if parity check fails
|
|
* @details Recalculates expected parity bits from input PID's data bits (0-5),
|
|
* compares with actual parity bits (6-7) in input PID:
|
|
* - If match: returns raw PID (bits 0-5)
|
|
* - If mismatch: returns 0xFFU (invalid PID)
|
|
*/
|
|
uint8_t LIN_DrvParityCheck(uint8_t PID)
|
|
{
|
|
uint8_t parity;
|
|
uint8_t retVal;
|
|
|
|
parity = (uint8_t)((((0xFFU & BIT(PID, 0U)) ^ (0xFFU & BIT(PID, 1U)) ^
|
|
(0xFFU & BIT(PID, 2U)) ^ (0xFFU & BIT(PID, 4U))) << 6U) |
|
|
((0xFFU ^ (BIT(PID, 1U)) ^ (0xFFU & BIT(PID, 3U)) ^
|
|
(0xFFU & BIT(PID, 4U)) ^ (0xFFU & BIT(PID, 5U))) << 7U));
|
|
|
|
/* If parity bits are incorrect */
|
|
if ((PID & 0xC0U) != parity)
|
|
{
|
|
retVal = 0xFFU;
|
|
}
|
|
else
|
|
{
|
|
retVal = (uint8_t)(PID & 0x3FU);
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize LIN controller instance.
|
|
* @param pLinHandle Pointer to LIN handle structure (stores configuration and state)
|
|
* @param pConfig Pointer to LIN configuration structure (contains node mode, baudrate, etc.)
|
|
* @return Operation status:
|
|
* - LIN_RET_SUCCESS: Initialization succeeded
|
|
* - LIN_RET_ERROR: Baudrate configuration failed
|
|
* @details Configures hardware registers and initializes transfer state:
|
|
* 1. Validates input pointers and hardware index
|
|
* 2. Resets hardware to default state
|
|
* 3. Calculates and sets baudrate configuration (oversampling ratio and divider)
|
|
* 4. Configures FIFO (TX/RX enable, depth) and watermark levels
|
|
* 5. Sets control register (error interrupts, parity, mode)
|
|
* 6. Initializes transfer state (buffer pointer, frame/channel state)
|
|
* 7. Enables receive active interrupt and starts LIN controller
|
|
*/
|
|
LIN_ReturnType LIN_DrvInit(LIN_HandleType *pLinHandle, LIN_ConfigType *pConfig)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
uint32_t baudRegVal = 0U;
|
|
uint32_t ctrlRegVal = 0U;
|
|
uint32_t statRegVal = 0U;
|
|
uint32_t fifoRegVal = 0U;
|
|
uint32_t watermarkRegVal = 0U;
|
|
LIN_ConfigType *pCfg;
|
|
LIN_XferStateType *pXfer;
|
|
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
if (LIN_RET_SUCCESS != LIN_AssertLinInit(pLinHandle, pConfig))
|
|
{
|
|
LIN_ReportDevError(LIN_INIT_ID, LIN_E_PARAM_POINTER);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertLinHwIndex(pConfig))
|
|
{
|
|
LIN_ReportDevError(LIN_INIT_ID, LIN_E_PARAM_INSTANCE);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pLinHandle->pCfg = pConfig;
|
|
pLinHandle->pXfer = &pConfig->xferState;
|
|
pCfg = pLinHandle->pCfg;
|
|
pXfer = pLinHandle->pXfer;
|
|
|
|
pXfer->u8LinChannel = pLinHandle->u8LinChannel;
|
|
pXfer->base = s_aFCUART_InstanceTable[pCfg->u8LinHwIndex];
|
|
|
|
/* Reset the hardware LIN instance to expect state. */
|
|
FCUART_HWA_SoftwareReset(pXfer->base);
|
|
|
|
pXfer->xferDlyCnt = (uint16_t)(pCfg->coreClkFreq / pCfg->funcClkFreq);
|
|
ret = LIN_DrvCalBaudrate(pLinHandle, &pXfer->xferBrCfg);
|
|
pXfer->xferBrCfg.u16OvrSamp = (uint16_t)(pXfer->xferBrCfg.u16OvrSamp - 1U);
|
|
baudRegVal = FCUART_BAUD_OVR_SAMP(pXfer->xferBrCfg.u16OvrSamp) |
|
|
FCUART_BAUD_BEDGE_SAMP(1U) | FCUART_BAUD_SBR(pXfer->xferBrCfg.u16SBR);
|
|
FCUART_HWA_SetBaud(pXfer->base, baudRegVal);
|
|
|
|
/* Set LIN break send and detect to 13 bits minimum. If in slave node, only enable detect feature. */
|
|
statRegVal |= FCUART_STAT_BCGL(1U) | FCUART_STAT_LBKDE(0U);
|
|
FCUART_HWA_WriteClearSTAT(pXfer->base, statRegVal);
|
|
|
|
fifoRegVal = FCUART_FIFO_TXFEN(1U) | FCUART_FIFO_RXFEN(1U);
|
|
FCUART_HWA_SetFifo(pXfer->base, fifoRegVal);
|
|
|
|
watermarkRegVal = FCUART_WATERMARK_RXWATER(0U) | FCUART_WATERMARK_TXWATER(0U);
|
|
FCUART_HWA_SetWaterMark(pXfer->base, watermarkRegVal);
|
|
|
|
ctrlRegVal = FCUART_CTRL_ORIE(0U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(0U) |
|
|
FCUART_CTRL_BMSEL(0U) | FCUART_CTRL_PE(0U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, ctrlRegVal);
|
|
|
|
/* Initialize the xfer state of LIN transfer. */
|
|
pXfer->xferPdu.SduPtr = pXfer->xferBuff;
|
|
pXfer->curFrState = LIN_FRAME_SLEEP_STATE;
|
|
pXfer->curChnState = LIN_CH_SLEEP;
|
|
|
|
if (LIN_NODE_SLAVE == pCfg->nodeMode)
|
|
{
|
|
FCUART_HWA_SetLinBreakDetectEnable(pXfer->base, true);
|
|
FCUART_HWA_SetLinBreakDetectInterrupt(pXfer->base, true);
|
|
}
|
|
/* need wakeup first */
|
|
FCUART_HWA_SetReceiveActiveInterrupt(pXfer->base, true);
|
|
|
|
FCUART_HWA_StartTransfer(pXfer->base);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief De-initialize LIN controller instance.
|
|
* @param pLinHandle Pointer to LIN handle structure
|
|
* @details
|
|
* - Resets FCUART hardware to default state
|
|
* - Clears internal pointers in LIN handle (pCfg, pXfer)
|
|
* - Disables interrupts and stops TX/RX transfer
|
|
* @note This function does not return a status; error checking is done via LIN_DEV_ERROR_REPORT
|
|
*/
|
|
void LIN_DrvDeInit(LIN_HandleType *pLinHandle)
|
|
{
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
if (LIN_RET_SUCCESS != LIN_AssertLinHandle(pLinHandle))
|
|
{
|
|
LIN_ReportDevError(LIN_DEINIT_ID, LIN_E_PARAM_POINTER);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertLinHwIndex(pLinHandle->pCfg))
|
|
{
|
|
LIN_ReportDevError(LIN_DEINIT_ID, LIN_E_PARAM_INSTANCE);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
LIN_XferStateType *pXfer = pLinHandle->pXfer;
|
|
|
|
/* Reset the FCUART instance. */
|
|
FCUART_HWA_SoftwareReset(pXfer->base);
|
|
|
|
/* Update LIN instance to initialize state. */
|
|
pLinHandle->pCfg = NULL;
|
|
pLinHandle->pXfer = NULL;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize default LIN node configuration parameters.
|
|
* @param eNodeMode LIN node mode (LIN_NODE_MASTER or LIN_NODE_SLAVE)
|
|
* @param pConfig Pointer to configuration structure to be initialized
|
|
* @details Sets default values for:
|
|
* - nodeMode: Input node mode (master/slave)
|
|
* - baudRate: Default baudrate (19200 bps)
|
|
* - timeCallback: NULL (user must set if needed)
|
|
* - xferState: Initial state (LIN_NOT_OK, LIN_FRAME_SLEEP_STATE)
|
|
* @note pConfig must be a valid pointer (checked via LIN_DEV_ERROR_REPORT)
|
|
*/
|
|
void LIN_DrvGetDefaultConfig(LIN_NodeType eNodeMode, LIN_ConfigType *pConfig)
|
|
{
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
/* Check NUll ptr for bus error. */
|
|
if (pConfig == NULL)
|
|
{
|
|
LIN_ReportDevError(LIN_GET_DEFAULT_CFG_ID, LIN_E_PARAM_POINTER);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
pConfig->nodeMode = eNodeMode; /**< Node mode as Master node or Slave node. 0: slave 1: master */
|
|
pConfig->baudRate = 19200U; /**< baudrate configurations for LIN protocol. */
|
|
pConfig->timeCallback = NULL; /**< Callback function to get time interval in nanoseconds */
|
|
pConfig->xferState.curChnState = LIN_NOT_OK;
|
|
pConfig->xferState.curFrState = LIN_FRAME_SLEEP_STATE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Send a LIN frame over the bus (Master/Slave mode).
|
|
* @param pLinHandle Pointer to LIN handle structure (contains configuration and state)
|
|
* @param pLinPdu Pointer to Protocol Data Unit (PDU) containing frame information (PID, data, etc.)
|
|
* @return Operation status:
|
|
* - LIN_RET_SUCCESS: Frame transmission initiated successfully
|
|
* - LIN_RET_STATE_ERROR: Node state is invalid for transmission
|
|
* - LIN_RET_ERROR: Parameter validation failed (via LIN_DEV_ERROR_REPORT)
|
|
* @details
|
|
* - Validates input parameters and node state
|
|
* - For master nodes: Prepares response buffer and sends header
|
|
* - Updates transfer state to LIN_TX_BUSY during transmission
|
|
* - Slave nodes: No action (only master initiates frame transmission)
|
|
*/
|
|
LIN_ReturnType LIN_DrvSendFrame(LIN_HandleType *pLinHandle, LIN_PduType *pLinPdu)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
LIN_ConfigType *pCfg = pLinHandle->pCfg;
|
|
LIN_XferStateType *pXfer = pLinHandle->pXfer;
|
|
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
if (LIN_RET_SUCCESS != LIN_AssertLinHandle(pLinHandle))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_SEND_FRAME_ID, LIN_E_PARAM_POINTER);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertLinHwIndex(pLinHandle->pCfg))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_SEND_FRAME_ID, LIN_E_PARAM_INSTANCE);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertLinPdu(pLinPdu))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_SEND_FRAME_ID, LIN_E_PARAM_POINTER);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Check node state. */
|
|
if ((pXfer->curChnState < LIN_TX_HEADER_OK) || (pXfer->curChnState > LIN_CH_OPERATIONAL))
|
|
{
|
|
ret = LIN_RET_STATE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
pXfer->sleepSignal = 0U;
|
|
/* Update node state. */
|
|
pXfer->xferPdu.Pid = pLinPdu->Pid;
|
|
pXfer->curEventId = LIN_NO_EVENT;
|
|
|
|
/* if master node, prepare send buffer first */
|
|
ret = LIN_DrvSendResponse(pXfer, pCfg, pLinPdu);
|
|
if ((LIN_RET_SUCCESS == ret) && (LIN_NODE_MASTER == pCfg->nodeMode))
|
|
{
|
|
(void)LIN_DrvSendHeader(pXfer);
|
|
pXfer->curChnState = LIN_TX_HEADER_BUSY;
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Command LIN master node to initiate sleep mode sequence
|
|
* @param pLinHandle Pointer to LIN handle structure (contains configuration and state)
|
|
* @return Operation status:
|
|
* - LIN_RET_SUCCESS: Sleep sequence initiated successfully
|
|
* - LIN_RET_STATE_ERROR: Node state is invalid for sleep
|
|
* - LIN_RET_ERROR: Parameter validation failed (via LIN_DEV_ERROR_REPORT)
|
|
* @details
|
|
* - Validates input parameters and node state (must be operational)
|
|
* - Constructs a sleep frame (PID=0x3C, 8-byte payload: 0x00 followed by 0xFFs)
|
|
* - Sends header to start sleep sequence transmission
|
|
* @note Only valid for master nodes (slave nodes cannot initiate sleep)
|
|
*/
|
|
LIN_ReturnType LIN_DrvGoToSleep(LIN_HandleType *pLinHandle)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
LIN_XferStateType *pXfer = pLinHandle->pXfer;
|
|
uint8_t index;
|
|
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
if (LIN_RET_SUCCESS != LIN_AssertLinHandle(pLinHandle))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_GOTO_SLEEP_ID, LIN_E_PARAM_POINTER);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertLinHwIndex(pLinHandle->pCfg))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_GOTO_SLEEP_ID, LIN_E_PARAM_INSTANCE);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertMasterApi(pLinHandle, LIN_DRV_GOTO_SLEEP_ID))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_GOTO_SLEEP_ID, LIN_E_PARAM_ERROR_CALL);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Check node state. */
|
|
if ((pXfer->curChnState < LIN_TX_HEADER_OK) || (pXfer->curChnState > LIN_CH_OPERATIONAL))
|
|
{
|
|
ret = LIN_RET_STATE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
/* Update node state. */
|
|
pXfer->xferPdu.Pid = 0x3C;
|
|
pXfer->xferPdu.Drc = LIN_FRAMERESPONSE_TX;
|
|
pXfer->xferPdu.Dl = 8U;
|
|
pXfer->xferPdu.SduPtr[0U] = 0x00U;
|
|
for (index = 1U; index < pXfer->xferPdu.Dl; index++)
|
|
{
|
|
pXfer->xferPdu.SduPtr[index] = 0xFF;
|
|
}
|
|
pXfer->checkSum = 0x00U;
|
|
|
|
pXfer->cntByte = 0U;
|
|
pXfer->curEventId = LIN_NO_EVENT;
|
|
|
|
/* if master node, prepare send buffer first */
|
|
ret = LIN_DrvSendHeader(pXfer);
|
|
pXfer->sleepSignal = 1U;
|
|
pXfer->curChnState = LIN_TX_HEADER_BUSY;
|
|
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Internal implementation to transition LIN node to sleep state
|
|
* @param pLinHandle Pointer to LIN handle structure (contains configuration and state)
|
|
* @return Operation status:
|
|
* - LIN_RET_SUCCESS: Sleep state transition completed
|
|
* - LIN_RET_STATE_ERROR: Node state is invalid for sleep
|
|
* - LIN_RET_ERROR: Parameter validation failed (via LIN_DEV_ERROR_REPORT)
|
|
* @details
|
|
* - Validates input parameters and node state (must be operational)
|
|
* - Configures control register to disable error interrupts
|
|
* - Flushes TX/RX FIFOs and clears data register
|
|
* - Updates frame/channel state to LIN_FRAME_SLEEP_STATE and LIN_CH_SLEEP
|
|
* - Enables receive active interrupt to detect wake-up signals
|
|
*/
|
|
LIN_ReturnType LIN_DrvGoToSleepInternal(LIN_HandleType *pLinHandle)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
LIN_XferStateType *pXfer = pLinHandle->pXfer;
|
|
uint32_t ctrlRegVal;
|
|
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
if (LIN_RET_SUCCESS != LIN_AssertLinHandle(pLinHandle))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_GOTO_SLEEP_INTERNAL_ID, LIN_E_PARAM_POINTER);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertLinHwIndex(pLinHandle->pCfg))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_GOTO_SLEEP_INTERNAL_ID, LIN_E_PARAM_INSTANCE);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Check node state. */
|
|
if ((pXfer->curChnState < LIN_TX_HEADER_OK) || (pXfer->curChnState > LIN_CH_OPERATIONAL))
|
|
{
|
|
ret = LIN_RET_STATE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ctrlRegVal = FCUART_CTRL_ORIE(0U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(0U) | \
|
|
FCUART_CTRL_TE(0U) | FCUART_CTRL_RE(0U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, ctrlRegVal);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
FCUART_HWA_FlushTxRxFifo(pXfer->base);
|
|
ctrlRegVal = FCUART_CTRL_ORIE(0U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(0U) | \
|
|
FCUART_CTRL_TE(1U) | FCUART_CTRL_RE(1U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, ctrlRegVal);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
|
|
(void)FCUART_HWA_GetData(pXfer->base);
|
|
|
|
pXfer->cntByte = 0U;
|
|
pXfer->curEventId = LIN_NO_EVENT;
|
|
pXfer->curFrState = LIN_FRAME_SLEEP_STATE;
|
|
pXfer->curChnState = LIN_CH_SLEEP;
|
|
|
|
FCUART_HWA_SetReceiveActiveInterrupt(pXfer->base, true);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Send wake-up signal to LIN bus to rouse all nodes from sleep mode
|
|
* @param pLinHandle Pointer to LIN handle structure (contains configuration and state)
|
|
* @return Operation status:
|
|
* - LIN_RET_SUCCESS: Wake-up signal sent successfully
|
|
* - LIN_RET_NOT_INIT: LIN node is not initialized
|
|
* - LIN_RET_ERROR: Parameter validation failed (via LIN_DEV_ERROR_REPORT)
|
|
* @details
|
|
* - Validates input parameters and node state (must be initialized)
|
|
* - Configures control register to enable error interrupts
|
|
* - Disables receive active interrupt to focus on wake-up signal transmission
|
|
* - Sends specific data (0x00 for baudrate >10000bps, 0xF8 otherwise) to generate 150us+ active level
|
|
* - Updates node state to LIN_CH_OPERATIONAL and frame state to LIN_FRAME_IDLE_STATE
|
|
* @note Master node sends signal; slave nodes enable break detection interrupt after wake-up
|
|
*/
|
|
LIN_ReturnType LIN_DrvWakeup(LIN_HandleType *pLinHandle)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
LIN_ConfigType *pCfg = pLinHandle->pCfg;
|
|
LIN_XferStateType *pXfer = pLinHandle->pXfer;
|
|
uint32_t ctrlRegVal;
|
|
uint32_t statRegVal;
|
|
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
if (LIN_RET_SUCCESS != LIN_AssertLinHandle(pLinHandle))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_WAKEUP_ID, LIN_E_PARAM_POINTER);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertLinHwIndex(pLinHandle->pCfg))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_WAKEUP_ID, LIN_E_PARAM_INSTANCE);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
|
|
/* Check node state. */
|
|
if (LIN_NOT_OK == pXfer->curChnState)
|
|
{
|
|
ret = LIN_RET_NOT_INIT;
|
|
}
|
|
else if ((LIN_CH_SLEEP == pXfer->curChnState) || (LIN_CH_SLEEP_PENDING == pXfer->curChnState))
|
|
{
|
|
ctrlRegVal = FCUART_CTRL_ORIE(1U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(1U) | \
|
|
FCUART_CTRL_TE(0U) | FCUART_CTRL_RE(0U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, ctrlRegVal);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
|
|
FCUART_HWA_FlushTxRxFifo(pXfer->base);
|
|
|
|
ctrlRegVal = FCUART_CTRL_ORIE(1U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(1U) | \
|
|
FCUART_CTRL_TE(1U) | FCUART_CTRL_RE(1U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, ctrlRegVal);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
|
|
FCUART_HWA_SetReceiveActiveInterrupt(pXfer->base, false);
|
|
/* clear register status flag */
|
|
statRegVal = FCUART_HWA_GetSTAT(pXfer->base);
|
|
FCUART_HWA_WriteClearSTAT(pXfer->base, statRegVal);
|
|
|
|
pXfer->sleepSignal = 0U;
|
|
|
|
if (LIN_NODE_SLAVE == pCfg->nodeMode)
|
|
{
|
|
FCUART_HWA_SetLinBreakDetectEnable(pXfer->base, true);
|
|
FCUART_HWA_SetLinBreakDetectInterrupt(pXfer->base, true);
|
|
}
|
|
|
|
if (pCfg->baudRate > 10000U)
|
|
{
|
|
/* Wakeup signal will be range from 400us to 800us depend on baudrate, 0x80U */
|
|
FCUART_HWA_SetData(pXfer->base, 0x00U);
|
|
}
|
|
else
|
|
{
|
|
/* Wakeup signal will be range from 400us to 4ms depend on baudrate, 0xF8U */
|
|
FCUART_HWA_SetData(pXfer->base, 0xF8U);
|
|
}
|
|
pXfer->curChnState = LIN_CH_OPERATIONAL;
|
|
pXfer->curFrState = LIN_FRAME_IDLE_STATE;
|
|
pXfer->curEventId = LIN_NO_EVENT;
|
|
}
|
|
else
|
|
{
|
|
/* already wake-up */
|
|
}
|
|
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Internal implementation to wake up LIN node from sleep state
|
|
* @param pLinHandle Pointer to LIN handle structure (contains configuration and state)
|
|
* @return Operation status:
|
|
* - LIN_RET_SUCCESS: Wake-up transition completed
|
|
* - LIN_RET_NOT_INIT: LIN node is not initialized
|
|
* - LIN_RET_ERROR: Parameter validation failed (via LIN_DEV_ERROR_REPORT)
|
|
* @details
|
|
* - Validates input parameters and node state (must be in sleep/sleep-pending state)
|
|
* - Configures control register to enable error interrupts (ORIE, FEIE)
|
|
* - Disables receive active interrupt to focus on operational mode
|
|
* - Clears status register flags and flushes TX/RX FIFOs
|
|
* - For slave nodes: Re-enables LIN break detection interrupt
|
|
* - Updates node state to LIN_CH_OPERATIONAL and frame state to LIN_FRAME_IDLE_STATE
|
|
*/
|
|
LIN_ReturnType LIN_DrvWakeupInternal(LIN_HandleType *pLinHandle)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
LIN_XferStateType *pXfer = pLinHandle->pXfer;
|
|
LIN_ConfigType *pCfg = pLinHandle->pCfg;
|
|
uint32_t ctrlRegVal;
|
|
uint32_t statRegVal;
|
|
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
if (LIN_RET_SUCCESS != LIN_AssertLinHandle(pLinHandle))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_WAKEUP_INTERNAL_ID, LIN_E_PARAM_POINTER);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertLinHwIndex(pLinHandle->pCfg))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_WAKEUP_INTERNAL_ID, LIN_E_PARAM_INSTANCE);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Check node state. */
|
|
if (LIN_NOT_OK == pXfer->curChnState)
|
|
{
|
|
ret = LIN_RET_NOT_INIT;
|
|
}
|
|
else if ((LIN_CH_SLEEP == pXfer->curChnState) || (LIN_CH_SLEEP_PENDING == pXfer->curChnState))
|
|
{
|
|
ctrlRegVal = FCUART_CTRL_ORIE(1U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(1U) | \
|
|
FCUART_CTRL_TE(0U) | FCUART_CTRL_RE(0U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, ctrlRegVal);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
|
|
FCUART_HWA_FlushTxRxFifo(pXfer->base);
|
|
|
|
ctrlRegVal = FCUART_CTRL_ORIE(1U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(1U) | \
|
|
FCUART_CTRL_TE(1U) | FCUART_CTRL_RE(1U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, ctrlRegVal);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
|
|
FCUART_HWA_SetReceiveActiveInterrupt(pXfer->base, false);
|
|
/* clear register status flag */
|
|
statRegVal = FCUART_HWA_GetSTAT(pXfer->base);
|
|
FCUART_HWA_WriteClearSTAT(pXfer->base, statRegVal);
|
|
|
|
|
|
|
|
if (LIN_NODE_SLAVE == pCfg->nodeMode)
|
|
{
|
|
FCUART_HWA_SetLinBreakDetectEnable(pXfer->base, true);
|
|
FCUART_HWA_SetLinBreakDetectInterrupt(pXfer->base, true);
|
|
}
|
|
pXfer->curChnState = LIN_CH_OPERATIONAL;
|
|
pXfer->curFrState = LIN_FRAME_IDLE_STATE;
|
|
pXfer->curEventId = LIN_NO_EVENT;
|
|
}
|
|
else
|
|
{
|
|
/* already wake-up */
|
|
}
|
|
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief Get current LIN node operational status
|
|
* @param pLinHandle Pointer to LIN handle structure
|
|
* @return Current node state:
|
|
* - LIN_CH_SLEEP: Node in sleep mode
|
|
* - LIN_CH_OPERATIONAL: Node in active mode
|
|
* - LIN_TX_BUSY/LIN_RX_BUSY: Transmitting/receiving data
|
|
* @details
|
|
* - Converts internal transfer state (curChnState) to user-visible status
|
|
* - Handles state transition from sleep-pending to sleep mode
|
|
* - Validates handle and hardware index via LIN_DEV_ERROR_REPORT
|
|
*/
|
|
LIN_StatusType LIN_DrvGetStatus(LIN_HandleType *pLinHandle)
|
|
{
|
|
LIN_StatusType status;
|
|
LIN_XferStateType *pXfer = pLinHandle->pXfer;
|
|
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
if (LIN_RET_SUCCESS != LIN_AssertLinHandle(pLinHandle))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_GET_STATUS_ID, LIN_E_PARAM_POINTER);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertLinHwIndex(pLinHandle->pCfg))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_GET_STATUS_ID, LIN_E_PARAM_INSTANCE);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
if (LIN_CH_SLEEP_PENDING == pXfer->curChnState)
|
|
{
|
|
pXfer->curChnState = LIN_CH_SLEEP;
|
|
}
|
|
status = pXfer->curChnState;
|
|
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/**
|
|
* @brief Abort ongoing LIN transfer and reset node to operational state
|
|
* @param pLinHandle Pointer to LIN handle structure
|
|
* @return Operation status:
|
|
* - LIN_RET_SUCCESS: Transfer aborted successfully
|
|
* - LIN_RET_STATE_ERROR: Invalid state for abort operation
|
|
* @details
|
|
* - Performs hardware-level abort operations including:
|
|
* • Disabling receive interrupts
|
|
* • Clearing status flags
|
|
* • Flushing Tx/Rx FIFOs
|
|
* - For slave nodes, re-enables LIN break detection
|
|
* - Resets internal state machines to operational/idle state
|
|
* - Validates handle and hardware index via LIN_DEV_ERROR_REPORT
|
|
*/
|
|
LIN_ReturnType LIN_DrvAbortTransfer(LIN_HandleType *pLinHandle)
|
|
{
|
|
LIN_ReturnType ret = LIN_RET_SUCCESS;
|
|
LIN_XferStateType *pXfer = pLinHandle->pXfer;
|
|
LIN_ConfigType *pCfg = pLinHandle->pCfg;
|
|
uint32_t ctrlRegVal;
|
|
uint32_t statRegVal;
|
|
|
|
/* Error reporting section (conditionally compiled) */
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
if (LIN_RET_SUCCESS != LIN_AssertLinHandle(pLinHandle))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_ABORT_TRANSFER_ID, LIN_E_PARAM_POINTER);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertLinHwIndex(pLinHandle->pCfg))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_ABORT_TRANSFER_ID, LIN_E_PARAM_INSTANCE);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Validate current transfer state */
|
|
if ((pXfer->curChnState < LIN_TX_HEADER_OK) || (pXfer->curChnState > LIN_CH_OPERATIONAL))
|
|
{
|
|
ret = LIN_RET_STATE_ERROR;
|
|
}
|
|
else
|
|
{
|
|
ctrlRegVal = FCUART_CTRL_ORIE(1U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(1U) | \
|
|
FCUART_CTRL_TE(0U) | FCUART_CTRL_RE(0U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, ctrlRegVal);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
|
|
/* Flush hardware FIFOs */
|
|
FCUART_HWA_FlushTxRxFifo(pXfer->base);
|
|
|
|
ctrlRegVal = FCUART_CTRL_ORIE(1U) | FCUART_CTRL_NEIE(0U) | FCUART_CTRL_FEIE(1U) | \
|
|
FCUART_CTRL_TE(1U) | FCUART_CTRL_RE(1U);
|
|
FCUART_HWA_SetCtrl(pXfer->base, ctrlRegVal);
|
|
LIN_DelayFunctionClk(pXfer, 2U);
|
|
|
|
/* Disable receive active interrupt */
|
|
FCUART_HWA_SetReceiveActiveInterrupt(pXfer->base, false);
|
|
|
|
/* Clear all pending status flags */
|
|
statRegVal = FCUART_HWA_GetSTAT(pXfer->base);
|
|
FCUART_HWA_WriteClearSTAT(pXfer->base, statRegVal);
|
|
|
|
|
|
|
|
/* Special handling for slave nodes */
|
|
if (LIN_NODE_SLAVE == pCfg->nodeMode)
|
|
{
|
|
/* Re-enable break detection for next frame */
|
|
FCUART_HWA_SetLinBreakDetectEnable(pXfer->base, true);
|
|
FCUART_HWA_SetLinBreakDetectInterrupt(pXfer->base, true);
|
|
}
|
|
|
|
/* Reset state machines */
|
|
pXfer->curChnState = LIN_CH_OPERATIONAL; // Set channel to operational
|
|
pXfer->curFrState = LIN_FRAME_IDLE_STATE; // Reset frame state machine
|
|
pXfer->curEventId = LIN_NO_EVENT; // Clear pending events
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* @brief LIN controller interrupt service routine (ISR)
|
|
* @param pLinHandle Pointer to LIN handle structure
|
|
* @details
|
|
* - Processes different types of interrupts based on hardware status:
|
|
* 1. Wake-up interrupt: Handles LIN bus wake-up signal detection
|
|
* 2. Transmission complete interrupt: Manages frame transmission completion
|
|
* 3. Break detection interrupt: Processes LIN bus break field reception
|
|
* 4. Data reception interrupt: Handles frame data reception
|
|
* 5. Error handling: Invokes error handler for unrecognized interrupts
|
|
* - Clears all interrupt flags after processing to prevent re-triggering
|
|
*/
|
|
void LIN_DrvIRQHandler(LIN_HandleType *pLinHandle)
|
|
{
|
|
LIN_ConfigType *pCfg = pLinHandle->pCfg;
|
|
LIN_XferStateType *pXfer = pLinHandle->pXfer;
|
|
uint32_t u32Ctrl = FCUART_HWA_GetCtrl(pXfer->base);
|
|
uint32_t u32Stat = FCUART_HWA_GetSTAT(pXfer->base);
|
|
uint32_t u32Baud = FCUART_HWA_GetBaud(pXfer->base);
|
|
|
|
#if LIN_DEV_ERROR_REPORT == STD_ON
|
|
if (LIN_RET_SUCCESS != LIN_AssertLinHandle(pLinHandle))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_IRQ_HANDLE, LIN_E_PARAM_POINTER);
|
|
}
|
|
else if (LIN_RET_SUCCESS != LIN_AssertLinHwIndex(pLinHandle->pCfg))
|
|
{
|
|
LIN_ReportDevError(LIN_DRV_IRQ_HANDLE, LIN_E_PARAM_INSTANCE);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Handle the wake-up feature by protocol. */
|
|
if ((u32Baud & FCUART_BAUD_RIAEIE_MASK) && (u32Stat & FCUART_STAT_RPAEIF_MASK))
|
|
{
|
|
LIN_DrvWakeupHandle(pXfer, pCfg);
|
|
}
|
|
else if ((u32Ctrl & FCUART_CTRL_TCIE_MASK) && (u32Stat & FCUART_STAT_TCF))
|
|
{
|
|
LIN_DrvFrameTxHandle(pXfer, pCfg);
|
|
}
|
|
else if ((u32Baud & FCUART_BAUD_LBKDIE_MASK) && (u32Stat & FCUART_STAT_LBKDIF_MASK))
|
|
{
|
|
LIN_DrvFrameRxBreakHandle(pXfer, pCfg);
|
|
}
|
|
else if ((u32Ctrl & FCUART_CTRL_RIE_MASK) && (u32Stat & FCUART_STAT_RDRFF_MASK))
|
|
{
|
|
LIN_DrvFrameRxDataHandle(pXfer, pCfg);
|
|
}
|
|
/* Handle error status. */
|
|
else
|
|
{
|
|
LIN_DrvErrorHandle(pXfer, pCfg);
|
|
}
|
|
|
|
/* read again to avoid set unexpected bits, clear all interrupt flag */
|
|
u32Stat = FCUART_HWA_GetSTAT(pXfer->base);
|
|
FCUART_HWA_WriteClearSTAT(pXfer->base, u32Stat);
|
|
}
|
|
}
|
|
|
|
#endif
|