OVMS3-idf/examples/peripherals/can/can_network/can_network_slave/main/can_network_example_slave_main.c
Darian Leung 1d2727f4c8 CAN Driver
The following commit contains the first version of the ESP32 CAN Driver.

closes #544
2018-07-04 14:01:57 +08:00

265 lines
9.7 KiB
C

/* 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);
}