|
| 1 | +/* |
| 2 | + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD |
| 3 | + * |
| 4 | + * SPDX-License-Identifier: Apache-2.0 |
| 5 | + */ |
| 6 | + |
| 7 | +#include <string.h> |
| 8 | +#include <sys/param.h> |
| 9 | +#include "unity.h" |
| 10 | +#include "esp_log.h" |
| 11 | +#include "esp_heap_caps.h" |
| 12 | +#include "freertos/FreeRTOS.h" |
| 13 | +#include "test_utils.h" |
| 14 | +#include "esp_twai.h" |
| 15 | +#include "esp_twai_onchip.h" |
| 16 | + |
| 17 | +#define TEST_TX_GPIO 4 |
| 18 | +#define TEST_RX_GPIO 5 |
| 19 | +#define TEST_TWAI_QUEUE_DEPTH 5 |
| 20 | +#define TEST_TRANS_LEN 100 |
| 21 | +#define TEST_FRAME_LEN 7 |
| 22 | +#define TEST_FRAME_NUM howmany(TEST_TRANS_LEN, TEST_FRAME_LEN) |
| 23 | + |
| 24 | +static bool test_driver_install_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx) |
| 25 | +{ |
| 26 | + twai_frame_header_t rx_header; |
| 27 | + if (ESP_OK == twai_node_receive_from_isr(handle, &rx_header, NULL, 0, NULL)) { |
| 28 | + ESP_EARLY_LOGI("Recv ", "id 0x%lx rtr %d", rx_header.id, rx_header.rtr); |
| 29 | + } |
| 30 | + if (rx_header.id != 0x100) { |
| 31 | + TEST_FAIL(); //callback is unregistered, should not run here |
| 32 | + } |
| 33 | + return false; |
| 34 | +} |
| 35 | + |
| 36 | +TEST_CASE("twai install uninstall (loopback)", "[TWAI]") |
| 37 | +{ |
| 38 | + esp_err_t ret; |
| 39 | + twai_node_handle_t node_hdl[SOC_TWAI_CONTROLLER_NUM + 1]; |
| 40 | + twai_onchip_node_config_t node_config = { |
| 41 | + .io_cfg.tx = TEST_TX_GPIO, |
| 42 | + .io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver |
| 43 | + .bit_timing.bitrate = 1000000, |
| 44 | + .data_timing.bitrate = 1000000, |
| 45 | + .tx_queue_depth = TEST_TWAI_QUEUE_DEPTH, |
| 46 | + .flags.enable_loopback = true, |
| 47 | + .flags.enable_self_test = true, |
| 48 | + }; |
| 49 | + |
| 50 | + // loop 10 times to check memory leak |
| 51 | + for (uint8_t loop = 0; loop < 10; loop ++) { |
| 52 | + for (uint8_t i = 0; i < SOC_TWAI_CONTROLLER_NUM + 1; i++) { |
| 53 | + ret = twai_new_node_onchip(&node_config, &node_hdl[i]); |
| 54 | + printf("Install TWAI%d return %s\n", i, esp_err_to_name(ret)); |
| 55 | + TEST_ASSERT(ret == ((i < SOC_TWAI_CONTROLLER_NUM) ? ESP_OK : ESP_ERR_NOT_FOUND)); |
| 56 | + } |
| 57 | + // can't disable before enable |
| 58 | + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_disable(node_hdl[0])); |
| 59 | + twai_event_callbacks_t user_cbs = { |
| 60 | + .on_rx_done = test_driver_install_rx_cb, |
| 61 | + }; |
| 62 | + TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[0], &user_cbs, NULL)); |
| 63 | + |
| 64 | + printf("Test unregister callback\n"); |
| 65 | + user_cbs.on_rx_done = NULL; |
| 66 | + TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[0], &user_cbs, NULL)); |
| 67 | + |
| 68 | + twai_frame_t tx_frame = { |
| 69 | + .header.id = 0x82, |
| 70 | + .header.rtr = true, |
| 71 | + }; |
| 72 | + printf("Test transmit before enable\n"); |
| 73 | + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_transmit(node_hdl[0], &tx_frame, 0)); |
| 74 | + TEST_ESP_OK(twai_node_enable(node_hdl[0])); |
| 75 | + TEST_ESP_OK(twai_node_disable(node_hdl[0])); |
| 76 | + TEST_ESP_OK(twai_node_enable(node_hdl[0])); |
| 77 | + TEST_ESP_OK(twai_node_transmit(node_hdl[0], &tx_frame, 0)); |
| 78 | + |
| 79 | + TEST_ESP_OK(twai_node_disable(node_hdl[0])); |
| 80 | + TEST_ESP_OK(twai_node_delete(node_hdl[0])); |
| 81 | + |
| 82 | + printf("Test install after delete\n"); |
| 83 | + TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl[SOC_TWAI_CONTROLLER_NUM])); |
| 84 | + user_cbs.on_rx_done = test_driver_install_rx_cb, |
| 85 | + TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[SOC_TWAI_CONTROLLER_NUM], &user_cbs, NULL)); |
| 86 | + TEST_ESP_OK(twai_node_enable(node_hdl[SOC_TWAI_CONTROLLER_NUM])); |
| 87 | + tx_frame.header.id = 0x100; |
| 88 | + TEST_ESP_OK(twai_node_transmit(node_hdl[SOC_TWAI_CONTROLLER_NUM], &tx_frame, 0)); |
| 89 | + twai_frame_t rx_frame; |
| 90 | + printf("Test receive from task\n"); |
| 91 | + TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_receive_from_isr(node_hdl[SOC_TWAI_CONTROLLER_NUM], &rx_frame.header, rx_frame.buffer, rx_frame.buffer_len, NULL)); |
| 92 | + |
| 93 | + TEST_ESP_OK(twai_node_disable(node_hdl[SOC_TWAI_CONTROLLER_NUM])); |
| 94 | + for (uint8_t i = 1; i <= SOC_TWAI_CONTROLLER_NUM; i++) { |
| 95 | + printf("Uninstall TWAI%d\n", i - 1); |
| 96 | + TEST_ESP_OK(twai_node_delete(node_hdl[i])); |
| 97 | + } |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +static bool test_enable_disable_rx_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx) |
| 102 | +{ |
| 103 | + twai_frame_t *rx_frame = user_ctx; |
| 104 | + size_t ret_len; |
| 105 | + if (ESP_OK == twai_node_receive_from_isr(handle, &rx_frame->header, rx_frame->buffer, rx_frame->buffer_len, &ret_len)) { |
| 106 | + ESP_EARLY_LOGI("twai", "RX id 0x%x len %d ext %d fd %d brs %d esi %d", rx_frame->header.id, ret_len, rx_frame->header.ide, rx_frame->header.fdf, rx_frame->header.brs, rx_frame->header.esi); |
| 107 | + rx_frame->buffer += rx_frame->buffer_len; |
| 108 | + } |
| 109 | + return false; |
| 110 | +} |
| 111 | + |
| 112 | +TEST_CASE("twai transmit stop resume (loopback)", "[TWAI]") |
| 113 | +{ |
| 114 | + // prepare test memory |
| 115 | + uint8_t *send_pkg_ptr = heap_caps_malloc(TEST_TRANS_LEN, MALLOC_CAP_8BIT); |
| 116 | + uint8_t *recv_pkg_ptr = heap_caps_malloc(TEST_TRANS_LEN, MALLOC_CAP_8BIT); |
| 117 | + TEST_ASSERT(send_pkg_ptr && recv_pkg_ptr); |
| 118 | + printf("Transmit %d bytes package in %d frames\n", TEST_TRANS_LEN, TEST_FRAME_NUM); |
| 119 | + |
| 120 | + twai_node_handle_t node_hdl; |
| 121 | + twai_onchip_node_config_t node_config = { |
| 122 | + .io_cfg.tx = TEST_TX_GPIO, |
| 123 | + .io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver |
| 124 | + .bit_timing.bitrate = 1000000, |
| 125 | + .data_timing.bitrate = 4000000, |
| 126 | + .data_timing.ssp_permill = 700, // ssp 70.0% |
| 127 | + .tx_queue_depth = TEST_TWAI_QUEUE_DEPTH, |
| 128 | + .flags.enable_loopback = true, |
| 129 | + .flags.enable_self_test = true, |
| 130 | + }; |
| 131 | + TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl)); |
| 132 | + |
| 133 | + // reconfig fd timing to 48M/(12+5+6+1)=2MHz, ssp=20/(12+5+6+1)=83% |
| 134 | + twai_timing_advanced_config_t timing_fd = { |
| 135 | + .brp = 1, |
| 136 | + .prop_seg = 12, |
| 137 | + .tseg_1 = 5, |
| 138 | + .tseg_2 = 6, |
| 139 | + .sjw = 3, |
| 140 | + .ssp_offset = 20, |
| 141 | + }; |
| 142 | + TEST_ESP_OK(twai_node_reconfig_timing(node_hdl, NULL, &timing_fd)); |
| 143 | + |
| 144 | + twai_frame_t rx_frame = { |
| 145 | + .buffer = recv_pkg_ptr, |
| 146 | + .buffer_len = TEST_FRAME_LEN, |
| 147 | + }; |
| 148 | + twai_event_callbacks_t user_cbs = { |
| 149 | + .on_rx_done = test_enable_disable_rx_cb, |
| 150 | + }; |
| 151 | + TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, &rx_frame)); |
| 152 | + TEST_ESP_OK(twai_node_enable(node_hdl)); |
| 153 | + |
| 154 | + //create and enqueue all transfers |
| 155 | + twai_frame_t *tx_msgs = heap_caps_calloc(TEST_FRAME_NUM, sizeof(twai_frame_t), MALLOC_CAP_8BIT); |
| 156 | + TEST_ASSERT(tx_msgs); |
| 157 | + for (uint32_t tx_cnt = 0; tx_cnt < TEST_FRAME_NUM; tx_cnt++) { |
| 158 | + tx_msgs[tx_cnt].header.id = tx_cnt | 0x400; |
| 159 | + tx_msgs[tx_cnt].header.ide = !!(tx_cnt % 3); |
| 160 | + tx_msgs[tx_cnt].header.fdf = !!(tx_cnt % 4); |
| 161 | + tx_msgs[tx_cnt].header.brs = !!(tx_cnt % 2); |
| 162 | + tx_msgs[tx_cnt].buffer = send_pkg_ptr + tx_cnt * TEST_FRAME_LEN; |
| 163 | + tx_msgs[tx_cnt].buffer_len = ((tx_cnt + 1) == TEST_FRAME_NUM) ? (TEST_TRANS_LEN - tx_cnt * TEST_FRAME_LEN) : TEST_FRAME_LEN; |
| 164 | + TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msgs[tx_cnt], 500)); |
| 165 | + } |
| 166 | + |
| 167 | + TEST_ESP_OK(twai_node_disable(node_hdl)); |
| 168 | + for (uint8_t i = 3; i > 0; i--) { |
| 169 | + printf("interrupted, %d sec\n", i); |
| 170 | + vTaskDelay(1000); |
| 171 | + } |
| 172 | + printf("continuing ...\n"); |
| 173 | + TEST_ESP_OK(twai_node_enable(node_hdl)); |
| 174 | + |
| 175 | + //waiting pkg receive finish |
| 176 | + while (rx_frame.buffer < recv_pkg_ptr + TEST_TRANS_LEN); |
| 177 | + free(tx_msgs); |
| 178 | + |
| 179 | + // check if pkg receive correct |
| 180 | + printf("pkg check %s!!\n", memcmp(recv_pkg_ptr, send_pkg_ptr, TEST_TRANS_LEN) ? "failed" : "ok"); |
| 181 | + TEST_ASSERT_EQUAL_HEX8_ARRAY(send_pkg_ptr, recv_pkg_ptr, TEST_TRANS_LEN); |
| 182 | + |
| 183 | + free(send_pkg_ptr); |
| 184 | + free(recv_pkg_ptr); |
| 185 | + TEST_ESP_OK(twai_node_disable(node_hdl)); |
| 186 | + TEST_ESP_OK(twai_node_delete(node_hdl)); |
| 187 | +} |
| 188 | + |
| 189 | +static void test_random_trans_generator(twai_node_handle_t node_hdl, uint32_t trans_num) |
| 190 | +{ |
| 191 | + uint8_t send_pkg_ptr[TWAIFD_FRAME_MAX_LEN]; |
| 192 | + twai_frame_t tx_msg = { |
| 193 | + .buffer = send_pkg_ptr, |
| 194 | + }; |
| 195 | + printf("Sending %ld random trans ...\n", trans_num); |
| 196 | + for (uint32_t tx_cnt = 0; tx_cnt < trans_num; tx_cnt++) { |
| 197 | + tx_msg.header.id = tx_cnt | 0xf000; |
| 198 | + tx_msg.header.ide = !!(tx_cnt % 2); |
| 199 | + tx_msg.header.rtr = !!(tx_cnt % 3); |
| 200 | + tx_msg.header.fdf = !!(tx_cnt % 5); |
| 201 | + tx_msg.buffer_len = tx_msg.header.fdf ? (tx_cnt % TWAIFD_FRAME_MAX_LEN) : (tx_cnt % TWAI_FRAME_MAX_LEN); |
| 202 | + TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msg, 0)); |
| 203 | + vTaskDelay(8); //as async transaction, waiting trans done |
| 204 | + } |
| 205 | +} |
| 206 | + |
| 207 | +static bool test_filter_rx_done_cb(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx) |
| 208 | +{ |
| 209 | + uint8_t *test_ctrl = user_ctx; |
| 210 | + size_t ret_len; |
| 211 | + uint8_t recv_pkg_ptr[TWAIFD_FRAME_MAX_LEN]; |
| 212 | + twai_frame_t rx_frame = { |
| 213 | + .buffer = recv_pkg_ptr, |
| 214 | + .buffer_len = TWAIFD_FRAME_MAX_LEN, |
| 215 | + }; |
| 216 | + if (ESP_OK == twai_node_receive_from_isr(handle, &rx_frame.header, rx_frame.buffer, rx_frame.buffer_len, &ret_len)) { |
| 217 | + ESP_EARLY_LOGI("twai", "RX id 0x%4x len %2d ext %d rmt %d fd %d", rx_frame.header.id, ret_len, rx_frame.header.ide, rx_frame.header.rtr, rx_frame.header.fdf); |
| 218 | + switch (test_ctrl[0]) { |
| 219 | + case 0: //filter 0 |
| 220 | + TEST_ASSERT(rx_frame.header.id >= 0x10); |
| 221 | + TEST_ASSERT(!rx_frame.header.ide); |
| 222 | + break; |
| 223 | + case 1: |
| 224 | + case 2: break; |
| 225 | + case 3: //filter 1 |
| 226 | + TEST_ASSERT((rx_frame.header.id & 0xfff0) == 0xf000); |
| 227 | + TEST_ASSERT(rx_frame.header.ide && !rx_frame.header.fdf); |
| 228 | + break; |
| 229 | + case 4: //range filter 0 |
| 230 | + TEST_ASSERT(!rx_frame.header.ide && rx_frame.header.fdf); |
| 231 | + break; |
| 232 | + default: TEST_ASSERT(false); |
| 233 | + } |
| 234 | + test_ctrl[1] ++; |
| 235 | + } |
| 236 | + return false; |
| 237 | +} |
| 238 | + |
| 239 | +TEST_CASE("twai filter (loopback)", "[TWAI]") |
| 240 | +{ |
| 241 | + uint8_t test_ctrl[2]; |
| 242 | + twai_node_handle_t node_hdl; |
| 243 | + twai_onchip_node_config_t node_config = { |
| 244 | + .io_cfg.tx = TEST_TX_GPIO, |
| 245 | + .io_cfg.rx = TEST_TX_GPIO, // Using same pin for test without transceiver |
| 246 | + .bit_timing.bitrate = 1000000, |
| 247 | + .tx_queue_depth = TEST_TWAI_QUEUE_DEPTH, |
| 248 | + .flags.enable_loopback = true, |
| 249 | + .flags.enable_self_test = true, |
| 250 | + }; |
| 251 | + TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl)); |
| 252 | + |
| 253 | + twai_event_callbacks_t user_cbs = { |
| 254 | + .on_rx_done = test_filter_rx_done_cb, |
| 255 | + }; |
| 256 | + TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl, &user_cbs, test_ctrl)); |
| 257 | + |
| 258 | + //------------------ Filter 0 -------------------// |
| 259 | + test_ctrl[0] = 0; |
| 260 | + test_ctrl[1] = 0; |
| 261 | + printf("Testing mask filter 0\n"); |
| 262 | + twai_mask_filter_config_t mfilter_cfg0 = { |
| 263 | + .id = 0x10, |
| 264 | + .mask = 0xf0, |
| 265 | + .is_ext = false, |
| 266 | + }; |
| 267 | + TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 0, &mfilter_cfg0)); |
| 268 | + TEST_ESP_OK(twai_node_enable(node_hdl)); |
| 269 | + test_random_trans_generator(node_hdl, 30); |
| 270 | + TEST_ASSERT_UINT8_WITHIN(30 / 2 - 1, 30 / 2, test_ctrl[1]); //after filter, rx frame should less than tx num |
| 271 | + |
| 272 | + printf("Change to receive ALL\n"); |
| 273 | + test_ctrl[0] = 1; |
| 274 | + test_ctrl[1] = 0; |
| 275 | + // set to receive ALL |
| 276 | + mfilter_cfg0.id = 0; |
| 277 | + mfilter_cfg0.mask = 0; |
| 278 | + TEST_ESP_OK(twai_node_disable(node_hdl)); |
| 279 | + TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 0, &mfilter_cfg0)); |
| 280 | + TEST_ESP_OK(twai_node_enable(node_hdl)); |
| 281 | + test_random_trans_generator(node_hdl, 30); |
| 282 | + TEST_ASSERT_EQUAL(30, test_ctrl[1]); |
| 283 | + |
| 284 | + printf("Change to receive NONE\n"); |
| 285 | + test_ctrl[0] = 2; |
| 286 | + test_ctrl[1] = 0; |
| 287 | + // set to receive NONE |
| 288 | + mfilter_cfg0.id = 0xFFFFFFFF; |
| 289 | + mfilter_cfg0.mask = 0xFFFFFFFF; |
| 290 | + TEST_ESP_OK(twai_node_disable(node_hdl)); |
| 291 | + TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 0, &mfilter_cfg0)); |
| 292 | + TEST_ESP_OK(twai_node_enable(node_hdl)); |
| 293 | + test_random_trans_generator(node_hdl, 30); |
| 294 | + TEST_ASSERT_EQUAL(0, test_ctrl[1]); |
| 295 | + |
| 296 | + //------------------ Filter 1 -------------------// |
| 297 | + test_ctrl[0] = 3; |
| 298 | + test_ctrl[1] = 0; |
| 299 | + printf("Testing mask filter 1\n"); |
| 300 | + twai_mask_filter_config_t mfilter_cfg1 = { |
| 301 | + .id = 0xf000, |
| 302 | + .mask = 0xfff0, |
| 303 | + .is_ext = true, |
| 304 | + .no_fd = true, |
| 305 | + }; |
| 306 | + TEST_ESP_OK(twai_node_disable(node_hdl)); |
| 307 | + TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 1, &mfilter_cfg1)); |
| 308 | + TEST_ESP_OK(twai_node_enable(node_hdl)); |
| 309 | + test_random_trans_generator(node_hdl, 30); |
| 310 | + TEST_ASSERT_UINT8_WITHIN(30 / 2 - 1, 30 / 2, test_ctrl[1]); |
| 311 | + // set to receive NONE |
| 312 | + mfilter_cfg1.id = 0xFFFFFFFF; |
| 313 | + mfilter_cfg1.mask = 0xFFFFFFFF; |
| 314 | + TEST_ESP_OK(twai_node_disable(node_hdl)); |
| 315 | + TEST_ESP_OK(twai_node_config_mask_filter(node_hdl, 1, &mfilter_cfg1)); |
| 316 | + |
| 317 | + //------------------ Range Filter 0 -------------------// |
| 318 | + test_ctrl[0] = 4; |
| 319 | + test_ctrl[1] = 0; |
| 320 | + printf("Testing range filter 0\n"); |
| 321 | + twai_range_filter_config_t rfilter_cfg0 = { |
| 322 | + .range_low = 0, |
| 323 | + .range_high = 0xFFFFFFFF, |
| 324 | + .is_ext = false, |
| 325 | + .no_classic = true, |
| 326 | + }; |
| 327 | + TEST_ESP_OK(twai_node_config_range_filter(node_hdl, 0, &rfilter_cfg0)); |
| 328 | + TEST_ESP_OK(twai_node_enable(node_hdl)); |
| 329 | + test_random_trans_generator(node_hdl, 30); |
| 330 | + TEST_ASSERT_UINT8_WITHIN(30 / 2 - 1, 30 / 2, test_ctrl[1]); |
| 331 | + |
| 332 | + TEST_ESP_OK(twai_node_disable(node_hdl)); |
| 333 | + TEST_ESP_OK(twai_node_delete(node_hdl)); |
| 334 | +} |
0 commit comments