tonton81 / FlexCAN_T4

FlexCAN (CAN 2.0 / CANFD) Library for Teensy 3.x and 4.0
https://forum.pjrc.com/threads/56035-FlexCAN_T4-FlexCAN-for-Teensy-4
MIT License
195 stars 65 forks source link

CAN-FD Interrupts #56

Closed flybrianfly closed 1 year ago

flybrianfly commented 1 year ago

I'm trying to receive CAN-FD messages in an interrupt context without needing to call events in the loop.

Example sender code is:

#include <FlexCAN_T4.h>
#include <elapsedMillis.h>

FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_128> can;
elapsedMillis t_ms;
CANFD_message_t msg;

void setup() {
  can.begin();
  CANFD_timings_t cfg;
  cfg.clock = CLK_80MHz;
  cfg.baudrate = 1000000;
  cfg.baudrateFD = 8000000;
  cfg.propdelay = 190;
  cfg.bus_length = 1;
  cfg.sample = 70;
  can.setRegions(64);
  can.setBaudRate(cfg);
  msg.id = 0x01;
  msg.len = 5;
  Serial.begin(115200);
  delay(1000);
}

void loop() {
  if (t_ms >= 1000) {
    t_ms = 0;
    msg.buf[0]++;
    can.write(msg);
  }
}

Receiver code is:

#include <FlexCAN_T4.h>
FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_64> can;
CANFD_message_t msg;

void setup(void) {
  Serial.begin(115200);
  delay(400);
  can.begin();
  CANFD_timings_t cfg;
  cfg.clock = CLK_80MHz;
  cfg.baudrate = 1000000;
  cfg.baudrateFD = 8000000;
  cfg.propdelay = 190;
  cfg.bus_length = 1;
  cfg.sample = 70;
  can.setRegions(64);
  can.setBaudRate(cfg);
  can.setMBFilter(REJECT_ALL);
  can.enableMBInterrupts();
  can.onReceive(MB0, isr);
  can.setMBFilterRange(MB0, 0x00, 0xFF);
  can.mailboxStatus();
}

void isr(const CANFD_message_t &msg) {
  Serial.println("RECEIVED INT");
}

void loop() {
  if (can.read(msg)) {
    Serial.println("RECEIVED LOOP");
  }
}

My expectation is that I would see "RECEIVED INT" in the serial monitor, but I don't. I don't really care to do anything fancy with the mailboxes, I just want to receive all of the messages in the interrupt context because I plan to use the loop to do some other tasks. I would use the FIFO, but my understanding is that CAN-FD doesn't have a FIFO buffer (even though there is an enableFIFO method, so maybe my assumption is wrong). What do I need to do to make this work?

tonton81 commented 1 year ago

try to run can.enableMBInterrupts() last, changing mailbox configuration will by default reset the interrupt flag for it

enableFIFO is for the legacy CAN2.0

The FD on the t4.x has no FIFO

flybrianfly commented 1 year ago

Thanks, I tried your suggestion, the following, with the same results - no interrupts driven without events in the loop.

#include <FlexCAN_T4.h>
FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_64> can;
CANFD_message_t msg;

void setup(void) {
  Serial.begin(115200);
  delay(400);
  can.begin();
  CANFD_timings_t cfg;
  cfg.clock = CLK_80MHz;
  cfg.baudrate = 1000000;
  cfg.baudrateFD = 8000000;
  cfg.propdelay = 190;
  cfg.bus_length = 1;
  cfg.sample = 70;
  can.setRegions(64);
  can.setBaudRate(cfg);
  can.setMBFilter(REJECT_ALL);
  can.onReceive(MB0, isr);
  can.setMBFilterRange(MB0, 0x00, 0xFF);
  can.enableMBInterrupts();
  can.mailboxStatus();
}

void isr(const CANFD_message_t &msg) {
  Serial.println("RECEIVED INT");
}

void loop() {
  if (can.read(msg)) {
    Serial.println("RECEIVED LOOP");
  }
}
tonton81 commented 1 year ago

are the terminations/transceiver correct? what happens if you transmit a few frames then check mailboxStatus(), if mailboxStatus shows frames stuck in TX it's a transceiver link issue

flybrianfly commented 1 year ago

Terminations and transceivers should be correct, everything works fine if I poll in loop using read or events.

If I run the following receiver code and check mailboxStatus in loop once per second, I can see the MB0 to MB3 fill. Notice that this has the mailbox setup and enableMBInterrupts removed.

#include <FlexCAN_T4.h>
FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_64> can;
CANFD_message_t msg;

void setup(void) {
  Serial.begin(115200);
  delay(400);
  can.begin();
  CANFD_timings_t cfg;
  cfg.clock = CLK_80MHz;
  cfg.baudrate = 1000000;
  cfg.baudrateFD = 8000000;
  cfg.propdelay = 190;
  cfg.bus_length = 1;
  cfg.sample = 70;
  can.setRegions(64);
  can.setBaudRate(cfg);
  can.mailboxStatus();
}

void isr(const CANFD_message_t &msg) {
  Serial.println("RECEIVED INT");
}

void loop() {
  can.mailboxStatus();
  delay(1000);
}

Screenshot from 2022-11-27 09-00-44

However, if I run the code that I'm trying to get to work and fire a MB interrupt, I don't see the MB fill.

#include <FlexCAN_T4.h>
FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_64> can;
CANFD_message_t msg;

void setup(void) {
  Serial.begin(115200);
  delay(400);
  can.begin();
  CANFD_timings_t cfg;
  cfg.clock = CLK_80MHz;
  cfg.baudrate = 1000000;
  cfg.baudrateFD = 8000000;
  cfg.propdelay = 190;
  cfg.bus_length = 1;
  cfg.sample = 70;
  can.setRegions(64);
  can.setBaudRate(cfg);
  can.setMBFilter(REJECT_ALL);
  can.onReceive(MB0, isr);
  can.setMBFilterRange(MB0, 0x00, 0xFF);
  can.enableMBInterrupts();
  can.mailboxStatus();
}

void isr(const CANFD_message_t &msg) {
  Serial.println("RECEIVED INT");
}

void loop() {
  can.mailboxStatus();
  delay(1000);
}

Screenshot from 2022-11-27 08-55-54

It almost seems like the onReceive interrupt is firing, but not propagating to the ISR.

tonton81 commented 1 year ago

curious, what happens if you individually enable the mailboxes ? (no s) enableMBinterrupt(MB)

flybrianfly commented 1 year ago

No change.

#include <FlexCAN_T4.h>
FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_64> can;
CANFD_message_t msg;

void setup(void) {
  Serial.begin(115200);
  delay(400);
  can.begin();
  CANFD_timings_t cfg;
  cfg.clock = CLK_80MHz;
  cfg.baudrate = 1000000;
  cfg.baudrateFD = 8000000;
  cfg.propdelay = 190;
  cfg.bus_length = 1;
  cfg.sample = 70;
  can.setRegions(64);
  can.setBaudRate(cfg);
  can.setMBFilter(REJECT_ALL);
  can.onReceive(MB0, isr);
  can.setMBFilterRange(MB0, 0x00, 0xFF);
  can.enableMBInterrupt(MB0);
  can.mailboxStatus();
}

void isr(const CANFD_message_t &msg) {
  Serial.println("RECEIVED INT");
}

void loop() {
  can.mailboxStatus();
  delay(1000);
}

Screenshot from 2022-11-27 13-58-54

tonton81 commented 1 year ago

what about without filters? if the mailbox shows empty it is probably being filtered out, no message means no interrupt, maybe need a debug line temporarily in the isr to see if thats firing as well (flexcan_interrupt), i dont have the stuff hooked up here yet for testing

flybrianfly commented 1 year ago

I tried the following and am getting the same result:

#include <FlexCAN_T4.h>
FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_64> can;
CANFD_message_t msg;

void setup(void) {
  Serial.begin(115200);
  delay(400);
  can.begin();
  CANFD_timings_t cfg;
  cfg.clock = CLK_80MHz;
  cfg.baudrate = 1000000;
  cfg.baudrateFD = 8000000;
  cfg.propdelay = 190;
  cfg.bus_length = 1;
  cfg.sample = 70;
  can.setRegions(64);
  can.setBaudRate(cfg);
  can.onReceive(MB0, isr);
  can.enableMBInterrupt(MB0);
  can.mailboxStatus();
}

void isr(const CANFD_message_t &msg) {
  Serial.println("RECEIVED INT");
}

void loop() {
  can.mailboxStatus();
  delay(1000);
}

Again, if I remove the onReceive(MB0, isr) and enableMBInterrupt(MB0), then I can see the mail boxes fill with the mailboxStatus message.

flybrianfly commented 1 year ago

Should I pay attention to the version of Teensyduino I'm using? Wonder when it was added to the interrupt vector table...

tonton81 commented 1 year ago

not sure its been along time since i worked on this, you could probably insert a serial print in the flexcan_interrupt just to see if it fires. the interrupt flags are fixed among 2 32 bit registers (identical ones used in 2.0 mode) so if theyre enabled and the ISR is not firing, then it could be an interrupt vector issue i guess

flybrianfly commented 1 year ago

Thanks, I think I figured out the issue. flexcan_interrupt fires, but unlike the CAN2.0 implementation of struct2queueRx, the CAN-FD implementation of struct2queueRx is missing the call to mbCallbacks((FLEXCAN_MAILBOX)msg.mb, msg). It looks to me like the only way to get the mbCallbacks to fire in the CAN-FD implementation is to call the events method, which makes sense based on what I'm seeing in testing. I think if I replicate the CAN2.0 approach in the CAN-FD implementation of struct2queueRx, it should fire the onReceive callback correctly.

tonton81 commented 1 year ago

Merged fix, thank you