This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CAN peripheral FIFO filling up and losing messages

Hi,

I set up a CAN receiver demo, based on the simple_rx.c example in TivaWare. Since I was losing messages, I am trying to add FIFO functionality, following the can_device_fifo.c example of StellarisWare. However, it seems no matter how hard I try, my FIFO fills up and I lose messages.

A minimal example to show what I'm having. It is based on TI's simple_rc.c example

void
CANIntHandler(void)
{
    uint32_t ui32Status;
    uint32_t ui32ErrStatus;

    // Read the CAN interrupt status to find the cause of the interrupt
    ui32Status = CANIntStatus(CAN0_BASE, CAN_INT_STS_CAUSE);

    UARTprintf("--> %u\n", ui32Status);

    // If the cause is a controller status interrupt, then get the status
    if(ui32Status == CAN_INT_INTID_STATUS)
    {
       ui32ErrStatus = CANStatusGet(CAN0_BASE, CAN_STS_CONTROL);
    }
    // Check if the cause is message object 1 to 8, which is the size of the FIFO that we are using for
    // receiving messages.
    else if(ui32Status <=8)
    {
        // Read message and clear the message object interrupt.
        CANMessageGet(CAN0_BASE, ui32Status, &sCANMessage, 1);

        if(sCANMessage.ui32Flags & MSG_OBJ_DATA_LOST)
        {
            UARTprintf("! ID=0x%08X %u\n", sCANMessage.ui32MsgID, ui32Status);
        } else {
            UARTprintf("ID=0x%08X %u\n", sCANMessage.ui32MsgID, ui32Status);
        }
        sCANMessage.ui32Flags = 0;

        // Increment a counter to keep track of how many messages have been
        // received.  In a real application this could be used to set flags to
        // indicate when a message is received.
        g_ui32MsgCount++;
    }

in the main loop I set up the FIFO using:

sCANMessage.ui32MsgID = 0;
sCANMessage.ui32MsgIDMask = 0;
sCANMessage.ui32Flags = MSG_OBJ_RX_INT_ENABLE | MSG_OBJ_USE_ID_FILTER | MSG_OBJ_EXTENDED_ID | MSG_OBJ_FIFO;
sCANMessage.ui32MsgLen = 8;
for (ui32idx = 1; ui32idx<8; ui32idx++){
    CANMessageSet(CAN0_BASE, ui32idx, &sCANMessage, MSG_OBJ_TYPE_RX);
}
// Last message object does not have the FIFO flag
sCANMessage.ui32Flags = MSG_OBJ_RX_INT_ENABLE | MSG_OBJ_USE_ID_FILTER | MSG_OBJ_EXTENDED_ID;
CANMessageSet(CAN0_BASE, 8, &sCANMessage, MSG_OBJ_TYPE_RX);

The main loop does nothing anymore, just stays in endless for loop.

Running this code with a device connected on CAN0 gives some output:

--> 32768
--> 1
ID=0x00010007 1
--> 32768
--> 32768
--> 2
ID=0x00010009 2
--> 32768
--> 2
ID=0x00010007 2
--> 32768
--> 32768
--> 3
ID=0x00010009 3
--> 32768
--> 3
ID=0x00010007 3
--> 32768
--> 32768
--> 4
ID=0x00010009 4
--> 32768
--> 4
ID=0x00010007 4
--> 32768
--> 32768
--> 5
ID=0x00010009 5
--> 32768
--> 5
ID=0x10000000 5
--> 32768
--> 6
ID=0x10000001 6
--> 32768
--> 7
ID=0x10000002 7
--> 8
! ID=0x10000500 8
--> 32768
--> 6
ID=0x10000600 6
--> 32768
--> 6
ID=0x00010007 6
--> 32768
--> 32768
--> 7
ID=0x00010009 7
--> 32768
--> 7
ID=0x00010007 7
--> 32768
--> 32768
--> 8
! ID=0x00010009 8
--> 32768
--> 8
! ID=0x00010007 8
--> 32768
--> 32768
--> 8
! ID=0x00010009 8

I have two questions:

  • You can see that the FIFO entries (1 to 8) fill up gradually, but never empties fast enough by the interrupt calls to CANGetMessage. Eventually I get message loss, indicated by the exclamation marks at the end.
    I don't know how messages can be removed faster than calling CANGetMessage in the interrupt already, so why does the FIFO still fill up so fast?

  • Also, lots of status interrupts are generated just for the sake of notifying an received message (RXOK). I wanted to avoid this by disabling CAN_INT_STATUS interrupts:

    CANIntEnable(CAN0_BASE, CAN_INT_MASTER | CAN_INT_ERROR | CAN_INT_STATUS);

    However, when I do that, I get "spurious" interrupts with ui32Status = 0. Why is that?
  • Why is your FIFO filling up?

    Well, you are spending a lot of time printing in the interrupt.

    You also don't exhaust the interrupt sources in the interrupt.

    Robert
  • I added the printf's to make my example clear and traceable. Of course it takes up processing time. If I remove them, the FIFO's still fill up and there is message loss regardless. Additionally, there is no other code running: no main loop, no other interrupts... only this CAN interrupt.


    About exhausting of the interrupt sources; In the above example, every message object is supposed to generate an interrupt, so each interrupt call clears an additional message object? What do you suggest in order to process more interrupt sources in one call?



    And what do you think about the useless status interrupts? If my bus is very busy, I get an RXOK interrupt for every receive event, even if it does not match an object filter. Disabling the status interrupts altogether then creates some spurious interrupt (with ui32Status = 0)
  • Hello Klass,

    What is the CAN Baud Rate and System Clock Frequency?

    Regards
    Amit
  • Klaas De Craemer said:
    What do you suggest in order to process more interrupt sources in one call?



    /* Read the CAN interrupt status to find the cause of the interrupt		*/
        interrupt_source = CANIntStatus(CAN0_BASE, CAN_INT_STS_CAUSE);
    
        while(interrupt_source != 0u ) {
                    :
                    CANIntClear(CAN0_BASE, interrupt_source);
                    :
             interrupt_source = CANIntStatus(CAN0_BASE, CAN_INT_STS_CAUSE);
             }   

    This is the usual loop for almost any interrupt source that can flag multiple sources or has a FIFO.  Re-interrupting for every case only adds overhead.

    Klaas De Craemer said:
    If I remove them, the FIFO's still fill up and there is message loss regardless.

    Set an output pin on entry to the interrupt and clear it just before exit.  Measure this on an oscilloscope or logic analyzer. This will give you your best measurement of time spent in the interrupt and the interrupt frequency. You can use other pins to indicate when you are processing a message (or other source).

    BTW, what is your message rate?

    Robert

  • Hi Amit,
    The clock frequency is 16Mhz, and CAN bus is at 500kBaud
  • Hello Klaas

    And does the issue happen if you reduce the CAN bus frequency or increase the System Clock Frequency?

    Regards
    Amit
  • Ah, I understand what you mean. I changed my code, and enabled FIFO of the UART as well:

        // Read the CAN interrupt status to find the cause of the interrupt
        ui32intCause = CANIntStatus(CAN0_BASE, CAN_INT_STS_CAUSE);
    
        while(ui32intCause != 0){
        	if(ui32intCause == CAN_INT_INTID_STATUS){
        		ui32ErrStatus = CANStatusGet(CAN0_BASE, CAN_STS_CONTROL);
    
        	} else if (ui32intCause <=8){
        		//Read message and reset NEWDAT bit
        		CANMessageGet(CAN0_BASE, ui32intCause, &sCANMessage, 1);
        		//Check for lost messages
        		if(sCANMessage.ui32Flags & MSG_OBJ_DATA_LOST){
        			UARTprintf("!");
    //    			UARTprintf("! ID=0x%08X %u\n", sCANMessage.ui32MsgID, ui32intCause);
        		} else {
        			//UARTprintf("ID=0x%08X %u\n", sCANMessage.ui32MsgID, ui32intCause);
        			UARTprintf("%u", ui32intCause);
        		}
        		sCANMessage.ui32Flags = 0;
        		g_ui32MsgCount++;
        	}
    
        	CANIntClear(CAN0_BASE, ui32intCause);
        	ui32intCause = CANIntStatus(CAN0_BASE, CAN_INT_STS_CAUSE);
        }

    This prints a continuous stream of 1's, so only the first FIFO position is used. If I make the printf statement longer, the buffers fill up again. So what you said about that was definitely correct. I enabled the UART tx FIFO, giving me 8 chars of "room" at least.

    Message rate on the bus is not extremely high, maybe 30 msgs/second on average, but they seem to come in short bursts.

    I am wondering about one thing. If my buffer fills up to the 8th message object, and then I disconnect the device sending the CAN messages, I would expect the int32Cause number to go back down to 1 as it reads all the leftover messages. Instead it stays at 8, effectively disabling the FIFO function for newer messages.

    This would suggest that CANMessageGet() function does not always clear the NEWDAT bit of the messages?

  • I have increased frequency to 50Mhz from 16Mhz and that seems to allow the UART function to finish in time, great :)
    Reducing CAN bus to 250kBaud did not help, but I cannot choose the speed in my application.
  • Hello Klass,

    And when running the system clock at 50MHz (with CAN at 500K) does the CAN work or does the message buffer still get full and message lost?

    Regards
    Amit
  • With 50Mhz sys and 500k it works, yes. I meant that exclusively decreasing CAN speed did not work.

    I have two question marks left.
    * When the FIFO does fill up, due to slow interrupt handling, it never empties again, even if the sender stop transmitting. I will try to solve it by ensuring all FIFO entries are read when ui32IntCause>1
    * Disabling the CAN_INT_STATUS interrupt creates spurious interrupt (in above code ui32IntCause is zero).
  • Hello Klaas

    I think the issue that we need to look at is the spurious interrupt. Can you toggle a GPIO to see how many interrupts you get for a single message and then how many you get for a burst of messages?

    Regards,
    Amit
  • This:

    Klaas De Craemer said:
    CANMessageGet(CAN0_BASE, ui32intCause, &sCANMessage, 1);

    I think is wrong.

    You are clearing the interrupt with the argument to MessageGet and with CANIntClear.

    Probably not good.

    Robert

  • Hello Robert

    That should be the Object ID. If the CAN_INT_STS_CAUSE is parsed then the ui32Status is the Object ID

    Regards
    Amit
  • Last argument to CANMessageGet Amit.

    If set to true it clears the interrupt. CANIntClear does the same.

    Robert
  • Hello Robert,

    OK. Yes.

    Regards
    Amit