mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
Merge branch 'feature/twai_support_esp32s2' into 'master'
TWAI: Add ESP32-S2 support Closes IDF-1455 See merge request espressif/esp-idf!8973
This commit is contained in:
@@ -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")
|
||||
|
@@ -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
|
@@ -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
|
347
components/driver/include/driver/twai.h
Normal file
347
components/driver/include/driver/twai.h
Normal 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
693
components/driver/twai.c
Normal 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;
|
||||
}
|
@@ -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 );
|
||||
|
@@ -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 );
|
||||
|
@@ -237,7 +237,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;
|
||||
|
@@ -245,7 +245,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 |
|
||||
|
@@ -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")
|
||||
|
@@ -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
|
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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"
|
@@ -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
|
||||
|
@@ -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))
|
||||
|
@@ -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
|
||||
|
@@ -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,
|
||||
|
@@ -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*/
|
||||
|
@@ -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
|
41
components/soc/soc/esp32/include/soc/twai_caps.h
Normal file
41
components/soc/soc/esp32/include/soc/twai_caps.h
Normal 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
|
210
components/soc/soc/esp32/include/soc/twai_struct.h
Normal file
210
components/soc/soc/esp32/include/soc/twai_struct.h
Normal 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
|
@@ -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
|
||||
|
@@ -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*/
|
||||
|
@@ -5,6 +5,6 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#define SOC_TWAI_SUPPORTED 1
|
||||
#define SOC_CPU_CORES_NUM 1
|
||||
|
||||
#define SOC_SUPPORTS_SECURE_DL_MODE 1
|
||||
|
@@ -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))
|
||||
|
28
components/soc/soc/esp32s2/include/soc/twai_caps.h
Normal file
28
components/soc/soc/esp32s2/include/soc/twai_caps.h
Normal 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
|
208
components/soc/soc/esp32s2/include/soc/twai_struct.h
Normal file
208
components/soc/soc/esp32s2/include/soc/twai_struct.h
Normal 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
|
28
components/soc/soc/include/soc/twai_periph.h
Normal file
28
components/soc/soc/include/soc/twai_periph.h
Normal 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
|
154
components/soc/src/esp32/include/hal/can_hal.h
Normal file
154
components/soc/src/esp32/include/hal/can_hal.h
Normal 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
|
@@ -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
|
||||
|
68
components/soc/src/esp32/include/hal/can_types.h
Normal file
68
components/soc/src/esp32/include/hal/can_types.h
Normal 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
|
@@ -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:
|
||||
|
703
components/soc/src/esp32/include/hal/twai_ll.h
Normal file
703
components/soc/src/esp32/include/hal/twai_ll.h
Normal 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
|
@@ -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:
|
||||
|
675
components/soc/src/esp32s2/include/hal/twai_ll.h
Normal file
675
components/soc/src/esp32s2/include/hal/twai_ll.h
Normal 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
|
@@ -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);
|
||||
}
|
||||
}
|
147
components/soc/src/hal/twai_hal.c
Normal file
147
components/soc/src/hal/twai_hal.c
Normal 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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user