CAN Example

End-to-End Example

There are three layers we are dealing with which build on top of each other to provide a robust way to send and receive CAN messages.

Layer

Purpose

Layer

Purpose

IO Stream

Provide Application IO for CAN

HAL CAN

Provide Queue based functionality

HW CAN

Provide interrupt-driven callbacks for HW CAN mailboxes

 

1. IO STREAM

  • High Level application code interface

  • Agnostic to the concept of CAN HW mailboxes, interrupts, or queues

  • Unaware of HW CAN mailbox filters

  • Allows timeouts to provide event-driven IO

    • io_stream_glue.c re-directs to HAL_CAN to provide polled or event driven operation

    • To deliberately use a polled mechanism, the timeout can be set to zero.

2. HAL CAN

  • Generic CAN interface for diverse CAN peripherals

  • Supports the concept of HW CAN mailboxes and message ID filters

  • Adds RTOS constructs on top of the HW CAN layer

    • Uses ISR callbacks to add queue capability for transmission or reception of CAN frames

3. HW CAN

  • Fundamentally a register writer which encapsulates a CAN peripheral

  • Provides interrupts and a callback function for mailbox transmission and receive events

Code Example

A micro-application that wants to utilize the CAN bus can use the IO Stream interface and can be designed to be agnostic to the CAN driver. It can simply say that it wants to open a /dev/CAN0/rx_all device, which is an arbitrary string value, and the io_stream_glue.c shall associate the string to configure the HAL_CAN HW mailbox to meet the objective.

The HAL CAN Configuration sample code given below can be referenced to setup mailboxes in a way to a facilitate the IO Stream descriptors for transmission and reception. For example, if there is a UDS firmware download application, then the IO Stream descriptor of /dev/CAN0/UDS should be match the configuration of a transmission mailbox with possibly an RTOS queue.

Application Interface

All use cases of the CAN bus should be supported through the IO Stream interface.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void uds_tx_app(void) { io_stream_T uds_tx = io_stream__open("/dev/can0/uds", IO_STREAM__FLAGS_OUTPUT); const bool extended_id = false; sl_can__message_S msg = sl_can__create_message(0x123, !extended_id, 8); io_stream__write(uds_tx, &msg, sizeof(msg), 1); } void micro_rx_app(void) { io_stream_T gateway = io_stream__open("/dev/can0/all", IO_STREAM__FLAGS_INPUT); const uint32_t timeout_ms = 10; io_stream__control(gateway, IO_STREAM__CONTROL_SET_READ_TIMEOUT, &timeout_ms); // Attempt to read a message within a timeout sl_can__message_S msg = {}; if (io_stream__read(gateway, &msg, sizeof(msg), 1) == sizeof(msg)) { } }

IO Stream Glue

IO Stream glues together the lower-level CAN driver.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include "io_stream.h" #include "hal_can.h" // The device types your platform supports typedef enum { IO_DEV_NONE = 0, IO_DEV_CAN0_UDS_TX, } io_dev_E; /// @returns true if the starts_with is at the beginning of str static bool str_starts_with(const char *str, const char *starts_with) { return str == strstr(str, starts_with); } io_stream_T io_stream__open(const char *path, const uint32_t flags) { if (str_starts_with(path, "/dev/can0/uds")) { return IO_DEV_CAN0_UDS_TX; } } /** * When a request to write arrives with the IO_DEV_CAN0_UDS_TX descriptor * we simply forward the request to the "HAL CAN" driver * * The configuration of the hal_can__mailbox_0 mailbox should have already * occurred during the initialization of the HAL CAN driver */ int io_stream__write(io_stream_T device, const void *output_buffer, size_t bytes_per_element, size_t num_elements) { int bytes_written = 0; switch (device) { case IO_DEV_CAN0_UDS_TX: if (bytes_per_element != sizeof(sl_can__message_S)) { bytes_written = 0; } else { const sl_can__message_S *msg_ptr = (sl_can__message_S *) output_buffer; if (hal_can__transmit(hal_can_0, hal_can__mailbox_0, msg_ptr, 0)) { bytes_written = sizeof(sl_can__message_S); } } break; default: break; } return bytes_written; }

HAL CAN Configuration

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 // Static FreeRTOS queues StaticQueue_t static_can_mailbox_0_queue; StaticQueue_t static_can_mailbox_1_queue; // Transmit mailbox configuration hal_can_transmit_config_S transmit_config[] = { [0] = { .mailbox_id = hal_can_mailbox_0, .queue_handle = &static_can_mailbox_0_queue}, ... }; // Receive mailbox configuration hal_can_receive_config_S receive_config[] = { [0] = { .mailbox_id = hal_can_mailbox_1, .queue_handle = &static_can_mailbox_1_queue, .filter = { .message_id = 0x710, .mask = 0xFF0 }}, ... }; // HAL CAN configuration hal_can__config_S can_config = { .receive_config = receive_config, .receive_config_size = ARRAY_SIZE(receive_config), .transmit_config = transmit_config, .transmit_config_size = ARRAY_SIZE(transmit_config), .baud_rate = HAL_CAN__BAUD_RATE_500K, .clock_speed_hz = 12UL * 1000 * 1000 }; // HAL CAN initialization void main(void) { static sl_can__message_S queue_memory_0[1]; static sl_can__message_S queue_memory_1[2]; // Initialize the FreeRTOS static queues xQueueCreateStatic(1, sizeof(sl_can__message_S), queue_memory_0, &static_can_mailbox_0_queue); xQueueCreateStatic(2, sizeof(sl_can__message_S), queue_memory_1, &static_can_mailbox_1_queue); hal_can_init(hal_can_0, &can_config); hal_can__enter_active_mode(hal_can_0); }