Teensy4.1 CAN FD throughput issues

AndyA

Well-known member
I have a project that needs to log all CAN-FD packets on a bus. It is working fine for low data rates (100Hz packets) but as soon as I turn the data rate up I get the overrun flag set and drop data.

The exact same logging code but reading from an external SPI CAN-FD device, which should be significantly slower, can keep up with 400Hz packets.
This makes me suspect it's some sort of buffering issue, I just need to turn the correct feature on but I can't see what that is.

Currently data is read in a loop in a teensyThread that queues it up into a buffer. This buffer is then written to the SD card in a different thread. The buffer is large enough that it's not overflowing.
I know polling in a thread like this isn't ideal but works fine for the SPI device, it could be that the SPI device has more buffering.
Moving to an RxInterrupt based solution would be fine but when I try this I don't get any interrupts, again hopefully something simple that I've missed.

It's part of a larger project so hard to give full code but I think I've included all the relevant bits below. setup(); calls the init function and then starts the thread.

Code:
FlexCAN_T4FD<CAN3, RX_SIZE_1024, TX_SIZE_16> IntCAN;

void initInternalCAN() {
  IntCAN.begin();

  CANFD_timings_t config;
  config.clock = CLK_24MHz;
  config.baudrate = 1000000;
  config.baudrateFD = 2000000;
  config.propdelay = 190;
  config.bus_length = 1;
  config.sample = 85;

  IntCAN.setBaudRate(config);
  IntCAN.setRegions(64);
  IntCAN.onReceive(internalCANRxIrq);
}

void internalCANRxIrq(const CANFD_message_t &packetIn){
// this never gets called
}

void CAN_Thread() {
  CANFD_message_t IntFrameIn;
  IntCAN.events();
  Serial.println("CAN thread running");
  while (1) {
    if (IntCAN.read(IntFrameIn)) {
      if (readIntCan(IntFrameIn,CANQueue+CANQueue_WritePtr)) {
         // increase packet queue tracking - queue never overflows.
      }
    } else if (useExternal && ExtCan.receive (ExtFrameIn)) {
      if (readExtCan(ExtFrameIn,CANQueue+CANQueue_WritePtr)) {
         // increase packet queue tracking - queue never overflows.
      }
    } else {
      threads.yield();
    }
  }
}

bool readIntCan(CANFD_message_t &packetIn, RL_CANPacket_t *PacketConverted) {
//  Serial.println("Internal CAN rx");
  if (packetIn.esi || packetIn.flags.overrun) {
    if (packetIn.flags.overrun)
      Serial.println("Overrun"); // hit this line with packet rates over 100Hz
    return false;
  }
// convert packet data to common logging structure
// ....
//
  return true;
}

bool readExtCan(ExternalCANPacketStruct &packetIn, RL_CANPacket_t *PacketConverted) {
// convert packet data to common logging structure
// ....
//
  return true;
}
 
Sort of half fixed it.
The interrupts weren't working because I needed to add IntCAN.enableMBInterrupts(); to the init function and then call IntCAN.events(); during the while loop.
After doing that and switching to an interrupt based system it not complaining about overruns but it ends up running behind so long term it's not sustainable. I can turn the packets off and it keeps processing for a while until it catches up.

Is there a way to make it truly interrupt driven rather than having to call a function to trigger the interrupt? This seems more like a convoluted polling system to me.

Also it still doesn't explain why I can use the same code for an external SPI device and that can keep up just fine. It's in the same loop so it's not like it's related to the speed of the loop.


Edit - Fixed.
I used void ext_outputFD1(const CANFD_message_t &msg);. I still need to call enableMBInterrupts() during setup but no longer need to call the events() function in the loop. It is now an actual interrupt rather than some pseudo interrupt that other than more buffering is no better than polling.
 
Last edited:
Back
Top