mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-07 06:34:34 +02:00
twai: add initial version of driver component test
This commit is contained in:
@@ -72,3 +72,7 @@ components/driver/test_apps/touch_sensor_v1:
|
||||
components/driver/test_apps/touch_sensor_v2:
|
||||
disable:
|
||||
- if: SOC_TOUCH_VERSION_2 != 1
|
||||
|
||||
components/driver/test_apps/twai:
|
||||
disable:
|
||||
- if: SOC_TWAI_SUPPORTED != 1
|
||||
|
18
components/driver/test_apps/twai/CMakeLists.txt
Normal file
18
components/driver/test_apps/twai/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
# This is the project CMakeLists.txt file for the test subproject
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(twai_test)
|
||||
|
||||
if(CONFIG_COMPILER_DUMP_RTL_FILES)
|
||||
add_custom_target(check_test_app_sections ALL
|
||||
COMMAND ${PYTHON} $ENV{IDF_PATH}/tools/ci/check_callgraph.py
|
||||
--rtl-dir ${CMAKE_BINARY_DIR}/esp-idf/driver/
|
||||
--elf-file ${CMAKE_BINARY_DIR}/twai_test.elf
|
||||
find-refs
|
||||
--from-sections=.iram0.text
|
||||
--to-sections=.flash.text,.flash.rodata
|
||||
--exit-code
|
||||
DEPENDS ${elf}
|
||||
)
|
||||
endif()
|
8
components/driver/test_apps/twai/README.md
Normal file
8
components/driver/test_apps/twai/README.md
Normal file
@@ -0,0 +1,8 @@
|
||||
| Supported Targets | ESP32 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- | -------- | -------- |
|
||||
|
||||
# Enable Socket CAN Device with bitrate 250Kbps
|
||||
|
||||
```bash
|
||||
sudo ip link set can0 up type can bitrate 250000 restart-ms 100
|
||||
```
|
8
components/driver/test_apps/twai/main/CMakeLists.txt
Normal file
8
components/driver/test_apps/twai/main/CMakeLists.txt
Normal file
@@ -0,0 +1,8 @@
|
||||
set(srcs "test_app_main.c"
|
||||
"test_twai_loop_back.c"
|
||||
"test_twai_interactive.c")
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
idf_component_register(SRCS ${srcs}
|
||||
WHOLE_ARCHIVE)
|
51
components/driver/test_apps/twai/main/test_app_main.c
Normal file
51
components/driver/test_apps/twai/main/test_app_main.c
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
// Some resources are lazy allocated in the TWAI driver, the threshold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-200)
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// _______ ___ ___ _____ _
|
||||
// |_ _\ \ / / \ |_ _| |_ _|__ ___| |_
|
||||
// | | \ \ /\ / / _ \ | | | |/ _ \/ __| __|
|
||||
// | | \ V V / ___ \ | | | | __/\__ \ |_
|
||||
// |_| \_/\_/_/ \_\___| |_|\___||___/\__|
|
||||
printf(" _______ ___ ___ _____ _\r\n");
|
||||
printf("|_ _\\ \\ / / \\ |_ _| |_ _|__ ___| |_\r\n");
|
||||
printf(" | | \\ \\ /\\ / / _ \\ | | | |/ _ \\/ __| __|\r\n");
|
||||
printf(" | | \\ V V / ___ \\ | | | | __/\\__ \\ |_\r\n");
|
||||
printf(" |_| \\_/\\_/_/ \\_\\___| |_|\\___||___/\\__|\r\n");
|
||||
unity_run_menu();
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_attr.h"
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
#include "driver/twai.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
#if CONFIG_TWAI_ISR_IN_IRAM
|
||||
static void IRAM_ATTR test_delay_post_cache_disable(void *args)
|
||||
{
|
||||
esp_rom_delay_us(1000);
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST_CASE("twai_listen_only", "[twai]")
|
||||
{
|
||||
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS();
|
||||
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
|
||||
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 2, TWAI_MODE_LISTEN_ONLY);
|
||||
#if CONFIG_TWAI_ISR_IN_IRAM
|
||||
g_config.intr_flags |= ESP_INTR_FLAG_IRAM;
|
||||
#endif
|
||||
// listen only mode doesn't need a tx queue
|
||||
g_config.tx_queue_len = 0;
|
||||
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
|
||||
TEST_ESP_OK(twai_start());
|
||||
|
||||
#if CONFIG_TWAI_ISR_IN_IRAM
|
||||
printf("disable flash cache and check if we can still receive the frame\r\n");
|
||||
for (int i = 0; i < 100; i++) {
|
||||
unity_utils_run_cache_disable_stub(test_delay_post_cache_disable, NULL);
|
||||
}
|
||||
#endif
|
||||
|
||||
uint8_t expected_data[8] = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88};
|
||||
twai_message_t rx_msg;
|
||||
TEST_ESP_OK(twai_receive(&rx_msg, portMAX_DELAY));
|
||||
|
||||
TEST_ASSERT_EQUAL(0x123, rx_msg.identifier);
|
||||
for (int i = 0; i < rx_msg.data_length_code; i++) {
|
||||
TEST_ASSERT_EQUAL_HEX8(expected_data[i], rx_msg.data[i]);
|
||||
}
|
||||
|
||||
TEST_ESP_OK(twai_stop());
|
||||
TEST_ESP_OK(twai_driver_uninstall());
|
||||
}
|
||||
|
||||
TEST_CASE("twai_remote_request", "[twai]")
|
||||
{
|
||||
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS();
|
||||
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
|
||||
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 2, TWAI_MODE_NORMAL);
|
||||
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
|
||||
TEST_ESP_OK(twai_start());
|
||||
|
||||
twai_message_t req_msg = {
|
||||
.identifier = 0x6688,
|
||||
.data_length_code = 8,
|
||||
.rtr = true, // remote request
|
||||
.extd = true,// extended ID
|
||||
};
|
||||
TEST_ESP_OK(twai_transmit(&req_msg, portMAX_DELAY));
|
||||
|
||||
uint8_t expected_data[8] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80};
|
||||
twai_message_t res_msg;
|
||||
TEST_ESP_OK(twai_receive(&res_msg, portMAX_DELAY));
|
||||
TEST_ASSERT_EQUAL(0x6688, res_msg.identifier);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
TEST_ASSERT_EQUAL_HEX8(expected_data[i], res_msg.data[i]);
|
||||
}
|
||||
|
||||
TEST_ESP_OK(twai_stop());
|
||||
TEST_ESP_OK(twai_driver_uninstall());
|
||||
}
|
117
components/driver/test_apps/twai/main/test_twai_loop_back.c
Normal file
117
components/driver/test_apps/twai/main/test_twai_loop_back.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "driver/twai.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
TEST_CASE("driver_life_cycle", "[twai-loop-back]")
|
||||
{
|
||||
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_100KBITS();
|
||||
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
|
||||
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 0, TWAI_MODE_NO_ACK);
|
||||
printf("install driver\r\n");
|
||||
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
|
||||
// can't install the driver multiple times
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_driver_install(&g_config, &t_config, &f_config));
|
||||
|
||||
printf("start driver\r\n");
|
||||
TEST_ESP_OK(twai_start());
|
||||
// can't start the driver again if it's already in running state
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_start());
|
||||
// can't uninstall the driver before stopping it
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_driver_uninstall());
|
||||
|
||||
printf("stop driver\r\n");
|
||||
TEST_ESP_OK(twai_stop());
|
||||
printf("uninstall driver\r\n");
|
||||
TEST_ESP_OK(twai_driver_uninstall());
|
||||
}
|
||||
|
||||
TEST_CASE("twai_bit_timing", "[twai-loop-back]")
|
||||
{
|
||||
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
|
||||
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 0, TWAI_MODE_NO_ACK);
|
||||
twai_timing_config_t t_config = {
|
||||
.quanta_resolution_hz = 33333, // invalid resolution
|
||||
.tseg_1 = 15,
|
||||
.tseg_2 = 4,
|
||||
.sjw = 1,
|
||||
};
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_ARG, twai_driver_install(&g_config, &t_config, &f_config));
|
||||
|
||||
t_config.quanta_resolution_hz = 2000000;
|
||||
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
|
||||
TEST_ESP_OK(twai_driver_uninstall());
|
||||
}
|
||||
|
||||
TEST_CASE("twai_mode_std_no_ack_25kbps", "[twai-loop-back]")
|
||||
{
|
||||
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_25KBITS();
|
||||
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
|
||||
// bind the TX and RX to the same GPIO to act like a loopback
|
||||
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 0, TWAI_MODE_NO_ACK);
|
||||
printf("install twai driver\r\n");
|
||||
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
|
||||
TEST_ESP_OK(twai_start());
|
||||
|
||||
twai_message_t tx_msg = {
|
||||
.identifier = 0x123,
|
||||
.data_length_code = 8,
|
||||
.data = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88},
|
||||
.self = true, // Transmitted message will also received by the same node
|
||||
};
|
||||
printf("transmit message\r\n");
|
||||
TEST_ESP_OK(twai_transmit(&tx_msg, pdMS_TO_TICKS(1000)));
|
||||
|
||||
printf("receive message\r\n");
|
||||
twai_message_t rx_msg;
|
||||
TEST_ESP_OK(twai_receive(&rx_msg, pdMS_TO_TICKS(1000)));
|
||||
TEST_ASSERT_TRUE(rx_msg.data_length_code == 8);
|
||||
for (int i = 0; i < 8; i++) {
|
||||
TEST_ASSERT_EQUAL(tx_msg.data[i], rx_msg.data[i]);
|
||||
}
|
||||
|
||||
TEST_ESP_OK(twai_stop());
|
||||
TEST_ESP_OK(twai_driver_uninstall());
|
||||
}
|
||||
|
||||
TEST_CASE("twai_mode_ext_no_ack_250kbps", "[twai-loop-back]")
|
||||
{
|
||||
twai_timing_config_t t_config = TWAI_TIMING_CONFIG_250KBITS();
|
||||
twai_filter_config_t f_config = TWAI_FILTER_CONFIG_ACCEPT_ALL();
|
||||
// bind the TX and RX to the same GPIO to act like a loopback
|
||||
twai_general_config_t g_config = TWAI_GENERAL_CONFIG_DEFAULT(0, 0, TWAI_MODE_NO_ACK);
|
||||
printf("install twai driver\r\n");
|
||||
TEST_ESP_OK(twai_driver_install(&g_config, &t_config, &f_config));
|
||||
TEST_ESP_OK(twai_start());
|
||||
|
||||
twai_message_t tx_msg = {
|
||||
.identifier = 0x12345,
|
||||
.data_length_code = 6,
|
||||
.data = {0x11, 0x22, 0x33, 0x44, 0x55, 0x66},
|
||||
.self = true, // Transmitted message will also received by the same node
|
||||
.extd = true, // Extended Frame Format (29bit ID)
|
||||
};
|
||||
printf("transmit message\r\n");
|
||||
TEST_ESP_OK(twai_transmit(&tx_msg, pdMS_TO_TICKS(1000)));
|
||||
|
||||
printf("receive message\r\n");
|
||||
twai_message_t rx_msg;
|
||||
TEST_ESP_OK(twai_receive(&rx_msg, pdMS_TO_TICKS(1000)));
|
||||
TEST_ASSERT_TRUE(rx_msg.data_length_code == 6);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
TEST_ASSERT_EQUAL(tx_msg.data[i], rx_msg.data[i]);
|
||||
}
|
||||
|
||||
TEST_ESP_OK(twai_stop());
|
||||
TEST_ESP_OK(twai_driver_uninstall());
|
||||
}
|
104
components/driver/test_apps/twai/pytest_twai.py
Normal file
104
components/driver/test_apps/twai/pytest_twai.py
Normal file
@@ -0,0 +1,104 @@
|
||||
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import logging
|
||||
from time import sleep
|
||||
|
||||
import pytest
|
||||
from can import Bus, Message
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.esp32c6
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.generic
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'release',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_twai_self(dut: Dut) -> None:
|
||||
dut.expect_exact('Press ENTER to see the list of tests')
|
||||
dut.write('[twai-loop-back]')
|
||||
dut.expect_unity_test_output()
|
||||
|
||||
|
||||
@pytest.fixture(name='socket_can', scope='module')
|
||||
def fixture_create_socket_can() -> Bus:
|
||||
# See README.md for instructions on how to set up the socket CAN with the bitrate
|
||||
bus = Bus(interface='socketcan', channel='can0', bitrate=250000)
|
||||
yield bus
|
||||
bus.shutdown()
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.esp32c6
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.skip(reason='Runner not set up yet')
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'iram_safe',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_twai_listen_only(dut: Dut, socket_can: Bus) -> None:
|
||||
dut.expect_exact('Press ENTER to see the list of tests')
|
||||
|
||||
# TEST_CASE("twai_listen_only", "[twai]")
|
||||
dut.write('"twai_listen_only"')
|
||||
|
||||
# wait the DUT to block at the receive API
|
||||
sleep(0.03)
|
||||
|
||||
message = Message(
|
||||
arbitration_id=0x123,
|
||||
is_extended_id=False,
|
||||
data=[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88],
|
||||
)
|
||||
socket_can.send(message, timeout=0.2)
|
||||
dut.expect_unity_test_output()
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32c3
|
||||
@pytest.mark.esp32c6
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.skip(reason='Runner not set up yet')
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'release',
|
||||
],
|
||||
indirect=True,
|
||||
)
|
||||
def test_twai_remote_request(dut: Dut, socket_can: Bus) -> None:
|
||||
dut.expect_exact('Press ENTER to see the list of tests')
|
||||
|
||||
# TEST_CASE("twai_remote_request", "[twai]")
|
||||
dut.write('"twai_remote_request"')
|
||||
|
||||
while True:
|
||||
req = socket_can.recv(timeout=0.2)
|
||||
# wait for the remote request frame
|
||||
if req is not None and req.is_remote_frame:
|
||||
break
|
||||
|
||||
logging.info(f'Received message: {req}')
|
||||
|
||||
reply = Message(
|
||||
arbitration_id=req.arbitration_id,
|
||||
is_extended_id=req.is_extended_id,
|
||||
data=[0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80],
|
||||
)
|
||||
socket_can.send(reply, timeout=0.2)
|
||||
|
||||
dut.expect_unity_test_output()
|
12
components/driver/test_apps/twai/sdkconfig.ci.iram_safe
Normal file
12
components/driver/test_apps/twai/sdkconfig.ci.iram_safe
Normal file
@@ -0,0 +1,12 @@
|
||||
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||
CONFIG_TWAI_ISR_IN_IRAM=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
||||
|
||||
# silent the error check, as the error string are stored in rodata, causing RTL check failure
|
||||
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
|
||||
|
||||
# place non-ISR FreeRTOS functions in Flash
|
||||
CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH=y
|
||||
|
||||
# twai driver uses assert in the ISR code path
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y
|
5
components/driver/test_apps/twai/sdkconfig.ci.release
Normal file
5
components/driver/test_apps/twai/sdkconfig.ci.release
Normal file
@@ -0,0 +1,5 @@
|
||||
CONFIG_PM_ENABLE=y
|
||||
CONFIG_FREERTOS_USE_TICKLESS_IDLE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_BOOTLOADER_COMPILER_OPTIMIZATION_SIZE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_SILENT=y
|
2
components/driver/test_apps/twai/sdkconfig.defaults
Normal file
2
components/driver/test_apps/twai/sdkconfig.defaults
Normal file
@@ -0,0 +1,2 @@
|
||||
CONFIG_FREERTOS_HZ=1000
|
||||
CONFIG_ESP_TASK_WDT=n
|
@@ -17,3 +17,6 @@ netifaces
|
||||
rangehttpserver
|
||||
dbus-python; sys_platform == 'linux'
|
||||
protobuf
|
||||
|
||||
# for twai tests, communicate with socket can device (e.g. Canable)
|
||||
python-can
|
||||
|
Reference in New Issue
Block a user