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.

RTOS/TM4C1294NCPDT: CAN driver for TM4C1294NCPDT

Part Number: TM4C1294NCPDT

Tool/software: TI-RTOS

I am using a TM4C1294NCPDT, CCS 6.1.2,  TIRTOS 2.16.0.08 and compiler 5.2.7.

I looked at a couple of examples on writing a CAN driver but my driver is not yet working for send or receive of CAN any data.

The CPU I am using has CAN hooked up to CPU pins PB0 (CAN RX) and PB1 (CAN TX).  I configure object number (CAN mailbox) 1 for receive with a wide open filter / mask and I use object number 2, 3, and 4 to transmit for testing the code.

Anyway, here is my code.

Globals:

typedef struct
{
    bool busy;
    bool CAN_Configured;
    unsigned long g_ulMsg1Count, g_ulMsg2Count, g_ulMsg3Count, g_bMsgObj3Sent;
    tCANMsgObject sMsgObjectRx;
    tCANMsgObject sMsgObjectTx;
    bool g_bErrFlag;
    uint8_t message[MESSAGE_LENGTH];  // buffer for data
} CAN_MANAGER;
static CAN_MANAGER CAN_Manager = {0,0,0,0,0,0,0,0,0};


tCANMsgObject testTx;

#define CAN_MSG_OBJ_RX      1

The main loop, init can and send a message over and over.

int main(void){  // >>>>>>>>>>Disable wdog to debug this or wdog will TMO on a break point and reset the board.

    Board_initGeneral();


    CAN_init();
    while(1)
    {
           sendTestCAN_Message();
           SMALL_DELAY;
    }

 

How the CAN init is done.

void CAN_init(void)
{
    Hwi_Handle myHwi;
    Error_Block eb;
    Hwi_Params  hwiParams;

    memset((void*)&CAN_Manager, 0x00, sizeof(CAN_Manager));

    SysCtlPeripheralEnable(GPIO_PORTB_BASE);
    SMALL_DELAY; // when SysCtlPeripheralEnable() is called, a small delay is needed before the device can be used.

     GPIOPinConfigure(GPIO_PB0_CAN1RX);        // CAN RX is PB0
    GPIOPinConfigure(GPIO_PB1_CAN1TX);        // CAN TX is PB1
    GPIOPinTypeCAN(GPIO_PORTB_BASE, GPIO_PIN_0 | GPIO_PIN_1);
    SysCtlPeripheralEnable(SYSCTL_PERIPH_CAN0);

      CANInit(CAN0_BASE);

    CANBitRateSet(CAN0_BASE, 120000000, 500000);
    printf("CAN 0 init\n");

    //CANBitRateSet(CAN0_BASE, SysCtlClockGet(), 500000);
    printf ("CAN 0 clock set\n");

    CANEnable(CAN0_BASE);
    printf ("CAN 0 enabled\n");

    //p81/706 Periph Driver Lib spmu298a.pdf Enable CAN Interrupts (all three IRQ's)
    CANIntEnable (CAN0_BASE, CAN_INT_MASTER | CAN_INT_ERROR | CAN_INT_STATUS);
    printf ("CAN 0 ints enabled\n");

    // Configure a receive object.  
    CAN_Manager.sMsgObjectRx.ui32MsgID = (0x602);        
    CAN_Manager.sMsgObjectRx.ui32MsgIDMask = 0;            // filter mask. 0=accept all e2e.ti.com/.../568684
    CAN_Manager.sMsgObjectRx.ui32Flags = MSG_OBJ_RX_INT_ENABLE | MSG_OBJ_USE_ID_FILTER;    // enable interrupt on RX
    CAN_Manager.sMsgObjectRx.ui32MsgLen = 8;       // allow up to 8 bytes
    CANMessageSet(CAN0_BASE, 1, &CAN_Manager.sMsgObjectRx, MSG_OBJ_TYPE_RX);

    /*    Install the IRQ now*/
    Error_init(&eb);
    Hwi_Params_init(&hwiParams);
    myHwi = Hwi_create(INT_CAN0_TM4C129, CANIntHandler, &hwiParams, &eb);  // see tivaware\inc\hw_ints.h

    if(NULL == myHwi)
    {
        LOG_EVENT(ERROR_CIB_APP_BOARD_ERROR, "Error installing IRQ for CAN0");
    }

    //IntEnable(INT_CAN0); //Enable CAN interrupt
    //CANEnable(CAN0_BASE); //Enable CAN for operation

    CAN_Manager.CAN_Configured = TRUE;
} // end can_init()

Send a message, called from main in a loop after init.

void sendTestCAN_Message(void)
{
    if(CAN_Manager.CAN_Configured ==0)
    {
        printf("CAN not configured, return from send CAN message\n");
        return;
    }

    CAN_Manager.sMsgObjectTx.ui32MsgID = 0x7A7;
    CAN_Manager.sMsgObjectTx.ui32Flags = MSG_OBJ_TX_INT_ENABLE;
    CAN_Manager.sMsgObjectTx.ui32MsgLen = 8;
    CAN_Manager.sMsgObjectTx.pui8MsgData = CAN_Manager.message; // ptr to TX message content


    CANMessageSet(CAN0_BASE, 2, &CAN_Manager.sMsgObjectTx, MSG_OBJ_TYPE_TX); // send the message out mbx 2
    CANMessageSet(CAN0_BASE, 3, &CAN_Manager.sMsgObjectTx, MSG_OBJ_TYPE_TX); // send the message out mbx 3
    CANMessageSet(CAN0_BASE, 4, &CAN_Manager.sMsgObjectTx, MSG_OBJ_TYPE_TX); // send the message out mbx 4



    testTx.ui32MsgID = 0x7A7;
    testTx.ui32Flags = MSG_OBJ_TX_INT_ENABLE;
    testTx.ui32MsgLen = 8;
    testTx.pui8MsgData = CAN_Manager.message; // ptr to TX message content
    CANMessageSet(CAN0_BASE, 2, &testTx, MSG_OBJ_TYPE_TX); // send the message

    printf("CAN TX\n");

}

The IRQ handler, note this never gets called.

void CANIntHandler(UArg arg)
{
    unsigned long ulStatus;

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

    //
    // If the cause is a controller status interrupt, then get the status
    if(ulStatus == CAN_INT_INTID_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.  If the
        // CAN peripheral is not connected to a CAN bus with other CAN devices
        // present, then errors will occur and will be indicated in the
        // controller status.
        //
        ulStatus = CANStatusGet(CAN0_BASE, CAN_STS_CONTROL);  // CAN_STS_NEWDAT

        //
        // Set a flag to indicate some errors may have occurred.
        //
        CAN_Manager.g_bErrFlag = 1;
        return;  // do we want to return here?
    }
    if(ulStatus == 1) //If msg object is 1, message received CAN obj 1
    {
        CANIntClear(CAN0_BASE, 1);
        CAN_Manager.g_ulMsg1Count++;
        CAN_Manager.g_bErrFlag = 0;
    }
    else if(ulStatus == 2)  //If msg object is 2, message received CAN obj 2
    {
        CANIntClear(CAN0_BASE, 2);
        CAN_Manager.g_ulMsg2Count++;
        CAN_Manager.g_bErrFlag = 0;
    }
    else if(ulStatus == 3)
    {
        CANIntClear(CAN0_BASE, 3);
        CAN_Manager.g_ulMsg3Count++;
        CAN_Manager.g_bMsgObj3Sent = 1;
        CAN_Manager.g_bErrFlag = 0;
    }
    else if(ulStatus == 8)
    {
        CANIntClear(CAN0_BASE, 8);
        CAN_Manager.g_ulMsg3Count++;
        CAN_Manager.g_bMsgObj3Sent = 1;
        CAN_Manager.g_bErrFlag = 0;
    }
    else if(ulStatus == 16)
    {
        CANIntClear(CAN0_BASE, 16);
        CAN_Manager.g_ulMsg3Count++;
        CAN_Manager.g_bMsgObj3Sent = 1;
        CAN_Manager.g_bErrFlag = 0;
    }
    //
    // Otherwise, something unexpected caused the interrupt.  This should
    // never happen.
    //
    else
    {
        //
        // Spurious interrupt handling can go here.
        //
    }
}

I probably missed something simple but I have not found it yet.

Thanks,

Doug

  • The CAN module will not receive its own message unless loopback mode has been set. Unfortunately there is no TivaWare function to do that. Here is the code I used to set that mode. I have attached a simple CCS project example as well.

        // Enable loopback mode
        HWREG(CAN1_BASE + CAN_O_CTL) |= CAN_CTL_TEST;  // Set test bit
        HWREG(CAN1_BASE + CAN_O_TST) |= CAN_TST_LBACK; // Set loopback bit
    

    /cfs-file/__key/communityserver-discussions-components-files/908/4213.CanLoopback.zip

  • Using the code I posted in the original post, I put a scope probe on the CAN TX pin to look for any CAN message being sent. The code will first init the CAN device 0 and then it sends a CAN message in a loop over and over and looking at the scope, I never saw any indication I was actually sending data. I'll look closer at your example and compare it to my code and hopefully I can see what I missed.
  • In your original example, without loopback enabled, unless you have a CAN transceiver and another device on the CAN bus, the first message will not see an acknowledge. It assumes the message was not sent successfully (by definition it is not sent successfully if no other node acknowledges it as a good message) and tries to send it again. This is all done by the CAN hardware.
  • If I am looking at the CAN TX pin on the CPU, shouldn't I see it attempt to send the message? I do have a CAN bus analyzer I am also using to try to talk to the Tiva so if the Tiva sends a message the bus analyzer should see it and I can also use the CAN bus analyzer to send a CAN message and I should be able to receive the CAN message on the Tiva. I used the scope to look at the CAN TX pin on the CPU to make the setup as basic as possible.
  • I cannot 'swear to' - yet believe - that the CAN RX pin must be 'ordered-up' - for CAN TX to 'come Alive!'     (achieved via the CAN Xcvr (if/when present) or simple 'pull-up R to CAN_RX' - during test/verify)

    Use of (proper) CAN Xcvrs (@ both ends) - we've found - proves more reliable & FAR 'Closer to real-world Application' - than (any) vendor's 'loop-back.'     (i.e. Why 'delay' - your 'Bout w/Reality?')

  • I got a chance to look at this again and found the problem with the code.  The problem was that the pins I used (PB0 and PB1) work with CAN1 and I was configuring CAN0 for these pins so that's why it didn't work.

    Doug.