FlexCAN_T4 - FlexCAN for Teensy 4

@tonton81, first of all , thanks for providing this amazing library. What I was trying to do it to send a CAN2.0 message using CAN1 on Teensy 4.0 and receive it on the same Teensy 4.0 using CAN2. The CAN transceiver I used was MCP2551. Baud rate is 1MHz. Rs pin was connected to GND and two 120 ohm resistors were connected to CANH and CANL. When I write() the CAN package and read() the CAN bus immediately after the write(), I can get the package without any problem. However, if I attached the canStiff() as an interrupt, it seemed that that interrupt was never triggered. Could you please help with this problem? Attached are my code and a picture showing my connection.

Code:
#include "Arduino.h"
#include "FlexCAN_T4.h"

FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> can1;
FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> can2;

void canSniff(const CAN_message_t &msg);

void setup() {
    Serial.begin(9600);
    can1.begin();
    can2.begin();
    can1.setBaudRate(1000000);
    can2.setBaudRate(1000000);
    can1.enableFIFO();
    can1.enableMBInterrupt(FIFO);
    can1.onReceive(FIFO, canSniff);
    can2.enableFIFO();
    can2.enableMBInterrupt(FIFO);
    can2.onReceive(FIFO, canSniff);
    delay(1000);
    Serial.println("CAN setup finished");
}

void loop() {
    can1.events();
    can2.events();
    if (Serial.available()) {
        char c = Serial.read();
        if (c == 'a') {
            Serial.println("Sending ...");
            CAN_message_t msg;
            msg.len = 8;
            msg.id = 0x321;
            msg.buf[0] = 1;
            msg.buf[1] = 2;
            msg.buf[2] = 3;
            msg.buf[3] = 4;
            msg.buf[4] = 5;
            msg.buf[5] = 6;
            msg.buf[6] = 7;
            msg.buf[7] = 8;

            can1.write(msg);

//            delay(100);
//            can2.read(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();
        }
    }
    delay(100);
}

void canSniff(const CAN_message_t &msg) {
    Serial.println("Interrupted");
    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();
}

20191122_144838-min.jpg
 
Hi tonton81,
there is an unexpected behaviour if I want to send a message with the CanFD Object with a edl value of 0.
Even if i set the bit to 0 in the CANFD_message_t on the sender side the bit is always 1 on the receiver side.

I traced that behaviour down to the function FCTPFD_OPT::writeTxMailbox in File FlexCAN_T4FD.tpp line 257.

Adding an extra if statement resolves that issue:
Code:
FCTPFD_FUNC void FCTPFD_OPT::writeTxMailbox(uint8_t mb_num, const CANFD_message_t &frame) {
  CANFD_message_t msg = frame;
  writeIFLAGBit(mb_num);
  uint8_t mbsize = 0;
  uint32_t code = 0;
  volatile uint32_t *mbxAddr = &(*(volatile uint32_t*)(mailbox_offset(mb_num, mbsize)));
  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.extended ) code |= (3UL << 21);
  for ( uint8_t i = 0; i < (mbsize >> 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];
  if ( msg.len > mbsize ) msg.len = mbsize;
  code |= len_to_dlc(msg.len) << 16;
  if ( msg.brs ) code |= (1UL << 30); // BRS
  /* Editded: */if ( msg.edl ) code |= (1UL << 31); // EDL
  mbxAddr[0] = code | FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_ONCE);
}

Cheers,
Daniel Stümke
 
Does FD work reliably on the T4 with this library?
I'm considering doing a custom PCB for work using a T4 and a MCP2542FD transceiver chip.

I have no idea how to route out CAN3 from the pads below the T4 yet though.
It's a shame they chose to hide the single most interesting feature of the T4 on the bottom and only comfortably route out the lame CAN2.0 channels.
 
Define reliably? TeensyCAN is using the FD library to distribute 640 byte data with 64byte frames in about 1.5ms constistantly for days. If there is any issues in the FD library I usually respond to fix it within the same day, as dastit03 pointed out, a minor bug for transmitting CAN2 frames on a FD bus was updated few hours after the post :)

I don't only look at the reliability, but the multiple features not available on any other library for flexcan. One example is, aside from the SDK not supporting both memory regions, and using an exponential search algorythm of divisions to find dynamically placed mailboxes in memory block 0 only, FlexCAN_T4 allows both memory accesses with direct mailbox access without any use of divisions. Features like automatic filtering and distributions you will not find on any libraries (except IFCT), and up to 3 raw data outputs for 3rd party libraries (TeensyCAN uses one). That and the advanced bitrate selection in CANFD mode allows even the novice users to pick the exact timings parameters based on a list printout to fine-tune for an error-free bus based on sampling points of different busses. The TDC values for tranceiver are fully automated based on the timings results and done without user intervention/requests. It has also, on CAN2.0 side, gave backwards compatibility for Teensy 3.x, and in a way, an upgraded more performance, replacement for IFCT.

Useful additions are always considered and implemented, and bugs, even though stable and without at the moment, are fixed promptly.
I agree the pads are not very user friendly but you have to understand that the focus was based on form factor and backwards compatibility as much as possible, with teensy 3.2 boards. It may change in the future T4.x but the form factor will still be maybe like a T3.5/6 but as I said backwards compatibility is always a factor in the design decision, and not all pins offer a MUX to the peripheral we want, so certain placements can hinder functionalities accross backwards compatibility.
 
Hello,
I need to set the speed of CANBus at an unusual speed of 307.2kbps.
How can I make this possible? I didn't try (because I don't have the Teensy 4 yet), but reading the library source code seems to refuse such speed.
Can someone help me?
Thanks
 
CAN2.0 or FD mode?
If CAN2.0 you can use 307200 for the speed

I've to use CAN 2.0.
Have I use a specific clock? How can the 307200 speed pass the bestError limits (492 bestError achieved is more than 300 admitted) on setBaudRate function with a clock of 24MHz (the default)?
There is also an error in the code at line 455: the error value can be negative, there is no check like on line 465.
 
The clock can be changed using setClock, default for Teensy 3.x is 16MHz, you can try up to 60MHz on T4

If there is an error in the setBaudRate function it's been there since the original teachop/pawelsky's FlexCAN_Library forks, till here, but on errors the function usually exits. I havn't rewritten it from scratch but I did combine it to be more clean and centralized in the function itself. Try a different flexcan clock and see if your error rates are reduced, because your baudrate isn't common it may need specific clock
 
Last edited:
The clock can be changed using setClock, default for Teensy 3.x is 16MHz, you can try up to 60MHz on T4

If there is an error in the setBaudRate function it's been there since the original teachop/pawelsky's FlexCAN_Library forks, till here, but on errors the function usually exits. I havn't rewritten it from scratch but I did combine it to be more clean and centralized in the function itself. Try a different flexcan clock and see if your error rates are reduced, because your baudrate isn't common it may need specific clock

I tried with all 3 possible clock sources and all 64 dividers, I can't reach the speed with an error below 300.
The unusual speed I think is related to the usage of power of 2 oscillators, like 3.072 MHz, 30.72 MHz or 307.2 MHz.
 
Hi guys,
I'm trying to make my teensy 4 works with SN65HVD230 CAN bus transceiver module with my car ODB2 port.
sku_385988_1.jpg

Wiring:
--------
Teensy---------SN65HVD230 module
3.3V------------3V3
GND------------GND
CRX1 (23)-----CRX
CTX1 (24)-----CTX
-----------------CANH to my OBD2 car port (pin 6)
-----------------CANL to my OBD2 car port (pin 14)

OBD2-Connector-DLC-Data-Link-16-Pin-Out-J1962-Explained-What-Is_v2.png

I have created a simple sketch, copied from tonton81 (thanks for your work) and modified for my use
Code:
#include <FlexCAN_T4.h>
FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can0;

CAN_message_t msg, rxmsg;

void setup(void) {
  
  Serial.begin(115200); 
  delay(400);
  Can0.begin();
  Can0.setBaudRate(500000);

}

void loop() {
  static uint32_t timeout = millis();
  if ( millis() - timeout > 1000 ) 
        { // send random frame every 1000ms
          CAN_message_t msg;
          msg.id = 0x7DF;
          msg.buf[0] = 0x02;  
          msg.buf[1] = 0x01;
          msg.buf[2] = 0x05;  // for this example: Engine coolant temp
          msg.buf[3] = 0;
          msg.buf[4] = 0;  
          msg.buf[5] = 0;
          msg.buf[6] = 0;  
          msg.buf[7] = 0;
          msg.len = 8;
          msg.flags.extended = 0; 
          msg.flags.remote = 0;
          Can0.write(msg);
          timeout = millis();

          Serial.print("SEND: ");
          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(); 
        }

  if (Can0.read(rxmsg))
        {
              if (rxmsg.id == 0x7E8)
                  {
                        Serial.print("RECEIVE: ");
                        Serial.print(" ID: "); Serial.print(rxmsg.id, HEX);
                        Serial.print(" Buffer: ");
                        for ( uint8_t i = 0; i < rxmsg.len; i++ ) {
                          Serial.print(rxmsg.buf[i], HEX); Serial.print(" ");
                        } Serial.println();    
                  }        
        }

}

OK...I cannot receive nothing :(

if I remove this line:
Code:
 if (rxmsg.id == 0x7E8)
i can read all messages:
Schermata 2019-12-12 alle 19.31.19.png

What went wrong? :confused:
 
You didn't do anything wrong, perhaps your car is using extended frames and not standard, this can be normal. The OBD responses on 2006+ cars are mostly extended id responses.

If you are writing to that ID it may not respond you have to use the extended one. A good trick is to use a simple OBD reader while logging in teensy, when it starts talking, you will see the ID you should use
 
Hi tonton81,
thanks for your answer.

The car is 2017 with extended frames, but it's compatible with standard frames and requests, because
i have an Arduino Nano connected in SPI with a MCP2515/TJA1050 like this:
mcp2515-can-bus-module-tja1050-receivers-spi-protocol.jpg
and with a normal CANbus library i can read and write all I want with standard or extended frames.

it's strange. Can be a problem of SN65HVD230 CAN bus transceiver module ?
 
The hardware will not pass in corrupted frames, they are CRC validated, before it hits the software end. If you are curious try reading the data while a OBD tester tool is plugged into the OBD port, to see what is really sent, the PID requests should look familiar
 
SOLVED!

Must be use the Extended Request IDs instead of Standard IDs :eek:

Code:
So a typical 11-bit exchange would appear as follows:
   Rx: 7DF 01 00
   Tx: 7E8 41 00 BE 1B 30 13
   Tx: 7E9 41 00 88 18 00 10
   Tx: 7EA 41 00 00 08 00 10

Same exchange on 29-bit CAN:
   Rx: 18DB33F1 01 00
   Tx: 18DAF110 41 00 BE 1B 30 13
   Tx: 18DAF118 41 00 88 18 00 10
   Tx: 18DAF128 41 00 00 08 00 10
 
FlexCAN_T4 timestamps

Hi,
Thanks for the library ! I have now used it with my new Teensy 3.6 and Teensy 4 boards. (Interesting that I can find Teensy boards in my local electronics shop here in Sweden..)
So far used it with CAN 2.0 only. I found some CAN test code and have then merged with my nmea2000 code. Nmea2000 uses CAN 2.0 and 250 kbps.
No problems what so ever. !!
One Teensy 3.6 emulating a Volvo Penta marine diesel transmitting RPM, Oil Pressure…. One Teensy 4.0 receiving CAN and decoding nmea2000. Runs in a CAN network together with a Garmin GPS Plotter and a B&G sailing instrument.
I have one question regarding timestamp. What is it showing and how precise is it ? Could it be used to measure CAN load ?
/Christer
 
The counter is not needed you just need to time each frame, local timer, and how many, based on their data lengths:

Check the last post on the NXP forum:
Link
 
My oscilloscope with Serial decode also for CANBUS shows CAN load. Also a good indication visually.
I am not in control of transmitting devices. It could be Garmin and other transmitting. It could be 11 or 29 bit, it could be 1 or 8 Data fields. So assuming I have an Teensy with FlexCan_T4 and a Can transceiver an din the "void loop" :
// ******* RX part *************
CAN_message_t msg;
if (myCan.read(msg) ) canSniff(msg); // polling
Here I could insert a local Arduino code for Frame-start-time . But how do I get Frame-end-time ?
 
You dont, it already ended when it hit the callback. If you can calculate the bytes by length and the stuffing bits need to be considered, ill have to read up on the details but would be a nice addition to the canbus decoder im working on, for the time between frames you can use the timestamp but be aware it is a 16bit rollover counter so you should be able to capture time between frames with that
 
I tested with Arduino UNO with CANBus shield. Inserted digitalWrite As below, and checked on an oscilloscope. 4 CAN messages transmitted. 3msec a new TX.

CAN0.readMsgBuf(&canId,&ext,&len, buf); // Read data: len = data length, buf = data byte(s)
digitalWrite(7, HIGH); // for testing testpoint 1. Ref time= 0 msec
...very short code with a few lines with Serial.print
digitalWrite(7, HIGH);

Serial.print slows down the void lopp very much! On UNO it takes about 4 msec for the loop.
The Adafruit M0 is a bit faster with Serial.print but the ESP32 i worse than M0. The Teensy 3.6 has no problems at all here. scope_1.jpg
 
Hello,

Tying to figure out how to activate and callback received data from a mailbox.
My assumption is that mailboxes can store data that you desire to receive while you are doing something else in your loop. Buffered in the mailbox until you read it and then deleted automatically. That's what I'm trying to achieve at least, hopefully someone can help me.
I've tried myself but without any luck.

Attached is my code, I'm using the filtering to limit the amount of data from the can bus.
Code works well and I can see the data from the can bus.



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

CAN_message_t msg, rxmsg;

void setup(void) {
  
  Serial.begin(115200); 
  delay(500);
  Can0.begin();
  Can0.setBaudRate(100000);

  Can0.setMBFilter(REJECT_ALL);
  Can0.setMBFilter(MB0, 0x800, 0x801, 0x803, 0x804, 0x805);

}

void loop() {


  if (Can0.read(rxmsg))
        {
                        Serial.print("RECEIVE: ");
                        Serial.print(" ID: "); Serial.print(rxmsg.id, HEX);
                        Serial.print(" Buffer: ");
                        for ( uint8_t i = 0; i < rxmsg.len; i++ ) {
                          Serial.print(rxmsg.buf[i], HEX); Serial.print(" ");
                        } Serial.println();           
        }

}
 
Can0.enableMBInterrupts(); is needed to enable all mailbox interrupt receptions (unless you rather poll as in your example)

For the callback, you can call Can0.onReceive(canSniff); and add the function:

Code:
void canSniff(const CAN_message_t &msg) { // global callback
Serial.print("T4: "); 
Serial.print("MB ");
 Serial.print(msg.mb); 
Serial.print(" OVERRUN: "); 
Serial.print(msg.flags.overrun); 
Serial.print(" BUS "); 
Serial.print(msg.bus); 
Serial.print(" LEN: "); 
Serial.print(msg.len); 
Serial.print(" EXT: "); 
Serial.print(msg.flags.extended); 
Serial.print(" REMOTE: "); 
Serial.print(msg.flags.remote); 
Serial.print(" TS: "); 
Serial.print(msg.timestamp); 
Serial.print(" ID: "); 
Serial.print(msg.id, HEX); 
Serial.print(" IDHIT: "); 
Serial.print(msg.idhit); 
Serial.print(" Buffer: "); 
for ( uint8_t i = 0; i < msg.len; i++ ) { 
Serial.print(msg.buf[i], HEX); 
Serial.print(" "); 
} 
Serial.println();
}

Can0.events() must also be in loop() to dequeue interrupt mailboxes
 
Last edited:
On a side note you can also use Can0.setMBFilterRange(MB0, 0x800, 0x805); since the IDs are within a tight range

You may also have a callback per mailbox, like have 0x800 goto one callback, 0x801 goto MB1 callback, etc. A single ID would be needed to be filtered on each mailbox, per your example. The onReceive I posted is for global (all mailboxes receive to same callback), but also a mailbox (MB0) could be specified if you wish to receive specific frames in a separate callback.
 
Back
Top