|
- // -------------------------------------------------------------
- // a simple Arduino Teensy 3.1/3.2/3.5/3.6 CAN driver
- // by teachop
- // dual CAN support for MK66FX1M0 and updates for MK64FX512 by Pawelsky
- // Interrupt driven Rx/Tx with buffers, object oriented callbacks by Collin Kidder
- // RTR related code by H4nky84
- // Statistics collection, timestamp and code clean-up my mdapoz
- //
- #include "FlexCAN.h"
- #include "kinetis_flexcan.h"
-
- #define FLEXCANb_MCR(b) (*(vuint32_t*)(b))
- #define FLEXCANb_CTRL1(b) (*(vuint32_t*)(b+4))
- #define FLEXCANb_RXMGMASK(b) (*(vuint32_t*)(b+0x10))
- #define FLEXCANb_IFLAG1(b) (*(vuint32_t*)(b+0x30))
- #define FLEXCANb_IMASK1(b) (*(vuint32_t*)(b+0x28))
- #define FLEXCANb_RXFGMASK(b) (*(vuint32_t*)(b+0x48))
- #define FLEXCANb_MBn_CS(b, n) (*(vuint32_t*)(b+0x80+n*0x10))
- #define FLEXCANb_MBn_ID(b, n) (*(vuint32_t*)(b+0x84+n*0x10))
- #define FLEXCANb_MBn_WORD0(b, n) (*(vuint32_t*)(b+0x88+n*0x10))
- #define FLEXCANb_MBn_WORD1(b, n) (*(vuint32_t*)(b+0x8C+n*0x10))
- #define FLEXCANb_IDFLT_TAB(b, n) (*(vuint32_t*)(b+0xE0+(n*4)))
- #define FLEXCANb_MB_MASK(b, n) (*(vuint32_t*)(b+0x880+(n*4)))
- #define FLEXCANb_ESR1(b) (*(vuint32_t*)(b+0x20))
-
- #if defined(__MK66FX1M0__)
- # define INCLUDE_FLEXCAN_CAN1
- #endif
-
- #undef INCLUDE_FLEXCAN_DEBUG
-
- #if defined(INCLUDE_FLEXCAN_DEBUG)
- # define dbg_print(fmt, args...) Serial.print (fmt , ## args)
- # define dbg_println(fmt, args...) Serial.println (fmt , ## args)
- #else
- # define dbg_print(fmt, args...)
- # define dbg_println(fmt, args...)
- #endif
-
- // Supported FlexCAN interfaces
-
- FlexCAN Can0 (0);
- #if defined(INCLUDE_FLEXCAN_CAN1)
- FlexCAN Can1 (1);
- #endif
-
- // default mask to apply to all mailboxes
-
- CAN_filter_t FlexCAN::defaultMask;
-
- // Some of these are complete guesses. Only really 8 and 16 have been validated.
- // You have been warned. But, there aren't too many options for some of these
-
- uint8_t bitTimingTable[21][3] = {
- // prop, seg1, seg2 (4 + prop + seg1 + seg2, seg2 must be at least 1)
- // No value can go over 7 here.
- {0,0,1}, //5
- {1,0,1}, //6
- {1,1,1}, //7
- {2,1,1}, //8
- {2,2,1}, //9
- {2,3,1}, //10
- {2,3,2}, //11
- {2,4,2}, //12
- {2,5,2}, //13
- {2,5,3}, //14
- {2,6,3}, //15
- {2,7,3}, //16
- {2,7,4}, //17
- {3,7,4}, //18
- {3,7,5}, //19
- {4,7,5}, //20
- {4,7,6}, //21
- {5,7,6}, //22
- {6,7,6}, //23
- {6,7,7}, //24
- {7,7,7}, //25
- };
-
- /*
- * \brief Initialize the FlexCAN driver class
- *
- * \param id - CAN bus interface selection
- *
- * \retval none
- *
- */
-
- FlexCAN::FlexCAN (uint8_t id)
- {
- uint32_t i;
-
- numTxMailboxes=2;
-
- flexcanBase = FLEXCAN0_BASE;
-
- #if defined (INCLUDE_FLEXCAN_CAN1)
- if (id > 0) {
- flexcanBase = FLEXCAN1_BASE;
- }
- #else
- (void)id; // Just for avoid warning.
- #endif
-
- #if defined(__MK20DX256__)
- IrqMessage=IRQ_CAN_MESSAGE;
- #elif defined(__MK64FX512__)
- IrqMessage=IRQ_CAN0_MESSAGE;
- #elif defined(__MK66FX1M0__)
- if (flexcanBase == FLEXCAN0_BASE) {
- IrqMessage=IRQ_CAN0_MESSAGE;
- } else {
- IrqMessage=IRQ_CAN1_MESSAGE;
- }
- #endif
-
- // Default mask is allow everything
-
- defaultMask.flags.remote = 0;
- defaultMask.flags.extended = 0;
- defaultMask.id = 0;
-
- sizeRxBuffer=SIZE_RX_BUFFER;
- sizeTxBuffer=SIZE_TX_BUFFER;
- tx_buffer=0;
- rx_buffer=0;
- // Initialize all message box spesific ring buffers to 0.
- for (i=0; i<getNumMailBoxes(); i++) {
- txRings[i]=0;
- }
-
- // clear any listeners for received packets
-
- for (i = 0; i < SIZE_LISTENERS; i++) {
- listener[i] = NULL;
- }
-
- // clear statistics counts
-
- clearStats ();
- }
-
-
- /*
- * \brief Bring the hardware into freeze which drops it off the CAN bus
- *
- * \param none
- *
- * \retval none
- *
- */
-
- void FlexCAN::end (void)
- {
- // enter freeze mode
- halt();
- FLEXCANb_MCR (flexcanBase) |= (FLEXCAN_MCR_HALT);
-
- while (!(FLEXCANb_MCR(flexcanBase) & FLEXCAN_MCR_FRZ_ACK))
- ;
- }
-
- /*
- * \brief Initializes the CAN bus to the given settings
- *
- * \param baud - Set the baud rate of the bus. Only certain values are valid 50000, 100000, 125000, 250000, 500000, 1000000
- * \param mask - A default mask to use for all mailbox masks. Optional.
- * \param txAlt - 1 to enable alternate Tx pin (where available)
- * \param rxAlt - 1 to enable alternate Rx pin (where available)
- *
- * \retval none
- *
- */
-
- void FlexCAN::begin (uint32_t baud, const CAN_filter_t &mask, uint8_t txAlt, uint8_t rxAlt)
- {
- initializeBuffers();
-
- // set up the pins
-
- setPins(txAlt,rxAlt);
-
- // select clock source 16MHz xtal
-
- OSC0_CR |= OSC_ERCLKEN;
-
- if (flexcanBase == FLEXCAN0_BASE) {
- SIM_SCGC6 |= SIM_SCGC6_FLEXCAN0;
- #if defined(INCLUDE_FLEXCAN_CAN1)
- } else if (flexcanBase == FLEXCAN1_BASE) {
- SIM_SCGC3 |= SIM_SCGC3_FLEXCAN1;
- #endif
- }
-
- FLEXCANb_CTRL1(flexcanBase) &= ~FLEXCAN_CTRL_CLK_SRC;
-
- // enable CAN
-
- FLEXCANb_MCR (flexcanBase) |= FLEXCAN_MCR_FRZ;
- FLEXCANb_MCR (flexcanBase) &= ~FLEXCAN_MCR_MDIS;
-
- while (FLEXCANb_MCR(flexcanBase) & FLEXCAN_MCR_LPM_ACK)
- ;
-
- // soft reset
-
- softReset();
-
- // wait for freeze ack
-
- waitFrozen();
-
- // disable self-reception
-
- FLEXCANb_MCR (flexcanBase) |= FLEXCAN_MCR_SRX_DIS;
-
- setBaudRate(baud);
-
- // enable per-mailbox filtering
-
- FLEXCANb_MCR(flexcanBase) |= FLEXCAN_MCR_IRMQ;
-
- // now have to set mask and filter for all the Rx mailboxes or they won't receive anything by default.
-
- for (uint8_t c = 0; c < getNumRxBoxes(); c++) {
- setMask (0, c);
- setFilter (mask, c);
- }
-
- // start the CAN
-
- exitHalt();
- waitReady();
-
- setNumTxBoxes (numTxMailboxes);
-
- NVIC_SET_PRIORITY (IrqMessage, IRQ_PRIORITY);
- NVIC_ENABLE_IRQ (IrqMessage);
-
- // enable interrupt masks for all 16 mailboxes
-
- FLEXCANb_IMASK1 (flexcanBase) = 0xFFFF;
-
- dbg_println ("CAN initialized properly");
- }
-
- /*
- * \brief
- *
- * \param
- *
- * \retval None.
- *
- */
-
- void FlexCAN::setMailBoxTxBufferSize(uint8_t mbox, uint16_t size) {
- if ( mbox>=getNumMailBoxes() || txRings[mbox]!=0 ) return;
-
- volatile CAN_message_t *buf=new CAN_message_t[size];
- txRings[mbox]=new ringbuffer_t;
- initRingBuffer (*(txRings[mbox]), buf, size);
- }
-
- /*
- * \brief Initializes dynamically sized buffers.
- *
- * \param mask - default filter mask
- *
- * \retval None.
- *
- */
-
- void FlexCAN::initializeBuffers() {
- if ( isInitialized() ) return;
-
- // set up the transmit and receive ring buffers
- if (tx_buffer==0) tx_buffer=new CAN_message_t[sizeTxBuffer];
- if (rx_buffer==0) rx_buffer=new CAN_message_t[sizeRxBuffer];
-
- initRingBuffer (txRing, tx_buffer, sizeTxBuffer);
- initRingBuffer (rxRing, rx_buffer, sizeRxBuffer);
- }
-
- /*
- * \brief Initializes CAN pin definitions.
- *
- * \param txAlt - Atrenate tx pin
- * \param rxAlt - Alternate rx pin
- *
- * \retval None.
- *
- */
-
- void FlexCAN::setPins(uint8_t txAlt, uint8_t rxAlt) {
- if (flexcanBase == FLEXCAN0_BASE) {
- dbg_println ("Begin setup of CAN0");
-
- #if defined(__MK66FX1M0__) || defined(__MK64FX512__)
- // 3=PTA12=CAN0_TX, 4=PTA13=CAN0_RX (default)
- // 29=PTB18=CAN0_TX, 30=PTB19=CAN0_RX (alternative)
-
- if (txAlt == 1)
- CORE_PIN29_CONFIG = PORT_PCR_MUX(2);
- else
- CORE_PIN3_CONFIG = PORT_PCR_MUX(2);
-
- // | PORT_PCR_PE | PORT_PCR_PS;
-
- if (rxAlt == 1)
- CORE_PIN30_CONFIG = PORT_PCR_MUX(2);
- else
- CORE_PIN4_CONFIG = PORT_PCR_MUX(2);
- #else
- // 3=PTA12=CAN0_TX, 4=PTA13=CAN0_RX (default)
- // 32=PTB18=CAN0_TX, 25=PTB19=CAN0_RX (alternative)
-
- if (txAlt == 1)
- CORE_PIN32_CONFIG = PORT_PCR_MUX(2);
- else
- CORE_PIN3_CONFIG = PORT_PCR_MUX(2);
-
- // | PORT_PCR_PE | PORT_PCR_PS;
-
- if (rxAlt == 1)
- CORE_PIN25_CONFIG = PORT_PCR_MUX(2);
- else
- CORE_PIN4_CONFIG = PORT_PCR_MUX(2);
- #endif
- }
- #if defined(INCLUDE_FLEXCAN_CAN1)
- else if (flexcanBase == FLEXCAN1_BASE) {
- dbg_println("Begin setup of CAN1");
-
- // 33=PTE24=CAN1_TX, 34=PTE25=CAN1_RX (default)
- // NOTE: Alternative CAN1 pins are not broken out on Teensy 3.6
-
- CORE_PIN33_CONFIG = PORT_PCR_MUX(2);
- CORE_PIN34_CONFIG = PORT_PCR_MUX(2);// | PORT_PCR_PE | PORT_PCR_PS;
- }
- #endif
- }
-
- /*
- * \brief Intializes bus baud rate setting.
- *
- * \param baud - desired baud rate
- *
- * \retval None.
- *
- now using a system that tries to automatically generate a viable baud setting.
- Bear these things in mind:
- - The master clock is 16Mhz
- - You can freely divide it by anything from 1 to 256
- - There is always a start bit (+1)
- - The rest (prop, seg1, seg2) are specified 1 less than their actual value (aka +1)
- - This gives the low end bit timing as 5 (1 + 1 + 2 + 1) and the high end 25 (1 + 8 + 8 + 8)
- A worked example: 16Mhz clock, divisor = 19+1, bit values add up to 16 = 16Mhz / 20 / 16 = 50k baud
- */
-
- void FlexCAN::setBaudRate(uint32_t baud) {
- // have to find a divisor that ends up as close to the target baud as possible while keeping the end result between 5 and 25
-
- dbg_println ("Set baud rate");
-
- uint32_t divisor = 0;
- uint32_t bestDivisor = 0;
- uint32_t result = 16000000 / baud / (divisor + 1);
- int error = baud - (16000000 / (result * (divisor + 1)));
- int bestError = error;
-
- while (result > 5) {
- divisor++;
- result = 16000000 / baud / (divisor + 1);
-
- if (result <= 25) {
- error = baud - (16000000 / (result * (divisor + 1)));
-
- if (error < 0)
- error *= -1;
-
- // if this error is better than we've ever seen then use it - it's the best option
-
- if (error < bestError) {
- bestError = error;
- bestDivisor = divisor;
- }
-
- // If this is equal to a previously good option then
- // switch to it but only if the bit time result was in the middle of the range
- // this biases the output to use the middle of the range all things being equal
- // Otherwise it might try to use a higher divisor and smaller values for prop, seg1, seg2
- // and that's not necessarily the best idea.
-
- if ((error == bestError) && (result > 11) && (result < 19)) {
- bestError = error;
- bestDivisor = divisor;
- }
- }
- }
-
- divisor = bestDivisor;
- result = 16000000 / baud / (divisor + 1);
-
- if ((result < 5) || (result > 25) || (bestError > 300)) {
- Serial.println ("Abort in CAN begin. Couldn't find a suitable baud config!");
- return;
- }
-
- result -= 5; // the bitTimingTable is offset by 5 since there was no reason to store bit timings for invalid numbers
- uint8_t propSeg = bitTimingTable[result][0];
- uint8_t pSeg1 = bitTimingTable[result][1];
- uint8_t pSeg2 = bitTimingTable[result][2];
-
- // baud rate debug information
-
- dbg_println (" Bit time values:");
- dbg_print (" Prop = ");
- dbg_println (propSeg + 1);
- dbg_print (" Seg1 = ");
- dbg_println (pSeg1 + 1);
- dbg_print (" Seg2 = ");
- dbg_println (pSeg2 + 1);
- dbg_print (" Divisor = ");
- dbg_println (divisor + 1);
-
- FLEXCANb_CTRL1 (flexcanBase) = (FLEXCAN_CTRL_PROPSEG(propSeg) | FLEXCAN_CTRL_RJW(1) | FLEXCAN_CTRL_ERR_MSK |
- FLEXCAN_CTRL_PSEG1(pSeg1) | FLEXCAN_CTRL_PSEG2(pSeg2) | FLEXCAN_CTRL_PRESDIV(divisor));
- }
-
- /*
- * \brief Halts CAN bus.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::halt() {
- FLEXCANb_MCR(flexcanBase) |= (FLEXCAN_MCR_HALT);
- waitFrozen();
- }
-
- /*
- * \brief Exits from hat state.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::exitHalt() {
- // exit freeze mode and wait until it is unfrozen.
-
- dbg_println ("Exit halt");
- FLEXCANb_MCR(flexcanBase) &= ~(FLEXCAN_MCR_HALT);
- waitNotFrozen();
- }
-
- /*
- * \brief Makes CAN bus soft reset.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::softReset() {
- dbg_println ("Soft reset");
- FLEXCANb_MCR (flexcanBase) ^= FLEXCAN_MCR_SOFT_RST;
-
- while (FLEXCANb_MCR (flexcanBase) & FLEXCAN_MCR_SOFT_RST)
- ;
- }
-
- /*
- * \brief Freezes CAN bus.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::freeze() {
- FLEXCANb_MCR(flexcanBase) |= FLEXCAN_MCR_FRZ;
- }
-
- /*
- * \brief Waits until CAN bus is frozen
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::waitFrozen() {
- // wait for freeze ack
- dbg_println ("Wait frozen");
- while (!isFrozen());
- }
-
- /*
- * \brief Waits until CAN bus is not frozen.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::waitNotFrozen() {
- // wait for freeze ack
- dbg_println ("Wait not frozen");
- while (isFrozen());
- }
-
- /*
- * \brief Waits until CAN bus is ready
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::waitReady() {
- while (FLEXCANb_MCR(flexcanBase) & FLEXCAN_MCR_NOT_RDY);
- }
-
- /*
- * \brief Tests is CAN bus frozen.
- *
- * \param None.
- *
- * \retval true, if CAN bus is frozen.
- *
- */
-
- bool FlexCAN::isFrozen() {
- return (FLEXCANb_MCR(flexcanBase) & FLEXCAN_MCR_FRZ_ACK);
- }
-
- /*
- * \brief Set listen only mode on or off.
- *
- * \param mode - set listen only mode?
- *
- * \retval None.
- *
- */
-
- void FlexCAN::setListenOnly (bool mode)
- {
- // enter freeze mode if not already there
-
- if (!(FLEXCANb_MCR(flexcanBase) & FLEXCAN_MCR_FRZ_ACK)) {
- FLEXCANb_MCR(flexcanBase) |= FLEXCAN_MCR_FRZ;
- FLEXCANb_MCR(flexcanBase) |= FLEXCAN_MCR_HALT;
-
- while (!(FLEXCANb_MCR(flexcanBase) & FLEXCAN_MCR_FRZ_ACK))
- ;
- }
-
- if (mode)
- FLEXCANb_CTRL1(flexcanBase) |= FLEXCAN_CTRL_LOM;
- else
- FLEXCANb_CTRL1(flexcanBase) &= ~FLEXCAN_CTRL_LOM;
-
- // exit freeze mode and wait until it is unfrozen.
-
- FLEXCANb_MCR(flexcanBase) &= ~FLEXCAN_MCR_HALT;
-
- while (FLEXCANb_MCR(flexcanBase) & FLEXCAN_MCR_FRZ_ACK)
- ;
- }
-
- /*
- * \brief Initializes mailboxes to the requested mix of Rx and Tx boxes
- *
- * \param txboxes - How many of the 8 boxes should be used for Tx
- *
- * \retval number of tx boxes set.
- *
- */
-
- uint8_t FlexCAN::setNumTxBoxes (uint8_t txboxes)
- {
- uint8_t c;
- uint32_t oldIde;
-
- if (txboxes > getNumMailBoxes() - 1)
- txboxes = getNumMailBoxes() - 1;
-
- if (txboxes < 1)
- txboxes = 1;
-
- numTxMailboxes = txboxes;
-
- if ( !isInitialized() ) return numTxMailboxes; // Just set the numTxMailboxes. Begin() will do final initialization.
-
- // Inialize Rx boxen
-
- for (c = 0; c < getNumRxBoxes(); c++) {
- // preserve the existing filter ide setting
-
- oldIde = FLEXCANb_MBn_CS(flexcanBase, c) & FLEXCAN_MB_CS_IDE;
-
- FLEXCANb_MBn_CS(flexcanBase, c) = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_RX_EMPTY) | oldIde;
- }
-
- // Initialize Tx boxen
-
- for (c = getFirstTxBox(); c < getNumMailBoxes(); c++) {
- FLEXCANb_MBn_CS(flexcanBase, c) = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_INACTIVE);
- }
-
- return (numTxMailboxes);
- }
-
- /*
- * \brief Sets a per-mailbox filter. Sets both the storage and the actual mailbox.
- *
- * \param filter - a filled out filter structure
- * \param mbox - the mailbox to update
- *
- * \retval Nothing
- *
- */
-
- void FlexCAN::setFilter (const CAN_filter_t &filter, uint8_t mbox)
- {
- if ( mbox < getNumRxBoxes() ) {
- MBFilters[mbox] = filter;
-
- if (filter.flags.extended) {
- FLEXCANb_MBn_ID(flexcanBase, mbox) = (filter.id & FLEXCAN_MB_ID_EXT_MASK);
- FLEXCANb_MBn_CS(flexcanBase, mbox) |= FLEXCAN_MB_CS_IDE;
- } else {
- FLEXCANb_MBn_ID(flexcanBase, mbox) = FLEXCAN_MB_ID_IDSTD(filter.id);
- FLEXCANb_MBn_CS(flexcanBase, mbox) &= ~FLEXCAN_MB_CS_IDE;
- }
- }
- }
-
- /*
- * \brief Gets a per-mailbox filter.
- *
- * \param filter - returned filter structure
- * \param mbox - mailbox selected
- *
- * \retval true if mailbox s valid, false otherwise
- *
- */
-
- bool FlexCAN::getFilter (CAN_filter_t &filter, uint8_t mbox)
- {
- if ( mbox < getNumRxBoxes() ) {
- filter.id = MBFilters[mbox].id;
- filter.flags.extended = MBFilters[mbox].flags.extended;
- filter.flags.remote = MBFilters[mbox].flags.remote;
- filter.flags.reserved = MBFilters[mbox].flags.reserved;
-
- return (true);
- }
-
- return (false);
- }
-
- /*
- * \brief Set the mailbox mask for filtering packets
- *
- * \param mask - mask to apply.
- * \param mbox - mailbox number
- *
- * \retval None.
- */
-
- void FlexCAN::setMask (uint32_t mask, uint8_t mbox)
- {
- if ( mbox < getNumRxBoxes() ) {
-
- /* Per mailbox masks can only be set in freeze mode so have to enter that mode if not already there. */
-
- bool wasFrozen=isFrozen();
-
- if (!wasFrozen) {
- freeze();
- halt();
- }
-
- FLEXCANb_MB_MASK(flexcanBase, mbox) = mask;
-
- if (!wasFrozen) exitHalt();
- }
- }
-
- /*
- * \brief How many messages are available to read.
- *
- * \param None
- *
- * \retval A count of the number of messages available.
- */
-
- uint32_t FlexCAN::available (void)
- {
- irqLock();
- uint32_t result=(ringBufferCount (rxRing));
- irqRelease();
-
- return result;
- }
-
- /*
- * \brief Clear the collected statistics
- *
- * \param None
- *
- * \retval None
- */
-
- #if defined(COLLECT_CAN_STATS)
-
- void FlexCAN::clearStats (void)
- {
- // initialize the statistics structure
-
- memset (&stats, 0, sizeof(stats));
-
- stats.enabled = false;
- stats.ringRxMax = SIZE_RX_BUFFER - 1;
- stats.ringTxMax = SIZE_TX_BUFFER - 1;
- stats.ringRxFramesLost = 0;
- }
- #endif
-
- /*
- * \brief Retrieve a frame from the RX buffer
- *
- * \param msg - buffer reference to the frame structure to fill out
- *
- * \retval 0 no frames waiting to be received, 1 if a frame was returned
- */
-
- int FlexCAN::read (CAN_message_t &msg)
- {
- /* pull the next available message from the ring */
-
- int result=0;
-
- irqLock();
- if (removeFromRingBuffer (rxRing, msg) == true) {
- result=1;
- }
-
- irqRelease();
-
- return result;
- }
-
- /*
- * \brief Send a frame out of this canbus port
- *
- * \param msg - the filled out frame structure to use for sending
- *
- * \note Will do one of two things - 1. Send the given frame out of the first available mailbox
- * or 2. queue the frame for sending later via interrupt. Automatically turns on TX interrupt
- * if necessary.
- * Messages may be transmitted out of order, if more than one transmit mailbox is enabled.
- * The message queue ignores the message priority.
- *
- * Returns whether sending/queueing succeeded. Will not smash the queue if it gets full.
- */
-
- int FlexCAN::write (const CAN_message_t &msg)
- {
- uint32_t index=getNumMailBoxes();
- int result=0;
-
- irqLock();
-
- if ( isRingBufferEmpty(txRing) ) { // If there is nothing buffered, find free mailbox
-
- for (index = getFirstTxBox(); index < getNumMailBoxes(); index++) {
- if ( usesGlobalTxRing(index) && FLEXCAN_get_code(FLEXCANb_MBn_CS(flexcanBase, index)) == FLEXCAN_MB_CODE_TX_INACTIVE ) {
- break;// found one
- }
- }
- }
-
- if (index < getNumMailBoxes()) {
- dbg_println ("Writing a frame directly.");
-
- writeTxRegisters (msg, index);
- result=1;
- } else {
- // no mailboxes available. Try to buffer it
-
- if (addToRingBuffer (txRing, msg) == true) {
- result=1;
- }
- // else could not send the frame!
- }
-
- irqRelease();
-
- return result;
- }
-
- /*
- * \brief Send a frame out of this canbus port, using a specific mailbox. The TX queue is not used.
- *
- * \param msg - the filled out frame structure to use for sending
- * \param mbox - mailbox selected
- *
- * \note If the mailbox is available, the message is placed in the mailbox. The CAN controller
- * selects the next message to send from all filled transmit mailboxes, based on the priority.
- * This method allows callers to not use the transmit queue and prioritize messages by using
- * different mailboxes for different priority levels.
- * Using the same mailbox for a group of messages enforces the transmit order for this group.
- *
- * Returns whether the message was placed in the mailbox for sending.
- */
-
- int FlexCAN::write (const CAN_message_t &msg, uint8_t mbox)
- {
- int result=0;
-
- if ( !isTxBox(mbox) ) return result;
-
- irqLock();
-
- if ( txRings[mbox]==0 || isRingBufferEmpty(*txRings[mbox]) ) {
- if ( FLEXCAN_get_code(FLEXCANb_MBn_CS(flexcanBase, mbox)) == FLEXCAN_MB_CODE_TX_INACTIVE ) {
- writeTxRegisters (msg, mbox);
- result=1;
- }
- }
- if (result==0 && txRings[mbox]!=0 && addToRingBuffer(*txRings[mbox], msg) ) {
- result=1;
- }
-
- irqRelease();
-
- return result;
- }
-
- /*
- * \brief Write CAN message to the FlexCAN hardware registers.
- *
- * \param msg - message structure to send.
- * \param buffer - mailbox number to write to.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::writeTxRegisters (const CAN_message_t &msg, uint8_t buffer)
- {
- // transmit the frame
- // Commented below by TTL. That caused lock time to time to FLEXCAN_MB_CODE_TX_ONCE state.
- // FLEXCANb_MBn_CS(flexcanBase, buffer) = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_INACTIVE);
-
- if (msg.flags.extended) {
- FLEXCANb_MBn_ID(flexcanBase, buffer) = (msg.id & FLEXCAN_MB_ID_EXT_MASK);
- } else {
- FLEXCANb_MBn_ID(flexcanBase, buffer) = FLEXCAN_MB_ID_IDSTD(msg.id);
- }
-
- FLEXCANb_MBn_WORD0(flexcanBase, buffer) = (msg.buf[0]<<24)|(msg.buf[1]<<16)|(msg.buf[2]<<8)|msg.buf[3];
- FLEXCANb_MBn_WORD1(flexcanBase, buffer) = (msg.buf[4]<<24)|(msg.buf[5]<<16)|(msg.buf[6]<<8)|msg.buf[7];
-
- if (msg.flags.extended) {
- if (msg.flags.remote) {
- FLEXCANb_MBn_CS(flexcanBase, buffer) = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_ONCE) |
- FLEXCAN_MB_CS_LENGTH(msg.len) | FLEXCAN_MB_CS_SRR |
- FLEXCAN_MB_CS_IDE | FLEXCAN_MB_CS_RTR;
- } else {
- FLEXCANb_MBn_CS(flexcanBase, buffer) = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_ONCE) |
- FLEXCAN_MB_CS_LENGTH(msg.len) | FLEXCAN_MB_CS_SRR |
- FLEXCAN_MB_CS_IDE;
- }
- } else {
- if (msg.flags.remote) {
- FLEXCANb_MBn_CS(flexcanBase, buffer) = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_ONCE) |
- FLEXCAN_MB_CS_LENGTH(msg.len) | FLEXCAN_MB_CS_RTR;
- } else {
- FLEXCANb_MBn_CS(flexcanBase, buffer) = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_ONCE) |
- FLEXCAN_MB_CS_LENGTH(msg.len);
- }
- }
- }
-
- /*
- * \brief Read CAN message from the FlexCAN hardware registers.
- *
- * \param msg - message structure to fill.
- * \param buffer - mailbox number to read from.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::readRxRegisters (CAN_message_t& msg, uint8_t buffer)
- {
- uint32_t mb_CS = FLEXCANb_MBn_CS(flexcanBase, buffer);
-
- // get identifier and dlc
-
- msg.len = FLEXCAN_get_length (mb_CS);
- msg.flags.extended = (mb_CS & FLEXCAN_MB_CS_IDE) ? 1:0;
- msg.flags.remote = (mb_CS & FLEXCAN_MB_CS_RTR) ? 1:0;
- msg.timestamp = FLEXCAN_get_timestamp (mb_CS);
- msg.flags.overrun = 0;
- msg.flags.reserved = 0;
-
- msg.id = (FLEXCANb_MBn_ID(flexcanBase, buffer) & FLEXCAN_MB_ID_EXT_MASK);
-
- if (!msg.flags.extended) {
- msg.id >>= FLEXCAN_MB_ID_STD_BIT_NO;
- }
-
- // check for mailbox buffer overruns
-
- if (FLEXCAN_get_code (mb_CS) == FLEXCAN_MB_CODE_RX_OVERRUN) {
- msg.flags.overrun = 1;
- }
-
- // copy out message
-
- uint32_t dataIn = FLEXCANb_MBn_WORD0(flexcanBase, buffer);
- msg.buf[3] = dataIn;
- dataIn >>=8;
- msg.buf[2] = dataIn;
- dataIn >>=8;
- msg.buf[1] = dataIn;
- dataIn >>=8;
- msg.buf[0] = dataIn;
-
- if (msg.len > 4) {
- dataIn = FLEXCANb_MBn_WORD1(flexcanBase, buffer);
- msg.buf[7] = dataIn;
- dataIn >>=8;
- msg.buf[6] = dataIn;
- dataIn >>=8;
- msg.buf[5] = dataIn;
- dataIn >>=8;
- msg.buf[4] = dataIn;
- }
-
- for (uint32_t loop=msg.len; loop < 8; loop++ ) {
- msg.buf[loop] = 0;
- }
- }
-
- /*
- * \brief Initialize the specified ring buffer.
- *
- * \param ring - ring buffer to initialize.
- * \param buffer - buffer to use for storage.
- * \param size - size of the buffer in bytes.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::initRingBuffer (ringbuffer_t &ring, volatile CAN_message_t *buffer, uint32_t size)
- {
- ring.buffer = buffer;
- ring.size = size;
- ring.head = 0;
- ring.tail = 0;
- }
-
- /*
- * \brief Add a CAN message to the specified ring buffer.
- *
- * \param ring - ring buffer to use.
- * \param msg - message structure to add.
- *
- * \retval true if added, false if the ring is full.
- *
- */
-
- bool FlexCAN::addToRingBuffer (ringbuffer_t &ring, const CAN_message_t &msg)
- {
- uint16_t nextEntry;
-
- nextEntry = (ring.head + 1) % ring.size;
-
- /* check if the ring buffer is full */
-
- if (nextEntry == ring.tail) {
- return (false);
- }
-
- /* add the element to the ring */
-
- memcpy ((void *)&ring.buffer[ring.head], (void *)&msg, sizeof (CAN_message_t));
-
- /* bump the head to point to the next free entry */
-
- ring.head = nextEntry;
-
- return (true);
- }
-
- /*
- * \brief Remove a CAN message from the specified ring buffer.
- *
- * \param ring - ring buffer to use.
- * \param msg - message structure to fill in.
- *
- * \retval true if a message was removed, false if the ring is empty.
- *
- */
-
- bool FlexCAN::removeFromRingBuffer (ringbuffer_t &ring, CAN_message_t &msg)
- {
-
- /* check if the ring buffer has data available */
-
- if (isRingBufferEmpty (ring) == true) {
- return (false);
- }
-
- /* copy the message */
-
- memcpy ((void *)&msg, (void *)&ring.buffer[ring.tail], sizeof (CAN_message_t));
-
- /* bump the tail pointer */
-
- ring.tail = (ring.tail + 1) % ring.size;
-
- return (true);
- }
-
- /*
- * \brief Check if the specified ring buffer is empty.
- *
- * \param ring - ring buffer to use.
- *
- * \retval true if the ring contains data, false if the ring is empty.
- *
- */
-
- bool FlexCAN::isRingBufferEmpty (ringbuffer_t &ring)
- {
- if (ring.head == ring.tail) {
- return (true);
- }
-
- return (false);
- }
-
- /*
- * \brief Count the number of entries in the specified ring buffer.
- *
- * \param ring - ring buffer to use.
- *
- * \retval a count of the number of elements in the ring buffer.
- *
- */
-
- uint32_t FlexCAN::ringBufferCount (ringbuffer_t &ring)
- {
- int32_t entries;
-
- entries = ring.head - ring.tail;
-
- if (entries < 0) {
- entries += ring.size;
- }
-
- return ((uint32_t)entries);
- }
-
- /*
- * \brief Interrupt service routine for the FlexCAN class message events.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::message_isr (void)
- {
- uint32_t status = FLEXCANb_IFLAG1(flexcanBase);
- uint8_t controller = 0;
- uint32_t i;
- CAN_message_t msg;
- bool handledFrame;
- CANListener *thisListener;
- ringbuffer_t *pRing;
- #if defined(COLLECT_CAN_STATS)
- uint32_t rxEntries;
- #endif
-
- // determine which controller we're servicing
-
- #if defined (INCLUDE_FLEXCAN_CAN1)
- if (flexcanBase == FLEXCAN1_BASE)
- controller = 1;
- #endif
-
- // a message either came in or was freshly sent. Figure out which and act accordingly.
-
- for (i = 0; i < getNumMailBoxes(); i++) {
-
- // skip mailboxes that haven't triggered an interrupt
-
- if ((status & (1UL << i)) == 0) {
- continue;
- }
-
- // examine the reason the mailbox interrupted us
-
- uint32_t code = FLEXCAN_get_code (FLEXCANb_MBn_CS(flexcanBase, i));
-
- switch (code) {
-
- case FLEXCAN_MB_CODE_RX_FULL: // rx full, Copy the frame to RX buffer
- case FLEXCAN_MB_CODE_RX_OVERRUN: // rx overrun. Incomming frame overwrote existing frame.
- readRxRegisters (msg, i);
- handledFrame = false;
-
- // track message use count if collecting statistics
-
- #if defined(COLLECT_CAN_STATS)
- if (stats.enabled == true) {
- stats.mb[i].refCount++;
-
- if (msg.flags.overrun) {
- stats.mb[i].overrunCount++;
- }
- }
- #endif
-
- // First, try and handle via callback. If callback fails then buffer the frame.
-
- for (uint32_t listenerPos = 0; listenerPos < SIZE_LISTENERS; listenerPos++) {
- thisListener = listener[listenerPos];
-
- // process active listeners
-
- if (thisListener != NULL) {
-
- // call the handler if it's active for this mailbox
-
- if (thisListener->callbacksActive & (1UL << i)) {
- handledFrame |= thisListener->frameHandler (msg, i, controller);
- } else if (thisListener->callbacksActive & (1UL << 31)) {
- handledFrame |= thisListener->frameHandler (msg, -1, controller);
- }
- }
- }
-
- // if no objects caught this frame then queue it in the ring buffer
-
- if (handledFrame == false) {
- if (addToRingBuffer (rxRing, msg) != true) {
- // ring buffer is full, track it
-
- dbg_println ("Receiver buffer overrun!");
-
- #if defined(COLLECT_CAN_STATS)
- if (stats.enabled == true) {
- stats.ringRxFramesLost++;
- }
- #endif
- }
- }
-
- #if defined(COLLECT_CAN_STATS)
- if (stats.enabled == true) {
-
- // track the high water mark for the receive ring buffer
-
- rxEntries = ringBufferCount (rxRing);
-
- if (stats.ringRxHighWater < rxEntries) {
- stats.ringRxHighWater = rxEntries;
- }
- }
- #endif
-
- // it seems filtering works by matching against the ID stored in the mailbox
- // so after a frame comes in we've got to refresh the ID field to be the filter ID and not the ID
- // that just came in.
-
- if (MBFilters[i].flags.extended) {
- FLEXCANb_MBn_ID(flexcanBase, i) = (MBFilters[i].id & FLEXCAN_MB_ID_EXT_MASK);
- } else {
- FLEXCANb_MBn_ID(flexcanBase, i) = FLEXCAN_MB_ID_IDSTD(MBFilters[i].id);
- }
- break;
-
- case FLEXCAN_MB_CODE_TX_INACTIVE: // TX inactive. Just chillin' waiting for a message to send. Let's see if we've got one.
- // if there is a frame in the queue then send it
- pRing=( usesGlobalTxRing(i) ? &txRing : txRings[i] );
-
- if (isRingBufferEmpty (*pRing) == false) {
- if (removeFromRingBuffer (*pRing, msg) == true) {
- writeTxRegisters (msg, i);
- }
- } else {
- for (uint32_t listenerPos = 0; listenerPos < SIZE_LISTENERS; listenerPos++) {
- thisListener = listener[listenerPos];
-
- // process active listeners
- if (thisListener != NULL) {
- if (thisListener->callbacksActive & (1UL << i | 1UL << 31)) {
- thisListener->txHandler (i, controller);
- }
- }
- }
- }
-
- break;
-
- // currently unhandled events
-
- case FLEXCAN_MB_CODE_RX_INACTIVE: // inactive Receive box. Must be a false alarm!?
- case FLEXCAN_MB_CODE_RX_BUSY: // mailbox is busy. Don't touch it.
- case FLEXCAN_MB_CODE_RX_EMPTY: // rx empty already. Why did it interrupt then?
- case FLEXCAN_MB_CODE_TX_ABORT: // TX being aborted.
- case FLEXCAN_MB_CODE_TX_RESPONSE: // remote request response (deprecated)
- case FLEXCAN_MB_CODE_TX_ONCE: // TX mailbox is full and will be sent as soon as possible
- case FLEXCAN_MB_CODE_TX_RESPONSE_TEMPO: // remote request junk again. Go away.
- break;
-
- default:
- break;
- }
- }
-
- // writing the flag value back to itself clears all flags
-
- FLEXCANb_IFLAG1(flexcanBase) = status;
- }
-
- /*
- * \brief Attach an object to the listening list.
- *
- * \param listener - pointer to listening object
- *
- * \retval true if listener added to list, false if the list is full.
- *
- */
-
- bool FlexCAN::attachObj (CANListener *listener)
- {
- uint32_t i;
-
- for (i = 0; i < SIZE_LISTENERS; i++) {
- if (this->listener[i] == NULL) {
- this->listener[i] = listener;
- listener->callbacksActive = 0;
- return true;
- }
- }
-
- return false;
- }
-
- /*
- * \brief Detatch an object from the listening list.
- *
- * \param listener - pointer to listening object
- *
- * \retval true if listener removed from list, false if object not found.
- *
- */
-
- bool FlexCAN::detachObj (CANListener *listener)
- {
- uint32_t i;
-
- for (i = 0; i < SIZE_LISTENERS; i++) {
- if (this->listener[i] == listener) {
- this->listener[i] = NULL;
-
- return true;
- }
- }
-
- return false;
- }
-
- void FlexCAN::bus_off_isr (void)
- {
- }
-
- /*
- * \brief Interrupt service routine for FlexCAN class device errors.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::error_isr (void)
- {
- uint32_t status = FLEXCANb_ESR1 (flexcanBase);
- // CAN_message_t msg;
-
- // an acknowledge error happened - frame was not ACK'd
-
- if (status & FLEXCAN_ESR_ACK_ERR) {
- // this ISR doesn't get a buffer passed to it so it would have to be cached elsewhere.
- // msg.flags.extended = (FLEXCANb_MBn_CS(flexcanBase, buffer) & FLEXCAN_MB_CS_IDE)? 1:0;
- // msg.id = (FLEXCANb_MBn_ID(flexcanBase, buffer) & FLEXCAN_MB_ID_EXT_MASK);
- // if (!msg.flags.extended) {
- // msg.id >>= FLEXCAN_MB_ID_STD_BIT_NO;
- //
- // }
- }
- }
-
- /*
- * \brief Interrupt service routine for the FlexCAN class transmit warnings.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::tx_warn_isr (void)
- {
- }
-
- /*
- * \brief Interrupt service routine for the FlexCAN class receive warnings.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::rx_warn_isr (void)
- {
- }
-
- /*
- * \brief Interrupt service routine for the FlexCAN class device wakeup.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void FlexCAN::wakeup_isr (void)
- {
- }
-
- /*
- * \brief Interrupt handler for FlexCAN can0 message events.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can0_message_isr (void)
- {
- Can0.message_isr ();
- }
-
- /*
- * \brief Interrupt handler for FlexCAN can0 bus off event.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can0_bus_off_isr (void)
- {
- Can0.bus_off_isr ();
- }
-
- /*
- * \brief Interrupt handler for FlexCAN can0 error events.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can0_error_isr (void)
- {
- Can0.error_isr ();
- }
-
- /*
- * \brief Interrupt handler for FlexCAN can0 transmit warning event.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can0_tx_warn_isr (void)
- {
- Can0.tx_warn_isr ();
- }
-
- /*
- * \brief Interrupt handler for FlexCAN can0 receive warning event.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can0_rx_warn_isr (void)
- {
- Can0.rx_warn_isr ();
- }
-
- /*
- * \brief Interrupt handler for FlexCAN can0 device wakeup event.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can0_wakeup_isr (void)
- {
- Can0.wakeup_isr ();
- }
-
- #if defined(INCLUDE_FLEXCAN_CAN1)
-
- /*
- * \brief Interrupt handler for FlexCAN can1 message events.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can1_message_isr (void)
- {
- Can1.message_isr ();
- }
-
- /*
- * \brief Interrupt handler for FlexCAN can1 bus off event.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can1_bus_off_isr (void)
- {
- Can1.bus_off_isr ();
- }
-
- /*
- * \brief Interrupt handler for FlexCAN can1 error events.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can1_error_isr (void)
- {
- Can1.error_isr ();
- }
-
- /*
- * \brief Interrupt handler for FlexCAN can1 transmit warning event.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can1_tx_warn_isr (void)
- {
- Can1.tx_warn_isr ();
- }
-
- /*
- * \brief Interrupt handler for FlexCAN can1 receive warning event.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can1_rx_warn_isr (void)
- {
- Can1.rx_warn_isr ();
- }
-
- /*
- * \brief Interrupt handler for FlexCAN can1 device wakeup event.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void can1_wakeup_isr (void)
- {
- Can1.wakeup_isr ();
- }
-
- #endif /* INCLUDE_FLEXCAN_CAN1 */
-
- /*
- * \brief CANListener constructor
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- CANListener::CANListener ()
- {
- // none. Bitfield were bits 0-15 are the mailboxes and bit 31 is the general callback
-
- callbacksActive = 0;
- }
-
- /*
- * \brief Default CAN received frame handler.
- *
- * \param frame - CAN frame to process.
- * \param mailbox - mailbox number frame arrived at.
- * \param controller - controller number frame arrived from.
- *
- * \retval true if frame was handled, false otherwise.
- *
- */
-
- bool CANListener::frameHandler (CAN_message_t &/*frame*/, int /*mailbox*/, uint8_t /*controller*/)
- {
-
- /* default implementation that doesn't handle frames */
-
- return (false);
- }
-
- /*
- * \brief Default CAN transmission completed handler.
- *
- * \param mailbox - transmit mailbox that is now available.
- * \param controller - controller number.
- *
- */
-
- void CANListener::txHandler (int /*mailbox*/, uint8_t /*controller*/)
- {
-
- }
-
- /*
- * \brief Indicate mailbox has an active callback.
- *
- * \param mailBox - mailbox number.
- *
- * \retval None.
- *
- */
-
- void CANListener::attachMBHandler (uint8_t mailBox)
- {
- if ( (mailBox < NUM_MAILBOXES) ) {
- callbacksActive |= (1L << mailBox);
- }
- }
-
- /*
- * \brief Clear callback indicator for a mailbox.
- *
- * \param mailBox - mailbox number.
- *
- * \retval None.
- *
- */
-
- void CANListener::detachMBHandler (uint8_t mailBox)
- {
- if ( (mailBox < NUM_MAILBOXES) ) {
- callbacksActive &= ~(1UL << mailBox);
- }
- }
-
- /*
- * \brief Set general purpose callback indicator.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void CANListener::attachGeneralHandler (void)
- {
- callbacksActive |= (1UL << 31);
- }
-
- /*
- * \brief Clear general purpose callback indicator.
- *
- * \param None.
- *
- * \retval None.
- *
- */
-
- void CANListener::detachGeneralHandler (void)
- {
- callbacksActive &= ~(1UL << 31);
- }
|