TWAI: Add ESP32-S2 support

This commit adds TWAI driver support for the
ESP32-S2. The following features were added:

- Expanded BRP support
- Expanded CLKOUT Divider Support
- Updated example READMEs
This commit is contained in:
Darian Leung 2020-03-11 17:45:02 +01:00
parent 4726076da5
commit e6db25af9d
100 changed files with 4788 additions and 2888 deletions

View file

@ -1,6 +1,5 @@
set(srcs
"adc_common.c"
"can.c"
"dac.c"
"gpio.c"
"i2c.c"
@ -21,6 +20,7 @@ set(srcs
"spi_bus_lock.c"
"timer.c"
"touch_sensor_common.c"
"twai.c"
"uart.c")
set(includes "include")

View file

@ -1,695 +0,0 @@
// 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 "soc/soc_caps.h"
#ifdef SOC_CAN_SUPPORTED
#include "sdkconfig.h"
#include "freertos/FreeRTOS.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 "esp_pm.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "driver/can.h"
#include "soc/can_periph.h"
#include "hal/can_hal.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"
#define DRIVER_DEFAULT_INTERRUPTS 0xE7 //Exclude data overrun (bit[3]) and brp_div (bit[4])
//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 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 ------------------- */
//Control structure for CAN driver
typedef struct {
//Control and status members
uint32_t control_flags;
can_mode_t mode;
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;
#ifdef CONFIG_PM_ENABLE
//Power Management
esp_pm_lock_handle_t pm_lock;
#endif
} can_obj_t;
static can_obj_t *p_can_obj = NULL;
static portMUX_TYPE can_spinlock = portMUX_INITIALIZER_UNLOCKED;
#define CAN_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&can_spinlock)
#define CAN_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&can_spinlock)
#define CAN_ENTER_CRITICAL() portENTER_CRITICAL(&can_spinlock)
#define CAN_EXIT_CRITICAL() portEXIT_CRITICAL(&can_spinlock)
static can_hal_context_t can_context;
/* -------------------- 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 inline void can_handle_bus_off(int *alert_req)
{
//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_hal_handle_bus_off(&can_context);
can_alert_handler(CAN_ALERT_BUS_OFF, alert_req);
}
static inline void can_handle_recovery_complete(int *alert_req)
{
//Bus recovery complete.
assert(can_hal_handle_bus_recov_cplt(&can_context));
//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);
}
static inline void can_handle_recovery_in_progress(int * alert_req)
{
//Bus-recovery in progress. TEC has dropped below error warning limit
can_alert_handler(CAN_ALERT_RECOVERY_IN_PROGRESS, alert_req);
}
static inline void can_handle_above_ewl(int *alert_req)
{
//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);
}
static inline void can_handle_below_ewl(int *alert_req)
{
//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 inline void can_handle_error_passive(int *alert_req)
{
//Entered error passive
CAN_SET_FLAG(p_can_obj->control_flags, CTRL_FLAG_ERR_PASSIVE);
can_alert_handler(CAN_ALERT_ERR_PASS, alert_req);
}
static inline void can_handle_error_active(int *alert_req)
{
//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 inline void can_handle_bus_error(int *alert_req)
{
// ECC register is read to re-arm bus error interrupt. ECC is not used
can_hal_handle_bus_error(&can_context);
p_can_obj->bus_error_count++;
can_alert_handler(CAN_ALERT_BUS_ERROR, alert_req);
}
static inline void can_handle_arb_lost(int *alert_req)
{
//ALC register is read to re-arm arb lost interrupt. ALC is not used
can_hal_handle_arb_lost(&can_context);
p_can_obj->arb_lost_count++;
can_alert_handler(CAN_ALERT_ARB_LOST, alert_req);
}
static inline void can_handle_rx_buffer_frames(BaseType_t *task_woken, int *alert_req)
{
uint32_t msg_count = can_hal_get_rx_msg_count(&can_context);
for (int i = 0; i < msg_count; i++) {
can_hal_frame_t frame;
can_hal_read_rx_buffer_and_clear(&can_context, &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);
}
}
//Todo: Add Software Filters
//Todo: Check for data overrun of RX FIFO, then trigger alert
}
static inline void can_handle_tx_buffer_frame(BaseType_t *task_woken, int *alert_req)
{
//Handle previously transmitted frame
if (can_hal_check_last_tx_successful(&can_context)) {
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--;
assert(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_hal_frame_t frame;
int res = xQueueReceiveFromISR(p_can_obj->tx_queue, &frame, task_woken);
if (res == pdTRUE) {
can_hal_set_tx_buffer_and_transmit(&can_context, &frame);
} else {
assert(false && "failed to get a frame from TX queue");
}
} 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;
uint32_t event;
CAN_ENTER_CRITICAL_ISR();
if (p_can_obj == NULL) { //Incase intr occurs whilst driver is being uninstalled
CAN_EXIT_CRITICAL_ISR();
return;
}
event = can_hal_decode_interrupt_events(&can_context, p_can_obj->control_flags & CTRL_FLAG_RECOVERING);
if (event & CAN_HAL_EVENT_BUS_OFF) {
can_handle_bus_off(&alert_req);
}
if (event & CAN_HAL_EVENT_BUS_RECOV_CPLT) {
can_handle_recovery_complete(&alert_req);
}
if (event & CAN_HAL_EVENT_BUS_RECOV_PROGRESS) {
can_handle_recovery_in_progress(&alert_req);
}
if (event & CAN_HAL_EVENT_ABOVE_EWL) {
can_handle_above_ewl(&alert_req);
}
if (event & CAN_HAL_EVENT_BELOW_EWL) {
can_handle_below_ewl(&alert_req);
}
if (event & CAN_HAL_EVENT_ERROR_PASSIVE) {
can_handle_error_passive(&alert_req);
}
if (event & CAN_HAL_EVENT_ERROR_ACTIVE) {
can_handle_error_active(&alert_req);
}
if (event & CAN_HAL_EVENT_BUS_ERR) {
can_handle_bus_error(&alert_req);
}
if (event & CAN_HAL_EVENT_ARB_LOST) {
can_handle_arb_lost(&alert_req);
}
if (event & CAN_HAL_EVENT_RX_BUFF_FRAME) {
can_handle_rx_buffer_frames(&task_woken, &alert_req);
}
//TX command related handlers should be called last, so that other commands
//do not overwrite the TX command related bits in the command register.
if (event & CAN_HAL_EVENT_TX_BUFF_FREE) {
can_handle_tx_buffer_frame(&task_woken, &alert_req);
}
CAN_EXIT_CRITICAL_ISR();
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();
}
}
/* --------------------------- GPIO functions ------------------------------ */
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);
gpio_set_direction(rx, GPIO_MODE_INPUT);
//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
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);
CAN_CHECK(CAN_BRP_IS_VALID(t_config->brp), ESP_ERR_INVALID_ARG);
esp_err_t ret;
can_obj_t *p_can_obj_dummy;
//Create a CAN object
p_can_obj_dummy = calloc(1, sizeof(can_obj_t));
CAN_CHECK(p_can_obj_dummy != NULL, ESP_ERR_NO_MEM);
//Initialize queues, semaphores, and power management locks
p_can_obj_dummy->tx_queue = (g_config->tx_queue_len > 0) ? xQueueCreate(g_config->tx_queue_len, sizeof(can_hal_frame_t)) : NULL;
p_can_obj_dummy->rx_queue = xQueueCreate(g_config->rx_queue_len, sizeof(can_hal_frame_t));
p_can_obj_dummy->alert_semphr = xSemaphoreCreateBinary();
if ((g_config->tx_queue_len > 0 && p_can_obj_dummy->tx_queue == NULL) ||
p_can_obj_dummy->rx_queue == NULL || p_can_obj_dummy->alert_semphr == NULL) {
ret = ESP_ERR_NO_MEM;
goto err;
}
#ifdef CONFIG_PM_ENABLE
esp_err_t pm_err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "can", &(p_can_obj_dummy->pm_lock));
if (pm_err != ESP_OK ) {
ret = pm_err;
goto err;
}
#endif
//Initialize flags and variables. All other members are 0 initialized by calloc()
p_can_obj_dummy->control_flags = CTRL_FLAG_STOPPED;
p_can_obj_dummy->mode = g_config->mode;
p_can_obj_dummy->alerts_enabled = g_config->alerts_enabled;
//Initialize CAN peripheral registers, and allocate interrupt
CAN_ENTER_CRITICAL();
if (p_can_obj == NULL) {
p_can_obj = p_can_obj_dummy;
} else {
//Check if driver is already installed
CAN_EXIT_CRITICAL();
ret = ESP_ERR_INVALID_STATE;
goto err;
}
periph_module_reset(PERIPH_CAN_MODULE);
periph_module_enable(PERIPH_CAN_MODULE); //Enable APB CLK to CAN peripheral
assert(can_hal_init(&can_context));
can_hal_configure(&can_context, t_config, f_config, DRIVER_DEFAULT_INTERRUPTS, g_config->clkout_divider);
//Todo: Allow interrupt to be registered to specified CPU
CAN_EXIT_CRITICAL();
//Allocate GPIO and Interrupts
can_configure_gpio(g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io);
ESP_ERROR_CHECK(esp_intr_alloc(ETS_CAN_INTR_SOURCE, 0, can_intr_handler_main, NULL, &p_can_obj->isr_handle));
#ifdef CONFIG_PM_ENABLE
ESP_ERROR_CHECK(esp_pm_lock_acquire(p_can_obj->pm_lock)); //Acquire pm_lock to keep APB clock at 80MHz
#endif
return ESP_OK; //CAN module is still in reset mode, users need to call can_start() afterwards
err:
//Cleanup CAN object and return error
if (p_can_obj_dummy != NULL) {
if (p_can_obj_dummy->tx_queue != NULL) {
vQueueDelete(p_can_obj_dummy->tx_queue);
p_can_obj_dummy->tx_queue = NULL;
}
if (p_can_obj_dummy->rx_queue != NULL) {
vQueueDelete(p_can_obj_dummy->rx_queue);
p_can_obj_dummy->rx_queue = NULL;
}
if (p_can_obj_dummy->alert_semphr != NULL) {
vSemaphoreDelete(p_can_obj_dummy->alert_semphr);
p_can_obj_dummy->alert_semphr = NULL;
}
#ifdef CONFIG_PM_ENABLE
if (p_can_obj_dummy->pm_lock != NULL) {
ESP_ERROR_CHECK(esp_pm_lock_delete(p_can_obj_dummy->pm_lock));
}
#endif
free(p_can_obj_dummy);
}
return ret;
}
esp_err_t can_driver_uninstall(void)
{
can_obj_t *p_can_obj_dummy;
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_STOPPED | CTRL_FLAG_BUS_OFF), ESP_ERR_INVALID_STATE);
//Todo: Add check to see if in reset mode. //Enter reset mode to stop any CAN bus activity
//Clear registers by reading
can_hal_deinit(&can_context);
periph_module_disable(PERIPH_CAN_MODULE); //Disable CAN peripheral
p_can_obj_dummy = p_can_obj; //Use dummy to shorten critical section
p_can_obj = NULL;
CAN_EXIT_CRITICAL();
ESP_ERROR_CHECK(esp_intr_free(p_can_obj_dummy->isr_handle)); //Free interrupt
//Delete queues, semaphores, and power management locks
if (p_can_obj_dummy->tx_queue != NULL) {
vQueueDelete(p_can_obj_dummy->tx_queue);
}
vQueueDelete(p_can_obj_dummy->rx_queue);
vSemaphoreDelete(p_can_obj_dummy->alert_semphr);
#ifdef CONFIG_PM_ENABLE
//Release and delete power management lock
ESP_ERROR_CHECK(esp_pm_lock_release(p_can_obj_dummy->pm_lock));
ESP_ERROR_CHECK(esp_pm_lock_delete(p_can_obj_dummy->pm_lock));
#endif
free(p_can_obj_dummy); //Free can driver object
return ESP_OK;
}
esp_err_t can_start(void)
{
//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;
//Todo: Add assert to see if in reset mode. //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
assert(can_hal_start(&can_context, p_can_obj->mode));
CAN_RESET_FLAG(p_can_obj->control_flags, CTRL_FLAG_STOPPED);
CAN_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t can_stop(void)
{
//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);
assert(can_hal_stop(&can_context));
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 <= CAN_FRAME_MAX_DLC) || message->dlc_non_comp, ESP_ERR_INVALID_ARG);
CAN_ENTER_CRITICAL();
//Check State
CAN_CHECK_FROM_CRIT(!(p_can_obj->mode == CAN_MODE_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_hal_frame_t tx_frame;
can_hal_format_frame(message, &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_hal_set_tx_buffer_and_transmit(&can_context, &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_BUS_OFF)) {
//TX queue was reset (due to stop/bus_off), remove copied frame from queue to prevent transmission
int res = xQueueReceive(p_can_obj->tx_queue, &tx_frame, 0);
assert(res == 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
int res = xQueueReceive(p_can_obj->tx_queue, &tx_frame, 0);
assert(res == pdTRUE);
can_hal_set_tx_buffer_and_transmit(&can_context, &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_hal_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_hal_parse_frame(&rx_frame, message);
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();
//Clear any unhandled alerts
if (current_alerts != NULL) {
*current_alerts = p_can_obj->alerts_triggered;;
}
p_can_obj->alerts_triggered = 0;
p_can_obj->alerts_enabled = alerts_enabled; //Update enabled alerts
CAN_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t can_initiate_recovery(void)
{
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
assert(can_hal_start_bus_recovery(&can_context));
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();
status_info->tx_error_counter = can_hal_get_tec(&can_context);
status_info->rx_error_counter = can_hal_get_rec(&can_context);
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;
}
esp_err_t can_clear_transmit_queue(void)
{
//Check State
CAN_CHECK(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_CHECK(p_can_obj->tx_queue != NULL, ESP_ERR_NOT_SUPPORTED);
CAN_ENTER_CRITICAL();
//If a message is currently undergoing transmission, the tx interrupt handler will decrement tx_msg_count
p_can_obj->tx_msg_count = (p_can_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED) ? 1 : 0;
xQueueReset(p_can_obj->tx_queue);
CAN_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t can_clear_receive_queue(void)
{
//Check State
CAN_CHECK(p_can_obj != NULL, ESP_ERR_INVALID_STATE);
CAN_ENTER_CRITICAL();
p_can_obj->rx_msg_count = 0;
xQueueReset(p_can_obj->rx_queue);
CAN_EXIT_CRITICAL();
return ESP_OK;
}
#endif

View file

@ -18,330 +18,57 @@
extern "C" {
#endif
#include "soc/soc_caps.h"
#ifndef SOC_CAN_SUPPORTED
#error CAN is not supported in this chip target
#endif
#warning driver/can.h is deprecated, please use driver/twai.h instead
#include "freertos/FreeRTOS.h"
#include "esp_types.h"
#include "esp_intr_alloc.h"
#include "esp_err.h"
#include "gpio.h"
#include "soc/can_caps.h"
#include "hal/can_types.h"
#include "driver/twai.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, }
/* ---------------------------- Compatibility ------------------------------- */
/**
* @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 */
#define CAN_GENERAL_CONFIG_DEFAULT(tx_io_num, rx_io_num, op_mode) TWAI_GENERAL_CONFIG_DEFAULT(tx_io_num, rx_io_num, op_mode)
/** @endcond */
#define CAN_ALERT_TX_IDLE TWAI_ALERT_TX_IDLE
#define CAN_ALERT_TX_SUCCESS TWAI_ALERT_TX_SUCCESS
#define CAN_ALERT_BELOW_ERR_WARN TWAI_ALERT_BELOW_ERR_WARN
#define CAN_ALERT_ERR_ACTIVE TWAI_ALERT_ERR_ACTIVE
#define CAN_ALERT_RECOVERY_IN_PROGRESS TWAI_ALERT_RECOVERY_IN_PROGRESS
#define CAN_ALERT_BUS_RECOVERED TWAI_ALERT_BUS_RECOVERED
#define CAN_ALERT_ARB_LOST TWAI_ALERT_ARB_LOST
#define CAN_ALERT_ABOVE_ERR_WARN TWAI_ALERT_ABOVE_ERR_WARN
#define CAN_ALERT_BUS_ERROR TWAI_ALERT_BUS_ERROR
#define CAN_ALERT_TX_FAILED TWAI_ALERT_TX_FAILED
#define CAN_ALERT_RX_QUEUE_FULL TWAI_ALERT_RX_QUEUE_FULL
#define CAN_ALERT_ERR_PASS TWAI_ALERT_ERR_PASS
#define CAN_ALERT_BUS_OFF TWAI_ALERT_BUS_OFF
#define CAN_ALERT_ALL TWAI_ALERT_ALL
#define CAN_ALERT_NONE TWAI_ALERT_NONE
#define CAN_ALERT_AND_LOG TWAI_ALERT_AND_LOG
#define CAN_IO_UNUSED ((gpio_num_t) -1) /**< Marks GPIO as unused in CAN configuration */
#define CAN_IO_UNUSED TWAI_IO_UNUSED
/* ----------------------- Enum and Struct Definitions ---------------------- */
#define CAN_STATE_STOPPED TWAI_STATE_STOPPED
#define CAN_STATE_RUNNING TWAI_STATE_RUNNING
#define CAN_STATE_BUS_OFF TWAI_STATE_BUS_OFF
#define CAN_STATE_RECOVERING TWAI_STATE_RECOVERING
/**
* @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;
typedef twai_state_t can_state_t;
typedef twai_general_config_t can_general_config_t;
typedef twai_status_info_t can_status_info_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 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;
/* ----------------------------- 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(void);
/**
* @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(void);
/**
* @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(void);
/**
* @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(void);
/**
* @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);
/**
* @brief Clear the transmit queue
*
* This function will clear the transmit queue of all messages.
*
* @note The transmit queue is automatically cleared when can_stop() or
* can_initiate_recovery() is called.
*
* @return
* - ESP_OK: Transmit queue cleared
* - ESP_ERR_INVALID_STATE: CAN driver is not installed or TX queue is disabled
*/
esp_err_t can_clear_transmit_queue(void);
/**
* @brief Clear the receive queue
*
* This function will clear the receive queue of all messages.
*
* @note The receive queue is automatically cleared when can_start() is
* called.
*
* @return
* - ESP_OK: Transmit queue cleared
* - ESP_ERR_INVALID_STATE: CAN driver is not installed
*/
esp_err_t can_clear_receive_queue(void);
#define can_driver_install(g_config, t_config, f_config) twai_driver_install(g_config, t_config, f_config)
#define can_driver_uninstall() twai_driver_uninstall()
#define can_start() twai_start()
#define can_stop() twai_stop()
#define can_transmit(message, ticks_to_wait) twai_transmit(message, ticks_to_wait)
#define can_receive(message, ticks_to_wait) twai_receive(message, ticks_to_wait)
#define can_read_alerts(alerts, ticks_to_wait) twai_read_alerts(alerts, ticks_to_wait)
#define can_reconfigure_alerts(alerts_enabled, current_alerts) twai_reconfigure_alerts(alerts_enabled, current_alerts)
#define can_initiate_recovery() twai_initiate_recovery()
#define can_get_status_info(status_info) twai_get_status_info(status_info)
#define can_clear_transmit_queue() twai_clear_transmit_queue()
#define can_clear_receive_queue() twai_clear_receive_queue()
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,347 @@
// 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "soc/soc_caps.h"
#ifndef SOC_TWAI_SUPPORTED
#error TWAI is not supported in this chip target
#endif
#include "freertos/FreeRTOS.h"
#include "esp_types.h"
#include "esp_intr_alloc.h"
#include "esp_err.h"
#include "gpio.h"
#include "soc/twai_caps.h"
#include "hal/twai_types.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 TWAI_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 = TWAI_IO_UNUSED, .bus_off_io = TWAI_IO_UNUSED, \
.tx_queue_len = 5, .rx_queue_len = 5, \
.alerts_enabled = TWAI_ALERT_NONE, .clkout_divider = 0, }
/**
* @brief Alert flags
*
* The following flags represents the various kind of alerts available in
* the TWAI driver. These flags can be used when configuring/reconfiguring
* alerts, or when calling twai_read_alerts().
*
* @note The TWAI_ALERT_AND_LOG flag is not an actual alert, but will configure
* the TWAI driver to log to UART when an enabled alert occurs.
*/
#define TWAI_ALERT_TX_IDLE 0x0001 /**< Alert(1): No more messages to transmit */
#define TWAI_ALERT_TX_SUCCESS 0x0002 /**< Alert(2): The previous transmission was successful */
#define TWAI_ALERT_BELOW_ERR_WARN 0x0004 /**< Alert(4): Both error counters have dropped below error warning limit */
#define TWAI_ALERT_ERR_ACTIVE 0x0008 /**< Alert(8): TWAI controller has become error active */
#define TWAI_ALERT_RECOVERY_IN_PROGRESS 0x0010 /**< Alert(16): TWAI controller is undergoing bus recovery */
#define TWAI_ALERT_BUS_RECOVERED 0x0020 /**< Alert(32): TWAI controller has successfully completed bus recovery */
#define TWAI_ALERT_ARB_LOST 0x0040 /**< Alert(64): The previous transmission lost arbitration */
#define TWAI_ALERT_ABOVE_ERR_WARN 0x0080 /**< Alert(128): One of the error counters have exceeded the error warning limit */
#define TWAI_ALERT_BUS_ERROR 0x0100 /**< Alert(256): A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus */
#define TWAI_ALERT_TX_FAILED 0x0200 /**< Alert(512): The previous transmission has failed (for single shot transmission) */
#define TWAI_ALERT_RX_QUEUE_FULL 0x0400 /**< Alert(1024): The RX queue is full causing a frame to be lost */
#define TWAI_ALERT_ERR_PASS 0x0800 /**< Alert(2048): TWAI controller has become error passive */
#define TWAI_ALERT_BUS_OFF 0x1000 /**< Alert(4096): Bus-off condition occurred. TWAI controller can no longer influence bus */
#define TWAI_ALERT_ALL 0x1FFF /**< Bit mask to enable all alerts during configuration */
#define TWAI_ALERT_NONE 0x0000 /**< Bit mask to disable all alerts during configuration */
#define TWAI_ALERT_AND_LOG 0x2000 /**< Bit mask to enable alerts to also be logged when they occur */
/** @endcond */
#define TWAI_IO_UNUSED ((gpio_num_t) -1) /**< Marks GPIO as unused in TWAI configuration */
/* ----------------------- Enum and Struct Definitions ---------------------- */
/**
* @brief TWAI driver states
*/
typedef enum {
TWAI_STATE_STOPPED, /**< Stopped state. The TWAI controller will not participate in any TWAI bus activities */
TWAI_STATE_RUNNING, /**< Running state. The TWAI controller can transmit and receive messages */
TWAI_STATE_BUS_OFF, /**< Bus-off state. The TWAI controller cannot participate in bus activities until it has recovered */
TWAI_STATE_RECOVERING, /**< Recovering state. The TWAI controller is undergoing bus recovery */
} twai_state_t;
/**
* @brief Structure for general configuration of the TWAI driver
*
* @note Macro initializers are available for this structure
*/
typedef struct {
twai_mode_t mode; /**< Mode of TWAI 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) */
} twai_general_config_t;
/**
* @brief Structure to store status information of TWAI driver
*/
typedef struct {
twai_state_t state; /**< Current state of TWAI 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 */
} twai_status_info_t;
/* ------------------------------ Public API -------------------------------- */
/**
* @brief Install TWAI driver
*
* This function installs the TWAI driver using three configuration structures.
* The required memory is allocated and the TWAI 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 TWAI driver, call twai_driver_uninstall() first
*
* @return
* - ESP_OK: Successfully installed TWAI 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 twai_driver_install(const twai_general_config_t *g_config, const twai_timing_config_t *t_config, const twai_filter_config_t *f_config);
/**
* @brief Uninstall the TWAI driver
*
* This function uninstalls the TWAI 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 TWAI driver
* - ESP_ERR_INVALID_STATE: Driver is not in stopped/bus-off state, or is not installed
*/
esp_err_t twai_driver_uninstall(void);
/**
* @brief Start the TWAI driver
*
* This function starts the TWAI driver, putting the TWAI driver into the running
* state. This allows the TWAI driver to participate in TWAI 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 TWAI
* driver is in the stopped state.
*
* @return
* - ESP_OK: TWAI driver is now running
* - ESP_ERR_INVALID_STATE: Driver is not in stopped state, or is not installed
*/
esp_err_t twai_start(void);
/**
* @brief Stop the TWAI driver
*
* This function stops the TWAI driver, preventing any further message from being
* transmitted or received until twai_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 TWAI driver is in the running state.
*
* @warning A message currently being transmitted/received on the TWAI bus will
* be ceased immediately. This may lead to other TWAI nodes interpreting
* the unfinished message as an error.
*
* @return
* - ESP_OK: TWAI driver is now Stopped
* - ESP_ERR_INVALID_STATE: Driver is not in running state, or is not installed
*/
esp_err_t twai_stop(void);
/**
* @brief Transmit a TWAI message
*
* This function queues a TWAI 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 times out. 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 TWAI 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: TWAI driver is not in running state, or is not installed
* - ESP_ERR_NOT_SUPPORTED: Listen Only Mode does not support transmissions
*/
esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait);
/**
* @brief Receive a TWAI 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: TWAI driver is not installed
*/
esp_err_t twai_receive(twai_message_t *message, TickType_t ticks_to_wait);
/**
* @brief Read TWAI driver alerts
*
* This function will read the alerts raised by the TWAI driver. If no alert has
* been issued 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: TWAI driver is not installed
*/
esp_err_t twai_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: TWAI driver is not installed
*/
esp_err_t twai_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 TWAI driver is in
* the bus-off state. Once initiated, the TWAI driver will enter the recovering
* state and wait for 128 occurrences of the bus-free signal on the TWAI 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: TWAI driver is not in the bus-off state, or is not installed
*/
esp_err_t twai_initiate_recovery(void);
/**
* @brief Get current status information of the TWAI driver
*
* @param[out] status_info Status information
*
* @return
* - ESP_OK: Status information retrieved
* - ESP_ERR_INVALID_ARG: Arguments are invalid
* - ESP_ERR_INVALID_STATE: TWAI driver is not installed
*/
esp_err_t twai_get_status_info(twai_status_info_t *status_info);
/**
* @brief Clear the transmit queue
*
* This function will clear the transmit queue of all messages.
*
* @note The transmit queue is automatically cleared when twai_stop() or
* twai_initiate_recovery() is called.
*
* @return
* - ESP_OK: Transmit queue cleared
* - ESP_ERR_INVALID_STATE: TWAI driver is not installed or TX queue is disabled
*/
esp_err_t twai_clear_transmit_queue(void);
/**
* @brief Clear the receive queue
*
* This function will clear the receive queue of all messages.
*
* @note The receive queue is automatically cleared when twai_start() is
* called.
*
* @return
* - ESP_OK: Transmit queue cleared
* - ESP_ERR_INVALID_STATE: TWAI driver is not installed
*/
esp_err_t twai_clear_receive_queue(void);
#ifdef __cplusplus
}
#endif

693
components/driver/twai.c Normal file
View file

@ -0,0 +1,693 @@
// 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 "soc/soc_caps.h"
#include "sdkconfig.h"
#include "freertos/FreeRTOS.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 "esp_pm.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "driver/twai.h"
#include "soc/twai_periph.h"
#include "hal/twai_hal.h"
/* ---------------------------- Definitions --------------------------------- */
//Internal Macros
#define TWAI_CHECK(cond, ret_val) ({ \
if (!(cond)) { \
return (ret_val); \
} \
})
#define TWAI_CHECK_FROM_CRIT(cond, ret_val) ({ \
if (!(cond)) { \
TWAI_EXIT_CRITICAL(); \
return ret_val; \
} \
})
#define TWAI_SET_FLAG(var, mask) ((var) |= (mask))
#define TWAI_RESET_FLAG(var, mask) ((var) &= ~(mask))
#define TWAI_TAG "TWAI"
#define DRIVER_DEFAULT_INTERRUPTS 0xE7 //Exclude data overrun (bit[3]) and brp_div (bit[4])
//Control flags
#define CTRL_FLAG_STOPPED 0x001 //TWAI 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 ALERT_LOG_LEVEL_WARNING TWAI_ALERT_ARB_LOST //Alerts above and including this level use ESP_LOGW
#define ALERT_LOG_LEVEL_ERROR TWAI_ALERT_TX_FAILED //Alerts above and including this level use ESP_LOGE
/* ------------------ Typedefs, structures, and variables ------------------- */
//Control structure for TWAI driver
typedef struct {
//Control and status members
uint32_t control_flags;
twai_mode_t mode;
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;
#ifdef CONFIG_PM_ENABLE
//Power Management
esp_pm_lock_handle_t pm_lock;
#endif
} twai_obj_t;
static twai_obj_t *p_twai_obj = NULL;
static portMUX_TYPE twai_spinlock = portMUX_INITIALIZER_UNLOCKED;
#define TWAI_ENTER_CRITICAL_ISR() portENTER_CRITICAL_ISR(&twai_spinlock)
#define TWAI_EXIT_CRITICAL_ISR() portEXIT_CRITICAL_ISR(&twai_spinlock)
#define TWAI_ENTER_CRITICAL() portENTER_CRITICAL(&twai_spinlock)
#define TWAI_EXIT_CRITICAL() portEXIT_CRITICAL(&twai_spinlock)
static twai_hal_context_t twai_context;
/* -------------------- Interrupt and Alert Handlers ------------------------ */
static void twai_alert_handler(uint32_t alert_code, int *alert_req)
{
if (p_twai_obj->alerts_enabled & alert_code) {
//Signify alert has occurred
TWAI_SET_FLAG(p_twai_obj->alerts_triggered, alert_code);
*alert_req = 1;
if (p_twai_obj->alerts_enabled & TWAI_ALERT_AND_LOG) {
if (alert_code >= ALERT_LOG_LEVEL_ERROR) {
ESP_EARLY_LOGE(TWAI_TAG, "Alert %d", alert_code);
} else if (alert_code >= ALERT_LOG_LEVEL_WARNING) {
ESP_EARLY_LOGW(TWAI_TAG, "Alert %d", alert_code);
} else {
ESP_EARLY_LOGI(TWAI_TAG, "Alert %d", alert_code);
}
}
}
}
static inline void twai_handle_bus_off(int *alert_req)
{
//Bus-Off condition. TEC should set and held at 127, REC should be 0, reset mode entered
TWAI_SET_FLAG(p_twai_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. */
twai_hal_handle_bus_off(&twai_context);
twai_alert_handler(TWAI_ALERT_BUS_OFF, alert_req);
}
static inline void twai_handle_recovery_complete(int *alert_req)
{
//Bus recovery complete.
assert(twai_hal_handle_bus_recov_cplt(&twai_context));
//Reset and set flags to the equivalent of the stopped state
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_RECOVERING | CTRL_FLAG_ERR_WARN |
CTRL_FLAG_ERR_PASSIVE | CTRL_FLAG_BUS_OFF |
CTRL_FLAG_TX_BUFF_OCCUPIED);
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_STOPPED);
twai_alert_handler(TWAI_ALERT_BUS_RECOVERED, alert_req);
}
static inline void twai_handle_recovery_in_progress(int * alert_req)
{
//Bus-recovery in progress. TEC has dropped below error warning limit
twai_alert_handler(TWAI_ALERT_RECOVERY_IN_PROGRESS, alert_req);
}
static inline void twai_handle_above_ewl(int *alert_req)
{
//TEC or REC surpassed error warning limit
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_WARN);
twai_alert_handler(TWAI_ALERT_ABOVE_ERR_WARN, alert_req);
}
static inline void twai_handle_below_ewl(int *alert_req)
{
//TEC and REC are both below error warning
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_WARN);
twai_alert_handler(TWAI_ALERT_BELOW_ERR_WARN, alert_req);
}
static inline void twai_handle_error_passive(int *alert_req)
{
//Entered error passive
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_PASSIVE);
twai_alert_handler(TWAI_ALERT_ERR_PASS, alert_req);
}
static inline void twai_handle_error_active(int *alert_req)
{
//Returned to error active
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_ERR_PASSIVE);
twai_alert_handler(TWAI_ALERT_ERR_ACTIVE, alert_req);
}
static inline void twai_handle_bus_error(int *alert_req)
{
// ECC register is read to re-arm bus error interrupt. ECC is not used
twai_hal_handle_bus_error(&twai_context);
p_twai_obj->bus_error_count++;
twai_alert_handler(TWAI_ALERT_BUS_ERROR, alert_req);
}
static inline void twai_handle_arb_lost(int *alert_req)
{
//ALC register is read to re-arm arb lost interrupt. ALC is not used
twai_hal_handle_arb_lost(&twai_context);
p_twai_obj->arb_lost_count++;
twai_alert_handler(TWAI_ALERT_ARB_LOST, alert_req);
}
static inline void twai_handle_rx_buffer_frames(BaseType_t *task_woken, int *alert_req)
{
uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context);
for (int i = 0; i < msg_count; i++) {
twai_hal_frame_t frame;
twai_hal_read_rx_buffer_and_clear(&twai_context, &frame);
//Copy frame into RX Queue
if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) {
p_twai_obj->rx_msg_count++;
} else {
p_twai_obj->rx_missed_count++;
twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
}
}
//Todo: Add Software Filters
//Todo: Check for data overrun of RX FIFO, then trigger alert
}
static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *alert_req)
{
//Handle previously transmitted frame
if (twai_hal_check_last_tx_successful(&twai_context)) {
twai_alert_handler(TWAI_ALERT_TX_SUCCESS, alert_req);
} else {
p_twai_obj->tx_failed_count++;
twai_alert_handler(TWAI_ALERT_TX_FAILED, alert_req);
}
//Update TX message count
p_twai_obj->tx_msg_count--;
assert(p_twai_obj->tx_msg_count >= 0); //Sanity check
//Check if there are more frames to transmit
if (p_twai_obj->tx_msg_count > 0 && p_twai_obj->tx_queue != NULL) {
twai_hal_frame_t frame;
int res = xQueueReceiveFromISR(p_twai_obj->tx_queue, &frame, task_woken);
if (res == pdTRUE) {
twai_hal_set_tx_buffer_and_transmit(&twai_context, &frame);
} else {
assert(false && "failed to get a frame from TX queue");
}
} else {
//No more frames to transmit
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
twai_alert_handler(TWAI_ALERT_TX_IDLE, alert_req);
}
}
static void twai_intr_handler_main(void *arg)
{
BaseType_t task_woken = pdFALSE;
int alert_req = 0;
uint32_t event;
TWAI_ENTER_CRITICAL_ISR();
if (p_twai_obj == NULL) { //Incase intr occurs whilst driver is being uninstalled
TWAI_EXIT_CRITICAL_ISR();
return;
}
event = twai_hal_decode_interrupt_events(&twai_context, p_twai_obj->control_flags & CTRL_FLAG_RECOVERING);
if (event & TWAI_HAL_EVENT_BUS_OFF) {
twai_handle_bus_off(&alert_req);
}
if (event & TWAI_HAL_EVENT_BUS_RECOV_CPLT) {
twai_handle_recovery_complete(&alert_req);
}
if (event & TWAI_HAL_EVENT_BUS_RECOV_PROGRESS) {
twai_handle_recovery_in_progress(&alert_req);
}
if (event & TWAI_HAL_EVENT_ABOVE_EWL) {
twai_handle_above_ewl(&alert_req);
}
if (event & TWAI_HAL_EVENT_BELOW_EWL) {
twai_handle_below_ewl(&alert_req);
}
if (event & TWAI_HAL_EVENT_ERROR_PASSIVE) {
twai_handle_error_passive(&alert_req);
}
if (event & TWAI_HAL_EVENT_ERROR_ACTIVE) {
twai_handle_error_active(&alert_req);
}
if (event & TWAI_HAL_EVENT_BUS_ERR) {
twai_handle_bus_error(&alert_req);
}
if (event & TWAI_HAL_EVENT_ARB_LOST) {
twai_handle_arb_lost(&alert_req);
}
if (event & TWAI_HAL_EVENT_RX_BUFF_FRAME) {
twai_handle_rx_buffer_frames(&task_woken, &alert_req);
}
//TX command related handlers should be called last, so that other commands
//do not overwrite the TX command related bits in the command register.
if (event & TWAI_HAL_EVENT_TX_BUFF_FREE) {
twai_handle_tx_buffer_frame(&task_woken, &alert_req);
}
TWAI_EXIT_CRITICAL_ISR();
if (p_twai_obj->alert_semphr != NULL && alert_req) {
//Give semaphore if alerts were triggered
xSemaphoreGiveFromISR(p_twai_obj->alert_semphr, &task_woken);
}
if (task_woken == pdTRUE) {
portYIELD_FROM_ISR();
}
}
/* --------------------------- GPIO functions ------------------------------ */
static void twai_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, TWAI_TX_IDX, false, false);
gpio_pad_select_gpio(tx);
//Set RX pin
gpio_set_pull_mode(rx, GPIO_FLOATING);
gpio_matrix_in(rx, TWAI_RX_IDX, false);
gpio_pad_select_gpio(rx);
gpio_set_direction(rx, GPIO_MODE_INPUT);
//Configure output clock pin (Optional)
if (clkout >= 0 && clkout < GPIO_NUM_MAX) {
gpio_set_pull_mode(clkout, GPIO_FLOATING);
gpio_matrix_out(clkout, TWAI_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, TWAI_BUS_OFF_ON_IDX, false, false);
gpio_pad_select_gpio(bus_status);
}
}
/* ---------------------------- Public Functions ---------------------------- */
esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_timing_config_t *t_config, const twai_filter_config_t *f_config)
{
//Check arguments
TWAI_CHECK(g_config != NULL, ESP_ERR_INVALID_ARG);
TWAI_CHECK(t_config != NULL, ESP_ERR_INVALID_ARG);
TWAI_CHECK(f_config != NULL, ESP_ERR_INVALID_ARG);
TWAI_CHECK(g_config->rx_queue_len > 0, ESP_ERR_INVALID_ARG);
TWAI_CHECK(g_config->tx_io >= 0 && g_config->tx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG);
TWAI_CHECK(g_config->rx_io >= 0 && g_config->rx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG);
TWAI_CHECK(TWAI_BRP_IS_VALID(t_config->brp), ESP_ERR_INVALID_ARG);
esp_err_t ret;
twai_obj_t *p_twai_obj_dummy;
//Create a TWAI object
p_twai_obj_dummy = calloc(1, sizeof(twai_obj_t));
TWAI_CHECK(p_twai_obj_dummy != NULL, ESP_ERR_NO_MEM);
//Initialize queues, semaphores, and power management locks
p_twai_obj_dummy->tx_queue = (g_config->tx_queue_len > 0) ? xQueueCreate(g_config->tx_queue_len, sizeof(twai_hal_frame_t)) : NULL;
p_twai_obj_dummy->rx_queue = xQueueCreate(g_config->rx_queue_len, sizeof(twai_hal_frame_t));
p_twai_obj_dummy->alert_semphr = xSemaphoreCreateBinary();
if ((g_config->tx_queue_len > 0 && p_twai_obj_dummy->tx_queue == NULL) ||
p_twai_obj_dummy->rx_queue == NULL || p_twai_obj_dummy->alert_semphr == NULL) {
ret = ESP_ERR_NO_MEM;
goto err;
}
#ifdef CONFIG_PM_ENABLE
esp_err_t pm_err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "twai", &(p_twai_obj_dummy->pm_lock));
if (pm_err != ESP_OK ) {
ret = pm_err;
goto err;
}
#endif
//Initialize flags and variables. All other members are 0 initialized by calloc()
p_twai_obj_dummy->control_flags = CTRL_FLAG_STOPPED;
p_twai_obj_dummy->mode = g_config->mode;
p_twai_obj_dummy->alerts_enabled = g_config->alerts_enabled;
//Initialize TWAI peripheral registers, and allocate interrupt
TWAI_ENTER_CRITICAL();
if (p_twai_obj == NULL) {
p_twai_obj = p_twai_obj_dummy;
} else {
//Check if driver is already installed
TWAI_EXIT_CRITICAL();
ret = ESP_ERR_INVALID_STATE;
goto err;
}
periph_module_reset(PERIPH_TWAI_MODULE);
periph_module_enable(PERIPH_TWAI_MODULE); //Enable APB CLK to TWAI peripheral
assert(twai_hal_init(&twai_context));
twai_hal_configure(&twai_context, t_config, f_config, DRIVER_DEFAULT_INTERRUPTS, g_config->clkout_divider);
//Todo: Allow interrupt to be registered to specified CPU
TWAI_EXIT_CRITICAL();
//Allocate GPIO and Interrupts
twai_configure_gpio(g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io);
ESP_ERROR_CHECK(esp_intr_alloc(ETS_TWAI_INTR_SOURCE, 0, twai_intr_handler_main, NULL, &p_twai_obj->isr_handle));
#ifdef CONFIG_PM_ENABLE
ESP_ERROR_CHECK(esp_pm_lock_acquire(p_twai_obj->pm_lock)); //Acquire pm_lock to keep APB clock at 80MHz
#endif
return ESP_OK; //TWAI module is still in reset mode, users need to call twai_start() afterwards
err:
//Cleanup TWAI object and return error
if (p_twai_obj_dummy != NULL) {
if (p_twai_obj_dummy->tx_queue != NULL) {
vQueueDelete(p_twai_obj_dummy->tx_queue);
p_twai_obj_dummy->tx_queue = NULL;
}
if (p_twai_obj_dummy->rx_queue != NULL) {
vQueueDelete(p_twai_obj_dummy->rx_queue);
p_twai_obj_dummy->rx_queue = NULL;
}
if (p_twai_obj_dummy->alert_semphr != NULL) {
vSemaphoreDelete(p_twai_obj_dummy->alert_semphr);
p_twai_obj_dummy->alert_semphr = NULL;
}
#ifdef CONFIG_PM_ENABLE
if (p_twai_obj_dummy->pm_lock != NULL) {
ESP_ERROR_CHECK(esp_pm_lock_delete(p_twai_obj_dummy->pm_lock));
}
#endif
free(p_twai_obj_dummy);
}
return ret;
}
esp_err_t twai_driver_uninstall(void)
{
twai_obj_t *p_twai_obj_dummy;
TWAI_ENTER_CRITICAL();
//Check state
TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF), ESP_ERR_INVALID_STATE);
//Todo: Add check to see if in reset mode. //Enter reset mode to stop any TWAI bus activity
//Clear registers by reading
twai_hal_deinit(&twai_context);
periph_module_disable(PERIPH_TWAI_MODULE); //Disable TWAI peripheral
p_twai_obj_dummy = p_twai_obj; //Use dummy to shorten critical section
p_twai_obj = NULL;
TWAI_EXIT_CRITICAL();
ESP_ERROR_CHECK(esp_intr_free(p_twai_obj_dummy->isr_handle)); //Free interrupt
//Delete queues, semaphores, and power management locks
if (p_twai_obj_dummy->tx_queue != NULL) {
vQueueDelete(p_twai_obj_dummy->tx_queue);
}
vQueueDelete(p_twai_obj_dummy->rx_queue);
vSemaphoreDelete(p_twai_obj_dummy->alert_semphr);
#ifdef CONFIG_PM_ENABLE
//Release and delete power management lock
ESP_ERROR_CHECK(esp_pm_lock_release(p_twai_obj_dummy->pm_lock));
ESP_ERROR_CHECK(esp_pm_lock_delete(p_twai_obj_dummy->pm_lock));
#endif
free(p_twai_obj_dummy); //Free can driver object
return ESP_OK;
}
esp_err_t twai_start(void)
{
//Check state
TWAI_ENTER_CRITICAL();
TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->control_flags & CTRL_FLAG_STOPPED, ESP_ERR_INVALID_STATE);
//Reset RX queue, and RX message count
xQueueReset(p_twai_obj->rx_queue);
p_twai_obj->rx_msg_count = 0;
//Todo: Add assert to see if in reset mode. //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
assert(twai_hal_start(&twai_context, p_twai_obj->mode));
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_STOPPED);
TWAI_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t twai_stop(void)
{
//Check state
TWAI_ENTER_CRITICAL();
TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(!(p_twai_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)), ESP_ERR_INVALID_STATE);
assert(twai_hal_stop(&twai_context));
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_STOPPED);
//Reset TX Queue and message count
if (p_twai_obj->tx_queue != NULL) {
xQueueReset(p_twai_obj->tx_queue);
}
p_twai_obj->tx_msg_count = 0;
TWAI_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
{
//Check arguments
TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK(message != NULL, ESP_ERR_INVALID_ARG);
TWAI_CHECK((message->data_length_code <= TWAI_FRAME_MAX_DLC) || message->dlc_non_comp, ESP_ERR_INVALID_ARG);
TWAI_ENTER_CRITICAL();
//Check State
TWAI_CHECK_FROM_CRIT(!(p_twai_obj->mode == TWAI_MODE_LISTEN_ONLY), ESP_ERR_NOT_SUPPORTED);
TWAI_CHECK_FROM_CRIT(!(p_twai_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)), ESP_ERR_INVALID_STATE);
//Format frame
esp_err_t ret = ESP_FAIL;
twai_hal_frame_t tx_frame;
twai_hal_format_frame(message, &tx_frame);
//Check if frame can be sent immediately
if ((p_twai_obj->tx_msg_count == 0) && !(p_twai_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED)) {
//No other frames waiting to transmit. Bypass queue and transmit immediately
twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame);
p_twai_obj->tx_msg_count++;
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
ret = ESP_OK;
}
TWAI_EXIT_CRITICAL();
if (ret != ESP_OK) {
if (p_twai_obj->tx_queue == NULL) {
//TX Queue is disabled and TX buffer is occupied, message was not sent
ret = ESP_FAIL;
} else if (xQueueSend(p_twai_obj->tx_queue, &tx_frame, ticks_to_wait) == pdTRUE) {
//Copied to TX Queue
TWAI_ENTER_CRITICAL();
if (p_twai_obj->control_flags & (CTRL_FLAG_STOPPED | CTRL_FLAG_BUS_OFF)) {
//TX queue was reset (due to stop/bus_off), remove copied frame from queue to prevent transmission
int res = xQueueReceive(p_twai_obj->tx_queue, &tx_frame, 0);
assert(res == pdTRUE);
ret = ESP_ERR_INVALID_STATE;
} else if ((p_twai_obj->tx_msg_count == 0) && !(p_twai_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED)) {
//TX buffer was freed during copy, manually trigger transmission
int res = xQueueReceive(p_twai_obj->tx_queue, &tx_frame, 0);
assert(res == pdTRUE);
twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame);
p_twai_obj->tx_msg_count++;
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
ret = ESP_OK;
} else {
//Frame was copied to queue, waiting to be transmitted
p_twai_obj->tx_msg_count++;
ret = ESP_OK;
}
TWAI_EXIT_CRITICAL();
} else {
//Timed out waiting for free space on TX queue
ret = ESP_ERR_TIMEOUT;
}
}
return ret;
}
esp_err_t twai_receive(twai_message_t *message, TickType_t ticks_to_wait)
{
//Check arguments and state
TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK(message != NULL, ESP_ERR_INVALID_ARG);
//Get frame from RX Queue or RX Buffer
twai_hal_frame_t rx_frame;
if (xQueueReceive(p_twai_obj->rx_queue, &rx_frame, ticks_to_wait) != pdTRUE) {
return ESP_ERR_TIMEOUT;
}
TWAI_ENTER_CRITICAL();
p_twai_obj->rx_msg_count--;
TWAI_EXIT_CRITICAL();
//Decode frame
twai_hal_parse_frame(&rx_frame, message);
return ESP_OK;
}
esp_err_t twai_read_alerts(uint32_t *alerts, TickType_t ticks_to_wait)
{
//Check arguments and state
TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK(alerts != NULL, ESP_ERR_INVALID_ARG);
//Wait for an alert to occur
if (xSemaphoreTake(p_twai_obj->alert_semphr, ticks_to_wait) == pdTRUE) {
TWAI_ENTER_CRITICAL();
*alerts = p_twai_obj->alerts_triggered;
p_twai_obj->alerts_triggered = 0; //Clear triggered alerts
TWAI_EXIT_CRITICAL();
return ESP_OK;
} else {
*alerts = 0;
return ESP_ERR_TIMEOUT;
}
}
esp_err_t twai_reconfigure_alerts(uint32_t alerts_enabled, uint32_t *current_alerts)
{
TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_ENTER_CRITICAL();
//Clear any unhandled alerts
if (current_alerts != NULL) {
*current_alerts = p_twai_obj->alerts_triggered;;
}
p_twai_obj->alerts_triggered = 0;
p_twai_obj->alerts_enabled = alerts_enabled; //Update enabled alerts
TWAI_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t twai_initiate_recovery(void)
{
TWAI_ENTER_CRITICAL();
//Check state
TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->control_flags & CTRL_FLAG_BUS_OFF, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(!(p_twai_obj->control_flags & CTRL_FLAG_RECOVERING), ESP_ERR_INVALID_STATE);
//Reset TX Queue/Counters
if (p_twai_obj->tx_queue != NULL) {
xQueueReset(p_twai_obj->tx_queue);
}
p_twai_obj->tx_msg_count = 0;
TWAI_RESET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_TX_BUFF_OCCUPIED);
TWAI_SET_FLAG(p_twai_obj->control_flags, CTRL_FLAG_RECOVERING);
//Trigger start of recovery process
assert(twai_hal_start_bus_recovery(&twai_context));
TWAI_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t twai_get_status_info(twai_status_info_t *status_info)
{
//Check parameters and state
TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK(status_info != NULL, ESP_ERR_INVALID_ARG);
TWAI_ENTER_CRITICAL();
status_info->tx_error_counter = twai_hal_get_tec(&twai_context);
status_info->rx_error_counter = twai_hal_get_rec(&twai_context);
status_info->msgs_to_tx = p_twai_obj->tx_msg_count;
status_info->msgs_to_rx = p_twai_obj->rx_msg_count;
status_info->tx_failed_count = p_twai_obj->tx_failed_count;
status_info->rx_missed_count = p_twai_obj->rx_missed_count;
status_info->arb_lost_count = p_twai_obj->arb_lost_count;
status_info->bus_error_count = p_twai_obj->bus_error_count;
if (p_twai_obj->control_flags & CTRL_FLAG_RECOVERING) {
status_info->state = TWAI_STATE_RECOVERING;
} else if (p_twai_obj->control_flags & CTRL_FLAG_BUS_OFF) {
status_info->state = TWAI_STATE_BUS_OFF;
} else if (p_twai_obj->control_flags & CTRL_FLAG_STOPPED) {
status_info->state = TWAI_STATE_STOPPED;
} else {
status_info->state = TWAI_STATE_RUNNING;
}
TWAI_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t twai_clear_transmit_queue(void)
{
//Check State
TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK(p_twai_obj->tx_queue != NULL, ESP_ERR_NOT_SUPPORTED);
TWAI_ENTER_CRITICAL();
//If a message is currently undergoing transmission, the tx interrupt handler will decrement tx_msg_count
p_twai_obj->tx_msg_count = (p_twai_obj->control_flags & CTRL_FLAG_TX_BUFF_OCCUPIED) ? 1 : 0;
xQueueReset(p_twai_obj->tx_queue);
TWAI_EXIT_CRITICAL();
return ESP_OK;
}
esp_err_t twai_clear_receive_queue(void)
{
//Check State
TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_ENTER_CRITICAL();
p_twai_obj->rx_msg_count = 0;
xQueueReset(p_twai_obj->rx_queue);
TWAI_EXIT_CRITICAL();
return ESP_OK;
}

View file

@ -278,7 +278,7 @@ void esp_perip_clk_init(void)
DPORT_LEDC_CLK_EN |
DPORT_TIMERGROUP1_CLK_EN |
DPORT_PWM0_CLK_EN |
DPORT_CAN_CLK_EN |
DPORT_TWAI_CLK_EN |
DPORT_PWM1_CLK_EN |
DPORT_PWM2_CLK_EN |
DPORT_PWM3_CLK_EN;

View file

@ -29,6 +29,7 @@ PROVIDE ( SDMMC = 0x3ff68000 );
PROVIDE ( EMAC_DMA = 0x3ff69000 );
PROVIDE ( EMAC_EXT = 0x3ff69800 );
PROVIDE ( EMAC_MAC = 0x3ff6A000 );
PROVIDE ( TWAI = 0x3ff6B000 );
PROVIDE ( CAN = 0x3ff6B000 );
PROVIDE ( MCPWM1 = 0x3ff6C000 );
PROVIDE ( I2S1 = 0x3ff6D000 );

View file

@ -255,7 +255,7 @@ void esp_perip_clk_init(void)
DPORT_SPI3_CLK_EN |
DPORT_SPI4_CLK_EN |
DPORT_PWM0_CLK_EN |
DPORT_CAN_CLK_EN |
DPORT_TWAI_CLK_EN |
DPORT_PWM1_CLK_EN |
DPORT_I2S1_CLK_EN |
DPORT_SPI2_DMA_CLK_EN |

View file

@ -24,7 +24,7 @@ PROVIDE ( GPSPI2 = 0x3f424000 );
PROVIDE ( GPSPI3 = 0x3f425000 );
PROVIDE ( SYSCON = 0x3f426000 );
PROVIDE ( I2C1 = 0x3f427000 );
PROVIDE ( CAN = 0x3f42B000 );
PROVIDE ( TWAI = 0x3f42B000 );
PROVIDE ( APB_SARADC = 0x3f440000 );
PROVIDE ( USB0 = 0x60080000 );
PROVIDE ( USB_WRAP = 0x3f439000 );

View file

@ -31,12 +31,12 @@ idf_component_register(SRCS "src/cpu_util.c"
"src/hal/soc_hal.c"
"src/hal/wdt_hal_iram.c"
"src/compare_set.c"
"src/hal/twai_hal.c"
PRIV_REQUIRES ${target}
LDFRAGMENTS linker.lf)
if(CONFIG_IDF_TARGET_ESP32)
target_sources(${COMPONENT_LIB} PRIVATE "src/hal/can_hal.c"
"src/hal/mcpwm_hal.c"
target_sources(${COMPONENT_LIB} PRIVATE "src/hal/mcpwm_hal.c"
"src/hal/sdio_slave_hal.c")
elseif(CONFIG_IDF_TARGET_ESP32S2)
target_sources(${COMPONENT_LIB} PRIVATE "src/hal/spi_flash_hal_gpspi.c")

View file

@ -26,54 +26,54 @@ extern "C" {
#include <stddef.h>
#include <stdbool.h>
#include "hal/can_types.h"
#include "hal/can_ll.h"
#include "hal/twai_types.h"
#include "hal/twai_ll.h"
/* ------------------------- Defines and Typedefs --------------------------- */
//Error active interrupt related
#define CAN_HAL_EVENT_BUS_OFF (1 << 0)
#define CAN_HAL_EVENT_BUS_RECOV_CPLT (1 << 1)
#define CAN_HAL_EVENT_BUS_RECOV_PROGRESS (1 << 2)
#define CAN_HAL_EVENT_ABOVE_EWL (1 << 3)
#define CAN_HAL_EVENT_BELOW_EWL (1 << 4)
#define CAN_HAL_EVENT_ERROR_PASSIVE (1 << 5)
#define CAN_HAL_EVENT_ERROR_ACTIVE (1 << 6)
#define CAN_HAL_EVENT_BUS_ERR (1 << 7)
#define CAN_HAL_EVENT_ARB_LOST (1 << 8)
#define CAN_HAL_EVENT_RX_BUFF_FRAME (1 << 9)
#define CAN_HAL_EVENT_TX_BUFF_FREE (1 << 10)
#define TWAI_HAL_EVENT_BUS_OFF (1 << 0)
#define TWAI_HAL_EVENT_BUS_RECOV_CPLT (1 << 1)
#define TWAI_HAL_EVENT_BUS_RECOV_PROGRESS (1 << 2)
#define TWAI_HAL_EVENT_ABOVE_EWL (1 << 3)
#define TWAI_HAL_EVENT_BELOW_EWL (1 << 4)
#define TWAI_HAL_EVENT_ERROR_PASSIVE (1 << 5)
#define TWAI_HAL_EVENT_ERROR_ACTIVE (1 << 6)
#define TWAI_HAL_EVENT_BUS_ERR (1 << 7)
#define TWAI_HAL_EVENT_ARB_LOST (1 << 8)
#define TWAI_HAL_EVENT_RX_BUFF_FRAME (1 << 9)
#define TWAI_HAL_EVENT_TX_BUFF_FREE (1 << 10)
typedef struct {
can_dev_t *dev;
} can_hal_context_t;
twai_dev_t *dev;
} twai_hal_context_t;
typedef can_ll_frame_buffer_t can_hal_frame_t;
typedef twai_ll_frame_buffer_t twai_hal_frame_t;
/* ---------------------------- Init and Config ----------------------------- */
/**
* @brief Initialize CAN peripheral and HAL context
* @brief Initialize TWAI peripheral and HAL context
*
* Sets HAL context, puts CAN peripheral into reset mode, then sets some
* Sets HAL context, puts TWAI peripheral into reset mode, then sets some
* registers with default values.
*
* @param hal_ctx Context of the HAL layer
* @return True if successfully initialized, false otherwise.
*/
bool can_hal_init(can_hal_context_t *hal_ctx);
bool twai_hal_init(twai_hal_context_t *hal_ctx);
/**
* @brief Deinitialize the CAN peripheral and HAL context
* @brief Deinitialize the TWAI peripheral and HAL context
*
* Clears any unhandled interrupts and unsets HAL context
*
* @param hal_ctx Context of the HAL layer
*/
void can_hal_deinit(can_hal_context_t *hal_ctx);
void twai_hal_deinit(twai_hal_context_t *hal_ctx);
/**
* @brief Configure the CAN peripheral
* @brief Configure the TWAI peripheral
*
* @param hal_ctx Context of the HAL layer
* @param t_config Pointer to timing configuration structure
@ -81,32 +81,32 @@ void can_hal_deinit(can_hal_context_t *hal_ctx);
* @param intr_mask Mask of interrupts to enable
* @param clkout_divider Clock divider value for CLKOUT. Set to -1 to disable CLKOUT
*/
void can_hal_configure(can_hal_context_t *hal_ctx, const can_timing_config_t *t_config, const can_filter_config_t *f_config, uint32_t intr_mask, uint32_t clkout_divider);
void twai_hal_configure(twai_hal_context_t *hal_ctx, const twai_timing_config_t *t_config, const twai_filter_config_t *f_config, uint32_t intr_mask, uint32_t clkout_divider);
/* -------------------------------- Actions --------------------------------- */
/**
* @brief Start the CAN peripheral
* @brief Start the TWAI peripheral
*
* Start the CAN peripheral by configuring its operating mode, then exiting
* reset mode so that the CAN peripheral can participate in bus activities.
* Start the TWAI peripheral by configuring its operating mode, then exiting
* reset mode so that the TWAI peripheral can participate in bus activities.
*
* @param hal_ctx Context of the HAL layer
* @param mode Operating mode
* @return True if successfully started, false otherwise.
*/
bool can_hal_start(can_hal_context_t *hal_ctx, can_mode_t mode);
bool twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode);
/**
* @brief Stop the CAN peripheral
* @brief Stop the TWAI peripheral
*
* Stop the CAN peripheral by entering reset mode to stop any bus activity, then
* Stop the TWAI peripheral by entering reset mode to stop any bus activity, then
* setting the operating mode to Listen Only so that REC is frozen.
*
* @param hal_ctx Context of the HAL layer
* @return True if successfully stopped, false otherwise.
*/
bool can_hal_stop(can_hal_context_t *hal_ctx);
bool twai_hal_stop(twai_hal_context_t *hal_ctx);
/**
* @brief Start bus recovery
@ -114,9 +114,9 @@ bool can_hal_stop(can_hal_context_t *hal_ctx);
* @param hal_ctx Context of the HAL layer
* @return True if successfully started bus recovery, false otherwise.
*/
static inline bool can_hal_start_bus_recovery(can_hal_context_t *hal_ctx)
static inline bool twai_hal_start_bus_recovery(twai_hal_context_t *hal_ctx)
{
return can_ll_exit_reset_mode(hal_ctx->dev);
return twai_ll_exit_reset_mode(hal_ctx->dev);
}
/**
@ -125,9 +125,9 @@ static inline bool can_hal_start_bus_recovery(can_hal_context_t *hal_ctx)
* @param hal_ctx Context of the HAL layer
* @return TX Error Counter Value
*/
static inline uint32_t can_hal_get_tec(can_hal_context_t *hal_ctx)
static inline uint32_t twai_hal_get_tec(twai_hal_context_t *hal_ctx)
{
return can_ll_get_tec((hal_ctx)->dev);
return twai_ll_get_tec((hal_ctx)->dev);
}
/**
@ -136,9 +136,9 @@ static inline uint32_t can_hal_get_tec(can_hal_context_t *hal_ctx)
* @param hal_ctx Context of the HAL layer
* @return RX Error Counter Value
*/
static inline uint32_t can_hal_get_rec(can_hal_context_t *hal_ctx)
static inline uint32_t twai_hal_get_rec(twai_hal_context_t *hal_ctx)
{
return can_ll_get_rec((hal_ctx)->dev);
return twai_ll_get_rec((hal_ctx)->dev);
}
/**
@ -147,9 +147,9 @@ static inline uint32_t can_hal_get_rec(can_hal_context_t *hal_ctx)
* @param hal_ctx Context of the HAL layer
* @return RX message count
*/
static inline uint32_t can_hal_get_rx_msg_count(can_hal_context_t *hal_ctx)
static inline uint32_t twai_hal_get_rx_msg_count(twai_hal_context_t *hal_ctx)
{
return can_ll_get_rx_msg_count((hal_ctx)->dev);
return twai_ll_get_rx_msg_count((hal_ctx)->dev);
}
/**
@ -158,9 +158,9 @@ static inline uint32_t can_hal_get_rx_msg_count(can_hal_context_t *hal_ctx)
* @param hal_ctx Context of the HAL layer
* @return True if successful
*/
static inline bool can_hal_check_last_tx_successful(can_hal_context_t *hal_ctx)
static inline bool twai_hal_check_last_tx_successful(twai_hal_context_t *hal_ctx)
{
return can_ll_is_last_tx_successful((hal_ctx)->dev);
return twai_ll_is_last_tx_successful((hal_ctx)->dev);
}
/* ----------------------------- Event Handling ----------------------------- */
@ -168,15 +168,15 @@ static inline bool can_hal_check_last_tx_successful(can_hal_context_t *hal_ctx)
/**
* @brief Decode current events that triggered an interrupt
*
* This function should be called on every CAN interrupt. It will read (and
* This function should be called on every TWAI interrupt. It will read (and
* thereby clear) the interrupt register, then determine what events have
* occurred to trigger the interrupt.
*
* @param hal_ctx Context of the HAL layer
* @param bus_recovering Whether the CAN peripheral was previous undergoing bus recovery
* @param bus_recovering Whether the TWAI peripheral was previous undergoing bus recovery
* @return Bit mask of events that have occurred
*/
uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx, bool bus_recovering);
uint32_t twai_hal_decode_interrupt_events(twai_hal_context_t *hal_ctx, bool bus_recovering);
/**
* @brief Handle bus recovery complete
@ -187,9 +187,9 @@ uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx, bool bus_re
* @param hal_ctx Context of the HAL layer
* @return True if successfully handled bus recovery completion, false otherwise.
*/
static inline bool can_hal_handle_bus_recov_cplt(can_hal_context_t *hal_ctx)
static inline bool twai_hal_handle_bus_recov_cplt(twai_hal_context_t *hal_ctx)
{
return can_ll_enter_reset_mode((hal_ctx)->dev);
return twai_ll_enter_reset_mode((hal_ctx)->dev);
}
/**
@ -200,9 +200,9 @@ static inline bool can_hal_handle_bus_recov_cplt(can_hal_context_t *hal_ctx)
*
* @param hal_ctx Context of the HAL layer
*/
static inline void can_hal_handle_arb_lost(can_hal_context_t *hal_ctx)
static inline void twai_hal_handle_arb_lost(twai_hal_context_t *hal_ctx)
{
can_ll_clear_arb_lost_cap((hal_ctx)->dev);
twai_ll_clear_arb_lost_cap((hal_ctx)->dev);
}
/**
@ -213,9 +213,9 @@ static inline void can_hal_handle_arb_lost(can_hal_context_t *hal_ctx)
*
* @param hal_ctx Context of the HAL layer
*/
static inline void can_hal_handle_bus_error(can_hal_context_t *hal_ctx)
static inline void twai_hal_handle_bus_error(twai_hal_context_t *hal_ctx)
{
can_ll_clear_err_code_cap((hal_ctx)->dev);
twai_ll_clear_err_code_cap((hal_ctx)->dev);
}
/**
@ -226,42 +226,42 @@ static inline void can_hal_handle_bus_error(can_hal_context_t *hal_ctx)
*
* @param hal_ctx Context of the HAL layer
*/
static inline void can_hal_handle_bus_off(can_hal_context_t *hal_ctx)
static inline void twai_hal_handle_bus_off(twai_hal_context_t *hal_ctx)
{
can_ll_set_mode((hal_ctx)->dev, CAN_MODE_LISTEN_ONLY);
twai_ll_set_mode((hal_ctx)->dev, TWAI_MODE_LISTEN_ONLY);
}
/* ------------------------------- TX and RX -------------------------------- */
/**
* @brief Format a CAN Frame
* @brief Format a TWAI Frame
*
* This function takes a CAN message structure (containing ID, DLC, data, and
* This function takes a TWAI message structure (containing ID, DLC, data, and
* flags) and formats it to match the layout of the TX frame buffer.
*
* @param message Pointer to CAN message
* @param message Pointer to TWAI message
* @param frame Pointer to empty frame structure
*/
static inline void can_hal_format_frame(const can_message_t *message, can_hal_frame_t *frame)
static inline void twai_hal_format_frame(const twai_message_t *message, twai_hal_frame_t *frame)
{
//Direct call to ll function
can_ll_format_frame_buffer(message->identifier, message->data_length_code, message->data,
twai_ll_format_frame_buffer(message->identifier, message->data_length_code, message->data,
message->flags, frame);
}
/**
* @brief Parse a CAN Frame
* @brief Parse a TWAI Frame
*
* This function takes a CAN frame (in the format of the RX frame buffer) and
* parses it to a CAN message (containing ID, DLC, data and flags).
* This function takes a TWAI frame (in the format of the RX frame buffer) and
* parses it to a TWAI message (containing ID, DLC, data and flags).
*
* @param frame Pointer to frame structure
* @param message Pointer to empty message structure
*/
static inline void can_hal_parse_frame(can_hal_frame_t *frame, can_message_t *message)
static inline void twai_hal_parse_frame(twai_hal_frame_t *frame, twai_message_t *message)
{
//Direct call to ll function
can_ll_prase_frame_buffer(frame, &message->identifier, &message->data_length_code,
twai_ll_prase_frame_buffer(frame, &message->identifier, &message->data_length_code,
message->data, &message->flags);
}
@ -275,7 +275,7 @@ static inline void can_hal_parse_frame(can_hal_frame_t *frame, can_message_t *me
* @param hal_ctx Context of the HAL layer
* @param tx_frame Pointer to structure containing formatted TX frame
*/
void can_hal_set_tx_buffer_and_transmit(can_hal_context_t *hal_ctx, can_hal_frame_t *tx_frame);
void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_frame_t *tx_frame);
/**
* @brief Copy a frame from the RX buffer and release
@ -286,10 +286,10 @@ void can_hal_set_tx_buffer_and_transmit(can_hal_context_t *hal_ctx, can_hal_fram
* @param hal_ctx Context of the HAL layer
* @param rx_frame Pointer to structure to store RX frame
*/
static inline void can_hal_read_rx_buffer_and_clear(can_hal_context_t *hal_ctx, can_hal_frame_t *rx_frame)
static inline void twai_hal_read_rx_buffer_and_clear(twai_hal_context_t *hal_ctx, twai_hal_frame_t *rx_frame)
{
can_ll_get_rx_buffer(hal_ctx->dev, rx_frame);
can_ll_set_cmd_release_rx_buffer(hal_ctx->dev);
twai_ll_get_rx_buffer(hal_ctx->dev, rx_frame);
twai_ll_set_cmd_release_rx_buffer(hal_ctx->dev);
/*
* Todo: Support overrun handling by:
* - Check overrun status bit. Return false if overrun

View file

@ -22,70 +22,75 @@ extern "C" {
#include <stdbool.h>
/**
* @brief CAN2.0B Constants
* @brief TWAI Constants
*/
#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_FRAME_MAX_DLC 8 /**< Max data bytes allowed in CAN2.0 */
#define CAN_FRAME_EXTD_ID_LEN_BYTES 4 /**< EFF ID requires 4 bytes (29bit) */
#define CAN_FRAME_STD_ID_LEN_BYTES 2 /**< SFF ID requires 2 bytes (11bit) */
#define CAN_ERR_PASS_THRESH 128 /**< Error counter threshold for error passive */
#define TWAI_EXTD_ID_MASK 0x1FFFFFFF /**< Bit mask for 29 bit Extended Frame Format ID */
#define TWAI_STD_ID_MASK 0x7FF /**< Bit mask for 11 bit Standard Frame Format ID */
#define TWAI_FRAME_MAX_DLC 8 /**< Max data bytes allowed in TWAI */
#define TWAI_FRAME_EXTD_ID_LEN_BYTES 4 /**< EFF ID requires 4 bytes (29bit) */
#define TWAI_FRAME_STD_ID_LEN_BYTES 2 /**< SFF ID requires 2 bytes (11bit) */
#define TWAI_ERR_PASS_THRESH 128 /**< Error counter threshold for error passive */
/** @cond */ //Doxy command to hide preprocessor definitions from docs
/**
* @brief CAN Message flags
* @brief TWAI 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. Unused for received. */
#define CAN_MSG_FLAG_SELF 0x08 /**< Transmit as a Self Reception Request. Unused for received. */
#define CAN_MSG_FLAG_DLC_NON_COMP 0x10 /**< Message's Data length code is larger than 8. This will break compliance with CAN2.0B */
#define TWAI_MSG_FLAG_NONE 0x00 /**< No message flags (Standard Frame Format) */
#define TWAI_MSG_FLAG_EXTD 0x01 /**< Extended Frame Format (29bit ID) */
#define TWAI_MSG_FLAG_RTR 0x02 /**< Message is a Remote Frame */
#define TWAI_MSG_FLAG_SS 0x04 /**< Transmit as a Single Shot Transmission. Unused for received. */
#define TWAI_MSG_FLAG_SELF 0x08 /**< Transmit as a Self Reception Request. Unused for received. */
#define TWAI_MSG_FLAG_DLC_NON_COMP 0x10 /**< Message's Data length code is larger than 8. This will break compliance with TWAI */
/**
* @brief Initializer macros for timing configuration structure
*
* The following initializer macros offer commonly found bit rates.
* The following initializer macros offer commonly found bit rates. These macros
* place the sample point at 80% or 67% of a bit time.
*
* @note These timing values are based on the assumption APB clock is at 80MHz
* @note The 20K, 16K and 12.5K bit rates are only available from ESP32 Revision 2 onwards
* @note The available bit rates are dependent on the chip target and revision.
*/
#ifdef CAN_BRP_DIV_SUPPORTED
#define CAN_TIMING_CONFIG_12_5KBITS() {.brp = 256, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
#define CAN_TIMING_CONFIG_16KBITS() {.brp = 200, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
#define CAN_TIMING_CONFIG_20KBITS() {.brp = 200, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#if (TWAI_BRP_MAX > 256)
#define TWAI_TIMING_CONFIG_1KBITS() {.brp = 4000, .tseg_1 = 15, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
#define TWAI_TIMING_CONFIG_5KBITS() {.brp = 800, .tseg_1 = 15, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
#define TWAI_TIMING_CONFIG_10KBITS() {.brp = 400, .tseg_1 = 15, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
#endif
#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}
#if (TWAI_BRP_MAX > 128)
#define TWAI_TIMING_CONFIG_12_5KBITS() {.brp = 256, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
#define TWAI_TIMING_CONFIG_16KBITS() {.brp = 200, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
#define TWAI_TIMING_CONFIG_20KBITS() {.brp = 200, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#endif
#define TWAI_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
#define TWAI_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#define TWAI_TIMING_CONFIG_100KBITS() {.brp = 40, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#define TWAI_TIMING_CONFIG_125KBITS() {.brp = 32, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#define TWAI_TIMING_CONFIG_250KBITS() {.brp = 16, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#define TWAI_TIMING_CONFIG_500KBITS() {.brp = 8, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false}
#define TWAI_TIMING_CONFIG_800KBITS() {.brp = 4, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false}
#define TWAI_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}
#define TWAI_FILTER_CONFIG_ACCEPT_ALL() {.acceptance_code = 0, .acceptance_mask = 0xFFFFFFFF, .single_filter = true}
/** @endcond */
/**
* @brief CAN Controller operating modes
* @brief TWAI Controller 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;
TWAI_MODE_NORMAL, /**< Normal operating mode where TWAI controller can send/receive/acknowledge messages */
TWAI_MODE_NO_ACK, /**< Transmission does not require acknowledgment. Use this mode for self testing */
TWAI_MODE_LISTEN_ONLY, /**< The TWAI controller will not influence the bus (No transmissions or acknowledgments) but can receive messages */
} twai_mode_t;
/**
* @brief Structure to store a CAN message
* @brief Structure to store a TWAI message
*
* @note
* @note The flags member is deprecated
*/
typedef struct {
@ -93,36 +98,36 @@ typedef struct {
struct {
//The order of these bits must match deprecated message flags for compatibility reasons
uint32_t extd: 1; /**< Extended Frame Format (29bit ID) */
uint32_t rtr: 1; /**< Message is a Remote Transmit Request */
uint32_t rtr: 1; /**< Message is a Remote Frame */
uint32_t ss: 1; /**< Transmit as a Single Shot Transmission. Unused for received. */
uint32_t self: 1; /**< Transmit as a Self Reception Request. Unused for received. */
uint32_t dlc_non_comp: 1; /**< Message's Data length code is larger than 8. This will break compliance with CAN2.0B. */
uint32_t dlc_non_comp: 1; /**< Message's Data length code is larger than 8. This will break compliance with ISO 11898-1 */
uint32_t reserved: 27; /**< Reserved bits */
};
//Todo: Deprecate flags
uint32_t flags; /**< Alternate way to set message flags using message flag macros (see documentation) */
uint32_t flags; /**< Deprecated: Alternate way to set bits using message flags */
};
uint32_t identifier; /**< 11 or 29 bit identifier */
uint8_t data_length_code; /**< Data length code */
uint8_t data[CAN_FRAME_MAX_DLC]; /**< Data bytes (not relevant in RTR frame) */
} can_message_t;
uint8_t data[TWAI_FRAME_MAX_DLC]; /**< Data bytes (not relevant in RTR frame) */
} twai_message_t;
/**
* @brief Structure for bit timing configuration of the CAN driver
* @brief Structure for bit timing configuration of the TWAI driver
*
* @note Macro initializers are available for this structure
*/
typedef struct {
uint32_t brp; /**< Baudrate prescaler (i.e., APB clock divider) can be any even number from 2 to 128.
uint32_t brp; /**< Baudrate prescaler (i.e., APB clock divider). Any even number from 2 to 128 for ESP32, 2 to 32768 for ESP32S2.
For ESP32 Rev 2 or later, multiples of 4 from 132 to 256 are also supported */
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;
bool triple_sampling; /**< Enables triple sampling when the TWAI controller samples a bit */
} twai_timing_config_t;
/**
* @brief Structure for acceptance filter configuration of the CAN driver (see documentation)
* @brief Structure for acceptance filter configuration of the TWAI driver (see documentation)
*
* @note Macro initializers are available for this structure
*/
@ -130,7 +135,7 @@ 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;
} twai_filter_config_t;
#ifdef __cplusplus
}

View file

@ -18,21 +18,23 @@
extern "C" {
#endif
#warning soc/can_caps.h is deprecated, please use soc/twai_caps.h instead
#include "sdkconfig.h"
#if __DOXYGEN__ || (CONFIG_ESP32_REV_MIN >= 2)
/* ---------------------------- Compatibility ------------------------------- */
#if (CONFIG_ESP32_REV_MIN >= 2)
#define CAN_BRP_DIV_SUPPORTED 1
#define CAN_BRP_DIV_THRESH 128
//Any even number from 2 to 128, or multiples of 4 from 132 to 256
#define CAN_BRP_IS_VALID(brp) (((brp) >= 2 && (brp) <= 128 && ((brp) & 0x1) == 0) || ((brp) >= 132 && (brp) <= 256 && ((brp) & 0x3) == 0))
#define CAN_BRP_MAX 256
#else
//Any even number from 2 to 128
#define CAN_BRP_IS_VALID(brp) ((brp) >= 2 && (brp) <= 128 && ((brp) & 0x1) == 0)
#define CAN_BRP_MAX 128
#endif
//Todo: Add FIFO overrun errata workaround
//Todo: Add ECC decode capabilities
//Todo: Add ALC decode capability
#define CAN_SUPPORT_MULTI_ADDRESS_LAYOUT 1
#ifdef __cplusplus
}

View file

@ -20,6 +20,8 @@
extern "C" {
#endif
#warning soc/can_periph.h is deprecated, please use soc/twai_periph.h instead
#if CONFIG_IDF_TARGET_ESP32
#include "soc/can_struct.h"
#include "soc/can_caps.h"

View file

@ -18,191 +18,11 @@
extern "C" {
#endif
#include <stdint.h>
#warning soc/can_struct.h is deprecated, please use soc/twai_struct.h instead
/* ---------------------------- Register Layout ------------------------------ */
/* 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 volatile struct can_dev_s {
//Configuration and Control Registers
union {
struct {
uint32_t rm: 1; /* MOD.0 Reset Mode */
uint32_t lom: 1; /* MOD.1 Listen Only Mode */
uint32_t stm: 1; /* MOD.2 Self Test Mode */
uint32_t afm: 1; /* MOD.3 Acceptance Filter Mode */
uint32_t reserved28: 28; /* Internal Reserved. MOD.4 Sleep Mode not supported */
};
uint32_t val;
} mode_reg; /* Address 0 */
union {
struct {
uint32_t tr: 1; /* CMR.0 Transmission Request */
uint32_t at: 1; /* CMR.1 Abort Transmission */
uint32_t rrb: 1; /* CMR.2 Release Receive Buffer */
uint32_t cdo: 1; /* CMR.3 Clear Data Overrun */
uint32_t srr: 1; /* CMR.4 Self Reception Request */
uint32_t reserved27: 27; /* Internal Reserved */
};
uint32_t val;
} command_reg; /* Address 1 */
union {
struct {
uint32_t rbs: 1; /* SR.0 Receive Buffer Status */
uint32_t dos: 1; /* SR.1 Data Overrun Status */
uint32_t tbs: 1; /* SR.2 Transmit Buffer Status */
uint32_t tcs: 1; /* SR.3 Transmission Complete Status */
uint32_t rs: 1; /* SR.4 Receive Status */
uint32_t ts: 1; /* SR.5 Transmit Status */
uint32_t es: 1; /* SR.6 Error Status */
uint32_t bs: 1; /* SR.7 Bus Status */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} status_reg; /* Address 2 */
union {
struct {
uint32_t ri: 1; /* IR.0 Receive Interrupt */
uint32_t ti: 1; /* IR.1 Transmit Interrupt */
uint32_t ei: 1; /* IR.2 Error Interrupt */
uint32_t reserved2: 2; /* Internal Reserved (Data Overrun interrupt and Wake-up not supported) */
uint32_t epi: 1; /* IR.5 Error Passive Interrupt */
uint32_t ali: 1; /* IR.6 Arbitration Lost Interrupt */
uint32_t bei: 1; /* IR.7 Bus Error Interrupt */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} interrupt_reg; /* Address 3 */
union {
struct {
uint32_t rie: 1; /* IER.0 Receive Interrupt Enable */
uint32_t tie: 1; /* IER.1 Transmit Interrupt Enable */
uint32_t eie: 1; /* IER.2 Error Interrupt Enable */
uint32_t doie: 1; /* IER.3 Data Overrun Interrupt Enable */
uint32_t brp_div: 1; /* THIS IS NOT AN INTERRUPT. brp_div will prescale BRP by 2. Only available on ESP32 Revision 2 or later. Reserved otherwise */
uint32_t epie: 1; /* IER.5 Error Passive Interrupt Enable */
uint32_t alie: 1; /* IER.6 Arbitration Lost Interrupt Enable */
uint32_t beie: 1; /* IER.7 Bus Error Interrupt Enable */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} interrupt_enable_reg; /* Address 4 */
uint32_t reserved_05; /* Address 5 */
union {
struct {
uint32_t brp: 6; /* BTR0[5:0] Baud Rate Prescaler */
uint32_t sjw: 2; /* BTR0[7:6] Synchronization Jump Width*/
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} bus_timing_0_reg; /* Address 6 */
union {
struct {
uint32_t tseg1: 4; /* BTR1[3:0] Timing Segment 1 */
uint32_t tseg2: 3; /* BTR1[6:4] Timing Segment 2 */
uint32_t sam: 1; /* BTR1.7 Sampling*/
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} 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
union {
struct {
uint32_t alc: 5; /* ALC[4:0] Arbitration lost capture */
uint32_t reserved27: 27; /* Internal Reserved */
};
uint32_t val;
} arbitration_lost_captue_reg; /* Address 11 */
union {
struct {
uint32_t seg: 5; /* ECC[4:0] Error Code Segment 0 to 5 */
uint32_t dir: 1; /* ECC.5 Error Direction (TX/RX) */
uint32_t errc: 2; /* ECC[7:6] Error Code */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} error_code_capture_reg; /* Address 12 */
union {
struct {
uint32_t ewl: 8; /* EWL[7:0] Error Warning Limit */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} error_warning_limit_reg; /* EWLR[7:0] Error Warning Limit: Address 13 */
union {
struct {
uint32_t rxerr: 8; /* RXERR[7:0] Receive Error Counter */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} rx_error_counter_reg; /* Address 12 */
union {
struct {
uint32_t txerr: 8; /* TXERR[7:0] Receive Error Counter */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} tx_error_counter_reg; /* Address 15 */
//Shared Registers (TX Buff/RX Buff/Acc Filter)
union {
struct {
union {
struct {
uint32_t byte: 8; /* ACRx[7:0] Acceptance Code */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} acr[4];
union {
struct {
uint32_t byte: 8; /* AMRx[7:0] Acceptance Mask */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} amr[4];
uint32_t reserved32[5];
} acceptance_filter;
union {
struct {
uint32_t byte: 8;
uint32_t reserved24: 24;
};
uint32_t val;
} tx_rx_buffer[13];
}; /* Address 16-28 TX/RX Buffer and Acc Filter*/;
//Misc Registers
union {
struct {
uint32_t rmc: 5; /* RMC[4:0] RX Message Counter */
uint32_t reserved27: 27; /* Internal Reserved */
};
uint32_t val;
} rx_message_counter_reg; /* Address 29 */
uint32_t reserved_30; /* Address 30 (RX Buffer Start Address not supported) */
union {
struct {
uint32_t cd: 3; /* CDR[2:0] CLKOUT frequency selector based of fOSC */
uint32_t co: 1; /* CDR.3 CLKOUT enable/disable */
uint32_t reserved3: 3; /* Internal Reserved. RXINTEN and CBP not supported */
uint32_t cm: 1; /* CDR.7 BasicCAN:0 PeliCAN:1 */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} clock_divider_reg; /* Address 31 */
} can_dev_t;
_Static_assert(sizeof(can_dev_t) == 128, "CAN registers should be 32 * 4 bytes");
#include "soc/twai_struct.h"
typedef twai_dev_t can_dev_t;
extern can_dev_t CAN;
#ifdef __cplusplus

View file

@ -958,7 +958,8 @@
#define DPORT_SPI_DMA_CLK_EN (BIT(22))
#define DPORT_I2S1_CLK_EN (BIT(21))
#define DPORT_PWM1_CLK_EN (BIT(20))
#define DPORT_CAN_CLK_EN (BIT(19))
#define DPORT_TWAI_CLK_EN (BIT(19))
#define DPORT_CAN_CLK_EN DPORT_TWAI_CLK_EN
#define DPORT_I2C_EXT1_CLK_EN (BIT(18))
#define DPORT_PWM0_CLK_EN (BIT(17))
#define DPORT_SPI3_CLK_EN (BIT(16))
@ -992,7 +993,8 @@
#define DPORT_SPI_DMA_RST (BIT(22))
#define DPORT_I2S1_RST (BIT(21))
#define DPORT_PWM1_RST (BIT(20))
#define DPORT_CAN_RST (BIT(19))
#define DPORT_TWAI_RST (BIT(19))
#define DPORT_CAN_RST DPORT_TWAI_RST
#define DPORT_I2C_EXT1_RST (BIT(18))
#define DPORT_PWM0_RST (BIT(17))
#define DPORT_SPI3_RST (BIT(16))

View file

@ -194,7 +194,8 @@
#define RMT_SIG_OUT5_IDX 92
#define EXT_ADC_START_IDX 93
#define RMT_SIG_OUT6_IDX 93
#define CAN_RX_IDX 94
#define TWAI_RX_IDX 94
#define CAN_RX_IDX TWAI_RX_IDX
#define RMT_SIG_OUT7_IDX 94
#define I2CEXT1_SCL_IN_IDX 95
#define I2CEXT1_SCL_OUT_IDX 95
@ -252,10 +253,13 @@
#define PWM2_OUT4L_IDX 121
#define PWM3_CAP1_IN_IDX 122
#define PWM3_CAP2_IN_IDX 123
#define CAN_TX_IDX 123
#define TWAI_TX_IDX 123
#define CAN_TX_IDX TWAI_TX_IDX
#define PWM3_CAP3_IN_IDX 124
#define CAN_BUS_OFF_ON_IDX 124
#define CAN_CLKOUT_IDX 125
#define TWAI_BUS_OFF_ON_IDX 124
#define CAN_BUS_OFF_ON_IDX TWAI_BUS_OFF_ON_IDX
#define TWAI_CLKOUT_IDX 125
#define CAN_CLKOUT_IDX TWAI_CLKOUT_IDX
#define SPID4_IN_IDX 128
#define SPID4_OUT_IDX 128
#define SPID5_IN_IDX 129

View file

@ -44,7 +44,8 @@ typedef enum {
PERIPH_SPI_DMA_MODULE,
PERIPH_SDMMC_MODULE,
PERIPH_SDIO_SLAVE_MODULE,
PERIPH_CAN_MODULE,
PERIPH_TWAI_MODULE,
PERIPH_CAN_MODULE = PERIPH_TWAI_MODULE,
PERIPH_EMAC_MODULE,
PERIPH_RNG_MODULE,
PERIPH_WIFI_MODULE,

View file

@ -327,7 +327,8 @@
#define ETS_PWM3_INTR_SOURCE 42/**< interruot of PWM3, level*/
#define ETS_LEDC_INTR_SOURCE 43/**< interrupt of LED PWM, level*/
#define ETS_EFUSE_INTR_SOURCE 44/**< interrupt of efuse, level, not likely to use*/
#define ETS_CAN_INTR_SOURCE 45/**< interrupt of can, level*/
#define ETS_TWAI_INTR_SOURCE 45/**< interrupt of twai, level*/
#define ETS_CAN_INTR_SOURCE ETS_TWAI_INTR_SOURCE
#define ETS_RTC_CORE_INTR_SOURCE 46/**< interrupt of rtc core, level, include rtc watchdog*/
#define ETS_RMT_INTR_SOURCE 47/**< interrupt of remote controller, level*/
#define ETS_PCNT_INTR_SOURCE 48/**< interrupt of pluse count, level*/

View file

@ -9,7 +9,8 @@
#define SOC_SDMMC_HOST_SUPPORTED 1
#define SOC_BT_SUPPORTED 1
#define SOC_SDIO_SLAVE_SUPPORTED 1
#define SOC_CAN_SUPPORTED 1
#define SOC_TWAI_SUPPORTED 1
#define SOC_CAN_SUPPORTED SOC_TWAI_SUPPORTED
#define SOC_EMAC_SUPPORTED 1
#define SOC_CPU_CORES_NUM 2

View file

@ -0,0 +1,41 @@
// Copyright 2019 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "sdkconfig.h"
//Chip specific TWAI related macros
#if __DOXYGEN__ || (CONFIG_ESP32_REV_MIN >= 2)
#define TWAI_BRP_DIV_SUPPORTED 1
#define TWAI_BRP_DIV_THRESH 128
//Any even number from 2 to 128, or multiples of 4 from 132 to 256
#define TWAI_BRP_IS_VALID(brp) (((brp) >= 2 && (brp) <= 128 && ((brp) & 0x1) == 0) || ((brp) >= 132 && (brp) <= 256 && ((brp) & 0x3) == 0))
#define TWAI_BRP_MAX 256
#else
//Any even number from 2 to 128
#define TWAI_BRP_IS_VALID(brp) ((brp) >= 2 && (brp) <= 128 && ((brp) & 0x1) == 0)
#define TWAI_BRP_MAX 128
#endif
//Chip specific TWAI capabilities
#define TWAI_SUPPORT_MULTI_ADDRESS_LAYOUT 1
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,210 @@
// 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* ---------------------------- Register Layout ------------------------------ */
/* The TWAI peripheral's registers are 8bits, however the ESP32 can only access
* peripheral registers every 32bits. Therefore each TWAI register is mapped to
* the least significant byte of every 32bits.
*/
typedef volatile struct twai_dev_s {
//Configuration and Control Registers
union {
struct {
uint32_t rm: 1; /* MOD.0 Reset Mode */
uint32_t lom: 1; /* MOD.1 Listen Only Mode */
uint32_t stm: 1; /* MOD.2 Self Test Mode */
uint32_t afm: 1; /* MOD.3 Acceptance Filter Mode */
uint32_t reserved28: 28; /* Internal Reserved. MOD.4 Sleep Mode not supported */
};
uint32_t val;
} mode_reg; /* Address 0 */
union {
struct {
uint32_t tr: 1; /* CMR.0 Transmission Request */
uint32_t at: 1; /* CMR.1 Abort Transmission */
uint32_t rrb: 1; /* CMR.2 Release Receive Buffer */
uint32_t cdo: 1; /* CMR.3 Clear Data Overrun */
uint32_t srr: 1; /* CMR.4 Self Reception Request */
uint32_t reserved27: 27; /* Internal Reserved */
};
uint32_t val;
} command_reg; /* Address 1 */
union {
struct {
uint32_t rbs: 1; /* SR.0 Receive Buffer Status */
uint32_t dos: 1; /* SR.1 Data Overrun Status */
uint32_t tbs: 1; /* SR.2 Transmit Buffer Status */
uint32_t tcs: 1; /* SR.3 Transmission Complete Status */
uint32_t rs: 1; /* SR.4 Receive Status */
uint32_t ts: 1; /* SR.5 Transmit Status */
uint32_t es: 1; /* SR.6 Error Status */
uint32_t bs: 1; /* SR.7 Bus Status */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} status_reg; /* Address 2 */
union {
struct {
uint32_t ri: 1; /* IR.0 Receive Interrupt */
uint32_t ti: 1; /* IR.1 Transmit Interrupt */
uint32_t ei: 1; /* IR.2 Error Interrupt */
uint32_t reserved2: 2; /* Internal Reserved (Data Overrun interrupt and Wake-up not supported) */
uint32_t epi: 1; /* IR.5 Error Passive Interrupt */
uint32_t ali: 1; /* IR.6 Arbitration Lost Interrupt */
uint32_t bei: 1; /* IR.7 Bus Error Interrupt */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} interrupt_reg; /* Address 3 */
union {
struct {
uint32_t rie: 1; /* IER.0 Receive Interrupt Enable */
uint32_t tie: 1; /* IER.1 Transmit Interrupt Enable */
uint32_t eie: 1; /* IER.2 Error Interrupt Enable */
uint32_t doie: 1; /* IER.3 Data Overrun Interrupt Enable */
uint32_t brp_div: 1; /* THIS IS NOT AN INTERRUPT. brp_div will prescale BRP by 2. Only available on ESP32 Revision 2 or later. Reserved otherwise */
uint32_t epie: 1; /* IER.5 Error Passive Interrupt Enable */
uint32_t alie: 1; /* IER.6 Arbitration Lost Interrupt Enable */
uint32_t beie: 1; /* IER.7 Bus Error Interrupt Enable */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} interrupt_enable_reg; /* Address 4 */
uint32_t reserved_05; /* Address 5 */
union {
struct {
uint32_t brp: 6; /* BTR0[5:0] Baud Rate Prescaler */
uint32_t sjw: 2; /* BTR0[7:6] Synchronization Jump Width*/
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} bus_timing_0_reg; /* Address 6 */
union {
struct {
uint32_t tseg1: 4; /* BTR1[3:0] Timing Segment 1 */
uint32_t tseg2: 3; /* BTR1[6:4] Timing Segment 2 */
uint32_t sam: 1; /* BTR1.7 Sampling*/
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} 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
union {
struct {
uint32_t alc: 5; /* ALC[4:0] Arbitration lost capture */
uint32_t reserved27: 27; /* Internal Reserved */
};
uint32_t val;
} arbitration_lost_captue_reg; /* Address 11 */
union {
struct {
uint32_t seg: 5; /* ECC[4:0] Error Code Segment 0 to 5 */
uint32_t dir: 1; /* ECC.5 Error Direction (TX/RX) */
uint32_t errc: 2; /* ECC[7:6] Error Code */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} error_code_capture_reg; /* Address 12 */
union {
struct {
uint32_t ewl: 8; /* EWL[7:0] Error Warning Limit */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} error_warning_limit_reg; /* EWLR[7:0] Error Warning Limit: Address 13 */
union {
struct {
uint32_t rxerr: 8; /* RXERR[7:0] Receive Error Counter */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} rx_error_counter_reg; /* Address 12 */
union {
struct {
uint32_t txerr: 8; /* TXERR[7:0] Receive Error Counter */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} tx_error_counter_reg; /* Address 15 */
//Shared Registers (TX Buff/RX Buff/Acc Filter)
union {
struct {
union {
struct {
uint32_t byte: 8; /* ACRx[7:0] Acceptance Code */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} acr[4];
union {
struct {
uint32_t byte: 8; /* AMRx[7:0] Acceptance Mask */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} amr[4];
uint32_t reserved32[5];
} acceptance_filter;
union {
struct {
uint32_t byte: 8;
uint32_t reserved24: 24;
};
uint32_t val;
} tx_rx_buffer[13];
}; /* Address 16-28 TX/RX Buffer and Acc Filter*/;
//Misc Registers
union {
struct {
uint32_t rmc: 5; /* RMC[4:0] RX Message Counter */
uint32_t reserved27: 27; /* Internal Reserved */
};
uint32_t val;
} rx_message_counter_reg; /* Address 29 */
uint32_t reserved_30; /* Address 30 (RX Buffer Start Address not supported) */
union {
struct {
uint32_t cd: 3; /* CDR[2:0] CLKOUT frequency selector based of fOSC */
uint32_t co: 1; /* CDR.3 CLKOUT enable/disable */
uint32_t reserved3: 3; /* Internal Reserved. RXINTEN and CBP not supported */
uint32_t cm: 1; /* CDR.7 Register Layout. Basic:0 Extended:1 */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} clock_divider_reg; /* Address 31 */
} twai_dev_t;
_Static_assert(sizeof(twai_dev_t) == 128, "TWAI registers should be 32 * 4 bytes");
extern twai_dev_t TWAI;
#ifdef __cplusplus
}
#endif

View file

@ -188,10 +188,10 @@
#define FSPICS3_OUT_IDX 120
#define FSPICS4_OUT_IDX 121
#define FSPICS5_OUT_IDX 122
#define CAN_RX_IDX 123
#define CAN_TX_IDX 123
#define CAN_BUS_OFF_ON_IDX 124
#define CAN_CLKOUT_IDX 125
#define TWAI_RX_IDX 123
#define TWAI_TX_IDX 123
#define TWAI_BUS_OFF_ON_IDX 124
#define TWAI_CLKOUT_IDX 125
#define SUBSPICLK_OUT_MUX_IDX 126
#define SUBSPIQ_IN_IDX 127
#define SUBSPIQ_OUT_IDX 127

View file

@ -43,7 +43,7 @@ typedef enum {
PERIPH_HSPI_MODULE, //SPI3
PERIPH_SPI2_DMA_MODULE,
PERIPH_SPI3_DMA_MODULE,
PERIPH_CAN_MODULE,
PERIPH_TWAI_MODULE,
PERIPH_RNG_MODULE,
PERIPH_WIFI_MODULE,
PERIPH_WIFI_BT_COMMON_MODULE,
@ -107,7 +107,7 @@ typedef enum {
ETS_PWM3_INTR_SOURCE, /**< interruot of PWM3, level*/
ETS_LEDC_INTR_SOURCE, /**< interrupt of LED PWM, level*/
ETS_EFUSE_INTR_SOURCE, /**< interrupt of efuse, level, not likely to use*/
ETS_CAN_INTR_SOURCE , /**< interrupt of can, level*/
ETS_TWAI_INTR_SOURCE , /**< interrupt of twai, level*/
ETS_USB_INTR_SOURCE = 48, /**< interrupt of USB, level*/
ETS_RTC_CORE_INTR_SOURCE, /**< interrupt of rtc core, level, include rtc watchdog*/

View file

@ -5,6 +5,6 @@
#pragma once
#define SOC_TWAI_SUPPORTED 1
#define SOC_CPU_CORES_NUM 1
#define SOC_SUPPORTS_SECURE_DL_MODE 1

View file

@ -262,12 +262,12 @@ extern "C" {
#define DPORT_PWM1_CLK_EN_M (BIT(20))
#define DPORT_PWM1_CLK_EN_V 0x1
#define DPORT_PWM1_CLK_EN_S 20
/* DPORT_CAN_CLK_EN : R/W ;bitpos:[19] ;default: 1'b0 ; */
/* DPORT_TWAI_CLK_EN : R/W ;bitpos:[19] ;default: 1'b0 ; */
/*description: */
#define DPORT_CAN_CLK_EN (BIT(19))
#define DPORT_CAN_CLK_EN_M (BIT(19))
#define DPORT_CAN_CLK_EN_V 0x1
#define DPORT_CAN_CLK_EN_S 19
#define DPORT_TWAI_CLK_EN (BIT(19))
#define DPORT_TWAI_CLK_EN_M (BIT(19))
#define DPORT_TWAI_CLK_EN_V 0x1
#define DPORT_TWAI_CLK_EN_S 19
/* DPORT_I2C_EXT1_CLK_EN : R/W ;bitpos:[18] ;default: 1'b0 ; */
/*description: */
#define DPORT_I2C_EXT1_CLK_EN (BIT(18))
@ -496,12 +496,12 @@ extern "C" {
#define DPORT_PWM1_RST_M (BIT(20))
#define DPORT_PWM1_RST_V 0x1
#define DPORT_PWM1_RST_S 20
/* DPORT_CAN_RST : R/W ;bitpos:[19] ;default: 1'b0 ; */
/* DPORT_TWAI_RST : R/W ;bitpos:[19] ;default: 1'b0 ; */
/*description: */
#define DPORT_CAN_RST (BIT(19))
#define DPORT_CAN_RST_M (BIT(19))
#define DPORT_CAN_RST_V 0x1
#define DPORT_CAN_RST_S 19
#define DPORT_TWAI_RST (BIT(19))
#define DPORT_TWAI_RST_M (BIT(19))
#define DPORT_TWAI_RST_V 0x1
#define DPORT_TWAI_RST_S 19
/* DPORT_I2C_EXT1_RST : R/W ;bitpos:[18] ;default: 1'b0 ; */
/*description: */
#define DPORT_I2C_EXT1_RST (BIT(18))

View file

@ -0,0 +1,28 @@
// Copyright 2019 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
//Chip specific TWAI related macros
//Any even number from 2 to 32768
#define TWAI_BRP_IS_VALID(brp) ((brp) >= 2 && (brp) <= 32768 && ((brp) & 0x1) == 0)
#define TWAI_BRP_MAX 32768
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,208 @@
// 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
/* ---------------------------- Register Layout ------------------------------ */
/* The TWAI peripheral's registers are 8bits, however the ESP32 can only access
* peripheral registers every 32bits. Therefore each TWAI register is mapped to
* the least significant byte of every 32bits.
*/
typedef volatile struct twai_dev_s {
//Configuration and Control Registers
union {
struct {
uint32_t rm: 1; /* MOD.0 Reset Mode */
uint32_t lom: 1; /* MOD.1 Listen Only Mode */
uint32_t stm: 1; /* MOD.2 Self Test Mode */
uint32_t afm: 1; /* MOD.3 Acceptance Filter Mode */
uint32_t reserved28: 28; /* Internal Reserved. MOD.4 Sleep Mode not supported */
};
uint32_t val;
} mode_reg; /* Address 0 */
union {
struct {
uint32_t tr: 1; /* CMR.0 Transmission Request */
uint32_t at: 1; /* CMR.1 Abort Transmission */
uint32_t rrb: 1; /* CMR.2 Release Receive Buffer */
uint32_t cdo: 1; /* CMR.3 Clear Data Overrun */
uint32_t srr: 1; /* CMR.4 Self Reception Request */
uint32_t reserved27: 27; /* Internal Reserved */
};
uint32_t val;
} command_reg; /* Address 1 */
union {
struct {
uint32_t rbs: 1; /* SR.0 Receive Buffer Status */
uint32_t dos: 1; /* SR.1 Data Overrun Status */
uint32_t tbs: 1; /* SR.2 Transmit Buffer Status */
uint32_t tcs: 1; /* SR.3 Transmission Complete Status */
uint32_t rs: 1; /* SR.4 Receive Status */
uint32_t ts: 1; /* SR.5 Transmit Status */
uint32_t es: 1; /* SR.6 Error Status */
uint32_t bs: 1; /* SR.7 Bus Status */
uint32_t ms: 1; /* SR.8 Miss Status */
uint32_t reserved24: 23; /* Internal Reserved */
};
uint32_t val;
} status_reg; /* Address 2 */
union {
struct {
uint32_t ri: 1; /* IR.0 Receive Interrupt */
uint32_t ti: 1; /* IR.1 Transmit Interrupt */
uint32_t ei: 1; /* IR.2 Error Interrupt */
uint32_t reserved2: 2; /* Internal Reserved (Data Overrun interrupt and Wake-up not supported) */
uint32_t epi: 1; /* IR.5 Error Passive Interrupt */
uint32_t ali: 1; /* IR.6 Arbitration Lost Interrupt */
uint32_t bei: 1; /* IR.7 Bus Error Interrupt */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} interrupt_reg; /* Address 3 */
union {
struct {
uint32_t rie: 1; /* IER.0 Receive Interrupt Enable */
uint32_t tie: 1; /* IER.1 Transmit Interrupt Enable */
uint32_t eie: 1; /* IER.2 Error Interrupt Enable */
uint32_t reserved2: 2; /* Internal Reserved (Data Overrun interrupt and Wake-up not supported) */
uint32_t epie: 1; /* IER.5 Error Passive Interrupt Enable */
uint32_t alie: 1; /* IER.6 Arbitration Lost Interrupt Enable */
uint32_t beie: 1; /* IER.7 Bus Error Interrupt Enable */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} interrupt_enable_reg; /* Address 4 */
uint32_t reserved_05; /* Address 5 */
union {
struct {
uint32_t brp: 14; /* BTR0[13:0] Baud Rate Prescaler */
uint32_t sjw: 2; /* BTR0[15:14] Synchronization Jump Width*/
uint32_t reserved16: 16; /* Internal Reserved */
};
uint32_t val;
} bus_timing_0_reg; /* Address 6 */
union {
struct {
uint32_t tseg1: 4; /* BTR1[3:0] Timing Segment 1 */
uint32_t tseg2: 3; /* BTR1[6:4] Timing Segment 2 */
uint32_t sam: 1; /* BTR1.7 Sampling*/
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} 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
union {
struct {
uint32_t alc: 5; /* ALC[4:0] Arbitration lost capture */
uint32_t reserved27: 27; /* Internal Reserved */
};
uint32_t val;
} arbitration_lost_captue_reg; /* Address 11 */
union {
struct {
uint32_t seg: 5; /* ECC[4:0] Error Code Segment 0 to 5 */
uint32_t dir: 1; /* ECC.5 Error Direction (TX/RX) */
uint32_t errc: 2; /* ECC[7:6] Error Code */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} error_code_capture_reg; /* Address 12 */
union {
struct {
uint32_t ewl: 8; /* EWL[7:0] Error Warning Limit */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} error_warning_limit_reg; /* EWLR[7:0] Error Warning Limit: Address 13 */
union {
struct {
uint32_t rxerr: 8; /* RXERR[7:0] Receive Error Counter */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} rx_error_counter_reg; /* Address 12 */
union {
struct {
uint32_t txerr: 8; /* TXERR[7:0] Receive Error Counter */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} tx_error_counter_reg; /* Address 15 */
//Shared Registers (TX Buff/RX Buff/Acc Filter)
union {
struct {
union {
struct {
uint32_t byte: 8; /* ACRx[7:0] Acceptance Code */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} acr[4];
union {
struct {
uint32_t byte: 8; /* AMRx[7:0] Acceptance Mask */
uint32_t reserved24: 24; /* Internal Reserved */
};
uint32_t val;
} amr[4];
uint32_t reserved32[5];
} acceptance_filter;
union {
struct {
uint32_t byte: 8;
uint32_t reserved24: 24;
};
uint32_t val;
} tx_rx_buffer[13];
}; /* Address 16-28 TX/RX Buffer and Acc Filter*/;
//Misc Registers
union {
struct {
uint32_t rmc: 7; /* RMC[6:0] RX Message Counter */
uint32_t reserved25: 25; /* Internal Reserved */
};
uint32_t val;
} rx_message_counter_reg; /* Address 29 */
uint32_t reserved_30; /* Address 30 (RX Buffer Start Address not supported) */
union {
struct {
uint32_t cd: 8; /* CDR[7:0] CLKOUT frequency selector based of fOSC */
uint32_t co: 1; /* CDR.8 CLKOUT enable/disable */
uint32_t reserved24: 23; /* Internal Reserved */
};
uint32_t val;
} clock_divider_reg; /* Address 31 */
} twai_dev_t;
_Static_assert(sizeof(twai_dev_t) == 128, "TWAI registers should be 32 * 4 bytes");
extern twai_dev_t TWAI;
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,28 @@
// Copyright 2019 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.
#pragma once
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "soc/twai_struct.h"
#include "soc/twai_caps.h"
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,154 @@
// Copyright 2015-2019 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.
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#warning hal/can_hal.h is deprecated, please use hal/twai_hal.h instead
#include "hal/twai_hal.h"
#include "hal/can_types.h"
/* ------------------------- Defines and Typedefs --------------------------- */
//Error active interrupt related
#define CAN_HAL_EVENT_BUS_OFF TWAI_HAL_EVENT_BUS_OFF
#define CAN_HAL_EVENT_BUS_RECOV_CPLT TWAI_HAL_EVENT_BUS_RECOV_CPLT
#define CAN_HAL_EVENT_BUS_RECOV_PROGRESS TWAI_HAL_EVENT_BUS_RECOV_PROGRESS
#define CAN_HAL_EVENT_ABOVE_EWL TWAI_HAL_EVENT_ABOVE_EWL
#define CAN_HAL_EVENT_BELOW_EWL TWAI_HAL_EVENT_BELOW_EWL
#define CAN_HAL_EVENT_ERROR_PASSIVE TWAI_HAL_EVENT_ERROR_PASSIVE
#define CAN_HAL_EVENT_ERROR_ACTIVE TWAI_HAL_EVENT_ERROR_ACTIVE
#define CAN_HAL_EVENT_BUS_ERR TWAI_HAL_EVENT_BUS_ERR
#define CAN_HAL_EVENT_ARB_LOST TWAI_HAL_EVENT_ARB_LOST
#define CAN_HAL_EVENT_RX_BUFF_FRAME TWAI_HAL_EVENT_RX_BUFF_FRAME
#define CAN_HAL_EVENT_TX_BUFF_FREE TWAI_HAL_EVENT_TX_BUFF_FREE
typedef twai_hal_context_t can_hal_context_t;
typedef twai_hal_frame_t can_hal_frame_t;
/* ---------------------------- Init and Config ----------------------------- */
static inline bool can_hal_init(can_hal_context_t *hal_ctx){
return twai_hal_init(hal_ctx);
}
static inline void can_hal_deinit(can_hal_context_t *hal_ctx)
{
twai_hal_deinit(hal_ctx);
}
static inline void can_hal_configure(can_hal_context_t *hal_ctx, const can_timing_config_t *t_config, const can_filter_config_t *f_config, uint32_t intr_mask, uint32_t clkout_divider)
{
twai_hal_configure(hal_ctx, t_config, f_config, intr_mask, clkout_divider);
}
/* -------------------------------- Actions --------------------------------- */
static inline bool can_hal_start(can_hal_context_t *hal_ctx, can_mode_t mode)
{
return twai_hal_start(hal_ctx, mode);
}
static inline bool can_hal_stop(can_hal_context_t *hal_ctx)
{
return twai_hal_stop(hal_ctx);
}
static inline bool can_hal_start_bus_recovery(can_hal_context_t *hal_ctx)
{
return twai_hal_start_bus_recovery(hal_ctx);
}
static inline uint32_t can_hal_get_tec(can_hal_context_t *hal_ctx)
{
return twai_hal_get_tec(hal_ctx);
}
static inline uint32_t can_hal_get_rec(can_hal_context_t *hal_ctx)
{
return twai_hal_get_rec(hal_ctx);
}
static inline uint32_t can_hal_get_rx_msg_count(can_hal_context_t *hal_ctx)
{
return twai_hal_get_rx_msg_count(hal_ctx);
}
static inline bool can_hal_check_last_tx_successful(can_hal_context_t *hal_ctx)
{
return twai_hal_check_last_tx_successful(hal_ctx);
}
/* ----------------------------- Event Handling ----------------------------- */
static inline uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx, bool bus_recovering) {
return twai_hal_decode_interrupt_events(hal_ctx, bus_recovering);
}
static inline bool can_hal_handle_bus_recov_cplt(can_hal_context_t *hal_ctx)
{
return twai_hal_handle_bus_recov_cplt(hal_ctx);
}
static inline void can_hal_handle_arb_lost(can_hal_context_t *hal_ctx)
{
twai_hal_handle_arb_lost(hal_ctx);
}
static inline void can_hal_handle_bus_error(can_hal_context_t *hal_ctx)
{
twai_hal_handle_bus_error(hal_ctx);
}
static inline void can_hal_handle_bus_off(can_hal_context_t *hal_ctx)
{
twai_hal_handle_bus_off(hal_ctx);
}
/* ------------------------------- TX and RX -------------------------------- */
static inline void can_hal_format_frame(const can_message_t *message, can_hal_frame_t *frame)
{
twai_hal_format_frame(message, frame);
}
static inline void can_hal_parse_frame(can_hal_frame_t *frame, can_message_t *message)
{
twai_hal_parse_frame(frame, message);
}
static inline void can_hal_set_tx_buffer_and_transmit(can_hal_context_t *hal_ctx, can_hal_frame_t *tx_frame)
{
twai_hal_set_tx_buffer_and_transmit(hal_ctx, tx_frame);
}
static inline void can_hal_read_rx_buffer_and_clear(can_hal_context_t *hal_ctx, can_hal_frame_t *rx_frame)
{
twai_hal_read_rx_buffer_and_clear(hal_ctx, rx_frame);
}
#ifdef __cplusplus
}
#endif

View file

@ -26,676 +26,227 @@
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#warning hal/can_ll.h is deprecated, please use hal/twai_ll.h instead
#include "hal/twai_ll.h"
#include "hal/can_types.h"
#include "soc/can_periph.h"
/* ------------------------- Defines and Typedefs --------------------------- */
#define CAN_LL_STATUS_RBS (0x1 << 0)
#define CAN_LL_STATUS_DOS (0x1 << 1)
#define CAN_LL_STATUS_TBS (0x1 << 2)
#define CAN_LL_STATUS_TCS (0x1 << 3)
#define CAN_LL_STATUS_RS (0x1 << 4)
#define CAN_LL_STATUS_TS (0x1 << 5)
#define CAN_LL_STATUS_ES (0x1 << 6)
#define CAN_LL_STATUS_BS (0x1 << 7)
#define CAN_LL_STATUS_RBS TWAI_LL_STATUS_RBS
#define CAN_LL_STATUS_DOS TWAI_LL_STATUS_DOS
#define CAN_LL_STATUS_TBS TWAI_LL_STATUS_TBS
#define CAN_LL_STATUS_TCS TWAI_LL_STATUS_TCS
#define CAN_LL_STATUS_RS TWAI_LL_STATUS_RS
#define CAN_LL_STATUS_TS TWAI_LL_STATUS_TS
#define CAN_LL_STATUS_ES TWAI_LL_STATUS_ES
#define CAN_LL_STATUS_BS TWAI_LL_STATUS_BS
#define CAN_LL_INTR_RI (0x1 << 0)
#define CAN_LL_INTR_TI (0x1 << 1)
#define CAN_LL_INTR_EI (0x1 << 2)
//Data overrun interrupt not supported in SW due to HW peculiarities
#define CAN_LL_INTR_EPI (0x1 << 5)
#define CAN_LL_INTR_ALI (0x1 << 6)
#define CAN_LL_INTR_BEI (0x1 << 7)
#define CAN_LL_INTR_RI TWAI_LL_INTR_RI
#define CAN_LL_INTR_TI TWAI_LL_INTR_TI
#define CAN_LL_INTR_EI TWAI_LL_INTR_EI
#define CAN_LL_INTR_EPI TWAI_LL_INTR_EPI
#define CAN_LL_INTR_ALI TWAI_LL_INTR_ALI
#define CAN_LL_INTR_BEI TWAI_LL_INTR_BEI
/*
* The following frame structure has an NEARLY identical bit field layout to
* each byte of the TX buffer. This allows for formatting and parsing frames to
* be done outside of time critical regions (i.e., ISRs). All the ISR needs to
* do is to copy byte by byte to/from the 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 which in turn indicate the type of transmission to execute.
*/
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[2]; //11 bit standard frame identifier
uint8_t data[8]; //Data bytes (0 to 8)
uint8_t reserved8[2];
} standard;
struct {
uint8_t id[4]; //29 bit extended frame identifier
uint8_t data[8]; //Data bytes (0 to 8)
} extended;
};
};
uint8_t bytes[13];
} __attribute__((packed)) can_ll_frame_buffer_t;
typedef twai_ll_frame_buffer_t can_ll_frame_buffer_t;
/* ---------------------------- Mode Register ------------------------------- */
/**
* @brief Enter reset mode
*
* When in reset mode, the CAN controller is effectively disconnected from the
* CAN bus and will not participate in any bus activates. Reset mode is required
* in order to write the majority of configuration registers.
*
* @param hw Start address of the CAN registers
* @return true if reset mode was entered successfully
*
* @note Reset mode is automatically entered on BUS OFF condition
*/
static inline bool can_ll_enter_reset_mode(can_dev_t *hw)
{
hw->mode_reg.rm = 1;
return hw->mode_reg.rm;
return twai_ll_enter_reset_mode(hw);
}
/**
* @brief Exit reset mode
*
* When not in reset mode, the CAN controller will take part in bus activities
* (e.g., send/receive/acknowledge messages and error frames) depending on the
* operating mode.
*
* @param hw Start address of the CAN registers
* @return true if reset mode was exit successfully
*
* @note Reset mode must be exit to initiate BUS OFF recovery
*/
static inline bool can_ll_exit_reset_mode(can_dev_t *hw)
{
hw->mode_reg.rm = 0;
return !(hw->mode_reg.rm);
return twai_ll_exit_reset_mode(hw);
}
/**
* @brief Check if in reset mode
* @param hw Start address of the CAN registers
* @return true if in reset mode
*/
static inline bool can_ll_is_in_reset_mode(can_dev_t *hw)
{
return hw->mode_reg.rm;
return twai_ll_is_in_reset_mode(hw);
}
/**
* @brief Set operating mode of CAN controller
*
* @param hw Start address of the CAN registers
* @param mode Operating mode
*
* @note Must be called in reset mode
*/
static inline void can_ll_set_mode(can_dev_t *hw, can_mode_t mode)
{
if (mode == CAN_MODE_NORMAL) { //Normal Operating mode
hw->mode_reg.lom = 0;
hw->mode_reg.stm = 0;
} else if (mode == CAN_MODE_NO_ACK) { //Self Test Mode (No Ack)
hw->mode_reg.lom = 0;
hw->mode_reg.stm = 1;
} else if (mode == CAN_MODE_LISTEN_ONLY) { //Listen Only Mode
hw->mode_reg.lom = 1;
hw->mode_reg.stm = 0;
}
twai_ll_set_mode(hw, mode);
}
/* --------------------------- Command Register ----------------------------- */
/**
* @brief Set TX command
*
* Setting the TX command will cause the CAN controller to attempt to transmit
* the frame stored in the TX buffer. The TX buffer will be occupied (i.e.,
* locked) until TX completes.
*
* @param hw Start address of the CAN registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void can_ll_set_cmd_tx(can_dev_t *hw)
{
hw->command_reg.tr = 1;
twai_ll_set_cmd_tx(hw);
}
/**
* @brief Set single shot TX command
*
* Similar to setting TX command, but the CAN controller will not automatically
* retry transmission upon an error (e.g., due to an acknowledgement error).
*
* @param hw Start address of the CAN registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void can_ll_set_cmd_tx_single_shot(can_dev_t *hw)
{
hw->command_reg.val = 0x03; //Writing to TR and AT simultaneously
twai_ll_set_cmd_tx_single_shot(hw);
}
/**
* @brief Aborts TX
*
* Frames awaiting TX will be aborted. Frames already being TX are not aborted.
* Transmission Complete Status bit is automatically set to 1.
* Similar to setting TX command, but the CAN controller will not automatically
* retry transmission upon an error (e.g., due to acknowledge error).
*
* @param hw Start address of the CAN registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void can_ll_set_cmd_abort_tx(can_dev_t *hw)
{
hw->command_reg.at = 1;
twai_ll_set_cmd_abort_tx(hw);
}
/**
* @brief Release RX buffer
*
* Rotates RX buffer to the next frame in the RX FIFO.
*
* @param hw Start address of the CAN registers
*/
static inline void can_ll_set_cmd_release_rx_buffer(can_dev_t *hw)
{
hw->command_reg.rrb = 1;
twai_ll_set_cmd_release_rx_buffer(hw);
}
/**
* @brief Clear data overrun
*
* Clears the data overrun status bit
*
* @param hw Start address of the CAN registers
*/
static inline void can_ll_set_cmd_clear_data_overrun(can_dev_t *hw)
{
hw->command_reg.cdo = 1;
twai_ll_set_cmd_clear_data_overrun(hw);
}
/**
* @brief Set self reception single shot command
*
* Similar to setting TX command, but the CAN controller also simultaneously
* receive the transmitted frame and is generally used for self testing
* purposes. The CAN controller will not ACK the received message, so consider
* using the NO_ACK operating mode.
*
* @param hw Start address of the CAN registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void can_ll_set_cmd_self_rx_request(can_dev_t *hw)
{
hw->command_reg.srr = 1;
twai_ll_set_cmd_self_rx_request(hw);
}
/**
* @brief Set self reception request command
*
* Similar to setting the self reception request, but the CAN controller will
* not automatically retry transmission upon an error (e.g., due to and
* acknowledgement error).
*
* @param hw Start address of the CAN registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void can_ll_set_cmd_self_rx_single_shot(can_dev_t *hw)
{
hw->command_reg.val = 0x12;
twai_ll_set_cmd_self_rx_single_shot(hw);
}
/* --------------------------- Status Register ------------------------------ */
/**
* @brief Get all status bits
*
* @param hw Start address of the CAN registers
* @return Status bits
*/
static inline uint32_t can_ll_get_status(can_dev_t *hw)
{
return hw->status_reg.val;
return twai_ll_get_status(hw);
}
/**
* @brief Check if RX FIFO overrun status bit is set
*
* @param hw Start address of the CAN registers
* @return Overrun status bit
*/
static inline bool can_ll_is_fifo_overrun(can_dev_t *hw)
{
return hw->status_reg.dos;
return twai_ll_is_fifo_overrun(hw);
}
/**
* @brief Check if previously TX was successful
*
* @param hw Start address of the CAN registers
* @return Whether previous TX was successful
*/
static inline bool can_ll_is_last_tx_successful(can_dev_t *hw)
{
return hw->status_reg.tcs;
return twai_ll_is_last_tx_successful(hw);
}
//Todo: Add stand alone status bit check functions when necessary
/* -------------------------- Interrupt Register ---------------------------- */
/**
* @brief Get currently set interrupts
*
* Reading the interrupt registers will automatically clear all interrupts
* except for the Receive Interrupt.
*
* @param hw Start address of the CAN registers
* @return Bit mask of set interrupts
*/
static inline uint32_t can_ll_get_and_clear_intrs(can_dev_t *hw)
{
return hw->interrupt_reg.val;
return twai_ll_get_and_clear_intrs(hw);
}
/* ----------------------- Interrupt Enable Register ------------------------ */
/**
* @brief Set which interrupts are enabled
*
* @param hw Start address of the CAN registers
* @param Bit mask of interrupts to enable
*
* @note Must be called in reset mode
*/
static inline void can_ll_set_enabled_intrs(can_dev_t *hw, uint32_t intr_mask)
{
#ifdef CAN_BRP_DIV_SUPPORTED
//ESP32 Rev 2 has brp div. Need to mask when setting
hw->interrupt_enable_reg.val = (hw->interrupt_enable_reg.val & 0x10) | intr_mask;
#else
hw->interrupt_enable_reg.val = intr_mask;
#endif
twai_ll_set_enabled_intrs(hw, intr_mask);
}
/* ------------------------ Bus Timing Registers --------------------------- */
/**
* @brief Set bus timing
*
* @param hw Start address of the CAN registers
* @param brp Baud Rate Prescaler
* @param sjw Synchronization Jump Width
* @param tseg1 Timing Segment 1
* @param tseg2 Timing Segment 2
* @param triple_sampling Triple Sampling enable/disable
*
* @note Must be called in reset mode
* @note ESP32 rev 2 or later can support a x2 brp by setting a brp_div bit,
* allowing the brp to go from a maximum of 128 to 256.
*/
static inline void can_ll_set_bus_timing(can_dev_t *hw, uint32_t brp, uint32_t sjw, uint32_t tseg1, uint32_t tseg2, bool triple_sampling)
{
#ifdef CAN_BRP_DIV_SUPPORTED
if (brp > CAN_BRP_DIV_THRESH) {
//Need to set brp_div bit
hw->interrupt_enable_reg.brp_div = 1;
brp /= 2;
}
#endif
hw->bus_timing_0_reg.brp = (brp / 2) - 1;
hw->bus_timing_0_reg.sjw = sjw - 1;
hw->bus_timing_1_reg.tseg1 = tseg1 - 1;
hw->bus_timing_1_reg.tseg2 = tseg2 - 1;
hw->bus_timing_1_reg.sam = triple_sampling;
twai_ll_set_bus_timing(hw, brp, sjw, tseg1, tseg2, triple_sampling);
}
/* ----------------------------- ALC Register ------------------------------- */
/**
* @brief Clear Arbitration Lost Capture Register
*
* Reading the ALC register rearms the Arbitration Lost Interrupt
*
* @param hw Start address of the CAN registers
*/
static inline void can_ll_clear_arb_lost_cap(can_dev_t *hw)
{
(void)hw->arbitration_lost_captue_reg.val;
//Todo: Decode ALC register
twai_ll_clear_arb_lost_cap(hw);
}
/* ----------------------------- ECC Register ------------------------------- */
/**
* @brief Clear Error Code Capture register
*
* Reading the ECC register rearms the Bus Error Interrupt
*
* @param hw Start address of the CAN registers
*/
static inline void can_ll_clear_err_code_cap(can_dev_t *hw)
{
(void)hw->error_code_capture_reg.val;
//Todo: Decode error code capture
twai_ll_clear_err_code_cap(hw);
}
/* ----------------------------- EWL Register ------------------------------- */
/**
* @brief Set Error Warning Limit
*
* @param hw Start address of the CAN registers
* @param ewl Error Warning Limit
*
* @note Must be called in reset mode
*/
static inline void can_ll_set_err_warn_lim(can_dev_t *hw, uint32_t ewl)
{
hw->error_warning_limit_reg.ewl = ewl;
twai_ll_set_err_warn_lim(hw, ewl);
}
/**
* @brief Get Error Warning Limit
*
* @param hw Start address of the CAN registers
* @return Error Warning Limit
*/
static inline uint32_t can_ll_get_err_warn_lim(can_dev_t *hw)
{
return hw->error_warning_limit_reg.val;
return twai_ll_get_err_warn_lim(hw);
}
/* ------------------------ RX Error Count Register ------------------------- */
/**
* @brief Get RX Error Counter
*
* @param hw Start address of the CAN registers
* @return REC value
*
* @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS
* OFF condition automatically sets the REC to 0.
*/
static inline uint32_t can_ll_get_rec(can_dev_t *hw)
{
return hw->rx_error_counter_reg.val;
return twai_ll_get_rec(hw);
}
/**
* @brief Set RX Error Counter
*
* @param hw Start address of the CAN registers
* @param rec REC value
*
* @note Must be called in reset mode
*/
static inline void can_ll_set_rec(can_dev_t *hw, uint32_t rec)
{
hw->rx_error_counter_reg.rxerr = rec;
twai_ll_set_rec(hw, rec);
}
/* ------------------------ TX Error Count Register ------------------------- */
/**
* @brief Get TX Error Counter
*
* @param hw Start address of the CAN registers
* @return TEC value
*
* @note A BUS OFF condition will automatically set this to 128
*/
static inline uint32_t can_ll_get_tec(can_dev_t *hw)
{
return hw->tx_error_counter_reg.val;
return twai_ll_get_tec(hw);
}
/**
* @brief Set TX Error Counter
*
* @param hw Start address of the CAN registers
* @param tec TEC value
*
* @note Must be called in reset mode
*/
static inline void can_ll_set_tec(can_dev_t *hw, uint32_t tec)
{
hw->tx_error_counter_reg.txerr = tec;
twai_ll_set_tec(hw, tec);
}
/* ---------------------- Acceptance Filter Registers ----------------------- */
/**
* @brief Set Acceptance Filter
* @param hw Start address of the CAN registers
* @param code Acceptance Code
* @param mask Acceptance Mask
* @param single_filter Whether to enable single filter mode
*
* @note Must be called in reset mode
*/
static inline void can_ll_set_acc_filter(can_dev_t* hw, uint32_t code, uint32_t mask, bool single_filter)
{
uint32_t code_swapped = __builtin_bswap32(code);
uint32_t mask_swapped = __builtin_bswap32(mask);
for (int i = 0; i < 4; i++) {
hw->acceptance_filter.acr[i].byte = ((code_swapped >> (i * 8)) & 0xFF);
hw->acceptance_filter.amr[i].byte = ((mask_swapped >> (i * 8)) & 0xFF);
}
hw->mode_reg.afm = single_filter;
twai_ll_set_acc_filter(hw, code, mask, single_filter);
}
/* ------------------------- TX/RX Buffer Registers ------------------------- */
/**
* @brief Copy a formatted CAN frame into TX buffer for transmission
*
* @param hw Start address of the CAN registers
* @param tx_frame Pointer to formatted frame
*
* @note Call can_ll_format_frame_buffer() to format a frame
*/
static inline void can_ll_set_tx_buffer(can_dev_t *hw, can_ll_frame_buffer_t *tx_frame)
{
//Copy formatted frame into TX buffer
for (int i = 0; i < 13; i++) {
hw->tx_rx_buffer[i].val = tx_frame->bytes[i];
}
twai_ll_set_tx_buffer(hw, tx_frame);
}
/**
* @brief Copy a received frame from the RX buffer for parsing
*
* @param hw Start address of the CAN registers
* @param rx_frame Pointer to store formatted frame
*
* @note Call can_ll_prase_frame_buffer() to parse the formatted frame
*/
static inline void can_ll_get_rx_buffer(can_dev_t *hw, can_ll_frame_buffer_t *rx_frame)
{
//Copy RX buffer registers into frame
for (int i = 0; i < 13; i++) {
rx_frame->bytes[i] = hw->tx_rx_buffer[i].byte;
}
twai_ll_get_rx_buffer(hw, rx_frame);
}
/**
* @brief Format contents of a CAN frame into layout of TX Buffer
*
* @param[in] id 11 or 29bit ID
* @param[in] dlc Data length code
* @param[in] data Pointer to an 8 byte array containing data. NULL if no data
* @param[in] format Type of CAN frame
* @param[in] single_shot Frame will not be retransmitted on failure
* @param[in] self_rx Frame will also be simultaneously received
* @param[out] tx_frame Pointer to store formatted frame
*/
static inline void can_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data,
uint32_t flags, can_ll_frame_buffer_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. */
bool is_extd = flags & CAN_MSG_FLAG_EXTD;
bool is_rtr = flags & CAN_MSG_FLAG_RTR;
//Set frame information
tx_frame->dlc = dlc;
tx_frame->frame_format = is_extd;
tx_frame->rtr = is_rtr;
tx_frame->self_reception = (flags & CAN_MSG_FLAG_SELF) ? 1 : 0;
tx_frame->single_shot = (flags & CAN_MSG_FLAG_SS) ? 1 : 0;
//Set ID
if (is_extd) {
uint32_t id_temp = __builtin_bswap32((id & CAN_EXTD_ID_MASK) << 3); //((id << 3) >> 8*(3-i))
for (int i = 0; i < 4; i++) {
tx_frame->extended.id[i] = (id_temp >> (8 * i)) & 0xFF;
}
} else {
uint32_t id_temp = __builtin_bswap16((id & CAN_STD_ID_MASK) << 5); //((id << 5) >> 8*(1-i))
for (int i = 0; i < 2; i++) {
tx_frame->standard.id[i] = (id_temp >> (8 * i)) & 0xFF;
}
}
//Set Data
uint8_t *data_buffer = (is_extd) ? tx_frame->extended.data : tx_frame->standard.data;
if (!is_rtr) {
for (int i = 0; (i < dlc) && (i < CAN_FRAME_MAX_DLC); i++) {
data_buffer[i] = data[i];
}
}
twai_ll_format_frame_buffer(id, dlc, data, flags, tx_frame);
}
/**
* @brief Parse formatted CAN frame (RX Buffer Layout) into its contents
*
* @param[in] rx_frame Pointer to formatted frame
* @param[out] id 11 or 29bit ID
* @param[out] dlc Data length code
* @param[out] data Data. Left over bytes set to 0.
* @param[out] format Type of CAN frame
*/
static inline void can_ll_prase_frame_buffer(can_ll_frame_buffer_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;
uint32_t flags_temp = 0;
flags_temp |= (rx_frame->frame_format) ? CAN_MSG_FLAG_EXTD : 0;
flags_temp |= (rx_frame->rtr) ? CAN_MSG_FLAG_RTR : 0;
flags_temp |= (rx_frame->dlc > CAN_FRAME_MAX_DLC) ? CAN_MSG_FLAG_DLC_NON_COMP : 0;
*flags = flags_temp;
//Copy ID
if (rx_frame->frame_format) {
uint32_t id_temp = 0;
for (int i = 0; i < 4; i++) {
id_temp |= rx_frame->extended.id[i] << (8 * i);
}
id_temp = __builtin_bswap32(id_temp) >> 3; //((byte[i] << 8*(3-i)) >> 3)
*id = id_temp & CAN_EXTD_ID_MASK;
} else {
uint32_t id_temp = 0;
for (int i = 0; i < 2; i++) {
id_temp |= rx_frame->standard.id[i] << (8 * i);
}
id_temp = __builtin_bswap16(id_temp) >> 5; //((byte[i] << 8*(1-i)) >> 5)
*id = id_temp & CAN_STD_ID_MASK;
}
//Copy data
uint8_t *data_buffer = (rx_frame->frame_format) ? rx_frame->extended.data : rx_frame->standard.data;
int data_length = (rx_frame->rtr) ? 0 : ((rx_frame->dlc > CAN_FRAME_MAX_DLC) ? CAN_FRAME_MAX_DLC : rx_frame->dlc);
for (int i = 0; i < data_length; i++) {
data[i] = data_buffer[i];
}
//Set remaining bytes of data to 0
for (int i = data_length; i < CAN_FRAME_MAX_DLC; i++) {
data[i] = 0;
}
twai_ll_prase_frame_buffer(rx_frame, id, dlc, data, flags);
}
/* ----------------------- RX Message Count Register ------------------------ */
/**
* @brief Get RX Message Counter
*
* @param hw Start address of the CAN registers
* @return RX Message Counter
*/
static inline uint32_t can_ll_get_rx_msg_count(can_dev_t *hw)
{
return hw->rx_message_counter_reg.val;
return twai_ll_get_rx_msg_count(hw);
}
/* ------------------------- Clock Divider Register ------------------------- */
/**
* @brief Set CLKOUT Divider and enable/disable
*
* @param hw Start address of the CAN registers
* @param divider Divider for CLKOUT. Set to 0 to disable CLKOUT
*/
static inline void can_ll_set_clkout(can_dev_t *hw, 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. */
if (divider >= 2 && divider <= 14) {
CAN.clock_divider_reg.co = 0;
CAN.clock_divider_reg.cd = (divider / 2) - 1;
} else if (divider == 1) {
CAN.clock_divider_reg.co = 0;
CAN.clock_divider_reg.cd = 7;
} else {
CAN.clock_divider_reg.co = 1;
CAN.clock_divider_reg.cd = 0;
}
twai_ll_set_clkout(hw, divider);
}
/**
* @brief Set register address mapping to extended mode
*
* Extended mode register address mapping consists of more registers and extra
* features.
*
* @param hw Start address of the CAN registers
*
* @note Must be called before setting any configuration
* @note Must be called in reset mode
*/
static inline void can_ll_enable_extended_reg_layout(can_dev_t *hw)
{
hw->clock_divider_reg.cm = 1;
twai_ll_enable_extended_reg_layout(hw);
}
#ifdef __cplusplus

View file

@ -0,0 +1,68 @@
// Copyright 2015-2019 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.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#warning hal/can_types.h is deprecated, please use hal/twai_types.h instead
#include "hal/twai_types.h"
/* ---------------------------- Compatibility ------------------------------- */
#define CAN_EXTD_ID_MASK TWAI_EXTD_ID_MASK
#define CAN_STD_ID_MASK TWAI_STD_ID_MASK
#define CAN_FRAME_MAX_DLC TWAI_FRAME_MAX_DLC
#define CAN_FRAME_EXTD_ID_LEN_BYTES TWAI_FRAME_EXTD_ID_LEN_BYTES
#define CAN_FRAME_STD_ID_LEN_BYTES TWAI_FRAME_STD_ID_LEN_BYTES
#define CAN_ERR_PASS_THRESH TWAI_ERR_PASS_THRESH
#define CAN_MSG_FLAG_NONE TWAI_MSG_FLAG_NONE
#define CAN_MSG_FLAG_EXTD TWAI_MSG_FLAG_EXTD
#define CAN_MSG_FLAG_RTR TWAI_MSG_FLAG_RTR
#define CAN_MSG_FLAG_SS TWAI_MSG_FLAG_SS
#define CAN_MSG_FLAG_SELF TWAI_MSG_FLAG_SELF
#define CAN_MSG_FLAG_DLC_NON_COMP TWAI_MSG_FLAG_DLC_NON_COMP
#if (TWAI_BRP_MAX > 128)
#define CAN_TIMING_CONFIG_12_5KBITS() TWAI_TIMING_CONFIG_12_5KBITS()
#define CAN_TIMING_CONFIG_16KBITS() TWAI_TIMING_CONFIG_16KBITS()
#define CAN_TIMING_CONFIG_20KBITS() TWAI_TIMING_CONFIG_20KBITS()
#endif
#define CAN_TIMING_CONFIG_25KBITS() TWAI_TIMING_CONFIG_25KBITS()
#define CAN_TIMING_CONFIG_50KBITS() TWAI_TIMING_CONFIG_50KBITS()
#define CAN_TIMING_CONFIG_100KBITS() TWAI_TIMING_CONFIG_100KBITS()
#define CAN_TIMING_CONFIG_125KBITS() TWAI_TIMING_CONFIG_125KBITS()
#define CAN_TIMING_CONFIG_250KBITS() TWAI_TIMING_CONFIG_250KBITS()
#define CAN_TIMING_CONFIG_500KBITS() TWAI_TIMING_CONFIG_500KBITS()
#define CAN_TIMING_CONFIG_800KBITS() TWAI_TIMING_CONFIG_800KBITS()
#define CAN_TIMING_CONFIG_1MBITS() TWAI_TIMING_CONFIG_1MBITS()
#define CAN_FILTER_CONFIG_ACCEPT_ALL() TWAI_FILTER_CONFIG_ACCEPT_ALL()
typedef twai_mode_t can_mode_t;
#define CAN_MODE_NORMAL TWAI_MODE_NORMAL
#define CAN_MODE_NO_ACK TWAI_MODE_NO_ACK
#define CAN_MODE_LISTEN_ONLY TWAI_MODE_LISTEN_ONLY
typedef twai_message_t can_message_t;
typedef twai_timing_config_t can_timing_config_t;
typedef twai_filter_config_t can_filter_config_t;
#ifdef __cplusplus
}
#endif

View file

@ -75,8 +75,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph)
return DPORT_WIFI_CLK_SDIO_HOST_EN;
case PERIPH_SDIO_SLAVE_MODULE:
return DPORT_WIFI_CLK_SDIOSLAVE_EN;
case PERIPH_CAN_MODULE:
return DPORT_CAN_CLK_EN;
case PERIPH_TWAI_MODULE:
return DPORT_TWAI_CLK_EN;
case PERIPH_EMAC_MODULE:
return DPORT_WIFI_CLK_EMAC_EN;
case PERIPH_RNG_MODULE:
@ -153,8 +153,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en
return DPORT_SDIO_HOST_RST;
case PERIPH_SDIO_SLAVE_MODULE:
return DPORT_SDIO_RST;
case PERIPH_CAN_MODULE:
return DPORT_CAN_RST;
case PERIPH_TWAI_MODULE:
return DPORT_TWAI_RST;
case PERIPH_EMAC_MODULE:
return DPORT_EMAC_RST;
case PERIPH_AES_MODULE:

View file

@ -0,0 +1,703 @@
// Copyright 2015-2019 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.
/*******************************************************************************
* NOTICE
* The ll is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
// The Lowlevel layer for TWAI
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include "hal/twai_types.h"
#include "soc/twai_periph.h"
/* ------------------------- Defines and Typedefs --------------------------- */
#define TWAI_LL_STATUS_RBS (0x1 << 0)
#define TWAI_LL_STATUS_DOS (0x1 << 1)
#define TWAI_LL_STATUS_TBS (0x1 << 2)
#define TWAI_LL_STATUS_TCS (0x1 << 3)
#define TWAI_LL_STATUS_RS (0x1 << 4)
#define TWAI_LL_STATUS_TS (0x1 << 5)
#define TWAI_LL_STATUS_ES (0x1 << 6)
#define TWAI_LL_STATUS_BS (0x1 << 7)
#define TWAI_LL_INTR_RI (0x1 << 0)
#define TWAI_LL_INTR_TI (0x1 << 1)
#define TWAI_LL_INTR_EI (0x1 << 2)
//Data overrun interrupt not supported in SW due to HW peculiarities
#define TWAI_LL_INTR_EPI (0x1 << 5)
#define TWAI_LL_INTR_ALI (0x1 << 6)
#define TWAI_LL_INTR_BEI (0x1 << 7)
/*
* The following frame structure has an NEARLY identical bit field layout to
* each byte of the TX buffer. This allows for formatting and parsing frames to
* be done outside of time critical regions (i.e., ISRs). All the ISR needs to
* do is to copy byte by byte to/from the 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 which in turn indicate the type of transmission to execute.
*/
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[2]; //11 bit standard frame identifier
uint8_t data[8]; //Data bytes (0 to 8)
uint8_t reserved8[2];
} standard;
struct {
uint8_t id[4]; //29 bit extended frame identifier
uint8_t data[8]; //Data bytes (0 to 8)
} extended;
};
};
uint8_t bytes[13];
} __attribute__((packed)) twai_ll_frame_buffer_t;
_Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should be 13 bytes");
/* ---------------------------- Mode Register ------------------------------- */
/**
* @brief Enter reset mode
*
* When in reset mode, the TWAI controller is effectively disconnected from the
* TWAI bus and will not participate in any bus activates. Reset mode is required
* in order to write the majority of configuration registers.
*
* @param hw Start address of the TWAI registers
* @return true if reset mode was entered successfully
*
* @note Reset mode is automatically entered on BUS OFF condition
*/
static inline bool twai_ll_enter_reset_mode(twai_dev_t *hw)
{
hw->mode_reg.rm = 1;
return hw->mode_reg.rm;
}
/**
* @brief Exit reset mode
*
* When not in reset mode, the TWAI controller will take part in bus activities
* (e.g., send/receive/acknowledge messages and error frames) depending on the
* operating mode.
*
* @param hw Start address of the TWAI registers
* @return true if reset mode was exit successfully
*
* @note Reset mode must be exit to initiate BUS OFF recovery
*/
static inline bool twai_ll_exit_reset_mode(twai_dev_t *hw)
{
hw->mode_reg.rm = 0;
return !(hw->mode_reg.rm);
}
/**
* @brief Check if in reset mode
* @param hw Start address of the TWAI registers
* @return true if in reset mode
*/
static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw)
{
return hw->mode_reg.rm;
}
/**
* @brief Set operating mode of TWAI controller
*
* @param hw Start address of the TWAI registers
* @param mode Operating mode
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode)
{
if (mode == TWAI_MODE_NORMAL) { //Normal Operating mode
hw->mode_reg.lom = 0;
hw->mode_reg.stm = 0;
} else if (mode == TWAI_MODE_NO_ACK) { //Self Test Mode (No Ack)
hw->mode_reg.lom = 0;
hw->mode_reg.stm = 1;
} else if (mode == TWAI_MODE_LISTEN_ONLY) { //Listen Only Mode
hw->mode_reg.lom = 1;
hw->mode_reg.stm = 0;
}
}
/* --------------------------- Command Register ----------------------------- */
/**
* @brief Set TX command
*
* Setting the TX command will cause the TWAI controller to attempt to transmit
* the frame stored in the TX buffer. The TX buffer will be occupied (i.e.,
* locked) until TX completes.
*
* @param hw Start address of the TWAI registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void twai_ll_set_cmd_tx(twai_dev_t *hw)
{
hw->command_reg.tr = 1;
}
/**
* @brief Set single shot TX command
*
* Similar to setting TX command, but the TWAI controller will not automatically
* retry transmission upon an error (e.g., due to an acknowledgement error).
*
* @param hw Start address of the TWAI registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw)
{
hw->command_reg.val = 0x03; //Writing to TR and AT simultaneously
}
/**
* @brief Aborts TX
*
* Frames awaiting TX will be aborted. Frames already being TX are not aborted.
* Transmission Complete Status bit is automatically set to 1.
* Similar to setting TX command, but the TWAI controller will not automatically
* retry transmission upon an error (e.g., due to acknowledge error).
*
* @param hw Start address of the TWAI registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw)
{
hw->command_reg.at = 1;
}
/**
* @brief Release RX buffer
*
* Rotates RX buffer to the next frame in the RX FIFO.
*
* @param hw Start address of the TWAI registers
*/
static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw)
{
hw->command_reg.rrb = 1;
}
/**
* @brief Clear data overrun
*
* Clears the data overrun status bit
*
* @param hw Start address of the TWAI registers
*/
static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw)
{
hw->command_reg.cdo = 1;
}
/**
* @brief Set self reception single shot command
*
* Similar to setting TX command, but the TWAI controller also simultaneously
* receive the transmitted frame and is generally used for self testing
* purposes. The TWAI controller will not ACK the received message, so consider
* using the NO_ACK operating mode.
*
* @param hw Start address of the TWAI registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw)
{
hw->command_reg.srr = 1;
}
/**
* @brief Set self reception request command
*
* Similar to setting the self reception request, but the TWAI controller will
* not automatically retry transmission upon an error (e.g., due to and
* acknowledgement error).
*
* @param hw Start address of the TWAI registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw)
{
hw->command_reg.val = 0x12;
}
/* --------------------------- Status Register ------------------------------ */
/**
* @brief Get all status bits
*
* @param hw Start address of the TWAI registers
* @return Status bits
*/
static inline uint32_t twai_ll_get_status(twai_dev_t *hw)
{
return hw->status_reg.val;
}
/**
* @brief Check if RX FIFO overrun status bit is set
*
* @param hw Start address of the TWAI registers
* @return Overrun status bit
*/
static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw)
{
return hw->status_reg.dos;
}
/**
* @brief Check if previously TX was successful
*
* @param hw Start address of the TWAI registers
* @return Whether previous TX was successful
*/
static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw)
{
return hw->status_reg.tcs;
}
//Todo: Add stand alone status bit check functions when necessary
/* -------------------------- Interrupt Register ---------------------------- */
/**
* @brief Get currently set interrupts
*
* Reading the interrupt registers will automatically clear all interrupts
* except for the Receive Interrupt.
*
* @param hw Start address of the TWAI registers
* @return Bit mask of set interrupts
*/
static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw)
{
return hw->interrupt_reg.val;
}
/* ----------------------- Interrupt Enable Register ------------------------ */
/**
* @brief Set which interrupts are enabled
*
* @param hw Start address of the TWAI registers
* @param Bit mask of interrupts to enable
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask)
{
#ifdef TWAI_BRP_DIV_SUPPORTED
//ESP32 Rev 2 has brp div. Need to mask when setting
hw->interrupt_enable_reg.val = (hw->interrupt_enable_reg.val & 0x10) | intr_mask;
#else
hw->interrupt_enable_reg.val = intr_mask;
#endif
}
/* ------------------------ Bus Timing Registers --------------------------- */
/**
* @brief Set bus timing
*
* @param hw Start address of the TWAI registers
* @param brp Baud Rate Prescaler
* @param sjw Synchronization Jump Width
* @param tseg1 Timing Segment 1
* @param tseg2 Timing Segment 2
* @param triple_sampling Triple Sampling enable/disable
*
* @note Must be called in reset mode
* @note ESP32 rev 2 or later can support a x2 brp by setting a brp_div bit,
* allowing the brp to go from a maximum of 128 to 256.
*/
static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t sjw, uint32_t tseg1, uint32_t tseg2, bool triple_sampling)
{
#ifdef TWAI_BRP_DIV_SUPPORTED
if (brp > TWAI_BRP_DIV_THRESH) {
//Need to set brp_div bit
hw->interrupt_enable_reg.brp_div = 1;
brp /= 2;
}
#endif
hw->bus_timing_0_reg.brp = (brp / 2) - 1;
hw->bus_timing_0_reg.sjw = sjw - 1;
hw->bus_timing_1_reg.tseg1 = tseg1 - 1;
hw->bus_timing_1_reg.tseg2 = tseg2 - 1;
hw->bus_timing_1_reg.sam = triple_sampling;
}
/* ----------------------------- ALC Register ------------------------------- */
/**
* @brief Clear Arbitration Lost Capture Register
*
* Reading the ALC register rearms the Arbitration Lost Interrupt
*
* @param hw Start address of the TWAI registers
*/
static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw)
{
(void)hw->arbitration_lost_captue_reg.val;
//Todo: Decode ALC register
}
/* ----------------------------- ECC Register ------------------------------- */
/**
* @brief Clear Error Code Capture register
*
* Reading the ECC register rearms the Bus Error Interrupt
*
* @param hw Start address of the TWAI registers
*/
static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw)
{
(void)hw->error_code_capture_reg.val;
//Todo: Decode error code capture
}
/* ----------------------------- EWL Register ------------------------------- */
/**
* @brief Set Error Warning Limit
*
* @param hw Start address of the TWAI registers
* @param ewl Error Warning Limit
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl)
{
hw->error_warning_limit_reg.ewl = ewl;
}
/**
* @brief Get Error Warning Limit
*
* @param hw Start address of the TWAI registers
* @return Error Warning Limit
*/
static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw)
{
return hw->error_warning_limit_reg.val;
}
/* ------------------------ RX Error Count Register ------------------------- */
/**
* @brief Get RX Error Counter
*
* @param hw Start address of the TWAI registers
* @return REC value
*
* @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS
* OFF condition automatically sets the REC to 0.
*/
static inline uint32_t twai_ll_get_rec(twai_dev_t *hw)
{
return hw->rx_error_counter_reg.val;
}
/**
* @brief Set RX Error Counter
*
* @param hw Start address of the TWAI registers
* @param rec REC value
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec)
{
hw->rx_error_counter_reg.rxerr = rec;
}
/* ------------------------ TX Error Count Register ------------------------- */
/**
* @brief Get TX Error Counter
*
* @param hw Start address of the TWAI registers
* @return TEC value
*
* @note A BUS OFF condition will automatically set this to 128
*/
static inline uint32_t twai_ll_get_tec(twai_dev_t *hw)
{
return hw->tx_error_counter_reg.val;
}
/**
* @brief Set TX Error Counter
*
* @param hw Start address of the TWAI registers
* @param tec TEC value
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec)
{
hw->tx_error_counter_reg.txerr = tec;
}
/* ---------------------- Acceptance Filter Registers ----------------------- */
/**
* @brief Set Acceptance Filter
* @param hw Start address of the TWAI registers
* @param code Acceptance Code
* @param mask Acceptance Mask
* @param single_filter Whether to enable single filter mode
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_t mask, bool single_filter)
{
uint32_t code_swapped = __builtin_bswap32(code);
uint32_t mask_swapped = __builtin_bswap32(mask);
for (int i = 0; i < 4; i++) {
hw->acceptance_filter.acr[i].byte = ((code_swapped >> (i * 8)) & 0xFF);
hw->acceptance_filter.amr[i].byte = ((mask_swapped >> (i * 8)) & 0xFF);
}
hw->mode_reg.afm = single_filter;
}
/* ------------------------- TX/RX Buffer Registers ------------------------- */
/**
* @brief Copy a formatted TWAI frame into TX buffer for transmission
*
* @param hw Start address of the TWAI registers
* @param tx_frame Pointer to formatted frame
*
* @note Call twai_ll_format_frame_buffer() to format a frame
*/
static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *tx_frame)
{
//Copy formatted frame into TX buffer
for (int i = 0; i < 13; i++) {
hw->tx_rx_buffer[i].val = tx_frame->bytes[i];
}
}
/**
* @brief Copy a received frame from the RX buffer for parsing
*
* @param hw Start address of the TWAI registers
* @param rx_frame Pointer to store formatted frame
*
* @note Call twai_ll_prase_frame_buffer() to parse the formatted frame
*/
static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *rx_frame)
{
//Copy RX buffer registers into frame
for (int i = 0; i < 13; i++) {
rx_frame->bytes[i] = hw->tx_rx_buffer[i].byte;
}
}
/**
* @brief Format contents of a TWAI frame into layout of TX Buffer
*
* 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.
*
* @param[in] 11bit or 29bit ID
* @param[in] dlc Data length code
* @param[in] data Pointer to an 8 byte array containing data. NULL if no data
* @param[in] format Type of TWAI frame
* @param[in] single_shot Frame will not be retransmitted on failure
* @param[in] self_rx Frame will also be simultaneously received
* @param[out] tx_frame Pointer to store formatted frame
*/
static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data,
uint32_t flags, twai_ll_frame_buffer_t *tx_frame)
{
bool is_extd = flags & TWAI_MSG_FLAG_EXTD;
bool is_rtr = flags & TWAI_MSG_FLAG_RTR;
//Set frame information
tx_frame->dlc = dlc;
tx_frame->frame_format = is_extd;
tx_frame->rtr = is_rtr;
tx_frame->self_reception = (flags & TWAI_MSG_FLAG_SELF) ? 1 : 0;
tx_frame->single_shot = (flags & TWAI_MSG_FLAG_SS) ? 1 : 0;
//Set ID. The ID registers are big endian and left aligned, therefore a bswap will be required
if (is_extd) {
uint32_t id_temp = __builtin_bswap32((id & TWAI_EXTD_ID_MASK) << 3); //((id << 3) >> 8*(3-i))
for (int i = 0; i < 4; i++) {
tx_frame->extended.id[i] = (id_temp >> (8 * i)) & 0xFF;
}
} else {
uint32_t id_temp = __builtin_bswap16((id & TWAI_STD_ID_MASK) << 5); //((id << 5) >> 8*(1-i))
for (int i = 0; i < 2; i++) {
tx_frame->standard.id[i] = (id_temp >> (8 * i)) & 0xFF;
}
}
uint8_t *data_buffer = (is_extd) ? tx_frame->extended.data : tx_frame->standard.data;
if (!is_rtr) { //Only copy data if the frame is a data frame (i.e not RTR)
for (int i = 0; (i < dlc) && (i < TWAI_FRAME_MAX_DLC); i++) {
data_buffer[i] = data[i];
}
}
}
/**
* @brief Parse formatted TWAI frame (RX Buffer Layout) into its constituent contents
*
* @param[in] rx_frame Pointer to formatted frame
* @param[out] id 11 or 29bit ID
* @param[out] dlc Data length code
* @param[out] data Data. Left over bytes set to 0.
* @param[out] format Type of TWAI frame
*/
static inline void twai_ll_prase_frame_buffer(twai_ll_frame_buffer_t *rx_frame, uint32_t *id, uint8_t *dlc,
uint8_t *data, uint32_t *flags)
{
//Copy frame information
*dlc = rx_frame->dlc;
uint32_t flags_temp = 0;
flags_temp |= (rx_frame->frame_format) ? TWAI_MSG_FLAG_EXTD : 0;
flags_temp |= (rx_frame->rtr) ? TWAI_MSG_FLAG_RTR : 0;
flags_temp |= (rx_frame->dlc > TWAI_FRAME_MAX_DLC) ? TWAI_MSG_FLAG_DLC_NON_COMP : 0;
*flags = flags_temp;
//Copy ID. The ID registers are big endian and left aligned, therefore a bswap will be required
if (rx_frame->frame_format) {
uint32_t id_temp = 0;
for (int i = 0; i < 4; i++) {
id_temp |= rx_frame->extended.id[i] << (8 * i);
}
id_temp = __builtin_bswap32(id_temp) >> 3; //((byte[i] << 8*(3-i)) >> 3)
*id = id_temp & TWAI_EXTD_ID_MASK;
} else {
uint32_t id_temp = 0;
for (int i = 0; i < 2; i++) {
id_temp |= rx_frame->standard.id[i] << (8 * i);
}
id_temp = __builtin_bswap16(id_temp) >> 5; //((byte[i] << 8*(1-i)) >> 5)
*id = id_temp & TWAI_STD_ID_MASK;
}
uint8_t *data_buffer = (rx_frame->frame_format) ? rx_frame->extended.data : rx_frame->standard.data;
//Only copy data if the frame is a data frame (i.e. not a remote frame)
int data_length = (rx_frame->rtr) ? 0 : ((rx_frame->dlc > TWAI_FRAME_MAX_DLC) ? TWAI_FRAME_MAX_DLC : rx_frame->dlc);
for (int i = 0; i < data_length; i++) {
data[i] = data_buffer[i];
}
//Set remaining bytes of data to 0
for (int i = data_length; i < TWAI_FRAME_MAX_DLC; i++) {
data[i] = 0;
}
}
/* ----------------------- RX Message Count Register ------------------------ */
/**
* @brief Get RX Message Counter
*
* @param hw Start address of the TWAI registers
* @return RX Message Counter
*/
static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw)
{
return hw->rx_message_counter_reg.val;
}
/* ------------------------- Clock Divider Register ------------------------- */
/**
* @brief Set CLKOUT Divider and enable/disable
*
* Configure CLKOUT. CLKOUT is a pre-scaled version of APB CLK. Divider can be
* 1, or any even number from 2 to 14. Set the divider to 0 to disable CLKOUT.
*
* @param hw Start address of the TWAI registers
* @param divider Divider for CLKOUT. Set to 0 to disable CLKOUT
*/
static inline void twai_ll_set_clkout(twai_dev_t *hw, uint32_t divider)
{
if (divider >= 2 && divider <= 14) {
hw->clock_divider_reg.co = 0;
hw->clock_divider_reg.cd = (divider / 2) - 1;
} else if (divider == 1) {
//Setting the divider reg to max value (7) means a divider of 1
hw->clock_divider_reg.co = 0;
hw->clock_divider_reg.cd = 7;
} else {
hw->clock_divider_reg.co = 1;
hw->clock_divider_reg.cd = 0;
}
}
/**
* @brief Set register address mapping to extended mode
*
* Extended mode register address mapping consists of more registers and extra
* features.
*
* @param hw Start address of the TWAI registers
*
* @note Must be called before setting any configuration
* @note Must be called in reset mode
*/
static inline void twai_ll_enable_extended_reg_layout(twai_dev_t *hw)
{
hw->clock_divider_reg.cm = 1;
}
#ifdef __cplusplus
}
#endif

View file

@ -74,8 +74,8 @@ static inline uint32_t periph_ll_get_clk_en_mask(periph_module_t periph)
return DPORT_SPI2_DMA_CLK_EN;
case PERIPH_SPI3_DMA_MODULE:
return DPORT_SPI3_DMA_CLK_EN;
case PERIPH_CAN_MODULE:
return DPORT_CAN_CLK_EN;
case PERIPH_TWAI_MODULE:
return DPORT_TWAI_CLK_EN;
case PERIPH_RNG_MODULE:
return DPORT_WIFI_CLK_RNG_EN;
case PERIPH_WIFI_MODULE:
@ -152,8 +152,8 @@ static inline uint32_t periph_ll_get_rst_en_mask(periph_module_t periph, bool en
return DPORT_SPI2_DMA_RST;
case PERIPH_SPI3_DMA_MODULE:
return DPORT_SPI3_DMA_RST;
case PERIPH_CAN_MODULE:
return DPORT_CAN_RST;
case PERIPH_TWAI_MODULE:
return DPORT_TWAI_RST;
case PERIPH_SYSTIMER_MODULE:
return DPORT_SYSTIMER_RST;
case PERIPH_AES_MODULE:

View file

@ -0,0 +1,675 @@
// Copyright 2015-2019 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.
/*******************************************************************************
* NOTICE
* The ll is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
// The Lowlevel layer for TWAI
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include "hal/twai_types.h"
#include "soc/twai_periph.h"
/* ------------------------- Defines and Typedefs --------------------------- */
#define TWAI_LL_STATUS_RBS (0x1 << 0)
#define TWAI_LL_STATUS_DOS (0x1 << 1)
#define TWAI_LL_STATUS_TBS (0x1 << 2)
#define TWAI_LL_STATUS_TCS (0x1 << 3)
#define TWAI_LL_STATUS_RS (0x1 << 4)
#define TWAI_LL_STATUS_TS (0x1 << 5)
#define TWAI_LL_STATUS_ES (0x1 << 6)
#define TWAI_LL_STATUS_BS (0x1 << 7)
//Todo: Add Miss status support
#define TWAI_LL_INTR_RI (0x1 << 0)
#define TWAI_LL_INTR_TI (0x1 << 1)
#define TWAI_LL_INTR_EI (0x1 << 2)
//Data overrun interrupt not supported in SW due to HW peculiarities
#define TWAI_LL_INTR_EPI (0x1 << 5)
#define TWAI_LL_INTR_ALI (0x1 << 6)
#define TWAI_LL_INTR_BEI (0x1 << 7)
/*
* The following frame structure has an NEARLY identical bit field layout to
* each byte of the TX buffer. This allows for formatting and parsing frames to
* be done outside of time critical regions (i.e., ISRs). All the ISR needs to
* do is to copy byte by byte to/from the 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 which in turn indicate the type of transmission to execute.
*/
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[2]; //11 bit standard frame identifier
uint8_t data[8]; //Data bytes (0 to 8)
uint8_t reserved8[2];
} standard;
struct {
uint8_t id[4]; //29 bit extended frame identifier
uint8_t data[8]; //Data bytes (0 to 8)
} extended;
};
};
uint8_t bytes[13];
} __attribute__((packed)) twai_ll_frame_buffer_t;
_Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should be 13 bytes");
/* ---------------------------- Mode Register ------------------------------- */
/**
* @brief Enter reset mode
*
* When in reset mode, the TWAI controller is effectively disconnected from the
* TWAI bus and will not participate in any bus activates. Reset mode is required
* in order to write the majority of configuration registers.
*
* @param hw Start address of the TWAI registers
* @return true if reset mode was entered successfully
*
* @note Reset mode is automatically entered on BUS OFF condition
*/
static inline bool twai_ll_enter_reset_mode(twai_dev_t *hw)
{
hw->mode_reg.rm = 1;
return hw->mode_reg.rm;
}
/**
* @brief Exit reset mode
*
* When not in reset mode, the TWAI controller will take part in bus activities
* (e.g., send/receive/acknowledge messages and error frames) depending on the
* operating mode.
*
* @param hw Start address of the TWAI registers
* @return true if reset mode was exit successfully
*
* @note Reset mode must be exit to initiate BUS OFF recovery
*/
static inline bool twai_ll_exit_reset_mode(twai_dev_t *hw)
{
hw->mode_reg.rm = 0;
return !(hw->mode_reg.rm);
}
/**
* @brief Check if in reset mode
* @param hw Start address of the TWAI registers
* @return true if in reset mode
*/
static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw)
{
return hw->mode_reg.rm;
}
/**
* @brief Set operating mode of TWAI controller
*
* @param hw Start address of the TWAI registers
* @param mode Operating mode
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode)
{
if (mode == TWAI_MODE_NORMAL) { //Normal Operating mode
hw->mode_reg.lom = 0;
hw->mode_reg.stm = 0;
} else if (mode == TWAI_MODE_NO_ACK) { //Self Test Mode (No Ack)
hw->mode_reg.lom = 0;
hw->mode_reg.stm = 1;
} else if (mode == TWAI_MODE_LISTEN_ONLY) { //Listen Only Mode
hw->mode_reg.lom = 1;
hw->mode_reg.stm = 0;
}
}
/* --------------------------- Command Register ----------------------------- */
/**
* @brief Set TX command
*
* Setting the TX command will cause the TWAI controller to attempt to transmit
* the frame stored in the TX buffer. The TX buffer will be occupied (i.e.,
* locked) until TX completes.
*
* @param hw Start address of the TWAI registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void twai_ll_set_cmd_tx(twai_dev_t *hw)
{
hw->command_reg.tr = 1;
}
/**
* @brief Set single shot TX command
*
* Similar to setting TX command, but the TWAI controller will not automatically
* retry transmission upon an error (e.g., due to an acknowledgement error).
*
* @param hw Start address of the TWAI registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw)
{
hw->command_reg.val = 0x03; //Set command_reg.tr and command_reg.at simultaneously for single shot transmittion request
}
/**
* @brief Aborts TX
*
* Frames awaiting TX will be aborted. Frames already being TX are not aborted.
* Transmission Complete Status bit is automatically set to 1.
* Similar to setting TX command, but the TWAI controller will not automatically
* retry transmission upon an error (e.g., due to acknowledge error).
*
* @param hw Start address of the TWAI registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw)
{
hw->command_reg.at = 1;
}
/**
* @brief Release RX buffer
*
* Rotates RX buffer to the next frame in the RX FIFO.
*
* @param hw Start address of the TWAI registers
*/
static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw)
{
hw->command_reg.rrb = 1;
}
/**
* @brief Clear data overrun
*
* Clears the data overrun status bit
*
* @param hw Start address of the TWAI registers
*/
static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw)
{
hw->command_reg.cdo = 1;
}
/**
* @brief Set self reception single shot command
*
* Similar to setting TX command, but the TWAI controller also simultaneously
* receive the transmitted frame and is generally used for self testing
* purposes. The TWAI controller will not ACK the received message, so consider
* using the NO_ACK operating mode.
*
* @param hw Start address of the TWAI registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw)
{
hw->command_reg.srr = 1;
}
/**
* @brief Set self reception request command
*
* Similar to setting the self reception request, but the TWAI controller will
* not automatically retry transmission upon an error (e.g., due to and
* acknowledgement error).
*
* @param hw Start address of the TWAI registers
*
* @note Transmit commands should be called last (i.e., after handling buffer
* release and clear data overrun) in order to prevent the other commands
* overwriting this latched TX bit with 0.
*/
static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw)
{
hw->command_reg.val = 0x12; //Set command_reg.srr and command_reg.at simultaneously for single shot self reception request
}
/* --------------------------- Status Register ------------------------------ */
/**
* @brief Get all status bits
*
* @param hw Start address of the TWAI registers
* @return Status bits
*/
static inline uint32_t twai_ll_get_status(twai_dev_t *hw)
{
return hw->status_reg.val;
}
/**
* @brief Check if RX FIFO overrun status bit is set
*
* @param hw Start address of the TWAI registers
* @return Overrun status bit
*/
static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw)
{
return hw->status_reg.dos;
}
/**
* @brief Check if previously TX was successful
*
* @param hw Start address of the TWAI registers
* @return Whether previous TX was successful
*/
static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw)
{
return hw->status_reg.tcs;
}
//Todo: Add stand alone status bit check functions when necessary
/* -------------------------- Interrupt Register ---------------------------- */
/**
* @brief Get currently set interrupts
*
* Reading the interrupt registers will automatically clear all interrupts
* except for the Receive Interrupt.
*
* @param hw Start address of the TWAI registers
* @return Bit mask of set interrupts
*/
static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw)
{
return hw->interrupt_reg.val;
}
/* ----------------------- Interrupt Enable Register ------------------------ */
/**
* @brief Set which interrupts are enabled
*
* @param hw Start address of the TWAI registers
* @param Bit mask of interrupts to enable
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask)
{
hw->interrupt_enable_reg.val = intr_mask;
}
/* ------------------------ Bus Timing Registers --------------------------- */
/**
* @brief Set bus timing
*
* @param hw Start address of the TWAI registers
* @param brp Baud Rate Prescaler
* @param sjw Synchronization Jump Width
* @param tseg1 Timing Segment 1
* @param tseg2 Timing Segment 2
* @param triple_sampling Triple Sampling enable/disable
*
* @note Must be called in reset mode
* @note ESP32S2 brp can be any even number between 2 to 32768
*/
static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t sjw, uint32_t tseg1, uint32_t tseg2, bool triple_sampling)
{
hw->bus_timing_0_reg.brp = (brp / 2) - 1;
hw->bus_timing_0_reg.sjw = sjw - 1;
hw->bus_timing_1_reg.tseg1 = tseg1 - 1;
hw->bus_timing_1_reg.tseg2 = tseg2 - 1;
hw->bus_timing_1_reg.sam = triple_sampling;
}
/* ----------------------------- ALC Register ------------------------------- */
/**
* @brief Clear Arbitration Lost Capture Register
*
* Reading the ALC register rearms the Arbitration Lost Interrupt
*
* @param hw Start address of the TWAI registers
*/
static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw)
{
(void)hw->arbitration_lost_captue_reg.val;
//Todo: Decode ALC register
}
/* ----------------------------- ECC Register ------------------------------- */
/**
* @brief Clear Error Code Capture register
*
* Reading the ECC register rearms the Bus Error Interrupt
*
* @param hw Start address of the TWAI registers
*/
static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw)
{
(void)hw->error_code_capture_reg.val;
//Todo: Decode error code capture
}
/* ----------------------------- EWL Register ------------------------------- */
/**
* @brief Set Error Warning Limit
*
* @param hw Start address of the TWAI registers
* @param ewl Error Warning Limit
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl)
{
hw->error_warning_limit_reg.ewl = ewl;
}
/**
* @brief Get Error Warning Limit
*
* @param hw Start address of the TWAI registers
* @return Error Warning Limit
*/
static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw)
{
return hw->error_warning_limit_reg.val;
}
/* ------------------------ RX Error Count Register ------------------------- */
/**
* @brief Get RX Error Counter
*
* @param hw Start address of the TWAI registers
* @return REC value
*
* @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS
* OFF condition automatically sets the REC to 0.
*/
static inline uint32_t twai_ll_get_rec(twai_dev_t *hw)
{
return hw->rx_error_counter_reg.val;
}
/**
* @brief Set RX Error Counter
*
* @param hw Start address of the TWAI registers
* @param rec REC value
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec)
{
hw->rx_error_counter_reg.rxerr = rec;
}
/* ------------------------ TX Error Count Register ------------------------- */
/**
* @brief Get TX Error Counter
*
* @param hw Start address of the TWAI registers
* @return TEC value
*
* @note A BUS OFF condition will automatically set this to 128
*/
static inline uint32_t twai_ll_get_tec(twai_dev_t *hw)
{
return hw->tx_error_counter_reg.val;
}
/**
* @brief Set TX Error Counter
*
* @param hw Start address of the TWAI registers
* @param tec TEC value
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec)
{
hw->tx_error_counter_reg.txerr = tec;
}
/* ---------------------- Acceptance Filter Registers ----------------------- */
/**
* @brief Set Acceptance Filter
* @param hw Start address of the TWAI registers
* @param code Acceptance Code
* @param mask Acceptance Mask
* @param single_filter Whether to enable single filter mode
*
* @note Must be called in reset mode
*/
static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_t mask, bool single_filter)
{
uint32_t code_swapped = __builtin_bswap32(code);
uint32_t mask_swapped = __builtin_bswap32(mask);
for (int i = 0; i < 4; i++) {
hw->acceptance_filter.acr[i].byte = ((code_swapped >> (i * 8)) & 0xFF);
hw->acceptance_filter.amr[i].byte = ((mask_swapped >> (i * 8)) & 0xFF);
}
hw->mode_reg.afm = single_filter;
}
/* ------------------------- TX/RX Buffer Registers ------------------------- */
/**
* @brief Copy a formatted TWAI frame into TX buffer for transmission
*
* @param hw Start address of the TWAI registers
* @param tx_frame Pointer to formatted frame
*
* @note Call twai_ll_format_frame_buffer() to format a frame
*/
static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *tx_frame)
{
//Copy formatted frame into TX buffer
for (int i = 0; i < 13; i++) {
hw->tx_rx_buffer[i].val = tx_frame->bytes[i];
}
}
/**
* @brief Copy a received frame from the RX buffer for parsing
*
* @param hw Start address of the TWAI registers
* @param rx_frame Pointer to store formatted frame
*
* @note Call twai_ll_prase_frame_buffer() to parse the formatted frame
*/
static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *rx_frame)
{
//Copy RX buffer registers into frame
for (int i = 0; i < 13; i++) {
rx_frame->bytes[i] = hw->tx_rx_buffer[i].byte;
}
}
/**
* @brief Format contents of a TWAI frame into layout of TX Buffer
*
* 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.
*
* @param[in] 11bit or 29bit ID
* @param[in] dlc Data length code
* @param[in] data Pointer to an 8 byte array containing data. NULL if no data
* @param[in] format Type of TWAI frame
* @param[in] single_shot Frame will not be retransmitted on failure
* @param[in] self_rx Frame will also be simultaneously received
* @param[out] tx_frame Pointer to store formatted frame
*/
static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data,
uint32_t flags, twai_ll_frame_buffer_t *tx_frame)
{
bool is_extd = flags & TWAI_MSG_FLAG_EXTD;
bool is_rtr = flags & TWAI_MSG_FLAG_RTR;
//Set frame information
tx_frame->dlc = dlc;
tx_frame->frame_format = is_extd;
tx_frame->rtr = is_rtr;
tx_frame->self_reception = (flags & TWAI_MSG_FLAG_SELF) ? 1 : 0;
tx_frame->single_shot = (flags & TWAI_MSG_FLAG_SS) ? 1 : 0;
//Set ID. The ID registers are big endian and left aligned, therefore a bswap will be required
if (is_extd) {
uint32_t id_temp = __builtin_bswap32((id & TWAI_EXTD_ID_MASK) << 3); //((id << 3) >> 8*(3-i))
for (int i = 0; i < 4; i++) {
tx_frame->extended.id[i] = (id_temp >> (8 * i)) & 0xFF;
}
} else {
uint32_t id_temp = __builtin_bswap16((id & TWAI_STD_ID_MASK) << 5); //((id << 5) >> 8*(1-i))
for (int i = 0; i < 2; i++) {
tx_frame->standard.id[i] = (id_temp >> (8 * i)) & 0xFF;
}
}
uint8_t *data_buffer = (is_extd) ? tx_frame->extended.data : tx_frame->standard.data;
if (!is_rtr) { //Only copy data if the frame is a data frame (i.e not a remote frame)
for (int i = 0; (i < dlc) && (i < TWAI_FRAME_MAX_DLC); i++) {
data_buffer[i] = data[i];
}
}
}
/**
* @brief Parse formatted TWAI frame (RX Buffer Layout) into its constituent contents
*
* @param[in] rx_frame Pointer to formatted frame
* @param[out] id 11 or 29bit ID
* @param[out] dlc Data length code
* @param[out] data Data. Left over bytes set to 0.
* @param[out] format Type of TWAI frame
*/
static inline void twai_ll_prase_frame_buffer(twai_ll_frame_buffer_t *rx_frame, uint32_t *id, uint8_t *dlc,
uint8_t *data, uint32_t *flags)
{
//Copy frame information
*dlc = rx_frame->dlc;
uint32_t flags_temp = 0;
flags_temp |= (rx_frame->frame_format) ? TWAI_MSG_FLAG_EXTD : 0;
flags_temp |= (rx_frame->rtr) ? TWAI_MSG_FLAG_RTR : 0;
flags_temp |= (rx_frame->dlc > TWAI_FRAME_MAX_DLC) ? TWAI_MSG_FLAG_DLC_NON_COMP : 0;
*flags = flags_temp;
//Copy ID. The ID registers are big endian and left aligned, therefore a bswap will be required
if (rx_frame->frame_format) {
uint32_t id_temp = 0;
for (int i = 0; i < 4; i++) {
id_temp |= rx_frame->extended.id[i] << (8 * i);
}
id_temp = __builtin_bswap32(id_temp) >> 3; //((byte[i] << 8*(3-i)) >> 3)
*id = id_temp & TWAI_EXTD_ID_MASK;
} else {
uint32_t id_temp = 0;
for (int i = 0; i < 2; i++) {
id_temp |= rx_frame->standard.id[i] << (8 * i);
}
id_temp = __builtin_bswap16(id_temp) >> 5; //((byte[i] << 8*(1-i)) >> 5)
*id = id_temp & TWAI_STD_ID_MASK;
}
uint8_t *data_buffer = (rx_frame->frame_format) ? rx_frame->extended.data : rx_frame->standard.data;
//Only copy data if the frame is a data frame (i.e. not a remote frame)
int data_length = (rx_frame->rtr) ? 0 : ((rx_frame->dlc > TWAI_FRAME_MAX_DLC) ? TWAI_FRAME_MAX_DLC : rx_frame->dlc);
for (int i = 0; i < data_length; i++) {
data[i] = data_buffer[i];
}
//Set remaining bytes of data to 0
for (int i = data_length; i < TWAI_FRAME_MAX_DLC; i++) {
data[i] = 0;
}
}
/* ----------------------- RX Message Count Register ------------------------ */
/**
* @brief Get RX Message Counter
*
* @param hw Start address of the TWAI registers
* @return RX Message Counter
*/
static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw)
{
return hw->rx_message_counter_reg.val;
}
/* ------------------------- Clock Divider Register ------------------------- */
/**
* @brief Set CLKOUT Divider and enable/disable
*
* Configure CLKOUT. CLKOUT is a pre-scaled version of APB CLK. Divider can be
* 1, or any even number from 2 to 490. Set the divider to 0 to disable CLKOUT.
*
* @param hw Start address of the TWAI registers
* @param divider Divider for CLKOUT (any even number from 2 to 490). Set to 0 to disable CLKOUT
*/
static inline void twai_ll_set_clkout(twai_dev_t *hw, uint32_t divider)
{
if (divider >= 2 && divider <= 490) {
hw->clock_divider_reg.co = 0;
hw->clock_divider_reg.cd = (divider / 2) - 1;
} else if (divider == 1) {
//Setting the divider reg to max value (255) means a divider of 1
hw->clock_divider_reg.co = 0;
hw->clock_divider_reg.cd = 255;
} else {
hw->clock_divider_reg.co = 1;
hw->clock_divider_reg.cd = 0;
}
}
#ifdef __cplusplus
}
#endif

View file

@ -1,144 +0,0 @@
// Copyright 2015-2019 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.
//Todo: Place the implementation of all common HAL functions here
#include <stddef.h>
#include "hal/can_hal.h"
//Default values written to various registers on initialization
#define CAN_HAL_INIT_TEC 0
#define CAN_HAL_INIT_REC 0
#define CAN_HAL_INIT_EWL 96
bool can_hal_init(can_hal_context_t *hal_ctx)
{
//Initialize HAL context
hal_ctx->dev = &CAN;
//Initialize CAN controller, and set default values to registers
if (!can_ll_enter_reset_mode(hal_ctx->dev)) { //Must enter reset mode to write to config registers
return false;
}
can_ll_enable_extended_reg_layout(hal_ctx->dev); //Set PeliCAN address layout
can_ll_set_mode(hal_ctx->dev, CAN_MODE_LISTEN_ONLY); //Freeze REC by changing to LOM mode
//Both TEC and REC should start at 0
can_ll_set_tec(hal_ctx->dev, CAN_HAL_INIT_TEC);
can_ll_set_rec(hal_ctx->dev, CAN_HAL_INIT_REC);
can_ll_set_err_warn_lim(hal_ctx->dev, CAN_HAL_INIT_EWL); //Set default value of for EWL
return true;
}
void can_hal_deinit(can_hal_context_t *hal_ctx)
{
//Clear any pending registers
(void) can_ll_get_and_clear_intrs(hal_ctx->dev);
can_ll_set_enabled_intrs(hal_ctx->dev, 0);
can_ll_clear_arb_lost_cap(hal_ctx->dev);
can_ll_clear_err_code_cap(hal_ctx->dev);
hal_ctx->dev = NULL;
}
void can_hal_configure(can_hal_context_t *hal_ctx, const can_timing_config_t *t_config, const can_filter_config_t *f_config, uint32_t intr_mask, uint32_t clkout_divider)
{
//Configure bus timing, acceptance filter, CLKOUT, and interrupts
can_ll_set_bus_timing(hal_ctx->dev, t_config->brp, t_config->sjw, t_config->tseg_1, t_config->tseg_2, t_config->triple_sampling);
can_ll_set_acc_filter(hal_ctx->dev, f_config->acceptance_code, f_config->acceptance_mask, f_config->single_filter);
can_ll_set_clkout(hal_ctx->dev, clkout_divider);
can_ll_set_enabled_intrs(hal_ctx->dev, intr_mask);
(void) can_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts
}
bool can_hal_start(can_hal_context_t *hal_ctx, can_mode_t mode)
{
can_ll_set_mode(hal_ctx->dev, mode); //Set operating mode
//Todo: Check if this can be removed
(void) can_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts
return can_ll_exit_reset_mode(hal_ctx->dev); //Return false if failed to exit reset mode
}
bool can_hal_stop(can_hal_context_t *hal_ctx)
{
if (!can_ll_enter_reset_mode(hal_ctx->dev)) {
return false;
}
//Todo: Check if this can be removed
(void) can_ll_get_and_clear_intrs(hal_ctx->dev);
can_ll_set_mode(hal_ctx->dev, CAN_MODE_LISTEN_ONLY); //Freeze REC by changing to LOM mode
return true;
}
uint32_t can_hal_decode_interrupt_events(can_hal_context_t *hal_ctx, bool bus_recovering)
{
uint32_t events = 0;
//Read interrupt, status
uint32_t interrupts = can_ll_get_and_clear_intrs(hal_ctx->dev);
uint32_t status = can_ll_get_status(hal_ctx->dev);
uint32_t tec = can_ll_get_tec(hal_ctx->dev);
uint32_t rec = can_ll_get_rec(hal_ctx->dev);
//Receive Interrupt set whenever RX FIFO is not empty
if (interrupts & CAN_LL_INTR_RI) {
events |= CAN_HAL_EVENT_RX_BUFF_FRAME;
}
//Transmit interrupt set whenever TX buffer becomes free
if (interrupts & CAN_LL_INTR_TI) {
events |= CAN_HAL_EVENT_TX_BUFF_FREE;
}
//Error Warning Interrupt set whenever Error or Bus Status bit changes
if (interrupts & CAN_LL_INTR_EI) {
if (status & CAN_LL_STATUS_BS) {
//Currently in BUS OFF state
//EWL is exceeded, thus must have entered BUS OFF
//Below EWL. Therefore TEC is counting down in bus recovery
events |= (status & CAN_LL_STATUS_ES) ? CAN_HAL_EVENT_BUS_OFF : CAN_HAL_EVENT_BUS_RECOV_PROGRESS;
} else {
//Not in BUS OFF
events |= (status & CAN_LL_STATUS_ES) ? CAN_HAL_EVENT_ABOVE_EWL : //Just Exceeded EWL
((bus_recovering) ? //If previously undergoing bus recovery
CAN_HAL_EVENT_BUS_RECOV_CPLT :
CAN_HAL_EVENT_BELOW_EWL);
}
}
//Error Passive Interrupt on transition from error active to passive or vice versa
if (interrupts & CAN_LL_INTR_EPI) {
events |= (tec >= CAN_ERR_PASS_THRESH || rec >= CAN_ERR_PASS_THRESH) ? CAN_HAL_EVENT_ERROR_PASSIVE : CAN_HAL_EVENT_ERROR_ACTIVE;
}
//Arbitration Lost Interrupt triggered on losing arbitration
if (interrupts & CAN_LL_INTR_ALI) {
events |= CAN_HAL_EVENT_ARB_LOST;
}
//Bus error interrupt triggered on a bus error (e.g. bit, ACK, stuff etc)
if (interrupts & CAN_LL_INTR_BEI) {
events |= CAN_HAL_EVENT_BUS_ERR;
}
return events;
}
void can_hal_set_tx_buffer_and_transmit(can_hal_context_t *hal_ctx, can_hal_frame_t *tx_frame)
{
//Copy frame into tx buffer
can_ll_set_tx_buffer(hal_ctx->dev, tx_frame);
//Hit the send command
if (tx_frame->self_reception) {
if (tx_frame->single_shot) {
can_ll_set_cmd_self_rx_single_shot(hal_ctx->dev);
} else {
can_ll_set_cmd_self_rx_request(hal_ctx->dev);
}
} else if (tx_frame->single_shot){
can_ll_set_cmd_tx_single_shot(hal_ctx->dev);
} else {
can_ll_set_cmd_tx(hal_ctx->dev);
}
}

View file

@ -0,0 +1,147 @@
// Copyright 2015-2019 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.
//Todo: Place the implementation of all common HAL functions here
#include <stddef.h>
#include "hal/twai_hal.h"
//Default values written to various registers on initialization
#define TWAI_HAL_INIT_TEC 0
#define TWAI_HAL_INIT_REC 0
#define TWAI_HAL_INIT_EWL 96
bool twai_hal_init(twai_hal_context_t *hal_ctx)
{
//Initialize HAL context
hal_ctx->dev = &TWAI;
//Initialize TWAI controller, and set default values to registers
if (!twai_ll_enter_reset_mode(hal_ctx->dev)) { //Must enter reset mode to write to config registers
return false;
}
#ifdef TWAI_SUPPORT_MULTI_ADDRESS_LAYOUT
twai_ll_enable_extended_reg_layout(hal_ctx->dev); //Changes the address layout of the registers
#endif
twai_ll_set_mode(hal_ctx->dev, TWAI_MODE_LISTEN_ONLY); //Freeze REC by changing to LOM mode
//Both TEC and REC should start at 0
twai_ll_set_tec(hal_ctx->dev, TWAI_HAL_INIT_TEC);
twai_ll_set_rec(hal_ctx->dev, TWAI_HAL_INIT_REC);
twai_ll_set_err_warn_lim(hal_ctx->dev, TWAI_HAL_INIT_EWL); //Set default value of for EWL
return true;
}
void twai_hal_deinit(twai_hal_context_t *hal_ctx)
{
//Clear any pending registers
(void) twai_ll_get_and_clear_intrs(hal_ctx->dev);
twai_ll_set_enabled_intrs(hal_ctx->dev, 0);
twai_ll_clear_arb_lost_cap(hal_ctx->dev);
twai_ll_clear_err_code_cap(hal_ctx->dev);
hal_ctx->dev = NULL;
}
void twai_hal_configure(twai_hal_context_t *hal_ctx, const twai_timing_config_t *t_config, const twai_filter_config_t *f_config, uint32_t intr_mask, uint32_t clkout_divider)
{
//Configure bus timing, acceptance filter, CLKOUT, and interrupts
twai_ll_set_bus_timing(hal_ctx->dev, t_config->brp, t_config->sjw, t_config->tseg_1, t_config->tseg_2, t_config->triple_sampling);
twai_ll_set_acc_filter(hal_ctx->dev, f_config->acceptance_code, f_config->acceptance_mask, f_config->single_filter);
twai_ll_set_clkout(hal_ctx->dev, clkout_divider);
twai_ll_set_enabled_intrs(hal_ctx->dev, intr_mask);
(void) twai_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts
}
bool twai_hal_start(twai_hal_context_t *hal_ctx, twai_mode_t mode)
{
twai_ll_set_mode(hal_ctx->dev, mode); //Set operating mode
//Todo: Check if this can be removed
(void) twai_ll_get_and_clear_intrs(hal_ctx->dev); //Clear any latched interrupts
return twai_ll_exit_reset_mode(hal_ctx->dev); //Return false if failed to exit reset mode
}
bool twai_hal_stop(twai_hal_context_t *hal_ctx)
{
if (!twai_ll_enter_reset_mode(hal_ctx->dev)) {
return false;
}
//Todo: Check if this can be removed
(void) twai_ll_get_and_clear_intrs(hal_ctx->dev);
twai_ll_set_mode(hal_ctx->dev, TWAI_MODE_LISTEN_ONLY); //Freeze REC by changing to LOM mode
return true;
}
uint32_t twai_hal_decode_interrupt_events(twai_hal_context_t *hal_ctx, bool bus_recovering)
{
uint32_t events = 0;
//Read interrupt, status
uint32_t interrupts = twai_ll_get_and_clear_intrs(hal_ctx->dev);
uint32_t status = twai_ll_get_status(hal_ctx->dev);
uint32_t tec = twai_ll_get_tec(hal_ctx->dev);
uint32_t rec = twai_ll_get_rec(hal_ctx->dev);
//Receive Interrupt set whenever RX FIFO is not empty
if (interrupts & TWAI_LL_INTR_RI) {
events |= TWAI_HAL_EVENT_RX_BUFF_FRAME;
}
//Transmit interrupt set whenever TX buffer becomes free
if (interrupts & TWAI_LL_INTR_TI) {
events |= TWAI_HAL_EVENT_TX_BUFF_FREE;
}
//Error Warning Interrupt set whenever Error or Bus Status bit changes
if (interrupts & TWAI_LL_INTR_EI) {
if (status & TWAI_LL_STATUS_BS) {
//Currently in BUS OFF state
//EWL is exceeded, thus must have entered BUS OFF
//Below EWL. Therefore TEC is counting down in bus recovery
//Todo: Check if BUS Recov can be removed for esp32s2
events |= (status & TWAI_LL_STATUS_ES) ? TWAI_HAL_EVENT_BUS_OFF : TWAI_HAL_EVENT_BUS_RECOV_PROGRESS;
} else {
//Not in BUS OFF
events |= (status & TWAI_LL_STATUS_ES) ? TWAI_HAL_EVENT_ABOVE_EWL : //Just Exceeded EWL
((bus_recovering) ? //If previously undergoing bus recovery
TWAI_HAL_EVENT_BUS_RECOV_CPLT :
TWAI_HAL_EVENT_BELOW_EWL);
}
}
//Error Passive Interrupt on transition from error active to passive or vice versa
if (interrupts & TWAI_LL_INTR_EPI) {
events |= (tec >= TWAI_ERR_PASS_THRESH || rec >= TWAI_ERR_PASS_THRESH) ? TWAI_HAL_EVENT_ERROR_PASSIVE : TWAI_HAL_EVENT_ERROR_ACTIVE;
}
//Arbitration Lost Interrupt triggered on losing arbitration
if (interrupts & TWAI_LL_INTR_ALI) {
events |= TWAI_HAL_EVENT_ARB_LOST;
}
//Bus error interrupt triggered on a bus error (e.g. bit, ACK, stuff etc)
if (interrupts & TWAI_LL_INTR_BEI) {
events |= TWAI_HAL_EVENT_BUS_ERR;
}
return events;
}
void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_frame_t *tx_frame)
{
//Copy frame into tx buffer
twai_ll_set_tx_buffer(hal_ctx->dev, tx_frame);
//Hit the send command
if (tx_frame->self_reception) {
if (tx_frame->single_shot) {
twai_ll_set_cmd_self_rx_single_shot(hal_ctx->dev);
} else {
twai_ll_set_cmd_self_rx_request(hal_ctx->dev);
}
} else if (tx_frame->single_shot){
twai_ll_set_cmd_tx_single_shot(hal_ctx->dev);
} else {
twai_ll_set_cmd_tx(hal_ctx->dev);
}
}

View file

@ -86,8 +86,6 @@ INPUT = \
##
## Peripherals - API Reference
##
$(IDF_PATH)/components/driver/include/driver/can.h \
$(IDF_PATH)/components/soc/include/hal/can_types.h \
$(IDF_PATH)/components/driver/include/driver/dac.h \
$(IDF_PATH)/components/driver/include/driver/gpio.h \
$(IDF_PATH)/components/driver/include/driver/rtc_io.h \
@ -107,6 +105,7 @@ INPUT = \
$(IDF_PATH)/components/driver/esp32s2/include/driver/temp_sensor.h \
$(IDF_PATH)/components/driver/include/driver/timer.h \
$(IDF_PATH)/components/driver/include/driver/touch_sensor_common.h \
$(IDF_PATH)/components/driver/include/driver/twai.h \
$(IDF_PATH)/components/driver/include/driver/adc_common.h \
$(IDF_PATH)/components/driver/include/driver/uart.h \
$(IDF_PATH)/components/esp_adc_cal/include/esp_adc_cal.h \
@ -125,6 +124,7 @@ INPUT = \
$(IDF_PATH)/components/soc/include/hal/adc_types.h \
$(IDF_PATH)/components/soc/include/hal/gpio_types.h \
$(IDF_PATH)/components/soc/include/hal/uart_types.h \
$(IDF_PATH)/components/soc/include/hal/twai_types.h \
$(IDF_PATH)/components/soc/include/hal/touch_sensor_types.h \
$(IDF_PATH)/components/soc/soc/esp32/include/soc/adc_channel.h \
$(IDF_PATH)/components/soc/soc/esp32/include/soc/dac_channel.h \

View file

@ -1,6 +1,6 @@
#Diagram of CAN Controller Acceptance Filter (Dual) Configuration
#Diagram of Acceptance Filter (Dual) Configuration
packetdiag can_acceptance_filter_dual {
packetdiag acceptance_filter_dual {
node_width = 30
node_height = 35
default_fontsize = 15

View file

@ -1,6 +1,6 @@
#Diagram of CAN Controller Acceptance Filter (Single) Configuration
#Diagram of Acceptance Filter (Single) Configuration
packetdiag can_acceptance_filter_single {
packetdiag acceptance_filter_single {
node_width = 30
node_height = 35
default_fontsize = 15

View file

@ -1,6 +1,6 @@
#Example of bit timing configuration for 500KBPS
packetdiag can_bit_timing_diag{
packetdiag bit_timing_diag{
node_width = 40
node_height = 35
default_fontsize = 15

View file

@ -1,12 +1,12 @@
#Diagram of CAN controller signal lines
#Diagram of TWAI controller signal lines
blockdiag can_controller_signals_diagram {
blockdiag controller_signals_diagram {
orientation = portrait;
span_width = 80;
#Column 1 nodes
can[label = "CAN Controller", fontsize = 15, shape = roundedbox];
twai[label = "TWAI Controller", fontsize = 15, shape = roundedbox];
#Column 2 nodes
tx[label = "TX", shape = endpoint];
@ -25,7 +25,7 @@ blockdiag can_controller_signals_diagram {
color = none;
#Group column 1 nodes vertically
can;
twai;
}
group {
orientation = portrait;
@ -45,13 +45,13 @@ blockdiag can_controller_signals_diagram {
hide1; hide2; hide3; hide4;
}
can -> tx [folded];
can -> rx [folded];
can -> bus_off [folded];
can -> clkout [folded];
twai -> tx [folded];
twai -> rx [folded, dir = none];
twai -> bus_off [folded];
twai -> clkout [folded];
tx -> hide1 [folded];
rx -> hide2 [folded];
rx <- hide2 [folded];
bus_off -> hide3 [folded, label = "Optional"];
clkout -> hide4 [folded, label = "Optional"];
}

View file

@ -1,6 +1,6 @@
#State transition diagram of the CAN Driver
#State transition diagram of the TWAI Driver
blockdiag can_state_transition_diagram {
blockdiag state_transition_diagram {
orientation = landscape;
default_fontsize = 18;

View file

@ -1,503 +0,0 @@
Controller Area Network (CAN)
=============================
.. -------------------------------- Overview -----------------------------------
Overview
--------
.. only:: esp32s2
.. note::
The CAN driver is not updated for {IDF_TARGET_NAME} and is temporarily disabled.
.. only:: esp32
The {IDF_TARGET_NAME}'s peripherals contains a CAN Controller that supports Standard Frame Format (11-bit ID) and Extended Frame Format (29-bit ID) of the CAN2.0B specification.
.. warning::
The {IDF_TARGET_NAME} 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**.
.. only:: esp32
If the ESP32 is a revision 2 or later chip, the ``brp`` will also support **any multiple of 4 from 132 to 256**, and can be enabled by setting the :ref:`CONFIG_ESP32_REV_MIN` to revision 2 or higher.
.. 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 initializers are provided by the CAN driver.
- ``CAN_TIMING_CONFIG_12_5KBITS()``
- ``CAN_TIMING_CONFIG_16KBITS()``
- ``CAN_TIMING_CONFIG_20KBITS()``
- ``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()``
.. only::esp32
.. note::
The macro initializers for 12.5K, 16K, and 20K bit rates are only available
for ESP32 revision 2 or later.
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 various bit field members of the :cpp:type:`can_message_t` structure. These bit field members 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. These bit field members can also be toggled using the the `flags` member of :cpp:type:`can_message_t` and the following message 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 not be |
| | retransmitted upon error or loss of |
| | arbitration). Unused for received message. |
+-------------------------------+----------------------------------------------+
| ``CAN_MSG_FLAG_SELF`` | Transmit message using Self Reception |
| | Request (Transmitted message will also |
| | received by the same node). Unused for |
| | received message. |
+-------------------------------+----------------------------------------------+
| ``CAN_MSG_FLAG_DLC_NON_COMP`` | Message's Data length code is larger than 8. |
| | This will break compliance with CAN2.0B |
+-------------------------------+----------------------------------------------+
| ``CAN_MSG_FLAG_NONE`` | Clears all bit fields. Equivalent to a |
| | Standard Frame Format (11bit ID) Data Frame. |
+-------------------------------+----------------------------------------------+
.. -------------------------------- 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.extd = 1;
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.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.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;
}
Multiple ID Filter Configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The acceptance mask in :cpp:type:`can_filter_config_t` can be configured such that two or more IDs will be accepted for a single filter. For a particular filter to accept multiple IDs, the conflicting bit positions amongst the IDs must be set in the acceptance mask. The acceptance code can be set to any one of the IDs.
The following example shows how the calculate the acceptance mask given multiple IDs::
ID1 = 11'b101 1010 0000
ID2 = 11'b101 1010 0001
ID3 = 11'b101 1010 0100
ID4 = 11'b101 1010 1000
//Acceptance Mask
MASK = 11'b000 0000 1101
Application Examples
^^^^^^^^^^^^^^^^^^^^
**Network Example:** The CAN Network example demonstrates communication between two {IDF_TARGET_NAME}s 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:`peripherals/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:`peripherals/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:`peripherals/can/can_self_test`.
.. ---------------------------- API Reference ----------------------------------
API Reference
-------------
.. include-build-file:: inc/can_types.inc
.. include-build-file:: inc/can.inc

View file

@ -8,7 +8,6 @@ Peripherals API
ADC <adc>
CAN <can>
DAC <dac>
GPIO (including RTC low power I/O) <gpio>
:esp32s2: HMAC <hmac>
@ -30,6 +29,7 @@ Peripherals API
:esp32s2: Temp sensor <temp_sensor>
Timer <timer>
Touch Sensor <touch_pad>
TWAI <twai>
UART <uart>

View file

@ -0,0 +1,507 @@
TWAI
====
.. -------------------------------- Overview -----------------------------------
Overview
--------
The Two-Wire Automotive Interface (TWAI) is a real-time serial communication protocol suited for automotive and industrial applications. It is compatible with ISO11898-1 Classical frames, thus can support Standard Frame Format (11-bit ID) and Extended Frame Format (29-bit ID). The {IDF_TARGET_NAME}'s peripherals contains a TWAI controller that can be configured to communicate on a TWAI bus via an external transceiver.
.. warning::
The TWAI controller is not compatible with ISO11898-1 FD Format frames, and will interpret such frames as errors.
This programming guide is split into the following sections:
1. :ref:`twai-protocol-summary`
2. :ref:`signals-lines-and-transceiver`
3. :ref:`configuration`
4. :ref:`driver-operation`
5. :ref:`examples`
.. --------------------------- Basic TWAI Concepts -----------------------------
.. _twai-protocol-summary:
TWAI Protocol Summary
---------------------
The TWAI is a multi-master, multi-cast, asynchronous, serial communication protocol. TWAI also supports error detection and signalling, and inbuilt message prioritization.
**Multi-master:** Any node on the bus can initiate the transfer of a message.
**Multi-cast:** When a node transmits a message, all nodes on the bus will receive the message (i.e., broadcast) thus ensuring data consistency across all nodes. However, some nodes can selectively choose which messages to accept via the use of acceptance filtering (multi-cast).
**Asynchronous:** The bus does not contain a clock signal. All nodes on the bus operate at the same bit rate and synchronize using the edges of the bits transmitted on the bus.
**Error Detection and Signalling:** Every node will constantly monitor the bus. When any node detects an error, it will signal the detection 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:** Messages contain an ID field. If two or more nodes attempt to transmit simultaneously, the node transmitting the message with the lower ID value will win arbitration of the bus. All other nodes will become receivers ensuring that there is at most one transmitter at any time.
TWAI Messages
^^^^^^^^^^^^^
TWAI Messages are split into Data Frames and Remote Frames. Data Frames are used to deliver a data payload to other nodes, whereas a Remote Frame is used to request a Data Frame from other nodes (other nodes can optionally respond with a Data Frame). Data and Remote Frames have two frame formats known as **Extended Frame** and **Standard Frame** which contain a 29-bit ID and an 11-bit ID respectively. A TWAI message consists of the following fields:
- 29-bit or 11-bit ID: Determines the priority of the message (lower value has higher priority).
- Data Length Code (DLC) between 0 to 8: Indicates the size (in bytes) of the data payload for a Data Frame, or the amount of data to request for a Remote Frame.
- Up to 8 bytes of data for a Data Frame (should match DLC).
Error States and Counters
^^^^^^^^^^^^^^^^^^^^^^^^^
The TWAI protocol implements a feature known as "fault confinement" where a persistently erroneous node will eventually eliminate itself form the bus. This is implemented by requiring every 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 incremented and decremented according to a set of rules (where the counters increase on an error, and decrease on a successful message transmission/reception). The values of the counters are used to determine a node's **error state**, namely **Error Active**, **Error Passive**, and **Bus-Off**.
**Error Active:** A node is Error Active when **both TEC and REC are less than 128** and indicates that the node is operating normally. Error Active nodes are allowed to participate in bus communications, and will actively signal the detection of any errors by automatically transmitting an **Active Error Flag** over the bus.
**Error Passive:** A 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 bus communications, but will instead transmit a **Passive Error Flag** upon detection of an error.
**Bus-Off:** A node becomes Bus-Off when the **TEC becomes greater than or equal to 256**. A Bus-Off node is unable influence the bus in any manner (essentially disconnected from the bus) thus eliminating itself from the bus. A node will remain in the Bus-Off state until it undergoes bus-off recovery.
.. ---------------------- Signal Lines and Transceiver -------------------------
.. _signals-lines-and-transceiver:
Signals Lines and Transceiver
-----------------------------
The TWAI controller does not contain a integrated transceiver. Therefore, to connect the TWAI controller to a TWAI bus, **an external transceiver is required**. The type of external transceiver used should depend on the application's physical layer specification (e.g. using SN65HVD23x transceivers for ISO 11898-2 compatibility).
The TWAI 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 the {IDF_TARGET_NAME}'s GPIO pads.
.. blockdiag:: ../../../_static/diagrams/twai/controller_signals.diag
:caption: Signal lines of the TWAI controller
:align: center
**TX and RX:** The TX and RX signal lines are required to interface with an external 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 TWAI 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 controller's source clock (APB Clock).
.. note::
An external transceiver **must internally loopback the TX to RX** 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 TWAI controller to interpret differences in logic levels between the two signal lines as a loss in arbitration or a bit error.
.. ------------------------------ Configuration --------------------------------
.. _configuration:
Driver Configuration
--------------------
This section covers how to configure the TWAI driver.
Operating Modes
^^^^^^^^^^^^^^^
The TWAI driver supports the following modes of operations:
**Normal Mode:** The normal operating mode allows the TWAI controller to take part in bus activities such as transmitting and receiving messages/error frames. Acknowledgement from another node is required when transmitting a message.
**No Ack Mode:** The No Acknowledgement mode is similar to normal mode, however acknowledgements are not required for a message transmission to be considered successful. This mode is useful when self testing the TWAI controller (loopback of transmissions).
**Listen Only Mode:** This mode will prevent the TWAI controller from influencing the bus. Therefore, transmission of messages/acknowledgement/error frames will be disabled. However the TWAI controller will still be able to receive messages but will not acknowledge the message. This mode is suited for bus monitor applications.
Alerts
^^^^^^
The TWAI driver contains an alert feature that is used to notify the application layer of certain TWAI controller or TWAI bus events. Alerts are selectively enabled when the TWAI driver is installed, but can be reconfigured during runtime by calling :cpp:func:`twai_reconfigure_alerts`. The application can then wait for any enabled alerts to occur by calling :cpp:func:`twai_read_alerts`. The TWAI driver supports the following alerts:
.. list-table:: TWAI Driver Alerts
:widths: 40 60
:header-rows: 1
* - Alert Flag
- Description
* - ``TWAI_ALERT_TX_IDLE``
- No more messages queued for transmission
* - ``TWAI_ALERT_TX_SUCCESS``
- The previous transmission was successful
* - ``TWAI_ALERT_BELOW_ERR_WARN``
- Both error counters have dropped below error warning limit
* - ``TWAI_ALERT_ERR_ACTIVE``
- TWAI controller has become error active
* - ``TWAI_ALERT_RECOVERY_IN_PROGRESS``
- TWAI controller is undergoing bus recovery
* - ``TWAI_ALERT_BUS_RECOVERED``
- TWAI controller has successfully completed bus recovery
* - ``TWAI_ALERT_ARB_LOST``
- The previous transmission lost arbitration
* - ``TWAI_ALERT_ABOVE_ERR_WARN``
- One of the error counters have exceeded the error warning limit
* - ``TWAI_ALERT_BUS_ERROR``
- A (Bit, Stuff, CRC, Form, ACK) error has occurred on the bus
* - ``TWAI_ALERT_TX_FAILED``
- The previous transmission has failed
* - ``TWAI_ALERT_RX_QUEUE_FULL``
- The RX queue is full causing a received frame to be lost
* - ``TWAI_ALERT_ERR_PASS``
- TWAI controller has become error passive
* - ``TWAI_ALERT_BUS_OFF``
- Bus-off condition occurred. TWAI controller can no longer influence bus
.. note::
The TWAI controller's **error warning limit** is used to preemptively warn the application of bus errors before the error passive state is reached. By default, the TWAI driver sets the **error warning limit** to **96**. The ``TWAI_ALERT_ABOVE_ERR_WARN`` is raised when the TEC or REC becomes larger then or equal to the error warning limit. The ``TWAI_ALERT_BELOW_ERR_WARN`` is raised when both TEC and REC return back to values below **96**.
.. note::
When enabling alerts, the ``TWAI_ALERT_AND_LOG`` flag can be used to cause the TWAI driver to log any raised alerts to UART. The ``TWAI_ALERT_ALL`` and ``TWAI_ALERT_NONE`` macros can also be used to enable/disable all alerts during configuration/reconfiguration.
Bit Timing
^^^^^^^^^^
The operating bit rate of the TWAI driver is configured using the :cpp:type:`twai_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 TWAI 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
{IDF_TARGET_MAX_BRP:default="128", esp32="128", esp32s2="32768"}
The **Baudrate Prescaler** is used to determine the period of each time quanta by dividing the TWAI controller's source clock (80 MHz APB clock). On the {IDF_TARGET_NAME}, the ``brp`` can be **any even number from 2 to {IDF_TARGET_MAX_BRP}**.
.. only:: esp32
If the ESP32 is a revision 2 or later chip, the ``brp`` will **also support any multiple of 4 from 132 to 256**, and can be enabled by setting the :ref:`CONFIG_ESP32_REV_MIN` to revision 2 or higher.
.. packetdiag:: ../../../_static/diagrams/twai/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 determine 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 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 bit rates. The following macro initializers are provided by the TWAI driver.
.. list::
- ``TWAI_TIMING_CONFIG_1MBITS()``
- ``TWAI_TIMING_CONFIG_800KBITS()``
- ``TWAI_TIMING_CONFIG_500KBITS()``
- ``TWAI_TIMING_CONFIG_250KBITS()``
- ``TWAI_TIMING_CONFIG_125KBITS()``
- ``TWAI_TIMING_CONFIG_100KBITS()``
- ``TWAI_TIMING_CONFIG_50KBITS()``
- ``TWAI_TIMING_CONFIG_25KBITS()``
:esp32s2: - ``TWAI_TIMING_CONFIG_20KBITS()``
:esp32s2: - ``TWAI_TIMING_CONFIG_16KBITS()``
:esp32s2: - ``TWAI_TIMING_CONFIG_12_5KBITS()``
:esp32s2: - ``TWAI_TIMING_CONFIG_10KBITS()``
:esp32s2: - ``TWAI_TIMING_CONFIG_5KBITS()``
:esp32s2: - ``TWAI_TIMING_CONFIG_1KBITS()``
.. only:: esp32
Revision 2 or later of the ESP32 also supports the following bit rates:
- ``TWAI_TIMING_CONFIG_20KBITS()``
- ``TWAI_TIMING_CONFIG_16KBITS()``
- ``TWAI_TIMING_CONFIG_12_5KBITS()``
Acceptance Filter
^^^^^^^^^^^^^^^^^
The TWAI controller contains a hardware acceptance filter which can be used to filter messages of a particular ID. A node that filters out a message **will not receive the message, but will still acknowledge it**. Acceptance filters can make a node more efficient by filtering out messages sent over the bus that are irrelevant to the node. The acceptance filter is configured using two 32-bit values within :cpp:type:`twai_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 TWAI 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 frame formats respectively).
.. packetdiag:: ../../../_static/diagrams/twai/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 frame formats respectively).
.. packetdiag:: ../../../_static/diagrams/twai/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:`twai_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 TWAI driver.
.. ------------------------------- TWAI Driver ---------------------------------
.. _driver-operation:
Driver Operation
----------------
The TWAI 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/twai/state_transition.diag
:caption: State transition diagram of the TWAI driver (see table below)
:align: center
.. list-table::
:widths: 20 40 40
:header-rows: 1
* - Label
- Transition
- Action/Condition
* - A
- Uninstalled -> Stopped
- :cpp:func:`twai_driver_install`
* - B
- Stopped -> Uninstalled
- :cpp:func:`twai_driver_uninstall`
* - C
- Stopped -> Running
- :cpp:func:`twai_start`
* - D
- Running -> Stopped
- :cpp:func:`twai_stop`
* - E
- Running -> Bus-Off
- Transmit Error Counter >= 256
* - F
- Bus-Off -> Uninstalled
- :cpp:func:`twai_driver_uninstall`
* - G
- Bus-Off -> Recovering
- :cpp:func:`twai_initiate_recovery`
* - H
- Recovering -> Stopped
- 128 occurrences of 11 consecutive recessive bits.
Driver States
^^^^^^^^^^^^^
**Uninstalled**: In the uninstalled state, no memory is allocated for the driver and the TWAI controller is powered OFF.
**Stopped**: In this state, the TWAI controller is powered ON and the TWAI driver has been installed. However the TWAI controller will be unable to take part in any bus activities such as transmitting, receiving, or acknowledging messages.
**Running**: In the running state, the TWAI controller is able to take part in bus activities. Therefore messages can be transmitted/received/acknowledged. Furthermore the TWAI controller will be able to transmit error frames upon detection of errors on the bus.
**Bus-Off**: The bus-off state is automatically entered when the TWAI controller's Transmit Error Counter becomes greater than or equal to 256. The bus-off state indicates the occurrence of severe errors on the bus or in the TWAI controller. Whilst in the bus-off state, the TWAI controller will be unable to take part in any bus activities. To exit the bus-off state, the TWAI controller must undergo the bus recovery process.
**Recovering**: The recovering state is entered when the TWAI controller undergoes bus recovery. The TWAI controller/TWAI driver will remain in the recovering state until the 128 occurrences of 11 consecutive recessive bits is observed on the bus.
Message Fields and Flags
^^^^^^^^^^^^^^^^^^^^^^^^
The TWAI driver distinguishes different types of messages by using the various bit field members of the :cpp:type:`twai_message_t` structure. These bit field members determine whether a message is in standard or extended format, a remote frame, and the type of transmission to use when transmitting such a message.
These bit field members can also be toggled using the the `flags` member of :cpp:type:`twai_message_t` and the following message flags:
.. list-table::
:widths: 30 70
:header-rows: 1
* - Message Flag
- Description
* - ``TWAI_MSG_FLAG_EXTD``
- Message is in Extended Frame Format (29bit ID)
* - ``TWAI_MSG_FLAG_RTR``
- Message is a Remote Frame (Remote Transmission Request)
* - ``TWAI_MSG_FLAG_SS``
- Transmit message using Single Shot Transmission (Message will not be retransmitted upon error or loss of arbitration). Unused for received message.
* - ``TWAI_MSG_FLAG_SELF``
- Transmit message using Self Reception Request (Transmitted message will also received by the same node). Unused for received message.
* - ``TWAI_MSG_FLAG_DLC_NON_COMP``
- Message's Data length code is larger than 8. This will break compliance with TWAI
* - ``TWAI_MSG_FLAG_NONE``
- Clears all bit fields. Equivalent to a Standard Frame Format (11bit ID) Data Frame.
.. -------------------------------- Examples -----------------------------------
.. _examples:
Examples
--------
Configuration & Installation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following code snippet demonstrates how to configure, install, and start the TWAI driver via the use of the various configuration structures, macro initializers, the :cpp:func:`twai_driver_install` function, and the :cpp:func:`twai_start` function.
.. code-block:: c
#include "driver/gpio.h"
#include "driver/twai.h"
void app_main()
{
//Initialize configuration structures using macro initializers
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_21, GPIO_NUM_22, TWAI_MODE_NORMAL);
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS();
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
//Install TWAI driver
if (twai_driver_install(&g_config, &t_config, &f_config) == ESP_OK) {
printf("Driver installed\n");
} else {
printf("Failed to install driver\n");
return;
}
//Start TWAI driver
if (twai_start() == ESP_OK) {
printf("Driver started\n");
} else {
printf("Failed to start driver\n");
return;
}
...
}
The usage of macro initializers is 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:`twai_message_t` type and :cpp:func:`twai_transmit` function.
.. code-block:: c
#include "driver/twai.h"
...
//Configure message to transmit
twai_message_t message;
message.identifier = 0xAAAA;
message.extd = 1;
message.data_length_code = 4;
for (int i = 0; i < 4; i++) {
message.data[i] = 0;
}
//Queue message for transmission
if (twai_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:`twai_message_t` type and :cpp:func:`twai_receive` function.
.. code-block:: c
#include "driver/twai.h"
...
//Wait for message to be received
twai_message_t message;
if (twai_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.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.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 TWAI driver alerts via the use of the :cpp:func:`twai_reconfigure_alerts` and :cpp:func:`twai_read_alerts` functions.
.. code-block:: c
#include "driver/twai.h"
...
//Reconfigure alerts to detect Error Passive and Bus-Off error states
uint32_t alerts_to_enable = TWAI_ALERT_ERR_PASS | TWAI_ALERT_BUS_OFF;
if (twai_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;
twai_read_alerts(&alerts_triggered, portMAX_DELAY);
Stop and Uninstall
^^^^^^^^^^^^^^^^^^
The following code demonstrates how to stop and uninstall the TWAI driver via the use of the :cpp:func:`twai_stop` and :cpp:func:`twai_driver_uninstall` functions.
.. code-block:: c
#include "driver/twai.h"
...
//Stop the TWAI driver
if (twai_stop() == ESP_OK) {
printf("Driver stopped\n");
} else {
printf("Failed to stop driver\n");
return;
}
//Uninstall the TWAI driver
if (twai_driver_uninstall() == ESP_OK) {
printf("Driver uninstalled\n");
} else {
printf("Failed to uninstall driver\n");
return;
}
Multiple ID Filter Configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The acceptance mask in :cpp:type:`twai_filter_config_t` can be configured such that two or more IDs will be accepted for a single filter. For a particular filter to accept multiple IDs, the conflicting bit positions amongst the IDs must be set in the acceptance mask. The acceptance code can be set to any one of the IDs.
The following example shows how the calculate the acceptance mask given multiple IDs::
ID1 = 11'b101 1010 0000
ID2 = 11'b101 1010 0001
ID3 = 11'b101 1010 0100
ID4 = 11'b101 1010 1000
//Acceptance Mask
MASK = 11'b000 0000 1101
Application Examples
^^^^^^^^^^^^^^^^^^^^
**Network Example:** The TWAI Network example demonstrates communication between two {IDF_TARGET_NAME}s using the TWAI driver API. One TWAI node acts as a network master that initiates and ceases the transfer of a data from another node acting as a network slave. The example can be found via :example:`peripherals/twai/twai_network`.
**Alert and Recovery Example:** This example demonstrates how to use the TWAI driver's alert and bus-off recovery API. The example purposely introduces errors on the bus to put the TWAI 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:`peripherals/twai/twai_alert_and_recovery`.
**Self Test Example:** This example uses the No Acknowledge Mode and Self Reception Request to cause the TWAI controller to send and simultaneously receive a series of messages. This example can be used to verify if the connections between the TWAI controller and the external transceiver are working correctly. The example can be found via :example:`peripherals/twai/twai_self_test`.
.. ---------------------------- API Reference ----------------------------------
API Reference
-------------
.. include-build-file:: inc/twai_types.inc
.. include-build-file:: inc/twai.inc

View file

@ -91,7 +91,7 @@ The following drivers will hold the ``ESP_PM_APB_FREQ_MAX`` lock while the drive
- **SPI slave**: between calls to :cpp:func:`spi_slave_initialize` and :cpp:func:`spi_slave_free`.
- **Ethernet**: between calls to :cpp:func:`esp_eth_driver_install` and :cpp:func:`esp_eth_driver_uninstall`.
- **WiFi**: between calls to :cpp:func:`esp_wifi_start` and :cpp:func:`esp_wifi_stop`. If modem sleep is enabled, the lock will be released for the periods of time when radio is disabled.
- **CAN**: between calls to :cpp:func:`can_driver_install` and :cpp:func:`can_driver_uninstall`.
- **TWAI**: between calls to :cpp:func:`twai_driver_install` and :cpp:func:`twai_driver_uninstall`.
.. only:: esp32

View file

@ -10,6 +10,7 @@ api-reference/ethernet/index api-reference/network/index
api-reference/ethernet/esp_eth api-reference/network/esp_eth
api-reference/mesh/index api-reference/network/index
api-reference/mesh/esp_mesh api-reference/network/esp_mesh
api-reference/peripherals/can api-reference/peripherals/twai
api-reference/wifi/index api-reference/network/index
api-reference/wifi/esp_now api-reference/network/esp_now
api-reference/wifi/esp_smartconfig api-reference/network/esp_smartconfig

View file

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

View file

@ -7,7 +7,6 @@
:maxdepth: 1
ADC <adc>
CAN <can>
DAC <dac>
GPIO (including RTC low power I/O) <gpio>
:esp32s2: HMAC <hmac>
@ -28,6 +27,7 @@
:esp32s2: Temp sensor <temp_sensor>
Timer <timer>
Touch Sensor <touch_pad>
TWAI <twai>
UART <uart>
本部分的 API 示例代码存放在 ESP-IDF 示例项目的 :example:`peripherals` 目录下。

View file

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

View file

@ -86,7 +86,7 @@ ESP32 支持下表中所述的三种电源管理锁。
- **SPI slave**:从调用 :cpp:func:`spi_slave_initialize`:cpp:func:`spi_slave_free` 期间。
- **Ethernet**:从调用 :cpp:func:`esp_eth_driver_install`:cpp:func:`esp_eth_driver_uninstall` 期间。
- **WiFi**:从调用 :cpp:func:`esp_wifi_start`:cpp:func:`esp_wifi_stop` 期间。如果启用了调制解调器睡眠模式,广播关闭时将释放此管理锁。
- **CAN**:从调用 :cpp:func:`can_driver_install`:cpp:func:`can_driver_uninstall` 期间。
- **TWAI**:从调用 :cpp:func:`twai_driver_install`:cpp:func:`twai_driver_uninstall` 期间。
.. only:: esp32

View file

@ -1,32 +0,0 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# 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

@ -1,21 +0,0 @@
# Need Python 3 string formatting functions
from __future__ import print_function
import ttfw_idf
# CAN Self Test Example constants
STR_EXPECT = ("CAN Alert and Recovery: Driver installed", "CAN Alert and Recovery: Driver uninstalled")
EXPECT_TIMEOUT = 20
@ttfw_idf.idf_example_test(env_tag='Example_CAN1')
def test_can_alert_and_recovery_example(env, extra_data):
dut = env.get_dut('dut1', 'examples/peripherals/can/can_alert_and_recovery', dut_class=ttfw_idf.ESP32DUT)
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

@ -1,2 +0,0 @@
idf_component_register(SRCS "can_alert_and_recovery_example_main.c"
INCLUDE_DIRS ".")

View file

@ -1,70 +0,0 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# 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

@ -1,2 +0,0 @@
idf_component_register(SRCS "can_network_example_listen_only_main.c"
INCLUDE_DIRS ".")

View file

@ -1,2 +0,0 @@
idf_component_register(SRCS "can_network_example_master_main.c"
INCLUDE_DIRS ".")

View file

@ -1,2 +0,0 @@
idf_component_register(SRCS "can_network_example_slave_main.c"
INCLUDE_DIRS ".")

View file

@ -1,36 +0,0 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# 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

@ -1,23 +0,0 @@
# Need Python 3 string formatting functions
from __future__ import print_function
import ttfw_idf
# CAN Self Test Example constants
STR_EXPECT = ("CAN Self Test: Driver installed", "CAN Self Test: Driver uninstalled")
EXPECT_TIMEOUT = 20
@ttfw_idf.idf_example_test(env_tag='Example_CAN1')
def test_can_self_test_example(env, extra_data):
# Get device under test, flash and start example. "dut1" must be defined in EnvConfig
dut = env.get_dut('dut1', 'examples/peripherals/can/can_self_test', dut_class=ttfw_idf.ESP32DUT)
dut.start_app()
for string in STR_EXPECT:
dut.expect(string, timeout=EXPECT_TIMEOUT)
if __name__ == '__main__':
test_can_self_test_example()

View file

@ -1,2 +0,0 @@
idf_component_register(SRCS "can_self_test_example_main.c"
INCLUDE_DIRS ".")

View file

@ -3,7 +3,7 @@
# project subdirectory.
#
PROJECT_NAME := can_alert_and_recovery_example
PROJECT_NAME := twai_alert_and_recovery_example
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,91 @@
# TWAI Alert and Recovery Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to use the alert and bus recovery features of the TWAI driver. The alert feature allows the TWAI driver to notify the application layer of certain TWAI driver or bus events. The bus recovery feature is used to recover the TWAI driver after it has entered the Bus-Off state. See the TWAI driver reference for more details.
## How to use example
### Hardware Required
This example requires only a single target (e.g., an ESP32 or ESP32-S2). The target must be connected to an external transceiver (e.g., a SN65HVD23X transceiver). This connection usually consists of a TX and an RX signal.
Note: If you don't have an external transceiver, this example can still be run by simply connecting the TX GPIO and RX GPIO with a jumper.
### Configure the project
* Set the target of the build (where `{IDF_TARGET}` stands for the target chip such as `eszp32` or `esp32s2`).
* Then run `menuconfig` to configure the example.
```
idf.py set-target {IDF_TARGET}
idf.py menuconfig
```
* Under `Example Configuration`, configure the pin assignments using the options `TX GPIO Number` and `RX GPIO Number` according to how the target was connected to the transceiver. By default, `TX GPIO Number` and `RX GPIO Number` are set to the following values:
* On the ESP32, `TX GPIO Number` and `RX GPIO Number` default to `21` and `22` respectively
* On the ESP32-S2, `TX GPIO Number` and `RX GPIO Number` default to `20` and `21` respectively
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (330) TWAI Alert and Recovery: Driver installed
I (340) TWAI Alert and Recovery: Driver started
I (340) TWAI Alert and Recovery: Starting transmissions
W (350) TWAI Alert and Recovery: Trigger TX errors in 3
W (1350) TWAI Alert and Recovery: Trigger TX errors in 2
W (2350) TWAI Alert and Recovery: Trigger TX errors in 1
I (3350) TWAI Alert and Recovery: Trigger errors
I (3650) TWAI Alert and Recovery: Surpassed Error Warning Limit
I (3650) TWAI Alert and Recovery: Entered Error Passive state
I (4300) TWAI Alert and Recovery: Bus Off state
W (4300) TWAI Alert and Recovery: Initiate bus recovery in 3
W (5300) TWAI Alert and Recovery: Initiate bus recovery in 2
W (6300) TWAI Alert and Recovery: Initiate bus recovery in 1
I (7300) TWAI Alert and Recovery: Initiate bus recovery
I (7350) TWAI Alert and Recovery: Bus Recovered
I (7350) TWAI Alert and Recovery: Driver uninstalled
```
## Troubleshooting
```
I (3350) TWAI Alert and Recovery: Trigger errors
```
If the example does not progress pass triggering errors, check that the target is correctly connected to the transceiver.
```
I (3350) TWAI Alert and Recovery: Trigger errors
I (3650) TWAI Alert and Recovery: Surpassed Error Warning Limit
I (3650) TWAI Alert and Recovery: Entered Error Passive state
```
If the example is able to trigger errors but does not enter the bus off state (i.e., stays in the error passive state), check that the triggering of the bit error is properly set to the examples operating bit rate. By default, the example runs at a bit rate of 125kbits/sec, and the bit error should be triggered after the arbitration phase of each transmitted message.
## Example Breakdown
The TWAI Alert and Recovery Example will do the following...
1. Initialize the TWAI driver in No Acknowledgement mode (so that another node is not required).
2. Create a transmit task to handle message transmission, and a control task to handle alerts.
3. Control task starts the TWAI driver, then reconfigures the alerts to trigger when the error passive or bus off state is entered. The control task then waits for those alerts.
4. The transmit repeatedly transmits single shot messages (i.e., message won't be retried if an error occurs).
5. When a message is being transmitted, the transmit task will purposely invert the TX pin to trigger a bit error. **Note that the triggering of the bit error is timed to occur after the arbitration phase of the transmitted message**.
6. The triggering of a bit error on each transmitted message eventually puts the TWAI driver into the Bus-Off state.
7. Control tasks detects the Bus-Off state via an alert, and triggers the Bus-Off recovery process after a short delay. Alerts are also reconfigured to trigger on the completion of Bus-Off recovery.
8. Once the Bus-Off recovery completion alert is detected by the control task, the TWAI driver is stopped and uninstalled.

View file

@ -0,0 +1,21 @@
# Need Python 3 string formatting functions
from __future__ import print_function
import ttfw_idf
# TWAI Self Test Example constants
STR_EXPECT = ("TWAI Alert and Recovery: Driver installed", "TWAI Alert and Recovery: Driver uninstalled")
EXPECT_TIMEOUT = 20
@ttfw_idf.idf_example_test(env_tag='Example_TWAI1')
def test_twai_alert_and_recovery_example(env, extra_data):
dut = env.get_dut('dut1', 'examples/peripherals/twai/twai_alert_and_recovery', dut_class=ttfw_idf.ESP32DUT)
dut.start_app()
for string in STR_EXPECT:
dut.expect(string, timeout=EXPECT_TIMEOUT)
if __name__ == '__main__':
test_twai_alert_and_recovery_example()

View file

@ -0,0 +1,2 @@
idf_component_register(SRCS "twai_alert_and_recovery_example_main.c"
INCLUDE_DIRS ".")

View file

@ -0,0 +1,19 @@
menu "Example Configuration"
config EXAMPLE_TX_GPIO_NUM
int "TX GPIO number"
default 20 if IDF_TARGET_ESP32S2
default 21 if IDF_TARGET_ESP32
help
This option selects the GPIO pin used for the TX signal. Connect the
TX signal to your transceiver.
config EXAMPLE_RX_GPIO_NUM
int "RX GPIO number"
default 21 if IDF_TARGET_ESP32S2
default 22 if IDF_TARGET_ESP32
help
This option selects the GPIO pin used for the RX signal. Connect the
RX signal to your transceiver.
endmenu

View file

@ -1,4 +1,4 @@
/* CAN Alert and Recovery Example
/* TWAI Alert and Recovery Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
@ -9,13 +9,13 @@
/*
* 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
* features of the TWAI driver. The example will do the following:
* 1) Install and start the TWAI 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
* 6) Uninstall TWAI driver
*/
#include <stdio.h>
@ -26,22 +26,22 @@
#include "esp_err.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "driver/can.h"
#include "driver/twai.h"
/* --------------------- Definitions and static variables ------------------ */
//Example Configuration
#define TX_GPIO_NUM 21
#define RX_GPIO_NUM 22
#define TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM
#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM
#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"
#define EXAMPLE_TAG "TWAI 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};
static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS();
static const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NO_ACK);
static const twai_message_t tx_msg = {.identifier = 0, .data_length_code = 0};
static SemaphoreHandle_t tx_task_sem;
static SemaphoreHandle_t ctrl_task_sem;
@ -53,10 +53,10 @@ 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);
gpio_matrix_out(TX_GPIO_NUM, TWAI_TX_IDX, true, false);
} else {
//Returns TX to default settings
gpio_matrix_out(TX_GPIO_NUM, CAN_TX_IDX, false, false);
gpio_matrix_out(TX_GPIO_NUM, TWAI_TX_IDX, false, false);
}
}
@ -64,7 +64,7 @@ static void tx_task(void *arg)
{
xSemaphoreTake(tx_task_sem, portMAX_DELAY);
while (1) {
if (can_transmit(&tx_msg, 0) == ESP_ERR_INVALID_STATE) {
if (twai_transmit(&tx_msg, 0) == ESP_ERR_INVALID_STATE) {
break; //Exit TX task when bus-off state is reached
}
if (trigger_tx_error) {
@ -82,13 +82,13 @@ static void tx_task(void *arg)
static void ctrl_task(void *arg)
{
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
ESP_ERROR_CHECK(can_start());
ESP_ERROR_CHECK(twai_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);
twai_reconfigure_alerts(TWAI_ALERT_ABOVE_ERR_WARN | TWAI_ALERT_ERR_PASS | TWAI_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));
@ -98,31 +98,31 @@ static void ctrl_task(void *arg)
while (1) {
uint32_t alerts;
can_read_alerts(&alerts, portMAX_DELAY);
if (alerts & CAN_ALERT_ABOVE_ERR_WARN) {
twai_read_alerts(&alerts, portMAX_DELAY);
if (alerts & TWAI_ALERT_ABOVE_ERR_WARN) {
ESP_LOGI(EXAMPLE_TAG, "Surpassed Error Warning Limit");
}
if (alerts & CAN_ALERT_ERR_PASS) {
if (alerts & TWAI_ALERT_ERR_PASS) {
ESP_LOGI(EXAMPLE_TAG, "Entered Error Passive state");
}
if (alerts & CAN_ALERT_BUS_OFF) {
if (alerts & TWAI_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);
twai_reconfigure_alerts(TWAI_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
twai_initiate_recovery(); //Needs 128 occurrences of bus free signal
ESP_LOGI(EXAMPLE_TAG, "Initiate bus recovery");
}
if (alerts & CAN_ALERT_BUS_RECOVERED) {
if (alerts & TWAI_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
//No need call twai_stop(), bus recovery will return to stopped state
xSemaphoreGive(ctrl_task_sem);
vTaskDelete(NULL);
}
@ -132,19 +132,19 @@ void app_main(void)
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);
xTaskCreatePinnedToCore(tx_task, "TWAI_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(ctrl_task, "TWAI_ctrl", 4096, NULL, CTRL_TASK_PRIO, NULL, tskNO_AFFINITY);
//Install CAN driver
ESP_ERROR_CHECK(can_driver_install(&g_config, &t_config, & f_config));
//Install TWAI driver
ESP_ERROR_CHECK(twai_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());
//Uninstall TWAI driver
ESP_ERROR_CHECK(twai_driver_uninstall());
ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled");
//Cleanup

View file

@ -0,0 +1,184 @@
# TWAI Network Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This example demonstrates how to use the TWAI driver to program a target (ESP32 or ESP32-S2) as a TWAI node, and have the two nodes (Network Master and Network Slave) communicate on a TWAI network. The Listen Only node is optional and acts as a network monitor meaning that it only receives messages and does not influence the bus in any way (i.e. doesn't not acknowledge or send error frames).
Note that concept of master/slave in this example refers to which node initiates
and stops the transfer of a stream of data messages.
## How to use example
### Hardware Required
This example requires at least two targets (e.g., an ESP32 or ESP32-S2) to act as the Network Master and Network Slave. The third target (Listen Only) is optional. Each target must be connected to an external transceiver (e.g., a SN65HVD23X transceiver). The transceivers must then be interconnected to form a TWAI network.
The following diagram illustrates an example network:
```
---------- ---------- --------------
| Master | | Slave | | Listen Only |
| | | | | |
| TX RX | | TX RX | | TX RX |
---------- ---------- --------------
| | | | | |
| | | | | |
---------- ---------- ----------
| 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 you don't have an external transceiver, you can still run the [TWAI Self Test example](../twai_self_test/README.md)
### Configure the project
For each node in the TWAI network (i.e., Master, Slave, Listen Only)...
* Set the target of the build (where `{IDF_TARGET}` stands for the target chip such as `eszp32` or `esp32s2`).
* Then run `menuconfig` to configure the example.
```
idf.py set-target {IDF_TARGET}
idf.py menuconfig
```
* Under `Example Configuration`, configure the pin assignments using the options `TX GPIO Number` and `RX GPIO Number` according to how the target was connected to the transceiver. By default, `TX GPIO Number` and `RX GPIO Number` are set to the following values:
* On the ESP32, `TX GPIO Number` and `RX GPIO Number` default to `21` and `22` respectively
* On the ESP32-S2, `TX GPIO Number` and `RX GPIO Number` default to `20` and `21` respectively
### Build and Flash
For each node, build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
Network Master
```
I (345) TWAI Master: Driver installed
I (345) TWAI Master: Driver started
I (345) TWAI Master: Transmitting ping
I (3105) TWAI Master: Transmitted start command
I (3105) TWAI Master: Received data value 339
...
I (5545) TWAI Master: Received data value 584
I (5545) TWAI Master: Transmitted stop command
I (5595) TWAI Master: Driver stopped
I (6595) TWAI Master: Driver started
I (6595) TWAI Master: Transmitting ping
I (7095) TWAI Master: Transmitted start command
I (7095) TWAI Master: Received data value 738
...
I (9535) TWAI Master: Received data value 983
I (9535) TWAI Master: Transmitted stop command
I (9585) TWAI Master: Driver stopped
I (10585) TWAI Master: Driver started
I (10585) TWAI Master: Transmitting ping
I (11085) TWAI Master: Transmitted start command
I (11085) TWAI Master: Received data value 1137
...
I (13525) TWAI Master: Received data value 1382
I (13525) TWAI Master: Transmitted stop command
I (13575) TWAI Master: Driver stopped
I (14575) TWAI Master: Driver uninstalled
```
Network Slave
```
Slave starting in 3
Slave starting in 2
Slave starting in 1
I (6322) TWAI Slave: Driver installed
I (6322) TWAI Slave: Driver started
I (6462) TWAI Slave: Transmitted ping response
I (6712) TWAI Slave: Start transmitting data
I (6712) TWAI Slave: Transmitted data value 339
...
I (9162) TWAI Slave: Transmitted data value 584
I (9212) TWAI Slave: Transmitted stop response
I (9312) TWAI Slave: Driver stopped
I (10312) TWAI Slave: Driver started
I (10452) TWAI Slave: Transmitted ping response
I (10702) TWAI Slave: Start transmitting data
I (10702) TWAI Slave: Transmitted data value 738
...
I (13152) TWAI Slave: Transmitted data value 983
I (13202) TWAI Slave: Transmitted stop response
I (13302) TWAI Slave: Driver stopped
I (14302) TWAI Slave: Driver started
I (14442) TWAI Slave: Transmitted ping response
I (14692) TWAI Slave: Start transmitting data
I (14692) TWAI Slave: Transmitted data value 1137
...
I (17142) TWAI Slave: Transmitted data value 1382
I (17192) TWAI Slave: Transmitted stop response
I (17292) TWAI Slave: Driver stopped
I (18292) TWAI Slave: Driver uninstalled
```
Network Listen Only
```
I (326) TWAI Listen Only: Driver installed
I (326) TWAI Listen Only: Driver started
I (366) TWAI Listen Only: Received master ping
...
I (1866) TWAI Listen Only: Received master ping
I (1866) TWAI Listen Only: Received slave ping response
I (2116) TWAI Listen Only: Received master start command
I (2116) TWAI Listen Only: Received data value 329
...
I (4566) TWAI Listen Only: Received data value 574
I (4566) TWAI Listen Only: Received master stop command
I (4606) TWAI Listen Only: Received slave stop response
I (5606) TWAI Listen Only: Received master ping
I (5856) TWAI Listen Only: Received master ping
I (5856) TWAI Listen Only: Received slave ping response
I (6106) TWAI Listen Only: Received master start command
I (6106) TWAI Listen Only: Received data value 728
...
I (8556) TWAI Listen Only: Received data value 973
I (8556) TWAI Listen Only: Received master stop command
I (8596) TWAI Listen Only: Received slave stop response
I (9596) TWAI Listen Only: Received master ping
I (9846) TWAI Listen Only: Received master ping
I (9846) TWAI Listen Only: Received slave ping response
I (10096) TWAI Listen Only: Received master start command
I (10096) TWAI Listen Only: Received data value 1127
...
I (12546) TWAI Listen Only: Received data value 1372
I (12546) TWAI Listen Only: Received master stop command
I (12586) TWAI Listen Only: Received slave stop response
I (12586) TWAI Listen Only: Driver stopped
I (12586) TWAI Listen Only: Driver uninstalled
```
## Example Breakdown
The communication between the Network Master and Network Slave execute the following steps over multiple iterations:
1. Both master and slave go through install and start their TWAI drivers independently.
2. The master repeatedly sends **PING** messages until it receives a **PING_RESP** (ping response message) 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.

View file

@ -6,9 +6,9 @@ from threading import Thread
import ttfw_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", "CAN Listen Only: Driver uninstalled")
master_expect = ("TWAI Master: Driver installed", "TWAI Master: Driver uninstalled")
slave_expect = ("TWAI Slave: Driver installed", "TWAI Slave: Driver uninstalled")
listen_only_expect = ("TWAI Listen Only: Driver installed", "TWAI Listen Only: Driver uninstalled")
def dut_thread_callback(**kwargs):
@ -27,15 +27,15 @@ def dut_thread_callback(**kwargs):
result[0] = True
@ttfw_idf.idf_example_test(env_tag='Example_CAN2')
def test_can_network_example(env, extra_data):
@ttfw_idf.idf_example_test(env_tag='Example_TWAI2')
def test_twai_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_master = env.get_dut("dut1", "examples/peripherals/twai/twai_network/twai_network_master",
dut_class=ttfw_idf.ESP32DUT)
dut_slave = env.get_dut("dut2", "examples/peripherals/can/can_network/can_network_slave",
dut_slave = env.get_dut("dut2", "examples/peripherals/twai/twai_network/twai_network_slave",
dut_class=ttfw_idf.ESP32DUT)
dut_listen_only = env.get_dut("dut3", "examples/peripherals/can/can_network/can_network_listen_only",
dut_listen_only = env.get_dut("dut3", "examples/peripherals/twai/twai_network/twai_network_listen_only",
dut_class=ttfw_idf.ESP32DUT)
# Flash app onto each DUT, each DUT is reset again at the start of each thread
@ -71,4 +71,4 @@ def test_can_network_example(env, extra_data):
if __name__ == '__main__':
test_can_network_example()
test_twai_network_example()

View file

@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(can_network_listen_only)
project(twai_network_listen_only)

View file

@ -3,7 +3,7 @@
# project subdirectory.
#
PROJECT_NAME := can_network_listen_only
PROJECT_NAME := twai_network_listen_only
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,2 @@
idf_component_register(SRCS "twai_network_example_listen_only_main.c"
INCLUDE_DIRS ".")

View file

@ -0,0 +1,19 @@
menu "Example Configuration"
config EXAMPLE_TX_GPIO_NUM
int "TX GPIO number"
default 20 if IDF_TARGET_ESP32S2
default 21 if IDF_TARGET_ESP32
help
This option selects the GPIO pin used for the TX signal. Connect the
TX signal to your transceiver.
config EXAMPLE_RX_GPIO_NUM
int "RX GPIO number"
default 21 if IDF_TARGET_ESP32S2
default 22 if IDF_TARGET_ESP32
help
This option selects the GPIO pin used for the RX signal. Connect the
RX signal to your transceiver.
endmenu

View file

@ -1,4 +1,4 @@
/* CAN Network Listen Only Example
/* TWAI Network Listen Only Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
@ -8,8 +8,8 @@
*/
/*
* 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
* The following example demonstrates a Listen Only node in a TWAI network. The
* Listen Only node will not take part in any TWAI 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
@ -25,15 +25,15 @@
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/can.h"
#include "driver/twai.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 TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM
#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM
#define EXAMPLE_TAG "TWAI Listen Only"
#define ID_MASTER_STOP_CMD 0x0A0
#define ID_MASTER_START_CMD 0x0A1
@ -42,21 +42,21 @@
#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();
static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
static const twai_timing_config_t t_config = TWAI_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,
static const twai_general_config_t g_config = {.mode = TWAI_MODE_LISTEN_ONLY,
.tx_io = TX_GPIO_NUM, .rx_io = RX_GPIO_NUM,
.clkout_io = CAN_IO_UNUSED, .bus_off_io = CAN_IO_UNUSED,
.clkout_io = TWAI_IO_UNUSED, .bus_off_io = TWAI_IO_UNUSED,
.tx_queue_len = 0, .rx_queue_len = 5,
.alerts_enabled = CAN_ALERT_NONE,
.alerts_enabled = TWAI_ALERT_NONE,
.clkout_divider = 0};
static SemaphoreHandle_t rx_sem;
/* --------------------------- Tasks and Functions -------------------------- */
static void can_receive_task(void *arg)
static void twai_receive_task(void *arg)
{
xSemaphoreTake(rx_sem, portMAX_DELAY);
bool start_cmd = false;
@ -64,8 +64,8 @@ static void can_receive_task(void *arg)
uint32_t iterations = 0;
while (iterations < NO_OF_ITERS) {
can_message_t rx_msg;
can_receive(&rx_msg, portMAX_DELAY);
twai_message_t rx_msg;
twai_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) {
@ -100,22 +100,22 @@ static void can_receive_task(void *arg)
void app_main(void)
{
rx_sem = xSemaphoreCreateBinary();
xTaskCreatePinnedToCore(can_receive_task, "CAN_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(twai_receive_task, "TWAI_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));
//Install and start TWAI driver
ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config));
ESP_LOGI(EXAMPLE_TAG, "Driver installed");
ESP_ERROR_CHECK(can_start());
ESP_ERROR_CHECK(twai_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());
//Stop and uninstall TWAI driver
ESP_ERROR_CHECK(twai_stop());
ESP_LOGI(EXAMPLE_TAG, "Driver stopped");
ESP_ERROR_CHECK(can_driver_uninstall());
ESP_ERROR_CHECK(twai_driver_uninstall());
ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled");
//Cleanup

View file

@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(can_network_slave)
project(twai_network_master)

View file

@ -3,7 +3,7 @@
# project subdirectory.
#
PROJECT_NAME := can_network_master
PROJECT_NAME := twai_network_master
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,2 @@
idf_component_register(SRCS "twai_network_example_master_main.c"
INCLUDE_DIRS ".")

View file

@ -0,0 +1,19 @@
menu "Example Configuration"
config EXAMPLE_TX_GPIO_NUM
int "TX GPIO number"
default 20 if IDF_TARGET_ESP32S2
default 21 if IDF_TARGET_ESP32
help
This option selects the GPIO pin used for the TX signal. Connect the
TX signal to your transceiver.
config EXAMPLE_RX_GPIO_NUM
int "RX GPIO number"
default 21 if IDF_TARGET_ESP32S2
default 22 if IDF_TARGET_ESP32
help
This option selects the GPIO pin used for the RX signal. Connect the
RX signal to your transceiver.
endmenu

View file

@ -1,4 +1,4 @@
/* CAN Network Master Example
/* TWAI Network Master Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
@ -8,15 +8,15 @@
*/
/*
* The following example demonstrates a master node in a CAN network. The master
* The following example demonstrates a master node in a TWAI 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
* 1) Start the TWAI 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
* 5) Stop the TWAI driver
*/
#include <stdio.h>
#include <stdlib.h>
@ -26,7 +26,7 @@
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/can.h"
#include "driver/twai.h"
/* --------------------- Definitions and static variables ------------------ */
//Example Configuration
@ -37,9 +37,9 @@
#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 TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM
#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM
#define EXAMPLE_TAG "TWAI Master"
#define ID_MASTER_STOP_CMD 0x0A0
#define ID_MASTER_START_CMD 0x0A1
@ -62,15 +62,15 @@ typedef enum {
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 twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS();
static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
static const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NORMAL);
static const can_message_t ping_message = {.identifier = ID_MASTER_PING, .data_length_code = 0,
static const twai_message_t ping_message = {.identifier = ID_MASTER_PING, .data_length_code = 0,
.ss = 1, .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,
static const twai_message_t start_message = {.identifier = ID_MASTER_START_CMD, .data_length_code = 0,
.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,
static const twai_message_t stop_message = {.identifier = ID_MASTER_STOP_CMD, .data_length_code = 0,
.data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}};
static QueueHandle_t tx_task_queue;
@ -81,7 +81,7 @@ static SemaphoreHandle_t done_sem;
/* --------------------------- Tasks and Functions -------------------------- */
static void can_receive_task(void *arg)
static void twai_receive_task(void *arg)
{
while (1) {
rx_task_action_t action;
@ -90,8 +90,8 @@ static void can_receive_task(void *arg)
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);
twai_message_t rx_msg;
twai_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_SLAVE_PING_RESP) {
xSemaphoreGive(stop_ping_sem);
xSemaphoreGive(ctrl_task_sem);
@ -102,8 +102,8 @@ static void can_receive_task(void *arg)
//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);
twai_message_t rx_msg;
twai_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++) {
@ -117,8 +117,8 @@ static void can_receive_task(void *arg)
} 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);
twai_message_t rx_msg;
twai_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_SLAVE_STOP_RESP) {
xSemaphoreGive(ctrl_task_sem);
break;
@ -131,7 +131,7 @@ static void can_receive_task(void *arg)
vTaskDelete(NULL);
}
static void can_transmit_task(void *arg)
static void twai_transmit_task(void *arg)
{
while (1) {
tx_task_action_t action;
@ -141,16 +141,16 @@ static void can_transmit_task(void *arg)
//Repeatedly transmit pings
ESP_LOGI(EXAMPLE_TAG, "Transmitting ping");
while (xSemaphoreTake(stop_ping_sem, 0) != pdTRUE) {
can_transmit(&ping_message, portMAX_DELAY);
twai_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);
twai_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);
twai_transmit(&stop_message, portMAX_DELAY);
ESP_LOGI(EXAMPLE_TAG, "Transmitted stop command");
} else if (action == TX_TASK_EXIT) {
break;
@ -159,14 +159,14 @@ static void can_transmit_task(void *arg)
vTaskDelete(NULL);
}
static void can_control_task(void *arg)
static void twai_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_ERROR_CHECK(twai_start());
ESP_LOGI(EXAMPLE_TAG, "Driver started");
//Start transmitting pings, and listen for ping response
@ -190,7 +190,7 @@ static void can_control_task(void *arg)
xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY);
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
ESP_ERROR_CHECK(can_stop());
ESP_ERROR_CHECK(twai_stop());
ESP_LOGI(EXAMPLE_TAG, "Driver stopped");
vTaskDelay(pdMS_TO_TICKS(ITER_DELAY_MS));
}
@ -213,19 +213,19 @@ void app_main(void)
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);
xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(twai_transmit_task, "TWAI_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(twai_control_task, "TWAI_ctrl", 4096, NULL, CTRL_TSK_PRIO, NULL, tskNO_AFFINITY);
//Install CAN driver
ESP_ERROR_CHECK(can_driver_install(&g_config, &t_config, &f_config));
//Install TWAI driver
ESP_ERROR_CHECK(twai_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());
//Uninstall TWAI driver
ESP_ERROR_CHECK(twai_driver_uninstall());
ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled");
//Cleanup

View file

@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(can_network_master)
project(twai_network_slave)

View file

@ -3,7 +3,7 @@
# project subdirectory.
#
PROJECT_NAME := can_network_slave
PROJECT_NAME := twai_network_slave
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,2 @@
idf_component_register(SRCS "twai_network_example_slave_main.c"
INCLUDE_DIRS ".")

View file

@ -0,0 +1,19 @@
menu "Example Configuration"
config EXAMPLE_TX_GPIO_NUM
int "TX GPIO number"
default 20 if IDF_TARGET_ESP32S2
default 21 if IDF_TARGET_ESP32
help
This option selects the GPIO pin used for the TX signal. Connect the
TX signal to your transceiver.
config EXAMPLE_RX_GPIO_NUM
int "RX GPIO number"
default 21 if IDF_TARGET_ESP32S2
default 22 if IDF_TARGET_ESP32
help
This option selects the GPIO pin used for the RX signal. Connect the
RX signal to your transceiver.
endmenu

View file

@ -1,4 +1,4 @@
/* CAN Network Slave Example
/* TWAI Network Slave Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
@ -8,16 +8,16 @@
*/
/*
* The following example demonstrates a slave node in a CAN network. The slave
* The following example demonstrates a slave node in a TWAI 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
* 1) Start the TWAI 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
* 6) Stop the TWAI driver
*/
#include <stdio.h>
@ -28,7 +28,7 @@
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/can.h"
#include "driver/twai.h"
/* --------------------- Definitions and static variables ------------------ */
//Example Configuration
@ -38,9 +38,9 @@
#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 TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM
#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM
#define EXAMPLE_TAG "TWAI Slave"
#define ID_MASTER_STOP_CMD 0x0A0
#define ID_MASTER_START_CMD 0x0A1
@ -63,15 +63,15 @@ typedef enum {
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,
static const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NORMAL);
static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS();
static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
static const twai_message_t ping_resp = {.identifier = ID_SLAVE_PING_RESP, .data_length_code = 0,
.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,
static const twai_message_t stop_resp = {.identifier = ID_SLAVE_STOP_RESP, .data_length_code = 0,
.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,
static twai_message_t data_message = {.identifier = ID_SLAVE_DATA, .data_length_code = 4,
.data = {0, 0 , 0 , 0 ,0 ,0 ,0 ,0}};
static QueueHandle_t tx_task_queue;
@ -82,16 +82,16 @@ static SemaphoreHandle_t done_sem;
/* --------------------------- Tasks and Functions -------------------------- */
static void can_receive_task(void *arg)
static void twai_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;
twai_message_t rx_msg;
while (1) {
can_receive(&rx_msg, portMAX_DELAY);
twai_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_MASTER_PING) {
xSemaphoreGive(ctrl_task_sem);
break;
@ -99,9 +99,9 @@ static void can_receive_task(void *arg)
}
} else if (action == RX_RECEIVE_START_CMD) {
//Listen for start command from master
can_message_t rx_msg;
twai_message_t rx_msg;
while (1) {
can_receive(&rx_msg, portMAX_DELAY);
twai_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_MASTER_START_CMD) {
xSemaphoreGive(ctrl_task_sem);
break;
@ -109,9 +109,9 @@ static void can_receive_task(void *arg)
}
} else if (action == RX_RECEIVE_STOP_CMD) {
//Listen for stop command from master
can_message_t rx_msg;
twai_message_t rx_msg;
while (1) {
can_receive(&rx_msg, portMAX_DELAY);
twai_receive(&rx_msg, portMAX_DELAY);
if (rx_msg.identifier == ID_MASTER_STOP_CMD) {
xSemaphoreGive(stop_data_sem);
xSemaphoreGive(ctrl_task_sem);
@ -125,7 +125,7 @@ static void can_receive_task(void *arg)
vTaskDelete(NULL);
}
static void can_transmit_task(void *arg)
static void twai_transmit_task(void *arg)
{
while (1) {
tx_task_action_t action;
@ -133,7 +133,7 @@ static void can_transmit_task(void *arg)
if (action == TX_SEND_PING_RESP) {
//Transmit ping response to master
can_transmit(&ping_resp, portMAX_DELAY);
twai_transmit(&ping_resp, portMAX_DELAY);
ESP_LOGI(EXAMPLE_TAG, "Transmitted ping response");
xSemaphoreGive(ctrl_task_sem);
} else if (action == TX_SEND_DATA) {
@ -145,7 +145,7 @@ static void can_transmit_task(void *arg)
for (int i = 0; i < 4; i++) {
data_message.data[i] = (sensor_data >> (i * 8)) & 0xFF;
}
can_transmit(&data_message, portMAX_DELAY);
twai_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) {
@ -154,7 +154,7 @@ static void can_transmit_task(void *arg)
}
} else if (action == TX_SEND_STOP_RESP) {
//Transmit stop response to master
can_transmit(&stop_resp, portMAX_DELAY);
twai_transmit(&stop_resp, portMAX_DELAY);
ESP_LOGI(EXAMPLE_TAG, "Transmitted stop response");
xSemaphoreGive(ctrl_task_sem);
} else if (action == TX_TASK_EXIT) {
@ -164,14 +164,14 @@ static void can_transmit_task(void *arg)
vTaskDelete(NULL);
}
static void can_control_task(void *arg)
static void twai_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_ERROR_CHECK(twai_start());
ESP_LOGI(EXAMPLE_TAG, "Driver started");
//Listen of pings from master
@ -202,14 +202,14 @@ static void can_control_task(void *arg)
xSemaphoreTake(ctrl_task_sem, portMAX_DELAY);
//Wait for bus to become free
can_status_info_t status_info;
can_get_status_info(&status_info);
twai_status_info_t status_info;
twai_get_status_info(&status_info);
while (status_info.msgs_to_tx > 0) {
vTaskDelay(pdMS_TO_TICKS(100));
can_get_status_info(&status_info);
twai_get_status_info(&status_info);
}
ESP_ERROR_CHECK(can_stop());
ESP_ERROR_CHECK(twai_stop());
ESP_LOGI(EXAMPLE_TAG, "Driver stopped");
vTaskDelay(pdMS_TO_TICKS(ITER_DELAY_MS));
}
@ -240,19 +240,19 @@ void app_main(void)
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);
xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(twai_transmit_task, "TWAI_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(twai_control_task, "TWAI_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));
//Install TWAI driver, trigger tasks to start
ESP_ERROR_CHECK(twai_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());
//Uninstall TWAI driver
ESP_ERROR_CHECK(twai_driver_uninstall());
ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled");
//Cleanup

View file

@ -3,4 +3,4 @@
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(can_self_test_example)
project(twai_self_test_example)

View file

@ -3,7 +3,7 @@
# project subdirectory.
#
PROJECT_NAME := can_self_test_example
PROJECT_NAME := twai_self_test_example
include $(IDF_PATH)/make/project.mk

View file

@ -0,0 +1,83 @@
# TWAI Self Test Example
(See the README.md file in the upper level 'examples' directory for more information about examples.)
The TWAI Self Test Example demonstrates how a node can transmit TWAI messages to itself using the TWAI driver's "No Acknowledgement" mode and Self Reception Requests. The Self Test Example can be run as a simple test to determine whether a target (ESP32 or ESP32-S2) is properly connected to a working external transceiver.
## How to use example
### Hardware Required
This example requires only a single target (e.g., an ESP32 or ESP32-S2). The target must be connected to an external transceiver (e.g., a SN65HVD23X transceiver). This connection usually consists of a TX and an RX signal.
Note: If you don't have an external transceiver, this example can still be run by simply connecting the TX GPIO and RX GPIO with a jumper.
### Configure the project
* Set the target of the build (where `{IDF_TARGET}` stands for the target chip such as `eszp32` or `esp32s2`).
* Then run `menuconfig` to configure the example.
```
idf.py set-target {IDF_TARGET}
idf.py menuconfig
```
* Under `Example Configuration`, configure the pin assignments using the options `TX GPIO Number` and `RX GPIO Number` according to how the target was connected to the transceiver. By default, `TX GPIO Number` and `RX GPIO Number` are set to the following values:
* On the ESP32, `TX GPIO Number` and `RX GPIO Number` default to `21` and `22` respectively
* On the ESP32-S2, `TX GPIO Number` and `RX GPIO Number` default to `20` and `21` respectively
### Build and Flash
Build the project and flash it to the board, then run monitor tool to view serial output:
```
idf.py -p PORT flash monitor
```
(Replace PORT with the name of the serial port to use.)
(To exit the serial monitor, type ``Ctrl-]``.)
See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects.
## Example Output
```
I (345) TWAI Self Test: Driver installed
I (345) TWAI Self Test: Driver started
I (355) TWAI Self Test: Msg received - Data = 0
...
I (1335) TWAI Self Test: Msg received - Data = 99
I (1335) TWAI Self Test: Driver stopped
I (1435) TWAI Self Test: Driver started
I (1435) TWAI Self Test: Msg received - Data = 0
...
I (2425) TWAI Self Test: Msg received - Data = 99
I (2425) TWAI Self Test: Driver stopped
I (2525) TWAI Self Test: Driver started
I (2525) TWAI Self Test: Msg received - Data = 0
...
I (3515) TWAI Self Test: Msg received - Data = 99
I (3515) TWAI Self Test: Driver stopped
I (3615) TWAI Self Test: Driver uninstalled
```
## Troubleshooting
```
I (345) TWAI Self Test: Driver installed
I (345) TWAI Self Test: Driver started
```
If the TWAI driver is installed and started but no messages are received, check that the target is correctly connected to the external transceiver, and that the external transceiver is operating properly (i.e., properly powered and not in sleep mode).
## Example Breakdown
The TWAI Self Test Example will do multiple iterations of the following steps:
1. Install the TWAI driver
2. Start the TWAI driver
3. Simultaneously transmit and receive multiple messages using the self reception request.
4. Stop the TWAI driver
5. Repeat steps 2 to 4 for multiple iterations
6. Uninstall the TWAI driver

View file

@ -0,0 +1,23 @@
# Need Python 3 string formatting functions
from __future__ import print_function
import ttfw_idf
# TWAI Self Test Example constants
STR_EXPECT = ("TWAI Self Test: Driver installed", "TWAI Self Test: Driver uninstalled")
EXPECT_TIMEOUT = 20
@ttfw_idf.idf_example_test(env_tag='Example_TWAI1')
def test_twai_self_test_example(env, extra_data):
# Get device under test, flash and start example. "dut1" must be defined in EnvConfig
dut = env.get_dut('dut1', 'examples/peripherals/twai/twai_self_test', dut_class=ttfw_idf.ESP32DUT)
dut.start_app()
for string in STR_EXPECT:
dut.expect(string, timeout=EXPECT_TIMEOUT)
if __name__ == '__main__':
test_twai_self_test_example()

View file

@ -0,0 +1,2 @@
idf_component_register(SRCS "twai_self_test_example_main.c"
INCLUDE_DIRS ".")

View file

@ -0,0 +1,19 @@
menu "Example Configuration"
config EXAMPLE_TX_GPIO_NUM
int "TX GPIO number"
default 20 if IDF_TARGET_ESP32S2
default 21 if IDF_TARGET_ESP32
help
This option selects the GPIO pin used for the TX signal. Connect the
TX signal to your transceiver.
config EXAMPLE_RX_GPIO_NUM
int "RX GPIO number"
default 21 if IDF_TARGET_ESP32S2
default 22 if IDF_TARGET_ESP32
help
This option selects the GPIO pin used for the RX signal. Connect the
RX signal to your transceiver.
endmenu

View file

@ -1,4 +1,4 @@
/* CAN Self Test Example
/* TWAI Self Test Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
@ -8,14 +8,14 @@
*/
/*
* The following example demonstrates the self testing capabilities of the CAN
* The following example demonstrates the self testing capabilities of the TWAI
* peripheral by utilizing the No Acknowledgment Mode and Self Reception Request
* capabilities. This example can be used to verify that the CAN peripheral and
* capabilities. This example can be used to verify that the TWAI 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
* 1) Start the TWAI driver
* 2) Transmit and receive 100 messages using self reception request
* 3) Stop the CAN driver
* 3) Stop the TWAI driver
*/
#include <stdio.h>
@ -25,28 +25,28 @@
#include "freertos/semphr.h"
#include "esp_err.h"
#include "esp_log.h"
#include "driver/can.h"
#include "driver/twai.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_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM
#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM
#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"
#define EXAMPLE_TAG "TWAI Self Test"
static const can_timing_config_t t_config = CAN_TIMING_CONFIG_25KBITS();
static const twai_timing_config_t t_config = TWAI_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),
static const twai_filter_config_t f_config = {.acceptance_code = (MSG_ID << 21),
.acceptance_mask = ~(TWAI_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 const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NO_ACK);
static SemaphoreHandle_t tx_sem;
static SemaphoreHandle_t rx_sem;
@ -55,30 +55,30 @@ static SemaphoreHandle_t done_sem;
/* --------------------------- Tasks and Functions -------------------------- */
static void can_transmit_task(void *arg)
static void twai_transmit_task(void *arg)
{
can_message_t tx_msg = {.data_length_code = 1, .identifier = MSG_ID, .self = 1};
twai_message_t tx_msg = {.data_length_code = 1, .identifier = MSG_ID, .self = 1};
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));
ESP_ERROR_CHECK(twai_transmit(&tx_msg, portMAX_DELAY));
vTaskDelay(pdMS_TO_TICKS(10));
}
}
vTaskDelete(NULL);
}
static void can_receive_task(void *arg)
static void twai_receive_task(void *arg)
{
can_message_t rx_message;
twai_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_ERROR_CHECK(twai_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
@ -87,12 +87,12 @@ static void can_receive_task(void *arg)
vTaskDelete(NULL);
}
static void can_control_task(void *arg)
static void twai_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());
//Start TWAI Driver for this iteration
ESP_ERROR_CHECK(twai_start());
ESP_LOGI(EXAMPLE_TAG, "Driver started");
//Trigger TX and RX tasks to start transmitting/receiving
@ -100,7 +100,7 @@ static void can_control_task(void *arg)
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_ERROR_CHECK(twai_stop()); //Stop the TWAI Driver
ESP_LOGI(EXAMPLE_TAG, "Driver stopped");
vTaskDelay(pdMS_TO_TICKS(100)); //Delay then start next iteration
}
@ -116,12 +116,12 @@ void app_main(void)
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);
xTaskCreatePinnedToCore(twai_control_task, "TWAI_ctrl", 4096, NULL, CTRL_TSK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY);
xTaskCreatePinnedToCore(twai_transmit_task, "TWAI_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY);
//Install CAN driver
ESP_ERROR_CHECK(can_driver_install(&g_config, &t_config, &f_config));
//Install TWAI driver
ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config));
ESP_LOGI(EXAMPLE_TAG, "Driver installed");
//Start control task
@ -129,8 +129,8 @@ void app_main(void)
//Wait for all iterations and tasks to complete running
xSemaphoreTake(done_sem, portMAX_DELAY);
//Uninstall CAN driver
ESP_ERROR_CHECK(can_driver_uninstall());
//Uninstall TWAI driver
ESP_ERROR_CHECK(twai_driver_uninstall());
ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled");
//Cleanup

View file

@ -257,13 +257,13 @@ example_test_004A:
extends: .example_test_template
tags:
- ESP32
- Example_CAN1
- Example_TWAI1
example_test_004B:
extends: .example_test_template
tags:
- ESP32
- Example_CAN2
- Example_TWAI2
example_test_005:
extends: .example_test_template