Important changes to forums and questions
All forums and questions are now archived. To start a new conversation or read the latest updates go to forums.mbed.com.
7 years, 1 month 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
5 years, 2 months 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 09 Jan 2019I 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 09 Jan 20195 years 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,
- If you use Mbed 2 (aka Mbed classic) then this example might help.
- In Mbed 5 (aka Mbed OS) we cannot call the
read()
method in ISR anymore. We should poll for new messages in themain()
(or in otherThread
) instead. For example:
#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; //... } } } }
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 06 Aug 2019Hi 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 07 Aug 2019Hmm, 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 } }
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);
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 08 Aug 2019Hi 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.
4 years, 7 months 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
I find it here: https://github.com/k-code/stm32f4-examples/blob/master/Task-8-US/inc/STM32F4xx_StdPeriph_Driver/src/stm32f4xx_can.c
posted by Mark Peter Vargha 05 Feb 2017