From fe8f0f08f35ede153cba95b8af9f8e27c779aac6 Mon Sep 17 00:00:00 2001 From: wanckl Date: Wed, 2 Jul 2025 20:26:13 +0800 Subject: [PATCH] feat(driver_twai): update and simplify network example using new driver --- components/esp_driver_twai/esp_twai_onchip.c | 13 +- docs/en/api-reference/peripherals/twai.rst | 1 + docs/zh_CN/api-reference/peripherals/twai.rst | 1 + examples/peripherals/.build-test-rules.yml | 10 +- .../peripherals/twai/twai_network/README.md | 282 ++++++++--------- .../twai/twai_network/pytest_twai_network.py | 107 +++++++ .../pytest_twai_network_example.py | 90 ------ .../CMakeLists.txt | 2 +- .../twai_listen_only/main/CMakeLists.txt | 5 + .../twai_listen_only/main/Kconfig.projbuild | 17 + .../twai_listen_only/main/twai_listen_only.c | 146 +++++++++ .../twai_network_listen_only/CMakeLists.txt | 8 - .../main/CMakeLists.txt | 3 - .../main/Kconfig.projbuild | 19 -- .../twai_network_example_listen_only_main.c | 121 -------- .../twai_network_master/main/CMakeLists.txt | 3 - .../main/Kconfig.projbuild | 19 -- .../main/twai_network_example_master_main.c | 266 ---------------- .../twai_network_slave/main/CMakeLists.txt | 3 - .../twai_network_slave/main/Kconfig.projbuild | 19 -- .../main/twai_network_example_slave_main.c | 293 ------------------ .../CMakeLists.txt | 2 +- .../twai_sender/main/CMakeLists.txt | 5 + .../twai_sender/main/Kconfig.projbuild | 17 + .../twai_sender/main/twai_sender.c | 139 +++++++++ 25 files changed, 581 insertions(+), 1010 deletions(-) create mode 100644 examples/peripherals/twai/twai_network/pytest_twai_network.py delete mode 100644 examples/peripherals/twai/twai_network/pytest_twai_network_example.py rename examples/peripherals/twai/twai_network/{twai_network_slave => twai_listen_only}/CMakeLists.txt (92%) create mode 100644 examples/peripherals/twai/twai_network/twai_listen_only/main/CMakeLists.txt create mode 100644 examples/peripherals/twai/twai_network/twai_listen_only/main/Kconfig.projbuild create mode 100644 examples/peripherals/twai/twai_network/twai_listen_only/main/twai_listen_only.c delete mode 100644 examples/peripherals/twai/twai_network/twai_network_listen_only/CMakeLists.txt delete mode 100644 examples/peripherals/twai/twai_network/twai_network_listen_only/main/CMakeLists.txt delete mode 100644 examples/peripherals/twai/twai_network/twai_network_listen_only/main/Kconfig.projbuild delete mode 100644 examples/peripherals/twai/twai_network/twai_network_listen_only/main/twai_network_example_listen_only_main.c delete mode 100644 examples/peripherals/twai/twai_network/twai_network_master/main/CMakeLists.txt delete mode 100644 examples/peripherals/twai/twai_network/twai_network_master/main/Kconfig.projbuild delete mode 100644 examples/peripherals/twai/twai_network/twai_network_master/main/twai_network_example_master_main.c delete mode 100644 examples/peripherals/twai/twai_network/twai_network_slave/main/CMakeLists.txt delete mode 100644 examples/peripherals/twai/twai_network/twai_network_slave/main/Kconfig.projbuild delete mode 100644 examples/peripherals/twai/twai_network/twai_network_slave/main/twai_network_example_slave_main.c rename examples/peripherals/twai/twai_network/{twai_network_master => twai_sender}/CMakeLists.txt (92%) create mode 100644 examples/peripherals/twai/twai_network/twai_sender/main/CMakeLists.txt create mode 100644 examples/peripherals/twai/twai_network/twai_sender/main/Kconfig.projbuild create mode 100644 examples/peripherals/twai/twai_network/twai_sender/main/twai_sender.c diff --git a/components/esp_driver_twai/esp_twai_onchip.c b/components/esp_driver_twai/esp_twai_onchip.c index 9ebf2ef9c6..383bd8f1c3 100644 --- a/components/esp_driver_twai/esp_twai_onchip.c +++ b/components/esp_driver_twai/esp_twai_onchip.c @@ -122,16 +122,19 @@ static void _ctrlr_release(int ctrlr_id) static esp_err_t _node_config_io(twai_onchip_ctx_t *node, const twai_onchip_node_config_t *node_config) { - ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(node_config->io_cfg.tx), ESP_ERR_INVALID_ARG, TAG, "Invalid tx gpio num"); + ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(node_config->io_cfg.tx) || (node_config->flags.enable_listen_only && (node_config->io_cfg.tx == -1)), ESP_ERR_INVALID_ARG, TAG, "Invalid tx gpio num"); ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(node_config->io_cfg.rx), ESP_ERR_INVALID_ARG, TAG, "Invalid rx gpio num"); + uint64_t reserve_mask = 0; // Set RX pin gpio_set_pull_mode(node_config->io_cfg.rx, GPIO_PULLUP_ONLY); // pullup to avoid noise if no connection to transceiver gpio_matrix_input(node_config->io_cfg.rx, twai_periph_signals[node->ctrlr_id].rx_sig, false); // Set TX pin - uint64_t reserve_mask = BIT64(node_config->io_cfg.tx); - gpio_matrix_output(node_config->io_cfg.tx, twai_periph_signals[node->ctrlr_id].tx_sig, false, false); + if (node_config->io_cfg.tx != -1) { // listen only node is able to not have TX pin + reserve_mask |= BIT64(node_config->io_cfg.tx); + gpio_matrix_output(node_config->io_cfg.tx, twai_periph_signals[node->ctrlr_id].tx_sig, false, false); + } //Configure output clock pin (Optional) if (GPIO_IS_VALID_OUTPUT_GPIO(node_config->io_cfg.quanta_clk_out)) { @@ -611,7 +614,7 @@ static esp_err_t _node_parse_rx(twai_node_handle_t node, twai_frame_t *rx_frame) esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twai_node_handle_t *node_ret) { esp_err_t ret = ESP_OK; - ESP_RETURN_ON_FALSE(node_config->tx_queue_depth > 0, ESP_ERR_INVALID_ARG, TAG, "tx_queue_depth at least 1"); + ESP_RETURN_ON_FALSE((node_config->tx_queue_depth > 0) || node_config->flags.enable_listen_only, ESP_ERR_INVALID_ARG, TAG, "tx_queue_depth at least 1"); ESP_RETURN_ON_FALSE(!node_config->intr_priority || (BIT(node_config->intr_priority) & ESP_INTR_FLAG_LOWMED), ESP_ERR_INVALID_ARG, TAG, "Invalid intr_priority level"); // Allocate TWAI node from internal memory because it contains atomic variable @@ -627,7 +630,7 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa // state is in bus_off before enabled atomic_store(&node->state, TWAI_ERROR_BUS_OFF); node->tx_mount_queue = xQueueCreateWithCaps(node_config->tx_queue_depth, sizeof(twai_frame_t *), TWAI_MALLOC_CAPS); - ESP_GOTO_ON_FALSE(node->tx_mount_queue, ESP_ERR_NO_MEM, err, TAG, "no_mem"); + ESP_GOTO_ON_FALSE(node->tx_mount_queue || node_config->flags.enable_listen_only, ESP_ERR_NO_MEM, err, TAG, "no_mem"); uint32_t intr_flags = TWAI_INTR_ALLOC_FLAGS; intr_flags |= (node_config->intr_priority > 0) ? BIT(node_config->intr_priority) : ESP_INTR_FLAG_LOWMED; ESP_GOTO_ON_ERROR(esp_intr_alloc(twai_periph_signals[ctrlr_id].irq_id, intr_flags, _node_isr_main, (void *)node, &node->intr_hdl), diff --git a/docs/en/api-reference/peripherals/twai.rst b/docs/en/api-reference/peripherals/twai.rst index ce7a43a005..396683acb1 100644 --- a/docs/en/api-reference/peripherals/twai.rst +++ b/docs/en/api-reference/peripherals/twai.rst @@ -373,6 +373,7 @@ Application Examples - :example:`peripherals/twai/twai_utils` demonstrates how to use the TWAI (Two-Wire Automotive Interface) APIs to create a command-line interface for TWAI bus communication, supporting frame transmission/reception, filtering, monitoring, and both classic and FD formats for testing and debugging TWAI networks. - :example:`peripherals/twai/twai_error_recovery` demonstrates how to recover nodes from the bus-off state and resume communication, as well as bus error reporting, node state changes, and other event information. + - :example:`peripherals/twai/twai_network` using 2 nodes with different roles: transmitting and listening, demonstrates how to use the driver for single and bulk data transmission, as well as configure filters to receive these data. API Reference ------------- diff --git a/docs/zh_CN/api-reference/peripherals/twai.rst b/docs/zh_CN/api-reference/peripherals/twai.rst index a2d9a81fd5..2c4a5ed622 100644 --- a/docs/zh_CN/api-reference/peripherals/twai.rst +++ b/docs/zh_CN/api-reference/peripherals/twai.rst @@ -373,6 +373,7 @@ TWAI控制器能够检测由于总线干扰产生的/损坏的不符合帧格式 - :example:`peripherals/twai/twai_utils` 演示了如何使用 TWAI(Two-Wire Automotive Interface,双线汽车接口)API 创建一个命令行工具,用于 TWAI 总线通信,支持帧的发送/接收、过滤、监控,以及经典和 FD 格式,以便测试和调试 TWAI 网络。 - :example:`peripherals/twai/twai_error_recovery` 演示了总线错误上报,节点状态变化等事件信息,以及如何从离线状态恢复节点并重新进行通信。 + - :example:`peripherals/twai/twai_network` 通过发送、监听, 2 个不同角色的节点,演示了如何使用驱动程序进行单次的和大量的数据发送,以及配置过滤器以接收这些数据。 API 参考 -------- diff --git a/examples/peripherals/.build-test-rules.yml b/examples/peripherals/.build-test-rules.yml index 7ebc331b6b..108219a239 100644 --- a/examples/peripherals/.build-test-rules.yml +++ b/examples/peripherals/.build-test-rules.yml @@ -511,12 +511,12 @@ examples/peripherals/twai/twai_error_recovery: examples/peripherals/twai/twai_network: disable: - - if: SOC_TWAI_SUPPORTED != 1 or SOC_TWAI_SUPPORT_FD == 1 - reason: This example not support FD + - if: SOC_TWAI_SUPPORTED != 1 disable_test: - - if: 1 == 1 - temporary: true - reason: Test is flakey, TODO IDF-2939 + - if: IDF_TARGET == "esp32" + reason: esp32c5,esp32 test has been disabled, because C5 twai don't support listen only in network test, see errata issue 5 + depends_components: + - esp_driver_twai examples/peripherals/twai/twai_utils: disable: diff --git a/examples/peripherals/twai/twai_network/README.md b/examples/peripherals/twai/twai_network/README.md index 9d5878a880..a0c7f8a64f 100644 --- a/examples/peripherals/twai/twai_network/README.md +++ b/examples/peripherals/twai/twai_network/README.md @@ -1,189 +1,163 @@ -| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S2 | ESP32-S3 | -| ----------------- | ----- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | +| Supported Targets | ESP32 | ESP32-C3 | ESP32-C5 | ESP32-C6 | ESP32-H2 | ESP32-H21 | ESP32-P4 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | -------- | -------- | # TWAI Network Example -(See the README.md file in the upper level 'examples' directory for more information about examples.) +This example demonstrates TWAI (Two-Wire Automotive Interface) network communication using the ESP-IDF TWAI driver. It consists of two programs that showcase different aspects of TWAI bus communication. -This example demonstrates how to use the TWAI driver to program a target (ESP32, ESP32-S2, ESP32-S3 or ESP32-C3) as a TWAI node, and have the two nodes (Network Master and Network Slave) communicate on a TWAI network. The Listen Only node is optional and acts as a network monitor meaning that it only receives messages and does not influence the bus in any way (i.e. doesn't not acknowledge or send error frames). +## Overview -Note that concept of master/slave in this example refers to which node initiates -and stops the transfer of a stream of data messages. +### Programs -## How to use example +- **twai_sender**: Sends periodic heartbeat messages and large data packets +- **twai_listen_only**: Monitors specific message ID using a filter -### Hardware Required +### Key Features -This example requires at least two targets (e.g., an ESP32 or ESP32-S2) to act as the Network Master and Network Slave. The third target (Listen Only) is optional. Each target must be connected to an external transceiver (e.g., a SN65HVD23X transceiver). The transceivers must then be interconnected to form a TWAI network. +- Event-driven message handling with callbacks +- Message filtering using acceptance filters in listen-only mode +- Single/Burst data transmission and reception +- Real-time bus error and node status reporting -The following diagram illustrates an example network: +## Hardware Setup -```text - ---------- ---------- -------------- - | Master | | Slave | | Listen Only | - | | | | | | - | TX RX | | TX RX | | TX RX | - ---------- ---------- -------------- - | | | | | | - | | | | | | - ---------- ---------- ---------- - | D R | | D R | | D R | - | | | | | | - | VP230 | | VP230 | | VP230 | - | | | | | | - | H L | | H L | | H L | - ---------- ---------- ---------- - | | | | | | - | | | | | | - |--x------|-----x------|-----x------|--| H - | | | - |---------x------------x------------x--| L +### Wiring -``` +For multi-device testing: +1. Connect TWAI transceivers to each ESP32xx +2. Wire TWAI_H and TWAI_L between all devices using line topology +3. Add 120Ω termination resistors at both ends of the bus -Note: If you don't have an external transceiver, you can still run the [TWAI Self Test example](../twai_self_test/README.md) +For single-device testing, enable self_test mode in the sender. -### Configure the project +### GPIO Configuration -For each node in the TWAI network (i.e., Master, Slave, Listen Only)... +The GPIO pins can be configured using menuconfig: -* Set the target of the build (where `{IDF_TARGET}` stands for the target chip such as `esp32` or `esp32s2`). -* Then run `menuconfig` to configure the example. - -```sh -idf.py set-target {IDF_TARGET} +```bash idf.py menuconfig ``` -* Under `Example Configuration`, configure the pin assignments using the options `TX GPIO Number` and `RX GPIO Number` according to how the target was connected to the transceiver. By default, `TX GPIO Number` and `RX GPIO Number` are set to the following values: - * On the ESP32, `TX GPIO Number` and `RX GPIO Number` default to `21` and `22` respectively - * On other chips, `TX GPIO Number` and `RX GPIO Number` default to `0` and `2` respectively +Navigate to: `Example Configuration` → Configure the following: +- **TWAI TX GPIO Num**: GPIO pin for TWAI TX +- **TWAI RX GPIO Num**: GPIO pin for TWAI RX -### Build and Flash - -For each node, build the project and flash it to the board, then run monitor tool to view serial output: - -```sh -idf.py -p PORT flash monitor +**Default configuration:** +```c +#define TWAI_TX_GPIO GPIO_NUM_4 +#define TWAI_RX_GPIO GPIO_NUM_5 +#define TWAI_BITRATE 1000000 // 1 Mbps ``` -(Replace PORT with the name of the serial port to use.) +## Message Types -(To exit the serial monitor, type ``Ctrl-]``.) +| ID | Type | Frequency | Size | Description | +|----|------|-----------|------|-------------| +| 0x7FF | Heartbeat | 1 Hz | 8 bytes | Timestamp data | +| 0x100 | Data | Every 10s | 1000 bytes | Test data (125 frames) | -See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. +## Building and Running -## Example Output +### Build each program (sender, listen_only): -Network Master +**Enter the sub-application directory and run:** -```text -I (345) TWAI Master: Driver installed -I (345) TWAI Master: Driver started -I (345) TWAI Master: Transmitting ping -I (3105) TWAI Master: Transmitted start command -I (3105) TWAI Master: Received data value 339 -... -I (5545) TWAI Master: Received data value 584 -I (5545) TWAI Master: Transmitted stop command -I (5595) TWAI Master: Driver stopped -I (6595) TWAI Master: Driver started -I (6595) TWAI Master: Transmitting ping -I (7095) TWAI Master: Transmitted start command -I (7095) TWAI Master: Received data value 738 -... -I (9535) TWAI Master: Received data value 983 -I (9535) TWAI Master: Transmitted stop command -I (9585) TWAI Master: Driver stopped -I (10585) TWAI Master: Driver started -I (10585) TWAI Master: Transmitting ping -I (11085) TWAI Master: Transmitted start command -I (11085) TWAI Master: Received data value 1137 -... -I (13525) TWAI Master: Received data value 1382 -I (13525) TWAI Master: Transmitted stop command -I (13575) TWAI Master: Driver stopped -I (14575) TWAI Master: Driver uninstalled +```bash +idf.py set-target esp32 build flash monitor ``` -Network Slave +## Expected Output -```text -Slave starting in 3 -Slave starting in 2 -Slave starting in 1 -I (6322) TWAI Slave: Driver installed -I (6322) TWAI Slave: Driver started -I (6462) TWAI Slave: Transmitted ping response -I (6712) TWAI Slave: Start transmitting data -I (6712) TWAI Slave: Transmitted data value 339 -... -I (9162) TWAI Slave: Transmitted data value 584 -I (9212) TWAI Slave: Transmitted stop response -I (9312) TWAI Slave: Driver stopped -I (10312) TWAI Slave: Driver started -I (10452) TWAI Slave: Transmitted ping response -I (10702) TWAI Slave: Start transmitting data -I (10702) TWAI Slave: Transmitted data value 738 -... -I (13152) TWAI Slave: Transmitted data value 983 -I (13202) TWAI Slave: Transmitted stop response -I (13302) TWAI Slave: Driver stopped -I (14302) TWAI Slave: Driver started -I (14442) TWAI Slave: Transmitted ping response -I (14692) TWAI Slave: Start transmitting data -I (14692) TWAI Slave: Transmitted data value 1137 -... -I (17142) TWAI Slave: Transmitted data value 1382 -I (17192) TWAI Slave: Transmitted stop response -I (17292) TWAI Slave: Driver stopped -I (18292) TWAI Slave: Driver uninstalled +### Sender +``` +===================TWAI Sender Example Starting...=================== +I (xxx) twai_sender: TWAI Sender started successfully +I (xxx) twai_sender: Sending messages on IDs: 0x100 (data), 0x7FF (heartbeat) +I (xxx) twai_sender: Sending heartbeat message: 1234567890 +I (xxx) twai_sender: Sending packet of 1000 bytes in 125 frames ``` -Network Listen Only - -```text -I (326) TWAI Listen Only: Driver installed -I (326) TWAI Listen Only: Driver started -I (366) TWAI Listen Only: Received master ping -... -I (1866) TWAI Listen Only: Received master ping -I (1866) TWAI Listen Only: Received slave ping response -I (2116) TWAI Listen Only: Received master start command -I (2116) TWAI Listen Only: Received data value 329 -... -I (4566) TWAI Listen Only: Received data value 574 -I (4566) TWAI Listen Only: Received master stop command -I (4606) TWAI Listen Only: Received slave stop response -I (5606) TWAI Listen Only: Received master ping -I (5856) TWAI Listen Only: Received master ping -I (5856) TWAI Listen Only: Received slave ping response -I (6106) TWAI Listen Only: Received master start command -I (6106) TWAI Listen Only: Received data value 728 -... -I (8556) TWAI Listen Only: Received data value 973 -I (8556) TWAI Listen Only: Received master stop command -I (8596) TWAI Listen Only: Received slave stop response -I (9596) TWAI Listen Only: Received master ping -I (9846) TWAI Listen Only: Received master ping -I (9846) TWAI Listen Only: Received slave ping response -I (10096) TWAI Listen Only: Received master start command -I (10096) TWAI Listen Only: Received data value 1127 -... -I (12546) TWAI Listen Only: Received data value 1372 -I (12546) TWAI Listen Only: Received master stop command -I (12586) TWAI Listen Only: Received slave stop response -I (12586) TWAI Listen Only: Driver stopped -I (12586) TWAI Listen Only: Driver uninstalled - +### Listen-Only Monitor +``` +===================TWAI Listen Only Example Starting...=================== +I (xxx) twai_listen: Buffer initialized: 200 slots for burst data +I (xxx) twai_listen: TWAI node created +I (xxx) twai_listen: Filter enabled for ID: 0x100 Mask: 0x7F0 +I (xxx) twai_listen: TWAI start listening... +I (xxx) twai_listen: RX: 100 [8] 0 0 0 0 0 0 0 0 +I (xxx) twai_listen: RX: 100 [8] 1 1 1 1 1 1 1 1 ``` -## Example Breakdown +## Implementation Details -The communication between the Network Master and Network Slave execute the following steps over multiple iterations: +### Message Buffering -1. Both master and slave go through install and start their TWAI drivers independently. -2. The master repeatedly sends **PING** messages until it receives a **PING_RESP** (ping response message) from the slave. The slave will only send a **PING_RESP** message when it receives a **PING** message from the master. -3. Once the master has received the **PING_RESP** from the slave, it will send a **START_CMD** message to the slave. -4. Upon receiving the **START_CMD** message, the slave will start transmitting **DATA** messages until the master sends a **STOP_CMD**. The master will send the **STOP_CMD** after receiving N **DATA** messages from the slave (N = 50 by default). -5. When the slave receives the **STOP_CMD**, it will confirm that it has stopped by sending a **STOP_RESP** message to the master. +Each program uses a buffer pool to handle incoming messages efficiently: + +- **Sender**: Small buffer for transmission completion tracking +- **Listen-Only**: 100-slot buffer for monitoring filtered traffic + +### Operating Modes + +- **Normal Mode** (Sender): Participates in bus communication, sends ACK frames +- **Listen-Only Mode** (Monitor): Receives filtered messages without transmitting anything + +### Message Filtering + +The listen-only monitor uses hardware acceptance filters to receive only specific message IDs: + +```c +twai_mask_filter_config_t data_filter = { + .id = TWAI_DATA_ID, + .mask = 0x7F0, // Match high 7 bits of the ID, ignore low 4 bits + .is_ext = false, // Receive only standard ID +}; +``` + +### Error Handling + +- Bus error logging and status monitoring + +## Configuration + +### Customizing Buffer Sizes + +Adjust buffer sizes in each program as needed: +```c +#define POLL_DEPTH 200 // Listen-only buffer size +``` + +### Changing Message IDs + +Update the message ID definitions: +```c +#define TWAI_DATA_ID 0x100 +#define TWAI_HEARTBEAT_ID 0x7FF +``` + +## Use Cases + +This example is suitable for: + +- Learning TWAI bus communication +- Testing TWAI network setups +- Developing custom TWAI protocols +- Bus monitoring and debugging +- Prototyping automotive communication systems + +## Troubleshooting + +### No Communication +- Check GPIO pin connections +- Verify bitrate settings match between devices +- Ensure proper bus termination + +### Buffer Overflows +- Increase buffer size (`POLL_DEPTH`) +- Reduce bus message transmission rate +- Optimize message processing code + +### Bus Errors +- Check physical bus wiring +- Verify termination resistors (120Ω at each end) +- Monitor error counters with `twai_node_get_info()` diff --git a/examples/peripherals/twai/twai_network/pytest_twai_network.py b/examples/peripherals/twai/twai_network/pytest_twai_network.py new file mode 100644 index 0000000000..ea0cfb56a5 --- /dev/null +++ b/examples/peripherals/twai/twai_network/pytest_twai_network.py @@ -0,0 +1,107 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import os.path +import subprocess + +import pytest +from can import Bus +from can import Message +from pytest_embedded_idf import IdfDut +from pytest_embedded_idf.utils import soc_filtered_targets + +TWAI_SUPPORTED_TARGETS = soc_filtered_targets('SOC_TWAI_SUPPORTED == 1') + + +# Socket CAN fixture +@pytest.fixture(name='socket_can') +def fixture_create_socket_can() -> Bus: + start_command = 'sudo ip link set can0 up type can bitrate 1000000' + stop_command = 'sudo ip link set can0 down' + try: + subprocess.run(start_command, shell=True, capture_output=True, text=True) + except Exception as e: + print(f'Open bus Error: {e}') + bus = Bus(interface='socketcan', channel='can0', bitrate=1000000) + yield bus # test invoked here + bus.shutdown() + subprocess.run(stop_command, shell=True, capture_output=True, text=True) + + +# Generate minimal combinations that each target appears in each app +def generate_target_combinations(target_list: list, count: int = 2) -> list: + combinations = [] + num_targets = len(target_list) + for round_num in range(num_targets): + selected_targets = [target_list[(round_num + i) % num_targets] for i in range(count)] + combinations.append('|'.join(selected_targets)) + + return combinations + + +@pytest.mark.twai_std +@pytest.mark.parametrize('count', [2], indirect=True) +@pytest.mark.timeout(120) +@pytest.mark.parametrize( + 'app_path,target', + [ + ( + f'{os.path.join(os.path.dirname(__file__), "twai_listen_only")}|' + f'{os.path.join(os.path.dirname(__file__), "twai_sender")}', + target_combo, + ) + for target_combo in generate_target_combinations(TWAI_SUPPORTED_TARGETS) + ], + indirect=True, +) +@pytest.mark.temp_skip_ci( + targets=['esp32c5,esp32'], reason="C5 twai don't support listen only in network test, see errata issue 5" +) +def test_twai_network_multi(dut: tuple[IdfDut, IdfDut], socket_can: Bus) -> None: + """ + Test TWAI network communication between two nodes: + - dut[0]: listener (first chip) - uses twai_listen_only + - dut[1]: sender (second chip) - uses twai_sender + """ + + # Print chip information for debugging + print(f'===> Pytest testing with chips: {dut[0].app.target} (listener), {dut[1].app.target} (sender)') + + # Initialize listener node first + dut[0].expect('===================TWAI Listen Only Example Starting...===================') + dut[0].expect('TWAI start listening...') + + # Initialize sender node and start communication + dut[1].expect('===================TWAI Sender Example Starting...===================') + dut[1].expect('TWAI Sender started successfully') + + # Verify communication is working + # Wait for sender to send messages + dut[1].expect('Sending heartbeat message:', timeout=10) + + # Check that listener is receiving data + dut[0].expect('RX:', timeout=15) # Listener should see filtered messages + + # Check if socket receive any messages + socket_rcv_cnt = 0 + for i in range(100): + msg = socket_can.recv(timeout=1) + if msg is not None: + socket_rcv_cnt += 1 + print(f'Socket receive {socket_rcv_cnt} messages') + assert socket_rcv_cnt > 50, 'Socket NO messages' + + # Wait a bit more to ensure stable communication + dut[1].expect('Sending packet of', timeout=10) + dut[0].expect('RX:', timeout=10) + + # Check if esp32 receive messages from usb can + message = Message( + arbitration_id=0x10A, + is_extended_id=False, + data=b'Hi ESP32', + ) + print('USB CAN Send:', message) + socket_can.send(message, timeout=0.2) + dut[0].expect_exact('RX: 10a [8] 48 69 20 45 53 50 33 32', timeout=10) # ASCII: Hi ESP32 + + print('===> TWAI network communication test completed successfully') diff --git a/examples/peripherals/twai/twai_network/pytest_twai_network_example.py b/examples/peripherals/twai/twai_network/pytest_twai_network_example.py deleted file mode 100644 index 5f7042e940..0000000000 --- a/examples/peripherals/twai/twai_network/pytest_twai_network_example.py +++ /dev/null @@ -1,90 +0,0 @@ -# SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD -# SPDX-License-Identifier: CC0-1.0 -import os.path -from threading import Thread -from typing import Tuple - -import pytest -from pytest_embedded import Dut - -# Define tuple of strings to expect for each DUT. -master_expect = ('TWAI Master: Driver installed', 'TWAI Master: Driver uninstalled') -slave_expect = ('TWAI Slave: Driver installed', 'TWAI Slave: Driver uninstalled') -listen_only_expect = ( - 'TWAI Listen Only: Driver installed', - 'TWAI Listen Only: Driver uninstalled', -) - - -def dut_thread_callback(**kwargs) -> None: # type: ignore - # Parse keyword arguments - dut = kwargs['dut'] # Get DUT from kwargs - expected = kwargs['expected'] - result = kwargs[ - 'result' - ] # Get result[out] from kwargs. MUST be of mutable type e.g. list - - # Must reset again as flashing during start_app will reset multiple times, causing unexpected results - dut.reset() - - for string in expected: - dut.expect(string, 20) - - # Mark thread has run to completion without any exceptions - result[0] = True - - -@pytest.mark.skip(reason="there's not a good approach to sync multiple DUTs") -@pytest.mark.twai_network -@pytest.mark.parametrize( - 'count, app_path', - [ - ( - 3, - f'{os.path.join(os.path.dirname(__file__), "twai_network_master")}|' - f'{os.path.join(os.path.dirname(__file__), "twai_network_slave")}|' - f'{os.path.join(os.path.dirname(__file__), "twai_network_listen_only")}', - ), - ], - indirect=True, -) -def test_twai_network_example(dut: Tuple[Dut, Dut, Dut]) -> None: - dut_master = dut[0] - dut_slave = dut[1] - dut_listen_only = dut[2] - - # Create dict of keyword arguments for each dut - results = [[False], [False], [False]] - master_kwargs = {'dut': dut_master, 'result': results[0], 'expected': master_expect} - slave_kwargs = {'dut': dut_slave, 'result': results[1], 'expected': slave_expect} - listen_only_kwargs = { - 'dut': dut_listen_only, - 'result': results[2], - 'expected': listen_only_expect, - } - - # Create thread for each dut - dut_master_thread = Thread( - target=dut_thread_callback, name='Master Thread', kwargs=master_kwargs - ) - dut_slave_thread = Thread( - target=dut_thread_callback, name='Slave Thread', kwargs=slave_kwargs - ) - dut_listen_only_thread = Thread( - target=dut_thread_callback, name='Listen Only Thread', kwargs=listen_only_kwargs - ) - - # Start each thread - dut_listen_only_thread.start() - dut_master_thread.start() - dut_slave_thread.start() - - # Wait for threads to complete - dut_listen_only_thread.join() - dut_master_thread.join() - dut_slave_thread.join() - - # check each thread ran to completion - for result in results: - if result[0] is not True: - raise Exception('One or more threads did not run successfully') diff --git a/examples/peripherals/twai/twai_network/twai_network_slave/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_listen_only/CMakeLists.txt similarity index 92% rename from examples/peripherals/twai/twai_network/twai_network_slave/CMakeLists.txt rename to examples/peripherals/twai/twai_network/twai_listen_only/CMakeLists.txt index dd8dec0f26..5bb36d37d1 100644 --- a/examples/peripherals/twai/twai_network/twai_network_slave/CMakeLists.txt +++ b/examples/peripherals/twai/twai_network/twai_listen_only/CMakeLists.txt @@ -5,4 +5,4 @@ cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) # "Trim" the build. Include the minimal set of components, main, and anything it depends on. idf_build_set_property(MINIMAL_BUILD ON) -project(twai_network_slave) +project(twai_listen_only) diff --git a/examples/peripherals/twai/twai_network/twai_listen_only/main/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_listen_only/main/CMakeLists.txt new file mode 100644 index 0000000000..53533a4b72 --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_listen_only/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "twai_listen_only.c" + REQUIRES esp_driver_twai + INCLUDE_DIRS "." +) diff --git a/examples/peripherals/twai/twai_network/twai_listen_only/main/Kconfig.projbuild b/examples/peripherals/twai/twai_network/twai_listen_only/main/Kconfig.projbuild new file mode 100644 index 0000000000..395fdb7a34 --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_listen_only/main/Kconfig.projbuild @@ -0,0 +1,17 @@ +menu "Example Configuration" + + config EXAMPLE_TWAI_TX_GPIO + int "TWAI TX GPIO Num" + range 0 SOC_GPIO_OUT_RANGE_MAX + default 4 + help + GPIO number for TWAI TX to transceiver. + + config EXAMPLE_TWAI_RX_GPIO + int "TWAI RX GPIO Num" + range 0 SOC_GPIO_IN_RANGE_MAX + default 5 + help + GPIO number for TWAI RX from transceiver. + +endmenu diff --git a/examples/peripherals/twai/twai_network/twai_listen_only/main/twai_listen_only.c b/examples/peripherals/twai/twai_network/twai_listen_only/main/twai_listen_only.c new file mode 100644 index 0000000000..fa53395bbb --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_listen_only/main/twai_listen_only.c @@ -0,0 +1,146 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_twai.h" +#include "esp_twai_onchip.h" + +#define TWAI_LISTENER_TX_GPIO -1 // Listen only node doesn't need TX pin +#define TWAI_LISTENER_RX_GPIO CONFIG_EXAMPLE_TWAI_RX_GPIO +#define TWAI_BITRATE 1000000 + +// Message IDs (must match sender) +#define TWAI_DATA_ID 0x100 + +// Buffer for burst data handling +#define POLL_DEPTH 200 + +static const char *TAG = "twai_listen"; + +typedef struct { + twai_frame_t frame; + uint8_t data[TWAI_FRAME_MAX_LEN]; +} twai_listener_data_t; + +typedef struct { + twai_node_handle_t node_hdl; + twai_listener_data_t *rx_pool; + SemaphoreHandle_t free_pool_semaphore; + SemaphoreHandle_t rx_result_semaphore; + int write_idx; + int read_idx; +} twai_listener_ctx_t; + +// Error callback +static bool IRAM_ATTR twai_listener_on_error_callback(twai_node_handle_t handle, const twai_error_event_data_t *edata, void *user_ctx) +{ + ESP_EARLY_LOGW(TAG, "bus error: 0x%x", edata->err_flags.val); + return false; +} + +// Node state +static bool IRAM_ATTR twai_listener_on_state_change_callback(twai_node_handle_t handle, const twai_state_change_event_data_t *edata, void *user_ctx) +{ + const char *twai_state_name[] = {"error_active", "error_warning", "error_passive", "bus_off"}; + ESP_EARLY_LOGI(TAG, "state changed: %s -> %s", twai_state_name[edata->old_sta], twai_state_name[edata->new_sta]); + return false; +} + +// TWAI receive callback - store data and signal +static bool IRAM_ATTR twai_listener_rx_callback(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx) +{ + BaseType_t woken; + twai_listener_ctx_t *ctx = (twai_listener_ctx_t *)user_ctx; + + if (xSemaphoreTakeFromISR(ctx->free_pool_semaphore, &woken) != pdTRUE) { + ESP_EARLY_LOGI(TAG, "Pool full, dropping frame"); + return (woken == pdTRUE); + } + if (twai_node_receive_from_isr(handle, &ctx->rx_pool[ctx->write_idx].frame) == ESP_OK) { + ctx->write_idx = (ctx->write_idx + 1) % POLL_DEPTH; + xSemaphoreGiveFromISR(ctx->rx_result_semaphore, &woken); + } + return (woken == pdTRUE); +} + +void app_main(void) +{ + printf("===================TWAI Listen Only Example Starting...===================\n"); + + // Create semaphore for receive notification + twai_listener_ctx_t twai_listener_ctx = {0}; + twai_listener_ctx.free_pool_semaphore = xSemaphoreCreateCounting(POLL_DEPTH, POLL_DEPTH); + twai_listener_ctx.rx_result_semaphore = xSemaphoreCreateCounting(POLL_DEPTH, 0); + assert(twai_listener_ctx.free_pool_semaphore != NULL); + assert(twai_listener_ctx.rx_result_semaphore != NULL); + + twai_listener_ctx.rx_pool = calloc(POLL_DEPTH, sizeof(twai_listener_data_t)); + assert(twai_listener_ctx.rx_pool != NULL); + for (int i = 0; i < POLL_DEPTH; i++) { + twai_listener_ctx.rx_pool[i].frame.buffer = twai_listener_ctx.rx_pool[i].data; + twai_listener_ctx.rx_pool[i].frame.buffer_len = sizeof(twai_listener_ctx.rx_pool[i].data); + } + ESP_LOGI(TAG, "Buffer initialized: %d slots for burst data", POLL_DEPTH); + + // Configure TWAI node + twai_onchip_node_config_t node_config = { + .io_cfg = { + .tx = TWAI_LISTENER_TX_GPIO, + .rx = TWAI_LISTENER_RX_GPIO, + .quanta_clk_out = -1, + .bus_off_indicator = -1, + }, + .bit_timing.bitrate = TWAI_BITRATE, + .flags.enable_listen_only = true, + }; + + // Create TWAI node + ESP_ERROR_CHECK(twai_new_node_onchip(&node_config, &twai_listener_ctx.node_hdl)); + ESP_LOGI(TAG, "TWAI node created"); + + // Configure acceptance filter + twai_mask_filter_config_t data_filter = { + .id = TWAI_DATA_ID, + .mask = 0x7F0, // Match high 7 bits of the ID, ignore low 4 bits + .is_ext = false, // Receive only standard ID + }; + ESP_ERROR_CHECK(twai_node_config_mask_filter(twai_listener_ctx.node_hdl, 0, &data_filter)); + ESP_LOGI(TAG, "Filter enabled for ID: 0x%03X Mask: 0x%03X", data_filter.id, data_filter.mask); + + // Register callbacks + twai_event_callbacks_t callbacks = { + .on_rx_done = twai_listener_rx_callback, + .on_error = twai_listener_on_error_callback, + .on_state_change = twai_listener_on_state_change_callback, + }; + ESP_ERROR_CHECK(twai_node_register_event_callbacks(twai_listener_ctx.node_hdl, &callbacks, &twai_listener_ctx)); + + // Enable TWAI node + ESP_ERROR_CHECK(twai_node_enable(twai_listener_ctx.node_hdl)); + ESP_LOGI(TAG, "TWAI start listening..."); + + // Main loop - process all buffered data when signaled + while (1) { + if (xSemaphoreTake(twai_listener_ctx.rx_result_semaphore, portMAX_DELAY) == pdTRUE) { + twai_frame_t *frame = &twai_listener_ctx.rx_pool[twai_listener_ctx.read_idx].frame; + ESP_LOGI(TAG, "RX: %x [%d] %x %x %x %x %x %x %x %x", \ + frame->header.id, frame->header.dlc, frame->buffer[0], frame->buffer[1], frame->buffer[2], frame->buffer[3], frame->buffer[4], frame->buffer[5], frame->buffer[6], frame->buffer[7]); + twai_listener_ctx.read_idx = (twai_listener_ctx.read_idx + 1) % POLL_DEPTH; + xSemaphoreGive(twai_listener_ctx.free_pool_semaphore); + } + } + + // Cleanup + vSemaphoreDelete(twai_listener_ctx.rx_result_semaphore); + vSemaphoreDelete(twai_listener_ctx.free_pool_semaphore); + free(twai_listener_ctx.rx_pool); + ESP_ERROR_CHECK(twai_node_disable(twai_listener_ctx.node_hdl)); + ESP_ERROR_CHECK(twai_node_delete(twai_listener_ctx.node_hdl)); +} diff --git a/examples/peripherals/twai/twai_network/twai_network_listen_only/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_network_listen_only/CMakeLists.txt deleted file mode 100644 index f5b56fa7ff..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_listen_only/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -# The following lines of boilerplate have to be in your project's CMakeLists -# in this exact order for cmake to work correctly -cmake_minimum_required(VERSION 3.16) - -include($ENV{IDF_PATH}/tools/cmake/project.cmake) -# "Trim" the build. Include the minimal set of components, main, and anything it depends on. -idf_build_set_property(MINIMAL_BUILD ON) -project(twai_network_listen_only) diff --git a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_network_listen_only/main/CMakeLists.txt deleted file mode 100644 index 9e924d4b1a..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "twai_network_example_listen_only_main.c" - REQUIRES driver - INCLUDE_DIRS ".") diff --git a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/Kconfig.projbuild b/examples/peripherals/twai/twai_network/twai_network_listen_only/main/Kconfig.projbuild deleted file mode 100644 index 14a2c83965..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/Kconfig.projbuild +++ /dev/null @@ -1,19 +0,0 @@ -menu "Example Configuration" - - config EXAMPLE_TX_GPIO_NUM - int "TX GPIO number" - default 21 if IDF_TARGET_ESP32 - default 0 - help - This option selects the GPIO pin used for the TX signal. Connect the - TX signal to your transceiver. - - config EXAMPLE_RX_GPIO_NUM - int "RX GPIO number" - default 22 if IDF_TARGET_ESP32 - default 2 - help - This option selects the GPIO pin used for the RX signal. Connect the - RX signal to your transceiver. - -endmenu diff --git a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/twai_network_example_listen_only_main.c b/examples/peripherals/twai/twai_network/twai_network_listen_only/main/twai_network_example_listen_only_main.c deleted file mode 100644 index dcfd828740..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_listen_only/main/twai_network_example_listen_only_main.c +++ /dev/null @@ -1,121 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2010-2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -/* - * The following example demonstrates a Listen Only node in a TWAI network. The - * Listen Only node will not take part in any TWAI bus activity (no acknowledgments - * and no error frames). This example will execute multiple iterations, with each - * iteration the Listen Only node will do the following: - * 1) Listen for ping and ping response - * 2) Listen for start command - * 3) Listen for data messages - * 4) Listen for stop and stop response - */ -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "freertos/semphr.h" -#include "esp_err.h" -#include "esp_log.h" -#include "driver/twai.h" - -/* --------------------- Definitions and static variables ------------------ */ -//Example Configuration -#define NO_OF_ITERS 3 -#define RX_TASK_PRIO 9 -#define TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM -#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM -#define EXAMPLE_TAG "TWAI Listen Only" - -#define ID_MASTER_STOP_CMD 0x0A0 -#define ID_MASTER_START_CMD 0x0A1 -#define ID_MASTER_PING 0x0A2 -#define ID_SLAVE_STOP_RESP 0x0B0 -#define ID_SLAVE_DATA 0x0B1 -#define ID_SLAVE_PING_RESP 0x0B2 - -static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); -static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS(); -//Set TX queue length to 0 due to listen only mode -static const twai_general_config_t g_config = {.mode = TWAI_MODE_LISTEN_ONLY, - .tx_io = TX_GPIO_NUM, .rx_io = RX_GPIO_NUM, - .clkout_io = TWAI_IO_UNUSED, .bus_off_io = TWAI_IO_UNUSED, - .tx_queue_len = 0, .rx_queue_len = 5, - .alerts_enabled = TWAI_ALERT_NONE, - .clkout_divider = 0 - }; - -static SemaphoreHandle_t rx_sem; - -/* --------------------------- Tasks and Functions -------------------------- */ - -static void twai_receive_task(void *arg) -{ - xSemaphoreTake(rx_sem, portMAX_DELAY); - bool start_cmd = false; - bool stop_resp = false; - uint32_t iterations = 0; - - while (iterations < NO_OF_ITERS) { - twai_message_t rx_msg; - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_MASTER_PING) { - ESP_LOGI(EXAMPLE_TAG, "Received master ping"); - } else if (rx_msg.identifier == ID_SLAVE_PING_RESP) { - ESP_LOGI(EXAMPLE_TAG, "Received slave ping response"); - } else if (rx_msg.identifier == ID_MASTER_START_CMD) { - ESP_LOGI(EXAMPLE_TAG, "Received master start command"); - start_cmd = true; - } else if (rx_msg.identifier == ID_SLAVE_DATA) { - uint32_t data = 0; - for (int i = 0; i < rx_msg.data_length_code; i++) { - data |= (rx_msg.data[i] << (i * 8)); - } - ESP_LOGI(EXAMPLE_TAG, "Received data value %"PRIu32, data); - } else if (rx_msg.identifier == ID_MASTER_STOP_CMD) { - ESP_LOGI(EXAMPLE_TAG, "Received master stop command"); - } else if (rx_msg.identifier == ID_SLAVE_STOP_RESP) { - ESP_LOGI(EXAMPLE_TAG, "Received slave stop response"); - stop_resp = true; - } - if (start_cmd && stop_resp) { - //Each iteration is complete after a start command and stop response is received - iterations++; - start_cmd = 0; - stop_resp = 0; - } - } - - xSemaphoreGive(rx_sem); - vTaskDelete(NULL); -} - -void app_main(void) -{ - rx_sem = xSemaphoreCreateBinary(); - xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY); - - //Install and start TWAI driver - ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config)); - ESP_LOGI(EXAMPLE_TAG, "Driver installed"); - ESP_ERROR_CHECK(twai_start()); - ESP_LOGI(EXAMPLE_TAG, "Driver started"); - - xSemaphoreGive(rx_sem); //Start RX task - vTaskDelay(pdMS_TO_TICKS(100)); - xSemaphoreTake(rx_sem, portMAX_DELAY); //Wait for RX task to complete - - //Stop and uninstall TWAI driver - ESP_ERROR_CHECK(twai_stop()); - ESP_LOGI(EXAMPLE_TAG, "Driver stopped"); - ESP_ERROR_CHECK(twai_driver_uninstall()); - ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled"); - - //Cleanup - vSemaphoreDelete(rx_sem); -} diff --git a/examples/peripherals/twai/twai_network/twai_network_master/main/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_network_master/main/CMakeLists.txt deleted file mode 100644 index 0fd5bdf859..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_master/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "twai_network_example_master_main.c" - REQUIRES driver - INCLUDE_DIRS ".") diff --git a/examples/peripherals/twai/twai_network/twai_network_master/main/Kconfig.projbuild b/examples/peripherals/twai/twai_network/twai_network_master/main/Kconfig.projbuild deleted file mode 100644 index 14a2c83965..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_master/main/Kconfig.projbuild +++ /dev/null @@ -1,19 +0,0 @@ -menu "Example Configuration" - - config EXAMPLE_TX_GPIO_NUM - int "TX GPIO number" - default 21 if IDF_TARGET_ESP32 - default 0 - help - This option selects the GPIO pin used for the TX signal. Connect the - TX signal to your transceiver. - - config EXAMPLE_RX_GPIO_NUM - int "RX GPIO number" - default 22 if IDF_TARGET_ESP32 - default 2 - help - This option selects the GPIO pin used for the RX signal. Connect the - RX signal to your transceiver. - -endmenu diff --git a/examples/peripherals/twai/twai_network/twai_network_master/main/twai_network_example_master_main.c b/examples/peripherals/twai/twai_network/twai_network_master/main/twai_network_example_master_main.c deleted file mode 100644 index cc56a0b2cd..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_master/main/twai_network_example_master_main.c +++ /dev/null @@ -1,266 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2010-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -/* - * The following example demonstrates a master node in a TWAI network. The master - * node is responsible for initiating and stopping the transfer of data messages. - * The example will execute multiple iterations, with each iteration the master - * node will do the following: - * 1) Start the TWAI driver - * 2) Repeatedly send ping messages until a ping response from slave is received - * 3) Send start command to slave and receive data messages from slave - * 4) Send stop command to slave and wait for stop response from slave - * 5) Stop the TWAI driver - */ -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "freertos/semphr.h" -#include "esp_err.h" -#include "esp_log.h" -#include "driver/twai.h" - -/* --------------------- Definitions and static variables ------------------ */ -//Example Configuration -#define PING_PERIOD_MS 250 -#define NO_OF_DATA_MSGS 50 -#define NO_OF_ITERS 3 -#define ITER_DELAY_MS 1000 -#define RX_TASK_PRIO 8 -#define TX_TASK_PRIO 9 -#define CTRL_TSK_PRIO 10 -#define TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM -#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM -#define EXAMPLE_TAG "TWAI Master" - -#define ID_MASTER_STOP_CMD 0x0A0 -#define ID_MASTER_START_CMD 0x0A1 -#define ID_MASTER_PING 0x0A2 -#define ID_SLAVE_STOP_RESP 0x0B0 -#define ID_SLAVE_DATA 0x0B1 -#define ID_SLAVE_PING_RESP 0x0B2 - -typedef enum { - TX_SEND_PINGS, - TX_SEND_START_CMD, - TX_SEND_STOP_CMD, - TX_TASK_EXIT, -} tx_task_action_t; - -typedef enum { - RX_RECEIVE_PING_RESP, - RX_RECEIVE_DATA, - RX_RECEIVE_STOP_RESP, - RX_TASK_EXIT, -} rx_task_action_t; - -static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS(); -static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); -static const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NORMAL); - -static const twai_message_t ping_message = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 1, // Is single shot (won't retry on error or NACK) - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_MASTER_PING, - .data_length_code = 0, - .data = {0}, -}; - -static const twai_message_t start_message = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_MASTER_START_CMD, - .data_length_code = 0, - .data = {0}, -}; - -static const twai_message_t stop_message = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_MASTER_STOP_CMD, - .data_length_code = 0, - .data = {0}, -}; - -static QueueHandle_t tx_task_queue; -static QueueHandle_t rx_task_queue; -static SemaphoreHandle_t stop_ping_sem; -static SemaphoreHandle_t ctrl_task_sem; -static SemaphoreHandle_t done_sem; - -/* --------------------------- Tasks and Functions -------------------------- */ - -static void twai_receive_task(void *arg) -{ - while (1) { - rx_task_action_t action; - xQueueReceive(rx_task_queue, &action, portMAX_DELAY); - - if (action == RX_RECEIVE_PING_RESP) { - //Listen for ping response from slave - while (1) { - twai_message_t rx_msg; - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_SLAVE_PING_RESP) { - xSemaphoreGive(stop_ping_sem); - xSemaphoreGive(ctrl_task_sem); - break; - } - } - } else if (action == RX_RECEIVE_DATA) { - //Receive data messages from slave - uint32_t data_msgs_rec = 0; - while (data_msgs_rec < NO_OF_DATA_MSGS) { - twai_message_t rx_msg; - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_SLAVE_DATA) { - uint32_t data = 0; - for (int i = 0; i < rx_msg.data_length_code; i++) { - data |= (rx_msg.data[i] << (i * 8)); - } - ESP_LOGI(EXAMPLE_TAG, "Received data value %"PRIu32, data); - data_msgs_rec ++; - } - } - xSemaphoreGive(ctrl_task_sem); - } else if (action == RX_RECEIVE_STOP_RESP) { - //Listen for stop response from slave - while (1) { - twai_message_t rx_msg; - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_SLAVE_STOP_RESP) { - xSemaphoreGive(ctrl_task_sem); - break; - } - } - } else if (action == RX_TASK_EXIT) { - break; - } - } - vTaskDelete(NULL); -} - -static void twai_transmit_task(void *arg) -{ - while (1) { - tx_task_action_t action; - xQueueReceive(tx_task_queue, &action, portMAX_DELAY); - - if (action == TX_SEND_PINGS) { - //Repeatedly transmit pings - ESP_LOGI(EXAMPLE_TAG, "Transmitting ping"); - while (xSemaphoreTake(stop_ping_sem, 0) != pdTRUE) { - twai_transmit(&ping_message, portMAX_DELAY); - vTaskDelay(pdMS_TO_TICKS(PING_PERIOD_MS)); - } - } else if (action == TX_SEND_START_CMD) { - //Transmit start command to slave - twai_transmit(&start_message, portMAX_DELAY); - ESP_LOGI(EXAMPLE_TAG, "Transmitted start command"); - } else if (action == TX_SEND_STOP_CMD) { - //Transmit stop command to slave - twai_transmit(&stop_message, portMAX_DELAY); - ESP_LOGI(EXAMPLE_TAG, "Transmitted stop command"); - } else if (action == TX_TASK_EXIT) { - break; - } - } - vTaskDelete(NULL); -} - -static void twai_control_task(void *arg) -{ - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - tx_task_action_t tx_action; - rx_task_action_t rx_action; - - for (int iter = 0; iter < NO_OF_ITERS; iter++) { - ESP_ERROR_CHECK(twai_start()); - ESP_LOGI(EXAMPLE_TAG, "Driver started"); - - //Start transmitting pings, and listen for ping response - tx_action = TX_SEND_PINGS; - rx_action = RX_RECEIVE_PING_RESP; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - - //Send Start command to slave, and receive data messages - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - tx_action = TX_SEND_START_CMD; - rx_action = RX_RECEIVE_DATA; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - - //Send Stop command to slave when enough data messages have been received. Wait for stop response - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - tx_action = TX_SEND_STOP_CMD; - rx_action = RX_RECEIVE_STOP_RESP; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - ESP_ERROR_CHECK(twai_stop()); - ESP_LOGI(EXAMPLE_TAG, "Driver stopped"); - vTaskDelay(pdMS_TO_TICKS(ITER_DELAY_MS)); - } - //Stop TX and RX tasks - tx_action = TX_TASK_EXIT; - rx_action = RX_TASK_EXIT; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - - //Delete Control task - xSemaphoreGive(done_sem); - vTaskDelete(NULL); -} - -void app_main(void) -{ - //Create tasks, queues, and semaphores - rx_task_queue = xQueueCreate(1, sizeof(rx_task_action_t)); - tx_task_queue = xQueueCreate(1, sizeof(tx_task_action_t)); - ctrl_task_sem = xSemaphoreCreateBinary(); - stop_ping_sem = xSemaphoreCreateBinary(); - done_sem = xSemaphoreCreateBinary(); - xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY); - xTaskCreatePinnedToCore(twai_transmit_task, "TWAI_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY); - xTaskCreatePinnedToCore(twai_control_task, "TWAI_ctrl", 4096, NULL, CTRL_TSK_PRIO, NULL, tskNO_AFFINITY); - - //Install TWAI driver - ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config)); - ESP_LOGI(EXAMPLE_TAG, "Driver installed"); - - xSemaphoreGive(ctrl_task_sem); //Start control task - xSemaphoreTake(done_sem, portMAX_DELAY); //Wait for completion - - //Uninstall TWAI driver - ESP_ERROR_CHECK(twai_driver_uninstall()); - ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled"); - - //Cleanup - vQueueDelete(rx_task_queue); - vQueueDelete(tx_task_queue); - vSemaphoreDelete(ctrl_task_sem); - vSemaphoreDelete(stop_ping_sem); - vSemaphoreDelete(done_sem); -} diff --git a/examples/peripherals/twai/twai_network/twai_network_slave/main/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_network_slave/main/CMakeLists.txt deleted file mode 100644 index 5220b9c8ff..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_slave/main/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "twai_network_example_slave_main.c" - REQUIRES driver - INCLUDE_DIRS ".") diff --git a/examples/peripherals/twai/twai_network/twai_network_slave/main/Kconfig.projbuild b/examples/peripherals/twai/twai_network/twai_network_slave/main/Kconfig.projbuild deleted file mode 100644 index 14a2c83965..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_slave/main/Kconfig.projbuild +++ /dev/null @@ -1,19 +0,0 @@ -menu "Example Configuration" - - config EXAMPLE_TX_GPIO_NUM - int "TX GPIO number" - default 21 if IDF_TARGET_ESP32 - default 0 - help - This option selects the GPIO pin used for the TX signal. Connect the - TX signal to your transceiver. - - config EXAMPLE_RX_GPIO_NUM - int "RX GPIO number" - default 22 if IDF_TARGET_ESP32 - default 2 - help - This option selects the GPIO pin used for the RX signal. Connect the - RX signal to your transceiver. - -endmenu diff --git a/examples/peripherals/twai/twai_network/twai_network_slave/main/twai_network_example_slave_main.c b/examples/peripherals/twai/twai_network/twai_network_slave/main/twai_network_example_slave_main.c deleted file mode 100644 index 63bcece707..0000000000 --- a/examples/peripherals/twai/twai_network/twai_network_slave/main/twai_network_example_slave_main.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2010-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: CC0-1.0 - */ - -/* - * The following example demonstrates a slave node in a TWAI network. The slave - * node is responsible for sending data messages to the master. The example will - * execute multiple iterations, with each iteration the slave node will do the - * following: - * 1) Start the TWAI driver - * 2) Listen for ping messages from master, and send ping response - * 3) Listen for start command from master - * 4) Send data messages to master and listen for stop command - * 5) Send stop response to master - * 6) Stop the TWAI driver - */ - -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "freertos/semphr.h" -#include "esp_err.h" -#include "esp_log.h" -#include "driver/twai.h" - -/* --------------------- Definitions and static variables ------------------ */ -//Example Configuration -#define DATA_PERIOD_MS 50 -#define NO_OF_ITERS 3 -#define ITER_DELAY_MS 1000 -#define RX_TASK_PRIO 8 //Receiving task priority -#define TX_TASK_PRIO 9 //Sending task priority -#define CTRL_TSK_PRIO 10 //Control task priority -#define TX_GPIO_NUM CONFIG_EXAMPLE_TX_GPIO_NUM -#define RX_GPIO_NUM CONFIG_EXAMPLE_RX_GPIO_NUM -#define EXAMPLE_TAG "TWAI Slave" - -#define ID_MASTER_STOP_CMD 0x0A0 -#define ID_MASTER_START_CMD 0x0A1 -#define ID_MASTER_PING 0x0A2 -#define ID_SLAVE_STOP_RESP 0x0B0 -#define ID_SLAVE_DATA 0x0B1 -#define ID_SLAVE_PING_RESP 0x0B2 - -typedef enum { - TX_SEND_PING_RESP, - TX_SEND_DATA, - TX_SEND_STOP_RESP, - TX_TASK_EXIT, -} tx_task_action_t; - -typedef enum { - RX_RECEIVE_PING, - RX_RECEIVE_START_CMD, - RX_RECEIVE_STOP_CMD, - RX_TASK_EXIT, -} rx_task_action_t; - -static const twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(TX_GPIO_NUM, RX_GPIO_NUM, TWAI_MODE_NORMAL); -static const twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS(); -static const twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL(); - -static const twai_message_t ping_resp = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_SLAVE_PING_RESP, - .data_length_code = 0, - .data = {0}, -}; - -static const twai_message_t stop_resp = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_SLAVE_STOP_RESP, - .data_length_code = 0, - .data = {0}, -}; - -// Data bytes of data message will be initialized in the transmit task -static twai_message_t data_message = { - // Message type and format settings - .extd = 0, // Standard Format message (11-bit ID) - .rtr = 0, // Send a data frame - .ss = 0, // Not single shot - .self = 0, // Not a self reception request - .dlc_non_comp = 0, // DLC is less than 8 - // Message ID and payload - .identifier = ID_SLAVE_DATA, - .data_length_code = 4, - .data = {1, 2, 3, 4}, -}; - -static QueueHandle_t tx_task_queue; -static QueueHandle_t rx_task_queue; -static SemaphoreHandle_t ctrl_task_sem; -static SemaphoreHandle_t stop_data_sem; -static SemaphoreHandle_t done_sem; - -/* --------------------------- Tasks and Functions -------------------------- */ - -static void twai_receive_task(void *arg) -{ - while (1) { - rx_task_action_t action; - xQueueReceive(rx_task_queue, &action, portMAX_DELAY); - if (action == RX_RECEIVE_PING) { - //Listen for pings from master - twai_message_t rx_msg; - while (1) { - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_MASTER_PING) { - xSemaphoreGive(ctrl_task_sem); - break; - } - } - } else if (action == RX_RECEIVE_START_CMD) { - //Listen for start command from master - twai_message_t rx_msg; - while (1) { - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_MASTER_START_CMD) { - xSemaphoreGive(ctrl_task_sem); - break; - } - } - } else if (action == RX_RECEIVE_STOP_CMD) { - //Listen for stop command from master - twai_message_t rx_msg; - while (1) { - twai_receive(&rx_msg, portMAX_DELAY); - if (rx_msg.identifier == ID_MASTER_STOP_CMD) { - xSemaphoreGive(stop_data_sem); - xSemaphoreGive(ctrl_task_sem); - break; - } - } - } else if (action == RX_TASK_EXIT) { - break; - } - } - vTaskDelete(NULL); -} - -static void twai_transmit_task(void *arg) -{ - while (1) { - tx_task_action_t action; - xQueueReceive(tx_task_queue, &action, portMAX_DELAY); - - if (action == TX_SEND_PING_RESP) { - //Transmit ping response to master - twai_transmit(&ping_resp, portMAX_DELAY); - ESP_LOGI(EXAMPLE_TAG, "Transmitted ping response"); - xSemaphoreGive(ctrl_task_sem); - } else if (action == TX_SEND_DATA) { - //Transmit data messages until stop command is received - ESP_LOGI(EXAMPLE_TAG, "Start transmitting data"); - while (1) { - //FreeRTOS tick count used to simulate sensor data - uint32_t sensor_data = xTaskGetTickCount(); - for (int i = 0; i < 4; i++) { - data_message.data[i] = (sensor_data >> (i * 8)) & 0xFF; - } - twai_transmit(&data_message, portMAX_DELAY); - ESP_LOGI(EXAMPLE_TAG, "Transmitted data value %"PRIu32, sensor_data); - vTaskDelay(pdMS_TO_TICKS(DATA_PERIOD_MS)); - if (xSemaphoreTake(stop_data_sem, 0) == pdTRUE) { - break; - } - } - } else if (action == TX_SEND_STOP_RESP) { - //Transmit stop response to master - twai_transmit(&stop_resp, portMAX_DELAY); - ESP_LOGI(EXAMPLE_TAG, "Transmitted stop response"); - xSemaphoreGive(ctrl_task_sem); - } else if (action == TX_TASK_EXIT) { - break; - } - } - vTaskDelete(NULL); -} - -static void twai_control_task(void *arg) -{ - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - tx_task_action_t tx_action; - rx_task_action_t rx_action; - - for (int iter = 0; iter < NO_OF_ITERS; iter++) { - ESP_ERROR_CHECK(twai_start()); - ESP_LOGI(EXAMPLE_TAG, "Driver started"); - - //Listen of pings from master - rx_action = RX_RECEIVE_PING; - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - - //Send ping response - tx_action = TX_SEND_PING_RESP; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - - //Listen for start command - rx_action = RX_RECEIVE_START_CMD; - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - - //Start sending data messages and listen for stop command - tx_action = TX_SEND_DATA; - rx_action = RX_RECEIVE_STOP_CMD; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - - //Send stop response - tx_action = TX_SEND_STOP_RESP; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xSemaphoreTake(ctrl_task_sem, portMAX_DELAY); - - //Wait for bus to become free - twai_status_info_t status_info; - twai_get_status_info(&status_info); - while (status_info.msgs_to_tx > 0) { - vTaskDelay(pdMS_TO_TICKS(100)); - twai_get_status_info(&status_info); - } - - ESP_ERROR_CHECK(twai_stop()); - ESP_LOGI(EXAMPLE_TAG, "Driver stopped"); - vTaskDelay(pdMS_TO_TICKS(ITER_DELAY_MS)); - } - - //Stop TX and RX tasks - tx_action = TX_TASK_EXIT; - rx_action = RX_TASK_EXIT; - xQueueSend(tx_task_queue, &tx_action, portMAX_DELAY); - xQueueSend(rx_task_queue, &rx_action, portMAX_DELAY); - - //Delete Control task - xSemaphoreGive(done_sem); - vTaskDelete(NULL); -} - -void app_main(void) -{ - //Add short delay to allow master it to initialize first - for (int i = 3; i > 0; i--) { - printf("Slave starting in %d\n", i); - vTaskDelay(pdMS_TO_TICKS(1000)); - } - - //Create semaphores and tasks - tx_task_queue = xQueueCreate(1, sizeof(tx_task_action_t)); - rx_task_queue = xQueueCreate(1, sizeof(rx_task_action_t)); - ctrl_task_sem = xSemaphoreCreateBinary(); - stop_data_sem = xSemaphoreCreateBinary(); - done_sem = xSemaphoreCreateBinary(); - xTaskCreatePinnedToCore(twai_receive_task, "TWAI_rx", 4096, NULL, RX_TASK_PRIO, NULL, tskNO_AFFINITY); - xTaskCreatePinnedToCore(twai_transmit_task, "TWAI_tx", 4096, NULL, TX_TASK_PRIO, NULL, tskNO_AFFINITY); - xTaskCreatePinnedToCore(twai_control_task, "TWAI_ctrl", 4096, NULL, CTRL_TSK_PRIO, NULL, tskNO_AFFINITY); - - //Install TWAI driver, trigger tasks to start - ESP_ERROR_CHECK(twai_driver_install(&g_config, &t_config, &f_config)); - ESP_LOGI(EXAMPLE_TAG, "Driver installed"); - - xSemaphoreGive(ctrl_task_sem); //Start Control task - xSemaphoreTake(done_sem, portMAX_DELAY); //Wait for tasks to complete - - //Uninstall TWAI driver - ESP_ERROR_CHECK(twai_driver_uninstall()); - ESP_LOGI(EXAMPLE_TAG, "Driver uninstalled"); - - //Cleanup - vSemaphoreDelete(ctrl_task_sem); - vSemaphoreDelete(stop_data_sem); - vSemaphoreDelete(done_sem); - vQueueDelete(tx_task_queue); - vQueueDelete(rx_task_queue); -} diff --git a/examples/peripherals/twai/twai_network/twai_network_master/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_sender/CMakeLists.txt similarity index 92% rename from examples/peripherals/twai/twai_network/twai_network_master/CMakeLists.txt rename to examples/peripherals/twai/twai_network/twai_sender/CMakeLists.txt index 2cfde1c6f9..0b3f7b267f 100644 --- a/examples/peripherals/twai/twai_network/twai_network_master/CMakeLists.txt +++ b/examples/peripherals/twai/twai_network/twai_sender/CMakeLists.txt @@ -5,4 +5,4 @@ cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) # "Trim" the build. Include the minimal set of components, main, and anything it depends on. idf_build_set_property(MINIMAL_BUILD ON) -project(twai_network_master) +project(twai_sender) diff --git a/examples/peripherals/twai/twai_network/twai_sender/main/CMakeLists.txt b/examples/peripherals/twai/twai_network/twai_sender/main/CMakeLists.txt new file mode 100644 index 0000000000..d9ba487f69 --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_sender/main/CMakeLists.txt @@ -0,0 +1,5 @@ +idf_component_register( + SRCS "twai_sender.c" + REQUIRES esp_driver_twai esp_timer + INCLUDE_DIRS "." +) diff --git a/examples/peripherals/twai/twai_network/twai_sender/main/Kconfig.projbuild b/examples/peripherals/twai/twai_network/twai_sender/main/Kconfig.projbuild new file mode 100644 index 0000000000..395fdb7a34 --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_sender/main/Kconfig.projbuild @@ -0,0 +1,17 @@ +menu "Example Configuration" + + config EXAMPLE_TWAI_TX_GPIO + int "TWAI TX GPIO Num" + range 0 SOC_GPIO_OUT_RANGE_MAX + default 4 + help + GPIO number for TWAI TX to transceiver. + + config EXAMPLE_TWAI_RX_GPIO + int "TWAI RX GPIO Num" + range 0 SOC_GPIO_IN_RANGE_MAX + default 5 + help + GPIO number for TWAI RX from transceiver. + +endmenu diff --git a/examples/peripherals/twai/twai_network/twai_sender/main/twai_sender.c b/examples/peripherals/twai/twai_network/twai_sender/main/twai_sender.c new file mode 100644 index 0000000000..794cd21ff1 --- /dev/null +++ b/examples/peripherals/twai/twai_network/twai_sender/main/twai_sender.c @@ -0,0 +1,139 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_timer.h" +#include "esp_twai.h" +#include "esp_twai_onchip.h" + +#define TWAI_SENDER_TX_GPIO CONFIG_EXAMPLE_TWAI_TX_GPIO +#define TWAI_SENDER_RX_GPIO CONFIG_EXAMPLE_TWAI_RX_GPIO +#define TWAI_QUEUE_DEPTH 10 +#define TWAI_BITRATE 1000000 + +// Message IDs +#define TWAI_DATA_ID 0x100 +#define TWAI_HEARTBEAT_ID 0x7FF +#define TWAI_DATA_LEN 1000 + +static const char *TAG = "twai_sender"; + +typedef struct { + twai_frame_t frame; + uint8_t data[TWAI_FRAME_MAX_LEN]; +} twai_sender_data_t; + +// Transmission completion callback +static IRAM_ATTR bool twai_sender_tx_done_callback(twai_node_handle_t handle, const twai_tx_done_event_data_t *edata, void *user_ctx) +{ + BaseType_t woken; + SemaphoreHandle_t *tx_semaphore = (SemaphoreHandle_t *)user_ctx; + if (!edata->is_tx_success) { + ESP_EARLY_LOGW(TAG, "Failed to transmit message, ID: 0x%X", edata->done_tx_frame->header.id); + } + xSemaphoreGiveFromISR(*tx_semaphore, &woken); + return (woken == pdTRUE); +} + +// Bus error callback +static IRAM_ATTR bool twai_sender_on_error_callback(twai_node_handle_t handle, const twai_error_event_data_t *edata, void *user_ctx) +{ + ESP_EARLY_LOGW(TAG, "TWAI node error: 0x%x", edata->err_flags.val); + return false; // No task wake required +} + +void app_main(void) +{ + twai_node_handle_t sender_node = NULL; + SemaphoreHandle_t tx_semaphore = NULL; + printf("===================TWAI Sender Example Starting...===================\n"); + + // Configure TWAI node + twai_onchip_node_config_t node_config = { + .io_cfg = { + .tx = TWAI_SENDER_TX_GPIO, + .rx = TWAI_SENDER_RX_GPIO, + .quanta_clk_out = -1, + .bus_off_indicator = -1, + }, + .bit_timing = { + .bitrate = TWAI_BITRATE, + }, + .fail_retry_cnt = 3, + .tx_queue_depth = TWAI_QUEUE_DEPTH, + }; + + // Create TWAI node + ESP_ERROR_CHECK(twai_new_node_onchip(&node_config, &sender_node)); + + // Register transmission completion callback + twai_event_callbacks_t callbacks = { + .on_tx_done = twai_sender_tx_done_callback, + .on_error = twai_sender_on_error_callback, + }; + ESP_ERROR_CHECK(twai_node_register_event_callbacks(sender_node, &callbacks, &tx_semaphore)); + + // Enable TWAI node + ESP_ERROR_CHECK(twai_node_enable(sender_node)); + + // Create semaphore for transmission completion + tx_semaphore = xSemaphoreCreateCounting(howmany(TWAI_DATA_LEN, TWAI_FRAME_MAX_LEN), 0); + assert(tx_semaphore != NULL); + + ESP_LOGI(TAG, "TWAI Sender started successfully"); + ESP_LOGI(TAG, "Sending messages with IDs: 0x%03X (data), 0x%03X (heartbeat)", TWAI_DATA_ID, TWAI_HEARTBEAT_ID); + + while (1) { + // Send heartbeat message + uint64_t timestamp = esp_timer_get_time(); + twai_frame_t tx_frame = { + .header.id = TWAI_HEARTBEAT_ID, + .buffer = (uint8_t *) ×tamp, + .buffer_len = sizeof(timestamp), + }; + ESP_ERROR_CHECK(twai_node_transmit(sender_node, &tx_frame, 500)); + ESP_LOGI(TAG, "Sending heartbeat message: %lld", timestamp); + xSemaphoreTake(tx_semaphore, portMAX_DELAY); + + // Send burst data messages every 10 seconds + if ((timestamp / 1000000) % 10 == 0) { + int num_frames = howmany(TWAI_DATA_LEN, TWAI_FRAME_MAX_LEN); + twai_sender_data_t *data = (twai_sender_data_t *)calloc(num_frames, sizeof(twai_sender_data_t)); + assert(data != NULL); + ESP_LOGI(TAG, "Sending packet of %d bytes in %d frames", TWAI_DATA_LEN, num_frames); + for (int i = 0; i < num_frames; i++) { + data[i].frame.header.id = TWAI_DATA_ID; + data[i].frame.buffer = data[i].data; + data[i].frame.buffer_len = TWAI_FRAME_MAX_LEN; + memset(data[i].data, i, TWAI_FRAME_MAX_LEN); + ESP_ERROR_CHECK(twai_node_transmit(sender_node, &data[i].frame, 500)); + } + + // Frames mounted, wait for all frames to be transmitted + for (int i = 0; i < num_frames; i++) { + xSemaphoreTake(tx_semaphore, portMAX_DELAY); + } + free(data); + } + + vTaskDelay(pdMS_TO_TICKS(1000)); + twai_node_status_t status; + twai_node_get_info(sender_node, &status, NULL); + if (status.state == TWAI_ERROR_BUS_OFF) { + ESP_LOGW(TAG, "Bus-off detected"); + return; + } + } + + vSemaphoreDelete(tx_semaphore); + ESP_ERROR_CHECK(twai_node_disable(sender_node)); + ESP_ERROR_CHECK(twai_node_delete(sender_node)); +}