Teensy 3.1 and CAN Bus

Hi

I just checked out the CAN driver progress and have 2 items.

1. Freescale recommends using the crystal clock source rather than the peripheral clock since it has lower jitter.

2. I have posted my code below which calculates the settings based on required CAN bus speed (working out the time quanta for PROP_SEG and PSEG1 to achieve compliant settings in each case). It requires a bit more code and the calculation to be performed but allows for more flexibility that a few fixed speeds.

The return value from fnOptimalCAN_clock(usMode, ulSpeed) can be written directly to the CAN_CTRL1 register. As can be seen the mode allows the clock source to be specified and also the caluclation to be overwridden if the user prefers specifying the CAN_CTRL1 register value directly.

Regards

Mark


// The best choice of clock input is from the external crystal (lowest jitter), however this may not always enable the best settings to achieve the required speed.
// The choice of clock source is user-defined but this routine tries to achieve best settings using highest time quanta resolution.
//
// There are up to 25 time quanta in a CAN bit time and the bit frequency is equal to the clock frequency divided by the quanta number (8..25 time quanta range)
// There is always a single time quanta at the start of a bit period called the SYNC_SEG which can not be changed (transitions are expected to occur on the bus during this period)
// The sampling occurs after time segment 1, which is made up of a propagation segment (1..8 time quanta) plus a phase buffer segment 1 (1..8 time quanta),
// followed by time segment 2, made up of phase buffer segment 2 (2..8 time quanta)
//
// CAN standard compliant bit segment settings give the following ranges (re-synchronisation jump width of 2 is used since it is complient with all))
// Time segment 1 should be 5..10 when time segment 2 is 2 (min/max time quanta per bit is 8/13)
// Time segment 1 should be 4..11 when time segment 2 is 3 (min/max time quanta per bit is 8/15)
// Time segment 1 should be 5..12 when time segment 2 is 4 (min/max time quanta per bit is 10/17)
// Time segment 1 should be 6..13 when time segment 2 is 5 (min/max time quanta per bit is 12/19)
// Time segment 1 should be 7..14 when time segment 2 is 6 (min/max time quanta per bit is 14/21)
// Time segment 1 should be 8..15 when time segment 2 is 7 (min/max time quanta per bit is 16/23)
// Time segment 1 should be 9..16 when time segment 2 is 8 (min/max time quanta per bit is 18/25)
//
static unsigned long fnOptimalCAN_clock(unsigned short usMode, unsigned long ulSpeed)
{
unsigned long ulClockSourceFlag = EXTAL_CLK_SOURCE;
unsigned long ulClockSpeed;
unsigned long ulLowestError = 0xffffffff;
unsigned long ulCanSpeed;
unsigned long ulError;
unsigned long ulPrescaler;
int iTimeQuanta = 25; // highest value for highest control resolution
int iBestTimeQuanta = 25;
unsigned long ulBestPrescaler;
if (CAN_USER_SETTINGS & usMode) {
return ulSpeed; // the user is passing optimal configuration settings directly
}
if (CAN_PLL_CLOCK & usMode) { // use the bus clock rather than crystal input directly
ulClockSpeed = (BUS_CLOCK); // the clock speed for calculation use is the bus speed
ulClockSourceFlag = CLK_SRC_PERIPH_CLK; // peripheral clock will be used as clock source
}
else {
ulClockSpeed = _EXTERNAL_CLOCK; // the clock speed for calculation use is the external/crystal clock speed
}
while (iTimeQuanta >= 8) { // test for best time quanta
ulCanSpeed = (ulClockSpeed/iTimeQuanta); // speed without prescaler
ulPrescaler = ((ulCanSpeed + (ulSpeed/2))/ulSpeed); // best prescale value
if (ulPrescaler > 256) {
ulPrescaler = 256; // maximum possible prescale divider
}
ulCanSpeed /= ulPrescaler;
if (ulCanSpeed >= ulSpeed) { // determine the absolute error value with this quanta setting
ulError = (ulCanSpeed - ulSpeed);
}
else {
ulError = (ulSpeed - ulCanSpeed);
}
if (ulError < ulLowestError) { // if this is an improvement
ulLowestError = ulError;
iBestTimeQuanta = iTimeQuanta; // best time quanta value
ulBestPrescaler = ulPrescaler;
}
iTimeQuanta--;
}
ulBestPrescaler--;
ulBestPrescaler <<= 24; // move the prescale value into position
if (iBestTimeQuanta >= 18) { // determine the phase buffer length value
ulBestPrescaler |= PHASE_BUF_SEG2_LEN8;
iBestTimeQuanta -= (8 + 1); // remaining time quanta (time segment 1) after removal of the time segment 2 and the SYN_SEG
}
else if (iBestTimeQuanta >= 16) {
ulBestPrescaler |= PHASE_BUF_SEG2_LEN7;
iBestTimeQuanta -= (7 + 1);
}
else if (iBestTimeQuanta >= 14) {
ulBestPrescaler |= PHASE_BUF_SEG2_LEN6;
iBestTimeQuanta -= (6 + 1);
}
else if (iBestTimeQuanta >= 12) {
ulBestPrescaler |= PHASE_BUF_SEG2_LEN5;
iBestTimeQuanta -= (5 + 1);
}
else if (iBestTimeQuanta >= 10) {
ulBestPrescaler |= PHASE_BUF_SEG2_LEN4;
iBestTimeQuanta -= (4 + 1);
}
else {
ulBestPrescaler |= PHASE_BUF_SEG2_LEN3;
iBestTimeQuanta -= (3 + 1);
}
if (iBestTimeQuanta & 0x1) { // odd
iBestTimeQuanta /= 2; // PROP_SEG and PSEG1 to achieve time segment 1
ulBestPrescaler |= iBestTimeQuanta; // set propogation bit time (1 more than phase buffer segment 1)
iBestTimeQuanta--;
ulBestPrescaler |= (iBestTimeQuanta << 19); // set phase buffer segment 1
}
else { // even
iBestTimeQuanta /= 2; // PROP_SEG and PSEG1 to achieve time segment 1 and phase buffer segment 1
iBestTimeQuanta--;
ulBestPrescaler |= ((iBestTimeQuanta << 19) | (iBestTimeQuanta));// set equal propogation bit times
}
return (RJW_2 | ulClockSourceFlag | ulBestPrescaler); // initialise the CAN controller with the required speed and parameters
}
 
I recently used the bxCAN in the ST-Micro STM32
Ok, thanks. I did a STM32F205F project on CAN, no complaints here it went quite smoothly (only 3 tx buffers but not an issue).

No real complaints on the Freescale either, the Teensy3 and 3.1 have been great.
 
Ok, thanks. I did a STM32F205F project on CAN, no complaints here it went quite smoothly (only 3 tx buffers but not an issue).

The bxCAN has powerful filtering banks (although keeping track of which one belongs to which ID is a bit complicated) but their mailboxes don't have the capability to be used as either Tx or RX (or a remote frame buffer automatically mutating from tx to rx) as the FlexCAN's can.

I don't think that the K20 on the Teensy 3 has CAN so I suppose you only use CAN on the Teensy 3.1 (?)

Regards

Mark
 
CAN bus speed
Ok. Thanks again! I pulled the segment timings from Freescale directly. Baud rates I cut down, dropping slow and odd ones to keep it simple.

You have probably noticed there is variation between CANbus standards as to "optimal" sample point.
 
Thanks for making this library. It is a good start for CAN support.

I have added support for extended IDs to write (it only supports standard ID as-is) and I have also added very basic RX filter support. Since I'm a newb at this, can you tell me how to share this? Simply post it here?
 
Ok. If I see that I will contribute more I will do the GitHub process but for this, it will be quicker to just post it.

The modified header file is this:
Code:
// -------------------------------------------------------------
// a simple Arduino Teensy3.1 CAN driver
// by teachop
//
#include <Arduino.h>

typedef struct CAN_message_t
{
  uint32_t id; // can identifier
  uint8_t ext; // identifier is extended
  uint8_t len; // length of data
  uint16_t timeout; // milliseconds, zero will disable waiting
  uint8_t buf[8];
} CAN_message_t;

typedef struct CAN_filter_t
{
  uint8_t rtr;
  uint8_t ext;
  uint32_t id;
} CAN_filter_t;

// -------------------------------------------------------------
class FlexCAN {
private:
  struct CAN_filter_t default_mask;

public:
  FlexCAN(uint32_t baud = 125000);
  void begin(const CAN_filter_t &mask);
  inline void begin() { begin(default_mask);}
  void set_filter(const CAN_filter_t &filter, uint8_t n);
  void end(void);
  int available(void);
  int write(const CAN_message_t &msg);
  int read(CAN_message_t &msg);

};

As you can see I have defined a new filter structure which is used for both the mask and the filter and allows filtering on the ID, the IDE and the RTR. I have modified begin to have an optional filter mask. This is a global mask that applies to all the RX FIFO filters. The default mask simply allows everything in (no filtering). And I have added function to set the filters: the parameters are the filter and the filter number. There are 8 filters by default and I haven't changed that.

When using filters, you need to set all of them because there is no initialization and they will be randomly set. If you don't need 8 filters, simply duplicate one filter for the unused ones.

The creator addition is simply to initialize the default mask (last few lines):
Code:
FlexCAN::FlexCAN(uint32_t baud)
{
  // set up the pins, 3=PTA12=CAN0_TX, 4=PTA13=CAN0_RX
  CORE_PIN3_CONFIG = PORT_PCR_MUX(2);
  CORE_PIN4_CONFIG = PORT_PCR_MUX(2);// | PORT_PCR_PE | PORT_PCR_PS;
  // select clock source
  SIM_SCGC6 |=  SIM_SCGC6_FLEXCAN0;
  FLEXCAN0_CTRL1 |= FLEXCAN_CTRL_CLK_SRC;

  // enable CAN
  FLEXCAN0_MCR |=  FLEXCAN_MCR_FRZ;
  FLEXCAN0_MCR &= ~FLEXCAN_MCR_MDIS;
  while(FLEXCAN0_MCR & FLEXCAN_MCR_LPM_ACK)
    ;
  // soft reset
  FLEXCAN0_MCR ^=  FLEXCAN_MCR_SOFT_RST;
  while(FLEXCAN0_MCR & FLEXCAN_MCR_SOFT_RST)
    ;
  // wait for freeze ack
  while(!(FLEXCAN0_MCR & FLEXCAN_MCR_FRZ_ACK))
    ;
  // disable self-reception
  FLEXCAN0_MCR |= FLEXCAN_MCR_SRX_DIS;

  //enable RX FIFO
  FLEXCAN0_MCR |= FLEXCAN_MCR_FEN;

  // segment timings from freescale loopback test
  if ( 250000 == baud ) {
    FLEXCAN0_CTRL1 = (FLEXCAN_CTRL_PROPSEG(2) | FLEXCAN_CTRL_RJW(1)
                    | FLEXCAN_CTRL_PSEG1(3) | FLEXCAN_CTRL_PSEG2(3) | FLEXCAN_CTRL_PRESDIV(15));
  } else if ( 500000 == baud ) {
    FLEXCAN0_CTRL1 = (FLEXCAN_CTRL_PROPSEG(2) | FLEXCAN_CTRL_RJW(1)
                    | FLEXCAN_CTRL_PSEG1(3) | FLEXCAN_CTRL_PSEG2(3) | FLEXCAN_CTRL_PRESDIV(7));
  } else if ( 1000000 == baud ) {
    FLEXCAN0_CTRL1 = (FLEXCAN_CTRL_PROPSEG(3) | FLEXCAN_CTRL_RJW(0)
                    | FLEXCAN_CTRL_PSEG1(0) | FLEXCAN_CTRL_PSEG2(1) | FLEXCAN_CTRL_PRESDIV(5));
  } else { // 125000
    FLEXCAN0_CTRL1 = (FLEXCAN_CTRL_PROPSEG(2) | FLEXCAN_CTRL_RJW(2)
                    | FLEXCAN_CTRL_PSEG1(3) | FLEXCAN_CTRL_PSEG2(3) | FLEXCAN_CTRL_PRESDIV(31));
  }

  // Default mask is ignore everything
  default_mask.rtr = 0;
  default_mask.ext = 0;
  default_mask.id = 0;
}

Begin looks like this:
Code:
void FlexCAN::begin(const CAN_filter_t &mask)
{
  FLEXCAN0_RXMGMASK = 0;

  //enable reception of all messages that fit the mask
  if (mask.ext)
    FLEXCAN0_RXFGMASK = ((mask.rtr?1:0) << 31) | ((mask.ext?1:0) << 30) | ((mask.id & FLEXCAN_MB_ID_EXT_MASK) << 1);
  else
    FLEXCAN0_RXFGMASK = ((mask.rtr?1:0) << 31) | ((mask.ext?1:0) << 30) | (FLEXCAN_MB_ID_IDSTD(mask.id) << 1);

  // start the CAN
  FLEXCAN0_MCR &= ~(FLEXCAN_MCR_HALT);
  // wait till exit of freeze mode
  while(FLEXCAN0_MCR & FLEXCAN_MCR_FRZ_ACK);

  // wait till ready 
  while(FLEXCAN0_MCR & FLEXCAN_MCR_NOT_RDY);

  //set tx buffers to inactive
  for (int i = txb; i < txb + txBuffers; i++) {
    FLEXCAN0_MBn_CS(i) = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_INACTIVE);
  }
}

This is actually not quite correct because I'm using the mask extended field to establish if the ID is extended or not but you may want to mask the extended bit and still filter only the standard messages. So that would require an additional parameter.

set_filter simply sets one of the 8 filters and sets the fields in the correct place depending on whether or not this uses an extended ID:
Code:
void FlexCAN::set_filter(const CAN_filter_t &filter, uint8_t n)
{
  if (filter.ext)
    FLEXCAN0_IDFLT_TAB(n) = ((filter.rtr?1:0) << 31) | ((filter.ext?1:0) << 30) | ((filter.id & FLEXCAN_MB_ID_EXT_MASK) << 1);
  else
    FLEXCAN0_IDFLT_TAB(n) = ((filter.rtr?1:0) << 31) | ((filter.ext?1:0) << 30) | (FLEXCAN_MB_ID_IDSTD(filter.id) << 1);
}

And this is the section of code from write that handles the extended IDs:
Code:
  // transmit the frame
  FLEXCAN0_MBn_CS(buffer) = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_INACTIVE);
  if(msg.ext) {
    FLEXCAN0_MBn_ID(buffer) = (msg.id & FLEXCAN_MB_ID_EXT_MASK);
  } else {
    FLEXCAN0_MBn_ID(buffer) = FLEXCAN_MB_ID_IDSTD(msg.id);
  }
  FLEXCAN0_MBn_WORD0(buffer) = (msg.buf[0]<<24)|(msg.buf[1]<<16)|(msg.buf[2]<<8)|msg.buf[3];
  FLEXCAN0_MBn_WORD1(buffer) = (msg.buf[4]<<24)|(msg.buf[5]<<16)|(msg.buf[6]<<8)|msg.buf[7];
  if(msg.ext) {
    FLEXCAN0_MBn_CS(buffer) = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_ONCE)
                      | FLEXCAN_MB_CS_LENGTH(msg.len) | FLEXCAN_MB_CS_SRR | FLEXCAN_MB_CS_IDE;
  } else {
    FLEXCAN0_MBn_CS(buffer) = FLEXCAN_MB_CS_CODE(FLEXCAN_MB_CODE_TX_ONCE)
                      | FLEXCAN_MB_CS_LENGTH(msg.len);
  }

Let me know if you need more information. This code has been tested and validated with extended IDs.
 
I wanted to add that your note on your GitHub repo that you need a 3.3V transceiver is not quite correct. A 5V transceiver will also work since the K20 on the Teensy is 5V tolerant and most 5V transceivers will work fine with 3.3V input signal. That's what I'm using because that fits better with my setup.
 
I wanted to add that your note on your GitHub repo that you need a 3.3V transceiver is not quite correct. A 5V transceiver will also work since the K20 on the Teensy is 5V tolerant and most 5V transceivers will work fine with 3.3V input signal. That's what I'm using because that fits better with my setup.

But will it work powered with 3.3V? Note that there is no 5V source on Tennsy when it is not connected via USB.
 
set_filter simply sets one of the 8 filters and sets the fields in the correct place depending on whether or not this uses an extended ID:
Code:
void FlexCAN::set_filter(const CAN_filter_t &filter, uint8_t n)
{
  if (filter.ext)
    FLEXCAN0_IDFLT_TAB(n) = ((filter.rtr?1:0) << 31) | ((filter.ext?1:0) << 30) | ((filter.id & FLEXCAN_MB_ID_EXT_MASK) << 1);
  else
    FLEXCAN0_IDFLT_TAB(n) = ((filter.rtr?1:0) << 31) | ((filter.ext?1:0) << 30) | (FLEXCAN_MB_ID_IDSTD(filter.id) << 1);
}

Just a small note. Arduino API style guide tells to use camel case function names, so setFilter shall be used rather than set_filter.

It is a minor thing, but let's do things right while it is not too late.
 
And my setup does have a regulated 5V supply which powers both the Teensy (USB power trace is cut on the Teensy) and the CAN transceiver, among other things. That is why it is much easier for me to do it that way in addition to the much greater (and cheaper) selection of 5V transceivers.
 
Mundane topic along these lines... Anybody have an issue with this:
I run the code through "astyle -A10 -s2" as a way to stay consistent on whitespace matters. It is 1TBS with 2 spaces per indent. Seems pretty Arduino-like to me. It doesn't cause much churn as that is **mostly** what I was already shooting for. Do it with this filtering / extended commit, and we keep it maintained from there.
 
The contributions for receive filtering and extended identifier support from this post have been added to the repository. Thanks JeanBelanger!

If you can, please help test as my setup (pure spit and bailing wire) is standard identifier until I have some free time to upgrade the other nodes (maybe this weekend).

Driver is here:
https://github.com/teachop/FlexCAN_Library

PS: I am not going to run astyle on the files until we have a few days to ring this out. The noise from the whitespace changes make it harder to see what we are working on. So that will come later...
 
Last edited:
Noticed one thing. You need to wrap the FlexCAN.h contents into

#ifndef __FLEXCAN_H
#define __FLEXCAN_H
...
#endif // __FLEXCAN_H

Otherwise there will be redeclaration errors if you eg. use the FlexCAN library in another library. See the kinetis_flexcan.h if what I'm saying is not clear.
 
BTW, I did a quick test of the filtering and it seems to be working fine (at least in my case - 3 filters, standard ID)
 
Last edited:
Teachop,

given most of the functions of your library seem to work fine, maybe you could add some tag in GitHub to mark a revision that we know is working before any further updates. Some beta_0.1 or something. What do you think?
 
Back
Top