/** ****************************************************************************** * @file ivec_mcal_mcan.c * @brief This file provides APIs for MCAN peripheral. * @date 15-feb-2024 * @Author Vecmocon Technology ****************************************************************************** */ #include <../Core/Include/ivec_mcal_mcan.h> #include "string.h" /* MCAN Protocol Status Header */ volatile DL_MCAN_ProtocolStatus g_xHeaderStat; /* MCAN Interrupt Line 1 Status */ volatile uint32_t g_u32InterruptLine1Status; __attribute__((weak)) void vMCU_FDCANRxFifoCallback(uint32_t u32Identifier, uint8_t *pu8data, uint16_t u16DataLength) { } /*=======================================================================================PRIVATE_MEMBERS======================================================================================*/ static const DL_MCAN_ClockConfig __gprv_xClockConf = { .clockSel = DL_MCAN_FCLK_HFCLK,////DL_MCAN_FCLK_SYSPLLCLK1,DL_MCAN_FCLK_HFCLK, .divider = DL_MCAN_FCLK_DIV_1, }; static DL_MCAN_InitParams __gprv_xInitParams= { /* Initialize MCAN Init parameters. */ .fdMode = false, .brsEnable = false, .txpEnable = false, .efbi = false, .pxhddisable = false, .darEnable = true, .wkupReqEnable = true, .autoWkupEnable = true, .emulationEnable = true, .tdcEnable = true, .wdcPreload = 255, /* Transmitter Delay Compensation parameters. */ .tdcConfig.tdcf = 10, .tdcConfig.tdco = 6, }; static DL_MCAN_ConfigParams __gprv_xConfigParams={ /* Initialize MCAN Config parameters. */ .monEnable = false, .asmEnable = false, .tsPrescalar = 15, .tsSelect = 0, .timeoutSelect = DL_MCAN_TIMEOUT_SELECT_CONT, .timeoutPreload = 65535, .timeoutCntEnable = false, .filterConfig.rrfs = false, .filterConfig.rrfe = false, .filterConfig.anfe = 0, .filterConfig.anfs = 0, }; static DL_MCAN_MsgRAMConfigParams __gprv_xMsgRAMConfigParams = { /* Standard ID Filter List Start Address. */ .flssa = 0x0, /* List Size: Standard ID. */ .lss = 10, /* Extended ID Filter List Start Address. */ .flesa = 10 , /* List Size: Extended ID. */ .lse = 10 , /* Tx Buffers Start Address. */ .txStartAddr = 40 + 80+20 , /* Number of Dedicated Transmit Buffers. */ .txBufNum = 0 , .txFIFOSize = 3, /* Tx Buffer Element Size. */ .txBufMode = 0, .txBufElemSize = DL_MCAN_ELEM_SIZE_8BYTES, /* Tx Event FIFO Start Address. */ .txEventFIFOStartAddr = 0 , /* Event FIFO Size. */ .txEventFIFOSize = 0 , /* Level for Tx Event FIFO watermark interrupt. */ .txEventFIFOWaterMark = 0, /* Rx FIFO0 Start Address. */ .rxFIFO0startAddr = 40 + 80 + 36+20 +40, /* Number of Rx FIFO elements. */ .rxFIFO0size = 3 , /* Rx FIFO0 Watermark. */ .rxFIFO0waterMark = 0, .rxFIFO0OpMode = 0, /* Rx FIFO1 Start Address. */ .rxFIFO1startAddr = 40 + 80 + 36 + 36+80 , /* Number of Rx FIFO elements. */ .rxFIFO1size = 3 , /* Level for Rx FIFO 1 watermark interrupt. */ .rxFIFO1waterMark = 0, /* FIFO blocking mode. */ .rxFIFO1OpMode = 0, /* Rx Buffer Start Address. */ .rxBufStartAddr = 0 , /* Rx Buffer Element Size. */ .rxBufElemSize = DL_MCAN_ELEM_SIZE_8BYTES, /* Rx FIFO0 Element Size. */ .rxFIFO0ElemSize = DL_MCAN_ELEM_SIZE_8BYTES, /* Rx FIFO1 Element Size. */ .rxFIFO1ElemSize = DL_MCAN_ELEM_SIZE_8BYTES, }; static DL_MCAN_BitTimingParams __gprv_xBitTimes_500 = { /* Arbitration Baud Rate Pre-scaler. */ .nomRatePrescalar = 0, /* Arbitration Time segment before sample point. */ .nomTimeSeg1 = 41, /* Arbitration Time segment after sample point. */ .nomTimeSeg2 = 4, /* Arbitration (Re)Synchronization Jump Width Range. */ .nomSynchJumpWidth = 4, /* Data Baud Rate Pre-scaler. */ .dataRatePrescalar = 0, /* Data Time segment before sample point. */ .dataTimeSeg1 = 0, /* Data Time segment after sample point. */ .dataTimeSeg2 = 0, /* Data (Re)Synchronization Jump Width. */ .dataSynchJumpWidth = 0, }; static const DL_MCAN_BitTimingParams __gprv_xBitTimes_250 = { /* Arbitration Baud Rate Pre-scaler. */ .nomRatePrescalar = 0, /* Arbitration Time segment before sample point. */ .nomTimeSeg1 = 82, /* Arbitration Time segment after sample point. */ .nomTimeSeg2 = 11, /* Arbitration (Re)Synchronization Jump Width Range. */ .nomSynchJumpWidth = 11, /* Data Baud Rate Pre-scaler. */ .dataRatePrescalar = 0, /* Data Time segment before sample point. */ .dataTimeSeg1 = 0, /* Data Time segment after sample point. */ .dataTimeSeg2 = 0, /* Data (Re)Synchronization Jump Width. */ .dataSynchJumpWidth = 0, }; /*____________________________________________________________________________________________________________________________________________________________________________________________*/ /*============================================================================================================================================================================================= PRIVATE_DECLARATIONS ==============================================================================================================================================================================================*/ /** * @brief Processes CAN interrupt events. * * Handles CAN RX FIFO events, reads messages from the RX FIFO, and invokes a callback * for each received message. It also processes error conditions and manages the interrupt status. * * @param[in] u32InterruptStatus The interrupt status indicating which event triggered the interrupt. * * @details * - If the interrupt is related to RX FIFO events (RF0F, RF0N, RF1F, RF1N): * - Retrieves the FIFO status and reads messages from the corresponding RX FIFO. * - Acknowledges the read message and processes the message ID and data payload. * - Calls `vMCAL_CanReceiveFifoCallback` with the received message. * - If the interrupt is related to errors (e.g., timeout, bus off, protocol errors): * - The interrupt status is saved for further processing (currently no action is taken for errors). */ static void __prv_McalCanInterruptProcess(uint32_t u32InterruptStatus) { volatile uint32_t l_u32CanInterrupt = 0; l_u32CanInterrupt = u32InterruptStatus; uint32_t u32IntrStatus = u32InterruptStatus; if (u32IntrStatus & (DL_MCAN_INTERRUPT_RF1F | DL_MCAN_INTERRUPT_RF0N | DL_MCAN_INTERRUPT_RF0F | DL_MCAN_INTERRUPT_RF1N)) { volatile DL_MCAN_RxFIFOStatus l_xRxFIFOStatus = {0}; if (u32IntrStatus & (DL_MCAN_INTERRUPT_RF0F | DL_MCAN_INTERRUPT_RF0N)) l_xRxFIFOStatus.num = DL_MCAN_RX_FIFO_NUM_0; else l_xRxFIFOStatus.num = DL_MCAN_RX_FIFO_NUM_1; DL_MCAN_getRxFIFOStatus(CANFD0, &l_xRxFIFOStatus); while ((l_xRxFIFOStatus.fillLvl) != 0) { volatile DL_MCAN_RxBufElement l_xTempRxMsg; DL_MCAN_readMsgRam(CANFD0, DL_MCAN_MEM_TYPE_FIFO, 0, l_xRxFIFOStatus.num, &l_xTempRxMsg); DL_MCAN_writeRxFIFOAck(CANFD0, l_xRxFIFOStatus.num, l_xRxFIFOStatus.getIdx); if (!l_xTempRxMsg.xtd) l_xTempRxMsg.id = (uint32_t)(l_xTempRxMsg.id >> 18); /* Extract data payload */ uint8_t au8DataArr[8] = {0}; for (int i = 0; i < 8; i++) { au8DataArr[i] = l_xTempRxMsg.data[i] & 0xFF; } vMCAL_CanReceiveFifoCallback(l_xTempRxMsg.id, &au8DataArr[0], l_xTempRxMsg.dlc); DL_MCAN_getRxFIFOStatus(CANFD0, &l_xRxFIFOStatus); } } if(u32IntrStatus & (DL_MCAN_INTR_SRC_MSG_RAM_ACCESS_FAILURE|DL_MCAN_INTR_SRC_TIMEOUT|DL_MCAN_INTR_SRC_ERR_LOG_OVRFLW|DL_MCAN_INTR_SRC_ERR_PASSIVE|DL_MCAN_INTR_SRC_ERR_PASSIVE|DL_MCAN_INTR_SRC_WARNING_STATUS|DL_MCAN_INTR_SRC_BUS_OFF_STATUS|DL_MCAN_INTR_SRC_WATCHDOG|DL_MCAN_INTR_SRC_PROTOCOL_ERR_ARB|DL_MCAN_INTR_SRC_PROTOCOL_ERR_DATA|DL_MCAN_INTR_SRC_RES_ADDR_ACCESS) ) { l_u32CanInterrupt = u32InterruptStatus; } } /** * @brief Default Interrupt Handler for MCAN * */ void CANFD0_IRQHandler(void) { /* Get the pending interrupt source */ switch (DL_MCAN_getPendingInterrupt(CANFD0)) { case DL_MCAN_IIDX_LINE1: { /* Get interrupt status for Line 1 */ uint32_t l_u32IntrStatus = DL_MCAN_getIntrStatus(CANFD0); /* Process the interrupt */ __prv_McalCanInterruptProcess(l_u32IntrStatus ); /* Clear the interrupt status for Line 1 */ DL_MCAN_clearIntrStatus(MCAN0_INST, l_u32IntrStatus, DL_MCAN_INTR_SRC_MCAN_LINE_1); break; } case DL_MCAN_IIDX_LINE0: { /* Get interrupt status for Line 0 */ uint32_t l_u32IntrStatus = DL_MCAN_getIntrStatus(CANFD0); /* Process the interrupt */ __prv_McalCanInterruptProcess(l_u32IntrStatus ); /* Clear the interrupt status for Line 0 */ DL_MCAN_clearIntrStatus(MCAN0_INST, l_u32IntrStatus, DL_MCAN_INTR_SRC_MCAN_LINE_0); break; } default: { /* Get interrupt status for external timestamp */ uint32_t l_u32IntrStatus = DL_MCAN_getIntrStatus(CANFD0); /* Clear the interrupt status for external timestamp */ DL_MCAN_clearIntrStatus(MCAN0_INST, l_u32IntrStatus, DL_MCAN_INTR_SRC_MCAN_EXT_TS); break; } } } /*============================================================================================================================================================================================= API ==============================================================================================================================================================================================*/ /** * @brief Initializes the MCAN module with specified configurations. * * This function configures the MCAN module with the following: * - Enables the module clock and sets the clock configuration. * - Initializes the MCAN in software initialization mode and waits for completion. * - Configures bit timings and message RAM. * - Sets up the filter configurations for both extended and standard IDs based on the provided filter values and masks. * - Enables necessary interrupts for CAN operation and sets interrupt priorities. * * @param[in] pxCanHandle Pointer to the CAN handle structure containing the configuration parameters. * * @return IVEC_MCAL_STATUS_SUCCESS if the initialization is successful, or an error status if failed. * * @details * The function performs the following steps: * 1. Validates the CAN instance and speed. * 2. Initializes the MCAN module by enabling the module clock, configuring the clock, and setting the operation mode to software initialization. * 3. Waits for memory initialization to complete, then sets the MCAN operation mode to normal. * 4. Configures the MCAN module parameters, including bit timings and message RAM. * 5. Sets up CAN filters for extended and standard IDs based on the provided filter and mask values. * 6. Enables CAN interrupts and configures interrupt lines for CAN events. * 7. Clears and enables interrupts in the NVIC for the CAN module. */ IVEC_McalCommonErr_e xMCAL_MCANInit(IVEC_McalCanHandle_s* pxCanHandle) { if((pxCanHandle->pMCAN != CANFD0) && (pxCanHandle->u16Speed == IVEC_MCAL_CAN_BR_500 || pxCanHandle->u16Speed == IVEC_MCAL_CAN_BR_250)) { return commonMCAL_INVALID_PARAM; } if(pxCanHandle->__u8Init == 1) { return commonMCAL_INVALID_PARAM; } /* Enable power to the MCAN module */ DL_MCAN_enablePower(pxCanHandle->pMCAN); delay_cycles(POWER_STARTUP_DELAY); /* Configure GPIO for CAN TX and RX */ DL_GPIO_initPeripheralOutputFunction(GPIO_MCAN0_IOMUX_CAN_TX, GPIO_MCAN0_IOMUX_CAN_TX_FUNC); DL_GPIO_initPeripheralInputFunction(GPIO_MCAN0_IOMUX_CAN_RX, GPIO_MCAN0_IOMUX_CAN_RX_FUNC); DL_MCAN_RevisionId xRevid; DL_MCAN_enableModuleClock(pxCanHandle->pMCAN); DL_MCAN_setClockConfig(pxCanHandle->pMCAN, (DL_MCAN_ClockConfig *) &__gprv_xClockConf); /* Get MCANSS Revision ID. */ DL_MCAN_getRevisionId(pxCanHandle->pMCAN, &xRevid); /* Wait for Memory initialization to be completed. */ while (false == DL_MCAN_isMemInitDone(pxCanHandle->pMCAN)); /* Put MCAN in SW initialization mode. */ DL_MCAN_setOpMode(pxCanHandle->pMCAN, DL_MCAN_OPERATION_MODE_SW_INIT); /* Wait till MCAN is not initialized. */ while (DL_MCAN_OPERATION_MODE_SW_INIT != DL_MCAN_getOpMode(pxCanHandle->pMCAN)); /* Initialize MCAN module. */ DL_MCAN_init(pxCanHandle->pMCAN, (DL_MCAN_InitParams*)&__gprv_xInitParams); __gprv_xConfigParams.filterConfig.anfe = 0; __gprv_xConfigParams.filterConfig.anfs = 0; for (int u32Index = 0; u32Index <= pxCanHandle->i32MaskCount; u32Index++) { if ((pxCanHandle->u32FilterValues[u32Index] > 0x7FF) || (pxCanHandle->u32MaskValues[u32Index] > 0x7FF)) { __gprv_xConfigParams.filterConfig.anfe = 3; } else { __gprv_xConfigParams.filterConfig.anfs = 3; } } /* Configure MCAN module. */ DL_MCAN_config(pxCanHandle->pMCAN, (DL_MCAN_ConfigParams*)&__gprv_xConfigParams); /* Configure Bit timings. */ if (pxCanHandle->u16Speed == IVEC_MCAL_CAN_BR_500) { DL_MCAN_setBitTime(pxCanHandle->pMCAN, (DL_MCAN_BitTimingParams*)&__gprv_xBitTimes_500); } else if (pxCanHandle->u16Speed == IVEC_MCAL_CAN_BR_250) { DL_MCAN_setBitTime(pxCanHandle->pMCAN, (DL_MCAN_BitTimingParams*)&__gprv_xBitTimes_250); } else { DL_MCAN_setBitTime(pxCanHandle->pMCAN, (DL_MCAN_BitTimingParams*)&__gprv_xBitTimes_250); } /* Configure Message RAM Sections */ DL_MCAN_msgRAMConfig(pxCanHandle->pMCAN, (DL_MCAN_MsgRAMConfigParams*)&__gprv_xMsgRAMConfigParams); uint8_t l_u8ExtendedFilterCount = 0; uint8_t l_u8StandardFilterCount = 0; for (int u32Index = 0; u32Index <= pxCanHandle->i32MaskCount; u32Index++) { if ((pxCanHandle->u32FilterValues[u32Index] > 0x7FF) || (pxCanHandle->u32MaskValues[u32Index] > 0x7FF)) { // Extended ID filter DL_MCAN_ExtMsgIDFilterElement extFilterElement; extFilterElement.efid1 = pxCanHandle->u32FilterValues[u32Index]; extFilterElement.efid2 = pxCanHandle->u32MaskValues[u32Index]; extFilterElement.efec = 001; extFilterElement.eft = 2; DL_MCAN_addExtMsgIDFilter(pxCanHandle->pMCAN, l_u8ExtendedFilterCount, (DL_MCAN_StdMsgIDFilterElement*)&extFilterElement); l_u8ExtendedFilterCount++; } else { // Standard ID filter DL_MCAN_StdMsgIDFilterElement stdFilterElement; stdFilterElement.sfid1 = pxCanHandle->u32FilterValues[u32Index]; stdFilterElement.sfid2 = pxCanHandle->u32MaskValues[u32Index]; stdFilterElement.sfec = 001; stdFilterElement.sft = 2; DL_MCAN_addStdMsgIDFilter(pxCanHandle->pMCAN, l_u8StandardFilterCount, (DL_MCAN_StdMsgIDFilterElement*)&stdFilterElement); l_u8StandardFilterCount++; } } DL_MCAN_setExtIDAndMask(pxCanHandle->pMCAN, (0x1FFFFFFFU)); /* Take MCAN out of the SW initialization mode */ DL_MCAN_setOpMode(pxCanHandle->pMCAN, DL_MCAN_OPERATION_MODE_NORMAL); while (DL_MCAN_OPERATION_MODE_NORMAL != DL_MCAN_getOpMode(pxCanHandle->pMCAN)); /* Enable MCAN mopdule Interrupts */ DL_MCAN_enableIntr(pxCanHandle->pMCAN, (MCAN_IR_RF0N_MASK | \ MCAN_IR_RF0F_MASK | \ MCAN_IR_RF1N_MASK | \ MCAN_IR_RF1F_MASK | \ MCAN_IR_TSW_MASK | \ MCAN_IR_MRAF_MASK | \ MCAN_IR_TOO_MASK | \ MCAN_IR_DRX_MASK | \ MCAN_IR_BEU_MASK | \ MCAN_IR_ELO_MASK | \ MCAN_IR_EP_MASK | \ MCAN_IR_EW_MASK | \ MCAN_IR_BO_MASK | \ MCAN_IR_WDI_MASK | \ MCAN_IR_ARA_MASK), 1); DL_MCAN_selectIntrLine(pxCanHandle->pMCAN, DL_MCAN_INTR_MASK_ALL, DL_MCAN_INTR_LINE_NUM_0); DL_MCAN_enableIntrLine(pxCanHandle->pMCAN, DL_MCAN_INTR_LINE_NUM_0, 1U); /* Enable MSPM0 MCAN interrupt */ DL_MCAN_clearInterruptStatus(pxCanHandle->pMCAN, (DL_MCAN_MSP_INTERRUPT_LINE0)); DL_MCAN_enableInterrupt(pxCanHandle->pMCAN, (DL_MCAN_MSP_INTERRUPT_LINE0)); NVIC_SetPriority(CANFD0_INT_IRQn, 1); NVIC_ClearPendingIRQ(CANFD0_INT_IRQn); NVIC_EnableIRQ(CANFD0_INT_IRQn); pxCanHandle->__u8Init = 1; return commonMCAL_SUCCESS; } /** * @brief Function to De-Initialize MCAN peripheral * @param MCAN Pointer to the register overlay for the peripheral * @retval IVEC MCAL status */ IVEC_McalCommonErr_e xMCAL_MCANDeInit(IVEC_McalCanHandle_s* pxCanHandle) { /* Assert that the MCAN module pointer is valid */ if((pxCanHandle->pMCAN != CANFD0) && (pxCanHandle->u16Speed == IVEC_MCAL_CAN_BR_500 || pxCanHandle->u16Speed == IVEC_MCAL_CAN_BR_250)) { return commonMCAL_INVALID_PARAM; } if(pxCanHandle->__u8Init == 0) /* Ensure the module was initialized before deinitializing. */ { return commonMCAL_INVALID_PARAM; } /* Reset the CAN module to initialization mode */ DL_MCAN_setOpMode(pxCanHandle->pMCAN, DL_MCAN_OPERATION_MODE_SW_INIT); /* Wait until the operation mode is set to initialization mode */ while (DL_MCAN_OPERATION_MODE_SW_INIT != DL_MCAN_getOpMode(pxCanHandle->pMCAN)); /* Disable the CAN interrupt */ NVIC_DisableIRQ(CANFD0_INT_IRQn); /* Disable interrupts on CAN module */ DL_MCAN_enableIntr(pxCanHandle->pMCAN, (DL_MCAN_INTERRUPT_BEU | \ DL_MCAN_INTERRUPT_BO | \ DL_MCAN_INTERRUPT_ELO | \ DL_MCAN_INTERRUPT_EP | \ DL_MCAN_INTERRUPT_EW | \ DL_MCAN_INTERRUPT_PEA | \ DL_MCAN_INTERRUPT_PED | \ DL_MCAN_INTERRUPT_RF0N | \ DL_MCAN_INTERRUPT_RF1N | \ DL_MCAN_INTERRUPT_TC | \ DL_MCAN_INTERRUPT_TOO), false); /* Disable interrupt lines */ DL_MCAN_enableIntrLine(pxCanHandle->pMCAN, DL_MCAN_INTR_LINE_NUM_0, false); DL_MCAN_enableIntrLine(pxCanHandle->pMCAN, DL_MCAN_INTR_LINE_NUM_1, false); /* Clear interrupt statuses for lines 0 and 1 */ DL_MCAN_clearInterruptStatus(pxCanHandle->pMCAN, DL_MCAN_MSP_INTERRUPT_LINE0); DL_MCAN_disableInterrupt(pxCanHandle->pMCAN, DL_MCAN_MSP_INTERRUPT_LINE0); DL_MCAN_clearInterruptStatus(pxCanHandle->pMCAN, DL_MCAN_MSP_INTERRUPT_LINE1); DL_MCAN_disableInterrupt(pxCanHandle->pMCAN, DL_MCAN_MSP_INTERRUPT_LINE1); /* Set the operation mode back to normal */ DL_MCAN_setOpMode(pxCanHandle->pMCAN, DL_MCAN_OPERATION_MODE_NORMAL); /* Reset the initialization flag */ pxCanHandle->__u8Init = 0; /* Disable the CAN module's GPIO peripheral function */ DL_GPIO_initPeripheralOutputFunction(IOMUX_PINCM59, 0); DL_GPIO_initPeripheralInputFunction(IOMUX_PINCM60, 0); /* Disable the module clock and power */ DL_MCAN_disableModuleClock(pxCanHandle->pMCAN); DL_MCAN_disablePower(pxCanHandle->pMCAN); /* Wait for power down to complete */ delay_cycles(16); return commonMCAL_SUCCESS; } /** * @brief Deinitializes the MCAN module and disables associated resources. * * This function performs the necessary steps to safely deinitialize the MCAN module, including: * - Resetting the MCAN to initialization mode. * - Disabling CAN interrupts and interrupt lines. * - Clearing interrupt statuses. * - Setting the MCAN operation mode back to normal. * - Disabling the CAN module clock and powering down the module. * - Disabling the GPIO peripheral functions for CAN. * * @param[in] pxCanHandle Pointer to the CAN handle structure containing the configuration parameters. * * @return IVEC_MCAL_STATUS_SUCCESS if the deinitialization is successful, or an error status if failed. * * @details * The function performs the following steps: * 1. Validates the CAN module pointer and checks if the module was initialized. * 2. Resets the CAN module to the software initialization mode and waits for completion. * 3. Disables interrupts for the CAN module and the interrupt lines associated with CAN events. * 4. Clears interrupt statuses for both interrupt lines and disables the interrupts in the NVIC. * 5. Sets the CAN operation mode back to normal. * 6. Resets the initialization flag and disables the GPIO peripheral functions for CAN pins. * 7. Disables the CAN module's clock and power, waiting for the power down process to complete. */ IVEC_McalCommonErr_e xMCAL_MCANTx(IVEC_McalCanHandle_s* pxCanHandle, uint32_t u32Id, uint16_t* pu16TxData, uint32_t u32BufNum, uint32_t u32Bytes) { /* Ensure that the MCAN instance is CANFD0 */ if(pxCanHandle->pMCAN != CANFD0) { return IVEC_MCAL_STATUS_UNSUPPORTED; } /* Ensure that the MCAN is initialized */ if(pxCanHandle->__u8Init == 0) { return IVEC_MCAL_STATUS_FALSE; } /* Validate the byte size */ if (u32Bytes > 8) { return IVEC_MCAL_STATUS_FALSE; } DL_MCAN_TxBufElement txMsg = {0}; /* Set the message type (standard or extended ID) */ if (u32Id > 0x7FF) { txMsg.xtd = 1; /* Extended ID */ txMsg.id = u32Id; /* Set the extended ID */ } else { txMsg.xtd = 0; /* Standard ID */ txMsg.id = ((uint32_t)(u32Id)) << 18U; /* Adjust for standard ID format */ } /* Copy the data bytes to the message */ for (uint32_t u32Index = 0; u32Index < u32Bytes; u32Index++) { txMsg.data[u32Index] = pu16TxData[u32Index]; } txMsg.dlc = u32Bytes; /* Set the Data Length Code (DLC) */ __asm("nop"); /* No operation for synchronization */ volatile DL_MCAN_TxFIFOStatus l_xTxFifoStatus = { 0 }; // Structure variable prefixed with 's' for structure type and 'x' for typedef suffix DL_MCAN_getTxFIFOQueStatus(pxCanHandle->pMCAN, &l_xTxFifoStatus); // Get the FIFO status // __disable_irq(); if (l_xTxFifoStatus.freeLvl) // Check if there is space in the FIFO { DL_MCAN_writeMsgRam(pxCanHandle->pMCAN, DL_MCAN_MEM_TYPE_FIFO, u32BufNum, &txMsg); // Function and variable name updated if (DL_MCAN_TXBufAddReq(pxCanHandle->pMCAN, l_xTxFifoStatus.putIdx) == IVEC_MCAL_STATUS_ERROR) // Function return type 'x' { return IVEC_MCAL_STATUS_ERROR; } } else { /* If no space in FIFO, return busy */ return IVEC_MCAL_STATUS_BUSY; } DL_MCAN_getTxFIFOQueStatus(pxCanHandle->pMCAN, &l_xTxFifoStatus); volatile uint32_t l_i32TransmitTimeout = i32MCAL_getTicks(); volatile uint8_t l_u8StatusFlag = IVEC_MCAL_STATUS_BUSY; while (1) { if (l_u8StatusFlag == IVEC_MCAL_STATUS_SUCCESS) { return l_u8StatusFlag; } else if ((i32MCAL_getTicks() - l_i32TransmitTimeout) > 2) { return IVEC_MCAL_STATUS_TIMEOUT; } l_u8StatusFlag = (DL_MCAN_getTxBufTransmissionStatus(pxCanHandle->pMCAN) == 0x01) ? \ IVEC_MCAL_STATUS_SUCCESS : IVEC_MCAL_STATUS_ERROR; } // __enable_irq(); return IVEC_MCAL_STATUS_SUCCESS; } /** * @brief Receives a message from the CAN bus. * * Retrieves the received CAN message, including the message ID and data. * Handles bus-off recovery and copies the received data into the provided buffer. * * @param[in] pxCanHandle Pointer to the CAN handle. * @param[out] pu32ID Pointer to store the received message ID. * @param[out] pu8RxData Pointer to the buffer to store the received message data. * @param[in] iDLC The Data Length Code (DLC), specifying the number of bytes in the data field. * * @return IVEC_MCAL_STATUS_SUCCESS on successful reception. */ IVEC_McalCommonErr_e xMCAL_MCANRx(IVEC_McalCanHandle_s* pxCanHandle, uint32_t *pu32ID, uint8_t *pu8RxData, int iDLC) { /* Assert that the CAN module is CANFD0 and that the MCAL initialization flag is set */ if((pxCanHandle->pMCAN != CANFD0) && (pxCanHandle->u16Speed == IVEC_MCAL_CAN_BR_500 || pxCanHandle->u16Speed == IVEC_MCAL_CAN_BR_250)) { return commonMCAL_INVALID_PARAM; } if(pxCanHandle->__u8Init == 0) { return commonMCAL_INVALID_PARAM; } return commonMCAL_FAIL; } /** * @brief Retrieves the error status of the MCAN module. * * Copies the current error interrupt status and protocol status into a buffer and returns * an appropriate status based on error conditions. * * @param[in] pxCanHandle Pointer to the CAN handle. * @param[out] pcErrorStatus Buffer to store the error status string. * * @return IVEC_MCAL_STATUS_SUCCESS if no errors, IVEC_MCAL_STATUS_ERROR if errors are detected. */ IVEC_McalCommonErr_e xMCAL_MCANGetErrorStatus(IVEC_McalCanHandle_s* pxCanHandle, char *pcErrorStatus) { /* Assert that the MCAN initialization flag is set */ if(pxCanHandle->__u8Init == 0) { return commonMCAL_INVALID_PARAM; } /* Retrieve the current protocol status of the MCAN module */ DL_MCAN_getProtocolStatus(pxCanHandle->pMCAN, &g_xHeaderStat); /* Declare and initialize the error counter status structure */ volatile DL_MCAN_ErrCntStatus l_xCounter = {0}; DL_MCAN_getErrCounters(CANFD0, &l_xCounter); /* Return the status based on various error conditions */ if((g_xHeaderStat.lastErrCode == DL_MCAN_ERR_CODE_ACK_ERROR) || (g_xHeaderStat.busOffStatus)) DL_MCAN_setOpMode(CANFD0, DL_MCAN_OPERATION_MODE_NORMAL); return commonMCAL_SUCCESS; /* return ( (g_xHeaderStat.lastErrCode == DL_MCAN_ERR_CODE_ACK_ERROR) || \ (g_xHeaderStat.busOffStatus) ) ? commonMCAL_FAIL : commonMCAL_SUCCESS; */ } /** * @brief Retrieves the status of interrupt line 1 for the MCAN module. * * Copies the current interrupt status for line 1 into the provided status buffer. * * @param[in] pxCanHandle Pointer to the CAN handle. * @param[out] pu32InterruptStatus Pointer to store the interrupt status. * * @return IVEC_MCAL_STATUS_SUCCESS if successful. */ IVEC_McalCommonErr_e xMCAL_MCANGetInterruptLine1Status(IVEC_McalCanHandle_s* pxCanHandle, uint32_t *pu32InterruptStatus) { // Ensure CAN module is initialized (optional assertion, can be uncommented as needed) if(pxCanHandle->__u8Init == 0) { return commonMCAL_INVALID_PARAM; } *pu32InterruptStatus = g_u32InterruptLine1Status; return commonMCAL_SUCCESS; }