CAN API on LPC1768 mixes up CAN message order

23 Apr 2014

Hi there,

finally I found the cause for following problem:

when fast sending out several CAN messages using the write-function of the mbed API, the first message gets transmitted, followed by the fourth message. The last two transmitted messages are message 2 and 3. The reason for this is that function can_write from can_api.c makes use of the LPC1768's three TX buffers. It starts by trying to write the current message into the first buffer. If this is impossible because of a not completed transaction, it tries the second buffer. So this is what happens when several CAN messages with the same ID are sent to the can_write function:

  • all buffers are empty
  • message 1 is stored into buffer 1, transmit is started
  • message 2 can't be stored into buffer 1 as transmit is not completed, buffer 2 is used instead
  • message 3 therefore is stored into buffer 3
  • message 4 can't be written to any of the buffers as the first transaction is not yet completed, so can_write returns error
  • this error is processed by user software using some sort of blocking while(!can.write(msg));
  • as soon as message 1 has been completey transmitted, message 4 gets stored into buffer 1 and will be transmitted next (the CAN logic is set up to use the message ID for determining the priority of the TX buffers)
  • message 2 and 3 will be transmitted if user application slows down the message creation (so that between two new messages at least one CAN message can be completely sent out by CAN hardware - this depends on bus traffic and baudrate) or completely stops it

To prevent this, I commented out the attempt of writing into buffer 2 and 3. Instead, only the first buffer is used. This is fine for my application as it comes with a software ringbuffer for the outgoing CAN messages. A more subtle fix would be to keep track of how old the messages in the three buffers are and then use priorities to ensure the correct transmit order.

Fixed problematic function

int can_write(can_t *obj, CAN_Message msg, int cc) {
    unsigned int CANStatus;
    CANMsg m;

    can_enable(obj);

    m.id   = msg.id ;
    m.dlc  = msg.len & 0xF;
    m.rtr  = msg.type;
    m.type = msg.format;
    memcpy(m.data, msg.data, msg.len);
    const unsigned int *buf = (const unsigned int *)&m;

    CANStatus = obj->dev->SR;
    
    if (CANStatus & 0x00000004) {
        obj->dev->TFI1 = buf[0] & 0xC00F0000;
        obj->dev->TID1 = buf[1];
        obj->dev->TDA1 = buf[2];
        obj->dev->TDB1 = buf[3];
        if(cc) {
            obj->dev->CMR = 0x30;
        } else {
            obj->dev->CMR = 0x21;
        }
        return 1;

    } 
    /*
    else if (CANStatus & 0x00000400) {
        obj->dev->TFI2 = buf[0] & 0xC00F0000;
        obj->dev->TID2 = buf[1];
        obj->dev->TDA2 = buf[2];
        obj->dev->TDB2 = buf[3];
        if (cc) {
            obj->dev->CMR = 0x50;
        } else {
            obj->dev->CMR = 0x41;
        }
        return 1;

    }
    else if (CANStatus & 0x00040000) {
        obj->dev->TFI3 = buf[0] & 0xC00F0000;
        obj->dev->TID3 = buf[1];
        obj->dev->TDA3 = buf[2];
        obj->dev->TDB3 = buf[3];
        if (cc) {
            obj->dev->CMR = 0x90;
        } else {
            obj->dev->CMR = 0x81;
        }
        return 1;
    }
    */

    return 0;
}

With best regards, Stefan.

23 Apr 2014

Excellent sleuthing Stefan.

I would like to see a fix for this in the mbed library - either an effective [in order] double-buffering on transmit or the reduction to a single outbound object as Stefan presents above. A CAN network is not like an IP network with redundant paths and the potential for out of order delivery. Often the sequence of a set of messages is essential to their interpretation.

03 Jul 2014

mbed team

Have you looked at this issue, prioritized any work on it? While researching it a bit more, I also found this older post, which appears to be the same issue.

I see there is a priority mode option in the CAN registers, but with 3 buffers, this may not be worth the work to create some priority scheme/table to parse the top 2 bits of the identifiers to sort them into one of three 3 message objects.

Perhaps the easiest/best solution is to use a single message object for transmitting, as Stefan applied above, and then using the attach( ) method, user code can get the callback when the transmit is complete and use that to load the next message.

10 Oct 2019

Deprecated This team is no longer maintained, please use: https://github.com/ARMmbed/mbed-mqtt instead.

Has this been fixed somewhere, as i am currently experiencing this same problem? Is there not a way to have a function that enable or disable this section of code Stefan found