CAN bus interrupt problem (MBED 5.15)

Hi
I’ve been attempting to use the CAN bus with interrupts (using .attach).
However whenever I attach my handler, it appears that I can not read the CAN bus.
And after a while, MBED crashes with a Mutex error.

I found this link over on github which describes a problem similar but this is a 3 yr old bug that appears open. CAN read doesn't work when the interrupt is attached. · Issue #5267 · ARMmbed/mbed-os · GitHub

Can anyone using STM32 give me a simplistic piece of code that works with CAN RX interrupts? Much appreciated.
Bill

Hello Bill,

Actually, you are allowed to attach a CAN RX interrupt handler (ISR) and for example set a flag indicating that data have been received. But, unfortunately, you must not call the read method in an ISR anymore because it is calling the Mutex lock method an according to this explanation Mbed OS will treat that as a fatal system error.
I think it’s simpler to call the read method in the main’s while loop and poll for new data, as in this example, rather than to set a flag in the CAN RX interrupt handler and then clear it in the main.
Best regards, Zoltan

Correct. That’s what I do. But that is still crashing as in the github report.

Sorry, I was focusing on RX. Yes, you are right. That example is wrong. The TX ISR calls the write method which is not allowed because the write method, like the read one, calls the Mutex lock method.
Another option, which I forgot to mention in my first post, is to use EventQueue rather than interrupt:

...
CAN             can(PA_11, PA_12);
CANMessage      rxMsg;
EventQueue      eventQueue;
Thread          t;
...
void onCanReceived(void)
{
    can.read(&rxMsg);
   ...
}
...
int main()
{
    t.start(callback(&eventQueue, &EventQueue::dispatch_forever));
    can.attach(eventQueue.event(onCanReceived));
    ...

    while(1) {
        ...
    }   
}

Or you can call EventQueue from the interrupt handler:

...
CAN             can(PA_11, PA_12);
CANMessage      rxMsg;
EventQueue      eventQueue;
Thread          t;
...
void onCanReceived(void)
{
     eventQueue.call(can.read(&rxMsg));  // No Mutex method is called. Execution of can.read is deferred to thread t.
     ...
}
...
int main()
{
    t.start(callback(&eventQueue, &EventQueue::dispatch_forever));
    can.attach(onCanReceived, CAN::RxIrq);  // attach interrupt handler
    ...

    while(1) {
        ...
    }   
}
1 Like

Nope. Same issue in MBED5.15. Crashes on interrupt. The bug I posted in the original link shows the issue is still open. Your syntax is mostly clean and it makes sense but I have tried a variety of implementations such as yours and still crashes. I think the bug is still there.
Did you happen to try your code on an STM32 board? I suspect you’ll see the same bug.

Hello,

yes, that is here for a long time.
The Interrupt flag can be clean only by CAN::read() method but this method is covered by the Mutex so It can not be used in interupt context. When CAN::read() is not called in the interrupt handler then the program will stay in the interrupt for ever.
The EventQueue leads to the ISR queue overflow.

BR, Jan

I tested both methods from my previous post today on a Bluepill board (STM32F103C8T6) and they both did work fine. No runtime error occurred and all messaged were received correctly when using CAN frequency 500000Hz.
I built my test programs offline with Mbed OS 6.4.0.

Best regards, Zoltan

Ok, thanks. Sounds like 6.x might have the fix I need but it is not supported on my variant of ST board.
Can you revert your mbed-os to 5.15 to see if it fails? That would close this case in my mind.

I appreciate your effort on this!
Thank you
Bill

That’s what it feels like. Was going to pull out the debugger but my senses were the ISR was stuck…
Thanks for posting this.
Bill

Ok, after understanding the root cause (stuck interrupt) I took out a BFH and fixed it quickly with the following…

volatile uint32_t can_rx_msg = 0;
uint32_t can_ier;

void onCanReceived(void)
{
can_ier = *(uint32_t *)0x40006414;
*(uint32_t *)0x40006414 = 0;
LED2 = LED_ON;
_cmdQ.AddMsg(__PRI_NORM,cmd_can_read_all,0);
can_rx_msg++;
}

void ecu_init(enum control_port)
{
can1.frequency(500000);
can1.filter(0x700, 0x700, CANStandard);
can_initialized = true;
can1.attach(onCanReceived);
}

//Snip from USER context code
ecu_sim(CAN1_PORT); //This actually does the can read operation
LED2 = LED_OFF;//Debug
*(uint32_t *)0x40006414 = can_ier;//restore ier to CAN

Working great now. No more unneeded polling on CAN and back to bigger things to figure out

Wonder what mbed & ST did in the 6.x OS to fix this?

Thanks again to all who helped!
Bill

I’m glad you solved it. In case you need to build it with Mbed OS 6.4.0 (maybe also with any other Mbed OS 6.X.X revision) add the following to the mbed_app.json configuration file:

{
    "target_overrides": {
        "*": {
            "platform.callback-nontrivial": true
        }
    }
}

Otherwise the project will not compile.

Best regards, Zoltan

1 Like

It works fine for me

//NUCLEO_F446RE

#include "mbed.h"

#define CAN_RX                      PB_12
#define CAN_TX                      PB_13
#define CAN_DATA_RATE               250000

CAN can(CAN_RX, CAN_TX, CAN_DATA_RATE);  
CANMessage       rxMsg;

Thread thread;

void printMsg(CANMessage& msg){
    printf("  ID      = 0x%.3x\r\n", msg.id);
    printf("  Type    = %d\r\n", msg.type);
    printf("  Format  = %d\r\n", msg.format);
    printf("  Length  = %d\r\n", msg.len);
    printf("  Data    =");
    for(int i = 0; i < msg.len; i++)
        printf(" 0x%.2X", msg.data[i]);
    printf("\r\n");
 
}
void can_handler() {
    while(1){
         while(can.read(rxMsg) == 1) {
                if(can.rderror()){
                    printf("\ncan error\n");
                    can.reset();
                }else{
                    printMsg(rxMsg);           
                } 
                printf("can ping\n");          
        } 
    }
}

int main() {

    thread.start(can_handler);
  
        printf("\n***  CAN Handler with Thread ***\n");

        while(1){
             ThisThread::sleep_for(500);
             printf("main ping\n");  
        }

}

Hello,

thanks for sharing, but your solution is done via periodical polling of CAN::read(…), however this topic was about how to do this via interrupts.

BR, Jan

Here you can find a tip. However, its for Mbed 6. Maybe it works for Mbed 5 too.