From 59274a818f7ca1578d52ad939094b08bcb7b0084 Mon Sep 17 00:00:00 2001 From: wanckl Date: Mon, 12 May 2025 16:25:54 +0800 Subject: [PATCH 1/3] test(driver_twai): new driver add interctive test --- .../test_apps/legacy_twai/pytest_twai.py | 2 +- components/esp_driver_twai/esp_twai_onchip.c | 6 + .../test_apps/test_twai/main/CMakeLists.txt | 2 +- .../test_twai/main/test_twai_common.c | 12 +- .../test_apps/test_twai/main/test_twai_fd.c | 4 +- .../test_twai/main/test_twai_network.c | 117 ++++++++++++++++++ .../test_apps/test_twai/pytest_driver_twai.py | 73 ++++++++++- 7 files changed, 205 insertions(+), 11 deletions(-) create mode 100644 components/esp_driver_twai/test_apps/test_twai/main/test_twai_network.c diff --git a/components/driver/test_apps/legacy_twai/pytest_twai.py b/components/driver/test_apps/legacy_twai/pytest_twai.py index 8e73a13167c..5d6bd65bfc0 100644 --- a/components/driver/test_apps/legacy_twai/pytest_twai.py +++ b/components/driver/test_apps/legacy_twai/pytest_twai.py @@ -26,7 +26,7 @@ def test_twai_self(dut: Dut) -> None: dut.run_all_single_board_cases(group='twai-loop-back') -@pytest.fixture(name='socket_can', scope='module') +@pytest.fixture(name='socket_can') def fixture_create_socket_can() -> Bus: # Set up the socket CAN with the bitrate start_command = 'sudo ip link set can0 up type can bitrate 250000 restart-ms 100' diff --git a/components/esp_driver_twai/esp_twai_onchip.c b/components/esp_driver_twai/esp_twai_onchip.c index d05e1e8cbc6..1a2ea9006ad 100644 --- a/components/esp_driver_twai/esp_twai_onchip.c +++ b/components/esp_driver_twai/esp_twai_onchip.c @@ -632,7 +632,13 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa // Configure GPIO ESP_GOTO_ON_ERROR(_node_config_io(node, node_config), err, TAG, "gpio config failed"); #if CONFIG_PM_ENABLE +#if SOC_TWAI_CLK_SUPPORT_APB + // DFS can change APB frequency. So add lock to prevent sleep and APB freq from changing + ESP_GOTO_ON_ERROR(esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, twai_controller_periph_signals.controllers[ctrlr_id].module_name, &node->pm_lock), err, TAG, "init power manager failed"); +#else // XTAL + // XTAL freq can be closed in light sleep, so we need to create a lock to prevent light sleep ESP_GOTO_ON_ERROR(esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, twai_controller_periph_signals.controllers[ctrlr_id].module_name, &node->pm_lock), err, TAG, "init power manager failed"); +#endif //SOC_TWAI_CLK_SUPPORT_APB #endif //CONFIG_PM_ENABLE node->api_base.enable = _node_enable; diff --git a/components/esp_driver_twai/test_apps/test_twai/main/CMakeLists.txt b/components/esp_driver_twai/test_apps/test_twai/main/CMakeLists.txt index 28874f143aa..6ad112911aa 100644 --- a/components/esp_driver_twai/test_apps/test_twai/main/CMakeLists.txt +++ b/components/esp_driver_twai/test_apps/test_twai/main/CMakeLists.txt @@ -1,7 +1,7 @@ set(srcs "test_app_main.c") if(CONFIG_SOC_TWAI_SUPPORTED) - list(APPEND srcs "test_twai_common.c") + list(APPEND srcs "test_twai_common.c" "test_twai_network.c") endif() if(CONFIG_SOC_TWAI_SUPPORT_FD) diff --git a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c index 50c5f5b9859..4fa3d8db809 100644 --- a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c +++ b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c @@ -36,7 +36,7 @@ static IRAM_ATTR bool test_driver_install_rx_cb(twai_node_handle_t handle, const return false; } -TEST_CASE("twai install uninstall (loopback)", "[TWAI]") +TEST_CASE("twai install uninstall (loopback)", "[twai]") { esp_err_t ret; twai_node_handle_t node_hdl[SOC_TWAI_CONTROLLER_NUM + 1]; @@ -145,7 +145,7 @@ static void test_twai_baudrate_correctness(twai_clock_source_t clk_src, uint32_t TEST_ESP_OK(twai_node_delete(twai_node)); } -TEST_CASE("twai baudrate measurement", "[TWAI]") +TEST_CASE("twai baudrate measurement", "[twai]") { twai_clock_source_t twai_available_clk_srcs[] = SOC_TWAI_CLKS; for (size_t i = 0; i < sizeof(twai_available_clk_srcs) / sizeof(twai_available_clk_srcs[0]); i++) { @@ -163,7 +163,7 @@ static IRAM_ATTR bool test_enable_disable_rx_cb(twai_node_handle_t handle, const return false; } -TEST_CASE("twai transmit stop resume (loopback)", "[TWAI]") +TEST_CASE("twai transmit stop resume (loopback)", "[twai]") { // prepare test memory uint8_t *send_pkg_ptr = heap_caps_malloc(TEST_TRANS_LEN, MALLOC_CAP_8BIT); @@ -268,7 +268,7 @@ static IRAM_ATTR bool test_filter_rx_done_cb(twai_node_handle_t handle, const tw return false; } -TEST_CASE("twai mask filter (loopback)", "[TWAI]") +TEST_CASE("twai mask filter (loopback)", "[twai]") { uint8_t test_ctrl[2]; twai_node_handle_t node_hdl; @@ -352,7 +352,7 @@ static IRAM_ATTR bool test_dual_filter_rx_done_cb(twai_node_handle_t handle, con return false; } -TEST_CASE("twai dual 16bit mask filter (loopback)", "[TWAI]") +TEST_CASE("twai dual 16bit mask filter (loopback)", "[twai]") { uint8_t test_ctrl[2]; twai_node_handle_t node_hdl; @@ -421,7 +421,7 @@ static void IRAM_ATTR test_wait_trans_done_cache_disable(void *args) } } -TEST_CASE("twai driver cache safe (loopback)", "[TWAI]") +TEST_CASE("twai driver cache safe (loopback)", "[twai]") { // prepare test memory uint8_t *send_pkg_ptr = heap_caps_malloc(TEST_TRANS_LEN, MALLOC_CAP_8BIT); diff --git a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_fd.c b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_fd.c index 61cc4567c9e..6168d990fba 100644 --- a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_fd.c +++ b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_fd.c @@ -60,7 +60,7 @@ static IRAM_ATTR bool test_range_filter_rx_done_cb(twai_node_handle_t handle, co return false; } -TEST_CASE("twai range filter (loopback)", "[TWAI]") +TEST_CASE("twai range filter (loopback)", "[twai]") { uint8_t test_ctrl[2]; twai_node_handle_t node_hdl; @@ -131,7 +131,7 @@ static IRAM_ATTR bool test_fd_trans_time_rx_cb(twai_node_handle_t handle, const return false; } -TEST_CASE("twai fd transmit time (loopback)", "[TWAI]") +TEST_CASE("twai fd transmit time (loopback)", "[twai]") { // prepare test memory uint8_t *send_pkg_ptr = heap_caps_malloc(TEST_TRANS_TIME_BUF_LEN, MALLOC_CAP_8BIT); diff --git a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_network.c b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_network.c new file mode 100644 index 00000000000..0d0a0152695 --- /dev/null +++ b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_network.c @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "unity.h" +#include "unity_test_utils_cache.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "test_utils.h" +#include "esp_twai.h" +#include "esp_twai_onchip.h" + +#define TEST_TX_GPIO 4 +#define TEST_RX_GPIO 5 + +static bool IRAM_ATTR test_listen_only_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx) +{ + twai_frame_t *rx_frame = ((twai_frame_t **)user_ctx)[1]; + uint8_t *rx_cnt = ((uint8_t **)user_ctx)[0]; + if (ESP_OK == twai_node_receive_from_isr(handle, rx_frame)) { + *rx_cnt += 1; + } + return false; +} + +TEST_CASE("twai_listen_only", "[twai_net]") +{ + twai_node_handle_t node_hdl; + twai_onchip_node_config_t node_config = { + .io_cfg.tx = TEST_TX_GPIO, + .io_cfg.rx = TEST_RX_GPIO, + .bit_timing.bitrate = 250000, + .tx_queue_depth = 3, + .flags.enable_listen_only = true, + }; + TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl)); + ESP_LOGI("Test", "driver installed"); + + uint8_t rx_buffer[8] = {0}; + twai_frame_t rx_frame = { + .buffer = rx_buffer, + .buffer_len = sizeof(rx_buffer), + }; + uint8_t rx_msg_cnt = 0; + void *user_data[2] = {&rx_msg_cnt, &rx_frame}; + + twai_event_callbacks_t user_cbs = { + .on_rx_done = test_listen_only_rx_cb, + }; + TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, user_data)); + TEST_ESP_OK(twai_node_enable(node_hdl)); + + ESP_LOGI("Test", "Listening ..."); + while (!rx_msg_cnt) { + vTaskDelay(1); + } + ESP_LOGI("Test", "receive with id 0x%lx", rx_frame.header.id); + ESP_LOG_BUFFER_HEX("Data", rx_frame.buffer, twaifd_dlc2len(rx_frame.header.dlc)); + TEST_ASSERT_EQUAL_HEX(0x6688, rx_frame.header.id); + uint8_t expected_data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; + TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_data, rx_frame.buffer, twaifd_dlc2len(rx_frame.header.dlc)); + + TEST_ESP_OK(twai_node_disable(node_hdl)); + TEST_ESP_OK(twai_node_delete(node_hdl)); +} + +TEST_CASE("twai_remote_request", "[twai_net]") +{ + twai_node_handle_t node_hdl; + twai_onchip_node_config_t node_config = { + .io_cfg.tx = TEST_TX_GPIO, + .io_cfg.rx = TEST_RX_GPIO, + .bit_timing.bitrate = 250000, + .fail_retry_cnt = -1, // retry forever if send remote frame failed + .tx_queue_depth = 3, + }; + TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl)); + ESP_LOGI("Test", "driver installed"); + + uint8_t rx_buffer[8] = {0}; + twai_frame_t rx_frame = { + .buffer = rx_buffer, + .buffer_len = sizeof(rx_buffer), + }; + uint8_t rx_msg_cnt = 0; + void *user_data[2] = {&rx_msg_cnt, &rx_frame}; + + twai_event_callbacks_t user_cbs = { + .on_rx_done = test_listen_only_rx_cb, + }; + TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, user_data)); + TEST_ESP_OK(twai_node_enable(node_hdl)); + + twai_frame_t tx_frame = { + .header.id = 0x123, + .header.dlc = 8, + .header.rtr = true, + .header.ide = true, + }; + TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_frame, 1000)); + ESP_LOGI("Test", "send remote frame"); + + uint8_t expected_data[8] = {0x80, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10}; + //waiting pkg receive finish + while (!rx_msg_cnt) { + vTaskDelay(1); + } + ESP_LOGI("Test", "receive with id 0x%lx", rx_frame.header.id); + ESP_LOG_BUFFER_HEX("Data", rx_frame.buffer, twaifd_dlc2len(rx_frame.header.dlc)); + TEST_ASSERT_EQUAL_HEX(0x123, rx_frame.header.id); + TEST_ASSERT_EQUAL_HEX8_ARRAY(expected_data, rx_frame.buffer, twaifd_dlc2len(rx_frame.header.dlc)); + + TEST_ESP_OK(twai_node_disable(node_hdl)); + TEST_ESP_OK(twai_node_delete(node_hdl)); +} diff --git a/components/esp_driver_twai/test_apps/test_twai/pytest_driver_twai.py b/components/esp_driver_twai/test_apps/test_twai/pytest_driver_twai.py index 7b9aabf1a9c..12a28fe91f4 100644 --- a/components/esp_driver_twai/test_apps/test_twai/pytest_driver_twai.py +++ b/components/esp_driver_twai/test_apps/test_twai/pytest_driver_twai.py @@ -1,7 +1,12 @@ # SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD # SPDX-License-Identifier: Apache-2.0 +import subprocess +from time import sleep + import pytest +from can import Bus +from can import Message from pytest_embedded import Dut from pytest_embedded_idf.utils import idf_parametrize from pytest_embedded_idf.utils import soc_filtered_targets @@ -11,4 +16,70 @@ from pytest_embedded_idf.utils import soc_filtered_targets @pytest.mark.parametrize('config', ['release', 'cache_safe'], indirect=True) @idf_parametrize('target', soc_filtered_targets('SOC_TWAI_SUPPORTED == 1'), indirect=['target']) def test_driver_twai_loopbk(dut: Dut) -> None: - dut.run_all_single_board_cases(reset=True) + dut.run_all_single_board_cases(group='twai', reset=True) + + +# -------------------------------- test twai interactive ------------------------------ +@pytest.fixture(name='socket_can') +def fixture_create_socket_can() -> Bus: + # Set up the socket CAN with the bitrate + start_command = 'sudo ip link set can0 up type can bitrate 250000' + stop_command = 'sudo ip link set can0 down' + try: + subprocess.run(start_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + except Exception as e: + print(f'Open bus Error: {e}') + bus = Bus(interface='socketcan', channel='can0', bitrate=250000) + yield bus # test invoked here + bus.shutdown() + subprocess.run(stop_command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + +@pytest.mark.twai_std +@pytest.mark.temp_skip_ci(targets=['esp32c5'], reason='no runner') +@pytest.mark.parametrize('config', ['release'], indirect=True) +@idf_parametrize('target', soc_filtered_targets('SOC_TWAI_SUPPORTED == 1'), indirect=['target']) +def test_driver_twai_listen_only(dut: Dut, socket_can: Bus) -> None: + dut.serial.hard_reset() + dut.expect_exact('Press ENTER to see the list of tests') + + dut.write('"twai_listen_only"') + + # wait the DUT to finish initialize + sleep(0.1) + + message = Message( + arbitration_id=0x6688, + is_extended_id=True, + data=[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88], + ) + print('USB Socket CAN Send:', message) + socket_can.send(message, timeout=0.2) + dut.expect_unity_test_output(timeout=10) + + +@pytest.mark.twai_std +@pytest.mark.temp_skip_ci(targets=['esp32c5'], reason='no runner') +@pytest.mark.parametrize('config', ['release'], indirect=True) +@idf_parametrize('target', soc_filtered_targets('SOC_TWAI_SUPPORTED == 1'), indirect=['target']) +def test_driver_twai_remote_request(dut: Dut, socket_can: Bus) -> None: + dut.serial.hard_reset() + dut.expect_exact('Press ENTER to see the list of tests') + + dut.write('"twai_remote_request"') + + print('Waiting remote frame ...') + while True: + req = socket_can.recv(timeout=0.2) + if req is not None and req.is_remote_frame: + break + print(f'USB Socket CAN Received: {req}') + + reply = Message( + arbitration_id=req.arbitration_id, + is_extended_id=req.is_extended_id, + data=[0x80, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10], + ) + socket_can.send(reply, timeout=0.2) + print('USB Socket CAN Replied:', reply) + dut.expect_unity_test_output(timeout=10) From a8d4196a3b47fb7441dd302c3f1bdcfb8e2d6bdb Mon Sep 17 00:00:00 2001 From: wanckl Date: Thu, 26 Jun 2025 12:25:43 +0800 Subject: [PATCH 2/3] fix(driver_twai): add rx buffer check and c5 errata doc --- components/esp_driver_twai/esp_twai.c | 1 + components/esp_driver_twai/esp_twai_onchip.c | 11 +++++++++-- .../test_twai/main/test_twai_common.c | 4 ++-- components/hal/twai_hal_ctufd.c | 18 +++++++++++++----- components/soc/esp32/twai_periph.c | 1 + components/soc/esp32c3/twai_periph.c | 1 + components/soc/esp32c6/twai_periph.c | 2 ++ components/soc/esp32h2/twai_periph.c | 1 + components/soc/esp32p4/twai_periph.c | 3 +++ components/soc/esp32s2/twai_periph.c | 1 + components/soc/esp32s3/twai_periph.c | 1 + docs/en/api-reference/peripherals/twai.rst | 10 ++++++++-- docs/zh_CN/api-reference/peripherals/twai.rst | 12 +++++++++--- 13 files changed, 52 insertions(+), 14 deletions(-) diff --git a/components/esp_driver_twai/esp_twai.c b/components/esp_driver_twai/esp_twai.c index e79b666c394..0cbc86e7add 100644 --- a/components/esp_driver_twai/esp_twai.c +++ b/components/esp_driver_twai/esp_twai.c @@ -145,6 +145,7 @@ esp_err_t twai_node_transmit(twai_node_handle_t node, const twai_frame_t *frame, esp_err_t twai_node_receive_from_isr(twai_node_handle_t node, twai_frame_t *rx_frame) { ESP_RETURN_ON_FALSE_ISR(node && rx_frame, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null"); + ESP_RETURN_ON_FALSE_ISR((rx_frame->buffer_len == 0) || esp_ptr_in_dram(rx_frame->buffer) || esp_ptr_external_ram(rx_frame->buffer), ESP_ERR_INVALID_ARG, TAG, "invalid 'rx_frame->buffer' pointer or buffer_len"); ESP_RETURN_ON_FALSE_ISR(node->receive_isr, ESP_ERR_NOT_SUPPORTED, TAG, "receive func null"); return node->receive_isr(node, rx_frame); diff --git a/components/esp_driver_twai/esp_twai_onchip.c b/components/esp_driver_twai/esp_twai_onchip.c index 1a2ea9006ad..1a56595e5f4 100644 --- a/components/esp_driver_twai/esp_twai_onchip.c +++ b/components/esp_driver_twai/esp_twai_onchip.c @@ -496,7 +496,7 @@ static esp_err_t _node_config_mask_filter(twai_node_handle_t node, uint8_t filte // FD targets don't support Dual filter ESP_RETURN_ON_FALSE(!mask_cfg->dual_filter, ESP_ERR_NOT_SUPPORTED, TAG, "The target don't support Dual Filter"); #endif - ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) == TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "config filter must when node stopped"); + ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) == TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "filter config must do when node stopped"); twai_hal_configure_filter(twai_ctx->hal, filter_id, mask_cfg); return ESP_OK; @@ -509,7 +509,7 @@ static esp_err_t _node_config_range_filter(twai_node_handle_t node, uint8_t filt ESP_RETURN_ON_FALSE(filter_id < SOC_TWAI_RANGE_FILTER_NUM, ESP_ERR_INVALID_ARG, TAG, "Invalid range filter id %d", filter_id); ESP_RETURN_ON_FALSE((range_cfg->range_low > range_cfg->range_high) || range_cfg->is_ext || !(range_cfg->range_high & ~TWAI_STD_ID_MASK), \ ESP_ERR_INVALID_ARG, TAG, "std_id only (is_ext=0) but valid low/high id larger than 11 bits"); - ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) == TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "config filter must when node stopped"); + ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) == TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "filter config must do when node stopped"); twai_hal_configure_range_filter(twai_ctx->hal, filter_id, range_cfg); return ESP_OK; @@ -626,6 +626,13 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa .enable_self_test = node_config->flags.enable_self_test, .enable_loopback = node_config->flags.enable_loopback, }; +#if CONFIG_IDF_TARGET_ESP32C5 + // ESP32C5 has a bug that the listen only mode don't work when there are other nodes sending ACK each other + // See IDF-13059 for more details + if (node_config->flags.enable_listen_only) { + ESP_LOGW(TAG, "Listen only mode for ESP32C5 may not work properly when there are more than 2 nodes on the bus that are sending ACKs to each other"); + } +#endif ESP_GOTO_ON_FALSE(twai_hal_init(node->hal, &hal_config), ESP_ERR_INVALID_STATE, err, TAG, "hardware not in reset state"); // Configure bus timing ESP_GOTO_ON_ERROR(_node_calc_set_bit_timing(&node->api_base, node_config->clk_src, &node_config->bit_timing, &node_config->data_timing), err, TAG, "bitrate error"); diff --git a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c index 4fa3d8db809..60399000854 100644 --- a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c +++ b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c @@ -26,7 +26,7 @@ static IRAM_ATTR bool test_driver_install_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx) { - twai_frame_t rx_frame; + twai_frame_t rx_frame = {0}; if (ESP_OK == twai_node_receive_from_isr(handle, &rx_frame)) { ESP_EARLY_LOGI("Recv ", "id 0x%lx rtr %d", rx_frame.header.id, rx_frame.header.rtr); } @@ -89,7 +89,7 @@ TEST_CASE("twai install uninstall (loopback)", "[twai]") TEST_ESP_OK(twai_node_enable(node_hdl[SOC_TWAI_CONTROLLER_NUM])); tx_frame.header.id = 0x100; TEST_ESP_OK(twai_node_transmit(node_hdl[SOC_TWAI_CONTROLLER_NUM], &tx_frame, 0)); - twai_frame_t rx_frame; + twai_frame_t rx_frame = {0}; printf("Test receive from task\n"); TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_receive_from_isr(node_hdl[SOC_TWAI_CONTROLLER_NUM], &rx_frame)); diff --git a/components/hal/twai_hal_ctufd.c b/components/hal/twai_hal_ctufd.c index ecd12e58ba9..e71cb923b55 100644 --- a/components/hal/twai_hal_ctufd.c +++ b/components/hal/twai_hal_ctufd.c @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include "hal/twai_hal.h" #include "hal/twaifd_ll.h" @@ -118,17 +119,24 @@ void twai_hal_format_frame(const twai_hal_trans_desc_t *trans_desc, twai_hal_fra { const twai_frame_header_t *header = trans_desc->frame.header; int final_dlc = (header->dlc) ? header->dlc : twaifd_len2dlc(trans_desc->frame.buffer_len); - int data_len = (header->dlc) ? twaifd_dlc2len(header->dlc) : trans_desc->frame.buffer_len; twaifd_ll_format_frame_header(header, final_dlc, frame); - twaifd_ll_format_frame_data(trans_desc->frame.buffer, data_len, frame); + if (!header->rtr) { + int data_len = (header->dlc) ? twaifd_dlc2len(header->dlc) : trans_desc->frame.buffer_len; + data_len = (header->fdf) ? MIN(data_len, TWAIFD_FRAME_MAX_LEN) : MIN(data_len, TWAI_FRAME_MAX_LEN); + twaifd_ll_format_frame_data(trans_desc->frame.buffer, data_len, frame); + } } void twai_hal_parse_frame(const twai_hal_frame_t *frame, twai_frame_header_t *header, uint8_t *buffer, uint8_t buffer_len) { twaifd_ll_parse_frame_header(frame, header); - int frame_data_len = twaifd_dlc2len(header->dlc); - uint8_t final_len = (frame_data_len < buffer_len) ? frame_data_len : buffer_len; - twaifd_ll_parse_frame_data(frame, buffer, final_len); + if (!header->rtr) { + int frame_data_len = twaifd_dlc2len(header->dlc); + // limit data_len for twai classic non-iso mode. + frame_data_len = (header->fdf) ? MIN(frame_data_len, TWAIFD_FRAME_MAX_LEN) : MIN(frame_data_len, TWAI_FRAME_MAX_LEN); + uint8_t final_len = MIN(frame_data_len, buffer_len); + twaifd_ll_parse_frame_data(frame, buffer, final_len); + } } void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_frame_t *tx_frame, uint8_t buffer_idx) diff --git a/components/soc/esp32/twai_periph.c b/components/soc/esp32/twai_periph.c index cf763269fe1..ae4d056e349 100644 --- a/components/soc/esp32/twai_periph.c +++ b/components/soc/esp32/twai_periph.c @@ -11,6 +11,7 @@ const twai_controller_signal_conn_t twai_controller_periph_signals = { .controllers = { [0] = { + .module_name = "TWAI0", .module = PERIPH_TWAI_MODULE, .irq_id = ETS_TWAI_INTR_SOURCE, .tx_sig = TWAI_TX_IDX, diff --git a/components/soc/esp32c3/twai_periph.c b/components/soc/esp32c3/twai_periph.c index bae7c6ec5d9..1984188271d 100644 --- a/components/soc/esp32c3/twai_periph.c +++ b/components/soc/esp32c3/twai_periph.c @@ -10,6 +10,7 @@ const twai_controller_signal_conn_t twai_controller_periph_signals = { .controllers = { [0] = { + .module_name = "TWAI0", .module = PERIPH_TWAI_MODULE, .irq_id = ETS_TWAI_INTR_SOURCE, .tx_sig = TWAI_TX_IDX, diff --git a/components/soc/esp32c6/twai_periph.c b/components/soc/esp32c6/twai_periph.c index 3066be7bf8d..c184b2bac61 100644 --- a/components/soc/esp32c6/twai_periph.c +++ b/components/soc/esp32c6/twai_periph.c @@ -11,6 +11,7 @@ const twai_controller_signal_conn_t twai_controller_periph_signals = { .controllers = { [0] = { + .module_name = "TWAI0", .module = PERIPH_TWAI0_MODULE, .irq_id = ETS_TWAI0_INTR_SOURCE, .tx_sig = TWAI0_TX_IDX, @@ -20,6 +21,7 @@ const twai_controller_signal_conn_t twai_controller_periph_signals = { .stand_by_sig = TWAI0_STANDBY_IDX, }, [1] = { + .module_name = "TWAI1", .module = PERIPH_TWAI1_MODULE, .irq_id = ETS_TWAI1_INTR_SOURCE, .tx_sig = TWAI1_TX_IDX, diff --git a/components/soc/esp32h2/twai_periph.c b/components/soc/esp32h2/twai_periph.c index b81202a7411..f8934a14f28 100644 --- a/components/soc/esp32h2/twai_periph.c +++ b/components/soc/esp32h2/twai_periph.c @@ -11,6 +11,7 @@ const twai_controller_signal_conn_t twai_controller_periph_signals = { .controllers = { [0] = { + .module_name = "TWAI0", .module = PERIPH_TWAI0_MODULE, .irq_id = ETS_TWAI0_INTR_SOURCE, .tx_sig = TWAI_TX_IDX, diff --git a/components/soc/esp32p4/twai_periph.c b/components/soc/esp32p4/twai_periph.c index b9fde3d047e..9e6711c55fc 100644 --- a/components/soc/esp32p4/twai_periph.c +++ b/components/soc/esp32p4/twai_periph.c @@ -11,6 +11,7 @@ const twai_controller_signal_conn_t twai_controller_periph_signals = { .controllers = { [0] = { + .module_name = "TWAI0", .module = PERIPH_TWAI0_MODULE, .irq_id = ETS_TWAI0_INTR_SOURCE, .tx_sig = TWAI0_TX_PAD_OUT_IDX, @@ -20,6 +21,7 @@ const twai_controller_signal_conn_t twai_controller_periph_signals = { .stand_by_sig = TWAI0_STANDBY_PAD_OUT_IDX, }, [1] = { + .module_name = "TWAI1", .module = PERIPH_TWAI1_MODULE, .irq_id = ETS_TWAI1_INTR_SOURCE, .tx_sig = TWAI1_TX_PAD_OUT_IDX, @@ -29,6 +31,7 @@ const twai_controller_signal_conn_t twai_controller_periph_signals = { .stand_by_sig = TWAI1_STANDBY_PAD_OUT_IDX, }, [2] = { + .module_name = "TWAI2", .module = PERIPH_TWAI2_MODULE, .irq_id = ETS_TWAI2_INTR_SOURCE, .tx_sig = TWAI2_TX_PAD_OUT_IDX, diff --git a/components/soc/esp32s2/twai_periph.c b/components/soc/esp32s2/twai_periph.c index bae7c6ec5d9..1984188271d 100644 --- a/components/soc/esp32s2/twai_periph.c +++ b/components/soc/esp32s2/twai_periph.c @@ -10,6 +10,7 @@ const twai_controller_signal_conn_t twai_controller_periph_signals = { .controllers = { [0] = { + .module_name = "TWAI0", .module = PERIPH_TWAI_MODULE, .irq_id = ETS_TWAI_INTR_SOURCE, .tx_sig = TWAI_TX_IDX, diff --git a/components/soc/esp32s3/twai_periph.c b/components/soc/esp32s3/twai_periph.c index bae7c6ec5d9..1984188271d 100644 --- a/components/soc/esp32s3/twai_periph.c +++ b/components/soc/esp32s3/twai_periph.c @@ -10,6 +10,7 @@ const twai_controller_signal_conn_t twai_controller_periph_signals = { .controllers = { [0] = { + .module_name = "TWAI0", .module = PERIPH_TWAI_MODULE, .irq_id = ETS_TWAI_INTR_SOURCE, .tx_sig = TWAI_TX_IDX, diff --git a/docs/en/api-reference/peripherals/twai.rst b/docs/en/api-reference/peripherals/twai.rst index 7e4160fad2b..0a8c4070b1d 100644 --- a/docs/en/api-reference/peripherals/twai.rst +++ b/docs/en/api-reference/peripherals/twai.rst @@ -90,6 +90,12 @@ Below are additional configuration fields of the :cpp:type:`twai_onchip_node_con - :cpp:member:`twai_onchip_node_config_t::flags::enable_listen_only`: Configures the node in listen-only mode. In this mode, the node only receives and does not transmit any dominant bits, including ACK and error frames. - :cpp:member:`twai_onchip_node_config_t::flags::no_receive_rtr`: When using filters, determines whether remote frames matching the ID pattern should be filtered out. +.. only:: esp32c5 + + .. note:: + + Note: The listen-only mode on ESP32C5 can't work properly when there are multiple nodes on the bus that are sending ACKs to each other. An alternative is to use transceiver which supports listen-only mode itself (e.g. TJA1145), and combine it with self-test mode enabled. + The :cpp:func:`twai_node_enable` function starts the TWAI controller. Once enabled, the controller is connected to the bus and can transmit messages. It also generates events upon receiving messages from other nodes on the bus or when bus errors are detected. The corresponding function, :cpp:func:`twai_node_disable`, immediately stops the node and disconnects it from the bus. Any ongoing transmissions will be aborted. When the node is re-enabled later, if there are pending transmissions in the queue, the driver will immediately initiate a new transmission attempt. @@ -202,7 +208,7 @@ The following code demonstrates how to configure a baud rate of 500 Kbit/s with .. code:: c twai_timing_advanced_config_t timing_cfg = { - .brp = 8, // Prescaler set to 8, time quantum = 80M / 8 = 10 MHz (1M Tq) + .brp = 8, // Prescaler set to 8, time quantum = 80M / 8 = 10 MHz (10M Tq) .prop_seg = 10, // Propagation segment .tseg_1 = 4, // Phase segment 1 .tseg_2 = 5, // Phase segment 2 @@ -254,7 +260,7 @@ The following code demonstrates how to calculate the MASK and configure a filter Dual Filter Mode """""""""""""""" - {IDF_TARGET_NAME} supports dual filter mode, which allows the hardware to be configured as two parallel independent 16-bit mask filters. By enabling this, more IDs can be received. Note that when filtering 29-bit extended IDs, each filter can only filter the upper 16 bits of the ID, while the remaining 13 bits are not filtered. The following code demonstrates how to configure dual filter mode using the function :cpp:func:`twai_make_dual_filter`: + {IDF_TARGET_NAME} supports dual filter mode, which allows the hardware to be configured as two parallel independent 16-bit mask filters. By enabling this, more IDs can be received. Note that using dual filter mode to filter 29-bit extended IDs, each filter can only filter the upper 16 bits of the ID, while the remaining 13 bits are not filtered. The following code demonstrates how to configure dual filter mode using the function :cpp:func:`twai_make_dual_filter`: .. code:: c diff --git a/docs/zh_CN/api-reference/peripherals/twai.rst b/docs/zh_CN/api-reference/peripherals/twai.rst index f37633995fa..6547c08e532 100644 --- a/docs/zh_CN/api-reference/peripherals/twai.rst +++ b/docs/zh_CN/api-reference/peripherals/twai.rst @@ -90,6 +90,12 @@ TWAI 是一种适用于汽车和工业应用的高可靠性的多主机实时串 - :cpp:member:`twai_onchip_node_config_t::flags::enable_listen_only` 配置为监听模式,节点只接收,不发送任何显性位,包括 ACK 和错误帧。 - :cpp:member:`twai_onchip_node_config_t::flags::no_receive_rtr` 使用过滤器时是否同时过滤掉符合 ID 规则的远程帧。 +.. only:: esp32c5 + + .. note:: + + 注意: ESP32C5 的监听模式在总线上有多个节点相互发送 ACK 信号时无法正常工作。一种替代方案是使用本身支持监听模式的收发器(例如 TJA1145),并结合启用自测模式。 + 函数 :cpp:func:`twai_node_enable` 将启动 TWAI 控制器,此时 TWAI 控制器就连接到了总线,可以向总线发送报文。如果收到了总线上其他节点发送的报文,或者检测到了总线错误,也将产生相应事件。 与之对应的函数是 :cpp:func:`twai_node_disable`,该函数将立即停止节点工作并与总线断开,正在进行的传输将被中止。当下次重新启动时,如果发送队列中有未完成的任务,驱动将立即发起新的传输。 @@ -202,7 +208,7 @@ TWAI 报文有多种类型,由报头指定。一个典型的数据帧报文主 .. code:: c twai_timing_advanced_config_t timing_cfg = { - .brp = 8, // 预分频为 8,时间量子 80M/8=1M + .brp = 8, // 预分频为 8,时间量子 80M/8=10M .prop_seg = 10, .tseg_1 = 4, .tseg_2 = 5, @@ -227,7 +233,7 @@ TWAI 报文有多种类型,由报头指定。一个典型的数据帧报文主 TWAI 控制器硬件可以根据 ID 对报文进行过滤,从而减少软硬件开销使节点更加高效。过滤掉报文的节点 **不会接收到该报文,但仍会应答**。 -{IDF_TARGET_NAME} 包含 {IDF_TARGET_CONFIG_SOC_TWAI_MASK_FILTER_NUM} 个掩码过滤器,报文通过任意一个过滤器即能收到改报文。典型的 TWAI 掩码过滤器通过 ID 和 MASK 配置,其中: +{IDF_TARGET_NAME} 包含 {IDF_TARGET_CONFIG_SOC_TWAI_MASK_FILTER_NUM} 个掩码过滤器,报文通过任意一个过滤器即能收到该报文。典型的 TWAI 掩码过滤器通过 ID 和 MASK 配置,其中: - ID 表示期望接收的报文的标准11位或扩展29位ID。 - MASK 表示对ID的过滤规则: @@ -254,7 +260,7 @@ TWAI 控制器硬件可以根据 ID 对报文进行过滤,从而减少软硬 双过滤器模式 """""""""""" - {IDF_TARGET_NAME} 支持双过滤器模式,可将硬件配置为并列的两个独立的 16 位掩码过滤器,支持接收更多 ID,但当配置为过滤 29 位扩展ID时,每个过滤器只能过滤其ID的高 16 位,剩余13位不做过滤。以下代码展示了如何借助 :cpp:func:`twai_make_dual_filter` 配置双过滤器模式。 + {IDF_TARGET_NAME} 支持双过滤器模式,可将硬件配置为并列的两个独立的 16 位掩码过滤器,支持接收更多 ID。但注意,使用双过滤器模式过滤 29 位扩展ID时,每个过滤器只能过滤其ID的高 16 位,剩余13位不做过滤。以下代码展示了如何借助 :cpp:func:`twai_make_dual_filter` 配置双过滤器模式。 .. code:: c From 0b87e896b00f78d58add8b95f8b4e87ad87247d0 Mon Sep 17 00:00:00 2001 From: wanckl Date: Thu, 17 Jul 2025 15:50:23 +0800 Subject: [PATCH 3/3] fix(driver_twai): fixed clock source enable/disable --- components/esp_driver_twai/esp_twai_onchip.c | 36 +++++++++++++++---- .../test_twai/main/test_twai_common.c | 7 ++++ 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/components/esp_driver_twai/esp_twai_onchip.c b/components/esp_driver_twai/esp_twai_onchip.c index 1a56595e5f4..a03f0f5e1d2 100644 --- a/components/esp_driver_twai/esp_twai_onchip.c +++ b/components/esp_driver_twai/esp_twai_onchip.c @@ -317,6 +317,8 @@ static esp_err_t _node_delete(twai_node_handle_t node) _node_release_io(twai_ctx); twai_hal_deinit(twai_ctx->hal); _twai_rcc_clock_ctrl(twai_ctx->ctrlr_id, false); + // curr_clk_src must not NULL as we already set to Default in twai_new_node_onchip + ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src(twai_ctx->curr_clk_src, false), TAG, "disable clock source failed"); _node_destroy(twai_ctx); return ESP_OK; } @@ -349,6 +351,30 @@ static esp_err_t _node_check_timing_valid(twai_onchip_ctx_t *twai_ctx, const twa return ESP_OK; } +static esp_err_t _node_set_clock_source(twai_node_handle_t node, twai_clock_source_t clock_src) +{ + twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base); + if (clock_src != twai_ctx->curr_clk_src) { + // Order of operations is important here. + // First enable and switch to the new clock source, then disable the old one. + // To ensure the clock to controller is continuous. + ESP_RETURN_ON_ERROR(esp_clk_tree_enable_src(clock_src, true), TAG, "enable clock source failed"); + _twai_rcc_clock_sel(twai_ctx->ctrlr_id, clock_src); + if (twai_ctx->curr_clk_src) { + // Disable previous clock source + esp_err_t err = esp_clk_tree_enable_src(twai_ctx->curr_clk_src, false); + if (err != ESP_OK) { + ESP_LOGE(TAG, "disable previous clock source failed, err: %d", err); + esp_clk_tree_enable_src(clock_src, false); + return err; + } + } + twai_ctx->curr_clk_src = clock_src; + ESP_LOGD(TAG, "set clock source to %d", clock_src); + } + return ESP_OK; +} + static esp_err_t _node_set_bit_timing(twai_node_handle_t node, const twai_timing_advanced_config_t *timing, const twai_timing_advanced_config_t *timing_fd) { twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base); @@ -380,13 +406,7 @@ static esp_err_t _node_set_bit_timing(twai_node_handle_t node, const twai_timing } #endif - if (new_clock_src != twai_ctx->curr_clk_src) { - // TODO: IDF-13144 - ESP_ERROR_CHECK(esp_clk_tree_enable_src((soc_module_clk_t)(new_clock_src), true)); - twai_ctx->curr_clk_src = new_clock_src; - _twai_rcc_clock_sel(twai_ctx->ctrlr_id, new_clock_src); - } - return ESP_OK; + return _node_set_clock_source(node, new_clock_src); } static esp_err_t _node_calc_set_bit_timing(twai_node_handle_t node, twai_clock_source_t clk_src, const twai_timing_basic_config_t *timing, const twai_timing_basic_config_t *timing_fd) @@ -614,6 +634,8 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa ESP_GOTO_ON_ERROR(esp_intr_alloc(twai_controller_periph_signals.controllers[ctrlr_id].irq_id, intr_flags, _node_isr_main, (void *)node, &node->intr_hdl), err, TAG, "Alloc interrupt failed"); + // Set default clock source first + ESP_RETURN_ON_ERROR(_node_set_clock_source(&node->api_base, TWAI_CLK_SRC_DEFAULT), TAG, "enable default clock source failed"); // Enable bus clock and reset controller _twai_rcc_clock_ctrl(ctrlr_id, true); // Initialize HAL and configure register defaults. diff --git a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c index 60399000854..5ab6719a54c 100644 --- a/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c +++ b/components/esp_driver_twai/test_apps/test_twai/main/test_twai_common.c @@ -12,6 +12,7 @@ #include "esp_attr.h" #include "esp_log.h" #include "esp_heap_caps.h" +#include "esp_clk_tree.h" #include "freertos/FreeRTOS.h" #include "esp_twai.h" #include "esp_twai_onchip.h" @@ -115,6 +116,7 @@ static void test_twai_baudrate_correctness(twai_clock_source_t clk_src, uint32_t }; TEST_ESP_OK(twai_new_node_onchip(&node_config, &twai_node)); TEST_ESP_OK(twai_node_enable(twai_node)); + printf("TWAI driver installed @ %ld Hz\n", test_bitrate); // We use the UART baudrate detection submodule to measure the TWAI baudrate uart_bitrate_detect_config_t detect_config = { @@ -148,8 +150,13 @@ static void test_twai_baudrate_correctness(twai_clock_source_t clk_src, uint32_t TEST_CASE("twai baudrate measurement", "[twai]") { twai_clock_source_t twai_available_clk_srcs[] = SOC_TWAI_CLKS; + uint32_t source_freq = 0; for (size_t i = 0; i < sizeof(twai_available_clk_srcs) / sizeof(twai_available_clk_srcs[0]); i++) { + TEST_ESP_OK(esp_clk_tree_src_get_freq_hz(twai_available_clk_srcs[i], ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &source_freq)); + printf("Test clock source %d frequency: %ld Hz\n", twai_available_clk_srcs[i], source_freq); test_twai_baudrate_correctness(twai_available_clk_srcs[i], 200000); + + test_twai_baudrate_correctness(twai_available_clk_srcs[i], 1000000); } }