4 years, 5 months ago.

CAN message being dropped after being recieved STM32 F091rc

I have a F091RC STM32 microcontroller that I have configured to use its CAN peripheral as follows:

I then have another microcontroller send four consecutive CAN messages to this microcontroller.

CAN canBus(PB_8, PB_9); //CAN bus object 

int main(){

canBus.frequency(125000);

    while(1){
        CANMessage nextMessage;

        while( canBus.read( nextMessage) > 0 ){

            printCANMessage(nextMessage); //Custom function I wrote to print CAN message to serial monitor

        };
    };
}

These messages are all being printed out to the serial monitor

Now, I have pretty much the exact same setup running in my actual code, except for some reason the third message is getting dropped... or is being ignored by mbed-os platform for some reason.

bool enteredLoop = false;
        while( canBus.read( CANrxBuf[ nextRxBuf ] ) > 0 ){
            enteredLoop = true;
            messagesToProcess++;
            rxIndex = nextRxBuf;
            nextRxBuf = getNextRxIndex();
            
            pc.printf("\n-->Received CAN Message. ID = %#x  Saving to CANrxBuf[%d]\n", CANrxBuf[rxIndex].id, rxIndex);
            //New message was available!  It has already been saved at CANrxBuf[getNextRxIndex()]
            rxIndex = getNextRxIndex(); //Increment the RX index
            
        };
        
        if(enteredLoop) {
            pc.printf("Exiting canBus read loop!\n");
        }

The weird thing is that if I enclose the above code in a long while loop, I get all 4 messages!

 int numLoops = 0;
while(numLoops < 10000){
        messagesToProcess = 0;
        bool enteredLoop = false;
        while( canBus.read( CANrxBuf[ nextRxBuf ] ) > 0 ){
            enteredLoop = true;
            messagesToProcess++;
            rxIndex = nextRxBuf;
            nextRxBuf = getNextRxIndex();
            
            pc.printf("\n-->Received CAN Message. ID = %#x  Saving to CANrxBuf[%d]\n", CANrxBuf[rxIndex].id, rxIndex);
            //New message was available!  It has already been saved at CANrxBuf[getNextRxIndex()]
            rxIndex = getNextRxIndex(); //Increment the RX index
            
        };
        
        if(enteredLoop) {
            pc.printf("Exiting canBus read loop!\n");
        }
        
        numLoops++;
    };

if I change the while loop to while(numLoops<1000), I get mixed results: sometimes the third message is recognized, sometimes it isn't.

This seems extremely strange to me. Can anyone shed any light on this?

Also, for posterity, here is what my logic analyzer is saying is happening (note that the f091rc is receiving all of the CAN messages at its pins, just something in the mbed os software is crapping out).

Here is my logic analyzer results (note that in the labels for the channels in the screenshot below, the f091RC MCU is labelled "PDM" and the other micro that is sending the messages is labelled "CM".

/media/uploads/jaza_tom/can-message-dropped-by-pdm.png

So, the message is definitely making it to the PDM, however it appears as though it is getting lost somehow by mbed library. Any suggestions?

3 Answers

4 years, 5 months ago.

Tom,

Hey, I am working on a project using CAN with this exact nucleo board. I just put a custom pcb together so I'm reasonably committed to making this work. I am sorry to report that the mbed CAN library, out of the box is not really usable, at least for this micro. I have been reworking quite a bit. So far, read was broken, write was broken, filters were broken. Read and write always return true which is a big problem when you try to detect if a new frame is available. Your while loop is failing because CAN::read() reports true even if there is no data available.

If you aren't already, I would suggest moving to an Offline IDE with the full source code so you can dig into the lower layers. Keil has that free version for STM0 parts. Most of the functions that need to be modified live in can_api.c. And you may have to read the ST manual on that CAN module. The way you use the module is directly related to the hardware.

I would suggest maybe running the Nucleo board in loopback mode until you get the CAN driver read/write working the way you want. This saves you having to mess with two different boards.

can.mode(CAN::LocalTest);

Here is my fix for can_read() which probably the biggest impediment to having something that's truly functional. The change to can_write() is similar in that you have to return 0 if there are no free mailboxes to put new data into.

int can_read(can_t *obj, CAN_Message *msg, int handle) {
    int available = 0;
    int success   = 0;

    CAN_TypeDef *can = (CAN_TypeDef *)(obj->can);

    /// @changed Function to return 0 if no message
    /// @todo FIFO 1 interrupt is not set, so pointless to check it

    /* Check FIFO 0 Ready via FMP[1:0] */
    if ((handle == CAN_FIFO0) && ((can->RF0R & CAN_RF0R_FMP0) != 0)) {
        available = 1;

    /* Check FIFO 1 Ready via FMP[1:0] */
    } else if ((can->RF1R & CAN_RF1R_FMP1) != 0) {
        available = 1;
    }

    /* A FIFO is Ready */
    if (available == 1) {
        /* Get the Id */
        msg->format = (CANFormat)((uint8_t)0x04 & can->sFIFOMailBox[handle].RIR);

        if (!msg->format) {
            msg->id = (uint32_t)0x000007FF & (can->sFIFOMailBox[handle].RIR >> 21);
        } else {
            msg->id = (uint32_t)0x1FFFFFFF & (can->sFIFOMailBox[handle].RIR >> 3);
        }

        msg->type = (CANType)((uint8_t)0x02 & can->sFIFOMailBox[handle].RIR);

        /* Get the DLC */
        msg->len = (uint8_t)0x0F & can->sFIFOMailBox[handle].RDTR;

        /* Get the FMI */

        //  msg->FMI = (uint8_t)0xFF & (can->sFIFOMailBox[handle].RDTR >> 8);

        /* Get the data field */
        msg->data[0] = (uint8_t)0xFF & can->sFIFOMailBox[handle].RDLR;
        msg->data[1] = (uint8_t)0xFF & (can->sFIFOMailBox[handle].RDLR >> 8);
        msg->data[2] = (uint8_t)0xFF & (can->sFIFOMailBox[handle].RDLR >> 16);
        msg->data[3] = (uint8_t)0xFF & (can->sFIFOMailBox[handle].RDLR >> 24);
        msg->data[4] = (uint8_t)0xFF & can->sFIFOMailBox[handle].RDHR;
        msg->data[5] = (uint8_t)0xFF & (can->sFIFOMailBox[handle].RDHR >> 8);
        msg->data[6] = (uint8_t)0xFF & (can->sFIFOMailBox[handle].RDHR >> 16);
        msg->data[7] = (uint8_t)0xFF & (can->sFIFOMailBox[handle].RDHR >> 24);

        /* Release the FIFO */
        if (handle == CAN_FIFO0) {
            /* Release FIFO0 */
            can->RF0R = CAN_RF0R_RFOM0;
        } else { /* FIFONumber == CAN_FIFO1 */
            /* Release FIFO1 */
            can->RF1R = CAN_RF1R_RFOM1;
        }

        success = 1;
    }

    return success;
}

Graham

4 years, 5 months ago.

You should use interrupts on CAN Rx and on CAN Tx.

Hello Tom,
As Mark says, use interrupt service routines. At least for reception. Graham's suggestion to use FIFO is also a great improvement. The CanPipe library published by Paul Paterson combines both techniques, plus adds message filtering. Have a look also at the CanPipe_Example demo.

posted by Zoltan Hudak 08 Apr 2017
4 years, 5 months ago.

I have an excellent implementation of CAN running on the 'F091RC but using HAL drivers. I had 85% bus utilization of irrelevant messages whilst my messages still got through without error at 250KHz.

I have asked a question to help me reconstruct it inside the MBED framework.

My implementation has the CAN_Rx totally running under interrupts filling a table of messages. and an equivalent CAN_Tx message table being pumped out under a polling method, probably easily modified for interrupt control.

if someone can help me: please check: - How to use HAL commands??