FlexCAN_T4 - FlexCAN for Teensy 4

Do you have an oscilloscope or logic analyzer that you can use to verify signals? Viewing CANH/CANL and TX/RX signals would make quick work of troubleshooting.
 
check mailboxStatus(), if all of your TX mailboxes fill up, you have a bad physical connection, miswire, termination issue, worse case scenario, bad transceiver
 
Does anyone have a working recipe for interfacing a Teensy 4.0 with a Megasquirt?

I'm having issues with a SN65HVD230 and my MS3. I'm not getting anything back.
 
Do you have an oscilloscope or logic analyzer that you can use to verify signals? Viewing CANH/CANL and TX/RX signals would make quick work of troubleshooting.

Unfortunately not. All I can say is that I'm measuring 2.05ish volts on the L and H pins.
 
First loop

Code:
FIFO Disabled
	Mailboxes:
		MB0 code: RX_EMPTY	(Standard Frame)
		MB1 code: RX_EMPTY	(Standard Frame)
		MB2 code: RX_EMPTY	(Standard Frame)
		MB3 code: RX_EMPTY	(Standard Frame)
		MB4 code: RX_EMPTY	(Extended Frame)
		MB5 code: RX_EMPTY	(Extended Frame)
		MB6 code: RX_EMPTY	(Extended Frame)
		MB7 code: RX_EMPTY	(Extended Frame)
		MB8 code: TX_INACTIVE
		MB9 code: TX_INACTIVE
		MB10 code: TX_INACTIVE
		MB11 code: TX_INACTIVE
		MB12 code: TX_INACTIVE
		MB13 code: TX_INACTIVE
		MB14 code: TX_INACTIVE
		MB15 code: TX_INACTIVE

Second loop

Code:
FIFO Disabled
	Mailboxes:
		MB0 code: RX_FULL
		MB1 code: RX_FULL
		MB2 code: RX_FULL
		MB3 code: RX_OVERRUN
		MB4 code: RX_EMPTY	(Extended Frame)
		MB5 code: RX_EMPTY	(Extended Frame)
		MB6 code: RX_EMPTY	(Extended Frame)
		MB7 code: RX_EMPTY	(Extended Frame)
		MB8 code: TX_INACTIVE
		MB9 code: TX_INACTIVE
		MB10 code: TX_INACTIVE
		MB11 code: TX_INACTIVE
		MB12 code: TX_INACTIVE
		MB13 code: TX_INACTIVE
		MB14 code: TX_INACTIVE
		MB15 code: TX_INACTIVE

Edit:
Used this code from post #75 (just different enable pin) and everything seems fine.

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

void setup(void) {
  Serial.begin(115200); delay(400);
  pinMode(2, OUTPUT); digitalWrite(2, LOW); // enable tranceiver
  Can0.begin();
  Can0.setBaudRate(500000);
  Can0.setMaxMB(16); // up to 64 max for T4, not important in FIFO mode, unless you want to use additional mailboxes with FIFO
  Can0.enableFIFO();
  Can0.enableFIFOInterrupt();
  Can0.onReceive(canSniff);
  Can0.mailboxStatus();
}

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 loop() {
  Can0.events();

  static uint32_t timeout = millis();
  if ( millis() - timeout > 20 ) { // send random frame every 20ms
    CAN_message_t msg;
    msg.id = random(0x1,0x7FE);
    for ( uint8_t i = 0; i < 8; i++ ) msg.buf[i] = i + 1;
    Can0.write(msg);
    timeout = millis();
  }

}

Output

Code:
FIFO Enabled --> Interrupt Enabled
	FIFO Filters in use: 8
	Remaining Mailboxes: 8
		MB8 code: TX_INACTIVE
		MB9 code: TX_INACTIVE
		MB10 code: TX_INACTIVE
		MB11 code: TX_INACTIVE
		MB12 code: TX_INACTIVE
		MB13 code: TX_INACTIVE
		MB14 code: TX_INACTIVE
		MB15 code: TX_INACTIVE
MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 40290 ID: 7E8 Buffer: 3 7F 2 31 55 55 55 55 
MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 34684 ID: 7E8 Buffer: 3 7F 2 31 55 55 55 55 
MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 44981 ID: 7DF Buffer: 2 1 0 0 0 0 0 0 
MB 99  OVERRUN: 0  LEN: 7 EXT: 0 TS: 55476 ID: 7E8 Buffer: 6 41 0 FF FF FF FF 
MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 40179 ID: 7DF Buffer: 2 1 0 0 0 0 0 0 
MB 99  OVERRUN: 0  LEN: 7 EXT: 0 TS: 49934 ID: 7E8 Buffer: 6 41 0 FF FF FF FF 
MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 13198 ID: 7DF Buffer: 2 1 0 0 0 0 0 0 
MB 99  OVERRUN: 0  LEN: 7 EXT: 0 TS: 23311 ID: 7E8 Buffer: 6 41 0 FF FF FF FF 
MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 34185 ID: 7DF Buffer: 2 1 20 0 0 0 0 0 
MB 99  OVERRUN: 0  LEN: 7 EXT: 0 TS: 45268 ID: 7E8 Buffer: 6 41 20 FF FF FF FF 
MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 50372 ID: 7DF Buffer: 2 1 40 0 0 0 0 0 
MB 99  OVERRUN: 0  LEN: 7 EXT: 0 TS: 62225 ID: 7E8 Buffer: 6 41 40 FF FF FF FE 
MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 42899 ID: 7DF Buffer: 2 9 A 0 0 0 0 0 
MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 54184 ID: 7E8 Buffer: 10 17 49 A 1 45 43 4D 
MB 99  OVERRUN: 0  LEN: 8 EXT: 0 TS: 54317 ID: 7E0 Buffer: 30 0 0 0 0 0 0 0
 
Last edited:
oh, yeah. that could explain it. i just got a sample book in so i'll try 33pF. C6 was chosen fairly large as it shouldn't be switched during normal opperation, should i use a smaller cap there?

Edit: placed the 10k back and a C5 as an 33pF cap and it works!!!!!

@RayanR — could you draw up your final schematic? Would be super useful.
 
Got a fresh bare SN65HVD230 working. 10k from RS to ground, 100R across CANH and CANL. That was all. Seems that the breakout board I originally bought was duff.

Tempted to try out the TN330/332 as that seems newer and... probably better? Also saves me a 10k resistor by the looks of it.

I have a question regards mailboxes and what's the real use case for them.

MegaSquirt spits out data - e.g byte 2 and 3 from ID 1520 is RPM - for instance. IDs have multiple pieces of information. What's the most efficient way of processing this?

I presume I setup mailboxes filtered or each ID that I want to capture, and then in the interrupt routine I then parse the frame into my programs variables?

The other option would be to just keep a cache of can messages in an array - 1520 -> [0], 1521 -> [1] etc - and only process them when I need them (I don't need all of the variables all of the time, I only need them when I want to display them)
 
mailboxes are like a drop box queue, each one can hold one receive (or transmit) message, the others wait in line to grab the next one. FIFO (rx only) is 6 messages deep, in reception order. Mailboxes can receive frames in any order. You can set multiple filters on FIFO, mailboxes have only one filter.
 
Got a fresh bare SN65HVD230 working. 10k from RS to ground, 100R across CANH and CANL. That was all. Seems that the breakout board I originally bought was duff.

Tempted to try out the TN330/332 as that seems newer and... probably better? Also saves me a 10k resistor by the looks of it.

I have a question regards mailboxes and what's the real use case for them.

MegaSquirt spits out data - e.g byte 2 and 3 from ID 1520 is RPM - for instance. IDs have multiple pieces of information. What's the most efficient way of processing this?

I presume I setup mailboxes filtered or each ID that I want to capture, and then in the interrupt routine I then parse the frame into my programs variables?

The other option would be to just keep a cache of can messages in an array - 1520 -> [0], 1521 -> [1] etc - and only process them when I need them (I don't need all of the variables all of the time, I only need them when I want to display them)

If you have a limited enough set of rx IDs, I find it easiest to just assign each its own mailbox using the mailbox filters. Unless you are logging data or receiving multi-frame data packets, you generally only concerned with the most recent data. In this case, you want to service the mailbox rx fast enough to avoid queueing the incoming frames. This can be achieved by interrupt or polling, with the selection being based on your receive rates and other cpu load.
 
also if a mailbox gets a similar frame in other than it's own in filter mode, an overrun would occur and the frame would be overwritten. example without filters is, if all the mailboxes fill up without reading them, only the last mailbox to accept the frame is overwritten always with latest data. This is a hardware feature and not controlled by software. Technically if you block all IDs and assign only one mailbox to filter your ECU ID, that mailbox will always keep the latest frame as it keeps rewriting it over and over and over, so if ECU sends 3 frames, and if you read it, you'll see the 3rd only. If you are not using interrupts it's something to think about, useful or not, but with interrupts, you won't get overruns
 
Thanks, @tonton81 and @msadie

If I'm using interrupts... and can.events() in loop() —*will it process all the frames in one go? or does it rely on the loop to iterate through them?

I'd like to add an update flag to the interrupt — so that the display only bothers to update when there is new data.
 
No need to use can.events() if you are using mailbox interrupts. The interrupt will fire upon receipt of each message, and the received message object will be passed as an input arg. You can use one interrupt function for all MBs, check it's ID and process it accordingly. Or you can have different functions attached to different MBs based on their ID filters.
 
Regarding adding the 'new data' flag, that's a good idea. If your msg unpacking routine is intensive, you might just copy your message from the interrupt to a global, set a 'new msg 1501' flag, then later unpack it in loop().
 
Regarding adding the 'new data' flag, that's a good idea. If your msg unpacking routine is intensive, you might just copy your message from the interrupt to a global, set a 'new msg 1501' flag, then later unpack it in loop().

Hmmm — I was using FIFO.

If the mailbox has more than one frame in it —*how can I ensure that they are all dealt with in one go? or is that not possible?

I'm using MegaCAN which has quite a hefty parsing 450 line switch statement with 63 cases; https://github.com/mantonakakis1/MegaCAN/blob/master/MegaCAN.cpp

In truth I probably don't need to process the frames as soon as they arrive. Once I've rendered the gauges etc there's no rush until the next loop.

This is what I have currently:

Code:
void setup() {
  Serial.begin(115200);

  can.begin();
  can.setBaudRate(500000);
  can.enableFIFO();
  can.enableFIFOInterrupt();
  can.onReceive(decodeCAN);

}

void loop() {

  if (update) {

    update = false;

    Serial.println(ms.rpm);

  }

}

void decodeCAN(const CAN_message_t &msg) {

  MSCAN.getBCastData(msg.id, msg.buf, ms);

  update = true;

}
 
Last edited:
If you are using a library like MegaCAN, which will parse the message IDs and data, fifo with interrupt probably makes the most sense.
 
Hi,

Is there somewhere I can find more detailed documentation for this library? The git page assumes you understand why mailboxes and FIFO would be useful, and I’m looking for more information on these.

I am also trying to understand how the interrupts for this library work. I know system interrupts should be very lean, and I’m not sure if changing code in the canSniff function (argument of onReceive) is a bad idea.

Long story short, I am trying to implement a double click detection function using this library (if button press can packet detected twice within 400ms, then double click, else, single click). I had this function working perfectly using a different microcontroller and can library but I can not get it working properly with the FIFO setup here.

I’d like to try and sort this out myself, but the lack of documentation is making this difficult.

Thanks for the help,
Alex
 
if you don't use events(), message reception is fired directly to callbacks. I too use CAN button counting for OEM keyfob to control windows and sunroof, obviously you need to count them not in the callback scope only, I used a circular buffer to store the count, you could also use a volatile variable, I used a queue because i can look into the buffer to see the last button pressed (not only count) to engage the feature I want
 
if you don't use events(), message reception is fired directly to callbacks. I too use CAN button counting for OEM keyfob to control windows and sunroof, obviously you need to count them not in the callback scope only, I used a circular buffer to store the count, you could also use a volatile variable, I used a queue because i can look into the buffer to see the last button pressed (not only count) to engage the feature I want

Thanks, Got it working. I had two issues.

1) my counter was not volatile (oops, should have known that)
2) I was not checking for can events while waiting for the next button press.

This code is a little ugly, but it works!

Code:
int volatile clickCounter = 0;

int checkNumClicks(void){
    static unsigned long max_delay = 800;
    int returnVal = 0;
    //Serial.print("before Adding to Counter");
    //Serial.println(clickCounter);
    clickCounter += 1;
    //Serial.print("After Adding to Counter");
    //Serial.println(clickCounter);

    lastButtonPress = millis();

    while((millis() - lastButtonPress < max_delay) && clickCounter == 1){
       can1.events(); //NEED to CHECK FOR CAN MESSAGES WHILE YOU ARE WAITING FOR COUNTER TO INCREMENT!


        if (clickCounter == 2){
        Serial.println("Button pressed twice!");
        returnVal = 2;
        clickCounter = 0;
        lastButtonPress = millis();
        break;
        return(returnVal); //I don't think this actually does anything.
        }
    }
    if (clickCounter == 1){
        Serial.println("Button pressed once!");
        clickCounter = 0;
        returnVal = 1;
        lastButtonPress = millis();
        return(returnVal);
    }

    return (returnVal);
}
 
Back
Top