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.

TMS320F280025C: Issue with CAN_readMessage and CAN_clearMessage Functions in C2000Ware

Part Number: TMS320F280025C
Other Parts Discussed in Thread: C2000WARE

Tool/software:

Hello,

I am currently working with the F28002x microcontroller and using the CAN driver functions provided in C2000Ware driverlib. I have encountered an issue with the CAN_readMessage and CAN_clearMessage functions.

It appears that the CAN_readMessage function reads data from the IF2 register, while the CAN_clearMessage function clears the NewDat bit using the IF1 register. This discrepancy seems to cause issues where the NewDat bit is not being cleared properly, leading to the mailbox not being able to receive new messages after the first one. 

I found this implementation in the two versions of C2000Ware that I've tried: C2000Ware_4_03_00_00 and C2000Ware_DigitalPower_SDK_5_04_00_00.

Here is the relevant code snippet from the can.c file:

// CAN_readMessage function
bool CAN_readMessage(uint32_t base, uint32_t objID, uint16_t *msgData)
{
    bool status;
    uint16_t msgCtrl = 0U;

    // Check the arguments.
    ASSERT(CAN_isBaseValid(base));
    ASSERT((objID <= 32U) && (objID != 0U));

    // Set the Message Data A, Data B, and control values to be read
    // on request for data from the message object.
    HWREG_BP(base + CAN_O_IF2CMD) =
    ((uint32_t)CAN_IF2CMD_DATA_A | (uint32_t)CAN_IF2CMD_DATA_B |
     (uint32_t)CAN_IF2CMD_CONTROL | (objID & CAN_IF2CMD_MSG_NUM_M) |
     (uint32_t)CAN_IF2CMD_ARB);

    // Wait for busy bit to clear
    while((HWREGH(base + CAN_O_IF2CMD) & CAN_IF2CMD_BUSY) == CAN_IF2CMD_BUSY)
    {
    }

    // Read out the IF control Register.
    msgCtrl = HWREGH(base + CAN_O_IF2MCTL);

    // See if there is new data available.
    if((msgCtrl & CAN_IF2MCTL_NEWDAT) == CAN_IF2MCTL_NEWDAT)
    {
        // Read out the data from the CAN registers.
        CAN_readDataReg(msgData, (base + CAN_O_IF2DATA),
                        ((uint32_t)msgCtrl & CAN_IF2MCTL_DLC_M));

        status = true;

        // Now clear out the new data flag
        HWREG_BP(base + CAN_O_IF2CMD) = ((uint32_t)CAN_IF2CMD_TXRQST |
                                        (objID & CAN_IF2CMD_MSG_NUM_M));

        // Wait for busy bit to clear
        while((HWREGH(base + CAN_O_IF2CMD) & CAN_IF2CMD_BUSY) ==
               CAN_IF2CMD_BUSY)
        {
        }
    }
    else
    {
        status = false;
    }

    return(status);
}

// CAN_clearMessage function
void CAN_clearMessage(uint32_t base, uint32_t objID)
{
    // Check the arguments.
    ASSERT(CAN_isBaseValid(base));
    ASSERT((objID >= 1U) && (objID <= 32U));

    // Wait for busy bit to clear
    while((HWREGH(base + CAN_O_IF1CMD) & CAN_IF1CMD_BUSY) == CAN_IF1CMD_BUSY)
    {
    }

    // Clear the message valid bit in the arbitration register. This disables
    // the mailbox.
    HWREG_BP(base + CAN_O_IF1ARB) = 0U;

    // Initiate programming the message object
    HWREG_BP(base + CAN_O_IF1CMD) =
    (((uint32_t)CAN_IF1CMD_DIR | (uint32_t)CAN_IF1CMD_ARB) |
     (objID & CAN_IF1CMD_MSG_NUM_M));
}

The code snippets below shows that configuration of the CAN module and the routine used to read the received data:

    // Initialize the CAN-A module
    CAN_initModule(CANA_BASE);

    // Set up the CAN bus bit rate
    CAN_setBitRate(CANA_BASE, DEVICE_SYSCLK_FREQ, 500000, 20);

    // Enable loopback mode
    CAN_enableTestMode(CANA_BASE, CAN_TEST_EXL);

    // Enable the CAN module
    CAN_startModule(CANA_BASE);

    // Configure the CAN message object for transmission
    CAN_setupMessageObject(CANA_BASE, SEND_MAILBOX_1 + 1, SEND_MSG_ID_1, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_TX, 0, CAN_MSG_OBJ_NO_FLAGS, MSG_LEN);

    // Configure the CAN message object for reception
    CAN_setupMessageObject(CANA_BASE, RECEIVE_MAILBOX_1 + 1, RECEIVE_MSG_ID_2, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_RX, 0, CAN_MSG_OBJ_NO_FLAGS, MSG_LEN);

    // Configure the CAN message object for receiving PLC requests
    CAN_setupMessageObject(CANA_BASE, RECEIVE_MAILBOX_2 + 1, RECEIVE_MSG_ID_2, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_RX, 0, CAN_MSG_OBJ_NO_FLAGS, MSG_LEN);

    // Configure the CAN message object for sending diagnostics data
    CAN_setupMessageObject(CANA_BASE, SEND_MAILBOX_2 + 1, SEND_MSG_ID_2, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_TX, 0, CAN_MSG_OBJ_NO_FLAGS, MSG_LEN);

    // Configure the Loopback mailboxes
    // Configure the CAN message object for receiving PVL messages
    CAN_setupMessageObject(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1, SEND_MSG_ID_1, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_RX, 0, CAN_MSG_OBJ_NO_FLAGS, MSG_LEN);

    // Configure the CAN message object for receiving PLC requests
    CAN_setupMessageObject(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_2 + 1, SEND_MSG_ID_2, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_RX, 0, CAN_MSG_OBJ_NO_FLAGS, MSG_LEN);

    // Enable CAN interrupts
    CAN_enableInterrupt(CANA_BASE, CAN_INT_IE0 | CAN_INT_ERROR | CAN_INT_STATUS);
    CAN_enableGlobalInterrupt(CANA_BASE, CAN_GLOBAL_INT_CANINT0);

    // Register the CAN ISR
    Interrupt_register(INT_CANA0, &canISR);

    // Enable the CAN interrupt in the PIE
    Interrupt_enable(INT_CANA0);

    // Enable global interrupts
    EINT;
    ERTM;

            // Read the received message
            CAN_readMessage(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1, rxData);

            // Clear the New Data flag
            CAN_clearMessage(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1);
            
            // Clear the interrupt flag
            CAN_clearInterruptStatus(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1);
            
            // Clear CAN global interrupt flag
            CAN_clearGlobalInterruptStatus(CANA_BASE, CAN_GLOBAL_INT_CANINT0);
        
            // Acknowledge the interrupt
            Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);

I am facing a problem where the CAN_clearMessage function is not clearing the NewDat bit, so after receiving the first message, the microcontroller is not receiving any other messages in the same mailbox. It can receive messages in other mailboxes, but not in the same mailbox once this mailbox received the first message.

Is this an error in the driver, or am I using these functions incorrectly? How should I properly use these functions to ensure that the NewDat bit is cleared and the mailbox can receive new messages?

After replacing the CAN_clearMessage by the one below, which clears the NewDat bit of the IF2MCTL register, the CAN module was able to receive successive messages:

void MyCAN_clearMessage(uint32_t base, uint32_t objID)
{
    // Check the arguments.
    ASSERT(CAN_isBaseValid(base));
    ASSERT((objID >= 1U) && (objID <= 32U));

    // Wait for busy bit to clear
    while((HWREGH(base + CAN_O_IF2CMD) & CAN_IF2CMD_BUSY) == CAN_IF2CMD_BUSY)
    {
    }

    // Clear the New Data flag
    HWREG_BP(base + CAN_O_IF2CMD) = ((uint32_t)CAN_IF2CMD_TXRQST |
                                     (objID & CAN_IF2CMD_MSG_NUM_M));

    // Wait for busy bit to clear
    while((HWREGH(base + CAN_O_IF2CMD) & CAN_IF2CMD_BUSY) == CAN_IF2CMD_BUSY)
    {
    }
}

Thank you for your assistance.

Best regards,
Filipe

  • Hi Filipe,

    How are messages received?  Is the NewDat bit polled and if set, read the message or is it through a CAN ISR?  If through the interrupt, can you provide the code snippet as well?  It seems like there might be an oveflow flag that is set that is preventing the clearing of NewDat bit.

    Thanks,

    Joseph

  • Hi Joseph,

    The data is received through a CAN ISR. The ISR code snippet was provided previously, but I will include it in full here:

    __interrupt void canISR(void)
    {
        uint32_t status;
        uint32_t error_n_status;
        
        // Get the cause of the interrupt
        status = CAN_getInterruptCause(CANA_BASE);
        
        // LoopBack Mailboxes
        if (status == 0x8000) // Error and Status Register value is NOT 0x07 (0x07 means: No CAN bus event was detected since the last time when CPU has         read the Error and Status Register)
        {
            if (CanaRegs.CAN_IF1CMD.bit.MSG_NUM == SEND_MSG_ID_1)
            {
                // Read the received message
                CAN_readMessage(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1, rxData);
    
                // Clear the New Data flag
                MyCAN_clearMessage(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1);
    
                // Clear the interrupt flag
                CAN_clearInterruptStatus(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1);
            }
            
            // Read the Error and Status Register to update the Interrupt Register with the next lower priority interrupt value
            error_n_status = CAN_getStatus(CANA_BASE);
            status = CAN_getInterruptCause(CANA_BASE);
        }
        // Clear CAN global interrupt flag
        CAN_clearGlobalInterruptStatus(CANA_BASE, CAN_GLOBAL_INT_CANINT0);
    
        // Acknowledge the interrupt
        Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9);
    }


    The function used to sent the data is:

    void sendCANMessage(void)
    {
        // Define the data to be sent
        uint16_t txData = 0x1234;
    
        // Send the CAN message
        CAN_sendMessage_16bit(CANA_BASE, SEND_MAILBOX_1 + 1, MSG_LEN, &txData);
    }


    And the macros are:

    #define SEND_MSG_ID_1        (0x01)
    #define SEND_MSG_ID_2        (0x03)
    
    #define RECEIVE_MSG_ID_1     (0x02)
    #define RECEIVE_MSG_ID_2     (0x05)
    
    #define SEND_MAILBOX_1       (0)
    #define SEND_MAILBOX_2       (2)
    
    #define RECEIVE_MAILBOX_1    (16)
    #define RECEIVE_MAILBOX_2    (17)
    
    #define RECEIVE_LOOPBACK_MAILBOX_1       (30)
    #define RECEIVE_LOOPBACK_MAILBOX_2       (31)


    Best regards,

    Filipe

  • Hi Filipe,

    Thanks, sorry missed that.  I think you need to revisit this portion of the ISR:

        // Get the cause of the interrupt
        status = CAN_getInterruptCause(CANA_BASE);
        
        // LoopBack Mailboxes
        if (status == 0x8000) // Error and Status Register value is NOT 0x07 (0x07 means: No CAN bus event was detected since the last time when CPU has         read the Error and Status Register)
        {
            if (CanaRegs.CAN_IF1CMD.bit.MSG_NUM == SEND_MSG_ID_1)
            {
                // Read the received message
                CAN_readMessage(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1, rxData);
    
                // Clear the New Data flag
                MyCAN_clearMessage(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1);
    
                // Clear the interrupt flag
                CAN_clearInterruptStatus(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1);
            }
    

    Currently the ISR is only reading the message RAM when there is no error when CAN_INT register returns 0x8000.  What your routine should be doing is reading the message RAM content when CAN_INT returns the message RAM # that got populated when a qualified CAN frame got moved in the message RAM upon meeting the qualification requirement (like valid message ID, no CRC errors, no receive errors...etc).  I would rewrite it this way:

        // Get the cause of the interrupt
        status = CAN_getInterruptCause(CANA_BASE);
        
        // LoopBack Mailboxes
        if (status ==RECEIVE_LOOPBACK_MAILBOX_1 + 1) 
        {
            if (CanaRegs.CAN_IF1CMD.bit.MSG_NUM == SEND_MSG_ID_1)
            {
                // Read the received message
                CAN_readMessage(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1, rxData);
    
    
                // Clear the interrupt flag
                CAN_clearInterruptStatus(CANA_BASE, RECEIVE_LOOPBACK_MAILBOX_1 + 1);
            }
    

    Note that I commented out the function that clears NewDat.  If the CAN_INT returns the value of the correct mailbox that caused the interrupt, then CAN_readMessage() would take care of clearing the NewDat bit.  The existing ISR is actually not servicing the interrupt that is triggered by a valid received message which is causing an interrupt overflow.

    Regards,

    Joseph

  • Hi Joseph

    I tried the solution you proposed, but the value of status after "status = CAN_getInterruptCause(CANA_BASE)" is 0x00008000 so no data is received. The only bit 1 in the Error and Status register is the TxOk.

    If it's good to know, the Tx and Rx pins of the CAN module are directly connected to a CAN monitoring device, there is no CAN transceiver. This CAN monitoring device only measure the signals. A print of the signal measured by the CAN monitoring device is shown below:



    Do you think that this issue is not related to the fact the CAN_readMessage function reads data from IF2 and CAN_clearMessage resets the NewDat bit in the IF1 register?

    Let me know if you need any other information.

    Best regards,

    Filipe

  • Hi Filipe,

    Just to be clear CAN_readMessage is used to transfer received data from message RAM and clears the NewDat bit in the specified message object number.  From your description above, it seems to me that the C2000 device is transmitting data and a CAN monitoring device is just receiving the 2 bytes that the C2000 CAN is sending out.  I don't understand why you are using CAN_readMessage in your ISR if the routine is sending out CAN frames.  Any particular reason why the CAN_readMessage API is used?

    Regards,

    Joseph

  • Hi,

    CAN_readMessage is used because I'm using the CAN module in Loopback mode, and I would like to check if it's receiving the data correctly.

    It's only a single microcontroller in loopback mode. The CAN_readMessage is supposed to work correctly in Loopback mode, isn't it?

    ps: since you said the CAN_readMessage function cleans the NewDat bit, I just tried to comment out the CAN_clearMessage function from the ISR, and now it looks the code works as I expected.


    BR,

    Filipe

  • Hi Felipe,

    Yes it should work in loopback mode.  I think you are also missing the variable for enabling the interrupt in the message object definition that is why the message reception is not entering ISR.  In the message object definition, substitute argument CAN_MSG_OBJ_NO_FLAGS with CAN_MSG_OBJ_RX_INT_ENABLE, then you can monitor the return value of CAN_getInterruptCause.  It should return the mailbox# you assigned in the message object definition.

    Regards,

    Joseph

  • Hi Joseph,

    CAN_getInterruptCause is still returning 0x00008000 after replacing CAN_MSG_OBJ_NO_FLAGS by CAN_MSG_OBJ_RX_INT_ENABLE.

    Is there anything else missing?

    I have another quick question, if you don’t mind. The F280025's technical reference guide brings some recommendations about the CAN GPIOs configurations, but about the pull-ups it only says "The internal pull-ups can be configured in the GPyPUD register." Is it recommended or not to enable the pull-ups in the CAN GPIOs (considering that a CAN transceiver is used)?

    Regards,

    Filipe

  • Hi Filipe,

    You could either remove these flags (CAN_INT_ERROR | CAN_INT_STATUS) in the CAN_enableInterrupt() function call or assign an empty handler in the ISR for status==0x8000.  What happens is that every successful CAN transaction fires a CAN interrupt.

    For the second question, there is no need to enable the pull ups on the CAN pins.  Just use the function call GPIO_setPinConfig() to set the peripheral pin functions for the CAN pins selected.  The transceiver, with the 120-ohm termination resistor between CANL and CANH translates the CAN signals in differential mode on the CAN bus.

    Regards,

    Joseph