FlexCAN_T4 - FlexCAN for Teensy 4

I see, thank you very very much for the prompt reply. Is there anything wrong with my code though? Meaning, if I were to use a proper can transceiver module and connect its pins to its correct correspondents, this code should work?
 
Last edited:
yes with the transceivers it will work as it's intended.

9600 isn't a standard CAN bitrate though. Try 125000 on low end, recommended 500000
 
@tony got another quick one for you.
As mentioned before I have three mailboxes set up, each with it's own callback:
Code:
       Can0.setMBFilter(REJECT_ALL);
       Can0.setMBFilter(MB1, id1); // 29 bit id
       Can0.setMBFilter(MB2, id2); // 11 bit id
       Can0.setMBFilter(MB3, id3); // 11 bit id
       Can0.enableMBInterrupt(MB1);
       Can0.enableMBInterrupt(MB2);
       Can0.enableMBInterrupt(MB3);
       Can0.onReceive(MB1, callback1);
       Can0.onReceive(MB2, callback2);
       Can0.onReceive(MB3, callback3);

callback2 and 3 are triggering, but 1 is not.
I have T4 sitting on the CAN bus listening to the traffic between the nodes and I can see the 29 bit messages being sent by one of the nodes but it's not triggering callback1

Do I need to add in order to get it to fire the callback?
Code:
Can0.setMB(MB1, RX, EXT);
 
must be setMB configured before doing any filterwork, by default with fifo disabled the 4 first ones are STD while next 4 are EXT then 8 TX.
 
solder joints look weak, did you adjust your code?
the fifo example with interrupts is a good start as that is guarenteed to work, provided you choose the right bus in the constructor and baudrate in setup, if that still fails you have connection issue
 
Thank you all for the advice. I've resoldered all the headers with adequate amounts of solder, loaded the FIFO w/ Interrupts example onto the Teesny 4.0, and I've soldered on wires to the headers as well to the transistor (https://github.com/martovens/Testing_FlexCAN_T4/tree/HookedToECU_w_MoreSolderAndStuff). I used an oscilloscope to verify that the CAN TX is working (https://github.com/martovens/Testing_FlexCAN_T4/blob/HookedToECU_w_MoreSolderAndStuff/oscil_CTX.jpg) and I've also verified that the CAN HIGH and LOW lines are behaving as they should an are actually transmitting messages. However, I viewed the CAN RX line on the oscilloscope and got this: https://github.com/martovens/Testing_FlexCAN_T4/blob/HookedToECU_w_MoreSolderAndStuff/oscil_CRX.jpg . Those humps were uniform and would glide across the screen consistently, so I'm pretty sure those are CAN messages, but I'm not sure why the signal is so weak. Does anyone have any ideas? Maybe it's a bad transistor (though this is the second one I've been through to see if it was a bad one).
 
Hi tonton81, thanks for creating this wonderful library, I've been experimenting a lot with it and been having great fun! I ran into an issue however where 2 Teensies 4.1 are connected over CAN bus (one sends, the other receives).
The code isn't that pretty, contains some legacy stuff. The sender burst-sends 5000 CAN messages with a sequence number and the msg.seq=1 property, the receiver uses can.read(..) to poll messages and then checks if the order is maintained.

What I see happening is that the receiver only receives/processes every 15th messages or so. Below I plotted the buffer contents of every received message (blue) and the inter-message sequence number difference (orange). I hope that makes sense.
Screenshot 2022-04-05 131624.png

Do you have any suggestions as to why this might happen and how it can be mitigated? I can see that there is no issue anymore with the last few incoming messages. Perhaps a queue is overflowing?


In the end, the aim is to have 3 busses connected to the receiver each passing through high-bandwidth messages. As such, I'm hesitant to use fully interrupt-based callbacks because of dangers of deep interrupt nesting if callbacks get interrupted.
I also added delay(1) to the sender's for loop which solved it but that's a too large delay in the system that it needs to end up in.

Sender code
Code:
#include <FlexCAN_T4.h>
#define DRIVER1_ADDRESS 5

/* -------------------------------------------------------------------------- */
/*     Burst send many CAN messages to master. Add seq. number to messages    */
/* -------------------------------------------------------------------------- */

FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> can;

CAN_message_t msg;

const int measuringTimeMillis = 1e3; 
elapsedMillis elapsedTime; 
unsigned long sentInTotal = 0;
unsigned long sentInBatch = 0;

uint16_t bufContents = 0;

void setup() {
  can.begin();
  can.setBaudRate(1e6);
  Serial.begin(9600);

  Serial.println("Started. Waiting for empty Serial input");
}

void loop() {
  for ( int i = 0; i < 5000; i++) {
    sendMessage();
  }

  delay(5000);
  
}

void sendMessage() {
  uint16_t index = 0x6040;
  uint32_t data = 0x80;
  msg.id = DRIVER1_ADDRESS;
  msg.len = 8;
  msg.seq = 1;
  msg.buf[0] = (bufContents >> 8) & 0xFF;  // Index bit 1
  msg.buf[1] = (bufContents >> 0) & 0xFF;  // Index bit 2
//  Serial.print((bufContents >> 8) & 0xFF, BIN), Serial.println((bufContents >> 0) & 0xFF, BIN), 
  msg.buf[2] = 1;
  msg.buf[3] = 4;
  msg.buf[4] = 1;
  msg.buf[5] = 5;
  msg.buf[6] = 2;
  msg.buf[7] = 6;
  can.write(msg);
  Serial.println(bufContents);
  can.mailboxStatus();
  bufContents++;
}

Receiver
Code:
#include <FlexCAN_T4.h>

FlexCAN_T4<CAN1, RX_SIZE_1024, TX_SIZE_1024> can;
CAN_message_t a_msg;
unsigned long counterGood = 0;
unsigned long counterBad = 0;

// Implemented to check integrity;
int currentIndex = 0;
unsigned int count = 0;

void setup(void) {
  can.begin();
  can.setBaudRate(1e6);

  Serial.begin(9600);
  can.mailboxStatus();
  Serial.println("Starting receiver");
}

elapsedMicros interarrival = 0;

void loop() {
  elapsedMicros waiting = 0;
  
  if(can.read(a_msg)) {
    Serial.print("interarrival : "); Serial.println(interarrival);
    interarrival = 0;

    uint16_t num = (a_msg.buf[0] << 8) + (a_msg.buf[1]);
    Serial.print(currentIndex); Serial.print(" : receivedNum="); Serial.println(num);
    if(num == (currentIndex+1)) {
      counterGood++;
    } else {
      counterBad++;
    }
    count++;
    currentIndex = num;
    Serial.print("processingTime : "); Serial.println(waiting);
  }
  
  // Print statistics
  if(Serial.available()) {    // Only print if we send something (just send nothing (empty))
    Serial.read();            // Consume character
    Serial.print("CounterGood: "); Serial.println(counterGood);
    Serial.print("CounterBad: "); Serial.println(counterBad);
    Serial.print("Counter: "); Serial.println(count);
  }
}
 
the sender only has a transmit queue of 16, you'll need to increase that if you plan to burst

i went to check the write function unfortunately it doesn't check for overflows, as they'll return -1 if not successful in finding an available mailbox and go straight to queue, i'll try to fix that now so you can test something
 
Last edited:
Uploaded patch for return value 0 if queue was full.
Tested it as well:

Code:
    for ( int i = 0; i < 30; i++) {
      CAN_message_t msg;
      msg.id = 0x555;
      msg.seq = 1;
      msg.buf[0] = 8;
      msg.buf[1] = 8;
      msg.buf[2] = 8;
      msg.buf[3] = 8;
      msg.buf[4] = 8;
      msg.buf[5] = 8;
      msg.buf[6] = 8;
      msg.buf[7] = 8;
      if ( Can0.write(msg) ) {
        Serial.println("QUEUED!");
      }
      else {
        Serial.println("QUEUE LIMIT REACHED");
        int success = 0;
        while ( !success ) {
          success = Can0.write(msg);
        }
        Serial.println("QUEUE SUCCEEDED!");
      }
    }
    delay(5000);
  }
}


Code:
QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

QUEUED!

QUEUE LIMIT REACHED

QUEUE SUCCEEDED!

also consider the while loop is just for testing, if the bus is dead you'll get stuck there obviously because the queue will just return full all the time

EDIT: I added protection that will break the while loop, if the bus-off state is detected (CAN not connected or no nodes or shorted bus), -2 will be reported. So above code will not lock anymore if that occurs.
 
Last edited:
That's great, that gives a lot more insight into what's going on internally! And thanks for the lightning fast help!
What I don't understand however is why such a bottleneck occured in the first place. What is your view on this? My guess is that the write function doesn't wait until the mailbox is emptied and given that the Teensy's loop is faster than the Receiver node's transceiver, the message doesn't get a chance of being read before being replaced by a new one. From the IMXRT1060 user reference I read the 'Abort feature' (MCR[AEN] bit) might enable this however from reading the 'write' function implementation it appears that this bit is already set (line 166) so it should do that?

Ideally I'd like to reduce the bottleneck further. Could you suggest some other measures for streamlining the process? I'll make sure to increase the TX_SIZE on the sender and I read that increasing the clock speed with setClock(..) at the receiving end might be a good start too (not sure if that hurts stability though).
 
Last edited:
unfortunately you're limited by the bus speed, abort cancels a transmission and may or may not guarentee the frame be sent out or not still, plus that would lead to more latency and missing frame to send. the transmits have a priority to transmit in ISR context for back to back transmissions as fast as possible, remember sequencing frames only uses one mailbox, so that frame has to be successfully transmitted on the bus before the mailbox can be used to write the next frame.
 
Hello togehter!

Currently I want to use the Teensy 4.0 as CAN-FD-Reader on my car and therefore found your awesome library and thread!

So I bought this board: https://www.skpang.co.uk/products/teensy-4-0-can-fd-to-usb-converter

Sadly it seems that I am not able to receive nor send any messages! Could you help me here?

My current testing setup looks as follows:
1649404184515.jpg

The code that is running tries to send and receive messages:

Code:
#include <FlexCAN_T4.h>

FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_16> can1;

void setup(void) {
  can1.begin();
  Serial.begin(9600);
}

void loop() {

  CANFD_message_t msg;
  CANFD_message_t rx_msg;
  
  // Write Data
    msg.id = 0x11;
    msg.len = 16;
    msg.flags.extended = 0;
    msg.flags.overrun = 0;
    msg.flags.reserved = 0;
    msg.buf[0] = 0;
    msg.buf[1] = 0;             
    msg.buf[2] = 0;             
    msg.buf[3] = 0;    
    msg.buf[4] = 0;
    msg.buf[5] = 0;
    msg.buf[6] = 0;
    msg.buf[7] = 0;
    msg.buf[8] = 0;
    msg.buf[9] = 0;
    msg.buf[10] = 0;
    msg.buf[11] = 0;
    msg.buf[12] = 0;
    msg.buf[13] = 0;
    msg.buf[14] = 0;
    msg.buf[15] = 0;

    can1.write(msg);

    if (can1.read(rx_msg)) {
    Serial.print("CAN1 "); 
    Serial.print("MB: "); Serial.print(rx_msg.mb);
    Serial.print("  ID: 0x"); Serial.print(rx_msg.id, HEX );
    Serial.print("  EXT: "); Serial.print(rx_msg.flags.extended );
    Serial.print("  LEN: "); Serial.print(rx_msg.len);
    Serial.print(" DATA: ");
    for ( uint8_t i = 0; i < 16; i++ ) {
      Serial.print(rx_msg.buf[i]); Serial.print(" ");
    }
    Serial.print("  TS: "); Serial.println(rx_msg.timestamp);
    }
}

For me it seems, that the boards dont send data at all.

Looking forward to your help! Thank you!
 
If you are using two boards like that shown in the photo then you need the 120R terminator. Solder a 2way header pin to JP1 and insert a jumper to enable the terminator.
Later if you are going to connect to your car then you don't need the terminator. Just remove the jumper.

Cabling. You can not use a ribbon cable like that to connect the two boards. It looks like a one to one connection.

For quick test with the 2 boards use wires to link them up. On J2 connect H from one board to H on the other and repeat for L.

If you are going to connect to your car using a OBDII cable on the DB9 connector then you need to solder SJ1,2,3 like this:
IMG_0002-5_650x650_jpg.png
 
Thank you for your quick answer!

If I got that right I just needed to add the 120R Resistors. Here i used ones i had on hand and not the one from the board. Also I soldered together the J3 J2 J1 because of this manual: https://cdn.shopify.com/s/files/1/1560/1473/files/PICAN3.pdf?v=1620414574 because I definitly want to use the RS232 plug at the front!

But could you further explain why it is not able to use the cable I am using?

I am very sorry If my problems seems to be very trivial, but I am very new to the topic and generally more involved with the software side of things :)

This is the setup now:

1649409368079.jpg

Sadly it is still not working! The target is to attach the Teensy directly to the ECU later on via RS232 not OBD.

Thank you!
-Alex
 
Your code is not right. You have not setup the baud rate and the SILENT pin.

Try this code:
Code:
#include <FlexCAN_T4.h>

FlexCAN_T4<CAN3, RX_SIZE_256, TX_SIZE_16> can3;
IntervalTimer timer;
int SILENT = 9;

uint8_t a=0;
void setup(void) {

  pinMode(SILENT,OUTPUT); 

  digitalWrite(SILENT,LOW);
  
  can3.begin();
  can3.setBaudRate(500000);   // Data rate 500kbps
  can3.setMaxMB(16);
  can3.enableFIFO();
  can3.enableFIFOInterrupt();
  can3.onReceive(canSniff);
  can3.mailboxStatus();
  
  timer.begin(sendframe, 100000); // Send frame every 500ms
}


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

void sendframe()
{

  CAN_message_t msg;
  
  msg.id = 0x7df;

  msg.buf[0] = a++;     // Pot 1 value
  msg.buf[1] = 0;
  msg.buf[2] = 0;
  msg.buf[3] = 0;
  msg.buf[4] = 0;  // SW1 value
  msg.buf[5] = 0;
  msg.buf[6] = 0;
  msg.buf[7] = 0;

  msg.len = 8;
  msg.seq = 1;
  can3.write(MB15, msg);
 
}
void loop() {

  can3.events();
  

}
 
Thanks for your response!

The example you gave me is for CAN as far as I know, but I want to use CAN-FD. So is this correct?

Code:
#include <FlexCAN_T4.h>

FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_16> can3;
IntervalTimer timer;
int SILENT = 9;

uint8_t a=0;
void setup(void) {

  pinMode(SILENT,OUTPUT); 

  digitalWrite(SILENT,LOW);
  
  can3.begin();

  CANFD_timings_t config;
  config.clock = CLK_24MHz;
  config.baudrate = 500000;
  config.baudrateFD = 500000;
  config.propdelay = 190;
  config.bus_length = 1;
  config.sample = 70;

  can3.setBaudRate(config);
  can3.setMaxMB(16);
  can3.enableFIFO();
  can3.enableFIFOInterrupt();
  can3.onReceive(canSniff);
  can3.mailboxStatus();

  
  timer.begin(sendframe, 100000); // Send frame every 500ms
}


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

void sendframe()
{

  CANFD_message_t msg;
  
  msg.id = 0x7df;

  msg.buf[0] = a++;     // Pot 1 value
  msg.buf[1] = 0;
  msg.buf[2] = 0;
  msg.buf[3] = 0;
  msg.buf[4] = 0;  // SW1 value
  msg.buf[5] = 0;
  msg.buf[6] = 0;
  msg.buf[7] = 0;

  msg.len = 8;
  msg.seq = 1;
  can3.write(MB15, msg);
 
}
void loop() {

  can3.events();
  

}


Also could you explain why the SILENT Pin is necessary and what it does?
 
Best to get Classic CAN going first to prove the cabling is correct, then move to CAN FD.

The SILENT pin is to control the CAN transceiver. That is to disable the silent mode.
 
There are some CAN FD examples in the library.

You can also try this:

Code:
#include <FlexCAN_T4.h>

FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_16> FD;
IntervalTimer timer;
int SILENT = 9;

uint8_t a=0;
void setup(void) {

  pinMode(SILENT,OUTPUT); 

  digitalWrite(SILENT,LOW);
  
  FD.begin();
  CANFD_timings_t config;
  config.clock = CLK_24MHz;
  config.baudrate =   500000;     // 500kbps Nominal data rate
  config.baudrateFD = 2000000;    // 2000kpbs Data rate
  config.propdelay = 190;
  config.bus_length = 1;
  config.sample = 75;
  FD.setRegions(64);
  FD.setBaudRate(config);


  FD.setMBFilter(ACCEPT_ALL);
  FD.setMBFilter(MB13, 0x1);
  FD.setMBFilter(MB12, 0x1, 0x3);
  FD.setMBFilterRange(MB8, 0x1, 0x04);
  FD.enableMBInterrupt(MB8);
  FD.enableMBInterrupt(MB12);
  FD.enableMBInterrupt(MB13);
  FD.enhanceFilter(MB8);
  FD.enhanceFilter(MB10);
  FD.distribute();
  FD.mailboxStatus();

  timer.begin(sendframe, 100000); // Send frame every 500ms
}


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

void sendframe()
{

  CANFD_message_t msg;
  msg.len = 64;           // Set frame length to 64 bytes
  msg.id = 0x321;
  msg.seq = 1;
  msg.buf[0] = a++;       // Pot 1 value
  msg.buf[1] = 0;
  msg.buf[2] = 0;
  msg.buf[3] = 04;
  msg.buf[4] = 0;  // SW1 value
  msg.buf[5] = 0;
  msg.buf[6] = 0;
  msg.buf[7] = 0;
  FD.write( msg);
 
}
void loop() {
  CANFD_message_t msg;
  FD.events();
  if (FD.readMB(msg))
  {
      Serial.print("MB: "); Serial.print(msg.mb);
      Serial.print("  OVERRUN: "); Serial.print(msg.flags.overrun);
      Serial.print("  ID: 0x"); Serial.print(msg.id, HEX );
      Serial.print("  EXT: "); Serial.print(msg.flags.extended );
      Serial.print("  LEN: "); Serial.print(msg.len);
      Serial.print("  BRS: "); Serial.print(msg.brs);
      Serial.print(" DATA: ");
      for ( uint8_t i = 0; i <msg.len ; i++ ) {
        Serial.print(msg.buf[i]); Serial.print(" ");
      }
      Serial.print("  TS: "); Serial.println(msg.timestamp);
    
  }

}
 
Thank you for your answer! I actually managed to get the two teensy boards to communicate via CAN-FD!

Now I wanted to monitor the communication via CANalyzer but sadly I get this error:
Unbenannt.png

The code I am using right now ist just the example of your previous answer.

Thank you very much for your helpful support! :eek:
 
have you tried other baudrates? tried changing the clock to 60?

collink had same if not similar issue with 500000/2000000 specifically, while 1000000/2000000 was fine
 
Back
Top