/***************************************************************************//** * @file * @brief Controller Area Network API ******************************************************************************* * # License * Copyright 2018 Silicon Laboratories Inc. www.silabs.com ******************************************************************************* * * SPDX-License-Identifier: Zlib * * The licensor of this software is Silicon Laboratories Inc. * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * ******************************************************************************/ #include "em_can.h" #include "sl_common.h" #include "sl_assert.h" #include "em_cmu.h" #include #if defined(CAN_COUNT) && (CAN_COUNT > 0) /** @cond DO_NOT_INCLUDE_WITH_DOXYGEN */ /* Macros to use for the ID field in the CANn_MIRx_ARB register as an 11 bit * standard ID. The register field can be used for both an 11 bit standard * ID and a 29 bit extended ID. */ #define _CAN_MIR_ARB_STD_ID_SHIFT 18 #define _CAN_MIR_MASK_STD_SHIFT 18 #define _CAN_MIR_ARB_STD_ID_MASK 0x1FFC0000UL #define _CAN_MIR_ARB_STD_ID_MAX 0x7FFUL // = 2^11 - 1 #if (CAN_COUNT == 2) #define CAN_VALID(can) ((can == CAN0) || (can == CAN1)) #elif (CAN_COUNT == 1) #define CAN_VALID(can) (can == CAN0) #else #error "The actual number of CAN busses is not supported." #endif /** @endcond */ /***************************************************************************//** * @addtogroup can CAN - Controller Area Network * @brief Controller Area Network API * * @details The Controller Area Network Interface Bus (CAN) implements a * multi-master serial bus for connecting microcontrollers and devices, also * known as nodes, to communicate with each other in applications without a host * computer. CAN is a message-based protocol, designed originally for automotive * applications, but also used in many other scenarios. * The complexity of a node can range from a simple I/O device up to an * embedded computer with a CAN interface and sophisticated software. The node * may also be a gateway allowing a standard computer to communicate over a USB * or Ethernet port to the devices on a CAN network. Devices are connected to * the bus through a host processor, a CAN controller, and a CAN transceiver. * * @include em_can_send_example.c * * @{ ******************************************************************************/ /******************************************************************************* ************************** GLOBAL FUNCTIONS ******************************* ******************************************************************************/ /***************************************************************************//** * @brief * Initialize CAN. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] init * A pointer to the CAN initialization structure. ******************************************************************************/ void CAN_Init(CAN_TypeDef *can, const CAN_Init_TypeDef *init) { EFM_ASSERT(CAN_VALID(can)); CAN_Enable(can, false); can->CTRL = _CAN_CTRL_TEST_MASK; can->TEST = _CAN_TEST_RESETVALUE; if (init->resetMessages) { CAN_ResetMessages(can, 0); } can->CTRL = CAN_CTRL_INIT; CAN_SetBitTiming(can, init->bitrate, init->propagationTimeSegment, init->phaseBufferSegment1, init->phaseBufferSegment2, init->synchronisationJumpWidth); CAN_Enable(can, init->enable); } /***************************************************************************//** * @brief * Get the CAN module frequency. * * @details * An internal prescaler of 2 is inside the CAN module. * * @param[in] can * A pointer to the CAN peripheral register block. * * @return * A clock value. ******************************************************************************/ uint32_t CAN_GetClockFrequency(CAN_TypeDef *can) { #if defined CAN0 if (can == CAN0) { return CMU_ClockFreqGet(cmuClock_CAN0) / 2; } #endif #if defined CAN1 if (can == CAN1) { return CMU_ClockFreqGet(cmuClock_CAN1) / 2; } #endif EFM_ASSERT(false); return 0; } /***************************************************************************//** * @brief * Read a Message Object to find if a message was lost ; reset the * 'Message Lost' flag. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] interface * Indicate which Message Interface Register to use. * * @param[in] msgNum * A message number of the Message Object, [1 - 32]. * * @return * True if a message was lost, false otherwise. ******************************************************************************/ bool CAN_MessageLost(CAN_TypeDef *can, uint8_t interface, uint8_t msgNum) { CAN_MIR_TypeDef * mir = &can->MIR[interface]; bool messageLost; /* Make sure msgNum is in the correct range. */ EFM_ASSERT((msgNum > 0) && (msgNum <= 32)); CAN_ReadyWait(can, interface); /* Set which registers to read from RAM. */ mir->CMDMASK = CAN_MIR_CMDMASK_WRRD_READ | CAN_MIR_CMDMASK_CONTROL | CAN_MIR_CMDMASK_CLRINTPND; /* Send reading request and wait (3 to 6 cpu cycle). */ CAN_SendRequest(can, interface, msgNum, true); messageLost = mir->CTRL & _CAN_MIR_CTRL_MESSAGEOF_MASK; if (messageLost) { mir->CMDMASK = CAN_MIR_CMDMASK_WRRD | CAN_MIR_CMDMASK_CONTROL; /* Reset the 'MessageLost' bit. */ mir->CTRL &= ~_CAN_MIR_CTRL_MESSAGEOF_MASK; /* Send reading request and wait (3 to 6 cpu cycle). */ CAN_SendRequest(can, interface, msgNum, true); } /* Return the state of the MESSAGEOF bit. */ return messageLost; } /***************************************************************************//** * @brief * Set the ROUTE registers. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] active * A boolean indicating whether or not to activate the ROUTE registers. * * @param[in] pinRxLoc * A location of the RX pin. * * @param[in] pinTxLoc * A location of the TX pin. ******************************************************************************/ void CAN_SetRoute(CAN_TypeDef *can, bool active, uint16_t pinRxLoc, uint16_t pinTxLoc) { if (active) { /* Set the ROUTE register */ can->ROUTE = CAN_ROUTE_TXPEN | (pinRxLoc << _CAN_ROUTE_RXLOC_SHIFT) | (pinTxLoc << _CAN_ROUTE_TXLOC_SHIFT); } else { /* Deactivate the ROUTE register */ can->ROUTE = 0x0; } } /***************************************************************************//** * @brief * Set the bitrate and its parameters. * * @details * Multiple parameters need to be properly configured. * See the reference manual for a detailed description. * Careful : the BRP (Baud Rate Prescaler) is calculated by: * 'brp = freq / (period * bitrate);'. freq is the frequency of the CAN * device, period the time of transmission of a bit. The result is an uint32_t. * Hence it's truncated, causing an approximation error. This error is non * negligible when the period is high, the bitrate is high, and frequency is low. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] bitrate * A wanted bitrate on the CAN bus. * * @param[in] propagationTimeSegment * A value for the Propagation Time Segment. * * @param[in] phaseBufferSegment1 * A value for the Phase Buffer Segment 1. * * @param[in] phaseBufferSegment2 * A value for the Phase Buffer Segment 2. * * @param[in] synchronisationJumpWidth * A value for the Synchronization Jump Width. ******************************************************************************/ void CAN_SetBitTiming(CAN_TypeDef *can, uint32_t bitrate, uint16_t propagationTimeSegment, uint16_t phaseBufferSegment1, uint16_t phaseBufferSegment2, uint16_t synchronisationJumpWidth) { uint32_t sum, brp, period, freq, brpHigh, brpLow; /* Verification that the parameters are in range. */ EFM_ASSERT((propagationTimeSegment <= 8) && (propagationTimeSegment > 0)); EFM_ASSERT((phaseBufferSegment1 <= 8) && (phaseBufferSegment1 > 0)); EFM_ASSERT((phaseBufferSegment2 <= 8) && (phaseBufferSegment2 > 0)); EFM_ASSERT(bitrate > 0); EFM_ASSERT((synchronisationJumpWidth <= phaseBufferSegment1) && (synchronisationJumpWidth <= phaseBufferSegment2) && (synchronisationJumpWidth > 0)); /* propagationTimeSegment is counted as part of phaseBufferSegment1 in the BITTIMING register. */ sum = phaseBufferSegment1 + propagationTimeSegment; /* Period is the total length of one CAN bit. 1 is the Sync_seg. */ period = 1 + sum + phaseBufferSegment2; freq = CAN_GetClockFrequency(can); brp = freq / (period * bitrate); EFM_ASSERT(brp != 0); /* -1 because the hardware reads 'written value + 1'. */ brp = brp - 1; /* brp is divided between two registers. */ brpHigh = brp / 64; brpLow = brp % 64; /* Checking register limit. */ EFM_ASSERT(brpHigh <= 15); bool enabled = CAN_IsEnabled(can); /* Enable access to the bittiming registers. */ can->CTRL |= CAN_CTRL_CCE | CAN_CTRL_INIT; can->BITTIMING = (brpLow << _CAN_BITTIMING_BRP_SHIFT) | ((synchronisationJumpWidth - 1) << _CAN_BITTIMING_SJW_SHIFT) | ((sum - 1) << _CAN_BITTIMING_TSEG1_SHIFT) | ((phaseBufferSegment2 - 1) << _CAN_BITTIMING_TSEG2_SHIFT); can->BRPE = brpHigh; if (enabled) { can->CTRL &= ~(_CAN_CTRL_CCE_MASK | _CAN_CTRL_INIT_MASK); } else { can->CTRL &= ~_CAN_CTRL_CCE_MASK; } } /***************************************************************************//** * @brief * Set the CAN operation mode. * * @details * In initialization mode, the CAN module is deactivated. Reset the messages in all * other modes to be sure that there is no leftover data that * needs to be configured before use. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] mode * Mode of operation : Init, Normal, Loopback, SilentLoopback, Silent, Basic. ******************************************************************************/ void CAN_SetMode(CAN_TypeDef *can, CAN_Mode_TypeDef mode) { switch (mode) { case canModeNormal: can->CTRL |= _CAN_CTRL_TEST_MASK; can->TEST = _CAN_TEST_RESETVALUE; can->CTRL &= ~_CAN_CTRL_TEST_MASK; can->CTRL = _CAN_CTRL_EIE_MASK | _CAN_CTRL_SIE_MASK | _CAN_CTRL_IE_MASK; break; case canModeBasic: can->CTRL = _CAN_CTRL_EIE_MASK | _CAN_CTRL_SIE_MASK | _CAN_CTRL_IE_MASK | CAN_CTRL_TEST; can->TEST = CAN_TEST_BASIC; break; case canModeLoopBack: can->CTRL = _CAN_CTRL_EIE_MASK | _CAN_CTRL_SIE_MASK | _CAN_CTRL_IE_MASK | CAN_CTRL_TEST; can->TEST = CAN_TEST_LBACK; break; case canModeSilentLoopBack: can->CTRL = _CAN_CTRL_EIE_MASK | _CAN_CTRL_SIE_MASK | _CAN_CTRL_IE_MASK | CAN_CTRL_TEST; can->TEST = CAN_TEST_LBACK | CAN_TEST_SILENT; break; case canModeSilent: can->CTRL = _CAN_CTRL_EIE_MASK | _CAN_CTRL_SIE_MASK | _CAN_CTRL_IE_MASK | CAN_CTRL_TEST; can->TEST = CAN_TEST_SILENT; break; default: break; } } /***************************************************************************//** * @brief * Set the ID and the filter for a specific Message Object. * * @details * The initialization bit has to be 0 to use this function. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] interface * Indicate which Message Interface Register to use. * * @param[in] useMask * A boolean to choose whether or not to use the masks. * * @param[in] message * A Message Object. * * @param[in] wait * If true, wait for the end of the transfer between the MIRx registers and * the RAM to exit. If false, exit immediately, the transfer can still be * in progress. ******************************************************************************/ void CAN_SetIdAndFilter(CAN_TypeDef *can, uint8_t interface, bool useMask, const CAN_MessageObject_TypeDef *message, bool wait) { /* Make sure msgNum is in the correct range. */ EFM_ASSERT((message->msgNum > 0) && (message->msgNum <= 32)); CAN_MIR_TypeDef * mir = &can->MIR[interface]; CAN_ReadyWait(can, interface); /* Set which registers to read from RAM. */ mir->CMDMASK = CAN_MIR_CMDMASK_WRRD_READ | CAN_MIR_CMDMASK_ARBACC | CAN_MIR_CMDMASK_CONTROL; /* Send reading request and wait (3 to 6 CPU cycle). */ CAN_SendRequest(can, interface, message->msgNum, true); /* Reset MSGVAL. */ mir->CMDMASK |= CAN_MIR_CMDMASK_WRRD_WRITE; mir->ARB &= ~CAN_MIR_ARB_MSGVAL; CAN_SendRequest(can, interface, message->msgNum, true); /* Set which registers to write to RAM. */ mir->CMDMASK |= CAN_MIR_CMDMASK_MASKACC; /* Set UMASK bit. */ BUS_RegBitWrite(&mir->CTRL, _CAN_MIR_CTRL_UMASK_SHIFT, useMask); /* Configure the ID. */ if (message->extended) { EFM_ASSERT(message->id <= _CAN_MIR_ARB_ID_MASK); mir->ARB = (mir->ARB & ~_CAN_MIR_ARB_ID_MASK) | (message->id << _CAN_MIR_ARB_ID_SHIFT) | CAN_MIR_ARB_MSGVAL | CAN_MIR_ARB_XTD_EXT; } else { EFM_ASSERT(message->id <= _CAN_MIR_ARB_STD_ID_MAX); mir->ARB = (mir->ARB & ~(_CAN_MIR_ARB_ID_MASK | CAN_MIR_ARB_XTD_STD)) | (message->id << _CAN_MIR_ARB_STD_ID_SHIFT) | CAN_MIR_ARB_MSGVAL; } if (message->extendedMask) { mir->MASK = (message->mask << _CAN_MIR_MASK_MASK_SHIFT) & _CAN_MIR_MASK_MASK_MASK; } else { mir->MASK = (message->mask << _CAN_MIR_MASK_STD_SHIFT) & _CAN_MIR_ARB_STD_ID_MASK; } /* Configure the masks. */ mir->MASK |= (message->extendedMask << _CAN_MIR_MASK_MXTD_SHIFT) | (message->directionMask << _CAN_MIR_MASK_MDIR_SHIFT); /* Send a writing request. */ CAN_SendRequest(can, interface, message->msgNum, wait); } /***************************************************************************//** * @brief * Configure valid, TX/RX, remoteTransfer for a specific Message Object. * * @details * The initialization bit has to be 0 to use this function. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] interface * Indicate which Message Interface Register to use. * * @param[in] msgNum * A message number of this Message Object, [1 - 32]. * * @param[in] valid * True if the Message Object is valid, false otherwise. * * @param[in] tx * True if the Message Object is used for transmission, false if used for * reception. * * @param[in] remoteTransfer * True if the Message Object is used for remote transmission, false otherwise. * * @param[in] endOfBuffer * True if it is for a single Message Object or the end of a FIFO buffer, * false if the Message Object is part of a FIFO buffer and not the last. * * @param[in] wait * If true, wait for the end of the transfer between the MIRx registers and * the RAM to exit. If false, exit immediately, the transfer can still be * in progress. ******************************************************************************/ void CAN_ConfigureMessageObject(CAN_TypeDef *can, uint8_t interface, uint8_t msgNum, bool valid, bool tx, bool remoteTransfer, bool endOfBuffer, bool wait) { CAN_MIR_TypeDef * mir = &can->MIR[interface]; /* Make sure msgNum is in correct range. */ EFM_ASSERT((msgNum > 0) && (msgNum <= 32)); CAN_ReadyWait(can, interface); /* Set which registers to read from RAM. */ mir->CMDMASK = CAN_MIR_CMDMASK_WRRD_READ | CAN_MIR_CMDMASK_ARBACC | CAN_MIR_CMDMASK_CONTROL; /* Send reading request and wait (3 to 6 CPU cycle). */ CAN_SendRequest(can, interface, msgNum, true); /* Reset MSGVAL. */ mir->CMDMASK |= CAN_MIR_CMDMASK_WRRD_WRITE; mir->ARB &= ~CAN_MIR_ARB_MSGVAL; CAN_SendRequest(can, interface, msgNum, true); /* Configure a valid message and direction. */ mir->ARB = (mir->ARB & ~(_CAN_MIR_ARB_DIR_MASK | _CAN_MIR_ARB_MSGVAL_MASK)) | (valid << _CAN_MIR_ARB_MSGVAL_SHIFT) | (tx << _CAN_MIR_ARB_DIR_SHIFT); /* Set EOB bit, RX, and TX interrupts. */ mir->CTRL = (endOfBuffer << _CAN_MIR_CTRL_EOB_SHIFT) | _CAN_MIR_CTRL_TXIE_MASK | _CAN_MIR_CTRL_RXIE_MASK | (remoteTransfer << _CAN_MIR_CTRL_RMTEN_SHIFT); /* Send a writing request. */ CAN_SendRequest(can, interface, msgNum, wait); } /***************************************************************************//** * @brief * Send data from the Message Object message. * * @details * If the message is configured as TX and remoteTransfer = 0, calling this function * will send the data of this Message Object if its parameters are correct. * If the message is TX and remoteTransfer = 1, this function will set the data of * message to RAM and exit. Data will be automatically sent after * reception of a remote frame. * If the message is RX and remoteTransfer = 1, this function will send a remote * frame to the corresponding ID. * If the message is RX and remoteTransfer = 0, the user shouldn't call this * function. It will also send a remote frame. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] interface * Indicate which Message Interface Register to use. * * @param[in] message * A Message Object. * * @param[in] wait * If true, wait for the end of the transfer between the MIRx registers and * RAM to exit. If false, exit immediately. The transfer can still be * in progress. ******************************************************************************/ void CAN_SendMessage(CAN_TypeDef *can, uint8_t interface, const CAN_MessageObject_TypeDef *message, bool wait) { CAN_MIR_TypeDef * mir = &can->MIR[interface]; /* Make sure msgNum is in correct range. */ EFM_ASSERT((message->msgNum > 0) && (message->msgNum <= 32)); /* Make sure dlc is in correct range. */ EFM_ASSERT(message->dlc <= _CAN_MIR_CTRL_DLC_MASK); CAN_ReadyWait(can, interface); /* Set LEC to an unused value to be sure it is reset to 0 after sending. */ BUS_RegMaskedWrite(&can->STATUS, _CAN_STATUS_LEC_MASK, 0x7); /* Set which registers to read from RAM. */ mir->CMDMASK = CAN_MIR_CMDMASK_WRRD_READ | CAN_MIR_CMDMASK_ARBACC | CAN_MIR_CMDMASK_CONTROL; /* Send a reading request and wait (3 to 6 CPU cycle). */ CAN_SendRequest(can, interface, message->msgNum, true); /* Reset MSGVAL. */ mir->CMDMASK |= CAN_MIR_CMDMASK_WRRD_WRITE; mir->ARB &= ~CAN_MIR_ARB_MSGVAL; CAN_SendRequest(can, interface, message->msgNum, true); /* Set which registers to write to RAM. */ if ( ((mir->CTRL & _CAN_MIR_CTRL_RMTEN_MASK) == 0) || (((mir->CTRL & _CAN_MIR_CTRL_RMTEN_MASK) == CAN_MIR_CTRL_RMTEN) && ((mir->ARB & _CAN_MIR_ARB_DIR_MASK) == CAN_MIR_ARB_DIR_TX)) ) { mir->CMDMASK |= CAN_MIR_CMDMASK_DATAA | CAN_MIR_CMDMASK_DATAB; /* Set data. */ CAN_WriteData(can, interface, message); } /* If TX = 1 and remoteTransfer = 1, nothing is sent. */ if ( ((mir->CTRL & _CAN_MIR_CTRL_RMTEN_MASK) == 0) || ((mir->ARB & _CAN_MIR_ARB_DIR_MASK) == CAN_MIR_ARB_DIR_RX)) { mir->CTRL |= CAN_MIR_CTRL_TXRQST; /* DATAVALID is set only if it is not sending a remote message. */ if ((mir->CTRL & _CAN_MIR_CTRL_RMTEN_MASK) == 0) { mir->CTRL |= CAN_MIR_CTRL_DATAVALID; } } /* Set the data length code. */ mir->CTRL = (mir->CTRL & ~_CAN_MIR_CTRL_DLC_MASK) | message->dlc; /* Configure the ID. */ if (message->extended) { EFM_ASSERT(message->id <= _CAN_MIR_ARB_ID_MASK); mir->ARB = (mir->ARB & ~_CAN_MIR_ARB_ID_MASK) | (message->id << _CAN_MIR_ARB_ID_SHIFT) | CAN_MIR_ARB_MSGVAL | CAN_MIR_ARB_XTD_EXT; } else { EFM_ASSERT(message->id <= _CAN_MIR_ARB_STD_ID_MAX); mir->ARB = (mir->ARB & ~(_CAN_MIR_ARB_ID_MASK | _CAN_MIR_ARB_XTD_MASK)) | CAN_MIR_ARB_MSGVAL | (message->id << _CAN_MIR_ARB_STD_ID_SHIFT) | CAN_MIR_ARB_XTD_STD; } /* Send a writing request. */ CAN_SendRequest(can, interface, message->msgNum, wait); } /***************************************************************************//** * @brief * Read data and ID from a Message Object in RAM and store it in a message. * * @details * Read the information from RAM on this Message Object. Data and * the configuration of the Message Object is read. The information is only * read if the message stored in the Message Object is new and valid. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] interface * Indicate which Message Interface Register to use. * * @param[in] message * A Message Object. * * @return * True if the Message Object in RAM holds a new and valid message, which was * not read earlier, false otherwise. ******************************************************************************/ bool CAN_ReadMessage(CAN_TypeDef *can, uint8_t interface, CAN_MessageObject_TypeDef *message) { CAN_MIR_TypeDef * mir = &can->MIR[interface]; uint32_t buffer; uint32_t i; /* Make sure msgNum is in correct range. */ EFM_ASSERT((message->msgNum > 0) && (message->msgNum <= 32)); CAN_ReadyWait(can, interface); /* Set which registers to read from RAM. */ mir->CMDMASK = CAN_MIR_CMDMASK_WRRD_READ | CAN_MIR_CMDMASK_MASKACC | CAN_MIR_CMDMASK_ARBACC | CAN_MIR_CMDMASK_CONTROL | CAN_MIR_CMDMASK_CLRINTPND | CAN_MIR_CMDMASK_TXRQSTNEWDAT | CAN_MIR_CMDMASK_DATAA | CAN_MIR_CMDMASK_DATAB; /* Send a reading request and wait (3 to 6 cpu cycle). */ CAN_SendRequest(can, interface, message->msgNum, true); if ((mir->CTRL & CAN_MIR_CTRL_DATAVALID) == 0) { return false; } /* Get dlc from the control register. */ message->dlc = ((mir->CTRL & _CAN_MIR_CTRL_DLC_MASK) >> _CAN_MIR_CTRL_DLC_SHIFT); /* Make sure dlc is in correct range. */ EFM_ASSERT(message->dlc <= 8); /* Get id from the control register */ if (message->extended) { message->id = (mir->ARB & _CAN_MIR_ARB_ID_MASK); } else { message->id = ((mir->ARB & _CAN_MIR_ARB_STD_ID_MASK) >> _CAN_MIR_ARB_STD_ID_SHIFT); } /* Copy data from the MIR registers to the Message Object message. */ buffer = mir->DATAL; for (i = 0; i < SL_MIN(message->dlc, 4U); ++i) { message->data[i] = buffer & 0xFF; buffer = buffer >> 8; } if (message->dlc > 3) { buffer = mir->DATAH; for (i = 0; i < message->dlc - 4U; ++i) { message->data[i + 4] = buffer & 0xFF; buffer = buffer >> 8; } } return true; } /***************************************************************************//** * @brief * Abort sending a message. * * @details * Set the TXRQST of the CTRL register to 0. Doesn't touch data or the * other parameters. The user can call CAN_SendMessage() to send the object * after using CAN_AbortSendMessage(). * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] interface * Indicate which Message Interface Register to use. * * @param[in] msgNum * A message number of this Message Object, [1 - 32]. * * @param[in] wait * If true, wait for the end of the transfer between the MIRx registers and * the RAM to exit. If false, exit immediately. The transfer can still be * in progress. ******************************************************************************/ void CAN_AbortSendMessage(CAN_TypeDef *can, uint8_t interface, uint8_t msgNum, bool wait) { /* Make sure msgNum is in correct range. */ EFM_ASSERT((msgNum > 0) && (msgNum <= 32)); CAN_MIR_TypeDef * mir = &can->MIR[interface]; CAN_ReadyWait(can, interface); /* Set which registers to read from RAM. */ mir->CMDMASK = CAN_MIR_CMDMASK_WRRD_READ | CAN_MIR_CMDMASK_CONTROL; /* Send a reading request and wait (3 to 6 cpu cycle). */ CAN_SendRequest(can, interface, msgNum, true); /* Set which registers to write to RAM. */ mir->CMDMASK = CAN_MIR_CMDMASK_WRRD | CAN_MIR_CMDMASK_CONTROL; /* Set TXRQST bit to 0. */ mir->CTRL &= ~_CAN_MIR_CTRL_TXRQST_MASK; /* Send a writing request. */ CAN_SendRequest(can, interface, msgNum, wait); } /***************************************************************************//** * @brief * Reset all Message Objects and set their data to 0. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] interface * Indicate which Message Interface Register to use. ******************************************************************************/ void CAN_ResetMessages(CAN_TypeDef *can, uint8_t interface) { CAN_MIR_TypeDef * mir = &can->MIR[interface]; CAN_ReadyWait(can, interface); /* Set which registers to read from RAM. */ mir->CMDMASK = CAN_MIR_CMDMASK_WRRD | CAN_MIR_CMDMASK_MASKACC | CAN_MIR_CMDMASK_ARBACC | CAN_MIR_CMDMASK_CONTROL | CAN_MIR_CMDMASK_DATAA | CAN_MIR_CMDMASK_DATAB; mir->MASK = _CAN_MIR_MASK_RESETVALUE; mir->ARB = _CAN_MIR_ARB_RESETVALUE; mir->CTRL = _CAN_MIR_CTRL_RESETVALUE; mir->DATAL = 0x00000000; mir->DATAH = 0x00000000; /* Write each reset Message Object to RAM. */ for (int i = 1; i <= 32; ++i) { CAN_SendRequest(can, interface, i, true); } } /***************************************************************************//** * @brief * Set all CAN registers to RESETVALUE. Leave the CAN device disabled. * * @param[in] can * A pointer to the CAN peripheral register block. ******************************************************************************/ void CAN_Reset(CAN_TypeDef *can) { CAN_ReadyWait(can, 0); CAN_ReadyWait(can, 1); CAN_Enable(can, false); can->STATUS = _CAN_STATUS_RESETVALUE; can->CTRL |= _CAN_CTRL_CCE_MASK; can->BITTIMING = _CAN_BITTIMING_RESETVALUE; can->CTRL &= ~_CAN_CTRL_CCE_MASK; can->CTRL |= _CAN_CTRL_TEST_MASK; can->TEST = _CAN_TEST_RESETVALUE; can->CTRL &= ~_CAN_CTRL_TEST_MASK; can->BRPE = _CAN_BRPE_RESETVALUE; can->CONFIG = _CAN_CONFIG_RESETVALUE; can->IF0IFS = _CAN_IF0IFS_RESETVALUE; can->IF0IFC = _CAN_IF0IFC_RESETVALUE; can->IF0IEN = _CAN_IF0IEN_RESETVALUE; can->IF1IFS = _CAN_IF1IF_RESETVALUE; can->IF1IFC = _CAN_IF1IFC_RESETVALUE; can->IF1IEN = _CAN_IF1IEN_RESETVALUE; can->ROUTE = _CAN_ROUTE_RESETVALUE; for (int i = 0; i < 2; i++) { can->MIR[i].CMDMASK = _CAN_MIR_CMDMASK_RESETVALUE; can->MIR[i].MASK = _CAN_MIR_MASK_RESETVALUE; can->MIR[i].ARB = _CAN_MIR_ARB_RESETVALUE; can->MIR[i].CTRL = _CAN_MIR_CTRL_RESETVALUE; can->MIR[i].DATAL = _CAN_MIR_DATAL_RESETVALUE; can->MIR[i].DATAH = _CAN_MIR_DATAH_RESETVALUE; can->MIR[i].CMDREQ = _CAN_MIR_CMDREQ_RESETVALUE; } } /***************************************************************************//** * @brief * Write data from a message to the MIRx registers. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] interface * Indicate which Message Interface Register to use. * * @param[in] message * A Message Object. ******************************************************************************/ void CAN_WriteData(CAN_TypeDef *can, uint8_t interface, const CAN_MessageObject_TypeDef *message) { uint32_t tmp; uint8_t data[8] = { 0 }; size_t length = SL_MIN(8, message->dlc); CAN_MIR_TypeDef * mir = &can->MIR[interface]; for (size_t i = 0; i < length; i++) { data[i] = message->data[i]; } CAN_ReadyWait(can, interface); tmp = data[0]; tmp |= data[1] << 8; tmp |= data[2] << 16; tmp |= data[3] << 24; mir->DATAL = tmp; tmp = data[4]; tmp |= data[5] << 8; tmp |= data[6] << 16; tmp |= data[7] << 24; mir->DATAH = tmp; } /***************************************************************************//** * @brief * Send a request for writing or reading RAM of the Message Object msgNum. * * @param[in] can * A pointer to the CAN peripheral register block. * * @param[in] interface * Indicate which Message Interface Register to use. * * @param[in] msgNum * A message number of the Message Object, [1 - 32]. * * @param[in] wait * If true, wait for the end of the transfer between the MIRx registers and * the RAM to exit. If false, exit immediately. The transfer can still be * in progress. ******************************************************************************/ void CAN_SendRequest(CAN_TypeDef *can, uint8_t interface, uint8_t msgNum, bool wait) { CAN_MIR_TypeDef * mir = &can->MIR[interface]; /* Make sure msgNum is in correct range. */ EFM_ASSERT((msgNum > 0) && (msgNum <= 32)); /* Make sure the MIRx registers aren't busy. */ CAN_ReadyWait(can, interface); /* Write msgNum to the CMDREQ register. */ mir->CMDREQ = msgNum << _CAN_MIR_CMDREQ_MSGNUM_SHIFT; if (wait) { CAN_ReadyWait(can, interface); } } /** @} (end addtogroup can) */ #endif /* defined(CAN_COUNT) && (CAN_COUNT > 0) */