Просмотр исходного кода

Merge branch 'feature/2nd_twai_on_c6' into 'master'

twai: support multiple TWAI controller

See merge request espressif/esp-idf!23713
morris 2 лет назад
Родитель
Сommit
d4dbacf988

+ 1 - 71
components/driver/Kconfig

@@ -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

+ 25 - 12
components/driver/test_apps/twai/main/test_twai_loop_back.c

@@ -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]));
+    }
 }

+ 71 - 0
components/driver/twai/Kconfig.twai

@@ -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

+ 200 - 1
components/driver/twai/include/driver/twai.h

@@ -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

+ 263 - 161
components/driver/twai/twai.c

@@ -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_uninstall(void)
+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_obj_t *p_twai_obj_dummy;
+    TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
+    int controller_id = handle->controller_id;
+    twai_obj_t *p_twai_obj;
 
-    TWAI_ENTER_CRITICAL();
+    portENTER_CRITICAL(&g_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_STOPPED || p_twai_obj->state == TWAI_STATE_BUS_OFF, ESP_ERR_INVALID_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(&twai_context);
+    twai_hal_deinit(&p_twai_obj->hal);
     TWAI_RCC_ATOMIC() {
-        twai_ll_enable_bus_clock(p_twai_obj->controller_id, false);
+        twai_ll_enable_bus_clock(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) {
+    if (p_twai_obj->pm_lock) {
         //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_release(p_twai_obj->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);
+    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_start(void)
+esp_err_t twai_driver_uninstall(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_STOPPED, ESP_ERR_INVALID_STATE);
+    // 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_v2(twai_handle_t handle)
+{
+    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)
 {
+    // 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]);
+}
+
+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
-    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);
+    if (p_twai_obj->state != TWAI_STATE_RUNNING) {
+        portEXIT_CRITICAL(&handle->spinlock);
+        return ESP_ERR_INVALID_STATE;
+    }
 
-    twai_hal_stop(&twai_context);
+    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)
+{
+    // 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);
+}
+
+esp_err_t twai_reconfigure_alerts_v2(twai_handle_t handle, uint32_t alerts_enabled, uint32_t *current_alerts)
 {
-    TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
+    TWAI_CHECK(handle != NULL, ESP_ERR_INVALID_ARG);
+
+    twai_obj_t *p_twai_obj = handle;
 
-    TWAI_ENTER_CRITICAL();
+    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)
+{
+    // 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_ENTER_CRITICAL();
+    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)
+{
+    // 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]);
+}
+
+esp_err_t twai_get_status_info_v2(twai_handle_t handle, twai_status_info_t *status_info)
 {
-    //Check parameters and state
-    TWAI_CHECK(p_twai_obj != NULL, ESP_ERR_INVALID_STATE);
+    //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;
 
-    TWAI_ENTER_CRITICAL();
+    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_clear_transmit_queue(void)
+esp_err_t twai_get_status_info(twai_status_info_t *status_info)
 {
-    //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_get_status_info_v2(g_twai_objs[0], status_info);
+}
 
-    TWAI_ENTER_CRITICAL();
+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(&twai_context, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED) ? 1 : 0;
+    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);
-    TWAI_EXIT_CRITICAL();
+    portEXIT_CRITICAL(&handle->spinlock);
 
     return ESP_OK;
 }
 
-esp_err_t twai_clear_receive_queue(void)
+esp_err_t twai_clear_transmit_queue(void)
 {
-    //Check State
-    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_clear_transmit_queue_v2(g_twai_objs[0]);
+}
 
-    TWAI_ENTER_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);
-    TWAI_EXIT_CRITICAL();
+    portEXIT_CRITICAL(&handle->spinlock);
 
     return ESP_OK;
 }
+
+esp_err_t twai_clear_receive_queue(void)
+{
+    // 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]);
+}

+ 65 - 6
docs/en/api-reference/peripherals/twai.rst

@@ -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
 ^^^^^^^^^^^^^^^^^^^^