// ------------------------------------------------------------- // 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() || 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); }