1.25.3 Controller Area Network (CAN)

The CAN controller provides all the features required to implement the serial communication protocol CAN, the CAN specification as referred to by ISO/11898A (2.0 Part A and 2.0 Part B) for high speeds and ISO/11519-2 for low speeds. The CAN Controller is able to handle all types of frames (Data, Remote, Error and Overload) and achieves a bitrate of 1 Mbit/s.

CAN controller accesses are made through configuration registers. 8 independent message objects (mailboxes) are implemented.

Any mailbox can be programmed as a reception buffer block (even non-consecutive buffers). For the reception of defined messages, one or several message objects can be masked without participating in the buffer feature. An interrupt is generated when the buffer is full. According to the mailbox configuration, the first message received can be locked in the CAN controller registers until the application acknowledges it, or this message can be discarded by new received messages.

Any mailbox can be programmed for transmission. Several transmission mailboxes can be enabled in the same time.

Using The Library

The CAN library supports the normal mode. The CAN Plib can transfer message in a polling or an interrupt mode.

CAN normal operation with polling

The following example shows the CAN normal mode operation with polling implementation.
#include <stddef.h>                     // Defines NULL
#include <stdbool.h>                    // Defines true
#include <stdlib.h>                     // Defines EXIT_FAILURE
#include "definitions.h"                // SYS function prototypes

/* LED Toggle */
#define LED_Toggle() LED_GREEN_Toggle()

void print_menu(void)
{
    printf(" ------------------------------ \r\n");   
    printf(" Press '1' to Transmit message \r\n");
    printf(" Press '2' to Receive message \r\n");
}

// *****************************************************************************
// *****************************************************************************
// Section: Main Entry Point
// *****************************************************************************
// *****************************************************************************

int main ( void )
{
    uint8_t user_input = 0;
    uint32_t messageID = 0;
    uint8_t message[8];
    uint8_t messageLength = 0;
    uint32_t status = 0;
    
    uint8_t rx_message[8];
    uint32_t rx_messageID = 0;
    uint8_t rx_messageLength = 0;
    CAN_MSG_RX_ATTRIBUTE msgAttr = CAN_MSG_RX_DATA_FRAME;

    /* Initialize all modules */
    SYS_Initialize ( NULL );

    LED_RED_Clear();
    LED_GREEN_Clear();
    LED_BLUE_Clear();

    printf(" ------------------------------ \r\n");
    printf("            CAN Demo            \r\n");
    printf(" ------------------------------ \r\n");
    
    print_menu();
    
    /* Prepare the message to send*/
    messageID = 0x469;
    messageLength = 8;
    for (uint8_t count = 8; count >=1; count--){
        message[count - 1] = count;
    }
    
    while ( true )
    {
        /* Maintain state machines of all polled Harmony modules. */
        scanf("%c", (char *) &user_input);
        
        switch (user_input)
        {
            case '1': 
                printf(" Transmitting Message:");
                if (CAN1_MessageTransmit(messageID, messageLength, message, CAN_MAILBOX_DATA_FRAME_TX) == true)
                {    
                    printf("Success \r\n");
                    LED_Toggle();
                }
                else
                {
                    printf("Failed \r\n");
                }             
                break;
            case '2':
                printf(" Waiting for message: \r\n");
                while (true)
                {
                    if (CAN1_InterruptGet(CAN_INTERRUPT_MB1_MASK))
                    {
                        /* Check CAN Status */
                        status = CAN1_ErrorGet();

                        if ((status & (CAN_ERROR_BOFF | CAN_ERROR_CERR |
                                        CAN_ERROR_SERR | CAN_ERROR_AERR |
                                        CAN_ERROR_FERR | CAN_ERROR_BERR)) == CAN_ERROR_NONE)
                        {
                            memset(rx_message, 0x00, sizeof(rx_message));
                            
                            /* Receive New Message */
                            if (CAN1_MessageReceive(&rx_messageID, &rx_messageLength, rx_message, 0, CAN_MAILBOX_DATA_FRAME_RX_OVERWRITE, &msgAttr) == true)  
                            {
                                printf(" New Message Received    \r\n");
                                status = CAN1_ErrorGet();
                                if ((status & (CAN_ERROR_BOFF | CAN_ERROR_CERR |
                                               CAN_ERROR_SERR | CAN_ERROR_AERR |
                                               CAN_ERROR_FERR | CAN_ERROR_BERR)) == CAN_ERROR_NONE)
                                {
                                    /* Print message to Console */
                                    uint8_t length = rx_messageLength;
                                    printf(" Message - ID : 0x%x Length : 0x%x ", (unsigned int) rx_messageID,(unsigned int) rx_messageLength);
                                    printf("Message : ");
                                    while(length)
                                    {
                                        printf("0x%x ", rx_message[rx_messageLength - length--]);
                                    }
                                    printf("\r\n");
                                    LED_Toggle();
                                    break;
                                }
                                else
                                {
                                    printf("Error in received message");
                                }
                            }
                            else
                            {
                                printf("Message Reception Failed \r");
                            }
                        }
                        else
                        {
                            printf("Error in last received message");
                        }
                    }
                }
                break;
            default:
                printf(" Invalid Input \r\n");
                break;
        }
        
        print_menu();
        
    }

    /* Execution should not come here during normal operation */

    return ( EXIT_FAILURE );
}

CAN normal operation with interrupt

The following example shows the CAN normal mode operation with interrupt implementation.

#include <stddef.h>                     // Defines NULL
#include <stdbool.h>                    // Defines true
#include <stdlib.h>                     // Defines EXIT_FAILURE
#include "definitions.h"                // SYS function prototypes

/* Application's state machine enum */
typedef enum
{
    APP_STATE_CAN_RECEIVE,
    APP_STATE_CAN_TRANSMIT,
    APP_STATE_CAN_IDLE,
    APP_STATE_CAN_USER_INPUT,
    APP_STATE_CAN_XFER_SUCCESSFUL,
    APP_STATE_CAN_XFER_ERROR
} APP_STATES;

/* LED Toggle */
#define LED_Toggle() LED_GREEN_Toggle()

/* Variable to save application state */
static APP_STATES state = APP_STATE_CAN_USER_INPUT;
/* Variable to save Tx/Rx transfer status and context */
static uint32_t status = 0;
static uint32_t xferContext = 0;
/* Variable to save Tx/Rx message */
static uint32_t messageID = 0;
static uint8_t message[8];
static uint8_t messageLength = 0;
static uint8_t rx_message[8];
static uint32_t rx_messageID = 0;
static uint8_t rx_messageLength = 0;
static CAN_MSG_RX_ATTRIBUTE msgAttr = CAN_MSG_RX_DATA_FRAME;

// *****************************************************************************
// *****************************************************************************
// Section: Local functions
// *****************************************************************************
// *****************************************************************************

/* This function will be called by CAN PLIB when transfer is completed */
// *****************************************************************************
/* void APP_CAN_Callback(uintptr_t context)

  Summary:
    Function called by CAN PLIB upon transfer completion

  Description:
    This function will be called by CAN PLIB when transfer is completed.
    In this function, current state of the application is obtained by context
    parameter. Based on current state of the application and CAN error
    state, next state of the application is decided.

  Remarks:
    None.
*/
void APP_CAN_Callback(uintptr_t context)
{
    xferContext = context;

    /* Check CAN Status */
    status = CAN1_ErrorGet();

    if ((status & (CAN_ERROR_BOFF | CAN_ERROR_CERR |
                   CAN_ERROR_SERR | CAN_ERROR_AERR |
                   CAN_ERROR_FERR | CAN_ERROR_BERR)) == CAN_ERROR_NONE)
    {
        switch ((APP_STATES)context)
        {
            case APP_STATE_CAN_RECEIVE:
            case APP_STATE_CAN_TRANSMIT:
            {
                state = APP_STATE_CAN_XFER_SUCCESSFUL;
                break;
            }
            default:
                break;
        }
    }
    else
    {
        state = APP_STATE_CAN_XFER_ERROR;
    }
}

void print_menu(void)
{
    printf(" ------------------------------ \r\n");   
    printf(" Press '1' to Transmit message \r\n");
    printf(" Press '2' to Receive message \r\n");
}

// *****************************************************************************
// *****************************************************************************
// Section: Main Entry Point
// *****************************************************************************
// *****************************************************************************

int main ( void )
{
    uint8_t user_input = 0;
    
    /* Initialize all modules */
    SYS_Initialize ( NULL );

    LED_RED_Clear();
    LED_GREEN_Clear();
    LED_BLUE_Clear();

    printf(" ------------------------------ \r\n");
    printf("            CAN Demo            \r\n");
    printf(" ------------------------------ \r\n");
    
    print_menu();
    
    /* Prepare the message to send*/
    messageID = 0x469;
    messageLength = 8;
    for (uint8_t count = 8; count >=1; count--){
        message[count - 1] = count;
    }
    
    while ( true )
    {
        if (state == APP_STATE_CAN_USER_INPUT)
        {
            /* Read user input */
            scanf("%c", (char *) &user_input);
            
            switch (user_input)
            {
                case '1': 
                    printf(" Transmitting Message:");
                    CAN1_TxCallbackRegister( APP_CAN_Callback, (uintptr_t)APP_STATE_CAN_TRANSMIT, CAN_MAILBOX_DATA_FRAME_TX );
                    state = APP_STATE_CAN_IDLE;
                    if (CAN1_MessageTransmit(messageID, messageLength, message, CAN_MAILBOX_DATA_FRAME_TX) == false)
                    {
                        printf("CAN1_MessageTransmit request has failed\r\n");
                    }             
                    break;
                case '2':
                    printf(" Waiting for message: \r\n");
                    CAN1_RxCallbackRegister( APP_CAN_Callback, (uintptr_t)APP_STATE_CAN_RECEIVE, CAN_MAILBOX_DATA_FRAME_RX_OVERWRITE );
                    state = APP_STATE_CAN_IDLE;
                    memset(rx_message, 0x00, sizeof(rx_message));
                    /* Receive New Message */
                    if (CAN1_MessageReceive(&rx_messageID, &rx_messageLength, rx_message, 0, CAN_MAILBOX_DATA_FRAME_RX_OVERWRITE, &msgAttr) == false)  
                    {
                        printf("CAN1_MessageReceive request has failed\r\n");
                    }
                    break;
                default:
                    printf(" Invalid Input \r\n");
                    break;
            }
        }
        /* Check the application's current state. */
        switch (state)
        {
            case APP_STATE_CAN_IDLE:
            {
                /* Application can do other task here */
                break;
            }
            case APP_STATE_CAN_XFER_SUCCESSFUL:
            {
                if ((APP_STATES)xferContext == APP_STATE_CAN_RECEIVE)
                {
                    /* Print message to Console */
                    printf(" New Message Received    \r\n");
                    uint8_t length = rx_messageLength;
                    printf(" Message - ID : 0x%x Length : 0x%x ", (unsigned int) rx_messageID,(unsigned int) rx_messageLength);
                    printf("Message : ");
                    while(length)
                    {
                        printf("0x%x ", rx_message[rx_messageLength - length--]);
                    }
                    printf("\r\n");
                } 
                else if ((APP_STATES)xferContext == APP_STATE_CAN_TRANSMIT)
                {
                    printf("Success \r\n");
                }
                LED_Toggle();
                print_menu();
                state = APP_STATE_CAN_USER_INPUT;
                break;
            }
            case APP_STATE_CAN_XFER_ERROR:
            {
                if ((APP_STATES)xferContext == APP_STATE_CAN_RECEIVE)
                {
                    printf("Error in received message");
                }
                else
                {
                    printf("Failed \r\n");
                }
                print_menu();
                state = APP_STATE_CAN_USER_INPUT;
                break;
            }
            default:
                break;
        }
        
    }

    /* Execution should not come here during normal operation */

    return ( EXIT_FAILURE );
}

Library Interface

Controller Area Network peripheral library provides the following interfaces:

Functions

Name Description
CANx_Initialize Initializes given instance of the CAN peripheral
CANx_MessageTransmit Transmits a message into CAN bus
CANx_MessageReceive Receives a message from CAN bus
CANx_MessageAbort Abort request for a Mailbox
CANx_MessageIDSet Set Message ID in mailbox
CANx_MessageIDGet Get Message ID from mailbox
CANx_MessageAcceptanceMaskSet Set Message acceptance identifier mask in mailbox
CANx_MessageAcceptanceMaskGet Get Message acceptance identifier mask from mailbox
CANx_MessageTimestampGet Get the message timestamp from a mailbox
CANx_ErrorGet Returns the error during transfer
CANx_ErrorCountGet Returns the transmit and receive error count during transfer
CANx_InterruptGet Returns the Interrupt status
CANx_InterruptEnable Enables CAN Interrupt
CANx_InterruptDisable Disables CAN Interrupt
CANx_MailboxIsReady Returns true if Mailbox is ready otherwise false
CANx_BitTimingCalculationGet Returns the bit timing information
CANx_BitTimingSet Sets the bit timing
CANx_TxCallbackRegister Sets the pointer to the function (and it's context) to be called when the given CAN's Tx transfer events occur
CANx_RxCallbackRegister Sets the pointer to the function (and it's context) to be called when the given CAN's Rx transfer events occur

Data types and constants

Name Type Description
CAN_MAILBOX_TX_ATTRIBUTE Enum CAN Mailbox TX Attribute for Transmit mailbox, Producer Tx Mailbox and Consumer Tx Mailbox
CAN_MAILBOX_RX_ATTRIBUTE Enum CAN Mailbox RX Attribute for Reception Mailbox, Reception Mailbox with overwrite, Consumer Rx Mailbox and Producer Rx Mailbox
CAN_MSG_RX_ATTRIBUTE Enum CAN Message RX Attribute for Data Frame and Remote Frame
CAN_MAILBOX_NUM Enum CAN Mailbox Number
CAN_MAILBOX_MASK Enum CAN Mailbox Mask
CAN_ERROR Enum CAN Transfer Error data type
CAN_INTERRUPT_MASK Enum CAN Interrupt Mask
CAN_MAILBOX_STATE Enum CAN Mailbox State
CAN_CALLBACK Typedef CAN Callback Function Pointer
CAN_RX_MSG Struct CAN PLib Object structure
CAN_MAILBOX_CALLBACK Struct CAN transfer event callback structure
CAN_NOMINAL_BIT_TIMING Struct Nominal bit timing parameters
CAN_BIT_TIMING Struct Bit timing parameters