FlexCAN_T4 - FlexCAN for Teensy 4

That data shows that flexcan is in process of transmitting in hardware, and not receiving ACK from network, so that leaves 2 only possibilities:

Baudrate calculation (which i doubt, since you are receiving)
and
Transceiver.

Can you try CAN3 in FD mode? it is able to receive CAN2 frames and send them, but make sure for sending in CANFD that msg.brs and msg.edl are both set to 0 (zero), in order to send a CAN2 frame. CANFD uses a different calculation not adopted from IFCT but calculated from NXP documents. I'd try the recommended receiver approach tho
 
I tested your code on my T4 board with TI TCAN330GD transceivers on CAN1 and CAN2. I wired CAN2 to CAN1 and added termination resistors. Everything functioned as intended.

I suggest trying to replicate this setup and apply the following code to confirm your setup. I commented out everything that wasn't absolutely necessary for this test.


Code:
/* Teensy 4.0
  CAN2 using SN65HVD230 can tranciever
*/

// #include <EEPROM.h>
#include <FlexCAN_T4.h>
FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_256> myCan;

// CAN_message_t msg, rxmsg;


/******************* ADDED FOR DEBUGGING ***********************/
// CAN1 used to ACK messages sent by myCAN
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_256> myCanDEBUG;
/******************* END DEBUGGING ***********************/


// #define FEED 3 // oval gear flow meter hall effect digital input pin
// #define RETURN 4 // oval gear flow meter hall effect digital input pin
// #define Calibrate 5 // calibrate button

// boolean EEPROM_INIT = 0;

// unsigned long prevMillis;
// unsigned long txIntervalMillis = 20; // send once per second

// byte Data[8];

// volatile unsigned long FLOWIN_COUNT = 0;
// volatile unsigned long FLOWOUT_COUNT = 0;
// unsigned long CURRENT_millis = 0;
// unsigned long LAST_millis = 0;
// unsigned long LAST_eeprom = 0;

// int PPLIN;
// int PPLOUT;
// float UPDATE = 50.00; // ms between update cycles
// float PPL = 1448.00;
// float PPLin = 1448.00;  // pulses per litre
// float PPLout = 1448.00; // pulses per litre
// float LPM = 0;
// float FLOW_IN = 0;
// float FLOW_RET = 0;
// float FLOW_USED = 0;
// float LASTFLOW = 0;
// float LPMFACTOR;
// boolean CALIBRATION_MODE = false;
// boolean CAL = false;
// float per_diff;
// int PPLI1, PPLI2, PPLO1, PPLO2;
// int AVE_COUNT = 0;
// boolean AVE_RUNNING = false;


void setup()
{

  // pinMode(FEED, INPUT);   // yellow
  // pinMode(RETURN, INPUT); // blue
  // pinMode(Calibrate, INPUT_PULLUP);
  // pinMode(1, OUTPUT);
  // digitalWrite(1, LOW);
  Serial.begin(115200);
  // attachInterrupt(digitalPinToInterrupt(FEED), FLOW_INP, FALLING);   // FEED LINE FLOW METER INTERRUPT
  // attachInterrupt(digitalPinToInterrupt(RETURN), FLOW_OUT, FALLING); // FEED LINE FLOW METER INTERRUPT
  // LPMFACTOR = ((PPL / 60.00) / 1000.00);                             // L/M = ((PULSES PER LITRE / 60 SEC) /1000ms) ==> pulses per ms for 1 L/min flow

  // Serial.println("SimpleRxAckPayload Starting");

  myCan.begin();
  myCan.setBaudRate(500000);
  myCan.mailboxStatus();

/******************* ADDED FOR DEBUGGING ***********************/
// CAN1 used to ACK messages sent by myCAN
  myCanDEBUG.begin();
  myCanDEBUG.setBaudRate(500000);
/******************* END DEBUGGING *****************************/
  // if (EEPROM_INIT == true)
  // {
  //   EEPROM_INITIALISE();
  // }

  // READ_EEPROM();
}

// void READ_EEPROM()
// {

//   PPLIN = EEPROM.read(0) << 8;
//   PPLIN |= EEPROM.read(1);
//   PPLin = PPLIN / 10.00;
//   PPLOUT = EEPROM.read(2) << 8;
//   PPLOUT |= EEPROM.read(3);
//   PPLout = PPLOUT / 10.00;
//   Serial.print("PPLin = ");
//   Serial.print(PPLin);
//   Serial.print(" ");
//   Serial.println(PPLIN);
//   Serial.print("PPLout = ");
//   Serial.print(PPLout);
//   Serial.print(" ");
//   Serial.println(PPLOUT);
// }

// void EEPROM_INITIALISE()
// { // enable in setup to write initial values to eeprom

//   PPLIN = 1448.00;
//   PPLOUT = 1448.00;
//   PPLI1 = (byte)((PPLIN >> 8) & 0xFFu);  //split pulse per litre input into msb
//   PPLI2 = (byte)(PPLIN & 0xFFu);         //split pulse per litre input into lsb
//   PPLO1 = (byte)((PPLOUT >> 8) & 0xFFu); //split pulse per litre output into msb
//   PPLO2 = (byte)(PPLOUT & 0xFFu);        //split pulse per litre output into lsb

//   EEPROM.write(0, PPLI1); // Write values to EEPROM
//   EEPROM.write(1, PPLI2); // Write values to EEPROM
//   EEPROM.write(2, PPLO1); // Write values to EEPROM
//   EEPROM.write(3, PPLO2); // Write values to EEPROM
// }

// void WRITE_EEPROM()
// {

//   PPLIN = PPLin * 10;
//   PPLOUT = PPLout * 10;
//   PPLI1 = (byte)((PPLIN >> 8) & 0xFFu);  //msb
//   PPLI2 = (byte)(PPLIN & 0xFFu);         //lsb
//   PPLO1 = (byte)((PPLOUT >> 8) & 0xFFu); //msb
//   PPLO2 = (byte)(PPLOUT & 0xFFu);        //lsb

//   EEPROM.write(0, PPLI1);
//   EEPROM.write(1, PPLI2);
//   EEPROM.write(2, PPLO1);
//   EEPROM.write(3, PPLO2);
// }

void loop()
{

  // if (digitalRead(Calibrate) == LOW && CALIBRATION_MODE == false)
  // {
  //   AVE_RUNNING = true;
  //   CALIBRATION_MODE = true;
  // }

  // CURRENT_millis = millis();

  // if (CURRENT_millis - LAST_millis > UPDATE)
  // {
  //   LPM = FLOWIN_COUNT - LASTFLOW;                        // calculate how many pulses have occured since last calculation
  //   CURRENT_millis = millis();                            // update millis
  //   LPM = LPM / ((CURRENT_millis - LAST_millis) / 10.00); // divide by elapsed milliseconds
  //   LPM = LPM / LPMFACTOR;                                // divide by the 1 L/min factor
  //   FLOW_IN = FLOWIN_COUNT / PPLin;                       // TOTAL FLOW = #OF PULSES / PULSES PER LITRE
  //   FLOW_RET = FLOWOUT_COUNT / PPLout;                    // TOTAL RETURN = #OF PULSES / PULSES PER LITRE
  //   FLOW_USED = FLOW_IN - FLOW_RET;                       // USED = in - out count

  //   per_diff = abs(((FLOW_RET / FLOW_IN) * 100.00));
  //   Serial.print(" FLOW DIFF PERCENTAGE = ");
  //   Serial.print(per_diff);

  //   int LPMSEND = LPM * 100;
  //   int FLOWSEND = FLOW_USED * 100;

  //   //prepare data for can send . split into 2 bytes of most and least significant bits
  //   Data[0] = (byte)((LPMSEND >> 8) & 0xFFu);  // Litres per minute msb
  //   Data[1] = (byte)(LPMSEND & 0xFFu);         // Litres per minute lsb
  //   Data[2] = (byte)((FLOWSEND >> 8) & 0xFFu); // flow msb
  //   Data[3] = (byte)(FLOWSEND & 0xFFu);        // flow lsb
  //   Data[4] = 0;
  //   Data[5] = 0;
  //   Data[6] = 0;

  //   Serial.print(" FLOWIN_COUNT = ");
  //   Serial.print(FLOWIN_COUNT);
  //   Serial.print(" FLOWOUT_COUNT = ");
  //   Serial.print(FLOWOUT_COUNT);
  //   Serial.print(" FLOW_IN = ");
  //   Serial.print(FLOW_IN);
  //   Serial.print(" FLOW_RET = ");
  //   Serial.print(FLOW_RET);
  //   Serial.print(" FLOW_USED = ");
  //   Serial.print(FLOW_USED);
  //   Serial.print(" LPM = ");
  //   Serial.print(LPM);
  //   Serial.print(" LPMfactor = ");
  //   Serial.println(LPMFACTOR);

  //   LAST_millis = millis();
  //   LASTFLOW = FLOWIN_COUNT;

  //   if (AVE_RUNNING == true) // runs with pumps on engine off to zero differential reading
  //   {
  //     if (AVE_COUNT < 1000)
  //     {
  //       PPLout = PPLout * (per_diff / 100);
  //       Serial.print("PPLout = ");
  //       Serial.println(PPLout);
  //       AVE_COUNT++;
  //     }
  //     else if (AVE_COUNT == 100)
  //     {
  //       WRITE_EEPROM();      // save new factor for calculations
  //       AVE_RUNNING = false; // disable function
  //     }
  //   }
  // }

  // if (CURRENT_millis - prevMillis >= txIntervalMillis)
  // {
  SEND_CAN();
  // }

  static uint32_t timeoutMBStatus = millis();
  if ( millis() - timeoutMBStatus > 2000 )
  {
    myCan.mailboxStatus();
    timeoutMBStatus = millis();
  }
}

void SEND_CAN()
{
  static uint32_t timeout;
  if (millis() - timeout > 200)

  { // send random frame every 1000ms
    CAN_message_t msg;
    msg.id = 0x236; // address ID 566
    msg.buf[0] = lowByte(millis());
//    msg.buf[1] = Data[1];
//    msg.buf[2] = Data[2];
//    msg.buf[3] = Data[3];
//    msg.buf[4] = Data[4];
//    msg.buf[5] = Data[5];
//    msg.buf[6] = Data[6];
//    msg.buf[7] = Data[7];
//    msg.len = 8;
//    msg.flags.extended = 0;
//    msg.flags.remote = 0;
    myCan.write(msg);
    timeout = millis();

    Serial.print("SEND: ");
    Serial.print(" ID: ");
    Serial.print(msg.id);
    Serial.print(" Buffer: ");
    for (uint8_t i = 0; i < msg.len; i++)
    {
      Serial.print(msg.buf[i]);
      Serial.print(" ");
    }
    Serial.println();
  }
}


// void FLOW_INP()
// {
//   FLOWIN_COUNT++;
// }

// void FLOW_OUT()
// {
//   FLOWOUT_COUNT++;
// }
 
toton81 I wish i could access the Can3 pins but the Teensy has already been soldered to breadboard with header pins,...
And msadie i tried the modified code and still nothing.. I've ordered a few TCAN330GD's and TJA1051's as we know the software side of things is working. I'll let you know how it goes when they arrive
 
So i received the TCAN330GD's today and swapped it out on the breakout board and worked instantly. Even the original iteration of the software was flawless. Thank you so much for your support and suggestions along the way. Just for future reference the transceivers that did not work were SN65HVD230 chips marked VD230 if anyone runs into the same issues as me. Love this community and the support that comes with it. Thanks again
 
Tony, is there any type of wakeup interrup that can be used to wake up the T4 from sleep mode as soon as the bus comes alive?
I've searched throgh the forum and also looked at the .h file in the library but didn't see such a function.

I want to put the T4 to sleep if the global callback hasn't been triggered for a while (5-10 seconds).
The only option I see available is once the bus goes silent (car ignition off and locked), the sketch will reconfigure the Rx pin as a wake up interrupt input, and as soon as the bus comes alive, will trigger the wake up of the T4, reconfigure the pin and re-initialize the FlexCan library.

Do you think this is possible?
 
The WAKMSK bit is already set in the MCR register. It should already work just check the reference manual. Not sure if this applies to this scenario, or if flexcan refers to ANY frame for wakeup, but when a car shuts down the signal on the BCAN devices go to sleep renderring the bus silent. However, there is a wakeup-frame that you can send to temporarily wake the bus before issuing more frames, but you want the opposite, I havn't tested sleep modes but the library is set for wakeup though in the MCR
 
More info:

Screenshot_20200506-115135_Samsung Internet.jpg
 

A little advance for my basic coding capabilities.. :D
So if I understand right... I'll call that line of code when I want to put the T4 to sleep, and it will automatically wake up the T4 when a frame is received?
Also, regarding the method I mentioned above with resetting Can1's Rx pin, I guess I need to set the constructor again after the T4 wakes up:
Code:
FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> myCan;

Is there another way I can reconfigure the Rx pin as a CAN pin once T4 has woken up?
 
Can0.setRX() without value reinits the pin to default location
Theoretically the CAN supposed to self wake without user intervention

Dont rerun the constructor. These are compile time objects, it wont be dynamically called in code. This was the only way to allow the user to choose custom buffer rx/tx sizes along with user named objects instead of the default Can0 and Can1 in other libraries. Thats how I can label the CANs in my car as BCAN and HSCAN :)
 
Theoretically the CAN supposed to self wake without user intervention
I just want to confirm that I understand this statement correctly - If I put the Teensy to sleep (using Snooze library), and connect power up a CAN node thats connected to the bus, it should wake up by itself? Or does it have to receive a frame to wake up?
I ask because my car (VW MQB platform) does not transmit any frames via the OBD port during unlocking of the car, turning ignition on or starting the engine - everything needs to be requested.
My hope is that the Teensy will wake up as soon as the CAN gateway is powered or turns it's CAN transceiver on
 
Supposedly it can wake itself up, I havn't played in that low power field yet, but like I said on cars pins 6 and 14 are the high speed network thats only running when ignition is on, body controller network goes to sleep about 10-20 seconds (when ignition goes off) and all devices on the car body network goto sleep. A special wakeup packet can be transmitted to wake them up again for another 10-20 seconds, although looking at the nxp info i posted before it doesn't mention anything about a special frame so no clue on that stuff yet, or if it acts on any network activity, or if its possibly able to wake on a filtered id like the car does, hard to know, but I will look into it later on

Your workaround should work by reconfiguring the pin and after wakeup you can run setRX() to restore the pin to CAN mode.

Although my T4 runs 24/7 in car I never needed a use for sleep mode yet, but that I will add eventually, the stores are still closed so I havn't been able to replace my busted monitor yet with a high end ultrawide version (sometimes i have 4 arduino IDEs open at same time), but i am taking notes for what I will work on for this library:

1) store all transmit messages in queue (not just seq) and offload/distribute them via ISR, events() will no longer dequeue transmits, but will still retain and return the 12bits queue sizes for both transmit and receive queues.

2) wake up via CAN from sleep functionality
 
Last edited:
You can just cast it to an int

(int8_t)can_MsgRx.buf[4]
Although its been two months since I added this to my code, but due to covid lockdown I only got to put this to test today.
I tried casting the indexed buffer, but its still being set as an unsigned integer.
Anything else I could try to get it to cast to a signed int?
 
You can't set a signed value to a array of unsigned bytes. What you want to do is cast a signed byte to an unsigned byte to the array then at other end you cast back the unsigned byte as a int8_t to get it back
 
Well finally got my monitor today, first time I ordered one online after having a dead one for couple months!

20200516_232531.jpg

Fits perfect on the desk lol.......

Anyways, I started working on flexcan code, I will post test updates here of the new features for testing.
The FD side won't be touched unless good testing is done on CAN2.0 side, I don't want to run at 2 different codes for bugs :)

Added loop back mode to CAN2.0 side:
Code:
enableLoopBack();

Added all transmits to queue. These are the actions:

write(MB13,msg); /* this queues a frame to be submitted on a specific mailbox, the ISR will place it when the MB13 slot is free */
write(msg); /* places a frame in queue, that the ISR will drop in any available TX queue */

msg.seq (sequential) can be processed now in either write functions above. However, the ISR will only submit it to flexcan if and only if the absolute first mailbox is available.

retained remote frame support, also processed by the ISR. During transmission, flexcan transforms the mailbox to a RX_EMPTY mailbox with the iflag set. We use this to identify the mailbox we used to send a remote frame and switch it back to a transmit mailbox.

2 files have been updated for testing, always use the last updated github copy and override those 2 files:

Code:
[ATTACH]20157._xfImport[/ATTACH]

As per my source comment regarding the remote frames in the ISR in combination with sequential frames:

Code:
      /* if transmitting sequential frames, don't assume pending remote frame transmissions
         arn't processing, otherwise the swap to RX_EMPTY would cause the next MB to transmit
         the sequential, essentially consuming 2 mailboxes for sequential writes. This prevents
         this issue by checking next cycle after current transmissions in the status register */
      if ( msg.seq && FLEXCANb_ESR1(_bus) & (1UL << 6) ) continue;

events() no longer handles transmission dequeues, but still returns it's last 12 bits size of the transmit queue.
 
Last edited:
thanks, I was worried about the weight at first on the glass desk, but the back and front legs line up easily on the cross bars under holding the glass, so no pressure on the glass itself, the width is just perfect for the desk :D

Code:
https://www.asus.com/us/Monitors/ROG-Strix-XG438Q/
 
Just added wake up on traffic support.

You can call:
Code:
asm(" wfi");
in your code to goto sleep. The loop() will stop scrolling after shorting out the CAN lines when the wfi enters sleep. Removing the short on the CAN lines restores the CAN network, and Teensy 4 will self-wake when data starts rolling on the bus

Code:
[ATTACH]20158._xfImport[/ATTACH]
 
thanks, I was worried about the weight at first on the glass desk, but the back and front legs line up easily on the cross bars under holding the glass, so no pressure on the glass itself, the width is just perfect for the desk :D

Code:
https://www.asus.com/us/Monitors/ROG-Strix-XG438Q/

~34 pounds for 43 inches of screen - good indeed the broad base feet end up supported just to be sure.
 
https://www.asus.com/us/Monitors/ROG-Strix-XG438Q/
Wow! That is quite the monitor!

I started looking over your updates and found a small bug in flexcan_interrupt() for TX: The "code" value for TX MB's still contains the previous frame's DLC, which is then OR'd with the new frame's DLC. Subsequently, messages can only grow in length per MB (ex: once an 8-byte frame is sent, even 1-byte messages are transmitted as 8-byte. I confirmed clearing the code variable before assigning properties of the new frame fixes the problem. My change just for reference:

Code:
FCTP_FUNC void FCTP_OPT::flexcan_interrupt() {
  CAN_message_t msg; // setup a temporary storage buffer
  uint64_t imask = readIMASK(), iflag = readIFLAG();

  if ( !(FLEXCANb_MCR(_bus) & (1UL << 15)) ) { /* if DMA is disabled, ONLY THEN you can handle FIFO in ISR */
    if ( (FLEXCANb_MCR(_bus) & FLEXCAN_MCR_FEN) && (imask & FLEXCAN_IMASK1_BUF5M) && (iflag & FLEXCAN_IFLAG1_BUF5I) ) { /* FIFO is enabled, capture frames if triggered */
      volatile uint32_t *mbxAddr = &(*(volatile uint32_t*)(_bus + 0x80 + (0 * 0x10)));
      uint32_t code = mbxAddr[0];
      msg.len = (code & 0xF0000) >> 16;
      msg.flags.remote = (bool)(code & (1UL << 20));
      msg.flags.extended = (bool)(code & (1UL << 21));
      msg.timestamp = code & 0xFFFF;
      msg.id = (mbxAddr[1] & 0x1FFFFFFF) >> ((msg.flags.extended) ? 0 : 18);
      msg.idhit = code >> 23;
      for ( uint8_t i = 0; i < (8 >> 2); i++ ) for ( int8_t d = 0; d < 4 ; d++ ) msg.buf[(4 * i) + 3 - d] = (uint8_t)(mbxAddr[2 + i] >> (8 * d));
      msg.bus = busNumber;
      msg.mb = FIFO; /* store the mailbox the message came from (for callback reference) */
      (void)FLEXCANb_TIMER(_bus);
      writeIFLAGBit(5); /* clear FIFO bit only! */
      if ( iflag & FLEXCAN_IFLAG1_BUF6I ) writeIFLAGBit(6); /* clear FIFO bit only! */
      if ( iflag & FLEXCAN_IFLAG1_BUF7I ) writeIFLAGBit(7); /* clear FIFO bit only! */
      frame_distribution(msg);
      ext_output1(msg);
      ext_output2(msg);
      ext_output3(msg);
      if (fifo_filter_match(msg.id)) struct2queueRx(msg);
    }
  }

  uint8_t exit_point = 64 - __builtin_clzll(iflag | 1); /* break from MSB's if unset, add 1 to prevent undefined behaviour in clz for 0 check */
  int8_t first_tx_found = -1;

  for ( uint8_t mb_num = mailboxOffset(); mb_num < FLEXCANb_MAXMB_SIZE(_bus); mb_num++ ) {

    if ( !txBuffer.size() ) { /* if transmits exist, don't skip any mailboxes */
      if ( mb_num >= exit_point ) break; /* early exit from higher unflagged mailboxes */
      if (!(imask & (1ULL << mb_num))) continue; /* don't read non-interrupt mailboxes */
      if (!(iflag & (1ULL << mb_num))) continue; /* don't read unflagged mailboxes */
    }

    volatile uint32_t *mbxAddr = &(*(volatile uint32_t*)(_bus + 0x80 + (mb_num * 0x10)));
    uint32_t code = mbxAddr[0];

    if ( (FLEXCAN_get_code(code) >> 3) && (first_tx_found == -1) ) first_tx_found = mb_num;

    if ( FLEXCAN_get_code(code) == FLEXCAN_MB_CODE_RX_EMPTY ) {
      /* there are no flags for EMPTY reception boxes, however, when sending remote
         frames, the mailboxes switch to RX_EMPTY and trigger the flag */
      if (!(iflag & (1ULL << mb_num))) continue; /* only process the flagged RX_EMPTY mailboxes */
      writeIFLAGBit(mb_num); /* clear IFLAG */
      mbxAddr[0] = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_INACTIVE);
    }

    else if ( ( FLEXCAN_get_code(code) == FLEXCAN_MB_CODE_RX_FULL ) ||
         ( FLEXCAN_get_code(code) == FLEXCAN_MB_CODE_RX_OVERRUN ) ) {

      /* treat receptions normally if transmit queue exists */
      if (!(imask & (1ULL << mb_num))) continue; /* don't read non-interrupt mailboxes */
      if (!(iflag & (1ULL << mb_num))) continue; /* don't read unflagged mailboxes */

      msg.flags.extended = (bool)(code & (1UL << 21));
      msg.id = (mbxAddr[1] & 0x1FFFFFFF) >> ((msg.flags.extended) ? 0 : 18);
      if ( FLEXCAN_get_code(code) == FLEXCAN_MB_CODE_RX_OVERRUN ) msg.flags.overrun = 1;
      msg.len = (code & 0xF0000) >> 16;
      msg.mb = mb_num;
      msg.timestamp = code & 0xFFFF;
      msg.bus = busNumber;
      for ( uint8_t i = 0; i < (8 >> 2); i++ ) for ( int8_t d = 0; d < 4 ; d++ ) msg.buf[(4 * i) + 3 - d] = (uint8_t)(mbxAddr[2 + i] >> (8 * d));
      mbxAddr[0] = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_RX_EMPTY) | ((msg.flags.extended) ? (FLEXCAN_MB_CS_SRR | FLEXCAN_MB_CS_IDE) : 0);
      (void)FLEXCANb_TIMER(_bus);
      writeIFLAGBit(mb_num);
      if ( filter_match((FLEXCAN_MAILBOX)mb_num, msg.id) ) struct2queueRx(msg); /* store frame in queue */
      frame_distribution(msg);
      ext_output1(msg);
      ext_output2(msg);
      ext_output3(msg);
    }

    else if ( FLEXCAN_get_code(code) == FLEXCAN_MB_CODE_TX_INACTIVE ) {
      if (!(imask & (1ULL << mb_num))) writeIMASKBit(mb_num); /* enable interrupt for MB if not enabled */

      writeIFLAGBit(mb_num); /* clear IFLAG */
      if ( first_tx_found == -1 ) continue;
      if ( !txBuffer.size() ) continue;

      uint8_t buf[sizeof(CAN_message_t)];
      txBuffer.peek_front(buf, sizeof(CAN_message_t));
      memmove(&msg, buf, sizeof(msg));

      if ( msg.seq && mb_num != first_tx_found ) continue;
      else if ( msg.mb == -1 );
      else if ( msg.mb == mb_num );
      else continue;

      /* if transmitting sequential frames, don't assume pending remote frame transmissions
         arn't processing, otherwise the swap to RX_EMPTY would cause the next MB to transmit
         the sequential, essentially consuming 2 mailboxes for sequential writes. This prevents
         this issue by checking next cycle after current transmissions in the status register */
      if ( msg.seq && FLEXCANb_ESR1(_bus) & (1UL << 6) ) continue;

      txBuffer.pop_front(); /* clear on write */

      code = 0; /* MSADIE - clear code before configuring for new tx frame */

      mbxAddr[0] = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_INACTIVE);
      mbxAddr[1] = (( msg.flags.extended ) ? ( msg.id & FLEXCAN_MB_ID_EXT_MASK ) : FLEXCAN_MB_ID_IDSTD(msg.id));
      if ( msg.flags.remote ) code |= (1UL << 20);
      if ( msg.flags.extended ) code |= (3UL << 21);
      for ( uint8_t i = 0; i < (8 >> 2); i++ ) mbxAddr[2 + i] = (msg.buf[0 + i * 4] << 24) | (msg.buf[1 + i * 4] << 16) | (msg.buf[2 + i * 4] << 8) | msg.buf[3 + i * 4];
      code |= msg.len << 16;
      mbxAddr[0] = code | FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_ONCE);
    }

  }
  FLEXCANb_ESR1(_bus) |= FLEXCANb_ESR1(_bus);
  asm volatile ("dsb");
}

A few other thoughts:
- Is it possible to just set the imask once when configuring MB for TX, rather than checking it each time a TX interrupt is generated?
- Instead of having events() "generate transmit from queue on idle bus", is this something write() can do? The frame would be transmitted immediately and events() would only be needed in loop() for FIFO-RX. Or is this needed to be sure messages aren't abandon in the queue?
 
Last edited:
The interrupt needs to be enforced to be on even if the user hasn't implemented it, otherwise the transmits won't fire from the ISR, and it would break remote frame capability in the ISR since flexcan switches the MB to RX_EMPTY and we need to switch it back to TX_INACTIVE before a message of same ID drops in, otherwise it will become RX_FULL and remain an RX mailbox afterwards. Good catch on the DLC.

The generate transmit from idle bus is because if no nodes are talking on the bus the interrupt will not fire, and if that happens, ISR transmits will no longer send. So on a busy network, events() doesn't need to be pushing a transmit since the ISR would be receiving frames and processing transmits by priority. For 2 teensies on a quiet bus with transmit interrupts, events() must be used so that the dequeue from the ISR can be pushed onto the network. The imask is set to a variable and only checked once per fire, if the TX interrupt is not set, it will be set once after that. During debug the transmits were evenly being spread accross all mailboxes except priority sequential frames were only slotted to first MB (MB8 with FIFO enabled)

I guess I can have setMB() set the transmit mailbox to enable interrupt, but if no one runs it it won't be guarenteed. If we did the same to write() functions it would have to be checked there too then. Even for default structures we would have to set it there. I think that since we already have a imask variable in the ISR, we mightes well use it's bit when a TX mailbox is found to guarentee that it is set, or it will be set otherwise.

EDIT, Actually, instead of writing a mailbox via write(), i think i will have write do what events() does and just remove the line from events(), and not handle any mailboxes in write() itself. Write() would just ensure a transmission is in process, if not, fire the ISR to start distribution
 
Last edited:
Added your code correction, except rather than adding a line I moved:

Code:
      txBuffer.pop_front(); /* clear on write */

      [COLOR="#FF0000"]code = 0; /* MSADIE - clear code before configuring for new tx frame */[/COLOR] // deleted

      mbxAddr[0] = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_INACTIVE);
      mbxAddr[1] = (( msg.flags.extended ) ? ( msg.id & FLEXCAN_MB_ID_EXT_MASK ) : FLEXCAN_MB_ID_IDSTD(msg.id));
      if ( msg.flags.remote ) code |= (1UL << 20);
      if ( msg.flags.extended ) code |= (3UL << 21);
      for ( uint8_t i = 0; i < (8 >> 2); i++ ) mbxAddr[2 + i] = (msg.buf[0 + i * 4] << 24) | (msg.buf[1 + i * 4] << 16) | (msg.buf[2 + i * 4] << 8) | msg.buf[3 + i * 4];
      [COLOR="#FF0000"]code |= msg.len << 16;[/COLOR] // moved & changed
      mbxAddr[0] = code | FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_ONCE);

as:

Code:
      txBuffer.pop_front(); /* clear on write */

      mbxAddr[0] = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_INACTIVE);
      mbxAddr[1] = (( msg.flags.extended ) ? ( msg.id & FLEXCAN_MB_ID_EXT_MASK ) : FLEXCAN_MB_ID_IDSTD(msg.id));
      [COLOR="#FF0000"]code = msg.len << 16;[/COLOR] // set it as DLC before appending to it
      if ( msg.flags.remote ) code |= (1UL << 20);
      if ( msg.flags.extended ) code |= (3UL << 21);
      for ( uint8_t i = 0; i < (8 >> 2); i++ ) mbxAddr[2 + i] = (msg.buf[0 + i * 4] << 24) | (msg.buf[1 + i * 4] << 16) | (msg.buf[2 + i * 4] << 8) | msg.buf[3 + i * 4];
      mbxAddr[0] = code | FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_ONCE);

The issue with not including the offloading in events() is if there isn't frames in stream, write() now dequeues at least one frame, however, a remote frame on a single mailbox requires a timed period to transition. So sending a remote frame over a silent bus more than 2 times needs to be pushed again (The ISR), in order to offload the 2nd frame. Normal writes, seq, and specific mailbox works fine for immediate sends from write() functions but still need the events() if using remote frames on a silent bus. If the other nodes are scrolling traffic, this isn't an issue, and the ISR will be able to offload more transmits while the data is streaming, giving enough time for the remote frame MB to transition back and be reused again.

EDIT: Update:
Code:
[ATTACH]20173._xfImport[/ATTACH]

EDIT, new update includes direct callback firing if user never calls events(). The library will switch to events() queue system if events() is used, otherwise reception queues won't be issued, and they will be directly fired. Now FlexCAN_T4 can work even without events(). Enabling events puts all receptions in queue that you can read later, or if you prefer immediate processing or bridge support for critical processing, direct firing is now possible without using events().
Code:
[ATTACH]20174._xfImport[/ATTACH]
 
Last edited:
Just a disclaimer: I'm not very familiar with the particulars of remote request frames so some of my comments be seem misguided.

I applied update3 to my simplest gateway code, CAN2 (VCAN) receives frames with FIFO interrupt and the callback writes sequential to CAN1 (LCAN). Something with events() doesn't seem to be functioning as intended. Bus data is recorded HSCAN data I'm playing back with timestamp via Vector VN1610. I went through all permutations of including events() in loop and setting TX msg.seq.

The results reduced to:
For seq=0 message sending, if VCAN.events() is included in loop(), LCAN sends 3-4x number of messages as received on VCAN. Otherwise OK.
For seq=1 message sending, if LCAN.events() is NOT included in loop(), LCAN stops transmitting after a while (~200 frames with the recording im playing back). Otherwise OK.

I haven't had a chance to look into the cause of these anomalies yet. Maybe later tonight.

Simplified gateway code:
Code:
#include <FlexCAN_T4.h>

FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_64> LCAN;
FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> VCAN;

void setup() {
  // Setup VCAN (CAN2)
  VCAN.begin();
  VCAN.setClock(CLK_60MHz);
  VCAN.setBaudRate(500000);
  VCAN.enableFIFO();
  VCAN.enableFIFOInterrupt();
  VCAN.onReceive(canV2L);

  // Setup LCAN (CAN1)
  LCAN.begin();
  LCAN.setClock(CLK_60MHz);
  LCAN.setBaudRate(500000);
}

void loop() {
  VCAN.events();
  LCAN.events();
}

void canV2L(const CAN_message_t &msg) {
  CAN_message_t myMsg;
  myMsg = msg;
  myMsg.seq = 1;
  LCAN.write(myMsg);
}
 
lack of traffic you must use events(), it'll receive one and dequeue one or more transmits. without events() the transmits only happen whenever the ISR is firing, like from receptions. otherwise you need to include events() to push the ISR if theres not enough receptions to dequeue the transmits from ISR. The write() funtions only call the ISR once if there arn't any receptions in progress.

I just tested a stripped down version of your code with a dummy node sending 8 consistant frames per second:

Your code.

Code:
#include <FlexCAN_T4.h>

FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> VCAN;

void setup() {
  VCAN.begin();
  VCAN.setClock(CLK_60MHz);
  VCAN.setBaudRate(500000);
  VCAN.enableFIFO();
  VCAN.enableFIFOInterrupt();
  VCAN.onReceive(canV2L);
}

void loop() {
  VCAN.events();
}

void canV2L(const CAN_message_t &msg) {
  Serial.print("FRAME: 0x"); Serial.println(msg.id, HEX);
  CAN_message_t myMsg = msg;
  myMsg.seq = 1; // <-- toggle here
  VCAN.write(myMsg);
}

Dummy node code:

Code:
#include <FlexCAN_T4.h>
FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> Can0;

void setup(void) {
  Serial.begin(115200); delay(400);
  pinMode(6, OUTPUT); digitalWrite(6, LOW); /* optional tranceiver enable pin */
  Can0.begin();
  Can0.setBaudRate(500000);
  Can0.setMaxMB(16);
  Can0.enableFIFO();
  Can0.enableFIFOInterrupt();
  Can0.onReceive(canSniff);
  Can0.mailboxStatus();
//  Can0.enableMBInterrupts();
  //        Can0.FLEXCAN_EnterFreezeMode();
  //FLEXCANb_CTRL2(CAN3) = (FLEXCANb_CTRL2(CAN3) & 0xFF07FFFF) | 20UL << 19;
  //      Can0.FLEXCAN_ExitFreezeMode();

  pinMode(13, OUTPUT);
}

void canSniff(const CAN_message_t &msg) {
  Serial.print("MB "); Serial.print(msg.mb);
  Serial.print("  OVERRUN: "); Serial.print(msg.flags.overrun);
  Serial.print("  LEN: "); Serial.print(msg.len);
  Serial.print(" EXT: "); Serial.print(msg.flags.extended);
  Serial.print(" RTR: "); Serial.print(msg.flags.remote);
  Serial.print(" TS: "); Serial.print(msg.timestamp);
  Serial.print(" ID: "); Serial.print(msg.id, HEX);
  Serial.print(" Buffer: ");
  for ( uint8_t i = 0; i < msg.len; i++ ) {
    Serial.print(msg.buf[i], HEX); Serial.print(" ");
  } Serial.println();
  static uint32_t _time = millis();
  Serial.print("Time between frames: ");
  Serial.println(millis() - _time);
  _time = millis();
}


void loop() {
//  Can0.events();

  static uint32_t m = millis();
  if ( millis() - m > 50 ) {
    Serial.println(millis() - m);
    m = millis();
    digitalWrite(13, !digitalRead(13));
//    Serial.print("ESR1: "); Serial.print(FLEXCANb_ESR1(CAN3));
//    Serial.print(" - ECR: "); Serial.println(FLEXCANb_ECR(CAN3) & 0xFF);
  }

  static uint32_t timeout = micros();
  if ( micros() - timeout > 1000000 ) {
    //if ( 1 == 1 ) {
    CAN_message_t msg;
    static uint32_t canid = 0x0;
    canid++;
    if ( canid > 0x7FE ) canid = 0x1;
    msg.id = canid;
    static int16_t val = -1;
    val++;
    if ( val > 7 ) val = 0;
    for ( uint8_t i = 0; i < 8; i++ ) msg.buf[i] = val;
    for ( uint8_t i = 0; i < 8; i++ ) {
      Can0.write(msg);
    }
    //  uint32_t to = 0;
    //  while (!Can0.write(msg) && (++to < 10) );

    timeout = micros();
  }

}


I do see what is happening when toggling the seq bit, but again, this is expected, as since the first mailbox is only transmitting sequential frames, other ISR calls are needed to offload the next queue when the frames are sent. events() constantly pushes the ISR to process if the network activity drops and queues exist. Unless I find a better way around that, you need to use events for the bridge support. I'll have to see how to work around that
 
Last edited:
EDIT, update, can you try this? Seems to work now for sequential and non sequential with no events() in loop(). As for remote frames they seem to be causing their own problem in sequential, so those ones i force to any available mailbox, no one i think will constantly request anyways in sequential, it's a soon to be extinct feature of CAN :p

Code:
[ATTACH]20185._xfImport[/ATTACH]

I was pumping 25 frames from another node in a for loop every 1 second, TX buffer i set to 64 at both nodes. the receiver (your code) correctly rebroadcasted the data back to the other node, and i was counting the frames on the screen every so many refreshes, all intact
 
Not sure what's going on but it only forwards the first message after reboot, regardless of .seq or events().

EDIT: I added blink LED code to the main loop and it seems loop execution is stopping once first message is received/transmitted.
EDIT2: I added a Serial.print in flexcan_interrupt(). Execution is getting stuck there, looping indefinitely.
 
Last edited:
Back
Top