How to use CAN bus on Teensy 4.1, 4.0 and 3.2 ?

Bazziil

Member
Hi everyone,

I would like to communicate with a CAN bus between 3 teensy board (4.1, 4.0 and 3.2).
The only information that i found about Teensy and the CAN bus, is that the connection isn't native and i need a CAN Transceiver and a CAN receiver at least for the Teensy 3.2. Is that right ? Or can i directly wire it and use it like other connection ?
I couldn't find any more information about T4.0 and T4.1 concerning this port, if you have any documentation or knowledge about this, i will gladly read them.

Thanks !
 
All Teensy boards you mention have a CAN Controller onboard.
See this picture from Wikipedia:

CAN_Node.png

The only thing you need to add is a CAN bus transceiver like this one:

TI SN65HVD230 CAN-bus transceiver module.png

Google for "TI SN65HVD230 CAN-bus transceiver module".
No configuration needed for this transceiver, just hookup the CRX & CTX pins to the appropriate Teensy CAN bus pins.

Paul
 
Thanks for your answer !
I just need to add one transceiver for each Teens that will transmit with CAN, if i am right.
 
I have a question regarding the transceiver.
I have a project where similar communication is required between a 3.6 and 3.2. To summarize what I have done so far is I am running a CAN node that is sending data and one that is attempting to receive it. I have gotten it to send data, however it stops updating it after 10 attempts (simple check with setting Can0.write() equal to an int). It is consistently cutting off after 10 attempts and is repeatable, but I can't find a buffer that is causing this cutout so I am not sure why this is. The system IS sending data though, as the receiving node does display the first sent data point through the serial port. Also to confirm that the data received is in fact from the first Teensy I filled an 8 byte buffer full of random data and it was 100% accurate on the other teensy, as long as it was not changed during runtime.
I really want to understand CAN networks better so I have a few questions to follow up where I am at:
If the transceiver is required, how is it the first data frame is able to make it to the receiving side?
Is it possible that it times out after 10 attempts is that it is trying to write the first message over and over again but see's a reflection in the data which causes it to reset? I only 1 120 ohm resistor hooked up to it and that resistance seems to be to allow it to function (no resistor doesn't function)
Any help provided is appreciated, and if there is any lacking information let me know!
Thanks
 
I’m about to start out with CAN a on the Teensy 4.1. I’ve got both MCP2515 boards and some 3.3v transceiver chips too. I started out interested in using the 2515 as I found a fair amount of code out there online. Data rates will be fairly low so speed and overheads are a non-issue either way. I’m thinking now that there’s no real advantage to using the microchip part over Teensy hardware CAN via the transceiver alone. Though I’d welcome any comments to the contrary?

Steve
 
and if there is any lacking information let me know!
Thanks
Hi Dresio, could you post both pieces of code [TX node & RX node]. And perhaps a photo of your setup?

I I’m thinking now that there’s no real advantage to using the microchip part over Teensy hardware CAN via the transceiver alone.
Hi Steve, correct, why use an external CAN controller when it's already present in the Teensy? And software support is also available.

Paul
 
Both bits of code on the nodes are just slightly modified from the object oriented examples.
Here is the receive node:
Code:
#include <FlexCAN.h>

class ExampleClass : public CANListener 
{
public:
   void printFrame(CAN_message_t &frame, int mailbox);
   bool frameHandler(CAN_message_t &frame, int mailbox, uint8_t controller); //overrides the parent version so we can actually do something
};

void ExampleClass::printFrame(CAN_message_t &frame, int mailbox)
{
   Serial.print("ID: ");
   Serial.print(frame.id, HEX);
   Serial.print(" Data: ");
   for (int c = 0; c < frame.len; c++) 
   {
      Serial.print(frame.buf[c], HEX);
      Serial.write(' ');
   }
   Serial.write('\r');
   Serial.write('\n');
}

bool ExampleClass::frameHandler(CAN_message_t &frame, int mailbox, uint8_t controller)
{
    printFrame(frame, mailbox);

    return true;
}

ExampleClass exampleClass;

// -------------------------------------------------------------
void setup(void)
{
  delay(1000);
  Serial.println(F("Hello Teensy Single CAN Receiving Example With Objects."));

  Can0.begin(100000);  


  Can0.attachObj(&exampleClass);
  exampleClass.attachGeneralHandler();
}


// -------------------------------------------------------------
void loop(void)
{
  delay(1000);
  Serial.write('.');
}

And here is the send node:
Code:
#include <FlexCAN.h>

#ifndef __MK66FX1M0__
  #error "Teensy 3.6 runs this code"
#endif


CAN_message_t msg;
static uint8_t hex[17] = "0123456789abcdef";

class ExampleClass : public CANListener 
{
public:
   void printFrame(CAN_message_t &frame, int mailbox);
   bool frameHandler(CAN_message_t &frame, int mailbox, uint8_t controller); //overrides the parent version so we can actually do something
};

void ExampleClass::printFrame(CAN_message_t &frame, int mailbox)
{
   Serial.print("ID: ");
   Serial.print(frame.id, HEX);
   Serial.print(", MB: ");
   Serial.print(mailbox);
   Serial.print(" Data: ");
   for (int c = 0; c < frame.len; c++) 
   {
      Serial.print(frame.buf[c], HEX);
      Serial.write(' ');
   }
   Serial.write('\r');
   Serial.write('\n');
}

bool ExampleClass::frameHandler(CAN_message_t &frame, int mailbox, uint8_t controller)
{
    printFrame(frame, mailbox);

    return true;
}

ExampleClass exampleClass;

// -------------------------------------------------------------
void setup(void)
{
  delay(1000);
  Serial.println(F("Hello Teensy 3.6 dual CAN Test With Objects."));

  Can0.begin(100000);  

  //if using enable pins on a transceiver they need to be set on
  pinMode(2, OUTPUT);
  pinMode(35, OUTPUT);

  digitalWrite(2, HIGH);
  digitalWrite(35, HIGH);

  Can0.attachObj(&exampleClass);
  exampleClass.attachGeneralHandler();

  msg.ext = 0;
  msg.id = 0x100;
  msg.len = 8;
  msg.buf[0] = 10;
  msg.buf[1] = 20;
  msg.buf[2] = 0;
  msg.buf[3] = 100;
  msg.buf[4] = 128;
  msg.buf[5] = 64;
  msg.buf[6] = 32;
  msg.buf[7] = 16;
}


// -------------------------------------------------------------
void loop(void)
{
  msg.buf[0]++;
  int temp = Can0.write(msg);
  Serial.println(temp);
  delay(20);
}

Here is a picture of the setup:
Setup.jpg

Breadboard wires were too unstable so this is a modified PCB I found laying around. I am looking over it now and I think that I may of been off one pin in the socketing so the tx on the 3.6 was streaming directly to the rx pin on the 3.2, which would explain why I am seeing data on the 3.2, but I am still confused on why it is only receiving the first message.
 
Hi Dresio,
I got a bit confused with the object oriented examples so I crafted a slightly different piece of code for the receiver:
Code:
// https://github.com/collin80/FlexCAN_Library
// https://en.wikipedia.org/wiki/CAN_bus

#include <FlexCAN.h>                 // CAN transceiver: CTX pin 3, CRX pin 4

static CAN_message_t msg;
static CAN_filter_t allPassFilter;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  while (!Serial) ;       // wait for serial monitor
  
  allPassFilter.flags.extended = 1;  // 1 for extended/29 bit IDs
  Can0.begin(100000, allPassFilter); // init CAN bus @ 100kbps
  Serial.println("CAN initialized and listening");
}

void loop() {
  if (Can0.read(msg)) {
    digitalWrite(LED_BUILTIN, HIGH);
    Serial.printf("0x%08X", msg.id); Serial.print(" ");
    Serial.printf("0x%04X", msg.timestamp); Serial.print(" ");
    Serial.printf("0x%02X", msg.flags); Serial.print(" ");
    Serial.printf("0x%02X", msg.len); Serial.print(" ");
    for (byte i = 0; i < sizeof(msg.buf); i++) {
      Serial.printf("0x%02X", msg.buf[i]); Serial.print(" ");
    }
    Serial.println("");
    digitalWrite(LED_BUILTIN, LOW);
  }
}

When running this code on a Teensy 3.2, I was able to receive lots of CAN frames without any problem from my USB CAN analyzer.

Paul
 
I just realized that the issue you are seeing [only receiving the first message] is perhaps caused by the fact that the transmitting node does not see the ACK generated by the receiving node when a correct CAN message is received. In the case of a missing ACK, it may stop transmitting subsequent CAN frames.

Here is a logic analyzer snapshot showing the ACK inserted by the receiver:

CAN message.png
"CAN RX src" is measured on pin 4 of the T3.2, "CAN TX src" on pin 3. [by the way, this snapshot shows the CAN bus running at 250kbps]
CAN RX and CAN TX are ofcourse merged when you look at the actual CANL and CANH lines.

I'm also wondering whether you do need to use an actual transceiver such that the transmitter can check whether the ACK is given in the right time slot.

Paul
 
I try to setup filter for can bus. This does not work:

Code:
#include <FlexCAN.h>

void setup() {

  CAN_filter_t defaultFilter;
  
  defaultFilter.ext = 0;
  defaultFilter.rtr = 0;
  defaultFilter.id = 0x700;
  for (int c = 4; c < 16; c++){
    Can0.setMask(0xFFFF,c);
    Can0.setFilter(defaultFilter, c); // */
  }

  
  Can0.begin(500000);

  Serial.println("Init done");

}

void loop() {

   while ( Can0.read(rxmsg) ) {
    Serial.printf("%08X",rxmsg.id);for(byte i=0;i<8;i++){Serial.printf(" %02X",rxmsg.buf[i]);}Serial.printf(" time:%d \n\r",millis());

   }
  
}

any directions, what I need to change, to receive only 0x700 frames?
 
Hi jblaze, I modified your sketch since it compiled with an error ['rxmsg' was not declared in this scope]
Here is the modified code:
Code:
#include <FlexCAN.h>

static CAN_message_t rxmsg;
static CAN_filter_t defaultFilter;

void setup() {
  defaultFilter.id = 0x0700;
  defaultFilter.flags.extended = 0;       // this filter works, it blocks extended frames and vice-versa
  defaultFilter.flags.remote = 0;

  //  for (uint8_t c = 0; c = 13; c++) {    // 0-13 are RX mailbox, 14 & 15 are TX mailbox
  //    Can0.setMask(0x0700, c);
  //    Can0.setFilter(defaultFilter, c);
  //  }
  //  Can0.begin(500000);

  Can0.begin(500000, defaultFilter);   // uncomment when using for-loop to set mask
  Serial.println("Init done");
}

void loop() {
  if (Can0.read(rxmsg)) {
    Serial.printf("%08X", rxmsg.id);
    for (byte i = 0; i < 8; i++) {
      Serial.printf(" %02X", rxmsg.buf[i]);
    }
    Serial.printf(" time:%d \n\r", millis());
  }
}

Long story short: I did not get it to filter using setMask. That is to say: ALL frames are blocked.
With the for-loop uncommented as above, it does filter on extended/standard frames though.
Even when setting the masks after Can0.begin(500000); as tonton81 suggested, it did not work.

Tried a lot things the last 2 hours but I'm stuck now. Maybe I get another idea after a night's sleep.

Paul
 
Hi everyone. I see that is is an old thread, but i'll shoot my shot:

Is there a specific CAN-bus transciever one need to buy in order to use CAN FD and Teensy 4.1 / 4.0? The "TI SN65HVD230 CAN-bus transceiver module" seems very simple and good, but does that work with CAN FD, or is there a specific type of CAN-bus transcievers that works with CAN FD?

Also, what is the difference between using an IC as MCP2515 versus using something more user-friendly as the "TI SN65HVD230 CAN-bus transceiver module"?

Ole
 
The SN65HVD230 supports speeds up to 1Mbit/s. For CAN FD you need a higher speed chip like the MCP2562FD.
See a suitable board here.

By the way, in the meantime I have solved the filtering issue that I referred to in msg #16.
You need to set a 29 bit mask, an 11 bit mask doesn't work.

Code:
void setup() {
  defaultFilter.flags.extended = 0;
  Can0.begin(500000, defaultFilter);
 
  defaultFilter.id = 0x0700;          // pass only ID 0x0700
  for (int m = 0; m < 14; m++) {      // set mask & filter for RX mailbox 0-13 [14 & 15 are TX mailbox]
    Can0.setMask(0x1FFFFFFF, m);      // set 29 bit mask, 11 bit mask doesn't work
    Can0.setFilter(defaultFilter, m);
  }
  Serial.println("Init done");
}

Paul
 
Hi , I want to use all 3 CAN ports of teensy4.0 but I don't want to use the FD protocol. Is it possible to implement CAN2.0 on the CAN FD port too?

Also , I had a basic doubt. Since we are using an external transceiver can't we use any set of TX/RX pins (like RX1,TX1) on teensy4.0 for CAN communication?

teensy4_pinout.png
 
CAN3 supports CAN2.0 legacy OR CANFD, so yes you can use CAN2.0 on all 3 with identical code

the CAN pins are PINK on the card, don't confuse them for the serial ports, you can't install the transceiver on any pins other than the pink labelled ones as theyre the pins linked to the onboard CAN controller
 
Hi , I want to use all 3 CAN ports of teensy4.0 but I don't want to use the FD protocol. Is it possible to implement CAN2.0 on the CAN FD port too?

Also , I had a basic doubt. Since we are using an external transceiver can't we use any set of TX/RX pins (like RX1,TX1) on teensy4.0 for CAN communication?

View attachment 27306

The pinout picture is for Teensy 3.2 not Teensy 4.0
 
CANbus teensy4.1

Hi, I am communicating between two teensy 4.1 and I wanted to know if is there any way to test the communication without the can transceiver. I already saw one to buy but they take a lot of time to be delivered.
 
It is best to use a CAN transceiver to get the proper experience.

Digikey and Mouser have in stock now.

Recommend to use the MCP2562FD.
 
While I generally agree with SK Pang, sometimes it's helpful to rule out physical layer/transceiver issues when troubleshooting. The typical tool used would be an oscilloscope or logic analyzer. There is a specific way you can connect multiple devices without transceivers, but it negates the benefits of the differential pair.

Edit: be very careful if you try this to adapt the voltage to 3.3v and resistances as needed to protect the teensy's pins.
https://www.mikrocontroller.net/attachment/28831/siemens_AP2921.pdf
 
Back
Top