FlexCAN_T4 - FlexCAN for Teensy 4

Hi,

I have a question on the ACK between 2 Teensy 4.1's
My setup has one Teensy transmitting and the other receiving, which is working.
But, I don't want the transmitting Teensy to be interruped in case of an ACK fail, and having to retransmit the message.

I haven't found the option in the library to disable an automatic retransmission yet.
Does this option exist, or is there another possible solution to not get any interruption on the transmitting Teensy?
 
the hardware retransmits automatically, even if you call an abort on the mailbox it may or may not transmit a garbage frame, and if your code is retransmitting over a loop you'll just be enabling it to transmit until it acks until the abort command is issued. if there is no ACK on the network it'll just keep retransmitting, it wont block your code. once an ACK is issued the next frame can be written to the mailbox, until then other writes to a transmit box that is waiting for ACK is discarded. Even though there is a transmit once feature (which we DO actually use), the hardware will ALWAYS send endlessly until that ONE frame is successfully sent.
 
I have a question on the sample CAN2.0_example_FIFO_with_interrupts.ino

In the code, it appears to be making random transmits onto the CAN bus. What is the purpose of this? If one was on CAN bus, shouldn't 0x7DF be the message ID from a test scanner tool to instruct modules to report PIDs? I understand CAN has many applications but my question is specific to vehicle CAN bus applications.


if ( millis() - timeout > 200 ) {
CAN_message_t msg;
msg.id = random(0x1,0x7FE);
for ( uint8_t i = 0; i < 8; i++ ) msg.buf = i + 1;
Can0.write(msg);
timeout = millis();
}
 
yes that will work, you could also just send it 10ms after the request

hi Tony,

I created this example:
Code:
#include <FlexCAN_T4.h>
#include <isotp.h>
isotp<RX_BANKS_16, 512> tp; /* 16 slots for multi-ID support, at 512bytes buffer each payload rebuild */

FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> Can1;

void myCallback(const ISOTP_data &config, const uint8_t *buf) {
  Serial.print("ID: ");
  Serial.print(config.id, HEX);
  Serial.print("\tLEN: ");
  Serial.print(config.len);
  Serial.print("\tFINAL ARRAY: ");
  for ( int i = 0; i < config.len; i++ ) {
    Serial.print(buf[i], HEX);
    Serial.print(" ");
  } Serial.println();
}

void canSniff(const CAN_message_t &msg) {
 uint8_t flowControl[] = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 if (msg.buf[0] == 0x10){
    ISOTP_data configFlowCont;
    configFlowCont.id = 0x17FC0076;
    configFlowCont.flags.extended = 1; /* standard frame */
    configFlowCont.separation_time = 0; /* time between back-to-back frames in millisec */
    tp.write(configFlowCont, flowControl, sizeof(flowControl));
  }
}

void setup() {
  Serial.begin(115200); delay(400);
  Can1.begin();
  Can1.setClock(CLK_60MHz);
  Can1.setBaudRate(500000);
  Can1.setMaxMB(16);
  Can1.enableFIFO();
  Can1.enableFIFOInterrupt();
  Can1.onReceive(canSniff);
  tp.begin();
  tp.setWriteBus(&Can1); /* we write to this bus */
  tp.onReceive(myCallback); /* set callback */
}

void loop() {
  static uint32_t sendTimer = millis();
  if ( millis() - sendTimer > 1000 ) {
    CAN_message_t msg;
    uint8_t buf[] = {0x07, 0x22, 0xF4, 0x33, 0xF4, 0x0F, 0xF4, 0x3C};
    ISOTP_data config;
    config.id = 0x17FC0076;
    config.flags.extended = 1; /* standard frame */
    config.separation_time = 0; /* time between back-to-back frames in millisec */
    tp.write(config, buf, sizeof(buf));
    //memcpy(msg.buf, buf, sizeof(msg.buf)); // Copy the data array into the message buffer
    //msg.len = 8;
    //msg.flags.extended = 1; // 29 bit header
    //msg.id = 0x17FC0076; // request header for OBD
    //Can1.write(msg);
    sendTimer = millis();
  }
}

But what I see the library is transmit the following which :
Code:
0x10, 0x08, 0x07, 0x22, 0xF4, 0x33, 0xF4
0x21, 0xF4, 0x3C, 0x00, 0xF4, 0x33, 0xF4

where is should just transmit:
Code:
0x07, 0x22, 0xF4, 0x33, 0xF4, 0x0F, 0xF4, 0x3C
What could be the cause of this?
I tried to transmit with Can1 but then istop does not catch anything coming in.
 
effectively that writes the 8 bytes as a payload in isotp format (2 frames for payload)

so, 8 bytes as payload plus isotp header
to send that just as a frame itself use Can1.write(msg)
 
Last edited:
As I mentioned in the last line of my previous post, I transmitted with Can1.write(msg) but then istop did not catch the payload - so the istop callback is never triggered.
 
you need to send that frame, then send the flow control after, if you don't send the flow control frame the ecu will not send out the full isotp frame, and if the full isotp frame is not sent out you wont get a callback because it hasn't been assembled since the ecu didn't send the consecutive frames
 
But I do send out my flow control frame, using the canSniff callback in my example.
Per ISOTP, the flow control should be sent after the first frame is received, and not after the request frame has been sent as you suggest.
Perhaps I should also transmit from Can1.write in the canSniff callback and then it might work..
 
yes send both as normal frames, the isotp is there just to assemble messages in stream

Great, this is exactly the behavior I would like.

I modified the sketch, will test it tomorrow.
Code:
#include <FlexCAN_T4.h>
#include <isotp.h>
isotp<RX_BANKS_16, 512> tp; /* 16 slots for multi-ID support, at 512bytes buffer each payload rebuild */

FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> Can1;

void myCallback(const ISOTP_data &config, const uint8_t *buf) {
  Serial.print("ID: ");
  Serial.print(config.id, HEX);
  Serial.print("\tLEN: ");
  Serial.print(config.len);
  Serial.print("\tFINAL ARRAY: ");
  for ( int i = 0; i < config.len; i++ ) {
    Serial.print(buf[i], HEX);
    Serial.print(" ");
  } Serial.println();
}

void canSniff(const CAN_message_t &msg) {
 uint8_t flowControl[] = {0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 if (msg.buf[0] == 0x10){
    memcpy(msg.buf, flowControl, sizeof(msg.buf));
    msg.len = 8;
    msg.flags.extended = 1; 
    msg.id = 0x17FC0076; 
    Can1.write(msg);
  }
}

void setup() {
  Serial.begin(115200); delay(400);
  Can1.begin();
  Can1.setClock(CLK_60MHz);
  Can1.setBaudRate(500000);
  Can1.setMaxMB(16);
  Can1.enableFIFO();
  Can1.enableFIFOInterrupt();
  Can1.onReceive(canSniff);
  tp.begin();
  tp.setWriteBus(&Can1); /* we write to this bus */
  tp.onReceive(myCallback); /* set callback */
}

void loop() {
  static uint32_t sendTimer = millis();
  if ( millis() - sendTimer > 1000 ) {
    CAN_message_t msg;
    uint8_t buf[] = {0x07, 0x22, 0xF4, 0x33, 0xF4, 0x0F, 0xF4, 0x3C };
    memcpy(msg.buf, buf, sizeof(msg.buf));
    msg.len = 8;
    msg.flags.extended = 1;
    msg.id = 0x17FC0076; 
    Can1.write(msg);
    sendTimer = millis();
  }
}
 
So my test sketch worked, thanks Tony!
I do have two more questions:
1. If the response payload is 8 bytes (single frame) will the isotp callback still trigger?
2. In the isto constructor the comment says “16 slots for multi-ID support” - what does this mean? Is it a filter? How can I define the IDs?
 
1) it should provided the payload is 6 bytes or less, provided the first 2 bytes are passes as an isotp header. if you prefer not receiving those single frames whivh can exist during debugging, i usually just filter them out when the size is less than 6

2) 16 slots aka queues, each entry stores a unique canid, for rebuilding. so up to 16 canids can be assembled at same time. you don't define the IDs, isotp frames are assigned a slot automatically for rebuilding, if all slots are full the queue just rolls over the older slot. i doubt you'll be using 16 reassemblies at same time though

there are no filters for the plugin. as long as one or more mailboxes/fifo sees an isotp frame, it will attempt re-assembly of payload on it. if you apply filters to mailboxes/fifo, then it cant assemble data it wont see, it's just a non-invasive passive type of plugin i guess :)
 
Tony (@tonton81):

I would like to express my deep appreciation & gratitude for the FlexCAN_t4 library (I believe) that you have contributed. By utilizing that excellent capability, I have been able to create a CAN/isotp monitor that is able to send service requests & to properly process/parse the returning responses. As I have slowly been adding capabilities, I have been posting updates to my sketch in <another thread>, as well as example captures (also in <that thread>) from what I have been able to decode from monitoring the CAN bus pins of the OBD-II connector on my 2020 Honda Civic as I send out the supported service requests & process/parse the returning responses.

Thanks again for your expertise & contributions !!

Mark J Culross
KD5RXT
 
Sanity Check for Simple CAN Circuit

Hi All,

I had been working on a project a few months back and it ended up falling by the wayside, trying to pick it back up now and wanted to start out by validating a super simple communication circuit between two Teensy 4.0s. I'm running into repeated issues that are throwing me off, my next step is buying new hardware in case I have a bad CAN transceiver specifically, but figured I would come over this way for a sanity check on everything else before buying new stuff.

I've got two Teensy 4.0s, both wired through the CRX1 and CTX1 pins, configured to use CAN1 based off the CAN2.0_example_FIFO_with_interrupts example code. I've tested all of this at both 1,000,000 and 100,000 baudrates. I've modified one teensy to transmit with the ID 0x123 and the other with ID 0x456, with identical data packets. By sniffing the RX and TX lines between each Teensy and Transceiver, I am reliably seeing the message sending, but it does not appear to be transmitting across the bus between Teensies. I'm getting a NAK response according to the packet analyzer, with an example packet output being seen below.

ID: 456 DLC: 08 Data: 01 02 03 04 05 06 07 08 CRC: 274A NAK

I believe this means that the communication between both transceivers is failing and had a spare SN65HVD230 based chip that I tried to use to eliminate a single broken transceiver, but I only have the one, so if a transceiver is broken, it would seem both are. I figure this to be somewhat unlikely but I'm kind of grasping at straws. For the circuit, the transceivers each have the following pins:

3V3: Tied to its Teensy's 3V3
GND: Tied to its Teensy's GND
RX: Tied to its Teensy's CRX1
TX: Tied to its Teensy's CTX1
CANH: Tied to the other transceiver's CANH
CANL: Tied to the other transceiver's CANL

I can include the code if necessary, but it is literally the example mentioned above, but with the ID swapped out to differentiate.

I half feel like I'm just missing something I should know, but otherwise I might just need to get some new transceivers. Any advice would be greatly appreciated.

Quick Edit:
Wanted to mention I've checked all wires for continuity, checked voltages at the transceivers, everything looks good there. I'm going to check for a short at the CANH / CANL resistor in a moment, as it is built into the chip I'm using but don't have my hands on my multimeter at the moment. I've also been able to verify setup execution by seeing the mailboxStatus() function execution output, as well as testing for the write() function execution from using Serial output, along with being able to see the transmitted messages on the teensy pin nodes.
 
Last edited:
CAN extended frames seems to not be sent

Hi all,

I'm having trouble sending a CAN frame with an 29 bits extended ID on a Teensy 4.1.

I'm trying to communicate with an actuator (A CubeMars AK60) which uses both extended and standard ID frames for different modes of operation.

I successfully sent many standard frames and the actuator did what it was supposed to do. I'm now trying to use another mode of the actuator which requires an Extended ID frame and I've had no luck with that.

Here's a sample of the code where method run is called each 4 seconds with changed values:
Code:
void AK60_init()
{
    can1.begin();
    can1.setBaudRate(1000000);
}

void run()
{
    //buffer is a byte array containing the data to send
    send_std_can_message(buffer, Position_Velocity);
    can1.mailboxStatus();
}

void send_std_can_message(byte buff[], Mode mode = None)
{
    CAN_message_t msg;
    uint32_t id = TEENSY_CAN_ID;
    bool extended = 0;

    for (uint8_t i=0; i<8; i++){
        msg.buf[i] = buff[i];
    }

    if (mode != None)
    {
        id = TEENSY_CAN_ID | (mode << 8);
        extended = 1;
    }

    msg.flags.extended = extended;
    msg.id = id;
    msg.len = 8;

    can1.write(msg);
}

The AK60 is a bit tricky, it requires the mode type to be mixed with the ID of the Teensy. I've validated each data sent and they're all well packaged, so that wouldn't be the issue. I've also tried using the filters, but I'm not very familiar with them and assumed it wasn't the issue. The current setup uses the default Mailboxes, but they seem to receive no data after the can1.write(msg) method has been called. Does someone know what could be causing the problem here?

Thanks! :)
 
Unless I'm missing something, it seems like your code is calling the send_std_can_message function as fast as the teensy can loop. This is certainly faster than a 1Mbps bus can transmit those frames, causing the teensy to flood the bus. Try putting your function in a timing conditional like:
Code:
if ( millis() - timeout > 200 ) {
   send_std_can_message(buffer, Position_Velocity);
   can1.mailboxStatus();
   timeout = millis();
}
 
Last edited:
Thanks for the response!

It could be a problem, but in my case, the method run() is called with a delay of 4 seconds each time, which is far more than enough for the CAN transmission.
Any other ideas?
 
Sorry, I didn't read your post well enough. You say you interacted with the device in standard mode. Did you you successfully read messages sent by the device, or only write to the bus?
Any chance you have some sort of oscilloscope to verify the bus activity?
 
3V3: Tied to its Teensy's 3V3
GND: Tied to its Teensy's GND
RX: Tied to its Teensy's CRX1
TX: Tied to its Teensy's CTX1
CANH: Tied to the other transceiver's CANH
CANL: Tied to the other transceiver's CANL

I can include the code if necessary, but it is literally the example mentioned above, but with the ID swapped out to differentiate.

I half feel like I'm just missing something I should know, but otherwise I might just need to get some new transceivers. Any advice would be greatly appreciated.

Quick Edit:
Wanted to mention I've checked all wires for continuity, checked voltages at the transceivers, everything looks good there. I'm going to check for a short at the CANH / CANL resistor in a moment, as it is built into the chip I'm using but don't have my hands on my multimeter at the moment. I've also been able to verify setup execution by seeing the mailboxStatus() function execution output, as well as testing for the write() function execution from using Serial output, along with being able to see the transmitted messages on the teensy pin nodes.

The termination resistor is built in to the chip? What chip are you referring to? Maybe check the datasheet for the purpose of the 8th transceiver pin you didn't mention. It sometimes is a 'silent' logic pin that prevents the transceiver from ack'ing or sending.
 
Yes, I was able to read data sent by the actuator in standard mode. I do have access to an oscilloscope, but I haven't tried reading the bus with it yet. The method mailboxStatus() seems to indicate the data is received in the mailbox MB0, that the tram is an extended one and the payload is right. I will try the oscilloscope since I think it may not be a problem with the library.
 
The termination resistor is built in to the chip? What chip are you referring to? Maybe check the datasheet for the purpose of the 8th transceiver pin you didn't mention. It sometimes is a 'silent' logic pin that prevents the transceiver from ack'ing or sending.

I'm using an off-the-shelf breakout for the CAN transceiver chip, the exact chip is seen here:

https://www.amazon.com/WWZMDiB-SN65HVD230-Transceiver-Communication-Protection/dp/B0B82GJLH5/

I have tested with brand new ones as of today, so unless they were bad out of the box, I'm thinking I must be doing something stupid.

These boards only have those 6 pins, the termination resistor is built into the board, I have no access to the enable pin, which I believe is just tied high or low in the board. I tested using a logic analyzer on the TX, CANL, and CANH. I've attached a screenshot of what that looks like. It seems I have a readable message up until the transceiver, then once it hits the high and low buses it turns into a garbled mess. I'm kind of at a loss, the final part of the packet is a NAK by the way.

Logic Analyzer SS.jpg
 
Best to use an oscilloscope to view the CANL and CANH signal as it idle around 2.5v and the active state is around 3.5v for CANH and around 1.5 for CANL.

In a CAN network you need at least two active nodes.

Have you got both Teensys running a CAN sketch ?
 
Best to use an oscilloscope to view the CANL and CANH signal as it idle around 2.5v and the active state is around 3.5v for CANH and around 1.5 for CANL.

I’m using a desktop oscilloscope (Analog Discovery 2), the DIO pins are 5V tolerant with a sample rate of 100 million samples / sec. I’m not super great at electrical testing, but I had thought that should be fine?
 
Back
Top