2 years, 9 months ago.

How to clear CAN bus interrupt flag (Missing CAN interrupt documentation.)

I use STM32F429I discovery board for a CAN bus application. It workd fine until I started to use interrupts. Rx interrupt is OK, reading the actual incoming message automatically clears the interrupt flag. But there is no high level method to clear other interrupt flags. So I got unstoppable Tx interrupts after only one send, and the same on one error. Other interrupts, like wakeup or ready, never fires. Can someone point me to the documentation? Official mbed CAN doc is only about Rx interrupt.

#include "mbed.h"

#define SERIAL_BAUD 921600
#define CAN_BAUD 125000

CAN can1(PA_11, PA_12);
DigitalOut ledGreen(LED1);
DigitalOut ledRed(LED2);
Serial serial1(PA_9, PA_10);
InterruptIn userButton(USER_BUTTON);
Ticker aliveTicker;

void sendCanMessage();

void serialOut(char *message)
{
    int i = -1;
    while (message[++i] != '\0') serial1.putc(message[i]);
}

void isrButton()
{
    char *message = (char *)"BUTTON IRQ\r\n\0";
    serialOut(message);
    sendCanMessage();
}

void alive()
{
    ledGreen = !ledGreen;
    char *message = (char *)"Alive\r\n\0";
    serialOut(message);
}

void isrCanRx()
{
    ledRed = 1;
    CANMessage canMessage;
    can1.read(canMessage); //Clears interrupt flag
    char *message = (char *)"CAN RX IRQ\r\n\0";
    serialOut(message);
    ledRed = 0;
}

void isrCanTx()
{
    char *message = (char *)"CAN TX IRQ\r\n\0";
    serialOut(message);
}

void isrCanError()
{
    char *message = (char *)"CAN WERR IRQ\r\n\0";
    serialOut(message);
}

void isrCanDataOverrun()
{
    char *message = (char *)"CAN DAO IRQ\r\n\0";
    serialOut(message);
}

void isrCanWakeup()
{
    char *message = (char *)"CAN WAKEUP IRQ\r\n\0";
    serialOut(message);
}

void isrCanErrorPassive()
{
    char *message = (char *)"CAN EP IRQ\r\n\0";
    serialOut(message);
}

void isrCanArbitrationLost()
{
    char *message = (char *)"CAN ARB IRQ\r\n\0";
    serialOut(message);
}

void isrCanBusError()
{
    char *message = (char *)"CAN BUSERR IRQ\r\n\0";
    serialOut(message);
}

void isrCanReady()
{
    char *message = (char *)"CAN READY IRQ\r\n\0";
    serialOut(message);
}

void sendCanMessage()
{
    CANMessage msgOut;
    msgOut.format = CANExtended;
    msgOut.type = CANData;
    msgOut.len = 2;
    msgOut.id = 0x100000;
    msgOut.data[0] = 0xA;
    msgOut.data[1] = 0xB;
    can1.write(msgOut);
}

void setup()
{
    //Serial
    serial1.baud(SERIAL_BAUD);
    serial1.printf("START\r\n");

    //CAN
    can1.frequency(CAN_BAUD);

    //Interrupts
    userButton.fall(&isrButton);
    can1.attach(&isrCanRx, CAN::RxIrq);
    can1.attach(&isrCanTx, CAN::TxIrq);
    can1.attach(&isrCanError, CAN::EwIrq);
    can1.attach(&isrCanDataOverrun, CAN::DoIrq);
    can1.attach(&isrCanWakeup, CAN::WuIrq);
    can1.attach(&isrCanErrorPassive, CAN::EpIrq);
    can1.attach(&isrCanArbitrationLost, CAN::AlIrq);
    can1.attach(&isrCanBusError, CAN::BeIrq);
    can1.attach(&isrCanReady, CAN::IdIrq);

    aliveTicker.attach(&alive, 1.0);
}

int main()
{
    setup();
    while (true)
    {

    }
}

3 Answers

10 months, 2 weeks ago.

Hi, i'm having the same problem now, and i can't find the solution to work properly. Can you provide the piece of code that you use to clear the RxInterrupt Flag? Thanks in advance!

Edit: here is my source code. This is the error that i get in run-time: "Message: CMSIS-RTOS error: ISR Queue overflow". This is happenning probably because interrupt flag never gets cleared and the isr routine keeps being called. The only workaround until now was to subclasse the CAN API and remove the Mutex lock of can.read(), being able to call it from the ISR.

#include "mbed.h"
#include "CANMsg.h"

#define CAN_RF0R        (*((volatile unsigned long *)0x4000640C))
#define CAN_FLAG_FMP0   ((uint32_t)0x12000003) /*!< FIFO 0 Message Pending Flag */
#define CAN_RF0R_RFOM0 ((uint8_t)0x20) /*!<Release FIFO 0 Output Mailbox */
#define CAN_RF1R        (*((volatile unsigned long *)0x40006410))
#define CAN_IER         (*((volatile unsigned long *)0x40006414))

Serial              pc(PA_9, PA_10);
//CAN                 can1(PA_11, PA_12);
CAN                 can(PB_8, PB_9, 1000000);    // CAN Rx pin name, CAN Tx pin name
CANMsg              rxMsg;
CANMsg              txMsg;
DigitalOut          led(LED_PIN);
Timer               timer;
uint8_t             counter = 0;
AnalogIn            analogIn(A0);
float               voltage;

Thread eventThread;
EventQueue queue(1024);

/**
 * @brief   Prints CAN msg to PC's serial terminal
 * @note
 * @param   CANMessage to print
 * @retval
 */
void printMsg(CANMessage& msg) {
    pc.printf("  ID      = 0x%.3x\r\n", msg.id);
    pc.printf("  Type    = %d\r\n", msg.type);
    pc.printf("  Format  = %d\r\n", msg.format);
    pc.printf("  Length  = %d\r\n", msg.len);
    pc.printf("  Data    =");
    for(int i = 0; i < msg.len; i++)
        pc.printf(" 0x%.2X", msg.data[i]);
    pc.printf("\r\n");
}

void interruptHandler()
{
    led != led;
    pc.printf("isr\r\n");
    can.read(rxMsg);        // that should clear the interrupt flag
    printMsg(rxMsg);
}

void isr()
{
    queue.call(&interruptHandler);    // with mbed-os, can.read() can't be called from an isr context
}

/**
 * @brief   Main
 * @note
 * @param
 * @retval
 */
int main(void)
{
    pc.baud(9600);          // set Serial speed
    
    eventThread.start(callback(&queue, &EventQueue::dispatch_forever));
    // wrap calls in queue.event to automatically defer to the queue's thread
    can.attach(&interruptHandler, CAN::RxIrq);
    
    while(1) {
        pc.printf("waiting...\r\n");
        wait(0.5f);
    }
}

STM32 HAL (the layer below mbed) calls your Rx interrupt. The HAL part should clear the interrupt flag after your ISR returns. Could you please share your CAN code? Maybe we could spot the error. I never have problem with Rx interrupt.

posted by Mark Peter Vargha 09 Jan 2019

I was able to correct this situation disabling the message pending interrupt (FMPIEx bit) in the CAN_IER register, then calling the interrupt handler (read the message) and at the end of the handler, setting the interrupt again.

posted by Diego Hamilton 09 Jan 2019

The problem: You MUST read the CAN message in the Rx interrupt. The isr() method runs in interrupt context, the interruptHandler() runs in user (thread) context. You have to put can.read(rxMsg) into the isr() and pass the message via OS queue.

posted by Mark Peter Vargha 10 Jan 2019

Is my comment helped?

posted by Mark Peter Vargha 16 Jan 2019
8 months, 1 week ago.

I also fixed the problem by clearing the FIFO 0 message pending interrupt by calling the _HAL_CAN_DISABLE_IT function with the appropriate arguments. But is this the correct way? Is there no way by using the CAN driver? I should also note that reading the message with the CAN::read method as per @Mark Peter Vargha's comment did not clear the IRQ and did not work at all.

Hello Willie,

#include "mbed.h"

CAN         can(PB_8, PB_9);    // CAN Rx pin name, CAN Tx pin name
CANMsg      canMsg;
float       temperature;
//...

int main()
{
    //...
    
    while (true) {
        if (can.read(canMsg)) {  // polling for new message
            if (can.id == ID_TEMP) {
                canMsg >> temperature;
                //...
                
            }
        }
    }
}

posted by Zoltan Hudak 25 Mar 2019

Hi Willie,

What arguments did you feed the _HAL_CAN_DISABLE_IT function to clear the interrupt?

I've got the INTERRUPT arg as CAN_IT_FMP0 but can't find what to set the HANDLE argument to.

Thanks!

posted by Karl Swanson 06 Aug 2019

Hi Karl Swanson

Since Mbed OS (Mbes 5) the CAN bus must be polled with

CAN.read(CANMessage &msg, int handle = 0)

Preferably in a separate thread. See Zoltan Hudak's answer.

posted by Willie Visagie 07 Aug 2019

Hmm, I've been doing a CAN.read() in a separate thread but it does not seem to be fast enough to intercept the amount of CAN messages I am receiving, so I was hoping to go the interrupt route.

My plan was to attach a function to the CANbus that signals a thread to perform CAN::read on the reception of an interrupt. I just need to clear the interrupt in this function. You said you managed to do that by calling HAL_CAN_DISABLE_IT with the correct arguments, so I was curious as to what arguments you used.

For example:

CAN can(CAN_RD_PIN, CAN_TD_PIN, 250000);
CANMessage msg_in;
Thread thread;

void recv_can() {
  thread.signal_set(READ_IRQ);
  __HAL_CAN_DISABLE_IT(/*DON'T KNOW WHAT GOES HERE*/, CAN_IT_FMP0);
}

void can_main() {
  while (true) {
    osEvent evt = Thread::signal_wait(READ_IRQ);
    while (can.read(msg_in)) {
      pc.printf("RECV");
    }
    thread.signal_clr(READ_IRQ);
  }
}

int main() {
  thread.start(can_main);
  wait_ms(10);
  can.attach(&recv_can, CAN::RxIrq);
  while (1) {
  // DO NOTHING
  }
}
posted by Karl Swanson 07 Aug 2019

Hi Karl

It was done by clearing the FIFO 0 message pending interrupt by calling the _HAL_CAN_DISABLE_IT function with the appropriate arguments. The argument was CAN_IT_FMP0 as in __HAL_CAN_DISABLE_IT(&CanHandle, CAN_IT_FMP0);

posted by Willie Visagie 08 Aug 2019

Hi Willie, thanks for bearing with me here. I'm probably missing something silly, but where can I obtain the &CanHandle argument from? Is it a member of the CAN object from mbed?

posted by Karl Swanson 08 Aug 2019

Hi Karl

By instantiating a struct of type CAN_HandleTypeDef like CAN_HandleTypeDef canHandle and setting its Instance field as follows

CANName can_rd = (CANName)pinmap_peripheral(rd, PinMap_CAN_RD); CANName can_td = (CANName)pinmap_peripheral(td, PinMap_CAN_TD); CANName can = (CANName)pinmap_merge(can_rd, can_td); canHandle.Instance = (CAN_TypeDef *)can;

then you can call __HAL_CAN_DISABLE_IT(&canHandle, CAN_IT_FMP0);

Or something very close to this in any way... Please note that I got all this information from the stm32f4xx_hal_can_legacy.h and stm32f4xx_hal_can_legacy.c files and should not be used for new designs.

posted by Willie Visagie 12 Aug 2019
3 months, 1 week ago.

Understood, thank you for all the info Willie!

I'm currently updating a ticket with MBED trying to get the CAN interrupts fixed. I'll link it here in case things get fixed soon.

https://github.com/ARMmbed/mbed-os/issues/6714


You need to log in to post a question