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.

CCS/LAUNCHXL-F28379D: Bidirectional Interrupt Based CAN Messages with multiple IDs

Part Number: LAUNCHXL-F28379D

Tool/software: Code Composer Studio

I have been trying to get an interrupt based message system working on two of the Delfino LaunchpadXL's. I was able to get bidirectional code without interrupts working with multiple messages but it required me to add delay after each message is sent or received. When adding interrupt functionality, the messages get through at first but then, after 3-10 messages (it changes each time), the code gets stuck with each board waiting for an ACK from the other, I end up with LEC =111. I'm most interested in Rx interrupts so I've disable Tx ones for now.

I'm guessing this is some timing issues but I haven't been able to find online specifications for timing, the standards I'm using dictate using 500kbps and 5 messages. The example code is used for the ISR with some modifications.  Code for one of the boards is below, the other is very similar but obviously has the Tx and Rx msg definitions switched.

#include "driverlib.h"
#include "device.h"

#define MSG_DATA_LENGTH       8
#define RX_MSG_OBJ_ID100      1   // Use mailbox 1
#define RX_MSG_OBJ_ID101      2   // Use mailbox 2
#define RX_MSG_OBJ_ID102      3   // Use mailbox 3

#define TX_MSG_OBJ_ID108      4   // Use mailbox 4
#define TX_MSG_OBJ_ID109      5   // Use mailbox 5

#define V2C_ID100            0x100
#define V2C_ID101            0x101
#define V2C_ID102            0x102
#define C2V_ID108            0x108
#define C2V_ID109            0x109

volatile uint32_t txMsgCount = 0;
volatile uint32_t rx100, rx101, rx102;
volatile uint32_t errorFlag = 0;

uint32_t glblStatus;

//each rx and tx msg gets its own variable
uint16_t rxMsgData100[8],rxMsgData101[8],rxMsgData102[8];
uint16_t txMsgData108[8],txMsgData109[8];

int sent108 = 0;
int sent109 = 0;

__interrupt void canbISR(void);  // Receive interrupt for CAN-B.

// Main
//
void main(void)
{
    // Initialize device clock and peripherals
    Device_init();

    // Configure GPIO pins for CANTX/CANRX
    Device_initGPIO();
    GPIO_setPinConfig(GPIO_12_CANTXB);
    GPIO_setPinConfig(GPIO_17_CANRXB);

    // Initialize the CAN controller
    CAN_initModule(CANB_BASE);

    // Set up the CAN bus bit rate to 500kHz for each module
    CAN_setBitRate(CANB_BASE, DEVICE_SYSCLK_FREQ, 500000, 16);

    // Enable interrupts on the CAN B peripheral.
    // Enables Int.line0, Error & Status Change interrupts
    CAN_enableInterrupt(CANB_BASE, CAN_INT_IE0 | CAN_INT_ERROR |
                        CAN_INT_STATUS);

    // Initialize PIE and clear PIE registers. Disables CPU interrupts.
    Interrupt_initModule();

    // Initialize the PIE vector table with pointers to the shell Interrupt
    // Service Routines (ISR).
    Interrupt_initVectorTable();

    // Enable Global Interrupt (INTM) and realtime interrupt (DBGM)
    EINT;
    ERTM;

    // Interrupts that are used in this example are re-mapped to
    // ISR functions found within this file.
    // This registers the interrupt handler in PIE vector table.
    Interrupt_register(INT_CANB0, &canbISR);

    // Enable the CAN-B interrupt signal
    Interrupt_enable(INT_CANB0);

    // Set GLBINT0_EN bit in CAN_GLB_INT_EN register
    CAN_enableGlobalInterrupt(CANB_BASE, CAN_GLOBAL_INT_CANINT0);

    // Initialize the receive and transmit messages object V2C and C2V.
    // Message Object Parameters:
    //      CAN Module: B --all
    //      Message Object ID Number: 1,2,3,4,5
    //      Message Identifier: 0x100,0x101,0x102,0x108,0x109
    //      Message Frame: Standard --all
    //      Message Type: Receive 3x, transmit 2x
    //      Message ID Mask: 0x0
// Message Object Flags: receive interrupts, idk about transmit ones yet // Message Data Length: just keep everything at 8 bytes // CAN_setupMessageObject(CANB_BASE, RX_MSG_OBJ_ID100, V2C_ID100, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_RX, 0, CAN_MSG_OBJ_RX_INT_ENABLE, MSG_DATA_LENGTH); CAN_setupMessageObject(CANB_BASE, RX_MSG_OBJ_ID101, V2C_ID101, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_RX, 0, CAN_MSG_OBJ_RX_INT_ENABLE, MSG_DATA_LENGTH); CAN_setupMessageObject(CANB_BASE, RX_MSG_OBJ_ID102, V2C_ID102, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_RX, 0, CAN_MSG_OBJ_RX_INT_ENABLE, MSG_DATA_LENGTH); CAN_setupMessageObject(CANB_BASE, TX_MSG_OBJ_ID108, C2V_ID108, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_TX, 0, CAN_MSG_OBJ_NO_FLAGS, MSG_DATA_LENGTH); CAN_setupMessageObject(CANB_BASE, TX_MSG_OBJ_ID109, C2V_ID109, CAN_MSG_FRAME_STD, CAN_MSG_OBJ_TYPE_TX, 0, CAN_MSG_OBJ_NO_FLAGS, MSG_DATA_LENGTH); txMsgData108[0] = 0x01; txMsgData108[1] = 0x23; txMsgData108[2] = 0x45; txMsgData108[3] = 0x67; txMsgData108[4] = 0x89; txMsgData108[5] = 0xAB; txMsgData108[6] = 0xCD; txMsgData108[7] = 0xEF;
txMsgData109[0] = 0x01; txMsgData109[1] = 0x23; txMsgData109[2] = 0x45; txMsgData109[3] = 0x67; txMsgData109[4] = 0x89; txMsgData109[5] = 0xAB; txMsgData109[6] = 0xCD; txMsgData109[7] = 0xEF; // Start CAN module B operations CAN_startModule(CANB_BASE); // Start msg exchange while(1) { // Delay in us before continuing, hopefully allows enough time for rx interrupt, //lower id anyways, this may not be necessary DEVICE_DELAY_US(1000); CAN_sendMessage(CANB_BASE, TX_MSG_OBJ_ID108, MSG_DATA_LENGTH, txMsgData108); while(((HWREGH(CANB_BASE + CAN_O_ES) & CAN_ES_TXOK)) != CAN_ES_TXOK) { CAN_sendMessage(CANB_BASE, TX_MSG_OBJ_ID108, MSG_DATA_LENGTH, txMsgData108); } sent108++; DEVICE_DELAY_US(1000); CAN_sendMessage(CANB_BASE, TX_MSG_OBJ_ID109, MSG_DATA_LENGTH, txMsgData109); while(((HWREGH(CANB_BASE + CAN_O_ES) & CAN_ES_TXOK)) != CAN_ES_TXOK) { CAN_sendMessage(CANB_BASE, TX_MSG_OBJ_ID109, MSG_DATA_LENGTH, txMsgData109); } sent109++; DEVICE_DELAY_US(1000); } } __interrupt void canbISR(void) { uint32_t status; // Read the CAN-B interrupt status (in the CAN_INT register) to find the // cause of the interrupt status = CAN_getInterruptCause(CANB_BASE); // If the cause is a controller status interrupt, then get the status. // During first iteration of every ISR execution, status = 0x8000, // which simply means CAN_ES != 0x07. if(status == CAN_INT_INT0ID_STATUS) { // Read the controller status. This will return a field of status // error bits that can indicate various errors. Error processing // is not done in this example for simplicity. Refer to the // API documentation for details about the error status bits. // The act of reading this status will clear the interrupt. // status = CAN_getStatus(CANB_BASE); // Return CAN_ES value. // Now status = 0x00000010, indicating RxOK. // Check to see if an error occurred. if(((status & ~(CAN_STATUS_RXOK)) != CAN_STATUS_LEC_MSK) && ((status & ~(CAN_STATUS_RXOK)) != CAN_STATUS_LEC_NONE)) { // Set a flag to indicate some errors may have occurred. errorFlag = 1; glblStatus = status; } } else if((status == TX_MSG_OBJ_ID108) ||(status == TX_MSG_OBJ_ID109)) { // // Getting to this point means that the TX interrupt occurred on // message object 1, and the message TX is complete. Clear the // message object interrupt. // CAN_clearInterruptStatus(CANA_BASE, status); //TX_MSG_OBJ_ID // // Increment a counter to keep track of how many messages have been // sent. In a real application this could be used to set flags to // indicate when a message is sent. // txMsgCount++; // // Since the message was sent, clear any error flags. // errorFlag = 0; } // Check if the cause is the CAN-B receive message object 1. Will be skipped // in the first iteration of every ISR execution else if(status == RX_MSG_OBJ_ID100) { while(!(((HWREGH(CANB_BASE + CAN_O_ES) & CAN_ES_RXOK)) == CAN_ES_RXOK)) {} // Get the received message CAN_readMessage(CANB_BASE, RX_MSG_OBJ_ID100, rxMsgData100); DEVICE_DELAY_US(1000); // Getting to this point means that the RX interrupt occurred on // message object 1, and the message RX is complete. Clear the // message object interrupt. CAN_clearInterruptStatus(CANB_BASE, RX_MSG_OBJ_ID100); // 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. rx100++; // Since the message was received, clear any error flags. errorFlag = 0; } else if((status == RX_MSG_OBJ_ID101)) { while(!(((HWREGH(CANB_BASE + CAN_O_ES) & CAN_ES_RXOK)) == CAN_ES_RXOK)) {} // Get the received message CAN_readMessage(CANB_BASE, RX_MSG_OBJ_ID101, rxMsgData101); DEVICE_DELAY_US(1000); // Getting to this point means that the RX interrupt occurred on // message object 1, and the message RX is complete. Clear the // message object interrupt. CAN_clearInterruptStatus(CANB_BASE, RX_MSG_OBJ_ID101); // 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. rx101++; // Since the message was received, clear any error flags. errorFlag = 0; } else if(status == RX_MSG_OBJ_ID102) { while(!(((HWREGH(CANB_BASE + CAN_O_ES) & CAN_ES_RXOK)) == CAN_ES_RXOK)) {} // Get the received message CAN_readMessage(CANB_BASE, RX_MSG_OBJ_ID102, rxMsgData102); DEVICE_DELAY_US(1000); // Getting to this point means that the RX interrupt occurred on // message object 1, and the message RX is complete. Clear the // message object interrupt. CAN_clearInterruptStatus(CANB_BASE, RX_MSG_OBJ_ID102); // 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. rx102++; // Since the message was received, clear any error flags. errorFlag = 0; } // If something unexpected caused the interrupt, this would handle it. else { // // Spurious interrupt handling can go here. // } // Clear the global interrupt flag for the CAN interrupt line CAN_clearGlobalInterruptStatus(CANB_BASE, CAN_GLOBAL_INT_CANINT0); // Acknowledge this interrupt located in group 9 Interrupt_clearACKGroup(INTERRUPT_ACK_GROUP9); }

  • I was able to get bidirectional code without interrupts working with multiple messages but it required me to add delay after each message is sent or received. 

    Did you poll TxOK & RxOK bits in CAN_ES Register to check for completion of transmission/reception? That would be a better approach than a fixed delay. 

    but I haven't been able to find online specifications for timing,

    Because there is none. CAN is an asynchronous protocol. The only time you need to worry about timing is when you configure the bit-timing parameters. 

    It could be that there are two frames competing for the bus with the same MSGID. This is not permitted. If allowed, the bus will get repeatedly disturbed with error frames and no communication will happen.

  • I have polling in the code as (similar on the tx side)

    while(!(((HWREGH(CANB_BASE + CAN_O_ES) & CAN_ES_RXOK)) == CAN_ES_RXOK))

            {}

    but have now tried it without any delay in the code without the interrupts and it did end up working, so thank you for that.

    However when I try this in the interrupt based code I find the status as reported from the error interrupt changes sometimes as LEC = 011 "Ack Error" on both boards with one set of the code also being in an error passive state and sometimes as 1000, indicating TxOk on one of the boards. When looking to see where each code stopped I found it them both stuck in the read interrupt for different messages, as if waiting for messages that should have been the very things to trigger the interrupt. 

    I also tried resending the message if there was no ACK by having 

    CAN_sendMessage(CANB_BASE, TX_MSG_OBJ_ID108, MSG_DATA_LENGTH, txMsgData108);

    while(!(((HWREGH(CANB_BASE + CAN_O_ES) & CAN_ES_RXOK)) == CAN_ES_RXOK)){

    CAN_sendMessage(CANB_BASE, TX_MSG_OBJ_ID108, MSG_DATA_LENGTH, txMsgData108);

    }

    but this led to other issues.

    One strange thing is that one message did get through but it was the one with the message ID of 0x108, the first message of the above code. I would have expected the ID of 0x100, sent from the other code, to take priority but it was on this ID that the above code got stuck with the other code being stuck on the ID of 0x109, the second message of the above.

    Is there something I can do to stop this from happening?

  • Youssef,

                  You have not answered my question if there could ever be two (or more) frames competing for the bus (from two or more nodes) with the same MSGID. This is extremely important to consider. 

    We either use interrupts or polling in the code, but not both. I am sure you are aware of this, but just getting it out there. 

    However when I try this in the interrupt based code I find the status as reported from the error interrupt changes sometimes as LEC = 011 "Ack Error" on both boards with one set of the code also being in an error passive state and sometimes as 1000, indicating TxOk on one of the boards. When looking to see where each code stopped I found it them both stuck in the read interrupt for different messages, as if waiting for messages that should have been the very things to trigger the interrupt.

    I don’t understand how you can get ACK error on both boards.

    I also tried resending the message if there was no ACK by having

    You don’t need to do this. Retransmission due to lack of ACK is automatic in CAN protocol (unless you have set DAR = 1). 

    I am afraid this needs hands-on debug and my ability to support this moving forward is going to be constrained.

  • I think I was confused about the polling for waiting for ACK. I believe it is working now, thank you.