3.1 CAN 2.0/CAN FD Driver

3.1.1 Introduction

Overview

CAN FD (Controller Area Network Flexible Data-Rate) is a two-wire data communication protocol typically used for broadcasting sensor data and control information between different parts of electronic instrumentation and control systems. This protocol is generally used in modern high performance vehicles.

The MPLAB® Code Configurator (MCC) Melody CAN FD Driver generates a portable abstract interface for CAN hardware functions. The associated CAN Peripheral Library (PLIB) will generate the hardware-specific implementation.

This reference document covers implementation of the CAN FD Driver only. The implementation of CAN FD and CAN 2.0 Driver are almost similar. The additional features supported by CAN FD are mentioned wherever applicable.

Features

Configurable Clock settings
  • CAN FD operating frequency.
  • Nominal(Arbitration) Bit Rate.
  • Data Bit Rate - Supported only in CAN FD mode.
Configurable ISO CRC for CAN FD frames
FIFO Settings
  • Configurable FIFO for Transmit and Receive operation.
  • Configurable FIFO depth, payload and transmit priority.
  • User can provide Custom Name for the FIFOs.
  • Interrupt based Transmit and Receive operations.
Receive-Filter Settings
  • Supports both SID(11 bit) and EID(29 bit) type Message IDs.
  • 12 bit SIDs supported only in CAN FD base frames.
  • Displays Permissible ID based on Message IDs allowed for a given filter. Permissible ID is a binary depiction of Message ID that can pass through filter.

3.1.2 Module Documentation

3.1.2.1 CAN-FD Use Cases

Note:

These use cases are also applicable for CAN 2.0 Driver. For CAN 2.0 configuration, make sure that the data byte length is not more than 8 bytes.

3.1.2.1.1 Transmission of CAN frames

Use Case 1: Transmit CAN FD Message Periodically

This example shows how to transmit a CAN FD message with an Extended ID of 0x1FFFF, DLC of 16, data values of 0 to 15, and with bit rate switching enabled. The message is transmitted periodically every 1s.

Note:

This example assumes that a UART is configured with STDIO enabled for the printf functions to properly work.

#include "mcc_generated_files/system/system.h"

#define DATA_BYTE_LENGTH (16)
#define TIME_1S (1000)

void CANFD_Example_TransmitFrames(void)
{
    uint8_t transmitData[DATA_BYTE_LENGTH];
    enum CAN_TX_MSG_REQUEST_STATUS transmitStatus;
    struct CAN_MSG_OBJ transmitMsgObj;
 
    SYSTEM_Initialize();
 
    //Assign values to the data bytes to be transmitted
    for (uint8_t i = 0; i <  DATA_BYTE_LENGTH; i++)
    {
        transmitData [i] = i + 1;
    }
 
    // Create a transmit message object of the required frame consisting of message id, control bits and payload.
    if (CAN_NORMAL_FD_MODE == CAN_NODE.OperationModeGet())
    {
        transmitMsgObj.msgId = 0x1FFFF;
        transmitMsgObj.field.formatType = CAN_FD_FORMAT;
        transmitMsgObj.field.brs = CAN_BRS_MODE;
        transmitMsgObj.field.frameType = CAN_FRAME_DATA;
        transmitMsgObj.field.idType = CAN_FRAME_EXT;
        transmitMsgObj.field.dlc = DLC_16;
        transmitMsgObj.data = transmitData;
    }
 
    while (1)
    {
        // Before transmitting any message, the Transmitter FIFO status needs to be checked
        if (CAN_TX_FIFO_AVAILABLE == CAN_NODE.TransmitFIFOStatusGet(CAN1_TXQ))
        {
            // Writes a message object to CAN TX FIFO and returns the status of CAN transmit message
            transmitStatus = CAN_NODE.Transmit(CAN1_TXQ, &transmitMsgObj);
            // Checking cause of error if Transmit message object was not successfully placed into transmitter FIFO
            if (CAN_TX_MSG_REQUEST_SUCCESS != transmitStatus)
            {
                if (CAN_TX_MSG_REQUEST_DLC_EXCEED_ERROR == transmitStatus)
                {
                    printf("Transmit Error : Message object DLC size exceeds the FIFO configured DLC size \r\n");
                }
                else if (CAN_TX_MSG_REQUEST_BRS_ERROR == transmitStatus)
                {
                    printf("Transmit error : Bit rate switching not enabled for FIFO \r\n");
                }
                else if (CAN_TX_MSG_REQUEST_FIFO_FULL == transmitStatus)
                {
                    printf("Transmit error : Transmit FIFO full \r\n");
                }
            }
            else
            {
                printf("Message transmitted successfully \r\n");
            }
        }
        else
        {
            printf("Transmit FIFO FULL \n");
        }
        __delay_ms(TIME_1S);
    }
}
Note:

The custom name of CAN1 Driver Interface can be changed by the user in the Melody Driver User interface. This allows defining a structure with application specific name using the 'Custom Name' field as shown below. Application specific name allows the portability of APIs.

3.1.2.1.2 Receiving CAN frames

Use Case 2: Receive a CAN FD Message from a Particular FIFO (Interrupt Method)

This example shows how to check and read a CAN message from FIFO1 using the interrupt method.

Note:
This example assumes that a UART is configured with STDIO enabled for the printf functions to properly work. Enable RX FIFO Not Empty Interrupt for FIFO1 using Melody Driver User interface. Also enable the global interrupts in the application code.  

#include "mcc_generated_files/system/system.h"

#define DATA_BYTE_LENGTH (16)

volatile bool msgReceivedInFIFO1 = false;
volatile struct CAN_MSG_OBJ receiveMsgObj;
void CAN1_UserFIFO1InterruptHandler(void);
 
void CANFD_Example_Receive_FIFO1_Interrupt(void)
{
    SYSTEM_Initialize();
 
    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();
 
    // Set user interrupt handler which is called whenever the Receive buffer of FIFO1 is not empty.       
    CAN1_FIFO1NotEmptyCallbackRegister(CAN1_UserFIFO1InterruptHandler);
    
    while (1)
    {
        //Checking if any message was read from FIFO1.If true, print the data bytes of message frame received in the terminal.
        if (msgReceivedInFIFO1 == true)
        {
            printf("\r\nThe data received from CAN message: \r\n");
            for (uint8_t i = 0; i < DATA_BYTE_LENGTH; i++)
            {
                printf("%d \t", receiveMsgObj.data[i]);
            }
            msgReceivedInFIFO1 = false;
        }
    }
}
 
void CAN1_UserFIFO1InterruptHandler(void)
{
    // Reads the message object from the specified CAN receive FIFO
    CAN_NODE.ReceiveMessageGet(FIFO1, &receiveMsgObj);
    msgReceivedInFIFO1 = true;
}
Use Case 3: Receive a CAN FD Message from a Particular FIFO (Polling Method)

This example shows how to check and read a CAN message from FIFO1 using the polling method.

Note:
This example assumes that a UART is configured with STDIO enabled for the printf functions to properly work.
#include "mcc_generated_files/system/system.h"

#define DATA_BYTE_LENGTH (16)

void CANFD_Eaxmple_Receive_From_FIFO1_Polling(void)
{
    struct CAN_MSG_OBJ receiveMsgObj;
 
    SYSTEM_Initialize();
 
    while (1)
    {
        //Check if the receive FIFO (FIFO1) is not empty    
        if (CAN_RX_MSG_AVAILABLE == (CAN_NODE.ReceiveFIFOStatusGet(CAN1_FIFO_1) & CAN_RX_MSG_AVAILABLE))
        {
            //Reads the receive message object from the specified CAN Receive FIFO
            CAN_NODE.ReceiveMessageGet(CAN1_FIFO_1, &receiveMsgObj);
 
            // Read the data byte of message received and print it on terminal
            printf("\nThe data received from CAN message: \r\n");
            for (uint8_t i = 0; i < DATA_BYTE_LENGTH; i++)
            {
                printf("%d \t", receiveMsgObj.data[i]);
            }
        }
    }
}
Use Case 4: Receive a CAN FD Message from any FIFO (Polling Method)

This example shows how to check and read a CAN message from any FIFO using polling method.

Note:
This example assumes that a UART is configured with STDIO enabled for the printf functions to properly work.
#include "mcc_generated_files/system/system.h"

#define DATA_BYTE_LENGTH (16)

void CANFD_Example_Receive_From_Any_FIFO(void)
{
    struct CAN_MSG_OBJ receiveMsgObj;
 
    SYSTEM_Initialize();
 
    while (1)
    {
        //Checking if messages are received in any of the RX FIFO
        if (CAN_NODE.ReceivedMessageCountGet() > 0)
        {
            // Reads a receive message object from CAN RX FIFO by polling each of the RX FIFO.
            if (true == CAN_NODE.Receive(&receiveMsgObj))
            {
                // Read the data byte of message received and print it on terminal
                printf("\nThe data received from CAN message: \r\n");
                for (uint8_t i = 0; i < DATA_BYTE_LENGTH; i++)
                {
                    printf("%d \t", receiveMsgObj.data[i]);
                }
            }
            else
            {
                printf("Error : Message read from FIFO failed \r\n");
            }
        }
    }
}

3.1.2.1.3 Sleep and Wakeup Example

Use Case 5: Low power sleep mode and wake-up sequence

This example shows how to use low power sleep mode with CAN FD and the wake-up due to activity in the CAN bus.

Note:
This example assumes that a UART is configured with STDIO enabled for the printf functions to properly work.
#include "mcc_generated_files/system/system.h"

volatile bool busWakeUpOccured = false;
void CAN1_SleepAndWakeUpUserInterruptHandler(void);
 
void CANFD_Example_Sleep_Wakeup_Sequence(void)
{
    SYSTEM_Initialize();
 
    CAN_NODE.BusWakeUpActivityCallbackRegister(CAN1_SleepAndWakeUpUserInterruptHandler);
 
    // Enable the Global Interrupts
    INTERRUPT_GlobalInterruptEnable();
 
    /*
     * Switching CAN FD Module to disable mode. This has to be done before calling
     * SLEEP instruction. Also enable Wake up interrupt whenever bus activity is detected
     */
    CAN_NODE.SleepMode();
 
    //Checking if CAN module has entered to Disable mode
    if (CAN_DISABLE_MODE == CAN_NODE.OperationModeGet())
    {
        printf("[*] System entering into sleep.\r\n");
 
        //Once CAN module is in disable mode, the system is put to sleep after sleep mode request   
        Sleep();
        /*
         The module wakes up from sleep whenever any activity is detected on CAN receive line
         * ie when a falling edge is detected on CAN Rx line.
         * A flag is set in user interrupt handler to monitor if system woke up from sleep mode
         * due to activity in CAN Rx line
         */
        NOP();
        if (busWakeUpOccured == true)
        {
            printf("[*] System wakeup and CAN module wake-up due to activity in CAN bus\r\n");
 
            // Recover to Normal mode
            if (CAN_OP_MODE_REQUEST_SUCCESS == CAN_NODE.OperationModeSet(CAN_CONFIGURATION_MODE))
            {
                if (CAN_OP_MODE_REQUEST_SUCCESS == CAN_NODE.OperationModeSet(CAN_NORMAL_FD_MODE))
                {
                    printf("CAN module switched to Normal CAN FD mode \r\n");
                }
            }
            busWakeUpOccured = false;
        }
        else
        {
            printf("System wake up due to event other than can bus activity \r\n ");
        }
    }
}
 
void CAN1_SleepAndWakeUpUserInterruptHandler(void)
{
    busWakeUpOccured = true;
}

3.1.3 Class Documentation

3.1.3.1 CAN_INTERFACE Struct Reference

Contains the function pointers of CAN driver.

3.1.3.1.1 Detailed Description

Contains the function pointers of CAN driver.

#include <can_interface.h>

Public Attributes

3.1.3.1.2 Member Data Documentation

The documentation for this struct was generated from the following file:

source/

can_interface.h

BusErrorCallbackRegister

void(* BusErrorCallbackRegister) (void(*CallbackHandler)(void))

Pointer to CANx_BusErrorCallbackRegister e.g. CAN1_BusErrorCallbackRegister

BusWakeUpActivityCallbackRegister

void(* BusWakeUpActivityCallbackRegister) (void(*CallbackHandler)(void))

Pointer to CANx_BusWakeUpActivityCallbackRegister e.g. CAN1_BusWakeUpActivityCallbackRegister

Deinitialize

void(* Deinitialize) (void)

Pointer to CANx_Deinitialize e.g. CAN1_Deinitialize

Initialize

void(* Initialize) (void)

Pointer to CANx_Initialize e.g. CAN1_Initialize

InvalidMessageCallbackRegister

void(* InvalidMessageCallbackRegister) (void(*CallbackHandler)(void))

Pointer to CANx_InvalidMessageCallbackRegister e.g. CAN1_InvalidMessageCallbackRegister

IsBusOff

bool(* IsBusOff) (void)

Pointer to CANx_IsBusOff e.g. CAN1_IsBusOff

IsRxErrorActive

bool(* IsRxErrorActive) (void)

Pointer to CANx_IsRxErrorActive e.g. CAN1_IsRxErrorActive

IsRxErrorPassive

bool(* IsRxErrorPassive) (void)

Pointer to CANx_IsRxErrorPassive e.g. CAN1_IsRxErrorPassive

IsRxErrorWarning

bool(* IsRxErrorWarning) (void)

Pointer to CANx_IsRxErrorWarning e.g. CAN1_IsRxErrorWarning

IsTxErrorActive

bool(* IsTxErrorActive) (void)

Pointer to CANx_IsTxErrorActive e.g. CAN1_IsTxErrorActive

IsTxErrorPassive

bool(* IsTxErrorPassive) (void)

Pointer to CANx_IsTxErrorPassive e.g. CAN1_IsTxErrorPassive

IsTxErrorWarning

bool(* IsTxErrorWarning) (void)

Pointer to CANx_IsTxErrorWarning e.g. CAN1_IsTxErrorWarning

ModeChangeCallbackRegister

void(* ModeChangeCallbackRegister) (void(*CallbackHandler)(void))

Pointer to CANx_ModeChangeCallbackRegister e.g. CAN1_ModeChangeCallbackRegister

OperationModeGet

enum CAN_OP_MODES(* OperationModeGet) (void)

Pointer to CANx_OperationModeGet e.g. CAN1_OperationModeGet

OperationModeSet

enum CAN_OP_MODE_STATUS(* OperationModeSet) (const enum CAN_OP_MODES requestMode)

Pointer to CANx_OperationModeSet e.g. CAN1_OperationModeSet

Receive

bool(* Receive) (struct CAN_MSG_OBJ *rxCanMsg)

Pointer to CANx_Receive e.g. CAN1_Receive

ReceivedMessageCountGet

uint8_t(* ReceivedMessageCountGet) (void)

Pointer to CANx_ReceivedMessageCountGet e.g. CAN1_ReceivedMessageCountGet

ReceiveFIFOStatusGet

uint8_t(* ReceiveFIFOStatusGet) (const enum CAN_RX_FIFO_CHANNELS fifoChannel)

Pointer to CANx_ReceiveFIFOStatusGet e.g. CAN1_ReceiveFIFOStatusGet

ReceiveMessageGet

bool(* ReceiveMessageGet) (const enum CAN_RX_FIFO_CHANNELS fifoChannel, struct CAN_MSG_OBJ *rxCanMsg)

Pointer to CANx_ReceiveMessageGet e.g. CAN1_ReceiveMessageGet

RxBufferOverFlowCallbackRegister

void(* RxBufferOverFlowCallbackRegister) (void(*CallbackHandler)(void))

Pointer to CANx_RxBufferOverFlowCallbackRegister e.g. CAN1_RxBufferOverFlowCallbackRegister

SleepMode

void(* SleepMode) (void)

Pointer to CANx_Sleep e.g. CAN1_Sleep

SystemErrorCallbackRegister

void(* SystemErrorCallbackRegister) (void(*CallbackHandler)(void))

Pointer to CANx_SystemErrorCallbackRegister e.g. CAN1_SystemErrorCallbackRegister

Tasks

void(* Tasks) (void)

Pointer to CANx_Tasks e.g. CAN1_Tasks (Supported only in polling mode)

Transmit

enum CAN_TX_MSG_REQUEST_STATUS(* Transmit) (const enum CAN_TX_FIFO_CHANNELS fifoChannel, struct CAN_MSG_OBJ *txCanMsg)

Pointer to CANx_Transmit e.g. CAN1_Transmit

TransmitFIFOStatusGet

enum CAN_TX_FIFO_STATUS(* TransmitFIFOStatusGet) (const enum CAN_TX_FIFO_CHANNELS fifoChannel)

Pointer to CANx_TransmitFIFOStatusGet e.g. CAN1_TransmitFIFOStatusGet

TxAttemptCallbackRegister

void(* TxAttemptCallbackRegister) (void(*CallbackHandler)(void))

Pointer to CANx_TxAttemptCallbackRegister e.g. CAN1_TxAttemptCallbackRegister

3.1.4 File Documentation

3.1.4.1 source/can_examples.dox File Reference

3.1.4.2 source/can_interface.h File Reference

This is the interface header file for the CAN driver.

#include <stdbool.h>
#include <stdint.h>
#include "can_types.h"

3.1.4.2.1 Data structures

  • struct CAN_INTERFACE

    Contains the function pointers of CAN driver.

3.1.4.2.2 Detailed Description

This is the interface header file for the CAN driver.

CAN Generated Driver Interface Header File

Version: CAN Driver Version 1.0.0