feat(twai): support multiple twai controllers

Closes https://github.com/espressif/esp-idf/issues/11383
This commit is contained in:
morris 2023-05-12 14:22:31 +08:00
parent 239bc3b96f
commit 2ae3d4d7c4
6 changed files with 643 additions and 269 deletions

View File

@ -124,77 +124,7 @@ menu "Driver Configurations"
endmenu # SPI Configuration
menu "TWAI Configuration"
depends on SOC_TWAI_SUPPORTED
config TWAI_ISR_IN_IRAM
bool "Place TWAI ISR function into IRAM"
default n
help
Place the TWAI ISR in to IRAM. This will allow the ISR to avoid
cache misses, and also be able to run whilst the cache is disabled
(such as when writing to SPI Flash).
Note that if this option is enabled:
- Users should also set the ESP_INTR_FLAG_IRAM in the driver
configuration structure when installing the driver (see docs for
specifics).
- Alert logging (i.e., setting of the TWAI_ALERT_AND_LOG flag)
will have no effect.
config TWAI_ERRATA_FIX_BUS_OFF_REC
bool "Add SW workaround for REC change during bus-off"
depends on IDF_TARGET_ESP32
default y
help
When the bus-off condition is reached, the REC should be reset to 0 and frozen (via LOM) by the
driver's ISR. However on the ESP32, there is an edge case where the REC will increase before the
driver's ISR can respond in time (e.g., due to the rapid occurrence of bus errors), thus causing the
REC to be non-zero after bus-off. A non-zero REC can prevent bus-off recovery as the bus-off recovery
condition is that both TEC and REC become 0. Enabling this option will add a workaround in the driver
to forcibly reset REC to zero on reaching bus-off.
config TWAI_ERRATA_FIX_TX_INTR_LOST
bool "Add SW workaround for TX interrupt lost errata"
depends on IDF_TARGET_ESP32
default y
help
On the ESP32, when a transmit interrupt occurs, and interrupt register is read on the same APB clock
cycle, the transmit interrupt could be lost. Enabling this option will add a workaround that checks the
transmit buffer status bit to recover any lost transmit interrupt.
config TWAI_ERRATA_FIX_RX_FRAME_INVALID
bool "Add SW workaround for invalid RX frame errata"
depends on IDF_TARGET_ESP32
default y
help
On the ESP32, when receiving a data or remote frame, if a bus error occurs in the data or CRC field,
the data of the next received frame could be invalid. Enabling this option will add a workaround that
will reset the peripheral on detection of this errata condition. Note that if a frame is transmitted on
the bus whilst the reset is ongoing, the message will not be receive by the peripheral sent on the bus
during the reset, the message will be lost.
config TWAI_ERRATA_FIX_RX_FIFO_CORRUPT
bool "Add SW workaround for RX FIFO corruption errata"
depends on IDF_TARGET_ESP32
default y
help
On the ESP32, when the RX FIFO overruns and the RX message counter maxes out at 64 messages, the entire
RX FIFO is no longer recoverable. Enabling this option will add a workaround that resets the peripheral
on detection of this errata condition. Note that if a frame is being sent on the bus during the reset
bus during the reset, the message will be lost.
config TWAI_ERRATA_FIX_LISTEN_ONLY_DOM
bool "Add SW workaround for listen only transmits dominant bit errata"
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3
default y
help
When in the listen only mode, the TWAI controller must not influence the TWAI bus (i.e., must not send
any dominant bits). However, while in listen only mode on the ESP32/ESP32-S2/ESP32-S3/ESP32-C3, the
TWAI controller will still transmit dominant bits when it detects an error (i.e., as part of an active
error frame). Enabling this option will add a workaround that forces the TWAI controller into an error
passive state on initialization, thus preventing any dominant bits from being sent.
endmenu # TWAI Configuration
orsource "./twai/Kconfig.twai"
menu "Temperature sensor Configuration"
depends on SOC_TEMP_SENSOR_SUPPORTED

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -86,14 +86,11 @@ TEST_CASE("twai_mode_std_no_ack_25kbps", "[twai-loop-back]")
TEST_CASE("twai_mode_ext_no_ack_250kbps", "[twai-loop-back]")
{
twai_handle_t twai_buses[SOC_TWAI_CONTROLLER_NUM] = {0};
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS();
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
// bind the TX and RX to the same GPIO to act like a loopback
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 0, TWAI_MODE_NO_ACK);
printf("install twai driver\r\n");
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
TEST_ESP_OK(twai_start());
twai_message_t tx_msg = {
.identifier = 0x12345,
.data_length_code = 6,
@ -101,17 +98,33 @@ TEST_CASE("twai_mode_ext_no_ack_250kbps", "[twai-loop-back]")
.self = true, // Transmitted message will also received by the same node
.extd = true, // Extended Frame Format (29bit ID)
};
printf("install twai driver\r\n");
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
g_config.controller_id = i;
g_config.tx_io = i;
g_config.rx_io = i;
TEST_ESP_OK(twai_driver_install_v2(&g_config, &t_config, &f_config, &twai_buses[i]));
TEST_ESP_OK(twai_start_v2(twai_buses[i]));
}
printf("transmit message\r\n");
TEST_ESP_OK(twai_transmit(&tx_msg, pdMS_TO_TICKS(1000)));
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
TEST_ESP_OK(twai_transmit_v2(twai_buses[i], &tx_msg, pdMS_TO_TICKS(1000)));
}
printf("receive message\r\n");
twai_message_t rx_msg;
TEST_ESP_OK(twai_receive(&rx_msg, pdMS_TO_TICKS(1000)));
TEST_ASSERT_TRUE(rx_msg.data_length_code == 6);
for (int i = 0; i < 6; i++) {
TEST_ASSERT_EQUAL(tx_msg.data[i], rx_msg.data[i]);
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
TEST_ESP_OK(twai_receive_v2(twai_buses[i], &rx_msg, pdMS_TO_TICKS(1000)));
TEST_ASSERT_TRUE(rx_msg.data_length_code == 6);
for (int i = 0; i < 6; i++) {
TEST_ASSERT_EQUAL(tx_msg.data[i], rx_msg.data[i]);
}
}
TEST_ESP_OK(twai_stop());
TEST_ESP_OK(twai_driver_uninstall());
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
TEST_ESP_OK(twai_stop_v2(twai_buses[i]));
TEST_ESP_OK(twai_driver_uninstall_v2(twai_buses[i]));
}
}

View File

@ -0,0 +1,71 @@
menu "TWAI Configuration"
depends on SOC_TWAI_SUPPORTED
config TWAI_ISR_IN_IRAM
bool "Place TWAI ISR function into IRAM"
default n
help
Place the TWAI ISR in to IRAM. This will allow the ISR to avoid
cache misses, and also be able to run whilst the cache is disabled
(such as when writing to SPI Flash).
Note that if this option is enabled:
- Users should also set the ESP_INTR_FLAG_IRAM in the driver
configuration structure when installing the driver (see docs for
specifics).
- Alert logging (i.e., setting of the TWAI_ALERT_AND_LOG flag)
will have no effect.
config TWAI_ERRATA_FIX_BUS_OFF_REC
bool "Add SW workaround for REC change during bus-off"
depends on IDF_TARGET_ESP32
default y
help
When the bus-off condition is reached, the REC should be reset to 0 and frozen (via LOM) by the
driver's ISR. However on the ESP32, there is an edge case where the REC will increase before the
driver's ISR can respond in time (e.g., due to the rapid occurrence of bus errors), thus causing the
REC to be non-zero after bus-off. A non-zero REC can prevent bus-off recovery as the bus-off recovery
condition is that both TEC and REC become 0. Enabling this option will add a workaround in the driver
to forcibly reset REC to zero on reaching bus-off.
config TWAI_ERRATA_FIX_TX_INTR_LOST
bool "Add SW workaround for TX interrupt lost errata"
depends on IDF_TARGET_ESP32
default y
help
On the ESP32, when a transmit interrupt occurs, and interrupt register is read on the same APB clock
cycle, the transmit interrupt could be lost. Enabling this option will add a workaround that checks the
transmit buffer status bit to recover any lost transmit interrupt.
config TWAI_ERRATA_FIX_RX_FRAME_INVALID
bool "Add SW workaround for invalid RX frame errata"
depends on IDF_TARGET_ESP32
default y
help
On the ESP32, when receiving a data or remote frame, if a bus error occurs in the data or CRC field,
the data of the next received frame could be invalid. Enabling this option will add a workaround that
will reset the peripheral on detection of this errata condition. Note that if a frame is transmitted on
the bus whilst the reset is ongoing, the message will not be receive by the peripheral sent on the bus
during the reset, the message will be lost.
config TWAI_ERRATA_FIX_RX_FIFO_CORRUPT
bool "Add SW workaround for RX FIFO corruption errata"
depends on IDF_TARGET_ESP32
default y
help
On the ESP32, when the RX FIFO overruns and the RX message counter maxes out at 64 messages, the entire
RX FIFO is no longer recoverable. Enabling this option will add a workaround that resets the peripheral
on detection of this errata condition. Note that if a frame is being sent on the bus during the reset
bus during the reset, the message will be lost.
config TWAI_ERRATA_FIX_LISTEN_ONLY_DOM
bool "Add SW workaround for listen only transmits dominant bit errata"
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3 || IDF_TARGET_ESP32C3
default y
help
When in the listen only mode, the TWAI controller must not influence the TWAI bus (i.e., must not send
any dominant bits). However, while in listen only mode on the ESP32/ESP32-S2/ESP32-S3/ESP32-C3, the
TWAI controller will still transmit dominant bits when it detects an error (i.e., as part of an active
error frame). Enabling this option will add a workaround that forces the TWAI controller into an error
passive state on initialization, thus preventing any dominant bits from being sent.
endmenu # TWAI Configuration

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -69,6 +69,11 @@ extern "C" {
/* ----------------------- Enum and Struct Definitions ---------------------- */
/**
* @brief TWAI controller handle
*/
typedef struct twai_obj_t *twai_handle_t;
/**
* @brief TWAI driver states
*/
@ -85,6 +90,8 @@ typedef enum {
* @note Macro initializers are available for this structure
*/
typedef struct {
int controller_id; /**< TWAI controller ID, index from 0.
If you want to install TWAI driver with a non-zero controller_id, please use `twai_driver_install_v2` */
twai_mode_t mode; /**< Mode of TWAI controller */
gpio_num_t tx_io; /**< Transmit GPIO number */
gpio_num_t rx_io; /**< Receive GPIO number */
@ -138,6 +145,26 @@ typedef struct {
*/
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 Install TWAI driver and return a handle
*
* @note This is an advanced version of `twai_driver_install` that can return a driver handle, so that it allows you to install multiple TWAI drivers.
* Don't forget to set the proper controller_id in the `twai_general_config_t`
* Please refer to the documentation of `twai_driver_install` for more details.
*
* @param[in] g_config General configuration structure
* @param[in] t_config Timing configuration structure
* @param[in] f_config Filter configuration structure
* @param[out] ret_twai Pointer to a new created TWAI handle
*
* @return
* - ESP_OK: Successfully installed TWAI driver
* - ESP_ERR_INVALID_ARG: Arguments are invalid, e.g. invalid clock source, invalid quanta resolution, invalid controller ID
* - ESP_ERR_NO_MEM: Insufficient memory
* - ESP_ERR_INVALID_STATE: Driver is already installed
*/
esp_err_t twai_driver_install_v2(const twai_general_config_t *g_config, const twai_timing_config_t *t_config, const twai_filter_config_t *f_config, twai_handle_t *ret_twai);
/**
* @brief Uninstall the TWAI driver
*
@ -154,6 +181,20 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_
*/
esp_err_t twai_driver_uninstall(void);
/**
* @brief Uninstall the TWAI driver with a given handle
*
* @note This is an advanced version of `twai_driver_uninstall` that can uninstall a TWAI driver with a given handle.
* Please refer to the documentation of `twai_driver_uninstall` for more details.
*
* @param[in] handle TWAI driver handle returned by `twai_driver_install_v2`
*
* @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_v2(twai_handle_t handle);
/**
* @brief Start the TWAI driver
*
@ -169,6 +210,20 @@ esp_err_t twai_driver_uninstall(void);
*/
esp_err_t twai_start(void);
/**
* @brief Start the TWAI driver with a given handle
*
* @note This is an advanced version of `twai_start` that can start a TWAI driver with a given handle.
* Please refer to the documentation of `twai_start` for more details.
*
* @param[in] handle TWAI driver handle returned by `twai_driver_install_v2`
*
* @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_v2(twai_handle_t handle);
/**
* @brief Stop the TWAI driver
*
@ -188,6 +243,20 @@ esp_err_t twai_start(void);
*/
esp_err_t twai_stop(void);
/**
* @brief Stop the TWAI driver with a given handle
*
* @note This is an advanced version of `twai_stop` that can stop a TWAI driver with a given handle.
* Please refer to the documentation of `twai_stop` for more details.
*
* @param[in] handle TWAI driver handle returned by `twai_driver_install_v2`
*
* @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_v2(twai_handle_t handle);
/**
* @brief Transmit a TWAI message
*
@ -219,6 +288,26 @@ esp_err_t twai_stop(void);
*/
esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait);
/**
* @brief Transmit a TWAI message via a given handle
*
* @note This is an advanced version of `twai_transmit` that can transmit a TWAI message with a given handle.
* Please refer to the documentation of `twai_transmit` for more details.
*
* @param[in] handle TWAI driver handle returned by `twai_driver_install_v2`
* @param[in] message Message to transmit
* @param[in] ticks_to_wait Number of FreeRTOS ticks to block on the TX queue
*
* @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_v2(twai_handle_t handle, const twai_message_t *message, TickType_t ticks_to_wait);
/**
* @brief Receive a TWAI message
*
@ -240,6 +329,24 @@ esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
*/
esp_err_t twai_receive(twai_message_t *message, TickType_t ticks_to_wait);
/**
* @brief Receive a TWAI message via a given handle
*
* @note This is an advanced version of `twai_receive` that can receive a TWAI message with a given handle.
* Please refer to the documentation of `twai_receive` for more details.
*
* @param[in] handle TWAI driver handle returned by `twai_driver_install_v2`
* @param[out] message Received message
* @param[in] ticks_to_wait Number of FreeRTOS ticks to block on RX queue
*
* @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_v2(twai_handle_t handle, twai_message_t *message, TickType_t ticks_to_wait);
/**
* @brief Read TWAI driver alerts
*
@ -261,6 +368,24 @@ esp_err_t twai_receive(twai_message_t *message, TickType_t ticks_to_wait);
*/
esp_err_t twai_read_alerts(uint32_t *alerts, TickType_t ticks_to_wait);
/**
* @brief Read TWAI driver alerts with a given handle
*
* @note This is an advanced version of `twai_read_alerts` that can read TWAI driver alerts with a given handle.
* Please refer to the documentation of `twai_read_alerts` for more details.
*
* @param[in] handle TWAI driver handle returned by `twai_driver_install_v2`
* @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
*
* @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_v2(twai_handle_t handle, uint32_t *alerts, TickType_t ticks_to_wait);
/**
* @brief Reconfigure which alerts are enabled
*
@ -277,6 +402,22 @@ esp_err_t twai_read_alerts(uint32_t *alerts, TickType_t ticks_to_wait);
*/
esp_err_t twai_reconfigure_alerts(uint32_t alerts_enabled, uint32_t *current_alerts);
/**
* @brief Reconfigure which alerts are enabled, with a given handle
*
* @note This is an advanced version of `twai_reconfigure_alerts` that can reconfigure which alerts are enabled with a given handle.
* Please refer to the documentation of `twai_reconfigure_alerts` for more details.
*
* @param[in] handle TWAI driver handle returned by `twai_driver_install_v2`
* @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_v2(twai_handle_t handle, uint32_t alerts_enabled, uint32_t *current_alerts);
/**
* @brief Start the bus recovery process
*
@ -295,6 +436,20 @@ esp_err_t twai_reconfigure_alerts(uint32_t alerts_enabled, uint32_t *current_ale
*/
esp_err_t twai_initiate_recovery(void);
/**
* @brief Start the bus recovery process with a given handle
*
* @note This is an advanced version of `twai_initiate_recovery` that can start the bus recovery process with a given handle.
* Please refer to the documentation of `twai_initiate_recovery` for more details.
*
* @param[in] handle TWAI driver handle returned by `twai_driver_install_v2`
*
* @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_v2(twai_handle_t handle);
/**
* @brief Get current status information of the TWAI driver
*
@ -307,6 +462,22 @@ esp_err_t twai_initiate_recovery(void);
*/
esp_err_t twai_get_status_info(twai_status_info_t *status_info);
/**
* @brief Get current status information of a given TWAI driver handle
*
* @note This is an advanced version of `twai_get_status_info` that can get current status information of a given TWAI driver handle.
* Please refer to the documentation of `twai_get_status_info` for more details.
*
* @param[in] handle TWAI driver handle returned by `twai_driver_install_v2`
* @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_v2(twai_handle_t handle, twai_status_info_t *status_info);
/**
* @brief Clear the transmit queue
*
@ -321,6 +492,20 @@ esp_err_t twai_get_status_info(twai_status_info_t *status_info);
*/
esp_err_t twai_clear_transmit_queue(void);
/**
* @brief Clear the transmit queue of a given TWAI driver handle
*
* @note This is an advanced version of `twai_clear_transmit_queue` that can clear the transmit queue of a given TWAI driver handle.
* Please refer to the documentation of `twai_clear_transmit_queue` for more details.
*
* @param[in] handle TWAI driver handle returned by `twai_driver_install_v2`
*
* @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_v2(twai_handle_t handle);
/**
* @brief Clear the receive queue
*
@ -335,6 +520,20 @@ esp_err_t twai_clear_transmit_queue(void);
*/
esp_err_t twai_clear_receive_queue(void);
/**
* @brief Clear the receive queue of a given TWAI driver handle
*
* @note This is an advanced version of `twai_clear_receive_queue` that can clear the receive queue of a given TWAI driver handle.
* Please refer to the documentation of `twai_clear_receive_queue` for more details.
*
* @param[in] handle TWAI driver handle returned by `twai_driver_install_v2`
*
* @return
* - ESP_OK: Transmit queue cleared
* - ESP_ERR_INVALID_STATE: TWAI driver is not installed
*/
esp_err_t twai_clear_receive_queue_v2(twai_handle_t handle);
#ifdef __cplusplus
}
#endif

View File

@ -36,14 +36,10 @@
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))
#ifdef CONFIG_TWAI_ISR_IN_IRAM
#define TWAI_MALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
#else
@ -65,8 +61,9 @@
/* ------------------ Typedefs, structures, and variables ------------------- */
//Control structure for TWAI driver
typedef struct {
typedef struct twai_obj_t {
int controller_id;
twai_hal_context_t hal; // hal context
//Control and status members
twai_state_t state;
twai_mode_t mode;
@ -87,20 +84,15 @@ typedef struct {
uint32_t alerts_triggered;
//Power Management Lock
esp_pm_lock_handle_t pm_lock;
portMUX_TYPE spinlock;
} 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;
static twai_handle_t g_twai_objs[SOC_TWAI_CONTROLLER_NUM];
static portMUX_TYPE g_spinlock = portMUX_INITIALIZER_UNLOCKED;
/* -------------------- Interrupt and Alert Handlers ------------------------ */
static void twai_alert_handler(uint32_t alert_code, int *alert_req)
static void twai_alert_handler(twai_obj_t *p_twai_obj, uint32_t alert_code, int *alert_req)
{
if (p_twai_obj->alerts_enabled & alert_code) {
//Signify alert has occurred
@ -120,41 +112,41 @@ static void twai_alert_handler(uint32_t alert_code, int *alert_req)
}
}
static inline void twai_handle_rx_buffer_frames(BaseType_t *task_woken, int *alert_req)
static inline void twai_handle_rx_buffer_frames(twai_obj_t *p_twai_obj, BaseType_t *task_woken, int *alert_req)
{
#ifdef SOC_TWAI_SUPPORTS_RX_STATUS
uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context);
uint32_t msg_count = twai_hal_get_rx_msg_count(&p_twai_obj->hal);
for (uint32_t i = 0; i < msg_count; i++) {
twai_hal_frame_t frame;
if (twai_hal_read_rx_buffer_and_clear(&twai_context, &frame)) {
if (twai_hal_read_rx_buffer_and_clear(&p_twai_obj->hal, &frame)) {
//Valid frame copied from RX buffer
if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) {
p_twai_obj->rx_msg_count++;
twai_alert_handler(TWAI_ALERT_RX_DATA, alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_RX_DATA, alert_req);
} else { //Failed to send to queue
p_twai_obj->rx_missed_count++;
twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_RX_QUEUE_FULL, alert_req);
}
} else { //Failed to read from RX buffer because message is overrun
p_twai_obj->rx_overrun_count++;
twai_alert_handler(TWAI_ALERT_RX_FIFO_OVERRUN, alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_RX_FIFO_OVERRUN, alert_req);
}
}
#else //SOC_TWAI_SUPPORTS_RX_STATUS
uint32_t msg_count = twai_hal_get_rx_msg_count(&twai_context);
uint32_t msg_count = twai_hal_get_rx_msg_count(&p_twai_obj->hal);
bool overrun = false;
//Clear all valid RX frames
for (int i = 0; i < msg_count; i++) {
twai_hal_frame_t frame;
if (twai_hal_read_rx_buffer_and_clear(&twai_context, &frame)) {
if (twai_hal_read_rx_buffer_and_clear(&p_twai_obj->hal, &frame)) {
//Valid frame copied from RX buffer
if (xQueueSendFromISR(p_twai_obj->rx_queue, &frame, task_woken) == pdTRUE) {
p_twai_obj->rx_msg_count++;
twai_alert_handler(TWAI_ALERT_RX_DATA, alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_RX_DATA, alert_req);
} else {
p_twai_obj->rx_missed_count++;
twai_alert_handler(TWAI_ALERT_RX_QUEUE_FULL, alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_RX_QUEUE_FULL, alert_req);
}
} else {
overrun = true;
@ -163,20 +155,20 @@ static inline void twai_handle_rx_buffer_frames(BaseType_t *task_woken, int *ale
}
//All remaining frames are treated as overrun. Clear them all
if (overrun) {
p_twai_obj->rx_overrun_count += twai_hal_clear_rx_fifo_overrun(&twai_context);
twai_alert_handler(TWAI_ALERT_RX_FIFO_OVERRUN, alert_req);
p_twai_obj->rx_overrun_count += twai_hal_clear_rx_fifo_overrun(&p_twai_obj->hal);
twai_alert_handler(p_twai_obj, TWAI_ALERT_RX_FIFO_OVERRUN, alert_req);
}
#endif //SOC_TWAI_SUPPORTS_RX_STATUS
}
static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *alert_req)
static inline void twai_handle_tx_buffer_frame(twai_obj_t *p_twai_obj, 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);
if (twai_hal_check_last_tx_successful(&p_twai_obj->hal)) {
twai_alert_handler(p_twai_obj, TWAI_ALERT_TX_SUCCESS, alert_req);
} else {
p_twai_obj->tx_failed_count++;
twai_alert_handler(TWAI_ALERT_TX_FAILED, alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_TX_FAILED, alert_req);
}
//Update TX message count
@ -188,86 +180,83 @@ static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *aler
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);
twai_hal_set_tx_buffer_and_transmit(&p_twai_obj->hal, &frame);
} else {
assert(false && "failed to get a frame from TX queue");
}
} else {
//No more frames to transmit
twai_alert_handler(TWAI_ALERT_TX_IDLE, alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_TX_IDLE, alert_req);
}
}
static void twai_intr_handler_main(void *arg)
{
twai_obj_t *p_twai_obj = (twai_obj_t *)arg;
BaseType_t task_woken = pdFALSE;
int alert_req = 0;
uint32_t events;
TWAI_ENTER_CRITICAL_ISR();
if (p_twai_obj == NULL) { //In case intr occurs whilst driver is being uninstalled
TWAI_EXIT_CRITICAL_ISR();
return;
}
events = twai_hal_get_events(&twai_context); //Get the events that triggered the interrupt
portENTER_CRITICAL_ISR(&p_twai_obj->spinlock);
events = twai_hal_get_events(&p_twai_obj->hal); //Get the events that triggered the interrupt
#if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
if (events & TWAI_HAL_EVENT_NEED_PERIPH_RESET) {
twai_hal_prepare_for_reset(&twai_context);
twai_hal_prepare_for_reset(&p_twai_obj->hal);
TWAI_RCC_ATOMIC() {
twai_ll_reset_register(p_twai_obj->controller_id);
}
twai_hal_recover_from_reset(&twai_context);
p_twai_obj->rx_missed_count += twai_hal_get_reset_lost_rx_cnt(&twai_context);
twai_alert_handler(TWAI_ALERT_PERIPH_RESET, &alert_req);
twai_hal_recover_from_reset(&p_twai_obj->hal);
p_twai_obj->rx_missed_count += twai_hal_get_reset_lost_rx_cnt(&p_twai_obj->hal);
twai_alert_handler(p_twai_obj, TWAI_ALERT_PERIPH_RESET, &alert_req);
}
#endif
if (events & TWAI_HAL_EVENT_RX_BUFF_FRAME) {
//Note: This event will never occur if there is a periph reset event
twai_handle_rx_buffer_frames(&task_woken, &alert_req);
twai_handle_rx_buffer_frames(p_twai_obj, &task_woken, &alert_req);
}
if (events & TWAI_HAL_EVENT_TX_BUFF_FREE) {
twai_handle_tx_buffer_frame(&task_woken, &alert_req);
twai_handle_tx_buffer_frame(p_twai_obj, &task_woken, &alert_req);
}
//Handle events that only require alerting (i.e. no handler)
if (events & TWAI_HAL_EVENT_BUS_OFF) {
p_twai_obj->state = TWAI_STATE_BUS_OFF;
twai_alert_handler(TWAI_ALERT_BUS_OFF, &alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_BUS_OFF, &alert_req);
}
if (events & TWAI_HAL_EVENT_BUS_RECOV_CPLT) {
p_twai_obj->state = TWAI_STATE_STOPPED;
twai_alert_handler(TWAI_ALERT_BUS_RECOVERED, &alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_BUS_RECOVERED, &alert_req);
}
if (events & TWAI_HAL_EVENT_BUS_ERR) {
p_twai_obj->bus_error_count++;
twai_alert_handler(TWAI_ALERT_BUS_ERROR, &alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_BUS_ERROR, &alert_req);
}
if (events & TWAI_HAL_EVENT_ARB_LOST) {
p_twai_obj->arb_lost_count++;
twai_alert_handler(TWAI_ALERT_ARB_LOST, &alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_ARB_LOST, &alert_req);
}
if (events & TWAI_HAL_EVENT_BUS_RECOV_PROGRESS) {
//Bus-recovery in progress. TEC has dropped below error warning limit
twai_alert_handler(TWAI_ALERT_RECOVERY_IN_PROGRESS, &alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_RECOVERY_IN_PROGRESS, &alert_req);
}
if (events & TWAI_HAL_EVENT_ERROR_PASSIVE) {
//Entered error passive
twai_alert_handler(TWAI_ALERT_ERR_PASS, &alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_ERR_PASS, &alert_req);
}
if (events & TWAI_HAL_EVENT_ERROR_ACTIVE) {
//Returned to error active
twai_alert_handler(TWAI_ALERT_ERR_ACTIVE, &alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_ERR_ACTIVE, &alert_req);
}
if (events & TWAI_HAL_EVENT_ABOVE_EWL) {
//TEC or REC surpassed error warning limit
twai_alert_handler(TWAI_ALERT_ABOVE_ERR_WARN, &alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_ABOVE_ERR_WARN, &alert_req);
}
if (events & TWAI_HAL_EVENT_BELOW_EWL) {
//TEC and REC are both below error warning
twai_alert_handler(TWAI_ALERT_BELOW_ERR_WARN, &alert_req);
twai_alert_handler(p_twai_obj, TWAI_ALERT_BELOW_ERR_WARN, &alert_req);
}
TWAI_EXIT_CRITICAL_ISR();
portEXIT_CRITICAL_ISR(&p_twai_obj->spinlock);
if (p_twai_obj->alert_semphr != NULL && alert_req) {
//Give semaphore if alerts were triggered
@ -280,11 +269,10 @@ static void twai_intr_handler_main(void *arg)
/* -------------------------- Helper functions ----------------------------- */
static void twai_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout, gpio_num_t bus_status)
static void twai_configure_gpio(int controller_id, gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout, gpio_num_t bus_status)
{
// assert the GPIO number is not a negative number (shift operation on a negative number is undefined)
assert(tx >= 0 && rx >= 0);
int controller_id = p_twai_obj->controller_id;
// if TX and RX set to the same GPIO, which means we want to create a loop-back in the GPIO matrix
bool io_loop_back = (tx == rx);
gpio_config_t gpio_conf = {
@ -366,7 +354,7 @@ static esp_err_t twai_alloc_driver_obj(const twai_general_config_t *g_config, tw
ret = esp_intr_alloc(twai_controller_periph_signals.controllers[controller_id].irq_id,
g_config->intr_flags | ESP_INTR_FLAG_INTRDISABLED,
twai_intr_handler_main,
NULL,
p_obj,
&p_obj->isr_handle);
if (ret != ESP_OK) {
goto err;
@ -399,22 +387,21 @@ err:
}
/* ---------------------------- 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)
esp_err_t twai_driver_install_v2(const twai_general_config_t *g_config, const twai_timing_config_t *t_config, const twai_filter_config_t *f_config, twai_handle_t *ret_twai)
{
//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->controller_id < SOC_TWAI_CONTROLLER_NUM, ESP_ERR_INVALID_ARG);
TWAI_CHECK(g_config->rx_queue_len > 0, ESP_ERR_INVALID_ARG);
TWAI_CHECK(GPIO_IS_VALID_OUTPUT_GPIO(g_config->tx_io), ESP_ERR_INVALID_ARG);
TWAI_CHECK(GPIO_IS_VALID_GPIO(g_config->rx_io), ESP_ERR_INVALID_ARG);
#ifndef CONFIG_TWAI_ISR_IN_IRAM
TWAI_CHECK(!(g_config->intr_flags & ESP_INTR_FLAG_IRAM), ESP_ERR_INVALID_ARG);
#endif
TWAI_ENTER_CRITICAL();
TWAI_CHECK_FROM_CRIT(p_twai_obj == NULL, ESP_ERR_INVALID_STATE);
TWAI_EXIT_CRITICAL();
int controller_id = g_config->controller_id;
TWAI_CHECK(g_twai_objs[controller_id] == NULL, ESP_ERR_INVALID_STATE);
//Get clock source resolution
uint32_t clock_source_hz = 0;
@ -434,36 +421,37 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_
TWAI_CHECK(twai_ll_check_brp_validation(brp), ESP_ERR_INVALID_ARG);
esp_err_t ret;
twai_obj_t *p_twai_obj_dummy;
// TODO: Currently only controller 0 is supported by the driver. IDF-4775
const int controller_id = 0;
twai_obj_t *p_twai_obj;
//Create a TWAI object (including queues, semaphores, interrupts, and PM locks)
ret = twai_alloc_driver_obj(g_config, clk_src, controller_id, &p_twai_obj_dummy);
ret = twai_alloc_driver_obj(g_config, clk_src, controller_id, &p_twai_obj);
if (ret != ESP_OK) {
return ret;
}
//Initialize flags and variables. All other members are already set to zero by twai_alloc_driver_obj()
p_twai_obj_dummy->controller_id = controller_id;
p_twai_obj_dummy->state = TWAI_STATE_STOPPED;
p_twai_obj_dummy->mode = g_config->mode;
p_twai_obj_dummy->alerts_enabled = g_config->alerts_enabled;
portMUX_INITIALIZE(&p_twai_obj->spinlock);
p_twai_obj->controller_id = controller_id;
p_twai_obj->state = TWAI_STATE_STOPPED;
p_twai_obj->mode = g_config->mode;
p_twai_obj->alerts_enabled = g_config->alerts_enabled;
//Assign the TWAI object
TWAI_ENTER_CRITICAL();
if (p_twai_obj == NULL) {
p_twai_obj = p_twai_obj_dummy;
portENTER_CRITICAL(&g_spinlock);
if (g_twai_objs[controller_id] == NULL) {
g_twai_objs[controller_id] = p_twai_obj;
} else {
//Check if driver is already installed
TWAI_EXIT_CRITICAL();
portEXIT_CRITICAL(&g_spinlock);
ret = ESP_ERR_INVALID_STATE;
goto err;
}
portEXIT_CRITICAL(&g_spinlock);
//Enable TWAI peripheral register clock
TWAI_RCC_ATOMIC() {
twai_ll_enable_bus_clock(p_twai_obj->controller_id, true);
twai_ll_reset_register(p_twai_obj->controller_id);
twai_ll_enable_bus_clock(controller_id, true);
twai_ll_reset_register(controller_id);
}
//Initialize TWAI HAL layer
@ -471,65 +459,95 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_
.clock_source_hz = clock_source_hz,
.controller_id = controller_id,
};
bool res = twai_hal_init(&twai_context, &hal_config);
bool res = twai_hal_init(&p_twai_obj->hal, &hal_config);
assert(res);
twai_hal_configure(&twai_context, t_config, f_config, DRIVER_DEFAULT_INTERRUPTS, g_config->clkout_divider);
TWAI_EXIT_CRITICAL();
twai_hal_configure(&p_twai_obj->hal, t_config, f_config, DRIVER_DEFAULT_INTERRUPTS, g_config->clkout_divider);
//Assign GPIO and Interrupts
twai_configure_gpio(g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io);
twai_configure_gpio(controller_id, g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io);
#if CONFIG_PM_ENABLE
//Acquire PM lock
if (p_twai_obj->pm_lock) {
ESP_ERROR_CHECK(esp_pm_lock_acquire(p_twai_obj->pm_lock)); //Acquire pm_lock during the whole driver lifetime
}
#endif //CONFIG_PM_ENABLE
//Enable interrupt
ESP_ERROR_CHECK(esp_intr_enable(p_twai_obj->isr_handle));
*ret_twai = p_twai_obj;
return ESP_OK; //TWAI module is still in reset mode, users need to call twai_start() afterwards
err:
twai_free_driver_obj(p_twai_obj_dummy);
twai_free_driver_obj(p_twai_obj);
return ret;
}
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)
{
// the handle-less driver API only supports one TWAI controller, i.e. the g_twai_objs[0]
TWAI_CHECK(g_config->controller_id == 0, ESP_ERR_INVALID_ARG);
return twai_driver_install_v2(g_config, t_config, f_config, &g_twai_objs[0]);
}
esp_err_t twai_driver_uninstall_v2(twai_handle_t handle)
{
TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
int controller_id = handle->controller_id;
twai_obj_t *p_twai_obj;
portENTER_CRITICAL(&g_spinlock);
//Check state
if (g_twai_objs[controller_id] == NULL) {
portEXIT_CRITICAL(&g_spinlock);
return ESP_ERR_INVALID_STATE;
}
p_twai_obj = g_twai_objs[controller_id];
if (!(p_twai_obj->state == TWAI_STATE_STOPPED || p_twai_obj->state == TWAI_STATE_BUS_OFF)) {
portEXIT_CRITICAL(&g_spinlock);
return ESP_ERR_INVALID_STATE;
}
g_twai_objs[controller_id] = NULL;
portEXIT_CRITICAL(&g_spinlock);
//Clear registers by reading
twai_hal_deinit(&p_twai_obj->hal);
TWAI_RCC_ATOMIC() {
twai_ll_enable_bus_clock(controller_id, false);
}
#if CONFIG_PM_ENABLE
if (p_twai_obj->pm_lock) {
//Release and delete power management lock
ESP_ERROR_CHECK(esp_pm_lock_release(p_twai_obj->pm_lock));
}
#endif //CONFIG_PM_ENABLE
//Disable interrupt
ESP_ERROR_CHECK(esp_intr_disable(p_twai_obj->isr_handle));
//Free twai driver object
twai_free_driver_obj(p_twai_obj);
return ESP_OK;
}
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->state == TWAI_STATE_STOPPED || p_twai_obj->state == TWAI_STATE_BUS_OFF, ESP_ERR_INVALID_STATE);
//Clear registers by reading
twai_hal_deinit(&twai_context);
TWAI_RCC_ATOMIC() {
twai_ll_enable_bus_clock(p_twai_obj->controller_id, false);
}
p_twai_obj_dummy = p_twai_obj; //Use dummy to shorten critical section
p_twai_obj = NULL;
TWAI_EXIT_CRITICAL();
#if CONFIG_PM_ENABLE
if (p_twai_obj_dummy->pm_lock) {
//Release and delete power management lock
ESP_ERROR_CHECK(esp_pm_lock_release(p_twai_obj_dummy->pm_lock));
}
#endif //CONFIG_PM_ENABLE
//Disable interrupt
ESP_ERROR_CHECK(esp_intr_disable(p_twai_obj_dummy->isr_handle));
//Free can driver object
twai_free_driver_obj(p_twai_obj_dummy);
return ESP_OK;
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
return twai_driver_uninstall_v2(g_twai_objs[0]);
}
esp_err_t twai_start(void)
esp_err_t twai_start_v2(twai_handle_t handle)
{
//Check state
TWAI_ENTER_CRITICAL();
TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_STOPPED, ESP_ERR_INVALID_STATE);
TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
twai_obj_t *p_twai_obj = handle;
portENTER_CRITICAL(&handle->spinlock);
if (p_twai_obj->state != TWAI_STATE_STOPPED) {
portEXIT_CRITICAL(&handle->spinlock);
return ESP_ERR_INVALID_STATE;
}
//Reset RX queue, RX message count, amd TX queue
xQueueReset(p_twai_obj->rx_queue);
@ -538,21 +556,32 @@ esp_err_t twai_start(void)
}
p_twai_obj->rx_msg_count = 0;
p_twai_obj->tx_msg_count = 0;
twai_hal_start(&twai_context, p_twai_obj->mode);
twai_hal_start(&p_twai_obj->hal, p_twai_obj->mode);
p_twai_obj->state = TWAI_STATE_RUNNING;
TWAI_EXIT_CRITICAL();
portEXIT_CRITICAL(&handle->spinlock);
return ESP_OK;
}
esp_err_t twai_stop(void)
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->state == TWAI_STATE_RUNNING, ESP_ERR_INVALID_STATE);
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
return twai_start_v2(g_twai_objs[0]);
}
twai_hal_stop(&twai_context);
esp_err_t twai_stop_v2(twai_handle_t handle)
{
TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
twai_obj_t *p_twai_obj = handle;
portENTER_CRITICAL(&handle->spinlock);
//Check state
if (p_twai_obj->state != TWAI_STATE_RUNNING) {
portEXIT_CRITICAL(&handle->spinlock);
return ESP_ERR_INVALID_STATE;
}
twai_hal_stop(&p_twai_obj->hal);
//Reset TX Queue and message count
if (p_twai_obj->tx_queue != NULL) {
@ -561,22 +590,34 @@ esp_err_t twai_stop(void)
p_twai_obj->tx_msg_count = 0;
p_twai_obj->state = TWAI_STATE_STOPPED;
TWAI_EXIT_CRITICAL();
portEXIT_CRITICAL(&handle->spinlock);
return ESP_OK;
}
esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
esp_err_t twai_stop(void)
{
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
return twai_stop_v2(g_twai_objs[0]);
}
esp_err_t twai_transmit_v2(twai_handle_t handle, const twai_message_t *message, TickType_t ticks_to_wait)
{
//Check arguments
TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
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();
twai_obj_t *p_twai_obj = handle;
portENTER_CRITICAL(&handle->spinlock);
//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->state == TWAI_STATE_RUNNING, ESP_ERR_INVALID_STATE);
if (p_twai_obj->mode == TWAI_MODE_LISTEN_ONLY) {
portEXIT_CRITICAL(&handle->spinlock);
return ESP_ERR_NOT_SUPPORTED;
}
if (p_twai_obj->state != TWAI_STATE_RUNNING) {
portEXIT_CRITICAL(&handle->spinlock);
return ESP_ERR_INVALID_STATE;
}
//Format frame
esp_err_t ret = ESP_FAIL;
twai_hal_frame_t tx_frame;
@ -585,11 +626,11 @@ esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
//Check if frame can be sent immediately
if (p_twai_obj->tx_msg_count == 0) {
//No other frames waiting to transmit. Bypass queue and transmit immediately
twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame);
twai_hal_set_tx_buffer_and_transmit(&p_twai_obj->hal, &tx_frame);
p_twai_obj->tx_msg_count++;
ret = ESP_OK;
}
TWAI_EXIT_CRITICAL();
portEXIT_CRITICAL(&handle->spinlock);
if (ret != ESP_OK) {
if (p_twai_obj->tx_queue == NULL) {
@ -597,10 +638,10 @@ esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
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 ((!twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED)) && uxQueueMessagesWaiting(p_twai_obj->tx_queue) > 0) {
portENTER_CRITICAL(&handle->spinlock);
if ((!twai_hal_check_state_flags(&p_twai_obj->hal, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED)) && uxQueueMessagesWaiting(p_twai_obj->tx_queue) > 0) {
//If the TX buffer is free but the TX queue is not empty. Check if we need to manually start a transmission
if (twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_BUS_OFF) || !twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_RUNNING)) {
if (twai_hal_check_state_flags(&p_twai_obj->hal, TWAI_HAL_STATE_FLAG_BUS_OFF) || !twai_hal_check_state_flags(&p_twai_obj->hal, TWAI_HAL_STATE_FLAG_RUNNING)) {
//TX buffer became free due to bus-off or is no longer running. No need to start a transmission
ret = ESP_ERR_INVALID_STATE;
} else {
@ -608,7 +649,7 @@ esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
int res = xQueueReceive(p_twai_obj->tx_queue, &tx_frame, 0);
assert(res == pdTRUE);
(void)res;
twai_hal_set_tx_buffer_and_transmit(&twai_context, &tx_frame);
twai_hal_set_tx_buffer_and_transmit(&p_twai_obj->hal, &tx_frame);
p_twai_obj->tx_msg_count++;
ret = ESP_OK;
}
@ -617,7 +658,7 @@ esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
p_twai_obj->tx_msg_count++;
ret = ESP_OK;
}
TWAI_EXIT_CRITICAL();
portEXIT_CRITICAL(&handle->spinlock);
} else {
//Timed out waiting for free space on TX queue
ret = ESP_ERR_TIMEOUT;
@ -626,39 +667,54 @@ esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
return ret;
}
esp_err_t twai_receive(twai_message_t *message, TickType_t ticks_to_wait)
esp_err_t twai_transmit(const twai_message_t *message, TickType_t ticks_to_wait)
{
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
return twai_transmit_v2(g_twai_objs[0], message, ticks_to_wait);
}
esp_err_t twai_receive_v2(twai_handle_t handle, 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(handle != NULL, ESP_ERR_INVALID_ARG);
TWAI_CHECK(message != NULL, ESP_ERR_INVALID_ARG);
twai_obj_t *p_twai_obj = handle;
//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();
portENTER_CRITICAL(&handle->spinlock);
p_twai_obj->rx_msg_count--;
TWAI_EXIT_CRITICAL();
portEXIT_CRITICAL(&handle->spinlock);
//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)
esp_err_t twai_receive(twai_message_t *message, TickType_t ticks_to_wait)
{
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
return twai_receive_v2(g_twai_objs[0], message, ticks_to_wait);
}
esp_err_t twai_read_alerts_v2(twai_handle_t handle, uint32_t *alerts, TickType_t ticks_to_wait)
{
//Check arguments and state
TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
TWAI_CHECK(alerts != NULL, ESP_ERR_INVALID_ARG);
twai_obj_t *p_twai_obj = handle;
//Wait for an alert to occur
if (xSemaphoreTake(p_twai_obj->alert_semphr, ticks_to_wait) == pdTRUE) {
TWAI_ENTER_CRITICAL();
portENTER_CRITICAL(&handle->spinlock);
*alerts = p_twai_obj->alerts_triggered;
p_twai_obj->alerts_triggered = 0; //Clear triggered alerts
TWAI_EXIT_CRITICAL();
portEXIT_CRITICAL(&handle->spinlock);
return ESP_OK;
} else {
*alerts = 0;
@ -666,28 +722,47 @@ esp_err_t twai_read_alerts(uint32_t *alerts, TickType_t ticks_to_wait)
}
}
esp_err_t twai_reconfigure_alerts(uint32_t alerts_enabled, uint32_t *current_alerts)
esp_err_t twai_read_alerts(uint32_t *alerts, TickType_t ticks_to_wait)
{
TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
return twai_read_alerts_v2(g_twai_objs[0], alerts, ticks_to_wait);
}
TWAI_ENTER_CRITICAL();
esp_err_t twai_reconfigure_alerts_v2(twai_handle_t handle, uint32_t alerts_enabled, uint32_t *current_alerts)
{
TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
twai_obj_t *p_twai_obj = handle;
portENTER_CRITICAL(&handle->spinlock);
//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();
portEXIT_CRITICAL(&handle->spinlock);
return ESP_OK;
}
esp_err_t twai_initiate_recovery(void)
esp_err_t twai_reconfigure_alerts(uint32_t alerts_enabled, uint32_t *current_alerts)
{
TWAI_ENTER_CRITICAL();
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
return twai_reconfigure_alerts_v2(g_twai_objs[0], alerts_enabled, current_alerts);
}
esp_err_t twai_initiate_recovery_v2(twai_handle_t handle)
{
TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
twai_obj_t *p_twai_obj = handle;
portENTER_CRITICAL(&handle->spinlock);
//Check state
TWAI_CHECK_FROM_CRIT(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_BUS_OFF, ESP_ERR_INVALID_STATE);
if (p_twai_obj->state != TWAI_STATE_BUS_OFF) {
portEXIT_CRITICAL(&handle->spinlock);
return ESP_ERR_INVALID_STATE;
}
//Reset TX Queue/Counters
if (p_twai_obj->tx_queue != NULL) {
@ -696,27 +771,34 @@ esp_err_t twai_initiate_recovery(void)
p_twai_obj->tx_msg_count = 0;
//Trigger start of recovery process
twai_hal_start_bus_recovery(&twai_context);
twai_hal_start_bus_recovery(&p_twai_obj->hal);
p_twai_obj->state = TWAI_STATE_RECOVERING;
TWAI_EXIT_CRITICAL();
portEXIT_CRITICAL(&handle->spinlock);
return ESP_OK;
}
esp_err_t twai_get_status_info(twai_status_info_t *status_info)
esp_err_t twai_initiate_recovery(void)
{
//Check parameters and state
TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
TWAI_CHECK(status_info != NULL, ESP_ERR_INVALID_ARG);
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
return twai_initiate_recovery_v2(g_twai_objs[0]);
}
TWAI_ENTER_CRITICAL();
esp_err_t twai_get_status_info_v2(twai_handle_t handle, twai_status_info_t *status_info)
{
//Check parameters
TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
TWAI_CHECK(status_info != NULL, ESP_ERR_INVALID_ARG);
twai_obj_t *p_twai_obj = handle;
portENTER_CRITICAL(&handle->spinlock);
if (p_twai_obj->mode == TWAI_MODE_LISTEN_ONLY) {
//Error counters are frozen under listen only mode thus are meaningless. Simply return 0 in this case.
status_info->tx_error_counter = 0;
status_info->rx_error_counter = 0;
} else {
status_info->tx_error_counter = twai_hal_get_tec(&twai_context);
status_info->rx_error_counter = twai_hal_get_rec(&twai_context);
status_info->tx_error_counter = twai_hal_get_tec(&p_twai_obj->hal);
status_info->rx_error_counter = twai_hal_get_rec(&p_twai_obj->hal);
}
status_info->msgs_to_tx = p_twai_obj->tx_msg_count;
status_info->msgs_to_rx = p_twai_obj->rx_msg_count;
@ -726,35 +808,55 @@ esp_err_t twai_get_status_info(twai_status_info_t *status_info)
status_info->arb_lost_count = p_twai_obj->arb_lost_count;
status_info->bus_error_count = p_twai_obj->bus_error_count;
status_info->state = p_twai_obj->state;
TWAI_EXIT_CRITICAL();
portEXIT_CRITICAL(&handle->spinlock);
return ESP_OK;
}
esp_err_t twai_get_status_info(twai_status_info_t *status_info)
{
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
return twai_get_status_info_v2(g_twai_objs[0], status_info);
}
esp_err_t twai_clear_transmit_queue_v2(twai_handle_t handle)
{
//Check parameters
TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
TWAI_CHECK(handle->tx_queue != NULL, ESP_ERR_NOT_SUPPORTED);
twai_obj_t *p_twai_obj = handle;
portENTER_CRITICAL(&handle->spinlock);
//If a message is currently undergoing transmission, the tx interrupt handler will decrement tx_msg_count
p_twai_obj->tx_msg_count = twai_hal_check_state_flags(&p_twai_obj->hal, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED) ? 1 : 0;
xQueueReset(p_twai_obj->tx_queue);
portEXIT_CRITICAL(&handle->spinlock);
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);
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
return twai_clear_transmit_queue_v2(g_twai_objs[0]);
}
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 = twai_hal_check_state_flags(&twai_context, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED) ? 1 : 0;
xQueueReset(p_twai_obj->tx_queue);
TWAI_EXIT_CRITICAL();
esp_err_t twai_clear_receive_queue_v2(twai_handle_t handle)
{
//Check parameters
TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
twai_obj_t *p_twai_obj = handle;
portENTER_CRITICAL(&handle->spinlock);
p_twai_obj->rx_msg_count = 0;
xQueueReset(p_twai_obj->rx_queue);
portEXIT_CRITICAL(&handle->spinlock);
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;
// the handle-less driver API only support one TWAI controller, i.e. the g_twai_objs[0]
return twai_clear_receive_queue_v2(g_twai_objs[0]);
}

View File

@ -3,12 +3,6 @@ Two-Wire Automotive Interface (TWAI)
.. -------------------------------- Overview -----------------------------------
.. only:: esp32c6
.. warning::
{IDF_TARGET_NAME} has {IDF_TARGET_SOC_TWAI_CONTROLLER_NUM} TWAI controllers, but at the moment, the driver can only support ``TWAI0`` due to the limitation of the driver structure.
Overview
--------
@ -86,6 +80,13 @@ The TWAI controller's interface consists of 4 signal lines known as **TX, RX, BU
.. ------------------------------ Configuration --------------------------------
API Naming Conventions
----------------------
.. note::
The TWAI driver provides two sets of API. One is handle-free and is widely used in IDF versions earlier than v5.2, but it can only support one TWAI hardware controller. The other set is with handles, and the function name is usually suffixed with "v2", which can support any number of TWAI controllers. These two sets of API can be used at the same time, but it is recommended to use the "v2" version in your new projects.
Driver Configuration
--------------------
@ -397,6 +398,64 @@ The following code snippet demonstrates how to configure, install, and start the
The usage of macro initializers is not mandatory and each of the configuration structures can be manually.
Install Multiple TWAI Instances
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The following code snippet demonstrates how to install multiple TWAI instances via the use of the :cpp:func:`twai_driver_install_v2` function.
.. code-block:: c
#include "driver/gpio.h"
#include "driver/twai.h"
void app_main()
{
twai_handle_t twai_bus_0;
twai_handle_t twai_bus_1;
//Initialize configuration structures using macro initializers
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(GPIO_NUM_0, GPIO_NUM_1, TWAI_MODE_NORMAL);
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_500KBITS();
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
//Install driver for TWAI bus 0
g_config.controller_id = 0;
if (twai_driver_install_v2(&g_config, &t_config, &f_config, &twai_bus_0) == ESP_OK) {
printf("Driver installed\n");
} else {
printf("Failed to install driver\n");
return;
}
//Start TWAI driver
if (twai_start_v2(twai_bus_0) == ESP_OK) {
printf("Driver started\n");
} else {
printf("Failed to start driver\n");
return;
}
//Install driver for TWAI bus 1
g_config.controller_id = 1;
g_config.tx_io = GPIO_NUM_2;
g_config.rx_io = GPIO_NUM_3;
if (twai_driver_install_v2(&g_config, &t_config, &f_config, &twai_bus_1) == ESP_OK) {
printf("Driver installed\n");
} else {
printf("Failed to install driver\n");
return;
}
//Start TWAI driver
if (twai_start_v2(twai_bus_1) == ESP_OK) {
printf("Driver started\n");
} else {
printf("Failed to start driver\n");
return;
}
//Other Driver operations must use version 2 API as well
...
}
Message Transmission
^^^^^^^^^^^^^^^^^^^^