Skip to content

CAN

Introduction

The MikroSDK.Driver.CAN library offers a comprehensive set of functions and data structures for handling CAN communication. This includes setting up CAN configurations, filters, and handling CAN messages. The library is designed to be platform-independent, ensuring compatibility with various microcontrollers.

API Name

  • MikroSDK.Driver.CAN

API Files

Prerequisites

  • Library Manager:

    • Select Driver.CAN to add the API to your project using the Library Manager in NECTO Studio.
  • Headers:

    • Include the Driver.CAN header in your source files to access the CAN functions.
    • This header pops up in the lower right part of NECTO Studio once you perform the previous task (while selecting Driver.CAN in Library Manager)
    • Alternatively, include the drv_can.h header in your source files to access the CAN functions.
  • CMakeLists:

    • You do not have to perform anything, configuration (CMake) file is already taken care of automatically while you performed the first task in this Prerequisites!
    • You can ensure that your CMakeLists.txt includes the necessary configurations to link against the MikroSDK.Driver.CAN library.

Code Examples

  • How to properly test CAN?
/* Project name:
 *   CAN Loopback Mode Demo
 * Copyright:
 *   (c) MIKROE, 2024.
 * Description:
 *   Example is meant for demonstrating CAN Loopback Mode functionality
 *   using mikroSDK 2.10.0. If pin that is defined as TEST_PIN_SUCCESS
 *   is blinking, then data transmission was a success.
 *   For NXP, PIC32 and RISC-V architectures you need to provide CAN
 *   signals with the same baud rate to HIGH and LOW CAN pins in order
 *   to make Loopback Mode work. If you don't set baud rate manually
 *   using can_set_frequency() API, 125000 will be set by default.
 * Library dependencies?
 *   - Make sure `Driver.CAN`, `Driver.GPIO.Out`, `Driver.GPIO.Port`,
 *     `Log`, `Board` and `mikroSDKVersion` libraries are enabled
 *     in NECTO's Library Manager to ensure a successful build.
 */

/**
 * @brief For a detailed explanation of this demo, please visit:
 * <https://libstock.mikroe.com/projects/view/5624/can-loopback-mode-demo>
 */

// ------------------------------------------------------------------ INCLUDES

/**
 * Any initialization code needed for MCU to function properly.
 * Do not remove this line or clock might not be set correctly.
 */
#ifdef PREINIT_SUPPORTED
#include "preinit.h"
#endif

#ifdef MikroCCoreVersion
    #if MikroCCoreVersion >= 1
        #include "delays.h"
    #endif
#endif

#include "MikroSDK.Board"
#include "MikroSDK.Driver.CAN"
#include "MikroSDK.mikroSDKVersion"
#include "MikroSDK.Driver.GPIO.Out"
#include "MikroSDK.Driver.GPIO.Port"

#if mikroSDK_GET_VERSION < 21000ul
    #error "SDK version must be 2.10.0 or higher."
#endif

#if mikroSDK_GET_VERSION >= 21101
    #define UPDATED_CAN_IMPLEMENTATION
#endif

// -------------------------------------------------------------------- MACROS

#ifndef CAN_RX
#define CAN_RX HAL_PIN_NC // TODO define CAN RX pin
#endif
#ifndef CAN_TX
#define CAN_TX HAL_PIN_NC // TODO define CAN TX pin
#endif

#ifndef UPDATED_CAN_IMPLEMENTATION
// HW specific as TIVA architecture is using FIFO1 for transmitting.
#ifndef tiva
#define CAN_FILTER_FIFO_DEFAULT CAN_FILTER_FIFO1
#else
#define CAN_FILTER_FIFO_DEFAULT CAN_FILTER_FIFO2
#endif
#define CAN_FILTER_BANK_DEFAULT 0
#define CAN_BAUDRATE_VALUE      125000
#endif

#define TEST_PIN_CAN_RX CAN_RX // TODO define CAN RX pin
#define TEST_PIN_CAN_TX CAN_TX // TODO define CAN TX pin

// TODO Define test pins according to hardware.
#define TEST_PIN_1        HAL_PIN_NC
#define TEST_PIN_2        HAL_PIN_NC
#define TEST_PIN_3        HAL_PIN_NC
#define TEST_PIN_4        HAL_PIN_NC
#define TEST_PIN_5        HAL_PIN_NC
#define TEST_PIN_6        HAL_PIN_NC
#define TEST_PIN_7        HAL_PIN_NC
#define TEST_PIN_8        HAL_PIN_NC
#define TEST_PIN_SUCCESS  HAL_PIN_NC

// TODO Test different byte values with different filter configurations.
#define BYTE1 0x01
#define BYTE2 0x32
#define BYTE3 0xAB
#define BYTE4 0xCD
#define BYTE5 0x45
#define BYTE6 0x89
#define BYTE7 0xEF
#define BYTE8 0x67

#define signal_error(pin) digital_out_init( &test_pin, pin ); \
                          digital_out_high( &test_pin ); \
                          while(1)
#define signal_success    digital_out_high( &test_pin_success ); \
                          Delay_1sec(); \
                          digital_out_low( &test_pin_success ); \
                          Delay_1sec();

// ----------------------------------------------------------------- VARIABLES

static can_config_t can_config_struct;
static can_filter_config_t can_filter_config_struct;
static can_t can_struct;
static uint8_t byte_check[8];
can_transmit_message_struct tx_message;
can_receive_message_struct rx_message;
static digital_out_t test_pin, test_pin_success;

// ----------------------------------------------------------------- USER CODE
int main(void) {
    /* Do not remove this line or clock might not be set correctly. */
    #ifdef PREINIT_SUPPORTED
    preinit();
    #endif

    // Set default configuration for CAN module.
    can_configure_default( &can_config_struct );
    can_filter_configure_default( &can_filter_config_struct );

    // Check defined pins and get the CAN module number.
    can_config_struct.rx_pin = TEST_PIN_CAN_RX;
    can_config_struct.tx_pin = TEST_PIN_CAN_TX;
    if( ACQUIRE_FAIL == can_open( &can_struct, &can_config_struct )) {
        signal_error( TEST_PIN_1 );
    }

    // Initialize the CAN module.
    if( CAN_ERROR == can_init( &can_struct )) {
        signal_error( TEST_PIN_2 );
    }

    // Set CAN filer parameters.
    can_filter_config_struct.can_filter_scale = CAN_FILTER_SCALE_32BIT;
    // NOTE: If you don't have any filters enabled for FIFO, message won't be received.
    can_filter_config_struct.can_filter_enable = CAN_FILTER_ENABLE;
    can_filter_config_struct.can_filter_frame_type[0] = CAN_FRAME_TYPE_DATA;
    can_filter_config_struct.can_filter_frame_type[1] = CAN_FRAME_TYPE_DATA;
    can_filter_config_struct.can_filter_id = 0x0000123ul;
    can_filter_config_struct.can_filter_mask_id = 0x00000FFFul;
    can_filter_config_struct.can_filter_mode = CAN_FILTER_MODE_IDMASK;
    can_filter_config_struct.can_filter_fifo = CAN_FILTER_FIFO_DEFAULT;
    can_filter_config_struct.can_filter_bank = CAN_FILTER_BANK_DEFAULT;
    if( CAN_ERROR == can_set_filter( &can_struct, &can_filter_config_struct ) ) {
        signal_error( TEST_PIN_3 );
    }

    // Set the Loopback Mode operation.
    if( CAN_ERROR == can_set_mode( &can_struct, CAN_MODE_LOOPBACK )) {
        signal_error( TEST_PIN_4 );
    }

    #ifndef UPDATED_CAN_IMPLEMENTATION
    // Set the baud rate value.
    if( CAN_ERROR == can_set_frequency( &can_struct, CAN_BAUDRATE_VALUE )) {
        signal_error( TEST_PIN_5 );
    }
    #endif

    // Set bytes of data for transmission.
    tx_message.message.message_data[0] = BYTE1;
    tx_message.message.message_data[1] = BYTE2;
    tx_message.message.message_data[2] = BYTE3;
    tx_message.message.message_data[3] = BYTE4;
    tx_message.message.message_data[4] = BYTE5;
    tx_message.message.message_data[5] = BYTE6;
    tx_message.message.message_data[6] = BYTE7;
    tx_message.message.message_data[7] = BYTE8;
    tx_message.message.data_len = 8;
    // Set the format and ID of the transmit message.
    tx_message.message.frame_format = CAN_FRAME_FORMAT_STANDARD_11BITS;
    tx_message.message.std_id = 0x48cUL;
    tx_message.message.ext_id = 0x01;
    // Send the data.
    if( CAN_ERROR == can_transmit( &can_struct, &tx_message )) {
        signal_error( TEST_PIN_6 );
    }

    Delay_100ms();

    // Define an RX FIFO that should have the received message in it.
    // NOTE if no data was received by this FIFO can_receive will return error.
    rx_message.rx_fifo_number = CAN_FILTER_FIFO_DEFAULT;
    // Get the data.
    if( CAN_ERROR == can_receive( &can_struct, &rx_message )) {
        signal_error( TEST_PIN_7 );
    }

    Delay_100ms();

    digital_out_init( &test_pin_success, TEST_PIN_SUCCESS );

    // Define an array for checking received bytes.
    byte_check[0] = BYTE1;
    byte_check[1] = BYTE2;
    byte_check[2] = BYTE3;
    byte_check[3] = BYTE4;
    byte_check[4] = BYTE5;
    byte_check[5] = BYTE6;
    byte_check[6] = BYTE7;
    byte_check[7] = BYTE8;

    // Check if received data is the same as expected.
    if( !memcmp( &byte_check, &rx_message.message.message_data, sizeof( byte_check )))
    {
        while(1) {
            signal_success;
        }
    }
    else {
        signal_error( TEST_PIN_8 );
    }

    return 0;
}

// ----------------------------------------------------------------------- END