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
#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 |