From f02facbe280255aefaed314b1908afb9f99f4654 Mon Sep 17 00:00:00 2001 From: morris Date: Thu, 27 Oct 2022 18:22:52 +0800 Subject: [PATCH 1/5] twai: describe peripheral info Describe peripheral informations in the twai_peirph.c, including: Interrupt number, GPIO matrix signal ID, etc --- components/soc/esp32/CMakeLists.txt | 1 + components/soc/esp32/twai_periph.c | 23 +++++++++++++++ components/soc/esp32c3/CMakeLists.txt | 3 +- components/soc/esp32c3/twai_periph.c | 22 ++++++++++++++ components/soc/esp32c6/CMakeLists.txt | 3 +- components/soc/esp32c6/twai_periph.c | 31 ++++++++++++++++++++ components/soc/esp32s2/CMakeLists.txt | 1 + components/soc/esp32s2/twai_periph.c | 22 ++++++++++++++ components/soc/esp32s3/CMakeLists.txt | 3 +- components/soc/esp32s3/twai_periph.c | 22 ++++++++++++++ components/soc/include/soc/twai_periph.h | 37 ++++++++++++++---------- tools/ci/check_copyright_ignore.txt | 1 - 12 files changed, 149 insertions(+), 20 deletions(-) create mode 100644 components/soc/esp32/twai_periph.c create mode 100644 components/soc/esp32c3/twai_periph.c create mode 100644 components/soc/esp32c6/twai_periph.c create mode 100644 components/soc/esp32s2/twai_periph.c create mode 100644 components/soc/esp32s3/twai_periph.c diff --git a/components/soc/esp32/CMakeLists.txt b/components/soc/esp32/CMakeLists.txt index ff9ee1c5cb..f68511833c 100644 --- a/components/soc/esp32/CMakeLists.txt +++ b/components/soc/esp32/CMakeLists.txt @@ -17,6 +17,7 @@ set(srcs "sdmmc_periph.c" "spi_periph.c" "timer_periph.c" + "twai_periph.c" "touch_sensor_periph.c" "uart_periph.c") diff --git a/components/soc/esp32/twai_periph.c b/components/soc/esp32/twai_periph.c new file mode 100644 index 0000000000..cf763269fe --- /dev/null +++ b/components/soc/esp32/twai_periph.c @@ -0,0 +1,23 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc.h" +#include "soc/twai_periph.h" +#include "soc/gpio_sig_map.h" + +const twai_controller_signal_conn_t twai_controller_periph_signals = { + .controllers = { + [0] = { + .module = PERIPH_TWAI_MODULE, + .irq_id = ETS_TWAI_INTR_SOURCE, + .tx_sig = TWAI_TX_IDX, + .rx_sig = TWAI_RX_IDX, + .bus_off_sig = TWAI_BUS_OFF_ON_IDX, + .clk_out_sig = TWAI_CLKOUT_IDX, + .stand_by_sig = -1, + }, + } +}; diff --git a/components/soc/esp32c3/CMakeLists.txt b/components/soc/esp32c3/CMakeLists.txt index fb9dfaee3e..367b4da596 100644 --- a/components/soc/esp32c3/CMakeLists.txt +++ b/components/soc/esp32c3/CMakeLists.txt @@ -12,7 +12,8 @@ set(srcs "i2c_periph.c" "uart_periph.c" "temperature_sensor_periph.c" - "timer_periph.c") + "timer_periph.c" + "twai_periph.c") add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" "${srcs}") diff --git a/components/soc/esp32c3/twai_periph.c b/components/soc/esp32c3/twai_periph.c new file mode 100644 index 0000000000..bae7c6ec5d --- /dev/null +++ b/components/soc/esp32c3/twai_periph.c @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/twai_periph.h" +#include "soc/gpio_sig_map.h" + +const twai_controller_signal_conn_t twai_controller_periph_signals = { + .controllers = { + [0] = { + .module = PERIPH_TWAI_MODULE, + .irq_id = ETS_TWAI_INTR_SOURCE, + .tx_sig = TWAI_TX_IDX, + .rx_sig = TWAI_RX_IDX, + .bus_off_sig = TWAI_BUS_OFF_ON_IDX, + .clk_out_sig = TWAI_CLKOUT_IDX, + .stand_by_sig = -1, + }, + } +}; diff --git a/components/soc/esp32c6/CMakeLists.txt b/components/soc/esp32c6/CMakeLists.txt index 0eb7ac39a9..6d07768487 100644 --- a/components/soc/esp32c6/CMakeLists.txt +++ b/components/soc/esp32c6/CMakeLists.txt @@ -14,7 +14,8 @@ set(srcs "i2c_periph.c" "uart_periph.c" "temperature_sensor_periph.c" - "timer_periph.c") + "timer_periph.c" + "twai_periph.c") # ESP32C6-TODO list(REMOVE_ITEM srcs diff --git a/components/soc/esp32c6/twai_periph.c b/components/soc/esp32c6/twai_periph.c new file mode 100644 index 0000000000..712102bf14 --- /dev/null +++ b/components/soc/esp32c6/twai_periph.c @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/twai_periph.h" +#include "soc/gpio_sig_map.h" + +const twai_controller_signal_conn_t twai_controller_periph_signals = { + .controllers = { + [0] = { + .module = PERIPH_TWAI0_MODULE, + .irq_id = ETS_TWAI0_INTR_SOURCE, + .tx_sig = TWAI0_TX_IDX, + .rx_sig = TWAI0_RX_IDX, + .bus_off_sig = TWAI0_BUS_OFF_ON_IDX, + .clk_out_sig = TWAI0_CLKOUT_IDX, + .stand_by_sig = TWAI0_STANDBY_IDX, + }, + [1] = { + .module = PERIPH_TWAI1_MODULE, + .irq_id = ETS_TWAI1_INTR_SOURCE, + .tx_sig = TWAI1_TX_IDX, + .rx_sig = TWAI1_RX_IDX, + .bus_off_sig = TWAI1_BUS_OFF_ON_IDX, + .clk_out_sig = TWAI1_CLKOUT_IDX, + .stand_by_sig = TWAI1_STANDBY_IDX, + } + } +}; diff --git a/components/soc/esp32s2/CMakeLists.txt b/components/soc/esp32s2/CMakeLists.txt index 5f51433981..9ca95c4cfe 100644 --- a/components/soc/esp32s2/CMakeLists.txt +++ b/components/soc/esp32s2/CMakeLists.txt @@ -15,6 +15,7 @@ set(srcs "spi_periph.c" "timer_periph.c" "touch_sensor_periph.c" + "twai_periph.c" "uart_periph.c" "usb_periph.c" "temperature_sensor_periph.c" diff --git a/components/soc/esp32s2/twai_periph.c b/components/soc/esp32s2/twai_periph.c new file mode 100644 index 0000000000..bae7c6ec5d --- /dev/null +++ b/components/soc/esp32s2/twai_periph.c @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/twai_periph.h" +#include "soc/gpio_sig_map.h" + +const twai_controller_signal_conn_t twai_controller_periph_signals = { + .controllers = { + [0] = { + .module = PERIPH_TWAI_MODULE, + .irq_id = ETS_TWAI_INTR_SOURCE, + .tx_sig = TWAI_TX_IDX, + .rx_sig = TWAI_RX_IDX, + .bus_off_sig = TWAI_BUS_OFF_ON_IDX, + .clk_out_sig = TWAI_CLKOUT_IDX, + .stand_by_sig = -1, + }, + } +}; diff --git a/components/soc/esp32s3/CMakeLists.txt b/components/soc/esp32s3/CMakeLists.txt index e2d20fb00b..fcc344c301 100644 --- a/components/soc/esp32s3/CMakeLists.txt +++ b/components/soc/esp32s3/CMakeLists.txt @@ -16,9 +16,10 @@ set(srcs "sdio_slave_periph.c" "sdmmc_periph.c" "spi_periph.c" + "temperature_sensor_periph.c" "timer_periph.c" "touch_sensor_periph.c" - "temperature_sensor_periph.c" + "twai_periph.c" "uart_periph.c" "usb_periph.c" "usb_otg_periph.c") diff --git a/components/soc/esp32s3/twai_periph.c b/components/soc/esp32s3/twai_periph.c new file mode 100644 index 0000000000..bae7c6ec5d --- /dev/null +++ b/components/soc/esp32s3/twai_periph.c @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/twai_periph.h" +#include "soc/gpio_sig_map.h" + +const twai_controller_signal_conn_t twai_controller_periph_signals = { + .controllers = { + [0] = { + .module = PERIPH_TWAI_MODULE, + .irq_id = ETS_TWAI_INTR_SOURCE, + .tx_sig = TWAI_TX_IDX, + .rx_sig = TWAI_RX_IDX, + .bus_off_sig = TWAI_BUS_OFF_ON_IDX, + .clk_out_sig = TWAI_CLKOUT_IDX, + .stand_by_sig = -1, + }, + } +}; diff --git a/components/soc/include/soc/twai_periph.h b/components/soc/include/soc/twai_periph.h index 48fc1d8ea8..e97eb0e064 100644 --- a/components/soc/include/soc/twai_periph.h +++ b/components/soc/include/soc/twai_periph.h @@ -1,27 +1,32 @@ -// Copyright 2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2019-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once -#include "sdkconfig.h" +#include +#include "soc/soc_caps.h" +#include "soc/periph_defs.h" #ifdef __cplusplus extern "C" { #endif -#include "soc/soc_caps.h" -#include "soc/twai_struct.h" +typedef struct { + struct { + const periph_module_t module; // peripheral module + const int irq_id; // interrupt source ID + const int tx_sig; // TX signal ID in GPIO matrix + const int rx_sig; // RX signal ID in GPIO matrix + const int clk_out_sig; // CLK_OUT signal ID in GPIO matrix + const int bus_off_sig; // BUS_OFF status signal ID in GPIO matrix + const int stand_by_sig; // STAND_BY signal ID in GPIO matrix + } controllers[SOC_TWAI_CONTROLLER_NUM]; +} twai_controller_signal_conn_t; + +extern const twai_controller_signal_conn_t twai_controller_periph_signals; #ifdef __cplusplus } diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index c7e241016e..c6c4574d2e 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -1177,7 +1177,6 @@ components/soc/include/soc/ledc_periph.h components/soc/include/soc/sdio_slave_periph.h components/soc/include/soc/sdmmc_periph.h components/soc/include/soc/sens_periph.h -components/soc/include/soc/twai_periph.h components/soc/include/soc/uart_periph.h components/soc/include/soc/uhci_periph.h components/soc/lldesc.c From 6828c011d9fe49c652665975496ae29acf59a71c Mon Sep 17 00:00:00 2001 From: morris Date: Thu, 27 Oct 2022 18:26:02 +0800 Subject: [PATCH 2/5] twai: define clock source name twai clock source is target-specific, this commit is to define them in the soc layer --- components/soc/esp32/include/soc/clk_tree_defs.h | 15 +++++++++++++++ .../soc/esp32c3/include/soc/clk_tree_defs.h | 15 +++++++++++++++ .../soc/esp32c6/include/soc/clk_tree_defs.h | 15 +++++++++++++++ .../soc/esp32h2/include/soc/clk_tree_defs.h | 15 +++++++++++++++ .../soc/esp32s2/include/soc/clk_tree_defs.h | 15 +++++++++++++++ .../soc/esp32s3/include/soc/clk_tree_defs.h | 15 +++++++++++++++ 6 files changed, 90 insertions(+) diff --git a/components/soc/esp32/include/soc/clk_tree_defs.h b/components/soc/esp32/include/soc/clk_tree_defs.h index a634776295..2d9130464c 100644 --- a/components/soc/esp32/include/soc/clk_tree_defs.h +++ b/components/soc/esp32/include/soc/clk_tree_defs.h @@ -308,6 +308,21 @@ typedef enum { DAC_COSINE_CLK_SRC_DEFAULT = SOC_MOD_CLK_RTC_FAST, /*!< Select RTC FAST as the default source clock */ } soc_periph_dac_cosine_clk_src_t; +//////////////////////////////////////////////////TWAI///////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of TWAI + */ +#define SOC_TWAI_CLKS {SOC_MOD_CLK_APB} + +/** + * @brief TWAI clock source + */ +typedef enum { + TWAI_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + TWAI_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ +} soc_periph_twai_clk_src_t; + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32c3/include/soc/clk_tree_defs.h b/components/soc/esp32c3/include/soc/clk_tree_defs.h index c9203c587a..fbcf451396 100644 --- a/components/soc/esp32c3/include/soc/clk_tree_defs.h +++ b/components/soc/esp32c3/include/soc/clk_tree_defs.h @@ -248,6 +248,21 @@ typedef enum { SDM_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ } soc_periph_sdm_clk_src_t; +//////////////////////////////////////////////////TWAI///////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of TWAI + */ +#define SOC_TWAI_CLKS {SOC_MOD_CLK_APB} + +/** + * @brief TWAI clock source + */ +typedef enum { + TWAI_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + TWAI_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ +} soc_periph_twai_clk_src_t; + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32c6/include/soc/clk_tree_defs.h b/components/soc/esp32c6/include/soc/clk_tree_defs.h index 7a983ecf8c..8168786529 100644 --- a/components/soc/esp32c6/include/soc/clk_tree_defs.h +++ b/components/soc/esp32c6/include/soc/clk_tree_defs.h @@ -312,6 +312,21 @@ typedef enum { SDM_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ } soc_periph_sdm_clk_src_t; +//////////////////////////////////////////////////TWAI///////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of TWAI + */ +#define SOC_TWAI_CLKS {SOC_MOD_CLK_XTAL} + +/** + * @brief TWAI clock source + */ +typedef enum { + TWAI_CLK_SRC_XTAL = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the source clock */ + TWAI_CLK_SRC_DEFAULT = SOC_MOD_CLK_XTAL, /*!< Select XTAL as the default clock choice */ +} soc_periph_twai_clk_src_t; + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32h2/include/soc/clk_tree_defs.h b/components/soc/esp32h2/include/soc/clk_tree_defs.h index db53424894..bb57d01faa 100644 --- a/components/soc/esp32h2/include/soc/clk_tree_defs.h +++ b/components/soc/esp32h2/include/soc/clk_tree_defs.h @@ -253,6 +253,21 @@ typedef enum { SDM_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ } soc_periph_sdm_clk_src_t; +//////////////////////////////////////////////////TWAI///////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of TWAI + */ +#define SOC_TWAI_CLKS {SOC_MOD_CLK_APB} + +/** + * @brief TWAI clock source + */ +typedef enum { + TWAI_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + TWAI_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ +} soc_periph_twai_clk_src_t; + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32s2/include/soc/clk_tree_defs.h b/components/soc/esp32s2/include/soc/clk_tree_defs.h index 0c979c7753..edfe3f173d 100644 --- a/components/soc/esp32s2/include/soc/clk_tree_defs.h +++ b/components/soc/esp32s2/include/soc/clk_tree_defs.h @@ -296,6 +296,21 @@ typedef enum { DAC_COSINE_CLK_SRC_DEFAULT = SOC_MOD_CLK_RTC_FAST, /*!< Select RTC FAST as the default source clock */ } soc_periph_dac_cosine_clk_src_t; +//////////////////////////////////////////////////TWAI///////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of TWAI + */ +#define SOC_TWAI_CLKS {SOC_MOD_CLK_APB} + +/** + * @brief TWAI clock source + */ +typedef enum { + TWAI_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + TWAI_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ +} soc_periph_twai_clk_src_t; + #ifdef __cplusplus } #endif diff --git a/components/soc/esp32s3/include/soc/clk_tree_defs.h b/components/soc/esp32s3/include/soc/clk_tree_defs.h index ee74280f90..cc026ae661 100644 --- a/components/soc/esp32s3/include/soc/clk_tree_defs.h +++ b/components/soc/esp32s3/include/soc/clk_tree_defs.h @@ -294,6 +294,21 @@ typedef enum { SDM_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ } soc_periph_sdm_clk_src_t; +//////////////////////////////////////////////////TWAI///////////////////////////////////////////////////////////////// + +/** + * @brief Array initializer for all supported clock sources of TWAI + */ +#define SOC_TWAI_CLKS {SOC_MOD_CLK_APB} + +/** + * @brief TWAI clock source + */ +typedef enum { + TWAI_CLK_SRC_APB = SOC_MOD_CLK_APB, /*!< Select APB as the source clock */ + TWAI_CLK_SRC_DEFAULT = SOC_MOD_CLK_APB, /*!< Select APB as the default clock choice */ +} soc_periph_twai_clk_src_t; + #ifdef __cplusplus } #endif From a25123f7037b2967fe1c48382674eb39612fe894 Mon Sep 17 00:00:00 2001 From: morris Date: Wed, 19 Oct 2022 17:40:32 +0800 Subject: [PATCH 3/5] twai: bringup on esp32c6 --- components/driver/include/driver/twai.h | 19 +- components/driver/twai.c | 136 +++- components/hal/esp32/include/hal/twai_ll.h | 99 ++- components/hal/esp32c3/include/hal/twai_ll.h | 86 +- components/hal/esp32c6/include/hal/twai_ll.h | 761 ++++++++++++++++++ components/hal/esp32h2/include/hal/twai_ll.h | 85 +- components/hal/esp32s2/include/hal/twai_ll.h | 86 +- components/hal/esp32s3/include/hal/twai_ll.h | 88 +- components/hal/include/hal/twai_hal.h | 26 +- components/hal/include/hal/twai_types.h | 65 +- components/hal/twai_hal.c | 45 +- components/hal/twai_hal_iram.c | 18 +- .../soc/esp32/include/soc/Kconfig.soc_caps.in | 8 + components/soc/esp32/include/soc/soc_caps.h | 5 +- .../soc/esp32/include/soc/twai_struct.h | 18 +- .../esp32c3/include/soc/Kconfig.soc_caps.in | 8 + components/soc/esp32c3/include/soc/soc_caps.h | 2 + .../soc/esp32c3/include/soc/twai_struct.h | 18 +- .../esp32c6/include/soc/Kconfig.soc_caps.in | 14 +- components/soc/esp32c6/include/soc/soc_caps.h | 7 +- .../soc/esp32c6/include/soc/twai_struct.h | 27 +- .../esp32h2/include/soc/Kconfig.soc_caps.in | 8 + components/soc/esp32h2/include/soc/soc_caps.h | 2 + .../soc/esp32h2/include/soc/twai_struct.h | 18 +- .../esp32s2/include/soc/Kconfig.soc_caps.in | 8 + components/soc/esp32s2/include/soc/soc_caps.h | 2 + .../soc/esp32s2/include/soc/twai_struct.h | 18 +- .../esp32s3/include/soc/Kconfig.soc_caps.in | 32 +- components/soc/esp32s3/include/soc/soc_caps.h | 6 +- .../soc/esp32s3/include/soc/twai_caps.h | 28 - .../soc/esp32s3/include/soc/twai_struct.h | 18 +- .../twai/twai_alert_and_recovery/README.md | 4 +- .../peripherals/twai/twai_network/README.md | 4 +- .../peripherals/twai/twai_self_test/README.md | 4 +- tools/ci/check_copyright_ignore.txt | 9 - 35 files changed, 1521 insertions(+), 261 deletions(-) create mode 100644 components/hal/esp32c6/include/hal/twai_ll.h delete mode 100644 components/soc/esp32s3/include/soc/twai_caps.h diff --git a/components/driver/include/driver/twai.h b/components/driver/include/driver/twai.h index 70f88a4da1..ad4cc97bb5 100644 --- a/components/driver/include/driver/twai.h +++ b/components/driver/include/driver/twai.h @@ -1,25 +1,22 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - -#include "soc/soc_caps.h" -#if SOC_TWAI_SUPPORTED - #include "freertos/FreeRTOS.h" #include "esp_types.h" #include "esp_intr_alloc.h" #include "esp_err.h" -#include "gpio.h" +#include "driver/gpio.h" #include "hal/twai_types.h" +#ifdef __cplusplus +extern "C" { +#endif + /* -------------------- Default initializers and flags ---------------------- */ /** @cond */ //Doxy command to hide preprocessor definitions from docs /** @@ -135,7 +132,7 @@ typedef struct { * * @return * - ESP_OK: Successfully installed TWAI driver - * - ESP_ERR_INVALID_ARG: Arguments are invalid + * - ESP_ERR_INVALID_ARG: Arguments are invalid, e.g. invalid clock source, invalid quanta resolution * - ESP_ERR_NO_MEM: Insufficient memory * - ESP_ERR_INVALID_STATE: Driver is already installed */ @@ -341,5 +338,3 @@ esp_err_t twai_clear_receive_queue(void); #ifdef __cplusplus } #endif - -#endif //SOC_TWAI_SUPPORTED diff --git a/components/driver/twai.c b/components/driver/twai.c index 4e96929e2e..ea34737d04 100644 --- a/components/driver/twai.c +++ b/components/driver/twai.c @@ -18,6 +18,7 @@ #include "esp_heap_caps.h" #include "driver/gpio.h" #include "esp_private/periph_ctrl.h" +#include "esp_private/esp_clk.h" #include "driver/twai.h" #include "soc/soc_caps.h" #include "soc/soc.h" @@ -59,6 +60,8 @@ //Control structure for TWAI driver typedef struct { + int controller_id; + periph_module_t module; // peripheral module //Control and status members twai_state_t state; twai_mode_t mode; @@ -84,10 +87,8 @@ typedef struct { SemaphoreHandle_t alert_semphr; uint32_t alerts_enabled; uint32_t alerts_triggered; -#ifdef CONFIG_PM_ENABLE - //Power Management + //Power Management Lock esp_pm_lock_handle_t pm_lock; -#endif } twai_obj_t; static twai_obj_t *p_twai_obj = NULL; @@ -121,6 +122,7 @@ TWAI_ISR_ATTR static void twai_alert_handler(uint32_t alert_code, int *alert_req } } +TWAI_ISR_ATTR static inline void twai_handle_rx_buffer_frames(BaseType_t *task_woken, int *alert_req) { #ifdef SOC_TWAI_SUPPORTS_RX_STATUS @@ -170,6 +172,7 @@ static inline void twai_handle_rx_buffer_frames(BaseType_t *task_woken, int *ale #endif //SOC_TWAI_SUPPORTS_RX_STATUS } +TWAI_ISR_ATTR static inline void twai_handle_tx_buffer_frame(BaseType_t *task_woken, int *alert_req) { //Handle previously transmitted frame @@ -214,7 +217,7 @@ TWAI_ISR_ATTR static void twai_intr_handler_main(void *arg) #if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) if (events & TWAI_HAL_EVENT_NEED_PERIPH_RESET) { twai_hal_prepare_for_reset(&twai_context); - periph_module_reset(PERIPH_TWAI_MODULE); + periph_module_reset(p_twai_obj->module); twai_hal_recover_from_reset(&twai_context); p_twai_obj->rx_missed_count += twai_hal_get_reset_lost_rx_cnt(&twai_context); twai_alert_handler(TWAI_ALERT_PERIPH_RESET, &alert_req); @@ -281,28 +284,37 @@ TWAI_ISR_ATTR static void twai_intr_handler_main(void *arg) static void twai_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout, gpio_num_t bus_status) { - //Set TX pin - gpio_set_pull_mode(tx, GPIO_FLOATING); - esp_rom_gpio_connect_out_signal(tx, TWAI_TX_IDX, false, false); - esp_rom_gpio_pad_select_gpio(tx); - + int controller_id = p_twai_obj->controller_id; + // if TX and RX set to the same GPIO, which means we want to create a loop-back in the GPIO matrix + bool io_loop_back = (tx == rx); + gpio_config_t gpio_conf = { + .intr_type = GPIO_INTR_DISABLE, + .pull_down_en = false, + .pull_up_en = false, + }; //Set RX pin - gpio_set_pull_mode(rx, GPIO_FLOATING); - esp_rom_gpio_connect_in_signal(rx, TWAI_RX_IDX, false); - esp_rom_gpio_pad_select_gpio(rx); - gpio_set_direction(rx, GPIO_MODE_INPUT); + gpio_conf.mode = GPIO_MODE_INPUT | (io_loop_back ? GPIO_MODE_OUTPUT : 0); + gpio_conf.pin_bit_mask = 1ULL << rx; + gpio_config(&gpio_conf); + esp_rom_gpio_connect_in_signal(rx, twai_controller_periph_signals.controllers[controller_id].rx_sig, false); + + //Set TX pin + gpio_conf.mode = GPIO_MODE_OUTPUT | (io_loop_back ? GPIO_MODE_INPUT : 0); + gpio_conf.pin_bit_mask = 1ULL << tx; + gpio_config(&gpio_conf); + esp_rom_gpio_connect_out_signal(tx, twai_controller_periph_signals.controllers[controller_id].tx_sig, false, false); //Configure output clock pin (Optional) if (clkout >= 0 && clkout < GPIO_NUM_MAX) { gpio_set_pull_mode(clkout, GPIO_FLOATING); - esp_rom_gpio_connect_out_signal(clkout, TWAI_CLKOUT_IDX, false, false); + esp_rom_gpio_connect_out_signal(clkout, twai_controller_periph_signals.controllers[controller_id].clk_out_sig, false, false); esp_rom_gpio_pad_select_gpio(clkout); } //Configure bus status pin (Optional) if (bus_status >= 0 && bus_status < GPIO_NUM_MAX) { gpio_set_pull_mode(bus_status, GPIO_FLOATING); - esp_rom_gpio_connect_out_signal(bus_status, TWAI_BUS_OFF_ON_IDX, false, false); + esp_rom_gpio_connect_out_signal(bus_status, twai_controller_periph_signals.controllers[controller_id].bus_off_sig, false, false); esp_rom_gpio_pad_select_gpio(bus_status); } } @@ -310,11 +322,9 @@ static void twai_configure_gpio(gpio_num_t tx, gpio_num_t rx, gpio_num_t clkout, static void twai_free_driver_obj(twai_obj_t *p_obj) { //Free driver object and any dependent SW resources it uses (queues, semaphores etc) -#ifdef CONFIG_PM_ENABLE if (p_obj->pm_lock != NULL) { ESP_ERROR_CHECK(esp_pm_lock_delete(p_obj->pm_lock)); } -#endif //Delete queues and semaphores if (p_obj->tx_queue != NULL) { vQueueDelete(p_obj->tx_queue); @@ -382,12 +392,6 @@ static twai_obj_t *twai_alloc_driver_obj(uint32_t tx_queue_len, uint32_t rx_queu } #endif //CONFIG_TWAI_ISR_IN_IRAM -#ifdef CONFIG_PM_ENABLE - esp_err_t pm_err = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "twai", &(p_obj->pm_lock)); - if (pm_err != ESP_OK ) { - goto cleanup; - } -#endif return p_obj; cleanup: @@ -406,7 +410,6 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_ TWAI_CHECK(g_config->rx_queue_len > 0, ESP_ERR_INVALID_ARG); TWAI_CHECK(g_config->tx_io >= 0 && g_config->tx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG); TWAI_CHECK(g_config->rx_io >= 0 && g_config->rx_io < GPIO_NUM_MAX, ESP_ERR_INVALID_ARG); - TWAI_CHECK(t_config->brp >= SOC_TWAI_BRP_MIN && t_config->brp <= SOC_TWAI_BRP_MAX, ESP_ERR_INVALID_ARG); #ifndef CONFIG_TWAI_ISR_IN_IRAM TWAI_CHECK(!(g_config->intr_flags & ESP_INTR_FLAG_IRAM), ESP_ERR_INVALID_ARG); #endif @@ -414,6 +417,40 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_ TWAI_CHECK_FROM_CRIT(p_twai_obj == NULL, ESP_ERR_INVALID_STATE); TWAI_EXIT_CRITICAL(); + //Get clock source resolution + uint32_t clock_source_hz = 0; + twai_clock_source_t clk_src = t_config->clk_src; + //Fall back to default clock source + if (clk_src == 0) { + clk_src = TWAI_CLK_SRC_DEFAULT; + } + + switch (clk_src) { +#if SOC_TWAI_CLK_SUPPORT_APB + case TWAI_CLK_SRC_APB: + clock_source_hz = esp_clk_apb_freq(); + break; +#endif //SOC_TWAI_CLK_SUPPORT_APB + +#if SOC_TWAI_CLK_SUPPORT_XTAL + case TWAI_CLK_SRC_XTAL: + clock_source_hz = esp_clk_xtal_freq(); + break; +#endif //SOC_TWAI_CLK_SUPPORT_XTAL + + default: + //Invalid clock source + TWAI_CHECK(false, ESP_ERR_INVALID_ARG); + } + + //Check brp validation + uint32_t brp = t_config->brp; + if (t_config->quanta_resolution_hz) { + TWAI_CHECK(clock_source_hz % t_config->quanta_resolution_hz == 0, ESP_ERR_INVALID_ARG); + brp = clock_source_hz / t_config->quanta_resolution_hz; + } + TWAI_CHECK(twai_ll_check_brp_validation(brp), ESP_ERR_INVALID_ARG); + esp_err_t ret; twai_obj_t *p_twai_obj_dummy; @@ -421,10 +458,25 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_ p_twai_obj_dummy = twai_alloc_driver_obj(g_config->tx_queue_len, g_config->rx_queue_len); TWAI_CHECK(p_twai_obj_dummy != NULL, ESP_ERR_NO_MEM); + // TODO: Currently only controller 0 is supported by the driver. IDF-4775 + int controller_id = p_twai_obj_dummy->controller_id; + //Initialize flags and variables. All other members are already set to zero by twai_alloc_driver_obj() p_twai_obj_dummy->state = TWAI_STATE_STOPPED; p_twai_obj_dummy->mode = g_config->mode; p_twai_obj_dummy->alerts_enabled = g_config->alerts_enabled; + p_twai_obj_dummy->module = twai_controller_periph_signals.controllers[controller_id].module; + + +#ifdef CONFIG_PM_ENABLE + if (clk_src == TWAI_CLK_SRC_APB) { + // TODO: pm_lock name should also reflect the controller ID + ret = esp_pm_lock_create(ESP_PM_APB_FREQ_MAX, 0, "twai", &(p_twai_obj_dummy->pm_lock)); + if (ret != ESP_OK) { + goto err; + } + } +#endif //CONFIG_PM_ENABLE //Initialize TWAI peripheral registers, and allocate interrupt TWAI_ENTER_CRITICAL(); @@ -436,21 +488,28 @@ esp_err_t twai_driver_install(const twai_general_config_t *g_config, const twai_ ret = ESP_ERR_INVALID_STATE; goto err; } - periph_module_reset(PERIPH_TWAI_MODULE); - periph_module_enable(PERIPH_TWAI_MODULE); //Enable APB CLK to TWAI peripheral - bool init = twai_hal_init(&twai_context); - assert(init); - (void)init; + //Enable TWAI peripheral register clock + periph_module_reset(p_twai_obj_dummy->module); + periph_module_enable(p_twai_obj_dummy->module); + + //Initialize TWAI HAL layer + twai_hal_config_t hal_config = { + .clock_source_hz = clock_source_hz, + .controller_id = controller_id, + }; + bool res = twai_hal_init(&twai_context, &hal_config); + assert(res); twai_hal_configure(&twai_context, t_config, f_config, DRIVER_DEFAULT_INTERRUPTS, g_config->clkout_divider); TWAI_EXIT_CRITICAL(); //Allocate GPIO and Interrupts twai_configure_gpio(g_config->tx_io, g_config->rx_io, g_config->clkout_io, g_config->bus_off_io); - ESP_ERROR_CHECK(esp_intr_alloc(ETS_TWAI_INTR_SOURCE, g_config->intr_flags, twai_intr_handler_main, NULL, &p_twai_obj->isr_handle)); + ESP_ERROR_CHECK(esp_intr_alloc(twai_controller_periph_signals.controllers[controller_id].irq_id, g_config->intr_flags, + twai_intr_handler_main, NULL, &p_twai_obj->isr_handle)); -#ifdef CONFIG_PM_ENABLE - ESP_ERROR_CHECK(esp_pm_lock_acquire(p_twai_obj->pm_lock)); //Acquire pm_lock to keep APB clock at 80MHz -#endif + if (p_twai_obj->pm_lock) { + ESP_ERROR_CHECK(esp_pm_lock_acquire(p_twai_obj->pm_lock)); //Acquire pm_lock during the whole driver lifetime + } return ESP_OK; //TWAI module is still in reset mode, users need to call twai_start() afterwards err: @@ -468,17 +527,18 @@ esp_err_t twai_driver_uninstall(void) TWAI_CHECK_FROM_CRIT(p_twai_obj->state == TWAI_STATE_STOPPED || p_twai_obj->state == TWAI_STATE_BUS_OFF, ESP_ERR_INVALID_STATE); //Clear registers by reading twai_hal_deinit(&twai_context); - periph_module_disable(PERIPH_TWAI_MODULE); //Disable TWAI peripheral + periph_module_disable(p_twai_obj->module); //Disable TWAI peripheral p_twai_obj_dummy = p_twai_obj; //Use dummy to shorten critical section p_twai_obj = NULL; TWAI_EXIT_CRITICAL(); ESP_ERROR_CHECK(esp_intr_free(p_twai_obj_dummy->isr_handle)); //Free interrupt -#ifdef CONFIG_PM_ENABLE - //Release and delete power management lock - ESP_ERROR_CHECK(esp_pm_lock_release(p_twai_obj_dummy->pm_lock)); -#endif + if (p_twai_obj_dummy->pm_lock) { + //Release and delete power management lock + ESP_ERROR_CHECK(esp_pm_lock_release(p_twai_obj_dummy->pm_lock)); + } + //Free can driver object twai_free_driver_obj(p_twai_obj_dummy); return ESP_OK; diff --git a/components/hal/esp32/include/hal/twai_ll.h b/components/hal/esp32/include/hal/twai_ll.h index ad50f97422..d7dd448380 100644 --- a/components/hal/esp32/include/hal/twai_ll.h +++ b/components/hal/esp32/include/hal/twai_ll.h @@ -14,18 +14,24 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - #include #include +#include #include "sdkconfig.h" #include "hal/misc.h" +#include "hal/assert.h" #include "hal/twai_types.h" #include "soc/twai_periph.h" #include "soc/twai_struct.h" +#define TWAI_LL_GET_HW(controller_id) ((controller_id == 0) ? (&TWAI) : NULL) + +#define TWAI_LL_BRP_DIV_THRESH 128 + +#ifdef __cplusplus +extern "C" { +#endif + /* ------------------------- Defines and Typedefs --------------------------- */ #define TWAI_LL_STATUS_RBS (0x1 << 0) //Receive Buffer Status @@ -143,6 +149,33 @@ typedef enum { } twai_ll_err_seg_t; #endif +/* ---------------------------- Peripheral Control Register ----------------- */ + +/** + * @brief Enable TWAI module clock + * + * @param hw Start address of the TWAI registers + * @param en true to enable, false to disable + */ +__attribute__((always_inline)) +static inline void twai_ll_enable_clock(twai_dev_t *hw, bool en) +{ + (void)hw; +} + +/** + * @brief Set clock source for TWAI module + * + * @param hw Start address of the TWAI registers + * @param clk_src Clock source + */ +__attribute__((always_inline)) +static inline void twai_ll_set_clock_source(twai_dev_t *hw, twai_clock_source_t clk_src) +{ + (void)hw; + HAL_ASSERT(clk_src == TWAI_CLK_SRC_APB); +} + /* ---------------------------- Mode Register ------------------------------- */ /** @@ -156,6 +189,7 @@ typedef enum { * * @note Reset mode is automatically entered on BUS OFF condition */ +__attribute__((always_inline)) static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 1; @@ -172,6 +206,7 @@ static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) * * @note Reset mode must be exit to initiate BUS OFF recovery */ +__attribute__((always_inline)) static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 0; @@ -182,6 +217,7 @@ static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return true if in reset mode */ +__attribute__((always_inline)) static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) { return hw->mode_reg.rm; @@ -195,6 +231,7 @@ static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) { if (mode == TWAI_MODE_NORMAL) { //Normal Operating mode @@ -224,6 +261,7 @@ static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_tx(twai_dev_t *hw) { hw->command_reg.tr = 1; @@ -241,6 +279,7 @@ static inline void twai_ll_set_cmd_tx(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw) { hw->command_reg.val = 0x03; //Writing to TR and AT simultaneously @@ -260,6 +299,7 @@ static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw) { hw->command_reg.at = 1; @@ -272,6 +312,7 @@ static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw) { hw->command_reg.rrb = 1; @@ -284,6 +325,7 @@ static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw) { hw->command_reg.cdo = 1; @@ -303,6 +345,7 @@ static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw) { hw->command_reg.srr = 1; @@ -321,6 +364,7 @@ static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw) { hw->command_reg.val = 0x12; @@ -334,6 +378,7 @@ static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Status bits */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_status(twai_dev_t *hw) { return hw->status_reg.val; @@ -345,6 +390,7 @@ static inline uint32_t twai_ll_get_status(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Overrun status bit */ +__attribute__((always_inline)) static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw) { return hw->status_reg.dos; @@ -356,6 +402,7 @@ static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Whether previous TX was successful */ +__attribute__((always_inline)) static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) { return hw->status_reg.tcs; @@ -372,6 +419,7 @@ static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Bit mask of set interrupts */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw) { return hw->interrupt_reg.val; @@ -387,6 +435,7 @@ static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) { #if SOC_TWAI_BRP_DIV_SUPPORTED @@ -399,6 +448,25 @@ static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) /* ------------------------ Bus Timing Registers --------------------------- */ +/** + * @brief Check if the brp value valid + * + * @param brp Bit rate prescaler value + * @return true or False + */ +__attribute__((always_inline)) +static inline bool twai_ll_check_brp_validation(uint32_t brp) +{ + bool valid = (brp >= SOC_TWAI_BRP_MIN) && (brp <= SOC_TWAI_BRP_MAX); + // should be an even number + valid = valid && !(brp & 0x01); + if (brp > TWAI_LL_BRP_DIV_THRESH) { + // should be multiple of 4 + valid = valid && !(brp & 0x03); + } + return valid; +} + /** * @brief Set bus timing * @@ -413,10 +481,11 @@ static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) * @note ESP32 rev 2 or later can support a x2 brp by setting a brp_div bit, * allowing the brp to go from a maximum of 128 to 256. */ +__attribute__((always_inline)) static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t sjw, uint32_t tseg1, uint32_t tseg2, bool triple_sampling) { #if SOC_TWAI_BRP_DIV_SUPPORTED - if (brp > SOC_TWAI_BRP_DIV_THRESH) { + if (brp > TWAI_LL_BRP_DIV_THRESH) { //Need to set brp_div bit hw->interrupt_enable_reg.brp_div = 1; brp /= 2; @@ -440,6 +509,7 @@ static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) { (void)hw->arbitration_lost_captue_reg.val; @@ -454,6 +524,7 @@ static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) { (void)hw->error_code_capture_reg.val; @@ -482,6 +553,7 @@ static inline void twai_ll_parse_err_code_cap(twai_dev_t *hw, * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->error_warning_limit_reg, ewl, ewl); @@ -493,6 +565,7 @@ static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl) * @param hw Start address of the TWAI registers * @return Error Warning Limit */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw) { return hw->error_warning_limit_reg.val; @@ -509,6 +582,7 @@ static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw) * @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS * OFF condition automatically sets the REC to 0. */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_rec(twai_dev_t *hw) { return hw->rx_error_counter_reg.val; @@ -522,6 +596,7 @@ static inline uint32_t twai_ll_get_rec(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_error_counter_reg, rxerr, rec); @@ -537,6 +612,7 @@ static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec) * * @note A BUS OFF condition will automatically set this to 128 */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_tec(twai_dev_t *hw) { return hw->tx_error_counter_reg.val; @@ -550,6 +626,7 @@ static inline uint32_t twai_ll_get_tec(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_error_counter_reg, txerr, tec); @@ -566,7 +643,8 @@ static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec) * * @note Must be called in reset mode */ -static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_t mask, bool single_filter) +__attribute__((always_inline)) +static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_t mask, bool single_filter) { uint32_t code_swapped = HAL_SWAP32(code); uint32_t mask_swapped = HAL_SWAP32(mask); @@ -587,6 +665,7 @@ static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_ * * @note Call twai_ll_format_frame_buffer() to format a frame */ +__attribute__((always_inline)) static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *tx_frame) { //Copy formatted frame into TX buffer @@ -603,6 +682,7 @@ static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * * @note Call twai_ll_parse_frame_buffer() to parse the formatted frame */ +__attribute__((always_inline)) static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *rx_frame) { //Copy RX buffer registers into frame @@ -626,6 +706,7 @@ static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * @param[in] self_rx Frame will also be simultaneously received * @param[out] tx_frame Pointer to store formatted frame */ +__attribute__((always_inline)) static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data, uint32_t flags, twai_ll_frame_buffer_t *tx_frame) { @@ -669,6 +750,7 @@ static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const u * @param[out] data Data. Left over bytes set to 0. * @param[out] format Type of TWAI frame */ +__attribute__((always_inline)) static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, uint32_t *id, uint8_t *dlc, uint8_t *data, uint32_t *flags) { @@ -717,6 +799,7 @@ static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, * @param hw Start address of the TWAI registers * @return RX Message Counter */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) { return hw->rx_message_counter_reg.val; @@ -733,6 +816,7 @@ static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @param divider Divider for CLKOUT. Set to 0 to disable CLKOUT */ +__attribute__((always_inline)) static inline void twai_ll_set_clkout(twai_dev_t *hw, uint32_t divider) { if (divider >= 2 && divider <= 14) { @@ -759,6 +843,7 @@ static inline void twai_ll_set_clkout(twai_dev_t *hw, uint32_t divider) * @note Must be called before setting any configuration * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_enable_extended_reg_layout(twai_dev_t *hw) { hw->clock_divider_reg.cm = 1; @@ -779,6 +864,7 @@ static inline void twai_ll_enable_extended_reg_layout(twai_dev_t *hw) * @note Some registers are cleared on entering reset mode so must be saved * separate from this function. */ +__attribute__((always_inline)) static inline void twai_ll_save_reg(twai_dev_t *hw, twai_ll_reg_save_t *reg_save) { reg_save->mode_reg = (uint8_t) hw->mode_reg.val; @@ -806,6 +892,7 @@ static inline void twai_ll_save_reg(twai_dev_t *hw, twai_ll_reg_save_t *reg_save * @note Must be called in reset mode so that config registers become accessible * @note Some registers are read only thus cannot be restored */ +__attribute__((always_inline)) static inline void twai_ll_restore_reg(twai_dev_t *hw, twai_ll_reg_save_t *reg_save) { hw->mode_reg.val = reg_save->mode_reg; diff --git a/components/hal/esp32c3/include/hal/twai_ll.h b/components/hal/esp32c3/include/hal/twai_ll.h index 74010a3432..0af10a0c77 100644 --- a/components/hal/esp32c3/include/hal/twai_ll.h +++ b/components/hal/esp32c3/include/hal/twai_ll.h @@ -14,17 +14,21 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - #include #include +#include #include "hal/misc.h" +#include "hal/assert.h" #include "hal/twai_types.h" #include "soc/twai_periph.h" #include "soc/twai_struct.h" +#define TWAI_LL_GET_HW(controller_id) ((controller_id == 0) ? (&TWAI) : NULL) + +#ifdef __cplusplus +extern "C" { +#endif + /* ------------------------- Defines and Typedefs --------------------------- */ #define TWAI_LL_STATUS_RBS (0x1 << 0) //Receive Buffer Status @@ -79,6 +83,33 @@ typedef union { _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should be 13 bytes"); +/* ---------------------------- Peripheral Control Register ----------------- */ + +/** + * @brief Enable TWAI module clock + * + * @param hw Start address of the TWAI registers + * @param en true to enable, false to disable + */ +__attribute__((always_inline)) +static inline void twai_ll_enable_clock(twai_dev_t *hw, bool en) +{ + (void)hw; +} + +/** + * @brief Set clock source for TWAI module + * + * @param hw Start address of the TWAI registers + * @param clk_src Clock source + */ +__attribute__((always_inline)) +static inline void twai_ll_set_clock_source(twai_dev_t *hw, twai_clock_source_t clk_src) +{ + (void)hw; + HAL_ASSERT(clk_src == TWAI_CLK_SRC_APB); +} + /* ---------------------------- Mode Register ------------------------------- */ /** @@ -92,6 +123,7 @@ _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should b * * @note Reset mode is automatically entered on BUS OFF condition */ +__attribute__((always_inline)) static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 1; @@ -108,6 +140,7 @@ static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) * * @note Reset mode must be exit to initiate BUS OFF recovery */ +__attribute__((always_inline)) static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 0; @@ -118,6 +151,7 @@ static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return true if in reset mode */ +__attribute__((always_inline)) static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) { return hw->mode_reg.rm; @@ -131,6 +165,7 @@ static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) { if (mode == TWAI_MODE_NORMAL) { //Normal Operating mode @@ -160,6 +195,7 @@ static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_tx(twai_dev_t *hw) { hw->command_reg.tr = 1; @@ -177,6 +213,7 @@ static inline void twai_ll_set_cmd_tx(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw) { hw->command_reg.val = 0x03; //Set command_reg.tr and command_reg.at simultaneously for single shot transmittion request @@ -196,6 +233,7 @@ static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw) { hw->command_reg.at = 1; @@ -208,6 +246,7 @@ static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw) { hw->command_reg.rrb = 1; @@ -220,6 +259,7 @@ static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw) { hw->command_reg.cdo = 1; @@ -239,6 +279,7 @@ static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw) { hw->command_reg.srr = 1; @@ -257,6 +298,7 @@ static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw) { hw->command_reg.val = 0x12; //Set command_reg.srr and command_reg.at simultaneously for single shot self reception request @@ -270,6 +312,7 @@ static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Status bits */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_status(twai_dev_t *hw) { return hw->status_reg.val; @@ -281,6 +324,7 @@ static inline uint32_t twai_ll_get_status(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Overrun status bit */ +__attribute__((always_inline)) static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw) { return hw->status_reg.dos; @@ -292,6 +336,7 @@ static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Whether previous TX was successful */ +__attribute__((always_inline)) static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) { return hw->status_reg.tcs; @@ -308,6 +353,7 @@ static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Bit mask of set interrupts */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw) { return hw->interrupt_reg.val; @@ -323,6 +369,7 @@ static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) { hw->interrupt_enable_reg.val = intr_mask; @@ -330,6 +377,21 @@ static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) /* ------------------------ Bus Timing Registers --------------------------- */ +/** + * @brief Check if the brp value valid + * + * @param brp Bit rate prescaler value + * @return true or False + */ +__attribute__((always_inline)) +static inline bool twai_ll_check_brp_validation(uint32_t brp) +{ + bool valid = (brp >= SOC_TWAI_BRP_MIN) && (brp <= SOC_TWAI_BRP_MAX); + // should be an even number + valid = valid && !(brp & 0x01); + return valid; +} + /** * @brief Set bus timing * @@ -343,6 +405,7 @@ static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) * @note Must be called in reset mode * @note ESP32C3 brp can be any even number between 2 to 32768 */ +__attribute__((always_inline)) static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t sjw, uint32_t tseg1, uint32_t tseg2, bool triple_sampling) { hw->bus_timing_0_reg.brp = (brp / 2) - 1; @@ -361,6 +424,7 @@ static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) { (void)hw->arbitration_lost_captue_reg.val; @@ -375,6 +439,7 @@ static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) { (void)hw->error_code_capture_reg.val; @@ -390,6 +455,7 @@ static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->error_warning_limit_reg, ewl, ewl); @@ -401,6 +467,7 @@ static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl) * @param hw Start address of the TWAI registers * @return Error Warning Limit */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw) { return hw->error_warning_limit_reg.val; @@ -417,6 +484,7 @@ static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw) * @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS * OFF condition automatically sets the REC to 0. */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_rec(twai_dev_t *hw) { return hw->rx_error_counter_reg.val; @@ -430,6 +498,7 @@ static inline uint32_t twai_ll_get_rec(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_error_counter_reg, rxerr, rec); @@ -445,6 +514,7 @@ static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec) * * @note A BUS OFF condition will automatically set this to 128 */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_tec(twai_dev_t *hw) { return hw->tx_error_counter_reg.val; @@ -458,6 +528,7 @@ static inline uint32_t twai_ll_get_tec(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_error_counter_reg, txerr, tec); @@ -474,6 +545,7 @@ static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_t mask, bool single_filter) { uint32_t code_swapped = HAL_SWAP32(code); @@ -495,6 +567,7 @@ static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_ * * @note Call twai_ll_format_frame_buffer() to format a frame */ +__attribute__((always_inline)) static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *tx_frame) { //Copy formatted frame into TX buffer @@ -511,6 +584,7 @@ static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * * @note Call twai_ll_parse_frame_buffer() to parse the formatted frame */ +__attribute__((always_inline)) static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *rx_frame) { //Copy RX buffer registers into frame @@ -534,6 +608,7 @@ static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * @param[in] self_rx Frame will also be simultaneously received * @param[out] tx_frame Pointer to store formatted frame */ +__attribute__((always_inline)) static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data, uint32_t flags, twai_ll_frame_buffer_t *tx_frame) { @@ -577,6 +652,7 @@ static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const u * @param[out] data Data. Left over bytes set to 0. * @param[out] format Type of TWAI frame */ +__attribute__((always_inline)) static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, uint32_t *id, uint8_t *dlc, uint8_t *data, uint32_t *flags) { @@ -625,6 +701,7 @@ static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, * @param hw Start address of the TWAI registers * @return RX Message Counter */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) { return hw->rx_message_counter_reg.val; @@ -641,6 +718,7 @@ static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @param divider Divider for CLKOUT (any even number from 2 to 490). Set to 0 to disable CLKOUT */ +__attribute__((always_inline)) static inline void twai_ll_set_clkout(twai_dev_t *hw, uint32_t divider) { if (divider >= 2 && divider <= 490) { diff --git a/components/hal/esp32c6/include/hal/twai_ll.h b/components/hal/esp32c6/include/hal/twai_ll.h new file mode 100644 index 0000000000..6b3a9d8625 --- /dev/null +++ b/components/hal/esp32c6/include/hal/twai_ll.h @@ -0,0 +1,761 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/******************************************************************************* + * NOTICE + * The ll is not public api, don't use in application code. + * See readme.md in hal/include/hal/readme.md + ******************************************************************************/ + +// The Lowlevel layer for TWAI + +#pragma once + +#include +#include +#include +#include "hal/misc.h" +#include "hal/assert.h" +#include "hal/twai_types.h" +#include "soc/twai_struct.h" +#include "soc/pcr_struct.h" + +#define TWAI_LL_GET_HW(controller_id) ((controller_id == 0) ? (&TWAI0) : (&TWAI1)) + +#ifdef __cplusplus +extern "C" { +#endif + +/* ------------------------- Defines and Typedefs --------------------------- */ + +#define TWAI_LL_STATUS_RBS (0x1 << 0) //Receive Buffer Status +#define TWAI_LL_STATUS_DOS (0x1 << 1) //Data Overrun Status +#define TWAI_LL_STATUS_TBS (0x1 << 2) //Transmit Buffer Status +#define TWAI_LL_STATUS_TCS (0x1 << 3) //Transmission Complete Status +#define TWAI_LL_STATUS_RS (0x1 << 4) //Receive Status +#define TWAI_LL_STATUS_TS (0x1 << 5) //Transmit Status +#define TWAI_LL_STATUS_ES (0x1 << 6) //Error Status +#define TWAI_LL_STATUS_BS (0x1 << 7) //Bus Status +#define TWAI_LL_STATUS_MS (0x1 << 8) //Miss Status + +#define TWAI_LL_INTR_RI (0x1 << 0) //Receive Interrupt +#define TWAI_LL_INTR_TI (0x1 << 1) //Transmit Interrupt +#define TWAI_LL_INTR_EI (0x1 << 2) //Error Interrupt +//Data overrun interrupt not supported in SW due to HW peculiarities +#define TWAI_LL_INTR_EPI (0x1 << 5) //Error Passive Interrupt +#define TWAI_LL_INTR_ALI (0x1 << 6) //Arbitration Lost Interrupt +#define TWAI_LL_INTR_BEI (0x1 << 7) //Bus Error Interrupt +#define TWAI_LL_INTR_BISI (0x1 << 8) //Bus Idle Status Interrupt + +/* + * The following frame structure has an NEARLY identical bit field layout to + * each byte of the TX buffer. This allows for formatting and parsing frames to + * be done outside of time critical regions (i.e., ISRs). All the ISR needs to + * do is to copy byte by byte to/from the TX/RX buffer. The two reserved bits in + * TX buffer are used in the frame structure to store the self_reception and + * single_shot flags which in turn indicate the type of transmission to execute. + */ +typedef union { + struct { + struct { + uint8_t dlc: 4; //Data length code (0 to 8) of the frame + uint8_t self_reception: 1; //This frame should be transmitted using self reception command + uint8_t single_shot: 1; //This frame should be transmitted using single shot command + uint8_t rtr: 1; //This frame is a remote transmission request + uint8_t frame_format: 1; //Format of the frame (1 = extended, 0 = standard) + }; + union { + struct { + uint8_t id[2]; //11 bit standard frame identifier + uint8_t data[8]; //Data bytes (0 to 8) + uint8_t reserved8[2]; + } standard; + struct { + uint8_t id[4]; //29 bit extended frame identifier + uint8_t data[8]; //Data bytes (0 to 8) + } extended; + }; + }; + uint8_t bytes[13]; +} __attribute__((packed)) twai_ll_frame_buffer_t; + +_Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should be 13 bytes"); + +/* ---------------------------- Peripheral Control Register ----------------- */ + +/** + * @brief Enable TWAI module clock + * + * @param hw Start address of the TWAI registers + * @param en true to enable, false to disable + */ +__attribute__((always_inline)) +static inline void twai_ll_enable_clock(twai_dev_t *hw, bool en) +{ + if (hw == &TWAI0) { + PCR.twai0_func_clk_conf.twai0_func_clk_en = en; + } else { + PCR.twai1_func_clk_conf.twai1_func_clk_en = en; + } +} + +/** + * @brief Set clock source for TWAI module + * + * @param hw Start address of the TWAI registers + * @param clk_src Clock source + */ +__attribute__((always_inline)) +static inline void twai_ll_set_clock_source(twai_dev_t *hw, twai_clock_source_t clk_src) +{ + uint32_t clk_id = 0; + bool valid = true; + + switch (clk_src) { + case TWAI_CLK_SRC_XTAL: + clk_id = 0; + break; + default: + valid = false; + HAL_ASSERT(false); + } + + if (valid) { + if (hw == &TWAI0) { + PCR.twai0_func_clk_conf.twai0_func_clk_sel = clk_id; + } else { + PCR.twai1_func_clk_conf.twai1_func_clk_sel = clk_id; + } + } +} + +/* ---------------------------- Mode Register ------------------------------- */ + +/** + * @brief Enter reset mode + * + * When in reset mode, the TWAI controller is effectively disconnected from the + * TWAI bus and will not participate in any bus activates. Reset mode is required + * in order to write the majority of configuration registers. + * + * @param hw Start address of the TWAI registers + * + * @note Reset mode is automatically entered on BUS OFF condition + */ +__attribute__((always_inline)) +static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) +{ + hw->mode.reset_mode = 1; +} + +/** + * @brief Exit reset mode + * + * When not in reset mode, the TWAI controller will take part in bus activities + * (e.g., send/receive/acknowledge messages and error frames) depending on the + * operating mode. + * + * @param hw Start address of the TWAI registers + * + * @note Reset mode must be exit to initiate BUS OFF recovery + */ +__attribute__((always_inline)) +static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) +{ + hw->mode.reset_mode = 0; +} + +/** + * @brief Check if in reset mode + * @param hw Start address of the TWAI registers + * @return true if in reset mode + */ +__attribute__((always_inline)) +static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) +{ + return hw->mode.reset_mode; +} + +/** + * @brief Set operating mode of TWAI controller + * + * @param hw Start address of the TWAI registers + * @param mode Operating mode + * + * @note Must be called in reset mode + */ +__attribute__((always_inline)) +static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) +{ + if (mode == TWAI_MODE_NORMAL) { //Normal Operating mode + hw->mode.listen_only_mode = 0; + hw->mode.self_test_mode = 0; + } else if (mode == TWAI_MODE_NO_ACK) { //Self Test Mode (No Ack) + hw->mode.listen_only_mode = 0; + hw->mode.self_test_mode = 1; + } else if (mode == TWAI_MODE_LISTEN_ONLY) { //Listen Only Mode + hw->mode.listen_only_mode = 1; + hw->mode.self_test_mode = 0; + } +} + +/* --------------------------- Command Register ----------------------------- */ + +/** + * @brief Set TX command + * + * Setting the TX command will cause the TWAI controller to attempt to transmit + * the frame stored in the TX buffer. The TX buffer will be occupied (i.e., + * locked) until TX completes. + * + * @param hw Start address of the TWAI registers + * + * @note Transmit commands should be called last (i.e., after handling buffer + * release and clear data overrun) in order to prevent the other commands + * overwriting this latched TX bit with 0. + */ +__attribute__((always_inline)) +static inline void twai_ll_set_cmd_tx(twai_dev_t *hw) +{ + hw->cmd.tx_request = 1; +} + +/** + * @brief Set single shot TX command + * + * Similar to setting TX command, but the TWAI controller will not automatically + * retry transmission upon an error (e.g., due to an acknowledgement error). + * + * @param hw Start address of the TWAI registers + * + * @note Transmit commands should be called last (i.e., after handling buffer + * release and clear data overrun) in order to prevent the other commands + * overwriting this latched TX bit with 0. + */ +__attribute__((always_inline)) +static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw) +{ + hw->cmd.val = 0x03; //Set cmd.tx_request and cmd.abort_tx simultaneously for single shot transmitting request +} + +/** + * @brief Aborts TX + * + * Frames awaiting TX will be aborted. Frames already being TX are not aborted. + * Transmission Complete Status bit is automatically set to 1. + * Similar to setting TX command, but the TWAI controller will not automatically + * retry transmission upon an error (e.g., due to acknowledge error). + * + * @param hw Start address of the TWAI registers + * + * @note Transmit commands should be called last (i.e., after handling buffer + * release and clear data overrun) in order to prevent the other commands + * overwriting this latched TX bit with 0. + */ +__attribute__((always_inline)) +static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw) +{ + hw->cmd.abort_tx = 1; +} + +/** + * @brief Release RX buffer + * + * Rotates RX buffer to the next frame in the RX FIFO. + * + * @param hw Start address of the TWAI registers + */ +__attribute__((always_inline)) +static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw) +{ + hw->cmd.release_buffer = 1; +} + +/** + * @brief Clear data overrun + * + * Clears the data overrun status bit + * + * @param hw Start address of the TWAI registers + */ +__attribute__((always_inline)) +static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw) +{ + hw->cmd.clear_data_overrun = 1; +} + +/** + * @brief Set self reception single shot command + * + * Similar to setting TX command, but the TWAI controller also simultaneously + * receive the transmitted frame and is generally used for self testing + * purposes. The TWAI controller will not ACK the received message, so consider + * using the NO_ACK operating mode. + * + * @param hw Start address of the TWAI registers + * + * @note Transmit commands should be called last (i.e., after handling buffer + * release and clear data overrun) in order to prevent the other commands + * overwriting this latched TX bit with 0. + */ +__attribute__((always_inline)) +static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw) +{ + hw->cmd.self_rx_request = 1; +} + +/** + * @brief Set self reception request command + * + * Similar to setting the self reception request, but the TWAI controller will + * not automatically retry transmission upon an error (e.g., due to and + * acknowledgement error). + * + * @param hw Start address of the TWAI registers + * + * @note Transmit commands should be called last (i.e., after handling buffer + * release and clear data overrun) in order to prevent the other commands + * overwriting this latched TX bit with 0. + */ +__attribute__((always_inline)) +static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw) +{ + hw->cmd.val = 0x12; //Set cmd.self_rx_request and cmd.abort_tx simultaneously for single shot self reception request +} + +/* --------------------------- Status Register ------------------------------ */ + +/** + * @brief Get all status bits + * + * @param hw Start address of the TWAI registers + * @return Status bits + */ +__attribute__((always_inline)) +static inline uint32_t twai_ll_get_status(twai_dev_t *hw) +{ + return hw->status.val; +} + +/** + * @brief Check if RX FIFO overrun status bit is set + * + * @param hw Start address of the TWAI registers + * @return Overrun status bit + */ +__attribute__((always_inline)) +static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw) +{ + return hw->status.status_overrun; +} + +/** + * @brief Check if previously TX was successful + * + * @param hw Start address of the TWAI registers + * @return Whether previous TX was successful + */ +__attribute__((always_inline)) +static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) +{ + return hw->status.status_transmission_complete; +} + +/* -------------------------- Interrupt Register ---------------------------- */ + +/** + * @brief Get currently set interrupts + * + * Reading the interrupt registers will automatically clear all interrupts + * except for the Receive Interrupt. + * + * @param hw Start address of the TWAI registers + * @return Bit mask of set interrupts + */ +__attribute__((always_inline)) +static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw) +{ + return hw->interrupt.val; +} + +/* ----------------------- Interrupt Enable Register ------------------------ */ + +/** + * @brief Set which interrupts are enabled + * + * @param hw Start address of the TWAI registers + * @param Bit mask of interrupts to enable + * + * @note Must be called in reset mode + */ +__attribute__((always_inline)) +static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) +{ + hw->interrupt_enable.val = intr_mask; +} + +/* ------------------------ Bus Timing Registers --------------------------- */ + +/** + * @brief Check if the brp value valid + * + * @param brp Bit rate prescaler value + * @return true or False + */ +__attribute__((always_inline)) +static inline bool twai_ll_check_brp_validation(uint32_t brp) +{ + bool valid = (brp >= SOC_TWAI_BRP_MIN) && (brp <= SOC_TWAI_BRP_MAX); + // should be an even number + valid = valid && !(brp & 0x01); + return valid; +} + +/** + * @brief Set bus timing + * + * @param hw Start address of the TWAI registers + * @param brp Baud Rate Prescaler + * @param sjw Synchronization Jump Width + * @param tseg1 Timing Segment 1 + * @param tseg2 Timing Segment 2 + * @param triple_sampling Triple Sampling enable/disable + * + * @note Must be called in reset mode + * @note ESP32C6 brp can be any even number between 2 to 32768 + */ +__attribute__((always_inline)) +static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t sjw, uint32_t tseg1, uint32_t tseg2, bool triple_sampling) +{ + hw->bus_timing_0.baud_presc = (brp / 2) - 1; + hw->bus_timing_0.sync_jump_width = sjw - 1; + hw->bus_timing_1.time_segment1 = tseg1 - 1; + hw->bus_timing_1.time_segment2 = tseg2 - 1; + hw->bus_timing_1.time_sampling = triple_sampling; +} + +/* ----------------------------- ALC Register ------------------------------- */ + +/** + * @brief Clear Arbitration Lost Capture Register + * + * Reading the ALC register rearms the Arbitration Lost Interrupt + * + * @param hw Start address of the TWAI registers + */ +__attribute__((always_inline)) +static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) +{ + (void)hw->arb_lost_cap.val; +} + +/* ----------------------------- ECC Register ------------------------------- */ + +/** + * @brief Clear Error Code Capture register + * + * Reading the ECC register rearms the Bus Error Interrupt + * + * @param hw Start address of the TWAI registers + */ +__attribute__((always_inline)) +static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) +{ + (void)hw->err_code_cap.val; +} + +/* ----------------------------- EWL Register ------------------------------- */ + +/** + * @brief Set Error Warning Limit + * + * @param hw Start address of the TWAI registers + * @param ewl Error Warning Limit + * + * @note Must be called in reset mode + */ +__attribute__((always_inline)) +static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->err_warning_limit, err_warning_limit, ewl); +} + +/** + * @brief Get Error Warning Limit + * + * @param hw Start address of the TWAI registers + * @return Error Warning Limit + */ +__attribute__((always_inline)) +static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw) +{ + return hw->err_warning_limit.val; +} + +/* ------------------------ RX Error Count Register ------------------------- */ + +/** + * @brief Get RX Error Counter + * + * @param hw Start address of the TWAI registers + * @return REC value + * + * @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS + * OFF condition automatically sets the REC to 0. + */ +__attribute__((always_inline)) +static inline uint32_t twai_ll_get_rec(twai_dev_t *hw) +{ + return hw->rx_err_cnt.val; +} + +/** + * @brief Set RX Error Counter + * + * @param hw Start address of the TWAI registers + * @param rec REC value + * + * @note Must be called in reset mode + */ +__attribute__((always_inline)) +static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_err_cnt, rx_err_cnt, rec); +} + +/* ------------------------ TX Error Count Register ------------------------- */ + +/** + * @brief Get TX Error Counter + * + * @param hw Start address of the TWAI registers + * @return TEC value + * + * @note A BUS OFF condition will automatically set this to 128 + */ +__attribute__((always_inline)) +static inline uint32_t twai_ll_get_tec(twai_dev_t *hw) +{ + return hw->tx_err_cnt.val; +} + +/** + * @brief Set TX Error Counter + * + * @param hw Start address of the TWAI registers + * @param tec TEC value + * + * @note Must be called in reset mode + */ +__attribute__((always_inline)) +static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec) +{ + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_err_cnt, tx_err_cnt, tec); +} + +/* ---------------------- Acceptance Filter Registers ----------------------- */ + +/** + * @brief Set Acceptance Filter + * @param hw Start address of the TWAI registers + * @param code Acceptance Code + * @param mask Acceptance Mask + * @param single_filter Whether to enable single filter mode + * + * @note Must be called in reset mode + */ +__attribute__((always_inline)) +static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_t mask, bool single_filter) +{ + uint32_t code_swapped = HAL_SWAP32(code); + uint32_t mask_swapped = HAL_SWAP32(mask); + for (int i = 0; i < 4; i++) { + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->acceptance_filter.acr[i], byte, ((code_swapped >> (i * 8)) & 0xFF)); + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->acceptance_filter.amr[i], byte, ((mask_swapped >> (i * 8)) & 0xFF)); + } + hw->mode.acceptance_filter_mode = single_filter; +} + +/* ------------------------- TX/RX Buffer Registers ------------------------- */ + +/** + * @brief Copy a formatted TWAI frame into TX buffer for transmission + * + * @param hw Start address of the TWAI registers + * @param tx_frame Pointer to formatted frame + * + * @note Call twai_ll_format_frame_buffer() to format a frame + */ +__attribute__((always_inline)) +static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *tx_frame) +{ + //Copy formatted frame into TX buffer + for (int i = 0; i < 13; i++) { + hw->tx_rx_buffer[i].val = tx_frame->bytes[i]; + } +} + +/** + * @brief Copy a received frame from the RX buffer for parsing + * + * @param hw Start address of the TWAI registers + * @param rx_frame Pointer to store formatted frame + * + * @note Call twai_ll_parse_frame_buffer() to parse the formatted frame + */ +__attribute__((always_inline)) +static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *rx_frame) +{ + //Copy RX buffer registers into frame + for (int i = 0; i < 13; i++) { + rx_frame->bytes[i] = HAL_FORCE_READ_U32_REG_FIELD(hw->tx_rx_buffer[i], byte); + } +} + +/** + * @brief Format contents of a TWAI frame into layout of TX Buffer + * + * This function encodes a message into a frame structure. The frame structure + * has an identical layout to the TX buffer, allowing the frame structure to be + * directly copied into TX buffer. + * + * @param[in] 11bit or 29bit ID + * @param[in] dlc Data length code + * @param[in] data Pointer to an 8 byte array containing data. NULL if no data + * @param[in] format Type of TWAI frame + * @param[in] single_shot Frame will not be retransmitted on failure + * @param[in] self_rx Frame will also be simultaneously received + * @param[out] tx_frame Pointer to store formatted frame + */ +__attribute__((always_inline)) +static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data, + uint32_t flags, twai_ll_frame_buffer_t *tx_frame) +{ + bool is_extd = flags & TWAI_MSG_FLAG_EXTD; + bool is_rtr = flags & TWAI_MSG_FLAG_RTR; + + //Set frame information + tx_frame->dlc = dlc; + tx_frame->frame_format = is_extd; + tx_frame->rtr = is_rtr; + tx_frame->self_reception = (flags & TWAI_MSG_FLAG_SELF) ? 1 : 0; + tx_frame->single_shot = (flags & TWAI_MSG_FLAG_SS) ? 1 : 0; + + //Set ID. The ID registers are big endian and left aligned, therefore a bswap will be required + if (is_extd) { + uint32_t id_temp = HAL_SWAP32((id & TWAI_EXTD_ID_MASK) << 3); //((id << 3) >> 8*(3-i)) + for (int i = 0; i < 4; i++) { + tx_frame->extended.id[i] = (id_temp >> (8 * i)) & 0xFF; + } + } else { + uint32_t id_temp = HAL_SWAP16((id & TWAI_STD_ID_MASK) << 5); //((id << 5) >> 8*(1-i)) + for (int i = 0; i < 2; i++) { + tx_frame->standard.id[i] = (id_temp >> (8 * i)) & 0xFF; + } + } + + uint8_t *data_buffer = (is_extd) ? tx_frame->extended.data : tx_frame->standard.data; + if (!is_rtr) { //Only copy data if the frame is a data frame (i.e not a remote frame) + for (int i = 0; (i < dlc) && (i < TWAI_FRAME_MAX_DLC); i++) { + data_buffer[i] = data[i]; + } + } +} + +/** + * @brief Parse formatted TWAI frame (RX Buffer Layout) into its constituent contents + * + * @param[in] rx_frame Pointer to formatted frame + * @param[out] id 11 or 29bit ID + * @param[out] dlc Data length code + * @param[out] data Data. Left over bytes set to 0. + * @param[out] format Type of TWAI frame + */ +__attribute__((always_inline)) +static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, uint32_t *id, uint8_t *dlc, + uint8_t *data, uint32_t *flags) +{ + //Copy frame information + *dlc = rx_frame->dlc; + uint32_t flags_temp = 0; + flags_temp |= (rx_frame->frame_format) ? TWAI_MSG_FLAG_EXTD : 0; + flags_temp |= (rx_frame->rtr) ? TWAI_MSG_FLAG_RTR : 0; + flags_temp |= (rx_frame->dlc > TWAI_FRAME_MAX_DLC) ? TWAI_MSG_FLAG_DLC_NON_COMP : 0; + *flags = flags_temp; + + //Copy ID. The ID registers are big endian and left aligned, therefore a bswap will be required + if (rx_frame->frame_format) { + uint32_t id_temp = 0; + for (int i = 0; i < 4; i++) { + id_temp |= rx_frame->extended.id[i] << (8 * i); + } + id_temp = HAL_SWAP32(id_temp) >> 3; //((byte[i] << 8*(3-i)) >> 3) + *id = id_temp & TWAI_EXTD_ID_MASK; + } else { + uint32_t id_temp = 0; + for (int i = 0; i < 2; i++) { + id_temp |= rx_frame->standard.id[i] << (8 * i); + } + id_temp = HAL_SWAP16(id_temp) >> 5; //((byte[i] << 8*(1-i)) >> 5) + *id = id_temp & TWAI_STD_ID_MASK; + } + + uint8_t *data_buffer = (rx_frame->frame_format) ? rx_frame->extended.data : rx_frame->standard.data; + //Only copy data if the frame is a data frame (i.e. not a remote frame) + int data_length = (rx_frame->rtr) ? 0 : ((rx_frame->dlc > TWAI_FRAME_MAX_DLC) ? TWAI_FRAME_MAX_DLC : rx_frame->dlc); + for (int i = 0; i < data_length; i++) { + data[i] = data_buffer[i]; + } + //Set remaining bytes of data to 0 + for (int i = data_length; i < TWAI_FRAME_MAX_DLC; i++) { + data[i] = 0; + } +} + +/* ----------------------- RX Message Count Register ------------------------ */ + +/** + * @brief Get RX Message Counter + * + * @param hw Start address of the TWAI registers + * @return RX Message Counter + */ +__attribute__((always_inline)) +static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) +{ + return hw->rx_message_counter.val; +} + +/* ------------------------- Clock Divider Register ------------------------- */ + +/** + * @brief Set CLKOUT Divider and enable/disable + * + * Configure CLKOUT. CLKOUT is a pre-scaled version of peripheral source clock. Divider can be + * 1, or any even number from 2 to 490. Set the divider to 0 to disable CLKOUT. + * + * @param hw Start address of the TWAI registers + * @param divider Divider for CLKOUT (any even number from 2 to 490). Set to 0 to disable CLKOUT + */ +__attribute__((always_inline)) +static inline void twai_ll_set_clkout(twai_dev_t *hw, uint32_t divider) +{ + if (divider >= 2 && divider <= 490) { + hw->clock_divider.clock_off = 0; + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clock_divider, cd, (divider / 2) - 1); + } else if (divider == 1) { + //Setting the divider reg to max value (255) means a divider of 1 + hw->clock_divider.clock_off = 0; + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clock_divider, cd, 255); + } else { + hw->clock_divider.clock_off = 1; + HAL_FORCE_MODIFY_U32_REG_FIELD(hw->clock_divider, cd, 0); + } +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/esp32h2/include/hal/twai_ll.h b/components/hal/esp32h2/include/hal/twai_ll.h index ac8930b02d..29486f9662 100644 --- a/components/hal/esp32h2/include/hal/twai_ll.h +++ b/components/hal/esp32h2/include/hal/twai_ll.h @@ -14,17 +14,22 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif #include #include +#include #include "hal/misc.h" +#include "hal/assert.h" #include "hal/twai_types.h" #include "soc/twai_periph.h" #include "soc/twai_struct.h" +#define TWAI_LL_GET_HW(controller_id) ((controller_id == 0) ? (&TWAI) : NULL) + +#ifdef __cplusplus +extern "C" { +#endif + /* ------------------------- Defines and Typedefs --------------------------- */ #define TWAI_LL_STATUS_RBS (0x1 << 0) //Receive Buffer Status @@ -79,6 +84,33 @@ typedef union { _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should be 13 bytes"); +/* ---------------------------- Peripheral Control Register ----------------- */ + +/** + * @brief Enable TWAI module clock + * + * @param hw Start address of the TWAI registers + * @param en true to enable, false to disable + */ +__attribute__((always_inline)) +static inline void twai_ll_enable_clock(twai_dev_t *hw, bool en) +{ + (void)hw; +} + +/** + * @brief Set clock source for TWAI module + * + * @param hw Start address of the TWAI registers + * @param clk_src Clock source + */ +__attribute__((always_inline)) +static inline void twai_ll_set_clock_source(twai_dev_t *hw, twai_clock_source_t clk_src) +{ + (void)hw; + HAL_ASSERT(clk_src == TWAI_CLK_SRC_APB); +} + /* ---------------------------- Mode Register ------------------------------- */ /** @@ -92,6 +124,7 @@ _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should b * * @note Reset mode is automatically entered on BUS OFF condition */ +__attribute__((always_inline)) static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 1; @@ -108,6 +141,7 @@ static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) * * @note Reset mode must be exit to initiate BUS OFF recovery */ +__attribute__((always_inline)) static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 0; @@ -118,6 +152,7 @@ static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return true if in reset mode */ +__attribute__((always_inline)) static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) { return hw->mode_reg.rm; @@ -131,6 +166,7 @@ static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) { if (mode == TWAI_MODE_NORMAL) { //Normal Operating mode @@ -160,6 +196,7 @@ static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_tx(twai_dev_t *hw) { hw->command_reg.tr = 1; @@ -177,6 +214,7 @@ static inline void twai_ll_set_cmd_tx(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw) { hw->command_reg.val = 0x03; //Set command_reg.tr and command_reg.at simultaneously for single shot transmittion request @@ -196,6 +234,7 @@ static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw) { hw->command_reg.at = 1; @@ -208,6 +247,7 @@ static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw) { hw->command_reg.rrb = 1; @@ -220,6 +260,7 @@ static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw) { hw->command_reg.cdo = 1; @@ -239,6 +280,7 @@ static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw) { hw->command_reg.srr = 1; @@ -257,6 +299,7 @@ static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw) { hw->command_reg.val = 0x12; //Set command_reg.srr and command_reg.at simultaneously for single shot self reception request @@ -270,6 +313,7 @@ static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Status bits */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_status(twai_dev_t *hw) { return hw->status_reg.val; @@ -281,6 +325,7 @@ static inline uint32_t twai_ll_get_status(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Overrun status bit */ +__attribute__((always_inline)) static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw) { return hw->status_reg.dos; @@ -292,6 +337,7 @@ static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Whether previous TX was successful */ +__attribute__((always_inline)) static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) { return hw->status_reg.tcs; @@ -308,6 +354,7 @@ static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Bit mask of set interrupts */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw) { return hw->interrupt_reg.val; @@ -323,6 +370,7 @@ static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) { hw->interrupt_enable_reg.val = intr_mask; @@ -330,6 +378,21 @@ static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) /* ------------------------ Bus Timing Registers --------------------------- */ +/** + * @brief Check if the brp value valid + * + * @param brp Bit rate prescaler value + * @return true or False + */ +__attribute__((always_inline)) +static inline bool twai_ll_check_brp_validation(uint32_t brp) +{ + bool valid = (brp >= SOC_TWAI_BRP_MIN) && (brp <= SOC_TWAI_BRP_MAX); + // should be an even number + valid = valid && !(brp & 0x01); + return valid; +} + /** * @brief Set bus timing * @@ -343,6 +406,7 @@ static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) * @note Must be called in reset mode * @note ESP32H2 brp can be any even number between 2 to 32768 */ +__attribute__((always_inline)) static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t sjw, uint32_t tseg1, uint32_t tseg2, bool triple_sampling) { hw->bus_timing_0_reg.brp = (brp / 2) - 1; @@ -361,6 +425,7 @@ static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) { (void)hw->arbitration_lost_captue_reg.val; @@ -375,6 +440,7 @@ static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) { (void)hw->error_code_capture_reg.val; @@ -390,6 +456,7 @@ static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->error_warning_limit_reg, ewl, ewl); @@ -401,6 +468,7 @@ static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl) * @param hw Start address of the TWAI registers * @return Error Warning Limit */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw) { return hw->error_warning_limit_reg.val; @@ -417,6 +485,7 @@ static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw) * @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS * OFF condition automatically sets the REC to 0. */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_rec(twai_dev_t *hw) { return hw->rx_error_counter_reg.val; @@ -430,6 +499,7 @@ static inline uint32_t twai_ll_get_rec(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_error_counter_reg, rxerr, rec); @@ -445,6 +515,7 @@ static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec) * * @note A BUS OFF condition will automatically set this to 128 */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_tec(twai_dev_t *hw) { return hw->tx_error_counter_reg.val; @@ -458,6 +529,7 @@ static inline uint32_t twai_ll_get_tec(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_error_counter_reg, txerr, tec); @@ -474,6 +546,7 @@ static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_t mask, bool single_filter) { uint32_t code_swapped = __builtin_bswap32(code); @@ -495,6 +568,7 @@ static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_ * * @note Call twai_ll_format_frame_buffer() to format a frame */ +__attribute__((always_inline)) static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *tx_frame) { //Copy formatted frame into TX buffer @@ -511,6 +585,7 @@ static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * * @note Call twai_ll_parse_frame_buffer() to parse the formatted frame */ +__attribute__((always_inline)) static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *rx_frame) { //Copy RX buffer registers into frame @@ -534,6 +609,7 @@ static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * @param[in] self_rx Frame will also be simultaneously received * @param[out] tx_frame Pointer to store formatted frame */ +__attribute__((always_inline)) static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data, uint32_t flags, twai_ll_frame_buffer_t *tx_frame) { @@ -577,6 +653,7 @@ static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const u * @param[out] data Data. Left over bytes set to 0. * @param[out] format Type of TWAI frame */ +__attribute__((always_inline)) static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, uint32_t *id, uint8_t *dlc, uint8_t *data, uint32_t *flags) { @@ -625,6 +702,7 @@ static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, * @param hw Start address of the TWAI registers * @return RX Message Counter */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) { return hw->rx_message_counter_reg.val; @@ -641,6 +719,7 @@ static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @param divider Divider for CLKOUT (any even number from 2 to 490). Set to 0 to disable CLKOUT */ +__attribute__((always_inline)) static inline void twai_ll_set_clkout(twai_dev_t *hw, uint32_t divider) { if (divider >= 2 && divider <= 490) { diff --git a/components/hal/esp32s2/include/hal/twai_ll.h b/components/hal/esp32s2/include/hal/twai_ll.h index 960a124d93..826bbf3c5b 100644 --- a/components/hal/esp32s2/include/hal/twai_ll.h +++ b/components/hal/esp32s2/include/hal/twai_ll.h @@ -14,17 +14,21 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - #include #include +#include #include "hal/misc.h" +#include "hal/assert.h" #include "hal/twai_types.h" #include "soc/twai_periph.h" #include "soc/twai_struct.h" +#define TWAI_LL_GET_HW(controller_id) ((controller_id == 0) ? (&TWAI) : NULL) + +#ifdef __cplusplus +extern "C" { +#endif + /* ------------------------- Defines and Typedefs --------------------------- */ #define TWAI_LL_STATUS_RBS (0x1 << 0) //Receive Buffer Status @@ -79,6 +83,33 @@ typedef union { _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should be 13 bytes"); +/* ---------------------------- Peripheral Control Register ----------------- */ + +/** + * @brief Enable TWAI module clock + * + * @param hw Start address of the TWAI registers + * @param en true to enable, false to disable + */ +__attribute__((always_inline)) +static inline void twai_ll_enable_clock(twai_dev_t *hw, bool en) +{ + (void)hw; +} + +/** + * @brief Set clock source for TWAI module + * + * @param hw Start address of the TWAI registers + * @param clk_src Clock source + */ +__attribute__((always_inline)) +static inline void twai_ll_set_clock_source(twai_dev_t *hw, twai_clock_source_t clk_src) +{ + (void)hw; + HAL_ASSERT(clk_src == TWAI_CLK_SRC_APB); +} + /* ---------------------------- Mode Register ------------------------------- */ /** @@ -92,6 +123,7 @@ _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should b * * @note Reset mode is automatically entered on BUS OFF condition */ +__attribute__((always_inline)) static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 1; @@ -108,6 +140,7 @@ static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) * * @note Reset mode must be exit to initiate BUS OFF recovery */ +__attribute__((always_inline)) static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 0; @@ -118,6 +151,7 @@ static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return true if in reset mode */ +__attribute__((always_inline)) static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) { return hw->mode_reg.rm; @@ -131,6 +165,7 @@ static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) { if (mode == TWAI_MODE_NORMAL) { //Normal Operating mode @@ -160,6 +195,7 @@ static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_tx(twai_dev_t *hw) { hw->command_reg.tr = 1; @@ -177,6 +213,7 @@ static inline void twai_ll_set_cmd_tx(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw) { hw->command_reg.val = 0x03; //Set command_reg.tr and command_reg.at simultaneously for single shot transmittion request @@ -196,6 +233,7 @@ static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw) { hw->command_reg.at = 1; @@ -208,6 +246,7 @@ static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw) { hw->command_reg.rrb = 1; @@ -220,6 +259,7 @@ static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw) { hw->command_reg.cdo = 1; @@ -239,6 +279,7 @@ static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw) { hw->command_reg.srr = 1; @@ -257,6 +298,7 @@ static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw) { hw->command_reg.val = 0x12; //Set command_reg.srr and command_reg.at simultaneously for single shot self reception request @@ -270,6 +312,7 @@ static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Status bits */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_status(twai_dev_t *hw) { return hw->status_reg.val; @@ -281,6 +324,7 @@ static inline uint32_t twai_ll_get_status(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Overrun status bit */ +__attribute__((always_inline)) static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw) { return hw->status_reg.dos; @@ -292,6 +336,7 @@ static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Whether previous TX was successful */ +__attribute__((always_inline)) static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) { return hw->status_reg.tcs; @@ -308,6 +353,7 @@ static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Bit mask of set interrupts */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw) { return hw->interrupt_reg.val; @@ -323,6 +369,7 @@ static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) { hw->interrupt_enable_reg.val = intr_mask; @@ -330,6 +377,21 @@ static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) /* ------------------------ Bus Timing Registers --------------------------- */ +/** + * @brief Check if the brp value valid + * + * @param brp Bit rate prescaler value + * @return true or False + */ +__attribute__((always_inline)) +static inline bool twai_ll_check_brp_validation(uint32_t brp) +{ + bool valid = (brp >= SOC_TWAI_BRP_MIN) && (brp <= SOC_TWAI_BRP_MAX); + // should be an even number + valid = valid && !(brp & 0x01); + return valid; +} + /** * @brief Set bus timing * @@ -343,6 +405,7 @@ static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) * @note Must be called in reset mode * @note ESP32S2 brp can be any even number between 2 to 32768 */ +__attribute__((always_inline)) static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t sjw, uint32_t tseg1, uint32_t tseg2, bool triple_sampling) { hw->bus_timing_0_reg.brp = (brp / 2) - 1; @@ -361,6 +424,7 @@ static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) { (void)hw->arbitration_lost_captue_reg.val; @@ -375,6 +439,7 @@ static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) { (void)hw->error_code_capture_reg.val; @@ -390,6 +455,7 @@ static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->error_warning_limit_reg, ewl, ewl); @@ -401,6 +467,7 @@ static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl) * @param hw Start address of the TWAI registers * @return Error Warning Limit */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw) { return hw->error_warning_limit_reg.val; @@ -417,6 +484,7 @@ static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw) * @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS * OFF condition automatically sets the REC to 0. */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_rec(twai_dev_t *hw) { return hw->rx_error_counter_reg.val; @@ -430,6 +498,7 @@ static inline uint32_t twai_ll_get_rec(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_error_counter_reg, rxerr, rec); @@ -445,6 +514,7 @@ static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec) * * @note A BUS OFF condition will automatically set this to 128 */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_tec(twai_dev_t *hw) { return hw->tx_error_counter_reg.val; @@ -458,6 +528,7 @@ static inline uint32_t twai_ll_get_tec(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_error_counter_reg, txerr, tec); @@ -474,6 +545,7 @@ static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_t mask, bool single_filter) { uint32_t code_swapped = HAL_SWAP32(code); @@ -495,6 +567,7 @@ static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_ * * @note Call twai_ll_format_frame_buffer() to format a frame */ +__attribute__((always_inline)) static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *tx_frame) { //Copy formatted frame into TX buffer @@ -511,6 +584,7 @@ static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * * @note Call twai_ll_parse_frame_buffer() to parse the formatted frame */ +__attribute__((always_inline)) static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *rx_frame) { //Copy RX buffer registers into frame @@ -534,6 +608,7 @@ static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * @param[in] self_rx Frame will also be simultaneously received * @param[out] tx_frame Pointer to store formatted frame */ +__attribute__((always_inline)) static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data, uint32_t flags, twai_ll_frame_buffer_t *tx_frame) { @@ -577,6 +652,7 @@ static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const u * @param[out] data Data. Left over bytes set to 0. * @param[out] format Type of TWAI frame */ +__attribute__((always_inline)) static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, uint32_t *id, uint8_t *dlc, uint8_t *data, uint32_t *flags) { @@ -625,6 +701,7 @@ static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, * @param hw Start address of the TWAI registers * @return RX Message Counter */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) { return hw->rx_message_counter_reg.val; @@ -641,6 +718,7 @@ static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @param divider Divider for CLKOUT (any even number from 2 to 490). Set to 0 to disable CLKOUT */ +__attribute__((always_inline)) static inline void twai_ll_set_clkout(twai_dev_t *hw, uint32_t divider) { if (divider >= 2 && divider <= 490) { diff --git a/components/hal/esp32s3/include/hal/twai_ll.h b/components/hal/esp32s3/include/hal/twai_ll.h index 29e2dc4155..90939896e9 100644 --- a/components/hal/esp32s3/include/hal/twai_ll.h +++ b/components/hal/esp32s3/include/hal/twai_ll.h @@ -14,17 +14,21 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - #include #include +#include #include "hal/misc.h" +#include "hal/assert.h" #include "hal/twai_types.h" #include "soc/twai_periph.h" #include "soc/twai_struct.h" +#define TWAI_LL_GET_HW(controller_id) ((controller_id == 0) ? (&TWAI) : NULL) + +#ifdef __cplusplus +extern "C" { +#endif + /* ------------------------- Defines and Typedefs --------------------------- */ #define TWAI_LL_STATUS_RBS (0x1 << 0) //Receive Buffer Status @@ -79,6 +83,33 @@ typedef union { _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should be 13 bytes"); +/* ---------------------------- Peripheral Control Register ----------------- */ + +/** + * @brief Enable TWAI module clock + * + * @param hw Start address of the TWAI registers + * @param en true to enable, false to disable + */ +__attribute__((always_inline)) +static inline void twai_ll_enable_clock(twai_dev_t *hw, bool en) +{ + (void)hw; +} + +/** + * @brief Set clock source for TWAI module + * + * @param hw Start address of the TWAI registers + * @param clk_src Clock source + */ +__attribute__((always_inline)) +static inline void twai_ll_set_clock_source(twai_dev_t *hw, twai_clock_source_t clk_src) +{ + (void)hw; + HAL_ASSERT(clk_src == TWAI_CLK_SRC_APB); +} + /* ---------------------------- Mode Register ------------------------------- */ /** @@ -92,6 +123,7 @@ _Static_assert(sizeof(twai_ll_frame_buffer_t) == 13, "TX/RX buffer type should b * * @note Reset mode is automatically entered on BUS OFF condition */ +__attribute__((always_inline)) static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 1; @@ -108,6 +140,7 @@ static inline void twai_ll_enter_reset_mode(twai_dev_t *hw) * * @note Reset mode must be exit to initiate BUS OFF recovery */ +__attribute__((always_inline)) static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) { hw->mode_reg.rm = 0; @@ -118,6 +151,7 @@ static inline void twai_ll_exit_reset_mode(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return true if in reset mode */ +__attribute__((always_inline)) static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) { return hw->mode_reg.rm; @@ -131,6 +165,7 @@ static inline bool twai_ll_is_in_reset_mode(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) { if (mode == TWAI_MODE_NORMAL) { //Normal Operating mode @@ -160,6 +195,7 @@ static inline void twai_ll_set_mode(twai_dev_t *hw, twai_mode_t mode) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_tx(twai_dev_t *hw) { hw->command_reg.tr = 1; @@ -177,6 +213,7 @@ static inline void twai_ll_set_cmd_tx(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw) { hw->command_reg.val = 0x03; //Set command_reg.tr and command_reg.at simultaneously for single shot transmittion request @@ -196,6 +233,7 @@ static inline void twai_ll_set_cmd_tx_single_shot(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw) { hw->command_reg.at = 1; @@ -208,6 +246,7 @@ static inline void twai_ll_set_cmd_abort_tx(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw) { hw->command_reg.rrb = 1; @@ -220,6 +259,7 @@ static inline void twai_ll_set_cmd_release_rx_buffer(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw) { hw->command_reg.cdo = 1; @@ -239,6 +279,7 @@ static inline void twai_ll_set_cmd_clear_data_overrun(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw) { hw->command_reg.srr = 1; @@ -257,6 +298,7 @@ static inline void twai_ll_set_cmd_self_rx_request(twai_dev_t *hw) * release and clear data overrun) in order to prevent the other commands * overwriting this latched TX bit with 0. */ +__attribute__((always_inline)) static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw) { hw->command_reg.val = 0x12; //Set command_reg.srr and command_reg.at simultaneously for single shot self reception request @@ -270,6 +312,7 @@ static inline void twai_ll_set_cmd_self_rx_single_shot(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Status bits */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_status(twai_dev_t *hw) { return hw->status_reg.val; @@ -281,6 +324,7 @@ static inline uint32_t twai_ll_get_status(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Overrun status bit */ +__attribute__((always_inline)) static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw) { return hw->status_reg.dos; @@ -292,6 +336,7 @@ static inline bool twai_ll_is_fifo_overrun(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Whether previous TX was successful */ +__attribute__((always_inline)) static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) { return hw->status_reg.tcs; @@ -308,6 +353,7 @@ static inline bool twai_ll_is_last_tx_successful(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @return Bit mask of set interrupts */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw) { return hw->interrupt_reg.val; @@ -323,6 +369,7 @@ static inline uint32_t twai_ll_get_and_clear_intrs(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) { hw->interrupt_enable_reg.val = intr_mask; @@ -330,6 +377,21 @@ static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) /* ------------------------ Bus Timing Registers --------------------------- */ +/** + * @brief Check if the brp value valid + * + * @param brp Bit rate prescaler value + * @return true or False + */ +__attribute__((always_inline)) +static inline bool twai_ll_check_brp_validation(uint32_t brp) +{ + bool valid = (brp >= SOC_TWAI_BRP_MIN) && (brp <= SOC_TWAI_BRP_MAX); + // should be an even number + valid = valid && !(brp & 0x01); + return valid; +} + /** * @brief Set bus timing * @@ -343,6 +405,7 @@ static inline void twai_ll_set_enabled_intrs(twai_dev_t *hw, uint32_t intr_mask) * @note Must be called in reset mode * @note ESP32S3 brp can be any even number between 2 to 32768 */ +__attribute__((always_inline)) static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t sjw, uint32_t tseg1, uint32_t tseg2, bool triple_sampling) { hw->bus_timing_0_reg.brp = (brp / 2) - 1; @@ -361,6 +424,7 @@ static inline void twai_ll_set_bus_timing(twai_dev_t *hw, uint32_t brp, uint32_t * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) { (void)hw->arbitration_lost_captue_reg.val; @@ -375,6 +439,7 @@ static inline void twai_ll_clear_arb_lost_cap(twai_dev_t *hw) * * @param hw Start address of the TWAI registers */ +__attribute__((always_inline)) static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) { (void)hw->error_code_capture_reg.val; @@ -390,6 +455,7 @@ static inline void twai_ll_clear_err_code_cap(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->error_warning_limit_reg, ewl, ewl); @@ -401,6 +467,7 @@ static inline void twai_ll_set_err_warn_lim(twai_dev_t *hw, uint32_t ewl) * @param hw Start address of the TWAI registers * @return Error Warning Limit */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw) { return hw->error_warning_limit_reg.val; @@ -417,6 +484,7 @@ static inline uint32_t twai_ll_get_err_warn_lim(twai_dev_t *hw) * @note REC is not frozen in reset mode. Listen only mode will freeze it. A BUS * OFF condition automatically sets the REC to 0. */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_rec(twai_dev_t *hw) { return hw->rx_error_counter_reg.val; @@ -430,6 +498,7 @@ static inline uint32_t twai_ll_get_rec(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->rx_error_counter_reg, rxerr, rec); @@ -445,6 +514,7 @@ static inline void twai_ll_set_rec(twai_dev_t *hw, uint32_t rec) * * @note A BUS OFF condition will automatically set this to 128 */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_tec(twai_dev_t *hw) { return hw->tx_error_counter_reg.val; @@ -458,6 +528,7 @@ static inline uint32_t twai_ll_get_tec(twai_dev_t *hw) * * @note Must be called in reset mode */ +__attribute__((always_inline)) static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec) { HAL_FORCE_MODIFY_U32_REG_FIELD(hw->tx_error_counter_reg, txerr, tec); @@ -474,7 +545,8 @@ static inline void twai_ll_set_tec(twai_dev_t *hw, uint32_t tec) * * @note Must be called in reset mode */ -static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_t mask, bool single_filter) +__attribute__((always_inline)) +static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_t mask, bool single_filter) { uint32_t code_swapped = HAL_SWAP32(code); uint32_t mask_swapped = HAL_SWAP32(mask); @@ -495,6 +567,7 @@ static inline void twai_ll_set_acc_filter(twai_dev_t* hw, uint32_t code, uint32_ * * @note Call twai_ll_format_frame_buffer() to format a frame */ +__attribute__((always_inline)) static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *tx_frame) { //Copy formatted frame into TX buffer @@ -511,6 +584,7 @@ static inline void twai_ll_set_tx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * * @note Call twai_ll_parse_frame_buffer() to parse the formatted frame */ +__attribute__((always_inline)) static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t *rx_frame) { //Copy RX buffer registers into frame @@ -534,6 +608,7 @@ static inline void twai_ll_get_rx_buffer(twai_dev_t *hw, twai_ll_frame_buffer_t * @param[in] self_rx Frame will also be simultaneously received * @param[out] tx_frame Pointer to store formatted frame */ +__attribute__((always_inline)) static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const uint8_t *data, uint32_t flags, twai_ll_frame_buffer_t *tx_frame) { @@ -577,6 +652,7 @@ static inline void twai_ll_format_frame_buffer(uint32_t id, uint8_t dlc, const u * @param[out] data Data. Left over bytes set to 0. * @param[out] format Type of TWAI frame */ +__attribute__((always_inline)) static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, uint32_t *id, uint8_t *dlc, uint8_t *data, uint32_t *flags) { @@ -625,6 +701,7 @@ static inline void twai_ll_parse_frame_buffer(twai_ll_frame_buffer_t *rx_frame, * @param hw Start address of the TWAI registers * @return RX Message Counter */ +__attribute__((always_inline)) static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) { return hw->rx_message_counter_reg.val; @@ -641,6 +718,7 @@ static inline uint32_t twai_ll_get_rx_msg_count(twai_dev_t *hw) * @param hw Start address of the TWAI registers * @param divider Divider for CLKOUT (any even number from 2 to 490). Set to 0 to disable CLKOUT */ +__attribute__((always_inline)) static inline void twai_ll_set_clkout(twai_dev_t *hw, uint32_t divider) { if (divider >= 2 && divider <= 490) { diff --git a/components/hal/include/hal/twai_hal.h b/components/hal/include/hal/twai_hal.h index aeeed60231..699687c20d 100644 --- a/components/hal/include/hal/twai_hal.h +++ b/components/hal/include/hal/twai_hal.h @@ -12,16 +12,16 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - #include #include #include "sdkconfig.h" #include "hal/twai_types.h" #include "hal/twai_ll.h" +#ifdef __cplusplus +extern "C" { +#endif + /* ------------------------- Defines and Typedefs --------------------------- */ #define TWAI_HAL_SET_BITS(var, flag) ((var) |= (flag)) @@ -59,6 +59,7 @@ typedef twai_ll_frame_buffer_t twai_hal_frame_t; typedef struct { twai_dev_t *dev; uint32_t state_flags; + uint32_t clock_source_hz; #if defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT) twai_hal_frame_t tx_frame_save; twai_ll_reg_save_t reg_save; @@ -68,6 +69,11 @@ typedef struct { /* ---------------------------- Init and Config ----------------------------- */ +typedef struct { + int controller_id; + uint32_t clock_source_hz; +} twai_hal_config_t; + /** * @brief Initialize TWAI peripheral and HAL context * @@ -75,9 +81,10 @@ typedef struct { * registers with default values. * * @param hal_ctx Context of the HAL layer + * @param config HAL driver configuration * @return True if successfully initialized, false otherwise. */ -bool twai_hal_init(twai_hal_context_t *hal_ctx); +bool twai_hal_init(twai_hal_context_t *hal_ctx, const twai_hal_config_t *config); /** * @brief Deinitialize the TWAI peripheral and HAL context @@ -161,6 +168,7 @@ static inline uint32_t twai_hal_get_rec(twai_hal_context_t *hal_ctx) * @param hal_ctx Context of the HAL layer * @return RX message count */ +__attribute__((always_inline)) static inline uint32_t twai_hal_get_rx_msg_count(twai_hal_context_t *hal_ctx) { return twai_ll_get_rx_msg_count((hal_ctx)->dev); @@ -172,6 +180,7 @@ static inline uint32_t twai_hal_get_rx_msg_count(twai_hal_context_t *hal_ctx) * @param hal_ctx Context of the HAL layer * @return True if successful */ +__attribute__((always_inline)) static inline bool twai_hal_check_last_tx_successful(twai_hal_context_t *hal_ctx) { return twai_ll_is_last_tx_successful((hal_ctx)->dev); @@ -230,7 +239,7 @@ static inline void twai_hal_format_frame(const twai_message_t *message, twai_hal { //Direct call to ll function twai_ll_format_frame_buffer(message->identifier, message->data_length_code, message->data, - message->flags, frame); + message->flags, frame); } /** @@ -246,7 +255,7 @@ static inline void twai_hal_parse_frame(twai_hal_frame_t *frame, twai_message_t { //Direct call to ll function twai_ll_parse_frame_buffer(frame, &message->identifier, &message->data_length_code, - message->data, &message->flags); + message->data, &message->flags); } /** @@ -275,6 +284,7 @@ void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_f * @param rx_frame Pointer to structure to store RX frame * @return True if a valid frame was copied and released. False if overrun. */ +__attribute__((always_inline)) static inline bool twai_hal_read_rx_buffer_and_clear(twai_hal_context_t *hal_ctx, twai_hal_frame_t *rx_frame) { #ifdef SOC_TWAI_SUPPORTS_RX_STATUS @@ -304,6 +314,7 @@ static inline bool twai_hal_read_rx_buffer_and_clear(twai_hal_context_t *hal_ctx * @param hal_ctx Context of the HAL layer * @return Number of overrun messages cleared from RX FIFO */ +__attribute__((always_inline)) static inline uint32_t twai_hal_clear_rx_fifo_overrun(twai_hal_context_t *hal_ctx) { uint32_t msg_cnt = 0; @@ -359,6 +370,7 @@ void twai_hal_recover_from_reset(twai_hal_context_t *hal_ctx); * @param hal_ctx Context of the HAL layer * @return uint32_t Number of RX messages lost due to HW reset */ +__attribute__((always_inline)) static inline uint32_t twai_hal_get_reset_lost_rx_cnt(twai_hal_context_t *hal_ctx) { return hal_ctx->rx_msg_cnt_save; diff --git a/components/hal/include/hal/twai_types.h b/components/hal/include/hal/twai_types.h index f7721dd4bf..594d9e2850 100644 --- a/components/hal/include/hal/twai_types.h +++ b/components/hal/include/hal/twai_types.h @@ -6,14 +6,15 @@ #pragma once -#ifdef __cplusplus -extern "C" { -#endif - #include #include #include "sdkconfig.h" #include "soc/soc_caps.h" +#include "soc/clk_tree_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif /** * @brief TWAI Constants @@ -49,27 +50,28 @@ extern "C" { * The following initializer macros offer commonly found bit rates. These macros * place the sample point at 80% or 67% of a bit time. * - * @note These timing values are based on the assumption APB clock is at 80MHz - * @note The available bit rates are dependent on the chip target and revision. + * @note The available bit rates are dependent on the chip target and ECO version. */ -#if (SOC_TWAI_BRP_MAX > 256) -#define TWAI_TIMING_CONFIG_1KBITS() {.brp = 4000, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define TWAI_TIMING_CONFIG_5KBITS() {.brp = 800, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define TWAI_TIMING_CONFIG_10KBITS() {.brp = 400, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#endif +#if SOC_TWAI_BRP_MAX > 256 +#define TWAI_TIMING_CONFIG_1KBITS() {.quanta_resolution_hz = 20000, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_5KBITS() {.quanta_resolution_hz = 100000, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_10KBITS() {.quanta_resolution_hz = 200000, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#endif // SOC_TWAI_BRP_MAX > 256 + #if (SOC_TWAI_BRP_MAX > 128) || (CONFIG_ESP32_REV_MIN_FULL >= 200) -#define TWAI_TIMING_CONFIG_12_5KBITS() {.brp = 256, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} -#define TWAI_TIMING_CONFIG_16KBITS() {.brp = 200, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} -#define TWAI_TIMING_CONFIG_20KBITS() {.brp = 200, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#endif -#define TWAI_TIMING_CONFIG_25KBITS() {.brp = 128, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} -#define TWAI_TIMING_CONFIG_50KBITS() {.brp = 80, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define TWAI_TIMING_CONFIG_100KBITS() {.brp = 40, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define TWAI_TIMING_CONFIG_125KBITS() {.brp = 32, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define TWAI_TIMING_CONFIG_250KBITS() {.brp = 16, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define TWAI_TIMING_CONFIG_500KBITS() {.brp = 8, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} -#define TWAI_TIMING_CONFIG_800KBITS() {.brp = 4, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} -#define TWAI_TIMING_CONFIG_1MBITS() {.brp = 4, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_12_5KBITS() {.quanta_resolution_hz = 312500, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_16KBITS() {.quanta_resolution_hz = 400000, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_20KBITS() {.quanta_resolution_hz = 400000, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#endif // (SOC_TWAI_BRP_MAX > 128) || (CONFIG_ESP32_REV_MIN_FULL >= 200) + +#define TWAI_TIMING_CONFIG_25KBITS() {.quanta_resolution_hz = 625000, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_50KBITS() {.quanta_resolution_hz = 1000000, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_100KBITS() {.quanta_resolution_hz = 2000000, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_125KBITS() {.quanta_resolution_hz = 2500000, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_250KBITS() {.quanta_resolution_hz = 5000000, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_500KBITS() {.quanta_resolution_hz = 10000000, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_800KBITS() {.quanta_resolution_hz = 20000000, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .triple_sampling = false} +#define TWAI_TIMING_CONFIG_1MBITS() {.quanta_resolution_hz = 20000000, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .triple_sampling = false} /** * @brief Initializer macro for filter configuration to accept all IDs @@ -110,14 +112,27 @@ typedef struct { uint8_t data[TWAI_FRAME_MAX_DLC]; /**< Data bytes (not relevant in RTR frame) */ } twai_message_t; +/** + * @brief RMT group clock source + * @note User should select the clock source based on the power and resolution requirement + */ +#if SOC_TWAI_SUPPORTED +typedef soc_periph_twai_clk_src_t twai_clock_source_t; +#else +typedef int twai_clock_source_t; +#endif + /** * @brief Structure for bit timing configuration of the TWAI driver * * @note Macro initializers are available for this structure */ typedef struct { - uint32_t brp; /**< Baudrate prescaler (i.e., APB clock divider). Any even number from 2 to 128 for ESP32, 2 to 32768 for ESP32S2. - For ESP32 Rev 2 or later, multiples of 4 from 132 to 256 are also supported */ + twai_clock_source_t clk_src; /**< Clock source, set to 0 or TWAI_CLK_SRC_DEFAULT if you want a default clock source */ + uint32_t quanta_resolution_hz; /**< The resolution of one timing quanta, in Hz. + Note: the value of `brp` will reflected by this field if it's non-zero, otherwise, `brp` needs to be set manually */ + uint32_t brp; /**< Baudrate prescale (i.e., clock divider). Any even number from 2 to 128 for ESP32, 2 to 32768 for non-ESP32 chip. + Note: For ESP32 ECO 2 or later, multiples of 4 from 132 to 256 are also supported */ uint8_t tseg_1; /**< Timing segment 1 (Number of time quanta, between 1 to 16) */ uint8_t tseg_2; /**< Timing segment 2 (Number of time quanta, 1 to 8) */ uint8_t sjw; /**< Synchronization Jump Width (Max time quanta jump for synchronize from 1 to 4) */ diff --git a/components/hal/twai_hal.c b/components/hal/twai_hal.c index ee6163817e..e609b94d17 100644 --- a/components/hal/twai_hal.c +++ b/components/hal/twai_hal.c @@ -1,20 +1,13 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include "sdkconfig.h" #include "hal/twai_hal.h" +#include "hal/efuse_hal.h" #include "soc/soc_caps.h" //Default values written to various registers on initialization @@ -24,11 +17,14 @@ /* ---------------------------- Init and Config ----------------------------- */ -bool twai_hal_init(twai_hal_context_t *hal_ctx) +bool twai_hal_init(twai_hal_context_t *hal_ctx, const twai_hal_config_t *config) { //Initialize HAL context - hal_ctx->dev = &TWAI; + hal_ctx->dev = TWAI_LL_GET_HW(config->controller_id); hal_ctx->state_flags = 0; + hal_ctx->clock_source_hz = config->clock_source_hz; + //Enable functional clock + twai_ll_enable_clock(hal_ctx->dev, true); //Initialize TWAI controller, and set default values to registers twai_ll_enter_reset_mode(hal_ctx->dev); if (!twai_ll_is_in_reset_mode(hal_ctx->dev)) { //Must enter reset mode to write to config registers @@ -52,13 +48,30 @@ void twai_hal_deinit(twai_hal_context_t *hal_ctx) twai_ll_set_enabled_intrs(hal_ctx->dev, 0); twai_ll_clear_arb_lost_cap(hal_ctx->dev); twai_ll_clear_err_code_cap(hal_ctx->dev); + //Disable functional clock + twai_ll_enable_clock(hal_ctx->dev, false); hal_ctx->dev = NULL; } void twai_hal_configure(twai_hal_context_t *hal_ctx, const twai_timing_config_t *t_config, const twai_filter_config_t *f_config, uint32_t intr_mask, uint32_t clkout_divider) { + uint32_t brp = t_config->brp; + // both quanta_resolution_hz and brp can affect the baud rate + // but a non-zero quanta_resolution_hz takes higher priority + if (t_config->quanta_resolution_hz) { + brp = hal_ctx->clock_source_hz / t_config->quanta_resolution_hz; + } + + // set clock source + twai_clock_source_t clk_src = t_config->clk_src; + //for backward compatible, zero value means default a default clock source + if (t_config->clk_src == 0) { + clk_src = TWAI_CLK_SRC_DEFAULT; + } + twai_ll_set_clock_source(hal_ctx->dev, clk_src); + //Configure bus timing, acceptance filter, CLKOUT, and interrupts - twai_ll_set_bus_timing(hal_ctx->dev, t_config->brp, t_config->sjw, t_config->tseg_1, t_config->tseg_2, t_config->triple_sampling); + twai_ll_set_bus_timing(hal_ctx->dev, brp, t_config->sjw, t_config->tseg_1, t_config->tseg_2, t_config->triple_sampling); twai_ll_set_acc_filter(hal_ctx->dev, f_config->acceptance_code, f_config->acceptance_mask, f_config->single_filter); twai_ll_set_clkout(hal_ctx->dev, clkout_divider); twai_ll_set_enabled_intrs(hal_ctx->dev, intr_mask); diff --git a/components/hal/twai_hal_iram.c b/components/hal/twai_hal_iram.c index ee9691d07a..5fd54fe9a9 100644 --- a/components/hal/twai_hal_iram.c +++ b/components/hal/twai_hal_iram.c @@ -1,16 +1,8 @@ -// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #include #include diff --git a/components/soc/esp32/include/soc/Kconfig.soc_caps.in b/components/soc/esp32/include/soc/Kconfig.soc_caps.in index 6a9a688872..a382d80fb5 100644 --- a/components/soc/esp32/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32/include/soc/Kconfig.soc_caps.in @@ -587,10 +587,18 @@ config SOC_TOUCH_PAD_THRESHOLD_MAX bool default n +config SOC_TWAI_CONTROLLER_NUM + int + default 1 + config SOC_TWAI_BRP_MIN int default 2 +config SOC_TWAI_CLK_SUPPORT_APB + bool + default y + config SOC_TWAI_SUPPORT_MULTI_ADDRESS_LAYOUT bool default y diff --git a/components/soc/esp32/include/soc/soc_caps.h b/components/soc/esp32/include/soc/soc_caps.h index 78f85615d9..f97ece5a98 100644 --- a/components/soc/esp32/include/soc/soc_caps.h +++ b/components/soc/esp32/include/soc/soc_caps.h @@ -294,14 +294,15 @@ #define SOC_TOUCH_PAD_THRESHOLD_MAX (0) /*!= 200 # define SOC_TWAI_BRP_MAX 256 # define SOC_TWAI_BRP_DIV_SUPPORTED 1 -# define SOC_TWAI_BRP_DIV_THRESH 128 #else # define SOC_TWAI_BRP_MAX 128 #endif +#define SOC_TWAI_CLK_SUPPORT_APB 1 #define SOC_TWAI_SUPPORT_MULTI_ADDRESS_LAYOUT 1 /*-------------------------- UART CAPS ---------------------------------------*/ diff --git a/components/soc/esp32/include/soc/twai_struct.h b/components/soc/esp32/include/soc/twai_struct.h index e4349d33ed..d13f775fd1 100644 --- a/components/soc/esp32/include/soc/twai_struct.h +++ b/components/soc/esp32/include/soc/twai_struct.h @@ -1,16 +1,8 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once diff --git a/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in index 6a715917e9..ce8c68b25f 100644 --- a/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c3/include/soc/Kconfig.soc_caps.in @@ -679,6 +679,14 @@ config SOC_TIMER_GROUP_TOTAL_TIMERS int default 2 +config SOC_TWAI_CONTROLLER_NUM + int + default 1 + +config SOC_TWAI_CLK_SUPPORT_APB + bool + default y + config SOC_TWAI_BRP_MIN int default 2 diff --git a/components/soc/esp32c3/include/soc/soc_caps.h b/components/soc/esp32c3/include/soc/soc_caps.h index ba86f24ded..f8b52f6aad 100644 --- a/components/soc/esp32c3/include/soc/soc_caps.h +++ b/components/soc/esp32c3/include/soc/soc_caps.h @@ -314,6 +314,8 @@ #define SOC_TIMER_GROUP_TOTAL_TIMERS (2) /*-------------------------- TWAI CAPS ---------------------------------------*/ +#define SOC_TWAI_CONTROLLER_NUM 1UL +#define SOC_TWAI_CLK_SUPPORT_APB 1 #define SOC_TWAI_BRP_MIN 2 #define SOC_TWAI_BRP_MAX 16384 #define SOC_TWAI_SUPPORTS_RX_STATUS 1 diff --git a/components/soc/esp32c3/include/soc/twai_struct.h b/components/soc/esp32c3/include/soc/twai_struct.h index fafc36393a..fe05b90a99 100644 --- a/components/soc/esp32c3/include/soc/twai_struct.h +++ b/components/soc/esp32c3/include/soc/twai_struct.h @@ -1,16 +1,8 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once diff --git a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in index 1493ebdc05..ecd3b32a24 100644 --- a/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32c6/include/soc/Kconfig.soc_caps.in @@ -19,6 +19,10 @@ config SOC_MCPWM_SUPPORTED bool default y +config SOC_TWAI_SUPPORTED + bool + default y + config SOC_BT_SUPPORTED bool default y @@ -687,13 +691,21 @@ config SOC_TIMER_SUPPORT_ETM bool default y +config SOC_TWAI_CONTROLLER_NUM + int + default 2 + +config SOC_TWAI_CLK_SUPPORT_XTAL + bool + default y + config SOC_TWAI_BRP_MIN int default 2 config SOC_TWAI_BRP_MAX int - default 16384 + default 32768 config SOC_TWAI_SUPPORTS_RX_STATUS bool diff --git a/components/soc/esp32c6/include/soc/soc_caps.h b/components/soc/esp32c6/include/soc/soc_caps.h index d87ad897c0..7ceb51463b 100644 --- a/components/soc/esp32c6/include/soc/soc_caps.h +++ b/components/soc/esp32c6/include/soc/soc_caps.h @@ -30,7 +30,7 @@ #define SOC_GDMA_SUPPORTED 1 #define SOC_PCNT_SUPPORTED 1 #define SOC_MCPWM_SUPPORTED 1 -// #define SOC_TWAI_SUPPORTED 1 // TODO: IDF-5313 +#define SOC_TWAI_SUPPORTED 1 #define SOC_BT_SUPPORTED 1 #define SOC_ASYNC_MEMCPY_SUPPORTED 1 #define SOC_USB_SERIAL_JTAG_SUPPORTED 1 @@ -350,10 +350,11 @@ #define SOC_TIMER_GROUP_TOTAL_TIMERS (2) #define SOC_TIMER_SUPPORT_ETM (1) -// TODO: IDF-5313 (Copy from esp32c3, need check) /*-------------------------- TWAI CAPS ---------------------------------------*/ +#define SOC_TWAI_CONTROLLER_NUM 2 +#define SOC_TWAI_CLK_SUPPORT_XTAL 1 #define SOC_TWAI_BRP_MIN 2 -#define SOC_TWAI_BRP_MAX 16384 +#define SOC_TWAI_BRP_MAX 32768 #define SOC_TWAI_SUPPORTS_RX_STATUS 1 // TODO: IDF-5357 (Copy from esp32c3, need check) diff --git a/components/soc/esp32c6/include/soc/twai_struct.h b/components/soc/esp32c6/include/soc/twai_struct.h index 11220fc1f2..1f67feaa99 100644 --- a/components/soc/esp32c6/include/soc/twai_struct.h +++ b/components/soc/esp32c6/include/soc/twai_struct.h @@ -502,6 +502,28 @@ typedef union { uint32_t val; } twai_tx_rx_buffer_reg_t; +typedef struct { + union { + struct { + uint32_t byte: 8; /* ACRx[7:0] Acceptance Code */ + uint32_t reserved8: 24; /* Internal Reserved */ + }; + uint32_t val; + } acr[4]; + union { + struct { + uint32_t byte: 8; /* AMRx[7:0] Acceptance Mask */ + uint32_t reserved8: 24; /* Internal Reserved */ + }; + uint32_t val; + } amr[4]; + uint32_t reserved_60; + uint32_t reserved_64; + uint32_t reserved_68; + uint32_t reserved_6c; + uint32_t reserved_70; +} acceptance_filter_reg_t; + typedef struct twai_dev_s { volatile twai_mode_reg_t mode; @@ -518,7 +540,10 @@ typedef struct twai_dev_s { volatile twai_err_warning_limit_reg_t err_warning_limit; volatile twai_rx_err_cnt_reg_t rx_err_cnt; volatile twai_tx_err_cnt_reg_t tx_err_cnt; - volatile twai_tx_rx_buffer_reg_t tx_rx_buffer[13]; + volatile union { + acceptance_filter_reg_t acceptance_filter; + twai_tx_rx_buffer_reg_t tx_rx_buffer[13]; + }; volatile twai_rx_message_counter_reg_t rx_message_counter; uint32_t reserved_078; volatile twai_clock_divider_reg_t clock_divider; diff --git a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in index 5fc424d9f5..d67a4f77de 100644 --- a/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32h2/include/soc/Kconfig.soc_caps.in @@ -651,6 +651,14 @@ config SOC_TIMER_GROUP_TOTAL_TIMERS int default 2 +config SOC_TWAI_CONTROLLER_NUM + int + default 1 + +config SOC_TWAI_CLK_SUPPORT_APB + bool + default y + config SOC_TWAI_BRP_MIN int default 2 diff --git a/components/soc/esp32h2/include/soc/soc_caps.h b/components/soc/esp32h2/include/soc/soc_caps.h index 1c7778c6f7..80d6e58458 100644 --- a/components/soc/esp32h2/include/soc/soc_caps.h +++ b/components/soc/esp32h2/include/soc/soc_caps.h @@ -319,6 +319,8 @@ #define SOC_TIMER_GROUP_TOTAL_TIMERS (2) /*-------------------------- TWAI CAPS ---------------------------------------*/ +#define SOC_TWAI_CONTROLLER_NUM 1UL +#define SOC_TWAI_CLK_SUPPORT_APB 1 #define SOC_TWAI_BRP_MIN 2 #define SOC_TWAI_BRP_MAX 16384 #define SOC_TWAI_SUPPORTS_RX_STATUS 1 diff --git a/components/soc/esp32h2/include/soc/twai_struct.h b/components/soc/esp32h2/include/soc/twai_struct.h index 8c9b60059c..8068055efc 100644 --- a/components/soc/esp32h2/include/soc/twai_struct.h +++ b/components/soc/esp32h2/include/soc/twai_struct.h @@ -1,16 +1,8 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once diff --git a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in index ad3cb1c70b..0b22d942c4 100644 --- a/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in +++ b/components/soc/esp32s2/include/soc/Kconfig.soc_caps.in @@ -667,6 +667,14 @@ config SOC_TOUCH_PAD_MEASURE_WAIT_MAX hex default 0xFF +config SOC_TWAI_CONTROLLER_NUM + int + default 1 + +config SOC_TWAI_CLK_SUPPORT_APB + bool + default y + config SOC_TWAI_BRP_MIN int default 2 diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index b90f75b72f..169dfa71d3 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -297,6 +297,8 @@ #define SOC_TOUCH_PAD_MEASURE_WAIT_MAX (0xFF) /*! Date: Thu, 27 Oct 2022 17:39:13 +0800 Subject: [PATCH 4/5] twai: add initial version of driver component test --- components/driver/.build-test-rules.yml | 4 + .../driver/test_apps/twai/CMakeLists.txt | 18 +++ components/driver/test_apps/twai/README.md | 8 ++ .../driver/test_apps/twai/main/CMakeLists.txt | 8 ++ .../test_apps/twai/main/test_app_main.c | 51 ++++++++ .../twai/main/test_twai_interactive.c | 85 +++++++++++++ .../test_apps/twai/main/test_twai_loop_back.c | 117 ++++++++++++++++++ .../driver/test_apps/twai/pytest_twai.py | 104 ++++++++++++++++ .../test_apps/twai/sdkconfig.ci.iram_safe | 12 ++ .../test_apps/twai/sdkconfig.ci.release | 5 + .../driver/test_apps/twai/sdkconfig.defaults | 2 + tools/requirements/requirements.pytest.txt | 3 + 12 files changed, 417 insertions(+) create mode 100644 components/driver/test_apps/twai/CMakeLists.txt create mode 100644 components/driver/test_apps/twai/README.md create mode 100644 components/driver/test_apps/twai/main/CMakeLists.txt create mode 100644 components/driver/test_apps/twai/main/test_app_main.c create mode 100644 components/driver/test_apps/twai/main/test_twai_interactive.c create mode 100644 components/driver/test_apps/twai/main/test_twai_loop_back.c create mode 100644 components/driver/test_apps/twai/pytest_twai.py create mode 100644 components/driver/test_apps/twai/sdkconfig.ci.iram_safe create mode 100644 components/driver/test_apps/twai/sdkconfig.ci.release create mode 100644 components/driver/test_apps/twai/sdkconfig.defaults diff --git a/components/driver/.build-test-rules.yml b/components/driver/.build-test-rules.yml index f5ec2a0dfd..3cbdbaecc6 100644 --- a/components/driver/.build-test-rules.yml +++ b/components/driver/.build-test-rules.yml @@ -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 diff --git a/components/driver/test_apps/twai/CMakeLists.txt b/components/driver/test_apps/twai/CMakeLists.txt new file mode 100644 index 0000000000..d1b8b52293 --- /dev/null +++ b/components/driver/test_apps/twai/CMakeLists.txt @@ -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() diff --git a/components/driver/test_apps/twai/README.md b/components/driver/test_apps/twai/README.md new file mode 100644 index 0000000000..4d24f6538c --- /dev/null +++ b/components/driver/test_apps/twai/README.md @@ -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 +``` diff --git a/components/driver/test_apps/twai/main/CMakeLists.txt b/components/driver/test_apps/twai/main/CMakeLists.txt new file mode 100644 index 0000000000..24a3bff0da --- /dev/null +++ b/components/driver/test_apps/twai/main/CMakeLists.txt @@ -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) diff --git a/components/driver/test_apps/twai/main/test_app_main.c b/components/driver/test_apps/twai/main/test_app_main.c new file mode 100644 index 0000000000..8d0972047f --- /dev/null +++ b/components/driver/test_apps/twai/main/test_app_main.c @@ -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(); +} diff --git a/components/driver/test_apps/twai/main/test_twai_interactive.c b/components/driver/test_apps/twai/main/test_twai_interactive.c new file mode 100644 index 0000000000..d72a0e0847 --- /dev/null +++ b/components/driver/test_apps/twai/main/test_twai_interactive.c @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#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()); +} diff --git a/components/driver/test_apps/twai/main/test_twai_loop_back.c b/components/driver/test_apps/twai/main/test_twai_loop_back.c new file mode 100644 index 0000000000..e5612f018f --- /dev/null +++ b/components/driver/test_apps/twai/main/test_twai_loop_back.c @@ -0,0 +1,117 @@ +/* + * SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#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()); +} diff --git a/components/driver/test_apps/twai/pytest_twai.py b/components/driver/test_apps/twai/pytest_twai.py new file mode 100644 index 0000000000..870c2727ce --- /dev/null +++ b/components/driver/test_apps/twai/pytest_twai.py @@ -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() diff --git a/components/driver/test_apps/twai/sdkconfig.ci.iram_safe b/components/driver/test_apps/twai/sdkconfig.ci.iram_safe new file mode 100644 index 0000000000..c0060c0887 --- /dev/null +++ b/components/driver/test_apps/twai/sdkconfig.ci.iram_safe @@ -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 diff --git a/components/driver/test_apps/twai/sdkconfig.ci.release b/components/driver/test_apps/twai/sdkconfig.ci.release new file mode 100644 index 0000000000..91d93f163e --- /dev/null +++ b/components/driver/test_apps/twai/sdkconfig.ci.release @@ -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 diff --git a/components/driver/test_apps/twai/sdkconfig.defaults b/components/driver/test_apps/twai/sdkconfig.defaults new file mode 100644 index 0000000000..b308cb2ddd --- /dev/null +++ b/components/driver/test_apps/twai/sdkconfig.defaults @@ -0,0 +1,2 @@ +CONFIG_FREERTOS_HZ=1000 +CONFIG_ESP_TASK_WDT=n diff --git a/tools/requirements/requirements.pytest.txt b/tools/requirements/requirements.pytest.txt index e576fb7f1a..a13ce3edfe 100644 --- a/tools/requirements/requirements.pytest.txt +++ b/tools/requirements/requirements.pytest.txt @@ -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 From aeaef08f0ea5cf9e6f0c762d084cd9c9b294767c Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 31 Oct 2022 11:24:59 +0800 Subject: [PATCH 5/5] doc: update twai api reference for esp32c6 --- docs/docs_not_updated/esp32c6.txt | 1 - docs/en/api-reference/peripherals/twai.rst | 69 +++++++++---------- .../api-reference/system/power_management.rst | 2 +- .../api-reference/system/power_management.rst | 2 +- 4 files changed, 35 insertions(+), 39 deletions(-) diff --git a/docs/docs_not_updated/esp32c6.txt b/docs/docs_not_updated/esp32c6.txt index d518487277..e2c8d8e48b 100644 --- a/docs/docs_not_updated/esp32c6.txt +++ b/docs/docs_not_updated/esp32c6.txt @@ -115,7 +115,6 @@ api-reference/peripherals/sdspi_share api-reference/peripherals/adc_continuous api-reference/peripherals/adc_oneshot api-reference/peripherals/usb_host -api-reference/peripherals/twai api-reference/peripherals/hmac api-reference/peripherals/usb_device api-reference/peripherals/sdspi_host diff --git a/docs/en/api-reference/peripherals/twai.rst b/docs/en/api-reference/peripherals/twai.rst index 3988210c06..2457acc186 100644 --- a/docs/en/api-reference/peripherals/twai.rst +++ b/docs/en/api-reference/peripherals/twai.rst @@ -1,12 +1,19 @@ Two-Wire Automotive Interface (TWAI) ==================================== +{IDF_TARGET_TWAI_NUM:default="1", esp32c6="2"} + .. -------------------------------- Overview ----------------------------------- +.. only:: esp32c6 + + .. warning:: + {IDF_TARGET_NAME} has {IDF_TARGET_TWAI_NUM} TWAI controllers, but at the moment, the driver can only support ``TWAI0`` due to the limitation of the driver structure. + Overview -------- -The Two-Wire Automotive Interface (TWAI) is a real-time serial communication protocol suited for automotive and industrial applications. It is compatible with ISO11898-1 Classical frames, thus can support Standard Frame Format (11-bit ID) and Extended Frame Format (29-bit ID). The {IDF_TARGET_NAME}'s peripherals contains a TWAI controller that can be configured to communicate on a TWAI bus via an external transceiver. +The Two-Wire Automotive Interface (TWAI) is a real-time serial communication protocol suited for automotive and industrial applications. It is compatible with ISO11898-1 Classical frames, thus can support Standard Frame Format (11-bit ID) and Extended Frame Format (29-bit ID). The {IDF_TARGET_NAME} contains {IDF_TARGET_TWAI_NUM} TWAI controller(s) that can be configured to communicate on a TWAI bus via an external transceiver. .. warning:: The TWAI controller is not compatible with ISO11898-1 FD Format frames, and will interpret such frames as errors. @@ -39,7 +46,7 @@ TWAI Messages TWAI Messages are split into Data Frames and Remote Frames. Data Frames are used to deliver a data payload to other nodes, whereas a Remote Frame is used to request a Data Frame from other nodes (other nodes can optionally respond with a Data Frame). Data and Remote Frames have two frame formats known as **Extended Frame** and **Standard Frame** which contain a 29-bit ID and an 11-bit ID respectively. A TWAI message consists of the following fields: - 29-bit or 11-bit ID: Determines the priority of the message (lower value has higher priority). - - Data Length Code (DLC) between 0 to 8: Indicates the size (in bytes) of the data payload for a Data Frame, or the amount of data to request for a Remote Frame. + - Data Length Code (DLC) between 0 to 8: Indicates the size (in bytes) of the data payload for a Data Frame, or the amount of data to request for a Remote Frame. - Up to 8 bytes of data for a Data Frame (should match DLC). Error States and Counters @@ -70,7 +77,7 @@ The TWAI controller's interface consists of 4 signal lines known as **TX, RX, BU **BUS-OFF:** The BUS-OFF signal line is **optional** and is set to a low logic level (0V) whenever the TWAI controller reaches a bus-off state. The BUS-OFF signal line is set to a high logic level (3.3V) otherwise. -**CLKOUT:** The CLKOUT signal line is **optional** and outputs a prescaled version of the controller's source clock (APB Clock). +**CLKOUT:** The CLKOUT signal line is **optional** and outputs a prescaled version of the controller's source clock. .. note:: An external transceiver **must internally loopback the TX to RX** such that a change in logic level to the TX signal line can be observed on the RX line. Failing to do so will cause the TWAI controller to interpret differences in logic levels between the two signal lines as a loss in arbitration or a bit error. @@ -139,7 +146,7 @@ The TWAI driver contains an alert feature that is used to notify the application .. note:: When enabling alerts, the ``TWAI_ALERT_AND_LOG`` flag can be used to cause the TWAI driver to log any raised alerts to UART. However, alert logging is disabled and ``TWAI_ALERT_AND_LOG`` if the :ref:`CONFIG_TWAI_ISR_IN_IRAM` option is enabled (see :ref:`placing-isr-into-iram`). - + .. note:: The ``TWAI_ALERT_ALL`` and ``TWAI_ALERT_NONE`` macros can also be used to enable/disable all alerts during configuration/reconfiguration. @@ -152,16 +159,18 @@ The operating bit rate of the TWAI driver is configured using the :cpp:type:`twa 2. **Timing Segment 1** consists of 1 to 16 time quanta before sample point 3. **Timing Segment 2** consists of 1 to 8 time quanta after sample point -{IDF_TARGET_MAX_BRP:default="128", esp32="128", esp32s2="32768", esp32c3="16384"} +{IDF_TARGET_MAX_BRP:default="128", esp32="128", esp32s2="32768", esp32c3="16384", esp32c6="16384"} -The **Baudrate Prescaler** is used to determine the period of each time quantum by dividing the TWAI controller's source clock (80 MHz APB clock). On the {IDF_TARGET_NAME}, the ``brp`` can be **any even number from 2 to {IDF_TARGET_MAX_BRP}**. +The **Baudrate Prescaler** is used to determine the period of each time quantum by dividing the TWAI controller's source clock. On the {IDF_TARGET_NAME}, the ``brp`` can be **any even number from 2 to {IDF_TARGET_MAX_BRP}**. Alternatively, you can decide the resolution of each quantum, by setting :cpp:member:`twai_timing_config_t::quanta_resolution_hz` to a non-zero value. In this way, the driver can calculate the underlying ``brp`` value for you. It's useful when you set different clock sources but want the bitrate to keep the same. + +Supported clock source for a TWAI controller is listed in the :cpp:type:`twai_clock_source_t` and can be specified in :cpp:member:`twai_timing_config_t::clk_src`. .. only:: esp32 If the ESP32 is a revision 2 or later chip, the ``brp`` will **also support any multiple of 4 from 132 to 256**, and can be enabled by setting the :ref:`CONFIG_ESP32_REV_MIN` to revision 2 or higher. .. packetdiag:: ../../../_static/diagrams/twai/bit_timing.diag - :caption: Bit timing configuration for 500kbit/s given BRP = 8 + :caption: Bit timing configuration for 500kbit/s given BRP = 8, clock source frequency is 80MHz :align: center The sample point of a bit is located on the intersection of Timing Segment 1 and 2. Enabling **Triple Sampling** will cause 3 time quanta to be sampled per bit instead of 1 (extra samples are located at the tail end of Timing Segment 1). @@ -175,40 +184,28 @@ Bit timing **macro initializers** are also available for commonly used bit rates .. list:: - - ``TWAI_TIMING_CONFIG_1MBITS()`` - - ``TWAI_TIMING_CONFIG_800KBITS()`` - - ``TWAI_TIMING_CONFIG_500KBITS()`` - - ``TWAI_TIMING_CONFIG_250KBITS()`` - - ``TWAI_TIMING_CONFIG_125KBITS()`` - - ``TWAI_TIMING_CONFIG_100KBITS()`` - - ``TWAI_TIMING_CONFIG_50KBITS()`` - - ``TWAI_TIMING_CONFIG_25KBITS()`` - :esp32s2: - ``TWAI_TIMING_CONFIG_20KBITS()`` - :esp32s2: - ``TWAI_TIMING_CONFIG_16KBITS()`` - :esp32s2: - ``TWAI_TIMING_CONFIG_12_5KBITS()`` - :esp32s2: - ``TWAI_TIMING_CONFIG_10KBITS()`` - :esp32s2: - ``TWAI_TIMING_CONFIG_5KBITS()`` - :esp32s2: - ``TWAI_TIMING_CONFIG_1KBITS()`` - :esp32s3: - ``TWAI_TIMING_CONFIG_20KBITS()`` - :esp32s3: - ``TWAI_TIMING_CONFIG_16KBITS()`` - :esp32s3: - ``TWAI_TIMING_CONFIG_12_5KBITS()`` - :esp32s3: - ``TWAI_TIMING_CONFIG_10KBITS()`` - :esp32s3: - ``TWAI_TIMING_CONFIG_5KBITS()`` - :esp32s3: - ``TWAI_TIMING_CONFIG_1KBITS()`` - :esp32c3: - ``TWAI_TIMING_CONFIG_20KBITS()`` - :esp32c3: - ``TWAI_TIMING_CONFIG_16KBITS()`` - :esp32c3: - ``TWAI_TIMING_CONFIG_12_5KBITS()`` - :esp32c3: - ``TWAI_TIMING_CONFIG_10KBITS()`` - :esp32c3: - ``TWAI_TIMING_CONFIG_5KBITS()`` - :esp32c3: - ``TWAI_TIMING_CONFIG_1KBITS()`` + - :c:macro:`TWAI_TIMING_CONFIG_1MBITS` + - :c:macro:`TWAI_TIMING_CONFIG_800KBITS` + - :c:macro:`TWAI_TIMING_CONFIG_500KBITS` + - :c:macro:`TWAI_TIMING_CONFIG_250KBITS` + - :c:macro:`TWAI_TIMING_CONFIG_125KBITS` + - :c:macro:`TWAI_TIMING_CONFIG_100KBITS` + - :c:macro:`TWAI_TIMING_CONFIG_50KBITS` + - :c:macro:`TWAI_TIMING_CONFIG_25KBITS` + :not esp32: - :c:macro:`TWAI_TIMING_CONFIG_20KBITS` + :not esp32: - :c:macro:`TWAI_TIMING_CONFIG_16KBITS` + :not esp32: - :c:macro:`TWAI_TIMING_CONFIG_12_5KBITS` + :not esp32: - :c:macro:`TWAI_TIMING_CONFIG_10KBITS` + :not esp32: - :c:macro:`TWAI_TIMING_CONFIG_5KBITS` + :not esp32: - :c:macro:`TWAI_TIMING_CONFIG_1KBITS` .. only:: esp32 Revision 2 or later of the ESP32 also supports the following bit rates: - - ``TWAI_TIMING_CONFIG_20KBITS()`` - - ``TWAI_TIMING_CONFIG_16KBITS()`` - - ``TWAI_TIMING_CONFIG_12_5KBITS()`` + - :c:macro:`TWAI_TIMING_CONFIG_20KBITS` + - :c:macro:`TWAI_TIMING_CONFIG_16KBITS` + - :c:macro:`TWAI_TIMING_CONFIG_12_5KBITS` Acceptance Filter ^^^^^^^^^^^^^^^^^ diff --git a/docs/en/api-reference/system/power_management.rst b/docs/en/api-reference/system/power_management.rst index 1f2f704764..0c830d50f0 100644 --- a/docs/en/api-reference/system/power_management.rst +++ b/docs/en/api-reference/system/power_management.rst @@ -120,7 +120,7 @@ The following drivers will hold the ``ESP_PM_APB_FREQ_MAX`` lock while the drive - **SPI slave**: between calls to :cpp:func:`spi_slave_initialize` and :cpp:func:`spi_slave_free`. - **Ethernet**: between calls to :cpp:func:`esp_eth_driver_install` and :cpp:func:`esp_eth_driver_uninstall`. - **WiFi**: between calls to :cpp:func:`esp_wifi_start` and :cpp:func:`esp_wifi_stop`. If modem sleep is enabled, the lock will be released for the periods of time when radio is disabled. - - **TWAI**: between calls to :cpp:func:`twai_driver_install` and :cpp:func:`twai_driver_uninstall`. + :SOC_TWAI_SUPPORTED: - **TWAI**: between calls to :cpp:func:`twai_driver_install` and :cpp:func:`twai_driver_uninstall` (only when the clock source is set to :cpp:enumerator:`TWAI_CLK_SRC_APB`). :SOC_BT_SUPPORTED and esp32: - **Bluetooth**: between calls to :cpp:func:`esp_bt_controller_enable` and :cpp:func:`esp_bt_controller_disable`. If Bluetooth modem sleep is enabled, the ``ESP_PM_APB_FREQ_MAX`` lock will be released for the periods of time when radio is disabled. However the ``ESP_PM_NO_LIGHT_SLEEP`` lock will still be held, unless :ref:`CONFIG_BTDM_CTRL_LOW_POWER_CLOCK` option is set to "External 32kHz crystal". :SOC_BT_SUPPORTED and not esp32: - **Bluetooth**: between calls to :cpp:func:`esp_bt_controller_enable` and :cpp:func:`esp_bt_controller_disable`. If Bluetooth modem sleep is enabled, the ``ESP_PM_APB_FREQ_MAX`` lock will be released for the periods of time when radio is disabled. However the ``ESP_PM_NO_LIGHT_SLEEP`` lock will still be held. diff --git a/docs/zh_CN/api-reference/system/power_management.rst b/docs/zh_CN/api-reference/system/power_management.rst index 0c38ed5a45..e199d07c24 100644 --- a/docs/zh_CN/api-reference/system/power_management.rst +++ b/docs/zh_CN/api-reference/system/power_management.rst @@ -120,7 +120,7 @@ ESP-IDF 中集成的电源管理算法可以根据应用程序组件的需求, - **SPI slave**:从调用 :cpp:func:`spi_slave_initialize` 至 :cpp:func:`spi_slave_free` 期间。 - **Ethernet**:从调用 :cpp:func:`esp_eth_driver_install` 至 :cpp:func:`esp_eth_driver_uninstall` 期间。 - **WiFi**:从调用 :cpp:func:`esp_wifi_start` 至 :cpp:func:`esp_wifi_stop` 期间。如果启用了调制解调器睡眠模式,广播关闭时将释放此管理锁。 - - **TWAI**:从调用 :cpp:func:`twai_driver_install` 至 :cpp:func:`twai_driver_uninstall` 期间。 + :SOC_TWAI_SUPPORTED: - **TWAI**:从调用 :cpp:func:`twai_driver_install` 至 :cpp:func:`twai_driver_uninstall` 期间 (只有在 TWAI 时钟源选择为 :cpp:enumerator:`TWAI_CLK_SRC_APB` 的时候生效)。 :SOC_BT_SUPPORTED and esp32: - **Bluetooth**:从调用 :cpp:func:`esp_bt_controller_enable` 至 :cpp:func:`esp_bt_controller_disable` 期间。如果启用了蓝牙调制解调器,广播关闭时将释放此管理锁。但依然占用 ``ESP_PM_NO_LIGHT_SLEEP`` 锁,除非将 :ref:`CONFIG_BTDM_CTRL_LOW_POWER_CLOCK` 选项设置为 “外部 32 kHz 晶振”。 :SOC_BT_SUPPORTED and not esp32: - **Bluetooth**:从调用 :cpp:func:`esp_bt_controller_enable` 至 :cpp:func:`esp_bt_controller_disable` 期间。如果启用了蓝牙调制解调器,广播关闭时将释放此管理锁。但依然占用 ``ESP_PM_NO_LIGHT_SLEEP`` 锁。