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
@@ -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
@@ -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
@@ -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()
@@ -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);
}
@@ -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.)
@@ -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.
@@ -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
@@ -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);
}
@@ -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.)
@@ -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
@@ -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);
}
@@ -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.)
@@ -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
@@ -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);
}
@@ -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.)
@@ -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()
@@ -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
@@ -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.
@@ -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()
@@ -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);
}
@@ -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.)