CAN Driver

The following commit contains the first version of the ESP32 CAN Driver.

closes #544
This commit is contained in:
Darian Leung 2017-12-18 20:32:29 +08:00
parent a3c43251b4
commit 1d2727f4c8
36 changed files with 3546 additions and 0 deletions

View file

@ -639,6 +639,12 @@ example_test_003_01:
- ESP32
- Example_SDIO
example_test_004_01:
<<: *example_test_template
tags:
- ESP32
- Example_CAN
UT_001_01:
<<: *unit_test_template
tags:

937
components/driver/can.c Normal file
View file

@ -0,0 +1,937 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_types.h"
#include "esp_log.h"
#include "esp_intr_alloc.h"
#include "soc/dport_reg.h"
#include "soc/can_struct.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "driver/can.h"
/* ---------------------------- Definitions --------------------------------- */
//Internal Macros
#define CAN_CHECK(cond, ret_val) ({ \
if (!(cond)) { \
return (ret_val); \
} \
})
#define CAN_CHECK_FROM_CRIT(cond, ret_val) ({ \
if (!(cond)) { \
CAN_EXIT_CRITICAL(); \
return ret_val; \
} \
})
#define CAN_SET_FLAG(var, mask) ((var) |= (mask))
#define CAN_RESET_FLAG(var, mask) ((var) &= ~(mask))
#define CAN_TAG "CAN"
//Driver default config/values
#define DRIVER_DEFAULT_EWL 96 //Default Error Warning Limit value
#define DRIVER_DEFAULT_TEC 0 //TX Error Counter starting value
#define DRIVER_DEFAULT_REC 0 //RX Error Counter starting value
#define DRIVER_DEFAULT_CLKOUT_DIV 14 //APB CLK divided by two
#define DRIVER_DEFAULT_INTERRUPTS 0xE7 //Exclude data overrun
#define DRIVER_DEFAULT_ERR_PASS_CNT 128 //Error counter threshold for error passive
//Command Bit Masks
#define CMD_TX_REQ 0x01 //Transmission Request
#define CMD_ABORT_TX 0x02 //Abort Transmission
#define CMD_RELEASE_RX_BUFF 0x04 //Release Receive Buffer
#define CMD_CLR_DATA_OVRN 0x08 //Clear Data Overrun
#define CMD_SELF_RX_REQ 0x10 //Self Reception Request
#define CMD_TX_SINGLE_SHOT 0x03 //Single Shot Transmission
#define CMD_SELF_RX_SINGLE_SHOT 0x12 //Single Shot Self Reception
//Control flags
#define CTRL_FLAG_STOPPED 0x001 //CAN peripheral in stopped state
#define CTRL_FLAG_RECOVERING 0x002 //Bus is undergoing bus recovery
#define CTRL_FLAG_ERR_WARN 0x004 //TEC or REC is >= error warning limit
#define CTRL_FLAG_ERR_PASSIVE 0x008 //TEC or REC is >= 128
#define CTRL_FLAG_BUS_OFF 0x010 //Bus-off due to TEC >= 256
#define CTRL_FLAG_TX_BUFF_OCCUPIED 0x020 //Transmit buffer is occupied
#define CTRL_FLAG_SELF_TEST 0x040 //Configured to Self Test Mode
#define CTRL_FLAG_LISTEN_ONLY 0x080 //Configured to Listen Only Mode
//Constants use for frame formatting and parsing
#define FRAME_MAX_LEN 13 //EFF with 8 bytes of data
#define FRAME_MAX_DATA_LEN 8 //Max data bytes allowed in CAN2.0
#define FRAME_EXTD_ID_LEN 4 //EFF ID requires 4 bytes (29bit)
#define FRAME_STD_ID_LEN 2 //SFF ID requires 2 bytes (11bit)
#define FRAME_INFO_LEN 1 //Frame info requires 1 byte
#define ALERT_LOG_LEVEL_WARNING CAN_ALERT_ARB_LOST //Alerts above and including this level use ESP_LOGW
#define ALERT_LOG_LEVEL_ERROR CAN_ALERT_TX_FAILED //Alerts above and including this level use ESP_LOGE
/* ------------------ Typedefs, structures, and variables ------------------- */
/* Formatted frame structure has identical layout as TX/RX buffer registers.
This allows for direct copy to/from TX/RX buffer. The two reserved bits in TX
buffer are used in the frame structure to store the self_reception and
single_shot flags. */
typedef union {
struct {
struct {
uint8_t dlc: 4; //Data length code (0 to 8) of the frame
uint8_t self_reception: 1; //This frame should be transmitted using self reception command
uint8_t single_shot: 1; //This frame should be transmitted using single shot command
uint8_t rtr: 1; //This frame is a remote transmission request
uint8_t frame_format: 1; //Format of the frame (1 = extended, 0 = standard)
};
union {
struct {
uint8_t id[FRAME_STD_ID_LEN]; //11 bit standard frame identifier
uint8_t data[FRAME_MAX_DATA_LEN]; //Data bytes (0 to 8)
uint8_t reserved8[2];
} standard;
struct {
uint8_t id[FRAME_EXTD_ID_LEN]; //29 bit extended frame identifier
uint8_t data[FRAME_MAX_DATA_LEN]; //Data bytes (0 to 8)
} extended;
};
};
uint8_t bytes[FRAME_MAX_LEN];
} can_frame_t;
//Control structure for CAN driver
typedef struct {
//Control and status members
uint32_t control_flags;
uint32_t rx_missed_count;
uint32_t tx_failed_count;
uint32_t arb_lost_count;
uint32_t bus_error_count;
intr_handle_t isr_handle;
//TX and RX
QueueHandle_t tx_queue;
QueueHandle_t rx_queue;
int tx_msg_count;
int rx_msg_count;
//Alerts
SemaphoreHandle_t alert_semphr;
uint32_t alerts_enabled;
uint32_t alerts_triggered;
} can_obj_t;
static can_obj_t *p_can_obj = NULL;
static portMUX_TYPE can_spinlock = portMUX_INITIALIZER_UNLOCKED;
#define CAN_ENTER_CRITICAL() portENTER_CRITICAL(&can_spinlock)
#define CAN_EXIT_CRITICAL() portEXIT_CRITICAL(&can_spinlock)
/* ------------------- Configuration Register Functions---------------------- */
static inline esp_err_t can_enter_reset_mode()
{
/* Enter reset mode (required to write to configuration registers). Reset mode
also prevents all CAN activity on the current module and is automatically
set upon entering a BUS-OFF condition. */
CAN.mode_reg.reset = 1; //Set reset mode bit
CAN_CHECK(CAN.mode_reg.reset == 1, ESP_ERR_INVALID_STATE); //Check bit was set
return ESP_OK;
}
static inline esp_err_t can_exit_reset_mode()
{
/* Exiting reset mode will return the CAN module to operating mode. Reset mode
must also be exited in order to trigger BUS-OFF recovery sequence. */
CAN.mode_reg.reset = 0; //Exit reset mode
CAN_CHECK(CAN.mode_reg.reset == 0, ESP_ERR_INVALID_STATE); //Check bit was reset
return ESP_OK;
}
static inline void can_config_pelican()
{
//Use PeliCAN address layout. Exposes extra registers
CAN.clock_divider_reg.can_mode = 1;
}
static inline void can_config_mode(can_mode_t mode)
{
//Configure CAN mode of operation
can_mode_reg_t mode_reg;
mode_reg.val = CAN.mode_reg.val; //Get current value of mode register
if (mode == CAN_MODE_NO_ACK) {
mode_reg.self_test = 1;
mode_reg.listen_only = 0;
} else if (mode == CAN_MODE_LISTEN_ONLY) {
mode_reg.self_test = 0;
mode_reg.listen_only = 1;
} else {
//Default to normal operating mode
mode_reg.self_test = 0;
mode_reg.listen_only = 0;
}
CAN.mode_reg.val = mode_reg.val; //Write back modified value to register
}
static inline void can_config_interrupts(uint32_t interrupts)
{
//Enable interrupt sources
CAN.interrupt_enable_reg.val = interrupts;
}
static inline void can_config_bus_timing(uint32_t brp, uint32_t sjw, uint32_t tseg_1, uint32_t tseg_2, bool triple_sampling)
{
/* Configure bus/bit timing of CAN peripheral.
- BRP (even from 2 to 128) divide APB to CAN system clock (T_scl)
- SJW (1 to 4) is number of T_scl to shorten/lengthen for bit synchronization
- TSEG_1 (1 to 16) is number of T_scl in a bit time before sample point
- TSEG_2 (1 to 8) is number of T_scl in a bit time after sample point
- triple_sampling will cause each bit time to be sampled 3 times*/
can_bus_tim_0_reg_t timing_reg_0;
can_bus_tim_1_reg_t timing_reg_1;
timing_reg_0.baud_rate_prescaler = (brp / 2) - 1;
timing_reg_0.sync_jump_width = sjw - 1;
timing_reg_1.time_seg_1 = tseg_1 - 1;
timing_reg_1.time_seg_2 = tseg_2 - 1;
timing_reg_1.sampling = triple_sampling;
CAN.bus_timing_0_reg.val = timing_reg_0.val;
CAN.bus_timing_1_reg.val = timing_reg_1.val;
}
static inline void can_config_error(int err_warn_lim, int rx_err_cnt, int tx_err_cnt)
{
/* Set error warning limit, RX error counter, and TX error counter. Note that
forcibly setting RX/TX error counters will incur the expected status changes
and interrupts as soon as reset mode exits. */
if (err_warn_lim >= 0 && err_warn_lim <= UINT8_MAX) {
//Defaults to 96 after hardware reset.
CAN.error_warning_limit_reg.byte = err_warn_lim;
}
if (rx_err_cnt >= 0 && rx_err_cnt <= UINT8_MAX) {
//Defaults to 0 after hardware reset.
CAN.rx_error_counter_reg.byte = rx_err_cnt;
}
if (tx_err_cnt >= 0 && tx_err_cnt <= UINT8_MAX) {
//Defaults to 0 after hardware reset, and 127 after BUS-OFF event
CAN.tx_error_counter_reg.byte = tx_err_cnt;
}
}
static inline void can_config_acceptance_filter(uint32_t code, uint32_t mask, bool single_filter)
{
//Set filter mode
CAN.mode_reg.acceptance_filter = (single_filter) ? 1 : 0;
//Swap code and mask to match big endian registers
uint32_t code_swapped = __builtin_bswap32(code);
uint32_t mask_swapped = __builtin_bswap32(mask);
for (int i = 0; i < 4; i++) {
CAN.acceptance_filter.code_reg[i].byte = ((code_swapped >> (i * 8)) & 0xFF);
CAN.acceptance_filter.mask_reg[i].byte = ((mask_swapped >> (i * 8)) & 0xFF);
}
}
static inline void can_config_clk_out(uint32_t divider)
{
/* Configure CLKOUT. CLKOUT is a pre-scaled version of APB CLK. Divider can be
1, or any even number from 2 to 14. Set to out of range value (0) to disable
CLKOUT. */
can_clk_div_reg_t clock_divider_reg;
clock_divider_reg.val = CAN.clock_divider_reg.val;
if (divider >= 2 && divider <= 14) {
clock_divider_reg.clock_off = 0;
clock_divider_reg.clock_divider = (divider / 2) - 1;
} else if (divider == 1) {
clock_divider_reg.clock_off = 0;
clock_divider_reg.clock_divider = 7;
} else {
clock_divider_reg.clock_off = 1;
clock_divider_reg.clock_divider = 0;
}
CAN.clock_divider_reg.val = clock_divider_reg.val;
}
/* ---------------------- Runtime Register Functions------------------------- */
static inline void can_set_command(uint8_t commands)
{
CAN.command_reg.val = commands;
}
static void can_set_tx_buffer_and_transmit(can_frame_t *frame)
{
//Copy frame structure into TX buffer registers
for (int i = 0; i < FRAME_MAX_LEN; i++) {
CAN.tx_rx_buffer[i].val = frame->bytes[i];
}
//Set correct transmit command
uint8_t command;
if (frame->self_reception) {
command = (frame->single_shot) ? CMD_SELF_RX_SINGLE_SHOT : CMD_SELF_RX_REQ;
} else {
command = (frame->single_shot) ? CMD_TX_SINGLE_SHOT : CMD_TX_REQ;
}
can_set_command(command);
}
static inline uint32_t can_get_status()
{
return CAN.status_reg.val;
}
static inline uint32_t can_get_interrupt_reason()
{
return CAN.interrupt_reg.val;
}
static inline uint32_t can_get_arbitration_lost_capture()
{
return CAN.arbitration_lost_captue_reg.val;
//Todo: ALC read only to re-arm arb lost interrupt. Add function to decode ALC
}
static inline uint32_t can_get_error_code_capture()
{
return CAN.error_code_capture_reg.val;
//Todo: ECC read only to re-arm bus error interrupt. Add function to decode ECC
}
static inline void can_get_error_counters(uint32_t *tx_error_cnt, uint32_t *rx_error_cnt)
{
if (tx_error_cnt != NULL) {
*tx_error_cnt = CAN.tx_error_counter_reg.byte;
}
if (rx_error_cnt != NULL) {
*rx_error_cnt = CAN.rx_error_counter_reg.byte;
}
}
static inline void can_get_rx_buffer_and_clear(can_frame_t *frame)
{
//Copy RX buffer registers into frame structure
for (int i = 0; i < FRAME_MAX_LEN; i++) {
frame->bytes[i] = CAN.tx_rx_buffer[i].val;
}
//Clear RX buffer
can_set_command(CMD_RELEASE_RX_BUFF);
}
static inline uint32_t can_get_rx_message_counter()
{
return CAN.rx_message_counter_reg.val;
}
/* -------------------- Interrupt and Alert Handlers ------------------------ */
static void can_alert_handler(uint32_t alert_code, int *alert_req)
{
if (p_can_obj->alerts_enabled & alert_code) {
//Signify alert has occurred
CAN_SET_FLAG(p_can_obj->alerts_triggered, alert_code);
*alert_req = 1;
if (p_can_obj->alerts_enabled & CAN_ALERT_AND_LOG) {
if (alert_code >= ALERT_LOG_LEVEL_ERROR) {
ESP_EARLY_LOGE(CAN_TAG, "Alert %d", alert_code);
} else if (alert_code >= ALERT_LOG_LEVEL_WARNING) {
ESP_EARLY_LOGW(CAN_TAG, "Alert %d", alert_code);
} else {
ESP_EARLY_LOGI(CAN_TAG, "Alert %d", alert_code);
}
}
}
}
static void can_intr_handler_err_warn(can_status_reg_t *status, BaseType_t *task_woken, int *alert_req)
{
if (status->bus) {
if (status->error) {
//Bus-Off condition. TEC should set and held at 127, REC should be 0, reset mode entered
CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_BUS_OFF);
/* Note: REC is still allowed to increase during bus-off. REC > err_warn
can prevent "bus recovery complete" interrupt from occurring. Set to
listen only mode to freeze REC. */
can_config_mode(CAN_MODE_LISTEN_ONLY);
can_alert_handler(CAN_ALERT_BUS_OFF, alert_req);
} else {
//Bus-recovery in progress. TEC has dropped below error warning limit
can_alert_handler(CAN_ALERT_RECOVERY_IN_PROGRESS, alert_req);
}
} else {
if (status->error) {
//TEC or REC surpassed error warning limit
CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_WARN);
can_alert_handler(CAN_ALERT_ABOVE_ERR_WARN, alert_req);
} else if (p_can_obj->control_flags & CTRL_FLAG_RECOVERING) {
//Bus recovery complete.
can_enter_reset_mode();
//Reset and set flags to the equivalent of the stopped state
CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_RECOVERING | CTRL_FLAG_ERR_WARN |
CTRL_FLAG_ERR_PASSIVE | CTRL_FLAG_BUS_OFF |
CTRL_FLAG_TX_BUFF_OCCUPIED);
CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_STOPPED);
can_alert_handler(CAN_ALERT_BUS_RECOVERED, alert_req);
} else {
//TEC and REC are both below error warning
CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_WARN);
can_alert_handler(CAN_ALERT_BELOW_ERR_WARN, alert_req);
}
}
}
static void can_intr_handler_err_passive(int *alert_req)
{
uint32_t tec, rec;
can_get_error_counters(&tec, &rec);
if (tec >= DRIVER_DEFAULT_ERR_PASS_CNT || rec >= DRIVER_DEFAULT_ERR_PASS_CNT) {
//Entered error passive
CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_PASSIVE);
can_alert_handler(CAN_ALERT_ERR_PASS, alert_req);
} else {
//Returned to error active
CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_PASSIVE);
can_alert_handler(CAN_ALERT_ERR_ACTIVE, alert_req);
}
}
static void can_intr_handler_bus_err(int *alert_req)
{
// ECC register is read to re-arm bus error interrupt. ECC is not used
(void) can_get_error_code_capture();
p_can_obj->bus_error_count++;
can_alert_handler(CAN_ALERT_BUS_ERROR, alert_req);
}
static void can_intr_handler_arb_lost(int *alert_req)
{
//ALC register is read to re-arm arb lost interrupt. ALC is not used
(void) can_get_arbitration_lost_capture();
p_can_obj->arb_lost_count++;
can_alert_handler(CAN_ALERT_ARB_LOST, alert_req);
}
static void can_intr_handler_rx(BaseType_t *task_woken, int *alert_req)
{
can_rx_msg_cnt_reg_t msg_count_reg;
msg_count_reg.val = can_get_rx_message_counter();
for (int i = 0; i < msg_count_reg.rx_message_counter; i++) {
can_frame_t frame;
can_get_rx_buffer_and_clear(&frame);
//Copy frame into RX Queue
if (xQueueSendFromISR(p_can_obj->rx_queue, &frame, task_woken) == pdTRUE) {
p_can_obj->rx_msg_count++;
} else {
p_can_obj->rx_missed_count++;
can_alert_handler(CAN_ALERT_RX_QUEUE_FULL, alert_req);
}
}
}
static void can_intr_handler_tx(can_status_reg_t *status, int *alert_req)
{
//Handle previously transmitted frame
if (status->tx_complete) {
can_alert_handler(CAN_ALERT_TX_SUCCESS, alert_req);
} else {
p_can_obj->tx_failed_count++;
can_alert_handler(CAN_ALERT_TX_FAILED, alert_req);
}
//Update TX message count
p_can_obj->tx_msg_count--;
configASSERT(p_can_obj->tx_msg_count >= 0); //Sanity check
//Check if there are more frames to transmit
if (p_can_obj->tx_msg_count > 0 && p_can_obj->tx_queue != NULL) {
can_frame_t frame;
configASSERT(xQueueReceiveFromISR(p_can_obj->tx_queue, &frame, NULL) == pdTRUE);
can_set_tx_buffer_and_transmit(&frame);
} else {
//No more frames to transmit
CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
can_alert_handler(CAN_ALERT_TX_IDLE, alert_req);
}
}
static void can_intr_handler_main(void *arg)
{
BaseType_t task_woken = pdFALSE;
int alert_req = 0;
can_status_reg_t status;
can_intr_reg_t intr_reason;
CAN_ENTER_CRITICAL();
status.val = can_get_status();
intr_reason.val = (p_can_obj != NULL) ? can_get_interrupt_reason() : 0; //Incase intr occurs whilst driver is being uninstalled
//Handle error counter related interrupts
if (intr_reason.err_warn) {
//Triggers when Bus-Status or Error-status bits change
can_intr_handler_err_warn(&status, &task_woken, &alert_req);
}
if (intr_reason.err_passive) {
//Triggers when entering/returning error passive/active state
can_intr_handler_err_passive(&alert_req);
}
//Handle other error interrupts
if (intr_reason.bus_err) {
//Triggers when an error (Bit, Stuff, CRC, Form, ACK) occurs on the CAN bus
can_intr_handler_bus_err(&alert_req);
}
if (intr_reason.arb_lost) {
//Triggers when arbitration is lost
can_intr_handler_arb_lost(&alert_req);
}
//Todo: Check data overrun bug where interrupt does not trigger even when enabled
//Handle TX/RX interrupts
if (intr_reason.rx) {
//Triggers when RX buffer has one or more frames. Disabled if RX Queue length = 0
can_intr_handler_rx(&task_woken, &alert_req);
}
if (intr_reason.tx) {
//Triggers when TX buffer becomes free after a transmission
can_intr_handler_tx(&status, &alert_req);
}
/* Todo: Check possible bug where transmitting self reception request then
clearing rx buffer will cancel the transmission. */
CAN_EXIT_CRITICAL();
if (p_can_obj->alert_semphr != NULL && alert_req) {
//Give semaphore if alerts were triggered
xSemaphoreGiveFromISR(p_can_obj->alert_semphr, &task_woken);
}
if (task_woken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
/* ---------------------- Frame and GPIO functions ------------------------- */
static void can_format_frame(uint32_t id, uint8_t dlc, const uint8_t *data, uint32_t flags, can_frame_t *tx_frame)
{
/* This function encodes a message into a frame structure. The frame structure has
an identical layout to the TX buffer, allowing the frame structure to be directly
copied into TX buffer. */
//Set frame information
tx_frame->dlc = dlc;
tx_frame->rtr = (flags & CAN_MSG_FLAG_RTR) ? 1 : 0;
tx_frame->frame_format = (flags & CAN_MSG_FLAG_EXTD) ? 1 : 0;
tx_frame->self_reception = (flags & CAN_MSG_FLAG_SELF) ? 1 : 0;
tx_frame->single_shot = (flags & CAN_MSG_FLAG_SS) ? 1 : 0;
//Set ID
int id_len = (flags & CAN_MSG_FLAG_EXTD) ? FRAME_EXTD_ID_LEN : FRAME_STD_ID_LEN;
uint8_t *id_buffer = (flags & CAN_MSG_FLAG_EXTD) ? tx_frame->extended.id : tx_frame->standard.id;
//Split ID into 4 or 2 bytes, and turn into big-endian with left alignment (<< 3 or 5)
uint32_t id_temp = (flags & CAN_MSG_FLAG_EXTD) ? __builtin_bswap32((id & CAN_EXTD_ID_MASK) << 3) : //((id << 3) >> 8*(3-i))
__builtin_bswap16((id & CAN_STD_ID_MASK) << 5); //((id << 5) >> 8*(1-i))
for (int i = 0; i < id_len; i++) {
id_buffer[i] = (id_temp >> (8 * i)) & 0xFF; //Copy big-endian ID byte by byte
}
//Set Data.
uint8_t *data_buffer = (flags & CAN_MSG_FLAG_EXTD) ? tx_frame->extended.data : tx_frame->standard.data;
for (int i = 0; (i < dlc) && (i < FRAME_MAX_DATA_LEN); i++) { //Handle case where dlc is > 8
data_buffer[i] = data[i];
}
}
static void can_parse_frame(can_frame_t *rx_frame, uint32_t *id, uint8_t *dlc, uint8_t *data, uint32_t *flags)
{
//This function decodes a frame structure into it's constituent components.
//Copy frame information
*dlc = rx_frame->dlc;
*flags = 0;
*flags |= (rx_frame->dlc > FRAME_MAX_DATA_LEN) ? CAN_MSG_FLAG_DLC_NON_COMP : 0;
*flags |= (rx_frame->rtr) ? CAN_MSG_FLAG_RTR : 0;
*flags |= (rx_frame->frame_format) ? CAN_MSG_FLAG_EXTD : 0;
//Copy ID
int id_len = (rx_frame->frame_format) ? FRAME_EXTD_ID_LEN : FRAME_STD_ID_LEN;
uint8_t *id_buffer = (rx_frame->frame_format) ? rx_frame->extended.id : rx_frame->standard.id;
uint32_t id_temp = 0;
for (int i = 0; i < id_len; i++) {
id_temp |= id_buffer[i] << (8 * i); //Copy big-endian ID byte by byte
}
//Revert endianness of 4 or 2 byte ID, and shift into 29 or 11 bit ID
id_temp = (rx_frame->frame_format) ? (__builtin_bswap32(id_temp) >> 3) : //((byte[i] << 8*(3-i)) >> 3)
(__builtin_bswap16(id_temp) >> 5); //((byte[i] << 8*(1-i)) >> 5)
*id = id_temp & ((rx_frame->frame_format) ? CAN_EXTD_ID_MASK : CAN_STD_ID_MASK);
//Copy data
uint8_t *data_buffer = (rx_frame->frame_format) ? rx_frame->extended.data : rx_frame->standard.data;
for (int i = 0; (i < rx_frame->dlc) && (i < FRAME_MAX_DATA_LEN); i++) {
data[i] = data_buffer[i];
}
//Set remaining bytes of data to 0
for (int i = rx_frame->dlc; i < FRAME_MAX_DATA_LEN; i++) {
data[i] = 0;
}
}
static void can_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout, gpio_num_t bus_status)
{
//Set TX pin
gpio_set_pull_mode(tx, GPIO_FLOATING);
gpio_matrix_out(tx, CAN_TX_IDX, false, false);
gpio_pad_select_gpio(tx);
//Set RX pin
gpio_set_pull_mode(rx, GPIO_FLOATING);
gpio_matrix_in(rx, CAN_RX_IDX, false);
gpio_pad_select_gpio(rx);
//Configure output clock pin (Optional)
if (clkout >= 0 && clkout < GPIO_NUM_MAX) {
gpio_set_pull_mode(clkout, GPIO_FLOATING);
gpio_matrix_out(clkout, CAN_CLKOUT_IDX, false, false);
gpio_pad_select_gpio(clkout);
}
//Configure bus status pin (Optional)
if (bus_status >= 0 && bus_status < GPIO_NUM_MAX) {
gpio_set_pull_mode(bus_status, GPIO_FLOATING);
gpio_matrix_out(bus_status, CAN_BUS_OFF_ON_IDX, false, false);
gpio_pad_select_gpio(bus_status);
}
}
/* ---------------------------- Public Functions ---------------------------- */
esp_err_t can_driver_install(const can_general_config_t *g_config, const can_timing_config_t *t_config, const can_filter_config_t *f_config)
{
//Check arguments and state
CAN_CHECK(p_can_obj == NULL, ESP_ERR_INVALID_STATE); //Check is driver is already installed
CAN_CHECK(g_config != NULL, ESP_ERR_INVALID_ARG);
CAN_CHECK(t_config != NULL, ESP_ERR_INVALID_ARG);
CAN_CHECK(f_config != NULL, ESP_ERR_INVALID_ARG);
CAN_CHECK(g_config->rx_queue_len > 0, ESP_ERR_INVALID_ARG);
CAN_CHECK(g_config->tx_io >= 0 && g_config->tx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG);
CAN_CHECK(g_config->rx_io >= 0 && g_config->rx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG);
esp_err_t ret;
//Initialize CAN object
p_can_obj = calloc(1, sizeof(can_obj_t));
CAN_CHECK(p_can_obj != NULL, ESP_ERR_NO_MEM);
p_can_obj->tx_queue = (g_config->tx_queue_len > 0) ? xQueueCreate(g_config->tx_queue_len, sizeof(can_frame_t)) : NULL;
p_can_obj->rx_queue = xQueueCreate(g_config->rx_queue_len, sizeof(can_frame_t));
p_can_obj->alert_semphr = xSemaphoreCreateBinary();
if ((g_config->tx_queue_len > 0 && p_can_obj->tx_queue == NULL) ||
p_can_obj->rx_queue == NULL || p_can_obj->alert_semphr == NULL) {
ret = ESP_ERR_NO_MEM;
goto err;
}
p_can_obj->control_flags = CTRL_FLAG_STOPPED;
p_can_obj->control_flags |= (g_config->mode == CAN_MODE_NO_ACK) ? CTRL_FLAG_SELF_TEST : 0;
p_can_obj->control_flags |= (g_config->mode == CAN_MODE_LISTEN_ONLY) ? CTRL_FLAG_LISTEN_ONLY : 0;
p_can_obj->tx_msg_count = 0;
p_can_obj->rx_msg_count = 0;
p_can_obj->tx_failed_count = 0;
p_can_obj->rx_missed_count = 0;
p_can_obj->arb_lost_count = 0;
p_can_obj->bus_error_count = 0;
p_can_obj->alerts_enabled = g_config->alerts_enabled;
p_can_obj->alerts_triggered = 0;
CAN_ENTER_CRITICAL();
//Initialize CAN peripheral
periph_module_enable(PERIPH_CAN_MODULE); //Enable APB CLK to CAN peripheral
configASSERT(can_enter_reset_mode() == ESP_OK); //Must enter reset mode to write to config registers
can_config_pelican(); //Use PeliCAN addresses
/* Note: REC is allowed to increase even in reset mode. Listen only mode
will freeze REC. The desired mode will be set when can_start() is called. */
can_config_mode(CAN_MODE_LISTEN_ONLY);
can_config_interrupts(DRIVER_DEFAULT_INTERRUPTS);
can_config_bus_timing(t_config->brp, t_config->sjw, t_config->tseg_1, t_config->tseg_2, t_config->triple_sampling);
can_config_error(DRIVER_DEFAULT_EWL, DRIVER_DEFAULT_REC, DRIVER_DEFAULT_TEC);
can_config_acceptance_filter(f_config->acceptance_code, f_config->acceptance_mask, f_config->single_filter);
can_config_clk_out(g_config->clkout_divider);
//Allocate GPIO and Interrupts
can_configure_gpio(g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io);
(void) can_get_interrupt_reason(); //Read interrupt reg to clear it before allocating ISR
ESP_ERROR_CHECK(esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, can_intr_handler_main, NULL, &p_can_obj->isr_handle));
CAN_EXIT_CRITICAL();
//Todo: Allow interrupt to be registered to specified CPU
//CAN module is still in reset mode, users need to call can_start() afterwards
return ESP_OK;
err:
//Cleanup and return error
if (p_can_obj != NULL) {
if (p_can_obj->tx_queue != NULL) {
vQueueDelete(p_can_obj->tx_queue);
p_can_obj->tx_queue = NULL;
}
if (p_can_obj->rx_queue != NULL) {
vQueueDelete(p_can_obj->rx_queue);
p_can_obj->rx_queue = NULL;
}
if (p_can_obj->alert_semphr != NULL) {
vSemaphoreDelete(p_can_obj->alert_semphr);
p_can_obj->alert_semphr = NULL;
}
free(p_can_obj);
}
return ret;
}
esp_err_t can_driver_uninstall()
{
//Check state
CAN_ENTER_CRITICAL();
CAN_CHECK_FROM_CRIT(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_CHECK_FROM_CRIT(p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF), ESP_ERR_INVALID_STATE);
//Clear registers
configASSERT(can_enter_reset_mode() == ESP_OK); //Enter reset mode to stop any CAN bus activity
(void) can_get_interrupt_reason();
(void) can_get_arbitration_lost_capture();
(void) can_get_error_code_capture();
ESP_ERROR_CHECK(esp_intr_free(p_can_obj->isr_handle)); //Free interrupt
periph_module_disable(PERIPH_CAN_MODULE); //Disable CAN peripheral
//Delete queues, semaphores
if (p_can_obj->tx_queue != NULL) {
vQueueDelete(p_can_obj->tx_queue);
}
vQueueDelete(p_can_obj->rx_queue);
vSemaphoreDelete(p_can_obj->alert_semphr);
free(p_can_obj); //Free can driver object
CAN_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t can_start()
{
//Check state
CAN_ENTER_CRITICAL();
CAN_CHECK_FROM_CRIT(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_CHECK_FROM_CRIT(p_can_obj->control_flags & CTRL_FLAG_STOPPED, ESP_ERR_INVALID_STATE);
//Reset RX queue, and RX message count
xQueueReset(p_can_obj->rx_queue);
p_can_obj->rx_msg_count = 0;
configASSERT(can_enter_reset_mode() == ESP_OK); //Should already be in bus-off mode, set again to make sure
//Currently in listen only mode, need to set to mode specified by configuration
can_mode_t mode;
if (p_can_obj->control_flags & CTRL_FLAG_SELF_TEST) {
mode = CAN_MODE_NO_ACK;
} else if (p_can_obj->control_flags & CTRL_FLAG_LISTEN_ONLY) {
mode = CAN_MODE_LISTEN_ONLY;
} else {
mode = CAN_MODE_NORMAL;
}
can_config_mode(mode); //Set mode
(void) can_get_interrupt_reason(); //Clear interrupt register
configASSERT(can_exit_reset_mode() == ESP_OK);
CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_STOPPED);
CAN_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t can_stop()
{
//Check state
CAN_ENTER_CRITICAL();
CAN_CHECK_FROM_CRIT(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_CHECK_FROM_CRIT(!(p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)), ESP_ERR_INVALID_STATE);
//Clear interrupts and reset flags
configASSERT(can_enter_reset_mode() == ESP_OK);
(void) can_get_interrupt_reason(); //Read interrupt register to clear interrupts
can_config_mode(CAN_MODE_LISTEN_ONLY); //Set to listen only mode to freeze REC
CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_STOPPED);
//Reset TX Queue and message count
if (p_can_obj->tx_queue != NULL) {
xQueueReset(p_can_obj->tx_queue);
}
p_can_obj->tx_msg_count = 0;
CAN_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t can_transmit(const can_message_t *message, TickType_t ticks_to_wait)
{
//Check arguments
CAN_CHECK(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_CHECK(message != NULL, ESP_ERR_INVALID_ARG);
CAN_CHECK((message->data_length_code <= FRAME_MAX_DATA_LEN) || (message->flags & CAN_MSG_FLAG_DLC_NON_COMP), ESP_ERR_INVALID_ARG);
CAN_ENTER_CRITICAL();
//Check State
CAN_CHECK_FROM_CRIT(!(p_can_obj->control_flags & CTRL_FLAG_LISTEN_ONLY), ESP_ERR_NOT_SUPPORTED);
CAN_CHECK_FROM_CRIT(!(p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)), ESP_ERR_INVALID_STATE);
//Format frame
esp_err_t ret = ESP_FAIL;
can_frame_t tx_frame;
can_format_frame(message->identifier, message->data_length_code, message->data, message->flags, &tx_frame);
//Check if frame can be sent immediately
if ((p_can_obj->tx_msg_count == 0) && !(p_can_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED)) {
//No other frames waiting to transmit. Bypass queue and transmit immediately
can_set_tx_buffer_and_transmit(&tx_frame);
p_can_obj->tx_msg_count++;
CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
ret = ESP_OK;
}
CAN_EXIT_CRITICAL();
if (ret != ESP_OK) {
if (p_can_obj->tx_queue == NULL) {
//TX Queue is disabled and TX buffer is occupied, message was not sent
ret = ESP_FAIL;
} else if (xQueueSend(p_can_obj->tx_queue, &tx_frame, ticks_to_wait) == pdTRUE) {
//Copied to TX Queue
CAN_ENTER_CRITICAL();
if (p_can_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_STOPPED)) {
//TX queue was reset (due to stop/bus_off), remove copied frame from queue to prevent transmission
configASSERT(xQueueReceive(p_can_obj->tx_queue, &tx_frame, 0) == pdTRUE);
ret = ESP_ERR_INVALID_STATE;
} else if ((p_can_obj->tx_msg_count == 0) && !(p_can_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED)) {
//TX buffer was freed during copy, manually trigger transmission
configASSERT(xQueueReceive(p_can_obj->tx_queue, &tx_frame, 0) == pdTRUE);
can_set_tx_buffer_and_transmit(&tx_frame);
p_can_obj->tx_msg_count++;
CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
ret = ESP_OK;
} else {
//Frame was copied to queue, waiting to be transmitted
p_can_obj->tx_msg_count++;
ret = ESP_OK;
}
CAN_EXIT_CRITICAL();
} else {
//Timed out waiting for free space on TX queue
ret = ESP_ERR_TIMEOUT;
}
}
return ret;
}
esp_err_t can_receive(can_message_t *message, TickType_t ticks_to_wait)
{
//Check arguments and state
CAN_CHECK(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_CHECK(message != NULL, ESP_ERR_INVALID_ARG);
//Get frame from RX Queue or RX Buffer
can_frame_t rx_frame;
if (xQueueReceive(p_can_obj->rx_queue, &rx_frame, ticks_to_wait) != pdTRUE) {
return ESP_ERR_TIMEOUT;
}
CAN_ENTER_CRITICAL();
p_can_obj->rx_msg_count--;
CAN_EXIT_CRITICAL();
//Decode frame
can_parse_frame(&rx_frame, &(message->identifier), &(message->data_length_code), message->data, &(message->flags));
return ESP_OK;
}
esp_err_t can_read_alerts(uint32_t *alerts, TickType_t ticks_to_wait)
{
//Check arguments and state
CAN_CHECK(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_CHECK(alerts != NULL, ESP_ERR_INVALID_ARG);
//Wait for an alert to occur
if (xSemaphoreTake(p_can_obj->alert_semphr, ticks_to_wait) == pdTRUE) {
CAN_ENTER_CRITICAL();
*alerts = p_can_obj->alerts_triggered;
p_can_obj->alerts_triggered = 0; //Clear triggered alerts
CAN_EXIT_CRITICAL();
return ESP_OK;
} else {
*alerts = 0;
return ESP_ERR_TIMEOUT;
}
}
esp_err_t can_reconfigure_alerts(uint32_t alerts_enabled, uint32_t *current_alerts)
{
CAN_CHECK(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_ENTER_CRITICAL();
uint32_t cur_alerts;
cur_alerts = can_read_alerts(&cur_alerts, 0); //Clear any unhandled alerts
p_can_obj->alerts_enabled = alerts_enabled; //Update enabled alerts
CAN_EXIT_CRITICAL();
if (current_alerts != NULL) {
*current_alerts = cur_alerts;
}
return ESP_OK;
}
esp_err_t can_initiate_recovery()
{
CAN_ENTER_CRITICAL();
//Check state
CAN_CHECK_FROM_CRIT(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_CHECK_FROM_CRIT(p_can_obj->control_flags & CTRL_FLAG_BUS_OFF, ESP_ERR_INVALID_STATE);
CAN_CHECK_FROM_CRIT(!(p_can_obj->control_flags & CTRL_FLAG_RECOVERING), ESP_ERR_INVALID_STATE);
//Reset TX Queue/Counters
if (p_can_obj->tx_queue != NULL) {
xQueueReset(p_can_obj->tx_queue);
}
p_can_obj->tx_msg_count = 0;
CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_RECOVERING);
//Trigger start of recovery process
configASSERT(can_exit_reset_mode() == ESP_OK);
CAN_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t can_get_status_info(can_status_info_t *status_info)
{
//Check parameters and state
CAN_CHECK(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_CHECK(status_info != NULL, ESP_ERR_INVALID_ARG);
CAN_ENTER_CRITICAL();
uint32_t tec, rec;
can_get_error_counters(&tec, &rec);
status_info->tx_error_counter = tec;
status_info->rx_error_counter = rec;
status_info->msgs_to_tx = p_can_obj->tx_msg_count;
status_info->msgs_to_rx = p_can_obj->rx_msg_count;
status_info->tx_failed_count = p_can_obj->tx_failed_count;
status_info->rx_missed_count = p_can_obj->rx_missed_count;
status_info->arb_lost_count = p_can_obj->arb_lost_count;
status_info->bus_error_count = p_can_obj->bus_error_count;
if (p_can_obj->control_flags & CTRL_FLAG_RECOVERING) {
status_info->state = CAN_STATE_RECOVERING;
} else if (p_can_obj->control_flags & CTRL_FLAG_BUS_OFF) {
status_info->state = CAN_STATE_BUS_OFF;
} else if (p_can_obj->control_flags & CTRL_FLAG_STOPPED) {
status_info->state = CAN_STATE_STOPPED;
} else {
status_info->state = CAN_STATE_RUNNING;
}
CAN_EXIT_CRITICAL();
return ESP_OK;
}

View file

@ -0,0 +1,398 @@
// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _DRIVER_CAN_H_
#define _DRIVER_CAN_H_
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_types.h"
#include "esp_intr.h"
#include "esp_err.h"
#include "gpio.h"
/* -------------------- Default initializers and flags ---------------------- */
/** @cond */ //Doxy command to hide preprocessor definitions from docs
/**
* @brief Initializer macro for general configuration structure.
*
* This initializer macros allows the TX GPIO, RX GPIO, and operating mode to be
* configured. The other members of the general configuration structure are
* assigned default values.
*/
#define CAN_GENERAL_CONFIG_DEFAULT(tx_io_num, rx_io_num, op_mode) {.mode = op_mode, .tx_io = tx_io_num, .rx_io = rx_io_num, \
.clkout_io = CAN_IO_UNUSED, .bus_off_io = CAN_IO_UNUSED, \
.tx_queue_len = 5, .rx_queue_len = 5, \
.alerts_enabled = CAN_ALERT_NONE, .clkout_divider = 0, }
/**
* @brief Initializer macros for timing configuration structure
*
* The following initializer macros offer commonly found bit rates.
*/
#define CAN_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
#define CAN_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#define CAN_TIMING_CONFIG_100KBITS() {.brp = 40, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#define CAN_TIMING_CONFIG_125KBITS() {.brp = 32, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#define CAN_TIMING_CONFIG_250KBITS() {.brp = 16, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#define CAN_TIMING_CONFIG_500KBITS() {.brp = 8, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#define CAN_TIMING_CONFIG_800KBITS() {.brp = 4, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
#define CAN_TIMING_CONFIG_1MBITS() {.brp = 4, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
/**
* @brief Initializer macro for filter configuration to accept all IDs
*/
#define CAN_FILTER_CONFIG_ACCEPT_ALL() {.acceptance_code = 0, .acceptance_mask = 0xFFFFFFFF, .single_filter = true}
/**
* @brief Alert flags
*
* The following flags represents the various kind of alerts available in
* the CAN driver. These flags can be used when configuring/reconfiguring
* alerts, or when calling can_read_alerts().
*
* @note The CAN_ALERT_AND_LOG flag is not an actual alert, but will configure
* the CAN driver to log to UART when an enabled alert occurs.
*/
#define CAN_ALERT_TX_IDLE 0x0001 /**< Alert(1): No more messages to transmit */
#define CAN_ALERT_TX_SUCCESS 0x0002 /**< Alert(2): The previous transmission was successful */
#define CAN_ALERT_BELOW_ERR_WARN 0x0004 /**< Alert(4): Both error counters have dropped below error warning limit */
#define CAN_ALERT_ERR_ACTIVE 0x0008 /**< Alert(8): CAN controller has become error active */
#define CAN_ALERT_RECOVERY_IN_PROGRESS 0x0010 /**< Alert(16): CAN controller is undergoing bus recovery */
#define CAN_ALERT_BUS_RECOVERED 0x0020 /**< Alert(32): CAN controller has successfully completed bus recovery */
#define CAN_ALERT_ARB_LOST 0x0040 /**< Alert(64): The previous transmission lost arbitration */
#define CAN_ALERT_ABOVE_ERR_WARN 0x0080 /**< Alert(128): One of the error counters have exceeded the error warning limit */
#define CAN_ALERT_BUS_ERROR 0x0100 /**< Alert(256): A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus */
#define CAN_ALERT_TX_FAILED 0x0200 /**< Alert(512): The previous transmission has failed (for single shot transmission) */
#define CAN_ALERT_RX_QUEUE_FULL 0x0400 /**< Alert(1024): The RX queue is full causing a frame to be lost */
#define CAN_ALERT_ERR_PASS 0x0800 /**< Alert(2048): CAN controller has become error passive */
#define CAN_ALERT_BUS_OFF 0x1000 /**< Alert(4096): Bus-off condition occurred. CAN controller can no longer influence bus */
#define CAN_ALERT_ALL 0x1FFF /**< Bit mask to enable all alerts during configuration */
#define CAN_ALERT_NONE 0x0000 /**< Bit mask to disable all alerts during configuration */
#define CAN_ALERT_AND_LOG 0x2000 /**< Bit mask to enable alerts to also be logged when they occur */
/**
* @brief Message flags
*
* The message flags are used to indicate the type of message transmitted/received.
* Some flags also specify the type of transmission.
*/
#define CAN_MSG_FLAG_NONE 0x00 /**< No message flags (Standard Frame Format) */
#define CAN_MSG_FLAG_EXTD 0x01 /**< Extended Frame Format (29bit ID) */
#define CAN_MSG_FLAG_RTR 0x02 /**< Message is a Remote Transmit Request */
#define CAN_MSG_FLAG_SS 0x04 /**< Transmit as a Single Shot Transmission */
#define CAN_MSG_FLAG_SELF 0x08 /**< Transmit as a Self Reception Request */
#define CAN_MSG_FLAG_DLC_NON_COMP 0x10 /**< Message's Data length code is larger than 8. This will break compliance with CAN2.0B */
/**
* @brief Miscellaneous macros
*/
#define CAN_EXTD_ID_MASK 0x1FFFFFFF /**< Bit mask for 29 bit Extended Frame Format ID */
#define CAN_STD_ID_MASK 0x7FF /**< Bit mask for 11 bit Standard Frame Format ID */
#define CAN_MAX_DATA_LEN 8 /**< Maximum number of data bytes in a CAN2.0B frame */
#define CAN_IO_UNUSED (-1) /**< Marks GPIO as unused in CAN configuration */
/** @endcond */
/* ----------------------- Enum and Struct Definitions ---------------------- */
/**
* @brief CAN driver operating modes
*/
typedef enum {
CAN_MODE_NORMAL, /**< Normal operating mode where CAN controller can send/receive/acknowledge messages */
CAN_MODE_NO_ACK, /**< Transmission does not require acknowledgment. Use this mode for self testing */
CAN_MODE_LISTEN_ONLY, /**< The CAN controller will not influence the bus (No transmissions or acknowledgments) but can receive messages */
} can_mode_t;
/**
* @brief CAN driver states
*/
typedef enum {
CAN_STATE_STOPPED, /**< Stopped state. The CAN controller will not participate in any CAN bus activities */
CAN_STATE_RUNNING, /**< Running state. The CAN controller can transmit and receive messages */
CAN_STATE_BUS_OFF, /**< Bus-off state. The CAN controller cannot participate in bus activities until it has recovered */
CAN_STATE_RECOVERING, /**< Recovering state. The CAN controller is undergoing bus recovery */
} can_state_t;
/**
* @brief Structure for general configuration of the CAN driver
*
* @note Macro initializers are available for this structure
*/
typedef struct {
can_mode_t mode; /**< Mode of CAN controller */
gpio_num_t tx_io; /**< Transmit GPIO number */
gpio_num_t rx_io; /**< Receive GPIO number */
gpio_num_t clkout_io; /**< CLKOUT GPIO number (optional, set to -1 if unused) */
gpio_num_t bus_off_io; /**< Bus off indicator GPIO number (optional, set to -1 if unused) */
uint32_t tx_queue_len; /**< Number of messages TX queue can hold (set to 0 to disable TX Queue) */
uint32_t rx_queue_len; /**< Number of messages RX queue can hold */
uint32_t alerts_enabled; /**< Bit field of alerts to enable (see documentation) */
uint32_t clkout_divider; /**< CLKOUT divider. Can be 1 or any even number from 2 to 14 (optional, set to 0 if unused) */
} can_general_config_t;
/**
* @brief Structure for bit timing configuration of the CAN driver
*
* @note Macro initializers are available for this structure
*/
typedef struct {
uint8_t brp; /**< Baudrate prescaler (APB clock divider, even number from 2 to 128) */
uint8_t tseg_1; /**< Timing segment 1 (Number of time quanta, between 1 to 16) */
uint8_t tseg_2; /**< Timing segment 2 (Number of time quanta, 1 to 8) */
uint8_t sjw; /**< Synchronization Jump Width (Max time quanta jump for synchronize from 1 to 4) */
bool triple_sampling; /**< Enables triple sampling when the CAN controller samples a bit */
} can_timing_config_t;
/**
* @brief Structure for acceptance filter configuration of the CAN driver (see documentation)
*
* @note Macro initializers are available for this structure
*/
typedef struct {
uint32_t acceptance_code; /**< 32-bit acceptance code */
uint32_t acceptance_mask; /**< 32-bit acceptance mask */
bool single_filter; /**< Use Single Filter Mode (see documentation) */
} can_filter_config_t;
/**
* @brief Structure to store status information of CAN driver
*/
typedef struct {
can_state_t state; /**< Current state of CAN controller (Stopped/Running/Bus-Off/Recovery) */
uint32_t msgs_to_tx; /**< Number of messages queued for transmission or awaiting transmission completion */
uint32_t msgs_to_rx; /**< Number of messages in RX queue waiting to be read */
uint32_t tx_error_counter; /**< Current value of Transmit Error Counter */
uint32_t rx_error_counter; /**< Current value of Receive Error Counter */
uint32_t tx_failed_count; /**< Number of messages that failed transmissions */
uint32_t rx_missed_count; /**< Number of messages that were lost due to a full RX queue */
uint32_t arb_lost_count; /**< Number of instances arbitration was lost */
uint32_t bus_error_count; /**< Number of instances a bus error has occurred */
} can_status_info_t;
/**
* @brief Structure to store a CAN message
*
* @note The flags member is used to control the message type, and transmission
* type (see documentation for message flags)
*/
typedef struct {
uint32_t flags; /**< Bit field of message flags indicates frame/transmission type (see documentation) */
uint32_t identifier; /**< 11 or 29 bit identifier */
uint8_t data_length_code; /**< Data length code */
uint8_t data[CAN_MAX_DATA_LEN]; /**< Data bytes (not relevant in RTR frame) */
} can_message_t;
/* ----------------------------- Public API -------------------------------- */
/**
* @brief Install CAN driver
*
* This function installs the CAN driver using three configuration structures.
* The required memory is allocated and the CAN driver is placed in the stopped
* state after running this function.
*
* @param[in] g_config General configuration structure
* @param[in] t_config Timing configuration structure
* @param[in] f_config Filter configuration structure
*
* @note Macro initializers are available for the configuration structures (see documentation)
*
* @note To reinstall the CAN driver, call can_driver_uninstall() first
*
* @return
* - ESP_OK: Successfully installed CAN driver
* - ESP_ERR_INVALID_ARG: Arguments are invalid
* - ESP_ERR_NO_MEM: Insufficient memory
* - ESP_ERR_INVALID_STATE: Driver is already installed
*/
esp_err_t can_driver_install(const can_general_config_t *g_config, const can_timing_config_t *t_config, const can_filter_config_t *f_config);
/**
* @brief Uninstall the CAN driver
*
* This function uninstalls the CAN driver, freeing the memory utilized by the
* driver. This function can only be called when the driver is in the stopped
* state or the bus-off state.
*
* @warning The application must ensure that no tasks are blocked on TX/RX
* queues or alerts when this function is called.
*
* @return
* - ESP_OK: Successfully uninstalled CAN driver
* - ESP_ERR_INVALID_STATE: Driver is not in stopped/bus-off state, or is not installed
*/
esp_err_t can_driver_uninstall();
/**
* @brief Start the CAN driver
*
* This function starts the CAN driver, putting the CAN driver into the running
* state. This allows the CAN driver to participate in CAN bus activities such
* as transmitting/receiving messages. The RX queue is reset in this function,
* clearing any unread messages. This function can only be called when the CAN
* driver is in the stopped state.
*
* @return
* - ESP_OK: CAN driver is now running
* - ESP_ERR_INVALID_STATE: Driver is not in stopped state, or is not installed
*/
esp_err_t can_start();
/**
* @brief Stop the CAN driver
*
* This function stops the CAN driver, preventing any further message from being
* transmitted or received until can_start() is called. Any messages in the TX
* queue are cleared. Any messages in the RX queue should be read by the
* application after this function is called. This function can only be called
* when the CAN driver is in the running state.
*
* @warning A message currently being transmitted/received on the CAN bus will
* be ceased immediately. This may lead to other CAN nodes interpreting
* the unfinished message as an error.
*
* @return
* - ESP_OK: CAN driver is now Stopped
* - ESP_ERR_INVALID_STATE: Driver is not in running state, or is not installed
*/
esp_err_t can_stop();
/**
* @brief Transmit a CAN message
*
* This function queues a CAN message for transmission. Transmission will start
* immediately if no other messages are queued for transmission. If the TX queue
* is full, this function will block until more space becomes available or until
* it timesout. If the TX queue is disabled (TX queue length = 0 in configuration),
* this function will return immediately if another message is undergoing
* transmission. This function can only be called when the CAN driver is in the
* running state and cannot be called under Listen Only Mode.
*
* @param[in] message Message to transmit
* @param[in] ticks_to_wait Number of FreeRTOS ticks to block on the TX queue
*
* @note This function does not guarantee that the transmission is successful.
* The TX_SUCCESS/TX_FAILED alert can be enabled to alert the application
* upon the success/failure of a transmission.
*
* @note The TX_IDLE alert can be used to alert the application when no other
* messages are awaiting transmission.
*
* @return
* - ESP_OK: Transmission successfully queued/initiated
* - ESP_ERR_INVALID_ARG: Arguments are invalid
* - ESP_ERR_TIMEOUT: Timed out waiting for space on TX queue
* - ESP_FAIL: TX queue is disabled and another message is currently transmitting
* - ESP_ERR_INVALID_STATE: CAN driver is not in running state, or is not installed
* - ESP_ERR_NOT_SUPPORTED: Listen Only Mode does not support transmissions
*/
esp_err_t can_transmit(const can_message_t *message, TickType_t ticks_to_wait);
/**
* @brief Receive a CAN message
*
* This function receives a message from the RX queue. The flags field of the
* message structure will indicate the type of message received. This function
* will block if there are no messages in the RX queue
*
* @param[out] message Received message
* @param[in] ticks_to_wait Number of FreeRTOS ticks to block on RX queue
*
* @warning The flags field of the received message should be checked to determine
* if the received message contains any data bytes.
*
* @return
* - ESP_OK: Message successfully received from RX queue
* - ESP_ERR_TIMEOUT: Timed out waiting for message
* - ESP_ERR_INVALID_ARG: Arguments are invalid
* - ESP_ERR_INVALID_STATE: CAN driver is not installed
*/
esp_err_t can_receive(can_message_t *message, TickType_t ticks_to_wait);
/**
* @brief Read CAN driver alerts
*
* This function will read the alerts raised by the CAN driver. If no alert has
* been when this function is called, this function will block until an alert
* occurs or until it timeouts.
*
* @param[out] alerts Bit field of raised alerts (see documentation for alert flags)
* @param[in] ticks_to_wait Number of FreeRTOS ticks to block for alert
*
* @note Multiple alerts can be raised simultaneously. The application should
* check for all alerts that have been enabled.
*
* @return
* - ESP_OK: Alerts read
* - ESP_ERR_TIMEOUT: Timed out waiting for alerts
* - ESP_ERR_INVALID_ARG: Arguments are invalid
* - ESP_ERR_INVALID_STATE: CAN driver is not installed
*/
esp_err_t can_read_alerts(uint32_t *alerts, TickType_t ticks_to_wait);
/**
* @brief Reconfigure which alerts are enabled
*
* This function reconfigures which alerts are enabled. If there are alerts
* which have not been read whilst reconfiguring, this function can read those
* alerts.
*
* @param[in] alerts_enabled Bit field of alerts to enable (see documentation for alert flags)
* @param[out] current_alerts Bit field of currently raised alerts. Set to NULL if unused
*
* @return
* - ESP_OK: Alerts reconfigured
* - ESP_ERR_INVALID_STATE: CAN driver is not installed
*/
esp_err_t can_reconfigure_alerts(uint32_t alerts_enabled, uint32_t *current_alerts);
/**
* @brief Start the bus recovery process
*
* This function initiates the bus recovery process when the CAN driver is in
* the bus-off state. Once initiated, the CAN driver will enter the recovering
* state and wait for 128 occurrences of the bus-free signal on the CAN bus
* before returning to the stopped state. This function will reset the TX queue,
* clearing any messages pending transmission.
*
* @note The BUS_RECOVERED alert can be enabled to alert the application when
* the bus recovery process completes.
*
* @return
* - ESP_OK: Bus recovery started
* - ESP_ERR_INVALID_STATE: CAN driver is not in the bus-off state, or is not installed
*/
esp_err_t can_initiate_recovery();
/**
* @brief Get current status information of the CAN driver
*
* @param[out] status_info Status information
*
* @return
* - ESP_OK: Status information retrieved
* - ESP_ERR_INVALID_ARG: Arguments are invalid
* - ESP_ERR_INVALID_STATE: CAN driver is not installed
*/
esp_err_t can_get_status_info(can_status_info_t *status_info);
#ifdef __cplusplus
}
#endif
#endif /*_DRIVER_CAN_H_*/

View file

@ -26,6 +26,7 @@ PROVIDE ( SPI3 = 0x3ff65000 );
PROVIDE ( SYSCON = 0x3ff66000 );
PROVIDE ( I2C1 = 0x3ff67000 );
PROVIDE ( SDMMC = 0x3ff68000 );
PROVIDE ( CAN = 0x3ff6B000 );
PROVIDE ( MCPWM1 = 0x3ff6C000 );
PROVIDE ( I2S1 = 0x3ff6D000 );
PROVIDE ( UART2 = 0x3ff6E000 );

View file

@ -0,0 +1,211 @@
// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _SOC_CAN_STRUCT_H_
#define _SOC_CAN_STRUCT_H_
#ifdef __cplusplus
extern "C" {
#endif
/* -------------------------- Register Definitions -------------------------- */
/* The CAN peripheral's registers are 8bits, however the ESP32 can only access
* peripheral registers every 32bits. Therefore each CAN register is mapped to
* the least significant byte of every 32bits.
*/
typedef union {
struct {
uint32_t byte: 8; /* LSB */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} can_reg_t;
typedef union {
struct {
uint32_t reset: 1; /* MOD.0 Reset Mode */
uint32_t listen_only: 1; /* MOD.1 Listen Only Mode */
uint32_t self_test: 1; /* MOD.2 Self Test Mode */
uint32_t acceptance_filter: 1; /* MOD.3 Acceptance Filter Mode */
uint32_t reserved28: 28; /* Internal Reserved. MOD.4 Sleep Mode not supported */
};
uint32_t val;
} can_mode_reg_t;
typedef union {
struct {
uint32_t tx_req: 1; /* CMR.0 Transmission Request */
uint32_t abort_tx: 1; /* CMR.1 Abort Transmission */
uint32_t release_rx_buff: 1; /* CMR.2 Release Receive Buffer */
uint32_t clear_data_overrun: 1; /* CMR.3 Clear Data Overrun */
uint32_t self_rx_req: 1; /* CMR.4 Self Reception Request */
uint32_t reserved27: 27; /* Internal Reserved */
};
uint32_t val;
} can_cmd_reg_t;
typedef union {
struct {
uint32_t rx_buff: 1; /* SR.0 Receive Buffer Status */
uint32_t data_overrun: 1; /* SR.1 Data Overrun Status */
uint32_t tx_buff: 1; /* SR.2 Transmit Buffer Status */
uint32_t tx_complete: 1; /* SR.3 Transmission Complete Status */
uint32_t rx: 1; /* SR.4 Receive Status */
uint32_t tx: 1; /* SR.5 Transmit Status */
uint32_t error: 1; /* SR.6 Error Status */
uint32_t bus: 1; /* SR.7 Bus Status */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} can_status_reg_t;
typedef union {
struct {
uint32_t rx: 1; /* IR.0 Receive Interrupt */
uint32_t tx: 1; /* IR.1 Transmit Interrupt */
uint32_t err_warn: 1; /* IR.2 Error Interrupt */
uint32_t data_overrun: 1; /* IR.3 Data Overrun Interrupt */
uint32_t reserved1: 1; /* Internal Reserved (Wake-up not supported) */
uint32_t err_passive: 1; /* IR.5 Error Passive Interrupt */
uint32_t arb_lost: 1; /* IR.6 Arbitration Lost Interrupt */
uint32_t bus_err: 1; /* IR.7 Bus Error Interrupt */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} can_intr_reg_t;
typedef union {
struct {
uint32_t rx: 1; /* IER.0 Receive Interrupt Enable */
uint32_t tx: 1; /* IER.1 Transmit Interrupt Enable */
uint32_t err_warn: 1; /* IER.2 Error Interrupt Enable */
uint32_t data_overrun: 1; /* IER.3 Data Overrun Interrupt Enable */
uint32_t reserved1: 1; /* Internal Reserved (Wake-up not supported) */
uint32_t err_passive: 1; /* IER.5 Error Passive Interrupt Enable */
uint32_t arb_lost: 1; /* IER.6 Arbitration Lost Interrupt Enable */
uint32_t bus_err: 1; /* IER.7 Bus Error Interrupt Enable */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} can_intr_en_reg_t;
typedef union {
struct {
uint32_t baud_rate_prescaler: 6; /* BTR0[5:0] Baud Rate Prescaler */
uint32_t sync_jump_width: 2; /* BTR0[7:6] Synchronization Jump Width*/
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} can_bus_tim_0_reg_t;
typedef union {
struct {
uint32_t time_seg_1: 4; /* BTR1[3:0] Timing Segment 1 */
uint32_t time_seg_2: 3; /* BTR1[6:4] Timing Segment 2 */
uint32_t sampling: 1; /* BTR1.7 Sampling*/
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} can_bus_tim_1_reg_t;
typedef union {
struct {
uint32_t arbitration_lost_capture: 5; /* ALC[4:0] Arbitration lost capture */
uint32_t reserved27: 27; /* Internal Reserved */
};
uint32_t val;
} can_arb_lost_cap_reg_t;
typedef union {
struct {
uint32_t segment: 5; /* ECC[4:0] Error Code Segment 0 to 5 */
uint32_t direction: 1; /* ECC.5 Error Direction (TX/RX) */
uint32_t error_code: 2; /* ECC[7:6] Error Code */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} can_err_code_cap_reg_t;
typedef struct {
can_reg_t code_reg[4];
can_reg_t mask_reg[4];
uint32_t reserved32[5];
} can_acc_filter_t;
typedef union {
struct {
uint32_t rx_message_counter: 5; /* RMC[4:0] RX Message Counter */
uint32_t reserved27: 27; /* Internal Reserved */
};
uint32_t val;
} can_rx_msg_cnt_reg_t;
typedef union {
struct {
uint32_t clock_divider: 3; /* CDR[2:0] CLKOUT frequency selector based of fOSC */
uint32_t clock_off: 1; /* CDR.3 CLKOUT enable/disable */
uint32_t reserved3: 3; /* Internal Reserved. RXINTEN and CBP not supported */
uint32_t can_mode: 1; /* CDR.7 BasicCAN:0 PeliCAN:1 */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} can_clk_div_reg_t;
/* ---------------------------- Register Layout ------------------------------ */
typedef volatile struct {
//Configuration and Control Registers
can_mode_reg_t mode_reg; /* Address 0 */
can_cmd_reg_t command_reg; /* Address 1 */
can_status_reg_t status_reg; /* Address 2 */
can_intr_reg_t interrupt_reg; /* Address 3 */
can_intr_en_reg_t interrupt_enable_reg; /* Address 4 */
uint32_t reserved_05; /* Address 5 */
can_bus_tim_0_reg_t bus_timing_0_reg; /* Address 6 */
can_bus_tim_1_reg_t bus_timing_1_reg; /* Address 7 */
uint32_t reserved_08; /* Address 8 (Output control not supported) */
uint32_t reserved_09; /* Address 9 (Test Register not supported) */
uint32_t reserved_10; /* Address 10 */
//Capture and Counter Registers
can_arb_lost_cap_reg_t arbitration_lost_captue_reg; /* Address 11 */
can_err_code_cap_reg_t error_code_capture_reg; /* Address 12 */
can_reg_t error_warning_limit_reg; /* EWLR[7:0] Error Warning Limit: Address 13 */
can_reg_t rx_error_counter_reg; /* RXERR[7:0] Receive Error Counter: Address 14 */
can_reg_t tx_error_counter_reg; /* TXERR[7:0] Transmit Error Counter: Address 15 */
//Shared Registers (TX Buff/RX Buff/Acc Filter)
union {
can_acc_filter_t acceptance_filter;
can_reg_t tx_rx_buffer[13];
}; /* Address 16-28 TX/RX Buffer and Acc Filter*/;
//Misc Registers
can_rx_msg_cnt_reg_t rx_message_counter_reg; /* Address 29 */
can_reg_t reserved_30; /* Address 30 (RX Buffer Start Address not supported) */
can_clk_div_reg_t clock_divider_reg; /* Address 31 */
//Start of RX FIFO
} can_dev_t;
_Static_assert(sizeof(can_dev_t) == 128, "CAN registers should be 32 * 4 bytes");
extern can_dev_t CAN;
#ifdef __cplusplus
}
#endif
#endif /* _SOC_CAN_STRUCT_H_ */

View file

@ -120,6 +120,7 @@
#define DR_REG_I2C1_EXT_BASE 0x3ff67000
#define DR_REG_SDMMC_BASE 0x3ff68000
#define DR_REG_EMAC_BASE 0x3ff69000
#define DR_REG_CAN_BASE 0x3ff6B000
#define DR_REG_PWM1_BASE 0x3ff6C000
#define DR_REG_I2S1_BASE 0x3ff6D000
#define DR_REG_UART2_BASE 0x3ff6E000

View file

@ -64,6 +64,7 @@ INPUT = \
## Peripherals - API Reference
##
../../components/driver/include/driver/adc.h \
../../components/driver/include/driver/can.h \
../../components/driver/include/driver/dac.h \
../../components/driver/include/driver/gpio.h \
../../components/driver/include/driver/rtc_io.h \

View file

@ -0,0 +1,19 @@
#Diagram of CAN Controller Acceptance Filter (Dual) Configuration
packetdiag can_acceptance_filter_dual {
node_width = 30
node_height = 35
default_fontsize = 15
colwidth = 32
0-3: "F1 DB1[0:3]" [color = lightyellow];
4: "F2\nRTR" [color = lightyellow];
5-15: "F2 11-bit ID" [color = lightyellow];
16-19: "F1 DB1[4:7]" [color = lightyellow];
20: "F1\nRTR" [color = lightyellow];
21-31: "F1 11-bit ID" [color = lightyellow];
32-47: "F2 29-bit ID [13:28]" [color = lightblue];
48-63: "F1 29-bit ID [13:28]" [color = lightblue];
}

View file

@ -0,0 +1,21 @@
#Diagram of CAN Controller Acceptance Filter (Single) Configuration
packetdiag can_acceptance_filter_single {
node_width = 30
node_height = 35
default_fontsize = 15
colwidth = 32
#Single Filter Standard Frame Format
0-7: Data Byte 2 [color = lightyellow];
8-15: Data Byte 1 [color = lightyellow];
16-19: Unsed [color = lightgrey];
20: RTR [color = lightyellow];
21-31: 11 bit ID [color = lightyellow];
#Single Filter Extended Frame Format
32-33: Unused [color = lightgrey];
34: RTR [color = lightblue];
35-63: 29 bit ID [color = lightblue];
}

View file

@ -0,0 +1,12 @@
#Example of bit timing configuration for 500KBPS
packetdiag can_bit_timing_diag{
node_width = 40
node_height = 35
default_fontsize = 15
colwidth = 20
0: "Sync" [color = lightgrey];
1-14: "Tseg1 = 15" [color = lightblue];
15-19: "Tseg2 = 4" [color = lightyellow];
}

View file

@ -0,0 +1,57 @@
#Diagram of CAN controller signal lines
blockdiag can_controller_signals_diagram {
orientation = portrait;
span_width = 80;
#Column 1 nodes
can[label = "CAN Controller", fontsize = 15, shape = roundedbox];
#Column 2 nodes
tx[label = "TX", shape = endpoint];
rx[label = "RX", shape = endpoint];
bus_off[label = "BUS-OFF", shape = endpoint];
clkout[label = "CLKOUT", shape = endpoint];
#Column 3 nodes
hide1 [shape = none];
hide2 [shape = none];
hide3 [shape = none];
hide4 [shape = none];
group {
orientation = portrait;
color = none;
#Group column 1 nodes vertically
can;
}
group {
orientation = portrait;
color = none;
#Group column 2 nodes vertically
tx; rx; bus_off; clkout;
}
group {
orientation = portrait;
color = none;
label = "GPIO Matrix";
fontsize = 20;
shape = line;
#Group column 3 nodes vertically
hide1; hide2; hide3; hide4;
}
can -> tx [folded];
can -> rx [folded];
can -> bus_off [folded];
can -> clkout [folded];
tx -> hide1 [folded];
rx -> hide2 [folded];
bus_off -> hide3 [folded, label = "Optional"];
clkout -> hide4 [folded, label = "Optional"];
}

View file

@ -0,0 +1,31 @@
#State transition diagram of the CAN Driver
blockdiag can_state_transition_diagram {
orientation = landscape;
default_fontsize = 18;
node_width = 180;
node_height = 40;
span_width = 100;
span_height = 40;
#First Row
bus_off [label = "Bus-Off"];
recovering [label = "Recovering"];
#Second Row
uninstalled [label = "Uninstalled"];
stopped [label = "Stopped"];
running [label = "Running"];
app_start[label = "Entry", shape = beginpoint];
bus_off -> uninstalled [folded, thick, fontsize = 14, label = "F"];
bus_off -> recovering [thick, fontsize = 14, label = "G"];
recovering -> stopped [folded, thick, color = blue, fontsize = 14, label = "H"];
uninstalled <-> stopped [thick, fontsize = 14, label = "A/B"];
stopped <-> running [thick, fontsize = 14, label = "C/D"];
running -> bus_off [folded, thick, color = red, fontsize = 14, label = "E"];
app_start -> uninstalled [folded, style = dashed]
}

View file

@ -0,0 +1,604 @@
Controller Area Network (CAN)
=============================
.. _CAN Protocol License Conditions: http://www.bosch-semiconductors.com/media/ip_modules/pdf_2/can_protocol/bosch_can_protocol_license_conditions.pdf
.. warning::
Please note that the ESP32 includes a CAN peripheral. The CAN Protocol is
protected by the intellectual property rights of Robert Bosch GmbH. Therefore
a license is required for any implementation of the CAN Protocol
(see `CAN Protocol License Conditions`_). **Since the selling price of the
ESP32 includes no such royalty fee, Espressif hereby disclaims any liability or
obligation regarding the CAN Protocol license. Users of the CAN Protocol via
the ESP32's CAN peripheral should contact Robert Bosch GmbH directly for the
necessary license.**
.. -------------------------------- Overview -----------------------------------
Overview
--------
The ESP32's peripherals contains a CAN Controller that supports Standard Frame
Format (16-bit ID) and Extended Frame Format (29-bit ID) of the CAN2.0B specification.
.. warning::
The ESP32 CAN controller is not compatible with CAN FD frames and will interpret
such frames as errors.
This programming guide is split into the following sections:
1. :ref:`basic-can-concepts`
2. :ref:`signals-lines-and-transceiver`
3. :ref:`configuration`
4. :ref:`driver-operation`
5. :ref:`examples`
.. --------------------------- Basic CAN Concepts ------------------------------
.. _basic-can-concepts:
Basic CAN Concepts
------------------
.. note::
The following section only covers the basic aspects of CAN. For full details,
see the CAN2.0B specification
The CAN protocol is a multi-master, multi-cast communication protocol with error
detection/signalling and inbuilt message prioritization. The CAN protocol is
commonly used as a communication bus in automotive applications.
**Multi-master:** Any node in a CAN bus is allowed initiate the transfer of data.
**Multi-cast:** When a node transmits a message, all nodes are able to receive
the message (broadcast). However some nodes can selective choose which messages
to accept via the use of acceptance filtering (multi-cast).
**Error Detection and Signalling:** Every CAN node will constantly monitor the
CAN bus. When any node detects an error, it will signal the error by transmitting an error
frame. Other nodes will receive the error frame and transmit their own error frames
in response. This will result in an error detection being propagated to all nodes on
the bus.
**Message Priorities:** If two nodes attempt to transmit simultaneously, the
node transmitting the message with the lower ID will win arbitration. All other
nodes will become receivers ensuring there is at most one transmitter at any time.
CAN Message Frames
^^^^^^^^^^^^^^^^^^
The CAN2.0B specification contains two frame formats known as **Extended Frame**
and **Standard Frame** which contain 29-bit IDs and 11-bit IDs respectively.
A CAN message consists of the following components
- 29-bit or 11-bit ID
- Data Length Code (DLC) between 0 to 8
- Up to 8 bytes of data (should match DLC)
Error States and Counters
^^^^^^^^^^^^^^^^^^^^^^^^^
The CAN2.0B specification implements fault confinement by requiring every CAN node
to maintain two internal error counters known as the **Transmit Error Counter (TEC)**
and the **Receive Error Counter (REC)**. The two error counters are used to determine
a CAN node's **error state**, and the counters are incremented and decremented
following a set of rules (see CAN2.0B specification). These error states are known
as **Error Active**, **Error Passive**, and **Bus-Off**.
**Error Active:** A CAN node is Error Active when **both TEC and REC are less
than 128** and indicates a CAN node is operating normally. Error Active nodes are
allowed to participate in CAN bus activities, and will actively signal any error
conditions it detects by transmitting an **Active Error Flag** over the CAN bus.
**Error Passive:** A CAN node is Error Passive when **either the TEC or REC becomes
greater than or equal to 128**. Error Passive nodes are still able to take part in
CAN bus activities, but will instead transmit a **Passive Error Flag** upon
detection of an error.
**Bus-Off:** A CAN node becomes Bus-Off when the **TEC becomes greater than or equal
to 256**. A Bus-Off node is unable take part in CAN bus activity and will remain so
until it undergoes bus recovery.
.. ---------------------- Signal Lines and Transceiver -------------------------
.. _signals-lines-and-transceiver:
Signals Lines and Transceiver
-----------------------------
The CAN controller does not contain a internal transceiver and therefore
**requires an external transceiver** to operate. The type of external transceiver will
depend on the application's physical layer specification (e.g. using SN65HVD23X
transceivers for ISO 11898-2 compatibility).
The CAN controller's interface consists of 4 signal lines known as **TX, RX, BUS-OFF,
and CLKOUT**. These four signal lines can be routed through the GPIO Matrix to GPIOs.
.. blockdiag:: ../../../_static/diagrams/can/can_controller_signals.diag
:caption: Signal lines of the CAN controller
:align: center
**TX and RX:** The TX and RX signal lines are required to interface with an
external CAN transceiver. Both signal lines represent/interpret a dominant bit
as a low logic level (0V), and a recessive bit as a high logic level (3.3V).
**BUS-OFF:** The BUS-OFF signal line is **optional** and is set to a low logic level
(0V) whenever the CAN controller reaches a bus-off state. The BUS-OFF signal line
is set to a high logic level (3.3V) otherwise.
**CLKOUT:** The CLKOUT signal line is **optional** and outputs a prescaled version
of the CAN controller's source clock (APB Clock).
.. note::
An external transceiver **must internally tie the TX input and the RX output**
such that a change in logic level to the TX signal line can be observed on the
RX line. Failing to do so will cause the CAN controller to interpret differences
in logic levels between the two signal lines as a lost in arbitration or a
bit error.
.. ------------------------------ Configuration --------------------------------
.. _configuration:
Configuration
-------------
Operating Modes
^^^^^^^^^^^^^^^
The CAN driver supports the following modes of operations:
**Normal Mode:** The normal operating mode allows the CAN controller to take part
in bus activities such as transmitting and receiving messages/error frames.
Acknowledgement from another CAN node is required when transmitting message frames.
**No Ack Mode:** The No Acknowledgement mode is similar to normal mode, however
acknowledgements are not required when transmitting message frames. This mode is
useful when self testing the CAN controller.
**Listen Only Mode:** This mode will prevent the CAN controller from taking part
in bus activities. Therefore transmissions of messages/acknowledgement/error frames
will be disabled. However the the CAN controller will still be able to receive
messages (without acknowledging). This mode is suited for applications such as
CAN bus monitoring.
Alerts
^^^^^^
The CAN driver contains an alert feature which is used to notify the application
level of certain CAN driver events. Alerts are selectively enabled when the
CAN driver is installed, but can be reconfigured during runtime by calling
:cpp:func:`can_reconfigure_alerts`. The application can then wait for any enabled
alerts to occur by calling :cpp:func:`can_read_alerts`. The CAN driver supports
the following alerts:
+------------------------------------+------------------------------------------------------------------------+
| Alert | Description |
+====================================+=============================================+==========================+
| ``CAN_ALERT_TX_IDLE`` | No more messages queued for transmission |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_TX_SUCCESS`` | The previous transmission was successful |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_BELOW_ERR_WARN`` | Both error counters have dropped below error warning limit |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_ERR_ACTIVE`` | CAN controller has become error active |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_RECOVERY_IN_PROGRESS`` | CAN controller is undergoing bus recovery |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_BUS_RECOVERED`` | CAN controller has successfully completed bus recovery |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_ARB_LOST`` | The previous transmission lost arbitration |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_ABOVE_ERR_WARN`` | One of the error counters have exceeded the error warning limit |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_BUS_ERROR`` | A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_TX_FAILED`` | The previous transmission has failed |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_RX_QUEUE_FULL`` | The RX queue is full causing a received frame to be lost |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_ERR_PASS`` | CAN controller has become error passive |
+------------------------------------+------------------------------------------------------------------------+
| ``CAN_ALERT_BUS_OFF`` | Bus-off condition occurred. CAN controller can no longer influence bus |
+------------------------------------+------------------------------------------------------------------------+
.. note::
The **error warning limit** can be used to preemptively warn the application
of bus errors before the error passive state is reached. By default the CAN
driver sets the **error warning limit** to **96**. The ``CAN_ALERT_ABOVE_ERR_WARN``
is raised when the TEC or REC becomes larger then or equal to the error warning
limit. The ``CAN_ALERT_BELOW_ERR_WARN`` is raised when both TEC and REC return
back to values below **96**.
.. note::
When enabling alerts, the ``CAN_ALERT_AND_LOG`` flag can be used to cause the
CAN driver to log any raised alerts to UART. The ``CAN_ALERT_ALL`` and
``CAN_ALERT_NONE`` macros can also be used to enable/disable all alerts during
configuration/reconfiguration.
Bit Timing
^^^^^^^^^^
The operating bit rate of the CAN controller is configured using the
:cpp:type:`can_timing_config_t` structure. The period of each bit is made up of
multiple **time quanta**, and the period of a **time quanta** is determined by a
prescaled version of the CAN controller's source clock. A single bit contains the
following segments in the following order:
1. The **Synchronization Segment** consists of a single time quanta
2. **Timing Segment 1** consists of 1 to 16 time quanta before sample point
3. **Timing Segment 2** consists of 1 to 8 time quanta after sample point
The **Baudrate Prescaler** is used to determine the period of each time quanta by
dividing the CAN controller's source clock (80 MHz APB clock). The ``brp`` can be
**any even number from 2 to 128**.
.. packetdiag:: ../../../_static/diagrams/can/can_bit_timing.diag
:caption: Bit timing configuration for 500kbit/s given BRP = 8
:align: center
The sample point of a bit is located on the intersection of Timing Segment 1 and
2. Enabling **Triple Sampling** will cause 3 time quanta to be sampled per bit
instead of 1 (extra samples are located at the tail end of Timing Segment 1).
The **Synchronization Jump Width** is used to determined the maximum number of
time quanta a single bit time can be lengthened/shortened for synchronization
purposes. ``sjw`` can **range from 1 to 4**.
.. note::
Multiple combinations of ``brp``, ``tseg_1``, ``tseg_2``, and ``sjw`` can
achieve the same bit rate. Users should tune these values to the physical
characteristics of their CAN bus by taking into account factors such as
**propagation delay, node information processing time, and phase errors**.
Bit timing **macro initializers** are also available for commonly used CAN bus bit rates.
The following macro initiliazers are provided by the CAN driver.
- ``CAN_TIMING_CONFIG_25KBITS()``
- ``CAN_TIMING_CONFIG_50KBITS()``
- ``CAN_TIMING_CONFIG_100KBITS()``
- ``CAN_TIMING_CONFIG_125KBITS()``
- ``CAN_TIMING_CONFIG_250KBITS()``
- ``CAN_TIMING_CONFIG_500KBITS()``
- ``CAN_TIMING_CONFIG_800KBITS()``
- ``CAN_TIMING_CONFIG_1MBITS()``
Acceptance Filter
^^^^^^^^^^^^^^^^^
The CAN controller contains a hardware acceptance filter which can be used to
filter CAN messages of a particular ID. A node that filters out a message
**will not receive the message, but will still acknowledge it**. Acceptances
filters can make a node more efficient by filtering out messages sent over the
CAN bus that are irrelevant to the CAN node in question. The CAN controller's
acceptance filter is configured using two 32-bit values within :cpp:type:`can_filter_config_t`
known as the **acceptance code** and the **acceptance mask**.
The **acceptance code** specifies the bit sequence which a message's ID, RTR, and
data bytes must match in order for the message to be received by the CAN
controller. The **acceptance mask** is a bit sequence specifying which bits of
the acceptance code can be ignored. This allows for a messages of different IDs
to be accepted by a single acceptance code.
The acceptance filter can be used under **Single or Dual Filter Mode**.
Single Filter Mode will use the acceptance code and mask to define a single
filter. This allows for the first two data bytes of a standard frame to be filtered,
or the entirety of an extended frame's 29-bit ID. The following diagram illustrates
how the 32-bit acceptance code and mask will be interpreted under Single Filter Mode
(Note: The yellow and blue fields represent standard and extended CAN frames respectively).
.. packetdiag:: ../../../_static/diagrams/can/can_acceptance_filter_single.diag
:caption: Bit layout of single filter mode (Right side MSBit)
:align: center
**Dual Filter Mode** will use the acceptance code and mask to define two separate
filters allowing for increased flexibility of ID's to accept, but does not allow
for all 29-bits of an extended ID to be filtered. The following diagram illustrates
how the 32-bit acceptance code and mask will be interpreted under **Dual Filter Mode**
(Note: The yellow and blue fields represent standard and extended CAN frames respectively).
.. packetdiag:: ../../../_static/diagrams/can/can_acceptance_filter_dual.diag
:caption: Bit layout of dual filter mode (Right side MSBit)
:align: center
Disabling TX Queue
^^^^^^^^^^^^^^^^^^
The TX queue can be disabled during configuration by setting the ``tx_queue_len``
member of :cpp:type:`can_general_config_t` to ``0``. This will allow applications
that do not require message transmission to save a small amount of memory when
using the CAN driver.
.. -------------------------------- CAN Driver ---------------------------------
.. _driver-operation:
Driver Operation
----------------
The CAN driver is designed with distinct states and strict rules regarding the
functions or conditions that trigger a state transition. The following diagram
illustrates the various states and their transitions.
.. blockdiag:: ../../../_static/diagrams/can/can_state_transition.diag
:caption: State transition diagram of the CAN driver (see table below)
:align: center
+-------+------------------------+------------------------------------+
| Label | Transition | Action/Condition |
+=======+========================+====================================+
| A | Uninstalled -> Stopped | :cpp:func:`can_driver_install` |
+-------+------------------------+------------------------------------+
| B | Stopped -> Uninstalled | :cpp:func:`can_driver_uninstall` |
+-------+------------------------+------------------------------------+
| C | Stopped -> Running | :cpp:func:`can_start` |
+-------+------------------------+------------------------------------+
| D | Running -> Stopped | :cpp:func:`can_stop` |
+-------+------------------------+------------------------------------+
| E | Running -> Bus-Off | Transmit Error Counter >= 256 |
+-------+------------------------+------------------------------------+
| F | Bus-Off -> Uninstalled | :cpp:func:`can_driver_uninstall` |
+-------+------------------------+------------------------------------+
| G | Bus-Off -> Recovering | :cpp:func:`can_initiate_recovery` |
+-------+------------------------+------------------------------------+
| H | Recovering -> Stopped | 128 occurrences of bus-free signal |
+-------+------------------------+------------------------------------+
Driver States
^^^^^^^^^^^^^
**Uninstalled**: In the uninstalled state, no memory is allocated for the driver
and the CAN controller is powered OFF.
**Stopped**: In this state, the CAN controller is powered ON and the CAN driver
has been installed. However the CAN controller will be unable to take part in
any CAN bus activities such as transmitting, receiving, or acknowledging messages.
**Running**: In the running state, the CAN controller is able to take part in
bus activities. Therefore messages can be transmitted/received/acknowledged.
Furthermore the CAN controller will be able to transmit error frames upon detection
of errors on the CAN bus.
**Bus-Off**: The bus-off state is automatically entered when the CAN controller's
Transmit Error Counter becomes greater than or equal to 256 (see CAN2.0B specification
regarding error counter rules). The bus-off state indicates the occurrence of severe
errors on the CAN bus or in the CAN controller. Whilst in the bus-off state, the
CAN controller will be unable to take part in any CAN bus activities. To exit
the bus-off state, the CAN controller must undergo the bus recovery process.
**Recovering**: The recovering state is entered when the CAN driver undergoes
bus recovery. The CAN driver/controller will remain in the recovering state until
the 128 occurrences of the bus-free signal (see CAN2.0B specification) is observed
on the CAN bus.
Message Flags
^^^^^^^^^^^^^
The CAN driver distinguishes different types of CAN messages by using the message
flags in the ``flags`` field of :cpp:type:`can_message_t`. These flags help
distinguish whether a message is in standard or extended format, an RTR, and the
type of transmission to use when transmitting such a message. The CAN driver
supports the following flags:
+-------------------------------+---------------------------------------------------------------+
| Flag | Description |
+===============================+===============================================================+
| ``CAN_MSG_FLAG_EXTD`` | Message is in Extended Frame Format (29bit ID) |
+-------------------------------+---------------------------------------------------------------+
| ``CAN_MSG_FLAG_RTR`` | Message is a Remote Transmit Request |
+-------------------------------+---------------------------------------------------------------+
| ``CAN_MSG_FLAG_SS`` | Transmit message using Single Shot Transmission (Message will |
| | note be retransmitted upon error or loss of arbitration) |
+-------------------------------+---------------------------------------------------------------+
| ``CAN_MSG_FLAG_SELF`` | Transmit message using Self Reception Request (Transmitted |
| | message will also received by the same node) |
+-------------------------------+---------------------------------------------------------------+
| ``CAN_MSG_FLAG_DLC_NON_COMP`` | Message's Data length code is larger than 8. This |
| | will break compliance with CAN2.0B |
+-------------------------------+---------------------------------------------------------------+
.. note::
The ``CAN_MSG_FLAG_NONE`` flag can be used for Standard Frame Format messages
.. -------------------------------- Examples -----------------------------------
.. _examples:
Examples
--------
Configuration & Installation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following code snippet demonstrates how to configure, install, and start the
CAN driver via the use of the various configuration structures, macro initializers,
the :cpp:func:`can_driver_install` function, and the :cpp:func:`can_start` function.
.. code-block:: c
#include "driver/gpio.h"
#include "driver/can.h"
void app_main()
{
//Initialize configuration structures using macro initializers
can_general_config_t g_config = CAN_GENERAL_CONFIG_DEFAULT(GPIO_NUM_21, GPIO_NUM_22, CAN_MODE_NORMAL);
can_timing_config_t t_config = CAN_TIMING_CONFIG_500KBITS();
can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL();
//Install CAN driver
if (can_driver_install(&g_config, &t_config, &f_config) == ESP_OK) {
printf("Driver installed\n");
} else {
printf("Failed to install driver\n");
return;
}
//Start CAN driver
if (can_start() == ESP_OK) {
printf("Driver started\n");
} else {
printf("Failed to start driver\n");
return;
}
...
}
The usage of macro initializers are not mandatory and each of the configuration
structures can be manually.
Message Transmission
^^^^^^^^^^^^^^^^^^^^
The following code snippet demonstrates how to transmit a message via the usage
of the :cpp:type:`can_message_t` type and :cpp:func:`can_transmit` function.
.. code-block:: c
#include "driver/can.h"
...
//Configure message to transmit
can_message_t message;
message.identifier = 0xAAAA;
message.flags = CAN_MSG_FLAG_EXTD;
message.data_length_code = 4;
for (int i = 0; i < 4; i++) {
message.data[i] = 0;
}
//Queue message for transmission
if (can_transmit(&message, pdMS_TO_TICKS(1000)) == ESP_OK) {
printf("Message queued for transmission\n");
} else {
printf("Failed to queue message for transmission\n");
}
Message Reception
^^^^^^^^^^^^^^^^^
The following code snippet demonstrates how to receive a message via the usage
of the :cpp:type:`can_message_t` type and :cpp:func:`can_receive` function.
.. code-block:: c
#include "driver/can.h"
...
//Wait for message to be received
can_message_t message;
if (can_receive(&message, pdMS_TO_TICKS(10000)) == ESP_OK) {
printf("Message received\n");
} else {
printf("Failed to receive message\n");
return;
}
//Process received message
if (message.flags & CAN_MSG_FLAG_EXTD) {
printf("Message is in Extended Format\n");
} else {
printf("Message is in Standard Format\n");
}
printf("ID is %d\n", message.identifier);
if (!(message.flags & CAN_MSG_FLAG_RTR)) {
for (int i = 0; i < message.data_length_code; i++) {
printf("Data byte %d = %d\n", i, message.data[i]);
}
}
Reconfiguring and Reading Alerts
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following code snippet demonstrates how to reconfigure and read CAN driver
alerts via the use of the :cpp:func:`can_reconfigure_alerts` and
:cpp:func:`can_read_alerts` functions.
.. code-block:: c
#include "driver/can.h"
...
//Reconfigure alerts to detect Error Passive and Bus-Off error states
uint32_t alerts_to_enable = CAN_ALERT_ERR_PASS | CAN_ALERT_BUS_OFF;
if (can_reconfigure_alerts(alerts_to_enable, NULL) == ESP_OK) {
printf("Alerts reconfigured\n");
} else {
printf("Failed to reconfigure alerts");
}
//Block indefinitely until an alert occurs
uint32_t alerts_triggered;
can_read_alerts(&alerts_triggered, portMAX_DELAY);
Stop and Uninstall
^^^^^^^^^^^^^^^^^^
The following code demonstrates how to stop and uninstall the CAN driver via the
use of the :cpp:func:`can_stop` and :cpp:func:`can_driver_uninstall` functions.
.. code-block:: c
#include "driver/can.h"
...
//Stop the CAN driver
if (can_stop() == ESP_OK) {
printf("Driver stopped\n");
} else {
printf("Failed to stop driver\n");
return;
}
//Uninstall the CAN driver
if (can_driver_uninstall() == ESP_OK) {
printf("Driver uninstalled\n");
} else {
printf("Failed to uninstall driver\n");
return;
}
Application Examples
^^^^^^^^^^^^^^^^^^^^
**Network Example:** The CAN Network example demonstrates communication between
two ESP32s using the CAN driver API. One CAN node acts as a network master initiate
and ceasing the transfer of a data from another CAN node acting as a network slave.
The example can be found via :example:`examples/peripheral/can/can_network`.
**Alert and Recovery Example:** This example demonstrates how to use the CAN driver's
alert and bus recovery API. The example purposely introduces errors on the CAN
bus to put the CAN controller into the Bus-Off state. An alert is used to detect
the Bus-Off state and trigger the bus recovery process. The example can be found
via :example:`examples/peripheral/can/can_alert_and_recovery`.
**Self Test Example:** This example uses the No Acknowledge Mode and Self Reception
Request to cause the CAN controller to send and simultaneously receive a series
of messages. This example can be used to verify if the connections between the CAN
controller and the external transceiver are working correctly. The example can be
found via :example:`examples/peripheral/can/can_self_test`.
.. ---------------------------- API Reference ----------------------------------
API Reference
-------------
.. include:: /_build/inc/can.inc

View file

@ -5,6 +5,7 @@ Peripherals API
:maxdepth: 1
ADC <adc>
CAN <can>
DAC <dac>
GPIO (including RTC low power I/O) <gpio>
I2C <i2c>

View file

@ -0,0 +1 @@
.. include:: ../../../en/api-reference/peripherals/can.rst

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := can_alert_and_recovery_example
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,29 @@
# CAN Alert and Recovery Example
## Overview
The CAN Alert and Recovery Example demonstrates the usage of alerts and bus
recovery in the CAN driver. This example **requires only a single ESP32 module
to run**.
The CAN Alert and Recovery Example will do the following...
1. Initialize and start the CAN driver on the ESP32 module
2. Repeatedly transmit messages (no acknowledgement required)
3. Reconfigure alerts to detect bus-off state
4. Purposely trigger errors on transmissions
5. Detect Bus Off condition
6. Initiate bus recovery
7. Deinitialize CAN driver on ESP32 module
## External Transceiver and Pin Assignment
The CAN controller in the ESP32 **does not contain an internal transceiver**.
Therefore users are responsible for providing an external transceiver compatible
with the physical layer specifications of their target ISO standard (such as
SN65HVD23X transceivers for ISO 11898-2 compatibility)
The CAN controller in the ESP32 represents dominant bits to the transceiver as
logic low, and recessive bits as logic high. The Alert and Recovery Example
utilizes the following default pin assignments
* TX Pin is routed to GPIO21
* RX Pin is routed to GPIO22

View file

@ -0,0 +1,29 @@
#Need Python 3 string formatting functions
from __future__ import print_function
import re
import os
import sys
# The test cause is dependent on the Tiny Test Framework. Ensure the
# `TEST_FW_PATH` environment variable is set to `$IDF_PATH/tools/tiny-test-fw`
test_fw_path = os.getenv("TEST_FW_PATH")
if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
import TinyFW
import IDF
# CAN Self Test Example constants
STR_EXPECT = ("CAN Alert and Recovery: Driver installed", "CAN Alert and Recovery: Driver uninstalled")
EXPECT_TIMEOUT = 20
@IDF.idf_example_test(env_tag='Example_CAN')
def test_can_alert_and_recovery_example(env, extra_data):
#Get device under test, flash and start example. "dut4" must be defined in EnvConfig
dut = env.get_dut('dut4', 'examples/peripherals/can/can_alert_and_recovery')
dut.start_app()
for string in STR_EXPECT:
dut.expect(string, timeout = EXPECT_TIMEOUT)
if __name__ == '__main__':
test_can_alert_and_recovery_example()

View file

@ -0,0 +1,153 @@
/* CAN Alert and Recovery Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/*
* The following example demonstrates how to use the alert and bus recovery
* features of the CAN driver. The example will do the following:
* 1) Install and start the CAN driver
* 2) Have the TX task periodically broadcast messages expecting no ACK
* 3) Reconfigure alerts to detect bus-off state
* 4) Trigger bus errors by inverting TX GPIO
* 5) Initiate bus-off recovery and wait for completion
* 6) Uninstall CAN driver
*/
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/can.h"
/* --------------------- Definitions and static variables ------------------ */
//Example Configuration
#define TX_GPIO_NUM 21
#define RX_GPIO_NUM 22
#define TX_TASK_PRIO 9
#define CTRL_TASK_PRIO 10
#define ERR_DELAY_US 800 //Approximate time for arbitration phase at 25KBPS
#define ERR_PERIOD_US 80 //Approximate time for two bits at 25KBPS
#define EXAMPLE_TAG "CAN Alert and Recovery"
static const can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL();
static const can_timing_config_t t_config = CAN_TIMING_CONFIG_25KBITS();
static const can_general_config_t g_config = CAN_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, CAN_MODE_NO_ACK);
static const can_message_t tx_msg = {.identifier = 0, .data_length_code = 0, .flags = CAN_MSG_FLAG_NONE};
static SemaphoreHandle_t tx_task_sem;
static SemaphoreHandle_t ctrl_task_sem;
static bool trigger_tx_error = false;
/* --------------------------- Tasks and Functions -------------------------- */
static void invert_tx_bits(bool enable)
{
if (enable) {
//Inverts output of TX to trigger errors
gpio_matrix_out(TX_GPIO_NUM, CAN_TX_IDX, true, false);
} else {
//Returns TX to default settings
gpio_matrix_out(TX_GPIO_NUM, CAN_TX_IDX, false, false);
}
}
static void tx_task(void *arg)
{
xSemaphoreTake(tx_task_sem, portMAX_DELAY);
while (1) {
if (can_transmit(&tx_msg, 0) == ESP_ERR_INVALID_STATE) {
break; //Exit TX task when bus-off state is reached
}
if (trigger_tx_error) {
//Trigger a bit error in transmission by inverting GPIO
ets_delay_us(ERR_DELAY_US); //Wait until arbitration phase is over
invert_tx_bits(true); //Trigger bit error for a few bits
ets_delay_us(ERR_PERIOD_US);
invert_tx_bits(false);
}
vTaskDelay(pdMS_TO_TICKS(50));
}
vTaskDelete(NULL);
}
static void ctrl_task(void *arg)
{
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
ESP_ERROR_CHECK(can_start());
ESP_LOGI(EXAMPLE_TAG, "Driver started");
ESP_LOGI(EXAMPLE_TAG, "Starting transmissions");
xSemaphoreGive(tx_task_sem); //Start transmit task
//Prepare to trigger errors, reconfigure alerts to detect change in error state
can_reconfigure_alerts(CAN_ALERT_ABOVE_ERR_WARN | CAN_ALERT_ERR_PASS | CAN_ALERT_BUS_OFF, NULL);
for (int i = 3; i > 0; i--) {
ESP_LOGW(EXAMPLE_TAG, "Trigger TX errors in %d", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
ESP_LOGI(EXAMPLE_TAG, "Trigger errors");
trigger_tx_error = true;
while (1) {
uint32_t alerts;
can_read_alerts(&alerts, portMAX_DELAY);
if (alerts & CAN_ALERT_ABOVE_ERR_WARN) {
ESP_LOGI(EXAMPLE_TAG, "Surpassed Error Warning Limit");
}
if (alerts & CAN_ALERT_ERR_PASS) {
ESP_LOGI(EXAMPLE_TAG, "Entered Error Passive state");
}
if (alerts & CAN_ALERT_BUS_OFF) {
ESP_LOGI(EXAMPLE_TAG, "Bus Off state");
//Prepare to initiate bus recovery, reconfigure alerts to detect bus recovery completion
can_reconfigure_alerts(CAN_ALERT_BUS_RECOVERED, NULL);
for (int i = 3; i > 0; i--) {
ESP_LOGW(EXAMPLE_TAG, "Initiate bus recovery in %d", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
can_initiate_recovery(); //Needs 128 occurrences of bus free signal
ESP_LOGI(EXAMPLE_TAG, "Initiate bus recovery");
}
if (alerts & CAN_ALERT_BUS_RECOVERED) {
//Bus recovery was successful, exit control task to uninstall driver
ESP_LOGI(EXAMPLE_TAG, "Bus Recovered");
break;
}
}
//No need call can_stop(), bus recovery will return to stopped state
xSemaphoreGive(ctrl_task_sem);
vTaskDelete(NULL);
}
void app_main()
{
tx_task_sem = xSemaphoreCreateBinary();
ctrl_task_sem = xSemaphoreCreateBinary();
xTaskCreatePinnedToCore(tx_task, "CAN_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(ctrl_task, "CAN_ctrl", 4096, NULL, CTRL_TASK_PRIO, NULL, tskNO_AFFINITY);
//Install CAN driver
ESP_ERROR_CHECK(can_driver_install(&g_config, &t_config, & f_config));
ESP_LOGI(EXAMPLE_TAG, "Driver installed");
xSemaphoreGive(ctrl_task_sem); //Start control task
vTaskDelay(pdMS_TO_TICKS(100));
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); //Wait for completion
//Uninstall CAN driver
ESP_ERROR_CHECK(can_driver_uninstall());
ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled");
//Cleanup
vSemaphoreDelete(tx_task_sem);
vSemaphoreDelete(ctrl_task_sem);
}

View file

@ -0,0 +1,4 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,67 @@
# CAN Network Example
## Overview
The CAN Network Example demonstrates communication between two ESP32 modules (master
and slave) using the CAN2.0B protocol. CAN is a multi-master protocol, therefore
the concept of master/slave in this example refers to which node initiates
and stops the transfer of a stream of data messages. The example also includes
an optional **Listen Only module** which can passively receive the CAN messages
sent between the master and slave module without participating in any CAN bus activity.
The CAN Network Example will execute the following steps over multiple iterations:
1. Both master and slave go through initialization process
2. The master repeatedly sends **PING** messages until it receives a **PING_RESP**
from the slave. The slave will only send a **PING_RESP** message when it receives
a **PING** message from the master.
3. Once the master has received the **PING_RESP** from the slave, it will send a
**START_CMD** message to the slave.
4. Upon receiving the **START_CMD** message, the slave will start transmitting
**DATA** messages until the master sends a **STOP_CMD**. The master will send
the **STOP_CMD** after receiving N **DATA** messages from the slave (N = 50 by
default).
5. When the slave receives the **STOP_CMD**, it will confirm that it has stopped
by sending a **STOP_RESP** message to the master.
## External Transceiver and Pin Assignment
The CAN controller in the ESP32 **does not contain an internal transceiver**.
Therefore users are responsible for providing an external transceiver compatible
with the physical layer specifications of their target ISO standard (such as
SN65HVD23X transceivers for ISO 11898-2 compatibility)
The CAN controller in the ESP32 represents dominant bits to the transceiver as
logic low, and recessive bits as logic high. The Network Example utilizes the
following default pin assignments
* TX Pin is routed to GPIO21
* RX Pin is routed to GPIO22
The following diagram illustrates an example network
~~~~
---------- ---------- --------------
| Master | | Slave | | Listen Only |
| | | | | |
| 21 22 | | 21 22 | | 21 22 |
---------- ---------- --------------
| | | | | |
| | | | | |
---------- ---------- ----------
| D R | | D R | | D R |
| | | | | |
| VP230 | | VP230 | | VP230 |
| | | | | |
| H L | | H L | | H L |
---------- ---------- ----------
| | | | | |
| | | | | |
|--x------|-----x------|-----x------|--| H
| | |
|---------x------------x------------x--| L
~~~~
## Note
If there appears to be no activity on the CAN bus when running the example, users
can try running the `can_self_test` example to verify if their transceivers are
wired properly.

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := can_network_listen_only
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,123 @@
/* CAN Network Listen Only Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/*
* The following example demonstrates a Listen Only node in a CAN network. The
* Listen Only node will not take part in any CAN bus activity (no acknowledgments
* and no error frames). This example will execute multiple iterations, with each
* iteration the Listen Only node will do the following:
* 1) Listen for ping and ping response
* 2) Listen for start command
* 3) Listen for data messages
* 4) Listen for stop and stop response
*/
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/can.h"
/* --------------------- Definitions and static variables ------------------ */
//Example Configuration
#define NO_OF_ITERS 3
#define RX_TASK_PRIO 9
#define TX_GPIO_NUM 21
#define RX_GPIO_NUM 22
#define EXAMPLE_TAG "CAN Listen Only"
#define ID_MASTER_STOP_CMD 0x0A0
#define ID_MASTER_START_CMD 0x0A1
#define ID_MASTER_PING 0x0A2
#define ID_SLAVE_STOP_RESP 0x0B0
#define ID_SLAVE_DATA 0x0B1
#define ID_SLAVE_PING_RESP 0x0B2
static const can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL();
static const can_timing_config_t t_config = CAN_TIMING_CONFIG_25KBITS();
//Set TX queue length to 0 due to listen only mode
static const can_general_config_t g_config = {.mode = CAN_MODE_LISTEN_ONLY,
.tx_io = TX_GPIO_NUM, .rx_io = RX_GPIO_NUM,
.clkout_io = CAN_IO_UNUSED, .bus_off_io = CAN_IO_UNUSED,
.tx_queue_len = 0, .rx_queue_len = 5,
.alerts_enabled = CAN_ALERT_NONE,
.clkout_divider = 0};
static SemaphoreHandle_t rx_sem;
/* --------------------------- Tasks and Functions -------------------------- */
static void can_receive_task(void *arg)
{
xSemaphoreTake(rx_sem, portMAX_DELAY);
bool start_cmd = false;
bool stop_resp = false;
uint32_t iterations = 0;
while (iterations < NO_OF_ITERS) {
can_message_t rx_msg;
can_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_MASTER_PING) {
ESP_LOGI(EXAMPLE_TAG, "Received master ping");
} else if (rx_msg.identifier == ID_SLAVE_PING_RESP) {
ESP_LOGI(EXAMPLE_TAG, "Received slave ping response");
} else if (rx_msg.identifier == ID_MASTER_START_CMD) {
ESP_LOGI(EXAMPLE_TAG, "Received master start command");
start_cmd = true;
} else if (rx_msg.identifier == ID_SLAVE_DATA) {
uint32_t data = 0;
for (int i = 0; i < rx_msg.data_length_code; i++) {
data |= (rx_msg.data[i] << (i * 8));
}
ESP_LOGI(EXAMPLE_TAG, "Received data value %d", data);
} else if (rx_msg.identifier == ID_MASTER_STOP_CMD) {
ESP_LOGI(EXAMPLE_TAG, "Received master stop command");
} else if (rx_msg.identifier == ID_SLAVE_STOP_RESP) {
ESP_LOGI(EXAMPLE_TAG, "Received slave stop response");
stop_resp = true;
}
if (start_cmd && stop_resp) {
//Each iteration is complete after a start command and stop response is received
iterations++;
start_cmd = 0;
stop_resp = 0;
}
}
xSemaphoreGive(rx_sem);
vTaskDelete(NULL);
}
void app_main()
{
rx_sem = xSemaphoreCreateBinary();
xTaskCreatePinnedToCore(can_receive_task, "CAN_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY);
//Install and start CAN driver
ESP_ERROR_CHECK(can_driver_install(&g_config, &t_config, &f_config));
ESP_LOGI(EXAMPLE_TAG, "Driver installed");
ESP_ERROR_CHECK(can_start());
ESP_LOGI(EXAMPLE_TAG, "Driver started");
xSemaphoreGive(rx_sem); //Start RX task
vTaskDelay(pdMS_TO_TICKS(100));
xSemaphoreTake(rx_sem, portMAX_DELAY); //Wait for RX task to complete
//Stop and uninstall CAN driver
ESP_ERROR_CHECK(can_stop());
ESP_LOGI(EXAMPLE_TAG, "Driver stopped");
ESP_ERROR_CHECK(can_driver_uninstall());
ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled");
//Cleanup
vSemaphoreDelete(rx_sem);
}

View file

@ -0,0 +1,4 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := can_network_master
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,235 @@
/* CAN Network Master Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/*
* The following example demonstrates a master node in a CAN network. The master
* node is responsible for initiating and stopping the transfer of data messages.
* The example will execute multiple iterations, with each iteration the master
* node will do the following:
* 1) Start the CAN driver
* 2) Repeatedly send ping messages until a ping response from slave is received
* 3) Send start command to slave and receive data messages from slave
* 4) Send stop command to slave and wait for stop response from slave
* 5) Stop the CAN driver
*/
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/can.h"
/* --------------------- Definitions and static variables ------------------ */
//Example Configuration
#define PING_PERIOD_MS 250
#define NO_OF_DATA_MSGS 50
#define NO_OF_ITERS 3
#define ITER_DELAY_MS 1000
#define RX_TASK_PRIO 8
#define TX_TASK_PRIO 9
#define CTRL_TSK_PRIO 10
#define TX_GPIO_NUM 21
#define RX_GPIO_NUM 22
#define EXAMPLE_TAG "CAN Master"
#define ID_MASTER_STOP_CMD 0x0A0
#define ID_MASTER_START_CMD 0x0A1
#define ID_MASTER_PING 0x0A2
#define ID_SLAVE_STOP_RESP 0x0B0
#define ID_SLAVE_DATA 0x0B1
#define ID_SLAVE_PING_RESP 0x0B2
typedef enum {
TX_SEND_PINGS,
TX_SEND_START_CMD,
TX_SEND_STOP_CMD,
TX_TASK_EXIT,
} tx_task_action_t;
typedef enum {
RX_RECEIVE_PING_RESP,
RX_RECEIVE_DATA,
RX_RECEIVE_STOP_RESP,
RX_TASK_EXIT,
} rx_task_action_t;
static const can_timing_config_t t_config = CAN_TIMING_CONFIG_25KBITS();
static const can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL();
static const can_general_config_t g_config = CAN_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, CAN_MODE_NORMAL);
static const can_message_t ping_message = {.identifier = ID_MASTER_PING, .data_length_code = 0,
.flags = CAN_MSG_FLAG_SS, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}};
static const can_message_t start_message = {.identifier = ID_MASTER_START_CMD, .data_length_code = 0,
.flags = CAN_MSG_FLAG_NONE, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}};
static const can_message_t stop_message = {.identifier = ID_MASTER_STOP_CMD, .data_length_code = 0,
.flags = CAN_MSG_FLAG_NONE, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}};
static QueueHandle_t tx_task_queue;
static QueueHandle_t rx_task_queue;
static SemaphoreHandle_t stop_ping_sem;
static SemaphoreHandle_t ctrl_task_sem;
static SemaphoreHandle_t done_sem;
/* --------------------------- Tasks and Functions -------------------------- */
static void can_receive_task(void *arg)
{
while (1) {
rx_task_action_t action;
xQueueReceive(rx_task_queue, &action, portMAX_DELAY);
if (action == RX_RECEIVE_PING_RESP) {
//Listen for ping response from slave
while (1) {
can_message_t rx_msg;
can_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_SLAVE_PING_RESP) {
xSemaphoreGive(stop_ping_sem);
xSemaphoreGive(ctrl_task_sem);
break;
}
}
} else if (action == RX_RECEIVE_DATA) {
//Receive data messages from slave
uint32_t data_msgs_rec = 0;
while (data_msgs_rec < NO_OF_DATA_MSGS) {
can_message_t rx_msg;
can_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_SLAVE_DATA) {
uint32_t data = 0;
for (int i = 0; i < rx_msg.data_length_code; i++) {
data |= (rx_msg.data[i] << (i * 8));
}
ESP_LOGI(EXAMPLE_TAG, "Received data value %d", data);
data_msgs_rec ++;
}
}
xSemaphoreGive(ctrl_task_sem);
} else if (action == RX_RECEIVE_STOP_RESP) {
//Listen for stop response from slave
while (1) {
can_message_t rx_msg;
can_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_SLAVE_STOP_RESP) {
xSemaphoreGive(ctrl_task_sem);
break;
}
}
} else if (action == RX_TASK_EXIT) {
break;
}
}
vTaskDelete(NULL);
}
static void can_transmit_task(void *arg) {
while (1) {
tx_task_action_t action;
xQueueReceive(tx_task_queue, &action, portMAX_DELAY);
if (action == TX_SEND_PINGS) {
//Repeatedly transmit pings
ESP_LOGI(EXAMPLE_TAG, "Transmitting ping");
while (xSemaphoreTake(stop_ping_sem, 0) != pdTRUE) {
can_transmit(&ping_message, portMAX_DELAY);
vTaskDelay(pdMS_TO_TICKS(PING_PERIOD_MS));
}
} else if (action == TX_SEND_START_CMD) {
//Transmit start command to slave
can_transmit(&start_message, portMAX_DELAY);
ESP_LOGI(EXAMPLE_TAG, "Transmitted start command");
} else if (action == TX_SEND_STOP_CMD) {
//Transmit stop command to slave
can_transmit(&stop_message, portMAX_DELAY);
ESP_LOGI(EXAMPLE_TAG, "Transmitted stop command");
} else if (action == TX_TASK_EXIT) {
break;
}
}
vTaskDelete(NULL);
}
void can_control_task(void *arg) {
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
tx_task_action_t tx_action;
rx_task_action_t rx_action;
for (int iter = 0; iter < NO_OF_ITERS; iter++) {
ESP_ERROR_CHECK(can_start());
ESP_LOGI(EXAMPLE_TAG, "Driver started");
//Start transmitting pings, and listen for ping response
tx_action = TX_SEND_PINGS;
rx_action = RX_RECEIVE_PING_RESP;
xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY);
xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY);
//Send Start command to slave, and receive data messages
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
tx_action = TX_SEND_START_CMD;
rx_action = RX_RECEIVE_DATA;
xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY);
xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY);
//Send Stop command to slave when enough data messages have been received. Wait for stop response
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
tx_action = TX_SEND_STOP_CMD;
rx_action = RX_RECEIVE_STOP_RESP;
xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY);
xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY);
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
ESP_ERROR_CHECK(can_stop());
ESP_LOGI(EXAMPLE_TAG, "Driver stopped");
vTaskDelay(pdMS_TO_TICKS(ITER_DELAY_MS));
}
//Stop TX and RX tasks
tx_action = TX_TASK_EXIT;
rx_action = RX_TASK_EXIT;
xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY);
xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY);
//Delete Control task
xSemaphoreGive(done_sem);
vTaskDelete(NULL);
}
void app_main()
{
//Create tasks, queues, and semaphores
rx_task_queue = xQueueCreate(1, sizeof(rx_task_action_t));
tx_task_queue = xQueueCreate(1, sizeof(tx_task_action_t));
ctrl_task_sem = xSemaphoreCreateBinary();
stop_ping_sem = xSemaphoreCreateBinary();
done_sem = xSemaphoreCreateBinary();
xTaskCreatePinnedToCore(can_receive_task, "CAN_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(can_transmit_task, "CAN_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(can_control_task, "CAN_ctrl", 4096, NULL, CTRL_TSK_PRIO, NULL, tskNO_AFFINITY);
//Install CAN driver
ESP_ERROR_CHECK(can_driver_install(&g_config, &t_config, &f_config));
ESP_LOGI(EXAMPLE_TAG, "Driver installed");
xSemaphoreGive(ctrl_task_sem); //Start control task
xSemaphoreTake(done_sem, portMAX_DELAY); //Wait for completion
//Uninstall CAN driver
ESP_ERROR_CHECK(can_driver_uninstall());
ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled");
//Cleanup
vQueueDelete(rx_task_queue);
vQueueDelete(tx_task_queue);
vSemaphoreDelete(ctrl_task_sem);
vSemaphoreDelete(stop_ping_sem);
vSemaphoreDelete(done_sem);
}

View file

@ -0,0 +1,4 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := can_network_slave
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,264 @@
/* CAN Network Slave Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/*
* The following example demonstrates a slave node in a CAN network. The slave
* node is responsible for sending data messages to the master. The example will
* execute multiple iterations, with each iteration the slave node will do the
* following:
* 1) Start the CAN driver
* 2) Listen for ping messages from master, and send ping response
* 3) Listen for start command from master
* 4) Send data messages to master and listen for stop command
* 5) Send stop response to master
* 6) Stop the CAN driver
*/
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/can.h"
/* --------------------- Definitions and static variables ------------------ */
//Example Configuration
#define DATA_PERIOD_MS 50
#define NO_OF_ITERS 3
#define ITER_DELAY_MS 1000
#define RX_TASK_PRIO 8 //Receiving task priority
#define TX_TASK_PRIO 9 //Sending task priority
#define CTRL_TSK_PRIO 10 //Control task priority
#define TX_GPIO_NUM 21
#define RX_GPIO_NUM 22
#define EXAMPLE_TAG "CAN Slave"
#define ID_MASTER_STOP_CMD 0x0A0
#define ID_MASTER_START_CMD 0x0A1
#define ID_MASTER_PING 0x0A2
#define ID_SLAVE_STOP_RESP 0x0B0
#define ID_SLAVE_DATA 0x0B1
#define ID_SLAVE_PING_RESP 0x0B2
typedef enum {
TX_SEND_PING_RESP,
TX_SEND_DATA,
TX_SEND_STOP_RESP,
TX_TASK_EXIT,
} tx_task_action_t;
typedef enum {
RX_RECEIVE_PING,
RX_RECEIVE_START_CMD,
RX_RECEIVE_STOP_CMD,
RX_TASK_EXIT,
} rx_task_action_t;
static const can_general_config_t g_config = CAN_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, CAN_MODE_NORMAL);
static const can_timing_config_t t_config = CAN_TIMING_CONFIG_25KBITS();
static const can_filter_config_t f_config = CAN_FILTER_CONFIG_ACCEPT_ALL();
static const can_message_t ping_resp = {.identifier = ID_SLAVE_PING_RESP, .data_length_code = 0,
.flags = CAN_MSG_FLAG_NONE, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}};
static const can_message_t stop_resp = {.identifier = ID_SLAVE_STOP_RESP, .data_length_code = 0,
.flags = CAN_MSG_FLAG_NONE, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}};
//Data bytes of data message will be initialized in the transmit task
static can_message_t data_message = {.identifier = ID_SLAVE_DATA, .data_length_code = 4,
.flags = CAN_MSG_FLAG_NONE, .data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}};
static QueueHandle_t tx_task_queue;
static QueueHandle_t rx_task_queue;
static SemaphoreHandle_t ctrl_task_sem;
static SemaphoreHandle_t stop_data_sem;
static SemaphoreHandle_t done_sem;
/* --------------------------- Tasks and Functions -------------------------- */
static void can_receive_task(void *arg)
{
while (1) {
rx_task_action_t action;
xQueueReceive(rx_task_queue, &action, portMAX_DELAY);
if (action == RX_RECEIVE_PING) {
//Listen for pings from master
can_message_t rx_msg;
while (1) {
can_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_MASTER_PING) {
xSemaphoreGive(ctrl_task_sem);
break;
}
}
} else if (action == RX_RECEIVE_START_CMD) {
//Listen for start command from master
can_message_t rx_msg;
while (1) {
can_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_MASTER_START_CMD) {
xSemaphoreGive(ctrl_task_sem);
break;
}
}
} else if (action == RX_RECEIVE_STOP_CMD) {
//Listen for stop command from master
can_message_t rx_msg;
while (1) {
can_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_MASTER_STOP_CMD) {
xSemaphoreGive(stop_data_sem);
xSemaphoreGive(ctrl_task_sem);
break;
}
}
} else if (action == RX_TASK_EXIT) {
break;
}
}
vTaskDelete(NULL);
}
static void can_transmit_task(void *arg)
{
while (1) {
tx_task_action_t action;
xQueueReceive(tx_task_queue, &action, portMAX_DELAY);
if (action == TX_SEND_PING_RESP) {
//Transmit ping response to master
can_transmit(&ping_resp, portMAX_DELAY);
ESP_LOGI(EXAMPLE_TAG, "Transmitted ping response");
xSemaphoreGive(ctrl_task_sem);
} else if (action == TX_SEND_DATA) {
//Transmit data messages until stop command is received
ESP_LOGI(EXAMPLE_TAG, "Start transmitting data");
while (1) {
//FreeRTOS tick count used to simulate sensor data
uint32_t sensor_data = xTaskGetTickCount();
for (int i = 0; i < 4; i++) {
data_message.data[i] = (sensor_data >> (i * 8)) & 0xFF;
}
can_transmit(&data_message, portMAX_DELAY);
ESP_LOGI(EXAMPLE_TAG, "Transmitted data value %d", sensor_data);
vTaskDelay(pdMS_TO_TICKS(DATA_PERIOD_MS));
if (xSemaphoreTake(stop_data_sem, 0) == pdTRUE) {
break;
}
}
} else if (action == TX_SEND_STOP_RESP) {
//Transmit stop response to master
can_transmit(&stop_resp, portMAX_DELAY);
ESP_LOGI(EXAMPLE_TAG, "Transmitted stop response");
xSemaphoreGive(ctrl_task_sem);
} else if (action == TX_TASK_EXIT) {
break;
}
}
vTaskDelete(NULL);
}
static void can_control_task(void *arg)
{
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
tx_task_action_t tx_action;
rx_task_action_t rx_action;
for (int iter = 0; iter < NO_OF_ITERS; iter++) {
ESP_ERROR_CHECK(can_start());
ESP_LOGI(EXAMPLE_TAG, "Driver started");
//Listen of pings from master
rx_action = RX_RECEIVE_PING;
xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY);
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
//Send ping response
tx_action = TX_SEND_PING_RESP;
xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY);
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
//Listen for start command
rx_action = RX_RECEIVE_START_CMD;
xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY);
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
//Start sending data messages and listen for stop command
tx_action = TX_SEND_DATA;
rx_action = RX_RECEIVE_STOP_CMD;
xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY);
xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY);
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
//Send stop response
tx_action = TX_SEND_STOP_RESP;
xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY);
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
//Wait for bus to become free
can_status_info_t status_info;
can_get_status_info(&status_info);
while (status_info.msgs_to_tx > 0) {
vTaskDelay(pdMS_TO_TICKS(100));
can_get_status_info(&status_info);
}
ESP_ERROR_CHECK(can_stop());
ESP_LOGI(EXAMPLE_TAG, "Driver stopped");
vTaskDelay(pdMS_TO_TICKS(ITER_DELAY_MS));
}
//Stop TX and RX tasks
tx_action = TX_TASK_EXIT;
rx_action = RX_TASK_EXIT;
xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY);
xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY);
//Delete Control task
xSemaphoreGive(done_sem);
vTaskDelete(NULL);
}
void app_main()
{
//Add short delay to allow master it to initialize first
for (int i = 3; i > 0; i--) {
printf("Slave starting in %d\n", i);
vTaskDelay(pdMS_TO_TICKS(1000));
}
//Create semaphores and tasks
tx_task_queue = xQueueCreate(1, sizeof(tx_task_action_t));
rx_task_queue = xQueueCreate(1, sizeof(rx_task_action_t));
ctrl_task_sem = xSemaphoreCreateBinary();
stop_data_sem = xSemaphoreCreateBinary();;
done_sem = xSemaphoreCreateBinary();;
xTaskCreatePinnedToCore(can_receive_task, "CAN_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(can_transmit_task, "CAN_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(can_control_task, "CAN_ctrl", 4096, NULL, CTRL_TSK_PRIO, NULL, tskNO_AFFINITY);
//Install CAN driver, trigger tasks to start
ESP_ERROR_CHECK(can_driver_install(&g_config, &t_config, &f_config));
ESP_LOGI(EXAMPLE_TAG, "Driver installed");
xSemaphoreGive(ctrl_task_sem); //Start Control task
xSemaphoreTake(done_sem, portMAX_DELAY); //Wait for tasks to complete
//Uninstall CAN driver
ESP_ERROR_CHECK(can_driver_uninstall());
ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled");
//Cleanup
vSemaphoreDelete(ctrl_task_sem);
vSemaphoreDelete(stop_data_sem);
vSemaphoreDelete(done_sem);
vQueueDelete(tx_task_queue);
vQueueDelete(rx_task_queue);
}

View file

@ -0,0 +1,4 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View file

@ -0,0 +1,77 @@
#Need Python 3 string formatting functions
from __future__ import print_function
import re
import os
import sys
import time
from threading import Thread
# The test cause is dependent on the Tiny Test Framework. Ensure the
# `TEST_FW_PATH` environment variable is set to `$IDF_PATH/tools/tiny-test-fw`
test_fw_path = os.getenv("TEST_FW_PATH")
if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
import TinyFW
import IDF
#Define tuple of strings to expect for each DUT.
master_expect = ("CAN Master: Driver installed", "CAN Master: Driver uninstalled")
slave_expect = ("CAN Slave: Driver installed", "CAN Slave: Driver uninstalled")
listen_only_expect = ("CAN Listen Only: Driver installed", "Listen Only: Driver uninstalled")
def dut_thread_callback(**kwargs):
#Parse keyword arguments
dut = kwargs['dut'] #Get DUT from kwargs
expected = kwargs['expected']
result = kwargs['result'] #Get result[out] from kwargs. MUST be of mutable type e.g. list
#Must reset again as flashing during start_app will reset multiple times, causing unexpected results
dut.reset()
for string in expected:
dut.expect(string, 20)
#Mark thread has run to completion without any exceptions
result[0] = True
@IDF.idf_example_test(env_tag='Example_CAN')
def test_can_network_example(env, extra_data):
#Get device under test. "dut1", "dut2", and "dut3" must be properly defined in EnvConfig
dut_master = env.get_dut("dut1", "examples/peripherals/can/can_network/can_network_master")
dut_slave = env.get_dut("dut2", "examples/peripherals/can/can_network/can_network_slave")
dut_listen_only = env.get_dut("dut3", "examples/peripherals/can/can_network/can_network_listen_only")
#Flash app onto each DUT, each DUT is reset again at the start of each thread
dut_master.start_app()
dut_slave.start_app()
dut_listen_only.start_app()
#Create dict of keyword arguments for each dut
results = [[False], [False], [False]]
master_kwargs = {"dut" : dut_master, "result" : results[0], "expected" : master_expect}
slave_kwargs = {"dut" : dut_slave, "result" : results[1], "expected" : slave_expect}
listen_only_kwargs = {"dut" : dut_listen_only, "result" : results[2], "expected" : listen_only_expect}
#Create thread for each dut
dut_master_thread = Thread(target = dut_thread_callback, name = "Master Thread", kwargs = master_kwargs)
dut_slave_thread = Thread(target = dut_thread_callback, name = "Slave Thread", kwargs = slave_kwargs)
dut_listen_only_thread = Thread(target = dut_thread_callback, name = "Listen Only Thread", kwargs = listen_only_kwargs)
#Start each thread
dut_listen_only_thread.start()
dut_master_thread.start()
dut_slave_thread.start()
#Wait for threads to complete
dut_listen_only_thread.join()
dut_master_thread.join()
dut_slave_thread.join()
#check each thread ran to completion
for result in results:
if result[0] != True:
raise Exception("One or more threads did not run successfully")
if __name__ == '__main__':
test_can_network_example()

View file

@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := can_self_test_example
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,33 @@
# CAN Self Test Example
## Overview
The CAN Self Test Example demonstrates the self testing capabilities of the
ESP32 CAN peripheral and **only requires a single ESP32 module to run**.
The Self Test Example can be used to verify that the wiring between the ESP32
and an external transceiver operates correctly.
The CAN Self Test Example will do the following over multiple iterations:
1. Start the CAN driver
2. Simultaneously transmit and receive messages using the self reception request.
3. Stop the CAN driver
## External Transceiver and Pin Assignment
The CAN controller in the ESP32 **does not contain an internal transceiver**.
Therefore users are responsible for providing an external transceiver compatible
with the physical layer specifications of their target ISO standard (such as
SN65HVD23X transceivers for ISO 11898-2 compatibility)
The CAN controller in the ESP32 represents dominant bits to the transceiver as
logic low, and recessive bits as logic high. The Self Test Example utilizes the
following default pin assignments
* TX Pin is routed to GPIO21
* RX Pin is routed to GPIO22
## Note
If the Self Test Example does not receive any messages, it is likely that the
wiring between the ESP32 and the external transceiver is incorrect. To verify
that the CAN controller in the ESP32 is operating correctly, users can bypass
the external transceiver by connecting the TX Pin directly to the RX Pin when
running the Self Test Example.

View file

@ -0,0 +1,29 @@
#Need Python 3 string formatting functions
from __future__ import print_function
import re
import os
import sys
# The test cause is dependent on the Tiny Test Framework. Ensure the
# `TEST_FW_PATH` environment variable is set to `$IDF_PATH/tools/tiny-test-fw`
test_fw_path = os.getenv("TEST_FW_PATH")
if test_fw_path and test_fw_path not in sys.path:
sys.path.insert(0, test_fw_path)
import TinyFW
import IDF
# CAN Self Test Example constants
STR_EXPECT = ("CAN Self Test: Driver installed", "CAN Self Test: Driver uninstalled")
EXPECT_TIMEOUT = 20
@IDF.idf_example_test(env_tag='Example_CAN')
def test_can_self_test_example(env, extra_data):
#Get device under test, flash and start example. "dut4" must be defined in EnvConfig
dut = env.get_dut('dut4', 'examples/peripherals/can/can_self_test')
dut.start_app()
for string in STR_EXPECT:
dut.expect(string, timeout = EXPECT_TIMEOUT)
if __name__ == '__main__':
test_can_self_test_example()

View file

@ -0,0 +1,141 @@
/* CAN Self Test Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/*
* The following example demonstrates the self testing capabilities of the CAN
* peripheral by utilizing the No Acknowledgment Mode and Self Reception Request
* capabilities. This example can be used to verify that the CAN peripheral and
* its connections to the external transceiver operates without issue. The example
* will execute multiple iterations, each iteration will do the following:
* 1) Start the CAN driver
* 2) Transmit and receive 100 messages using self reception request
* 3) Stop the CAN driver
*/
#include <stdio.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/can.h"
/* --------------------- Definitions and static variables ------------------ */
//Example Configurations
#define NO_OF_MSGS 100
#define NO_OF_ITERS 3
#define TX_GPIO_NUM 21
#define RX_GPIO_NUM 22
#define TX_TASK_PRIO 8 //Sending task priority
#define RX_TASK_PRIO 9 //Receiving task priority
#define CTRL_TSK_PRIO 10 //Control task priority
#define MSG_ID 0x555 //11 bit standard format ID
#define EXAMPLE_TAG "CAN Self Test"
static const can_timing_config_t t_config = CAN_TIMING_CONFIG_25KBITS();
//Filter all other IDs except MSG_ID
static const can_filter_config_t f_config = {.acceptance_code = (MSG_ID << 21),
.acceptance_mask = ~(CAN_STD_ID_MASK << 21),
.single_filter = true};
//Set to NO_ACK mode due to self testing with single module
static const can_general_config_t g_config = CAN_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, CAN_MODE_NO_ACK);
static SemaphoreHandle_t tx_sem;
static SemaphoreHandle_t rx_sem;
static SemaphoreHandle_t ctrl_sem;
static SemaphoreHandle_t done_sem;
/* --------------------------- Tasks and Functions -------------------------- */
static void can_transmit_task(void *arg)
{
can_message_t tx_msg = {.data_length_code = 1, .identifier = MSG_ID, .flags = CAN_MSG_FLAG_SELF};
for (int iter = 0; iter < NO_OF_ITERS; iter++) {
xSemaphoreTake(tx_sem, portMAX_DELAY);
for (int i = 0; i < NO_OF_MSGS; i++) {
//Transmit messages using self reception request
tx_msg.data[0] = i;
ESP_ERROR_CHECK(can_transmit(&tx_msg, portMAX_DELAY));
vTaskDelay(pdMS_TO_TICKS(10));
}
}
vTaskDelete(NULL);
}
static void can_receive_task(void *arg)
{
can_message_t rx_message;
for (int iter = 0; iter < NO_OF_ITERS; iter++) {
xSemaphoreTake(rx_sem, portMAX_DELAY);
for (int i = 0; i < NO_OF_MSGS; i++) {
//Receive message and print message data
ESP_ERROR_CHECK(can_receive(&rx_message, portMAX_DELAY))
ESP_LOGI(EXAMPLE_TAG, "Msg received - Data = %d", rx_message.data[0]);
}
//Indicate to control task all messages received for this iteration
xSemaphoreGive(ctrl_sem);
}
vTaskDelete(NULL);
}
static void can_control_task(void *arg)
{
xSemaphoreTake(ctrl_sem, portMAX_DELAY);
for (int iter = 0; iter < NO_OF_ITERS; iter++) {
//Start CAN Driver for this iteration
ESP_ERROR_CHECK(can_start());
ESP_LOGI(EXAMPLE_TAG, "Driver started");
//Trigger TX and RX tasks to start transmitting/receiving
xSemaphoreGive(rx_sem);
xSemaphoreGive(tx_sem);
xSemaphoreTake(ctrl_sem, portMAX_DELAY); //Wait for TX and RX tasks to finish iteration
ESP_ERROR_CHECK(can_stop()); //Stop the CAN Driver
ESP_LOGI(EXAMPLE_TAG, "Driver stopped");
vTaskDelay(pdMS_TO_TICKS(100)); //Delay then start next iteration
}
xSemaphoreGive(done_sem);
vTaskDelete(NULL);
}
void app_main()
{
//Create tasks and synchronization primitives
tx_sem = xSemaphoreCreateBinary();
rx_sem = xSemaphoreCreateBinary();
ctrl_sem = xSemaphoreCreateBinary();
done_sem = xSemaphoreCreateBinary();
xTaskCreatePinnedToCore(can_control_task, "CAN_ctrl", 4096, NULL, CTRL_TSK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(can_receive_task, "CAN_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(can_transmit_task, "CAN_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY);
//Install CAN driver
ESP_ERROR_CHECK(can_driver_install(&g_config, & t_config, &f_config));
ESP_LOGI(EXAMPLE_TAG, "Driver installed");
//Start control task
xSemaphoreGive(ctrl_sem);
//Wait for all iterations and tasks to complete running
xSemaphoreTake(done_sem, portMAX_DELAY);
//Uninstall CAN driver
ESP_ERROR_CHECK(can_driver_uninstall());
ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled");
//Cleanup
vSemaphoreDelete(tx_sem);
vSemaphoreDelete(rx_sem);
vSemaphoreDelete(ctrl_sem);
vQueueDelete(done_sem);
}

View file

@ -0,0 +1,4 @@
#
# Main Makefile. This is basically the same as a component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)