forked from espressif/esp-idf
feat(driver_twai): add new driver based on c5 twaifd
This commit is contained in:
@@ -81,9 +81,9 @@ if(CONFIG_SOC_TOUCH_SENSOR_SUPPORTED)
|
||||
endif()
|
||||
|
||||
# TWAI related source files
|
||||
if(CONFIG_SOC_TWAI_SUPPORTED)
|
||||
# TWAIFD is not supported by the legacy driver
|
||||
if(CONFIG_SOC_TWAI_SUPPORTED AND NOT CONFIG_SOC_TWAI_SUPPORT_FD)
|
||||
list(APPEND srcs "twai/twai.c")
|
||||
|
||||
list(APPEND ldfragments "twai/linker.lf")
|
||||
endif()
|
||||
|
||||
|
@@ -82,7 +82,8 @@ components/driver/test_apps/touch_sensor_v2:
|
||||
|
||||
components/driver/test_apps/twai:
|
||||
disable:
|
||||
- if: SOC_TWAI_SUPPORTED != 1
|
||||
- if: SOC_TWAI_SUPPORTED != 1 or SOC_TWAI_SUPPORT_FD == 1
|
||||
reason: legacy driver doesn't support FD
|
||||
depends_filepatterns:
|
||||
- components/driver/twai/**/*
|
||||
depends_components:
|
||||
|
22
components/esp_driver_twai/CMakeLists.txt
Normal file
22
components/esp_driver_twai/CMakeLists.txt
Normal file
@@ -0,0 +1,22 @@
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
|
||||
if(${target} STREQUAL "linux")
|
||||
return() # This component is not supported by the POSIX/Linux simulator
|
||||
endif()
|
||||
|
||||
set(srcs "esp_twai.c")
|
||||
set(public_include "include")
|
||||
set(priv_req esp_driver_gpio esp_pm)
|
||||
|
||||
# Currently support only FD targets
|
||||
if(CONFIG_SOC_TWAI_SUPPORT_FD)
|
||||
list(APPEND srcs "onchip/esp_twai_onchip.c")
|
||||
list(APPEND public_include "onchip/include")
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${srcs}
|
||||
INCLUDE_DIRS ${public_include}
|
||||
PRIV_REQUIRES ${priv_req}
|
||||
LDFRAGMENTS "linker.lf"
|
||||
)
|
18
components/esp_driver_twai/Kconfig
Normal file
18
components/esp_driver_twai/Kconfig
Normal file
@@ -0,0 +1,18 @@
|
||||
menu "ESP-Driver:TWAI Configurations"
|
||||
depends on SOC_TWAI_SUPPORTED
|
||||
|
||||
config TWAI_ISR_INTO_IRAM
|
||||
bool "Place TWAI ISR in IRAM to reduce latency"
|
||||
default n
|
||||
help
|
||||
Place ISR functions to IRAM to increase performance
|
||||
|
||||
config TWAI_ISR_CACHE_SAFE
|
||||
bool "Allow TWAI ISR to execute when cache is disabled"
|
||||
select TWAI_ISR_INTO_IRAM
|
||||
default n
|
||||
help
|
||||
Allow TWAI works under Cache disabled, to enabled this config,
|
||||
all callbacks and user_ctx should also place in IRAM
|
||||
|
||||
endmenu # TWAI Configuration
|
153
components/esp_driver_twai/esp_twai.c
Normal file
153
components/esp_driver_twai/esp_twai.c
Normal file
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <sys/param.h>
|
||||
#include "esp_check.h"
|
||||
#include "esp_twai.h"
|
||||
#include "esp_private/twai_interface.h"
|
||||
#include "esp_private/twai_priv.h"
|
||||
|
||||
static const char *TAG = "esp_twai";
|
||||
|
||||
/**
|
||||
* @brief Calculate twai timing param by giving bitrate and hardware limit.
|
||||
*
|
||||
* +---------------------------------------------------+
|
||||
* | bit time in time quanta (total_tq) |
|
||||
* +--------------+----------+------------+------------+
|
||||
* | sync_seg | prop_seg | phase_seg1 | phase_seg2 |
|
||||
* +--------------+----------+------------+------------+
|
||||
* | 1 | tseg1 | tseg2 |
|
||||
* +--------------+----------+------------+------------+
|
||||
* | tseg2/2 ^ ^
|
||||
* sjw sample_point
|
||||
*/
|
||||
uint32_t twai_node_timing_calc_param(const uint32_t source_freq, const twai_timing_basic_config_t *in_param, const twai_timing_constraint_t *hw_limit, twai_timing_advanced_config_t *out_param)
|
||||
{
|
||||
uint32_t total_div = (source_freq + in_param->bitrate / 2) / in_param->bitrate;
|
||||
uint32_t pre_div = hw_limit->brp_min;
|
||||
uint16_t tseg = 0;
|
||||
for (; pre_div <= hw_limit->brp_max; pre_div ++) {
|
||||
tseg = total_div / pre_div;
|
||||
if (total_div != tseg * pre_div) {
|
||||
continue; // no integer tseg
|
||||
}
|
||||
if ((tseg < (hw_limit->tseg1_max + hw_limit->tseg2_max)) && (tseg >= (hw_limit->tseg1_min + hw_limit->tseg2_min))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (pre_div > hw_limit->brp_max) { // no valid pre_div
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint16_t default_point = (in_param->bitrate >= 800000) ? 750 : ((in_param->bitrate >= 500000) ? 800 : 875);
|
||||
uint16_t sample_point = in_param->sample_point ? in_param->sample_point : default_point; // default sample point based on bitrate if not configured
|
||||
uint16_t tseg_1 = (tseg * sample_point) / 1000;
|
||||
tseg_1 = MAX(hw_limit->tseg1_min, MIN(tseg_1, hw_limit->tseg1_max));
|
||||
uint16_t tseg_2 = tseg - tseg_1 - 1;
|
||||
tseg_2 = MAX(hw_limit->tseg2_min, MIN(tseg_2, hw_limit->tseg2_max));
|
||||
tseg_1 = tseg - tseg_2 - 1;
|
||||
uint16_t prop = tseg_1 / 2; // distribute tseg1 evenly between prop_seg and tseg_1
|
||||
tseg_1 -= prop;
|
||||
|
||||
out_param->quanta_resolution_hz = 0; // going to deprecated IDF-12725
|
||||
out_param->brp = pre_div;
|
||||
out_param->prop_seg = prop;
|
||||
out_param->tseg_1 = tseg_1;
|
||||
out_param->tseg_2 = tseg_2;
|
||||
out_param->sjw = MAX(1, MIN(tseg_2 >> 1, hw_limit->sjw_max));
|
||||
out_param->ssp_offset = (tseg * in_param->ssp_permill) / 1000; // ssp is optional, default 0 if not configured
|
||||
|
||||
return source_freq / (pre_div * (prop + tseg_1 + tseg_2 + 1));
|
||||
}
|
||||
|
||||
esp_err_t twai_node_enable(twai_node_handle_t node)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(node, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null handle");
|
||||
ESP_RETURN_ON_FALSE(node->enable, ESP_ERR_NOT_SUPPORTED, TAG, "enable func null");
|
||||
|
||||
return node->enable(node);
|
||||
}
|
||||
|
||||
esp_err_t twai_node_disable(twai_node_handle_t node)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(node, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null handle");
|
||||
ESP_RETURN_ON_FALSE(node->disable, ESP_ERR_NOT_SUPPORTED, TAG, "disable func null");
|
||||
|
||||
return node->disable(node);
|
||||
}
|
||||
|
||||
esp_err_t twai_node_delete(twai_node_handle_t node)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(node, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null handle");
|
||||
ESP_RETURN_ON_FALSE(node->del, ESP_ERR_NOT_SUPPORTED, TAG, "delete func null");
|
||||
|
||||
return node->del(node);
|
||||
}
|
||||
|
||||
esp_err_t twai_node_config_mask_filter(twai_node_handle_t node, uint8_t filter_id, const twai_mask_filter_config_t *mask_cfg)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(node && mask_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null");
|
||||
ESP_RETURN_ON_FALSE(node->config_mask_filter, ESP_ERR_NOT_SUPPORTED, TAG, "config_mask_filter func null");
|
||||
|
||||
return node->config_mask_filter(node, filter_id, mask_cfg);
|
||||
}
|
||||
|
||||
esp_err_t twai_node_config_range_filter(twai_node_handle_t node, uint8_t filter_id, const twai_range_filter_config_t *range_cfg)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(node && range_cfg, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null");
|
||||
ESP_RETURN_ON_FALSE(node->config_range_filter, ESP_ERR_NOT_SUPPORTED, TAG, "config_range_filter func null");
|
||||
|
||||
return node->config_range_filter(node, filter_id, range_cfg);
|
||||
}
|
||||
|
||||
esp_err_t twai_node_reconfig_timing(twai_node_handle_t node, const twai_timing_advanced_config_t *bit_timing, const twai_timing_advanced_config_t *data_timing)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(node && (bit_timing || data_timing), ESP_ERR_INVALID_ARG, TAG, "invalid argument: null");
|
||||
ESP_RETURN_ON_FALSE(node->timing_reconfig, ESP_ERR_NOT_SUPPORTED, TAG, "timing_reconfig func null");
|
||||
|
||||
return node->timing_reconfig(node, bit_timing, data_timing);
|
||||
}
|
||||
|
||||
esp_err_t twai_node_register_event_callbacks(twai_node_handle_t node, const twai_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(node && cbs, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null");
|
||||
ESP_RETURN_ON_FALSE(node->register_cbs, ESP_ERR_NOT_SUPPORTED, TAG, "register_cbs func null");
|
||||
|
||||
return node->register_cbs(node, cbs, user_data);
|
||||
}
|
||||
|
||||
esp_err_t twai_node_recover(twai_node_handle_t node)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(node, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null handle");
|
||||
ESP_RETURN_ON_FALSE(node->recover, ESP_ERR_NOT_SUPPORTED, TAG, "recover func null");
|
||||
|
||||
return node->recover(node);
|
||||
}
|
||||
|
||||
esp_err_t twai_node_get_info(twai_node_handle_t node, twai_node_status_t *status_ret, twai_node_record_t *statistics_ret)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(node, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null handle");
|
||||
ESP_RETURN_ON_FALSE(node->get_info, ESP_ERR_NOT_SUPPORTED, TAG, "get_info func null");
|
||||
|
||||
return node->get_info(node, status_ret, statistics_ret);
|
||||
}
|
||||
|
||||
esp_err_t twai_node_transmit(twai_node_handle_t node, const twai_frame_t *frame, int timeout_ms)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(node && frame, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null");
|
||||
ESP_RETURN_ON_FALSE(node->transmit, ESP_ERR_NOT_SUPPORTED, TAG, "transmit func null");
|
||||
|
||||
return node->transmit(node, frame, timeout_ms);
|
||||
}
|
||||
|
||||
esp_err_t twai_node_receive_from_isr(twai_node_handle_t node, twai_frame_header_t *header, uint8_t *rx_buffer, size_t buf_sz, size_t *received_len)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE_ISR(node && header, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null");
|
||||
ESP_RETURN_ON_FALSE_ISR(node->receive_isr, ESP_ERR_NOT_SUPPORTED, TAG, "receive func null");
|
||||
|
||||
return node->receive_isr(node, header, rx_buffer, buf_sz, received_len);
|
||||
}
|
145
components/esp_driver_twai/include/esp_private/twai_interface.h
Normal file
145
components/esp_driver_twai/include/esp_private/twai_interface.h
Normal file
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_twai_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ESP TWAI driver API set definition
|
||||
*/
|
||||
struct twai_node_base {
|
||||
/**
|
||||
* @brief Enable the TWAI node
|
||||
*
|
||||
* @param[in] node Pointer to the TWAI node base
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_FAIL: Failed to enable the node
|
||||
*/
|
||||
esp_err_t (*enable)(struct twai_node_base *node);
|
||||
|
||||
/**
|
||||
* @brief Disable the TWAI node
|
||||
*
|
||||
* @param[in] node Pointer to the TWAI node base
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_FAIL: Failed to disable the node
|
||||
*/
|
||||
esp_err_t (*disable)(struct twai_node_base *node);
|
||||
|
||||
/**
|
||||
* @brief Delete the TWAI node and free its resources
|
||||
*
|
||||
* @param[in] node Pointer to the TWAI node base
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_FAIL: Failed to delete the node
|
||||
*/
|
||||
esp_err_t (*del)(struct twai_node_base *node);
|
||||
|
||||
/**
|
||||
* @brief Configure the mask filter of the TWAI node
|
||||
*
|
||||
* @param node Pointer to the TWAI node base
|
||||
* @param filter_id Index of the filter to configure
|
||||
* @param mask_cfg Pointer to the mask filter configuration
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t (*config_mask_filter)(struct twai_node_base *node, uint8_t filter_id, const twai_mask_filter_config_t *mask_cfg);
|
||||
|
||||
/**
|
||||
* @brief Configure the range filter of the TWAI node
|
||||
*
|
||||
* @param node Pointer to the TWAI node base
|
||||
* @param filter_id Index of the filter to configure
|
||||
* @param range_cfg Pointer to the range filter configuration
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t (*config_range_filter)(struct twai_node_base *node, uint8_t filter_id, const twai_range_filter_config_t *range_cfg);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the timing parameters for the TWAI node
|
||||
*
|
||||
* @param[in] node Pointer to the TWAI node base
|
||||
* @param bit_timing Pointer to new twai cc(classic) or arbitration phase of twai fd timing configuration
|
||||
* @param data_timing Pointer to new twai fd timing configuration
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid timing configuration
|
||||
*/
|
||||
esp_err_t (*timing_reconfig)(struct twai_node_base *node, const twai_timing_advanced_config_t *bit_timing, const twai_timing_advanced_config_t *data_timing);
|
||||
|
||||
/**
|
||||
* @brief Transmit a TWAI frame through the TWAI node
|
||||
*
|
||||
* @param[in] node Pointer to the TWAI node base
|
||||
* @param[in] frame Pointer to the frame to transmit
|
||||
* @param[in] timeout Timeout in milliseconds
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_FAIL: Failed to transmit frame
|
||||
*/
|
||||
esp_err_t (*transmit)(struct twai_node_base *node, const twai_frame_t *frame, int timeout);
|
||||
|
||||
/**
|
||||
* @brief Receive a TWAI frame through the ISR callback
|
||||
*
|
||||
* @param[in] node Pointer to the TWAI node base
|
||||
* @param[out] header Where to store frame header
|
||||
* @param[out] rx_buffer Where to store frame data
|
||||
* @param[in] buf_sz Bytes length of rx_buffer
|
||||
* @param[out] received_len Optional, the real data length of rx frame comes from 'header->dlc'
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_TIMEOUT: Reception timeout
|
||||
*/
|
||||
esp_err_t (*receive_isr)(struct twai_node_base *node, twai_frame_header_t *header, uint8_t *rx_buffer, size_t buf_sz, size_t *received_len);
|
||||
|
||||
/**
|
||||
* @brief Recover the TWAI node from a bus-off state
|
||||
*
|
||||
* @param[in] node Pointer to the TWAI node base
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_FAIL: Failed to recover node
|
||||
*/
|
||||
esp_err_t (*recover)(struct twai_node_base *node);
|
||||
|
||||
/**
|
||||
* @brief Register event callbacks for the TWAI node
|
||||
*
|
||||
* @param[in] node Pointer to the TWAI node base
|
||||
* @param[in] cbs Pointer to the event callback configuration
|
||||
* @param[in] user_data User data to pass to the callbacks
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid callback configuration
|
||||
*/
|
||||
esp_err_t (*register_cbs)(struct twai_node_base *node, const twai_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Get the status and statistics of the TWAI node
|
||||
*
|
||||
* @param[in] node Pointer to the TWAI node base
|
||||
* @param[out] status_ret Pointer to return the node status
|
||||
* @param[out] record_ret Pointer to return the node statistics
|
||||
* @return esp_err_t
|
||||
* - ESP_OK: Success
|
||||
* - ESP_FAIL: Failed to retrieve information
|
||||
*/
|
||||
esp_err_t (*get_info)(struct twai_node_base *node, twai_node_status_t *status_ret, twai_node_record_t *record_ret);
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
46
components/esp_driver_twai/include/esp_private/twai_priv.h
Normal file
46
components/esp_driver_twai/include/esp_private/twai_priv.h
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_twai_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief TWAI hardware-dependent bit-timing constant
|
||||
*
|
||||
* Used for calculating and checking bit-timing parameters
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t brp_min; /* Bit-rate prescaler */
|
||||
uint32_t brp_max;
|
||||
uint8_t tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */
|
||||
uint8_t tseg1_max;
|
||||
uint8_t tseg2_min; /* Time segment 2 = phase_seg2 */
|
||||
uint8_t tseg2_max;
|
||||
uint8_t sjw_max; /* Synchronisation jump width */
|
||||
} twai_timing_constraint_t;
|
||||
|
||||
/**
|
||||
* @brief Calculate TWAI timing parameters for a given source frequency and baud rate.
|
||||
*
|
||||
* This function computes the bit timing parameters (`brp`, `prop_seg`, `tseg_1`, `tseg_2`, `sjw`)
|
||||
* based on the provided source clock frequency and desired baud rate. It also adjusts for the sample
|
||||
* point ratio if specified.
|
||||
*
|
||||
* @param source_freq The source clock frequency in Hz.
|
||||
* @param in_param Pointer to the input configuration, which includes baud rate and sample rate.
|
||||
* @param hw_limit Pointer to const of hardware register limitation.
|
||||
* @param out_param Pointer to the output structure where the calculated timing parameters will be stored.
|
||||
* @return the actual hardware adopted baudrate.
|
||||
*/
|
||||
uint32_t twai_node_timing_calc_param(const uint32_t source_freq, const twai_timing_basic_config_t *in_param, const twai_timing_constraint_t *hw_limit, twai_timing_advanced_config_t *out_param);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
126
components/esp_driver_twai/include/esp_twai.h
Normal file
126
components/esp_driver_twai/include/esp_twai.h
Normal file
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_twai_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* -------------------------------------------------- Node Control -------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Enable the TWAI node
|
||||
*
|
||||
* @param node Handle to the TWAI node
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t twai_node_enable(twai_node_handle_t node);
|
||||
|
||||
/**
|
||||
* @brief Disable the TWAI node
|
||||
*
|
||||
* @param node Handle to the TWAI node
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t twai_node_disable(twai_node_handle_t node);
|
||||
|
||||
/**
|
||||
* @brief Init the recover process for TWAI node which in bus-off
|
||||
* @note Follow `on_state_change` callback or `twai_node_get_info` to know recover finish or not
|
||||
*
|
||||
* @param node Handle to the TWAI node
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t twai_node_recover(twai_node_handle_t node);
|
||||
|
||||
/**
|
||||
* @brief Delete the TWAI node and release resources
|
||||
*
|
||||
* @param node Handle to the TWAI node
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t twai_node_delete(twai_node_handle_t node);
|
||||
|
||||
/**
|
||||
* @brief Register event callbacks for the TWAI node
|
||||
*
|
||||
* @param node Handle to the TWAI node
|
||||
* @param cbs Pointer to a structure of event callback functions
|
||||
* @param user_data User-defined data passed to callback functions
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t twai_node_register_event_callbacks(twai_node_handle_t node, const twai_event_callbacks_t *cbs, void *user_data);
|
||||
|
||||
/**
|
||||
* @brief Reconfigure the timing settings of the TWAI node
|
||||
*
|
||||
* @param node Handle to the TWAI node
|
||||
* @param bit_timing Optional,pointer to new twai cc(classic) or arbitration phase of twai fd timing configuration
|
||||
* @param data_timing Optional, pointer to new twai fd timing configuration
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t twai_node_reconfig_timing(twai_node_handle_t node, const twai_timing_advanced_config_t *bit_timing, const twai_timing_advanced_config_t *data_timing);
|
||||
|
||||
/**
|
||||
* @brief Configure the mask filter of the TWAI node
|
||||
*
|
||||
* @param node Handle to the TWAI node
|
||||
* @param filter_id Index of the filter to configure
|
||||
* @param mask_cfg Pointer to the mask filter configuration
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t twai_node_config_mask_filter(twai_node_handle_t node, uint8_t filter_id, const twai_mask_filter_config_t *mask_cfg);
|
||||
|
||||
/**
|
||||
* @brief Configure the range filter of the TWAI node
|
||||
*
|
||||
* @param node Handle to the TWAI node
|
||||
* @param filter_id Index of the filter to configure
|
||||
* @param range_cfg Pointer to the range filter configuration
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t twai_node_config_range_filter(twai_node_handle_t node, uint8_t filter_id, const twai_range_filter_config_t *range_cfg);
|
||||
|
||||
/**
|
||||
* @brief Get information about the TWAI node
|
||||
*
|
||||
* @param node Handle to the TWAI node
|
||||
* @param status_ret Pointer to store the current node status
|
||||
* @param statistics_ret Pointer to store node statistics
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t twai_node_get_info(twai_node_handle_t node, twai_node_status_t *status_ret, twai_node_record_t *statistics_ret);
|
||||
|
||||
/* ----------------------------------------------- Node Communication ----------------------------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Transmit a TWAI frame
|
||||
*
|
||||
* @param[in] node Handle to the TWAI node
|
||||
* @param[in] frame Pointer to the frame to transmit
|
||||
* @param[in] timeout_ms Maximum wait time if the transmission queue is full (milliseconds), -1 to wait forever
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t twai_node_transmit(twai_node_handle_t node, const twai_frame_t *frame, int timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Receive a TWAI frame from 'rx_done_cb' (ONLY FROM 'rx_done_cb')
|
||||
*
|
||||
* @param[in] node Handle to the TWAI node
|
||||
* @param[out] header Where to store frame header
|
||||
* @param[out] rx_buffer Where to store frame data
|
||||
* @param[in] buf_sz Bytes length of rx_buffer
|
||||
* @param[out] received_len Optional, the real data length of rx frame comes from 'header->dlc', null if don't care
|
||||
* @return ESP_OK on success, error code otherwise
|
||||
*/
|
||||
esp_err_t twai_node_receive_from_isr(twai_node_handle_t node, twai_frame_header_t *header, uint8_t *rx_buffer, size_t buf_sz, size_t *received_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
181
components/esp_driver_twai/include/esp_twai_types.h
Normal file
181
components/esp_driver_twai/include/esp_twai_types.h
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stddef.h>
|
||||
#include "hal/twai_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief ESP TWAI controller handle
|
||||
*/
|
||||
typedef struct twai_node_base *twai_node_handle_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI bitrate timing config basic (simple) mode
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t bitrate; /**< Expected TWAI bus baud_rate/bitrate in bits/second */
|
||||
uint16_t sample_point; /**< Optional, sampling point in permill (1/1000) of the entire bit time */
|
||||
uint16_t ssp_permill; /**< Optional, secondary sample point(ssp) in permill (1/1000) of the entire bit time */
|
||||
} twai_timing_basic_config_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI transaction frame param type
|
||||
*/
|
||||
typedef struct twai_frame_t {
|
||||
twai_frame_header_t header; /**< message attribute/metadata, exclude data buffer*/
|
||||
uint8_t *buffer; /**< buffer address for tx and rx message data*/
|
||||
size_t buffer_len; /**< buffer length of provided data buffer pointer, in bytes.*/
|
||||
} twai_frame_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration for TWAI mask filter
|
||||
*
|
||||
* @note Set both id and mask to `0` to receive ALL frames, both `0xFFFFFFFF` to receive NONE frames
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t id; /**< Base ID for filtering */
|
||||
uint32_t mask; /**< Mask to determine the matching bits (1 = match bit, 0 = any bit) */
|
||||
struct {
|
||||
uint32_t is_ext: 1; /**< True for extended ID filtering, false for standard ID */
|
||||
uint32_t no_classic: 1; /**< If true, Classic CAN frames are excluded (only CAN FD allowed) */
|
||||
uint32_t no_fd: 1; /**< If true, CAN FD frames are excluded (only Classic CAN allowed) */
|
||||
};
|
||||
} twai_mask_filter_config_t;
|
||||
|
||||
/**
|
||||
* @brief Range-based filter configuration structure
|
||||
*
|
||||
* @note Set both range_low and range_high to `0` to receive ALL frames, both `0xFFFFFFFF` to receive NONE frames
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t range_low; /**< Lower bound of the filtering range */
|
||||
uint32_t range_high; /**< Upper bound of the filtering range */
|
||||
struct {
|
||||
uint32_t is_ext: 1; /**< True for extended ID filtering, false for standard ID */
|
||||
uint32_t no_classic: 1; /**< If true, Classic CAN frames are excluded (only CAN FD allowed) */
|
||||
uint32_t no_fd: 1; /**< If true, CAN FD frames are excluded (only Classic CAN allowed) */
|
||||
};
|
||||
} twai_range_filter_config_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI node's status
|
||||
*/
|
||||
typedef struct {
|
||||
twai_error_state_t state; /**< Node's error state */
|
||||
uint16_t tx_error_count; /**< Node's TX error count */
|
||||
uint16_t rx_error_count; /**< Node's RX error count */
|
||||
} twai_node_status_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI node's statistics/record type
|
||||
*
|
||||
* This structure contains some statistics regarding a node's communication on the TWAI bus
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t bus_err_num; /**< Cumulative number (since `twai_node_enable()`) of bus errors */
|
||||
} twai_node_record_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI "TX done" event data
|
||||
*/
|
||||
typedef struct {
|
||||
twai_frame_t *done_trans_tx;
|
||||
} twai_tx_done_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI "RX done" event data
|
||||
*/
|
||||
typedef struct {
|
||||
} twai_rx_done_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI "state change" event data
|
||||
*/
|
||||
typedef struct {
|
||||
twai_error_state_t old_sta; // Previous error state
|
||||
twai_error_state_t new_sta; // New error state after the change
|
||||
} twai_state_change_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI Error Type Structure
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t arb_lost: 1; /**< Arbitration lost error (lost arbitration during transmission) */
|
||||
uint32_t bit_err: 1; /**< Bit error detected (dominant/recessive mismatch during arbitration or transmission) */
|
||||
uint32_t form_err: 1; /**< Form error detected (frame fixed-form bit violation) */
|
||||
uint32_t stuff_err: 1; /**< Stuff error detected (e.g. dominant error frame received) */
|
||||
uint32_t reserved: 28; /**< Reserved bits for future use, must be set to zero */
|
||||
};
|
||||
uint32_t val; /**< Integrated error code */
|
||||
} twai_error_code_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI "error" event data
|
||||
*/
|
||||
typedef struct {
|
||||
twai_error_code_t err_type;
|
||||
} twai_error_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Group of supported TWAI callbacks
|
||||
*
|
||||
* @note All of these callbacks is invoked from ISR context. Thus, the implementation of the callback function must
|
||||
* adhere to the ISR restrictions such as not calling any blocking APIs.
|
||||
*
|
||||
* @note Set the particular event callback's entry to NULL to unregister it if not required.
|
||||
* @note When TWAI_ISR_CACHE_SAFE is enabled, the callbacks must be placed in IRAM.
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief TWAI "TX done" event callback prototype
|
||||
*
|
||||
* @param handle TWAI node handle
|
||||
* @param edata "TX done" event data (passed by the driver)
|
||||
* @param user_ctx User data, passed from `twai_node_register_event_callbacks()`
|
||||
* @return Whether a higher priority task has been unblocked by this function
|
||||
*/
|
||||
bool (*on_tx_done)(twai_node_handle_t handle, const twai_tx_done_event_data_t *edata, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief TWAI "RX done" event callback prototype
|
||||
*
|
||||
* @param handle TWAI node handle
|
||||
* @param edata "RX done" event data (passed by the driver)
|
||||
* @param user_ctx User data, passed from `twai_node_register_event_callbacks()`
|
||||
* @return Whether a higher priority task has been unblocked by this function
|
||||
*/
|
||||
bool (*on_rx_done)(twai_node_handle_t handle, const twai_rx_done_event_data_t *edata, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief TWAI "state change" event callback prototype
|
||||
*
|
||||
* @param handle TWAI node handle
|
||||
* @param edata "state change" event data (passed by the driver)
|
||||
* @param user_ctx User data, passed from `twai_node_register_event_callbacks()`
|
||||
* @return Whether a higher priority task has been unblocked by this function
|
||||
*/
|
||||
bool (*on_state_change)(twai_node_handle_t handle, const twai_state_change_event_data_t *edata, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief TWAI "error" event callback prototype
|
||||
*
|
||||
* @param[in] handle TWAI node handle
|
||||
* @param[in] edata "error" event data (passed by the driver)
|
||||
* @param[in] user_ctx User data, passed from `twai_node_register_event_callbacks()`
|
||||
* @return Whether a higher priority task has been unblocked by this function
|
||||
*/
|
||||
bool (*on_error)(twai_node_handle_t handle, const twai_error_event_data_t *edata, void *user_ctx);
|
||||
} twai_event_callbacks_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
8
components/esp_driver_twai/linker.lf
Normal file
8
components/esp_driver_twai/linker.lf
Normal file
@@ -0,0 +1,8 @@
|
||||
[mapping:esp_twai]
|
||||
archive: libesp_driver_twai.a
|
||||
entries:
|
||||
if TWAI_ISR_INTO_IRAM = y:
|
||||
esp_twai: twai_node_receive_from_isr (no_flash)
|
||||
esp_twai_onchip: _node_isr_main (no_flash)
|
||||
esp_twai_onchip: _node_start_trans (no_flash)
|
||||
esp_twai_onchip: _node_parse_rx (no_flash)
|
645
components/esp_driver_twai/onchip/esp_twai_onchip.c
Normal file
645
components/esp_driver_twai/onchip/esp_twai_onchip.c
Normal file
@@ -0,0 +1,645 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdatomic.h>
|
||||
#include <stdint.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/twai_periph.h"
|
||||
#include "soc/io_mux_reg.h"
|
||||
#include "hal/twai_hal.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_pm.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_clk_tree.h"
|
||||
#include "esp_twai.h"
|
||||
#include "esp_twai_onchip.h"
|
||||
#include "esp_private/twai_interface.h"
|
||||
#include "esp_private/twai_priv.h"
|
||||
#include "esp_private/gpio.h"
|
||||
#include "esp_private/esp_gpio_reserve.h"
|
||||
#include "esp_private/periph_ctrl.h"
|
||||
|
||||
static const char *TAG = "twai";
|
||||
|
||||
#ifdef CONFIG_TWAI_ISR_INTO_IRAM
|
||||
#define TWAI_MALLOC_CAPS (MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT)
|
||||
#else
|
||||
#define TWAI_MALLOC_CAPS MALLOC_CAP_DEFAULT
|
||||
#endif //CONFIG_TWAI_ISR_INTO_IRAM
|
||||
|
||||
#if !SOC_RCC_IS_INDEPENDENT
|
||||
#define TWAI_RCC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define TWAI_RCC_ATOMIC()
|
||||
#endif
|
||||
|
||||
#if SOC_PERIPH_CLK_CTRL_SHARED
|
||||
#define TWAI_PERI_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
#define TWAI_PERI_ATOMIC()
|
||||
#endif
|
||||
|
||||
#define DRIVER_DEFAULT_INTERRUPTS (TWAIFD_LL_INTR_TX_DONE |\
|
||||
TWAIFD_LL_INTR_TX_SUCCESS |\
|
||||
TWAIFD_LL_INTR_RX_NOT_EMPTY |\
|
||||
TWAIFD_LL_INTR_RX_FULL |\
|
||||
TWAIFD_LL_INTR_ERR_WARN |\
|
||||
TWAIFD_LL_INTR_BUS_ERR |\
|
||||
TWAIFD_LL_INTR_FSM_CHANGE |\
|
||||
TWAIFD_LL_INTR_ARBI_LOST |\
|
||||
TWAIFD_LL_INTR_DATA_OVERRUN)
|
||||
|
||||
static void _twai_rcc_clock_ctrl(uint8_t ctrlr_id, bool enable)
|
||||
{
|
||||
TWAI_RCC_ATOMIC() {
|
||||
twaifd_ll_enable_bus_clock(ctrlr_id, enable);
|
||||
twaifd_ll_reset_register(ctrlr_id);
|
||||
}
|
||||
TWAI_PERI_ATOMIC() {
|
||||
twaifd_ll_enable_clock(ctrlr_id, enable);
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
struct twai_node_base api_base;
|
||||
int ctrlr_id;
|
||||
uint64_t gpio_reserved;
|
||||
twai_hal_context_t hal;
|
||||
intr_handle_t intr_hdl;
|
||||
QueueHandle_t tx_mount_queue;
|
||||
twai_clock_source_t curr_clk_src;
|
||||
uint32_t valid_fd_timing;
|
||||
twai_event_callbacks_t cbs;
|
||||
void *user_data;
|
||||
esp_pm_lock_handle_t pm_lock;
|
||||
|
||||
_Atomic twai_error_state_t state;
|
||||
uint16_t tx_error_count;
|
||||
uint16_t rx_error_count;
|
||||
twai_node_record_t history;
|
||||
|
||||
atomic_bool hw_busy;
|
||||
atomic_bool rx_isr;
|
||||
const twai_frame_t *p_curr_tx;
|
||||
twai_hal_frame_t rcv_buff;
|
||||
} twai_onchip_ctx_t;
|
||||
|
||||
typedef struct twai_platform_s {
|
||||
_lock_t mutex;
|
||||
twai_onchip_ctx_t *nodes[SOC_TWAI_CONTROLLER_NUM];
|
||||
} twai_platform_t;
|
||||
static twai_platform_t s_platform;
|
||||
|
||||
static int _ctrlr_acquire(twai_onchip_ctx_t *node)
|
||||
{
|
||||
int ctrlr_id = -1;
|
||||
_lock_acquire(&s_platform.mutex);
|
||||
// Check if there is a controller available for use
|
||||
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
|
||||
if (s_platform.nodes[i] == NULL) {
|
||||
// Assign to node object to the controller slot
|
||||
s_platform.nodes[i] = node;
|
||||
ctrlr_id = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
_lock_release(&s_platform.mutex);
|
||||
|
||||
// Return the controller index or -1
|
||||
return ctrlr_id;
|
||||
}
|
||||
|
||||
static void _ctrlr_release(int ctrlr_id)
|
||||
{
|
||||
_lock_acquire(&s_platform.mutex);
|
||||
assert(s_platform.nodes[ctrlr_id]);
|
||||
// Clear the node object from the controller slot
|
||||
s_platform.nodes[ctrlr_id] = NULL;
|
||||
_lock_release(&s_platform.mutex);
|
||||
}
|
||||
|
||||
static esp_err_t _node_config_io(twai_onchip_ctx_t *node, const twai_onchip_node_config_t *node_config)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_OUTPUT_GPIO(node_config->io_cfg.tx), ESP_ERR_INVALID_ARG, TAG, "Invalid tx gpio num");
|
||||
ESP_RETURN_ON_FALSE(GPIO_IS_VALID_GPIO(node_config->io_cfg.rx), ESP_ERR_INVALID_ARG, TAG, "Invalid rx gpio num");
|
||||
uint64_t reserve_mask = BIT64(node_config->io_cfg.tx);
|
||||
|
||||
// Set RX pin
|
||||
gpio_input_enable(node_config->io_cfg.rx);
|
||||
gpio_set_pull_mode(node_config->io_cfg.rx, GPIO_PULLUP_ONLY); // pullup to avoid noise if no connection to transceiver
|
||||
gpio_func_sel(node_config->io_cfg.rx, PIN_FUNC_GPIO);
|
||||
esp_rom_gpio_connect_in_signal(node_config->io_cfg.rx, twai_controller_periph_signals.controllers[node->ctrlr_id].rx_sig, false);
|
||||
|
||||
// Set TX pin
|
||||
gpio_func_sel(node_config->io_cfg.tx, PIN_FUNC_GPIO);
|
||||
esp_rom_gpio_connect_out_signal(node_config->io_cfg.tx, twai_controller_periph_signals.controllers[node->ctrlr_id].tx_sig, false, false);
|
||||
|
||||
//Configure output clock pin (Optional)
|
||||
if (GPIO_IS_VALID_OUTPUT_GPIO(node_config->io_cfg.quanta_clk_out)) {
|
||||
reserve_mask |= BIT64(node_config->io_cfg.quanta_clk_out);
|
||||
gpio_func_sel(node_config->io_cfg.quanta_clk_out, PIN_FUNC_GPIO);
|
||||
esp_rom_gpio_connect_out_signal(node_config->io_cfg.quanta_clk_out, twai_controller_periph_signals.controllers[node->ctrlr_id].clk_out_sig, false, false);
|
||||
}
|
||||
|
||||
//Configure bus status pin (Optional)
|
||||
if (GPIO_IS_VALID_OUTPUT_GPIO(node_config->io_cfg.bus_off_indicator)) {
|
||||
reserve_mask |= BIT64(node_config->io_cfg.bus_off_indicator);
|
||||
gpio_func_sel(node_config->io_cfg.bus_off_indicator, PIN_FUNC_GPIO);
|
||||
esp_rom_gpio_connect_out_signal(node_config->io_cfg.bus_off_indicator, twai_controller_periph_signals.controllers[node->ctrlr_id].bus_off_sig, false, false);
|
||||
}
|
||||
|
||||
node->gpio_reserved = reserve_mask;
|
||||
uint64_t busy_mask = esp_gpio_reserve(reserve_mask);
|
||||
uint64_t conflict_mask = busy_mask & reserve_mask;
|
||||
for (; conflict_mask > 0;) {
|
||||
uint8_t pos = __builtin_ctzll(conflict_mask);
|
||||
conflict_mask &= ~(1ULL << pos);
|
||||
ESP_LOGW(TAG, "GPIO %d is not usable, maybe used by others", pos);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void _node_release_io(twai_onchip_ctx_t *node)
|
||||
{
|
||||
esp_rom_gpio_connect_in_signal(GPIO_MATRIX_CONST_ONE_INPUT, twai_controller_periph_signals.controllers[node->ctrlr_id].rx_sig, false);
|
||||
esp_gpio_revoke(node->gpio_reserved);
|
||||
for (; node->gpio_reserved > 0;) {
|
||||
uint8_t pos = __builtin_ctzll(node->gpio_reserved);
|
||||
node->gpio_reserved &= ~(1ULL << pos);
|
||||
gpio_output_disable(pos);
|
||||
}
|
||||
}
|
||||
|
||||
static void _node_start_trans(twai_onchip_ctx_t *node)
|
||||
{
|
||||
const twai_frame_header_t *format = &node->p_curr_tx->header;
|
||||
twai_hal_context_t *hal = &node->hal;
|
||||
twai_hal_frame_t tx_frame = {};
|
||||
|
||||
int final_dlc = (format->dlc) ? format->dlc : twaifd_len2dlc(node->p_curr_tx->buffer_len);
|
||||
int data_len = (format->dlc) ? twaifd_dlc2len(format->dlc) : node->p_curr_tx->buffer_len;
|
||||
twaifd_ll_format_frame_header(format, final_dlc, &tx_frame);
|
||||
twaifd_ll_format_frame_data(node->p_curr_tx->buffer, data_len, &tx_frame);
|
||||
//TODO: utilize all txt buffers
|
||||
twaifd_ll_mount_tx_buffer(hal->dev, &tx_frame, 0);
|
||||
twaifd_ll_set_tx_cmd(hal->dev, 0, TWAIFD_LL_TX_CMD_READY);
|
||||
}
|
||||
|
||||
static void _node_isr_main(void *arg)
|
||||
{
|
||||
BaseType_t do_yield = pdFALSE;
|
||||
twai_onchip_ctx_t *twai_ctx = arg;
|
||||
uint32_t int_stat = twaifd_ll_get_intr_status(twai_ctx->hal.dev);
|
||||
twai_ctx->tx_error_count = twaifd_ll_get_tec(twai_ctx->hal.dev);
|
||||
twai_ctx->rx_error_count = twaifd_ll_get_rec(twai_ctx->hal.dev);
|
||||
twaifd_ll_clr_intr_status(twai_ctx->hal.dev, int_stat);
|
||||
|
||||
// deal ERR event
|
||||
if (int_stat & (TWAIFD_LL_INTR_BUS_ERR | TWAIFD_LL_INTR_ARBI_LOST)) {
|
||||
uint32_t err_reason = twaifd_ll_get_err_reason_code(twai_ctx->hal.dev);
|
||||
twai_error_event_data_t e_data = {0};
|
||||
e_data.err_type.arb_lost = !!(int_stat & TWAIFD_LL_INTR_ARBI_LOST);
|
||||
e_data.err_type.bit_err = (err_reason == TWAIFD_LL_ERR_BIT_ERR);
|
||||
e_data.err_type.form_err = (err_reason == TWAIFD_LL_ERR_FRM_ERR);
|
||||
e_data.err_type.stuff_err = (err_reason == TWAIFD_LL_ERR_STUF_ERR);
|
||||
|
||||
twai_ctx->history.bus_err_num ++;
|
||||
if (twai_ctx->cbs.on_error) {
|
||||
do_yield |= twai_ctx->cbs.on_error(&twai_ctx->api_base, &e_data, twai_ctx->user_data);
|
||||
}
|
||||
}
|
||||
|
||||
// deal FSM event
|
||||
if (int_stat & (TWAIFD_LL_INTR_FSM_CHANGE | TWAIFD_LL_INTR_ERR_WARN)) {
|
||||
twai_state_change_event_data_t e_data = {.old_sta = twai_ctx->state};
|
||||
e_data.new_sta = twaifd_ll_get_fault_state(twai_ctx->hal.dev);
|
||||
uint32_t limit = twaifd_ll_get_err_warn_limit(twai_ctx->hal.dev);
|
||||
if ((e_data.new_sta == TWAI_ERROR_ACTIVE) && ((twai_ctx->tx_error_count >= limit) || (twai_ctx->rx_error_count >= limit))) {
|
||||
e_data.new_sta = TWAI_ERROR_WARNING;
|
||||
}
|
||||
atomic_store(&twai_ctx->state, e_data.new_sta);
|
||||
if (twai_ctx->cbs.on_state_change) {
|
||||
do_yield |= twai_ctx->cbs.on_state_change(&twai_ctx->api_base, &e_data, twai_ctx->user_data);
|
||||
}
|
||||
}
|
||||
|
||||
// deal TX event
|
||||
if (int_stat & (TWAIFD_LL_INTR_TX_DONE | TWAIFD_LL_INTR_TX_SUCCESS)) {
|
||||
// only call tx_done_cb when tx without error, otherwise on_error_cb should triggered if it is registered
|
||||
if (twai_ctx->cbs.on_tx_done && (int_stat & TWAIFD_LL_INTR_TX_DONE)) {
|
||||
twai_tx_done_event_data_t tx_ev = {
|
||||
.done_trans_tx = (twai_frame_t *)twai_ctx->p_curr_tx,
|
||||
};
|
||||
do_yield |= twai_ctx->cbs.on_tx_done(&twai_ctx->api_base, &tx_ev, twai_ctx->user_data);
|
||||
}
|
||||
// start a new TX
|
||||
if ((atomic_load(&twai_ctx->state) != TWAI_ERROR_BUS_OFF) && xQueueReceiveFromISR(twai_ctx->tx_mount_queue, &twai_ctx->p_curr_tx, &do_yield)) {
|
||||
// Sanity check, must in `hw_busy` here, otherwise logic bug is somewhere
|
||||
assert(twai_ctx->hw_busy);
|
||||
_node_start_trans(twai_ctx);
|
||||
} else {
|
||||
atomic_store(&twai_ctx->hw_busy, false);
|
||||
}
|
||||
}
|
||||
|
||||
// deal RX event
|
||||
if (int_stat & TWAIFD_LL_INTR_RX_NOT_EMPTY) {
|
||||
while (twaifd_ll_get_rx_frame_count(twai_ctx->hal.dev)) {
|
||||
twaifd_ll_get_rx_frame(twai_ctx->hal.dev, &twai_ctx->rcv_buff);
|
||||
if (twai_ctx->cbs.on_rx_done) {
|
||||
atomic_store(&twai_ctx->rx_isr, true);
|
||||
const twai_rx_done_event_data_t rx_ev = {
|
||||
};
|
||||
do_yield |= twai_ctx->cbs.on_rx_done(&twai_ctx->api_base, &rx_ev, twai_ctx->user_data);
|
||||
atomic_store(&twai_ctx->rx_isr, false);
|
||||
}
|
||||
}
|
||||
// rx flag can only cleared after read to empty
|
||||
twaifd_ll_clr_intr_status(twai_ctx->hal.dev, TWAIFD_LL_INTR_RX_NOT_EMPTY);
|
||||
}
|
||||
|
||||
if (do_yield) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t _node_delete(twai_node_handle_t node)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||
ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) == TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "delete node must when node stopped");
|
||||
|
||||
_node_release_io(twai_ctx);
|
||||
_ctrlr_release(twai_ctx->ctrlr_id);
|
||||
_twai_rcc_clock_ctrl(twai_ctx->ctrlr_id, false);
|
||||
#if CONFIG_PM_ENABLE
|
||||
if (twai_ctx->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_delete(twai_ctx->pm_lock), TAG, "delete power manager failed");
|
||||
}
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
esp_intr_free(twai_ctx->intr_hdl);
|
||||
vQueueDeleteWithCaps(twai_ctx->tx_mount_queue);
|
||||
free(twai_ctx);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _node_register_callbacks(twai_node_handle_t node, const twai_event_callbacks_t *cbs, void *user_data)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||
ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) == TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "register callback must when node stopped");
|
||||
#if CONFIG_TWAI_ISR_CACHE_SAFE
|
||||
ESP_RETURN_ON_FALSE(!cbs->on_tx_done || esp_ptr_in_iram(cbs->on_tx_done), ESP_ERR_INVALID_ARG, TAG, "on_tx_done callback not in IRAM");
|
||||
ESP_RETURN_ON_FALSE(!cbs->on_rx_done || esp_ptr_in_iram(cbs->on_rx_done), ESP_ERR_INVALID_ARG, TAG, "on_rx_done callback not in IRAM");
|
||||
ESP_RETURN_ON_FALSE(!cbs->on_state_change || esp_ptr_in_iram(cbs->on_state_change), ESP_ERR_INVALID_ARG, TAG, "on_state_change callback not in IRAM");
|
||||
ESP_RETURN_ON_FALSE(!cbs->on_error || esp_ptr_in_iram(cbs->on_error), ESP_ERR_INVALID_ARG, TAG, "on_error callback not in IRAM");
|
||||
#endif
|
||||
memcpy(&twai_ctx->cbs, cbs, sizeof(twai_event_callbacks_t));
|
||||
twai_ctx->user_data = user_data;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _node_check_timing_valid(twai_onchip_ctx_t *twai_ctx, const twai_timing_advanced_config_t *timing, uint32_t source_freq)
|
||||
{
|
||||
if (!timing) {
|
||||
return ESP_OK;
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(!timing->quanta_resolution_hz, ESP_ERR_INVALID_ARG, TAG, "quanta_resolution_hz is not supported"); //TODO: IDF-12725
|
||||
ESP_RETURN_ON_FALSE((timing->brp >= SOC_TWAI_BRP_MIN) && (timing->brp <= SOC_TWAI_BRP_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid brp");
|
||||
ESP_RETURN_ON_FALSE((timing->tseg_1 >= TWAI_LL_TSEG1_MIN) && (timing->tseg_1 <= TWAI_LL_TSEG1_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid tseg1");
|
||||
ESP_RETURN_ON_FALSE((timing->tseg_2 >= TWAI_LL_TSEG2_MIN) && (timing->tseg_2 <= TWAI_LL_TSEG2_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid tseg_2");
|
||||
ESP_RETURN_ON_FALSE((timing->sjw >= 1) && (timing->sjw <= TWAI_LL_SJW_MAX), ESP_ERR_INVALID_ARG, TAG, "invalid swj");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _node_set_bit_timing(twai_node_handle_t node, const twai_timing_advanced_config_t *timing, const twai_timing_advanced_config_t *timing_fd)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||
twai_clock_source_t new_clock_src = twai_ctx->curr_clk_src;
|
||||
ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) == TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "config timing must when node stopped");
|
||||
if (timing && timing_fd) {
|
||||
ESP_RETURN_ON_FALSE(timing->clk_src == timing_fd->clk_src, ESP_ERR_INVALID_ARG, TAG, "clk_src of 2 configs must same");
|
||||
new_clock_src = timing->clk_src ? timing->clk_src : TWAI_CLK_SRC_DEFAULT;
|
||||
} else {
|
||||
if (timing) {
|
||||
new_clock_src = timing->clk_src ? timing->clk_src : TWAI_CLK_SRC_DEFAULT;
|
||||
} else {
|
||||
ESP_RETURN_ON_FALSE(!timing_fd->clk_src || (timing_fd->clk_src == TWAI_CLK_SRC_DEFAULT), ESP_ERR_INVALID_ARG, TAG, "don't change clk_src in single config");
|
||||
}
|
||||
}
|
||||
uint32_t source_freq = 0;
|
||||
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(new_clock_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &source_freq), TAG, "clock src error, can't get freq");
|
||||
ESP_RETURN_ON_ERROR(_node_check_timing_valid(twai_ctx, timing, source_freq), TAG, "invalid param");
|
||||
ESP_RETURN_ON_ERROR(_node_check_timing_valid(twai_ctx, timing_fd, source_freq), TAG, "invalid fd param");
|
||||
|
||||
int ssp_offset = 0;
|
||||
if (timing) {
|
||||
twaifd_ll_set_nominal_bitrate(twai_ctx->hal.dev, timing);
|
||||
if (timing->ssp_offset) {
|
||||
ssp_offset = timing->ssp_offset;
|
||||
}
|
||||
}
|
||||
#if SOC_TWAI_SUPPORT_FD
|
||||
if (timing_fd) {
|
||||
twai_ctx->valid_fd_timing = true;
|
||||
twaifd_ll_set_fd_bitrate(twai_ctx->hal.dev, timing_fd);
|
||||
if (timing_fd->ssp_offset) {
|
||||
// prefer to config ssp by fd param
|
||||
ssp_offset = timing_fd->ssp_offset;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// config ssp, the hardware offset_val TWAIFD_LL_SSP_SRC_MEAS_OFFSET measured by clock_src freq
|
||||
twaifd_ll_config_secondary_sample_point(twai_ctx->hal.dev, (ssp_offset ? TWAIFD_LL_SSP_SRC_MEAS_OFFSET : TWAIFD_LL_SSP_SRC_NO_SSP), ssp_offset);
|
||||
if (new_clock_src != twai_ctx->curr_clk_src) {
|
||||
twai_ctx->curr_clk_src = new_clock_src;
|
||||
TWAI_PERI_ATOMIC() {
|
||||
twaifd_ll_set_clock_source(twai_ctx->ctrlr_id, new_clock_src);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _node_calc_set_bit_timing(twai_node_handle_t node, twai_clock_source_t clk_src, const twai_timing_basic_config_t *timing, const twai_timing_basic_config_t *timing_fd)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(timing->bitrate, ESP_ERR_INVALID_ARG, TAG, "classic timing config is required");
|
||||
#if !SOC_TWAI_SUPPORT_FD
|
||||
ESP_RETURN_ON_FALSE((!timing_fd->bitrate) || (timing_fd->bitrate == timing->bitrate), ESP_ERR_INVALID_ARG, TAG, "FD stage bitrate is not supported");
|
||||
#endif
|
||||
twai_clock_source_t root_clock_src = clk_src ? clk_src : TWAI_CLK_SRC_DEFAULT;
|
||||
uint32_t source_freq = 0;
|
||||
ESP_RETURN_ON_ERROR(esp_clk_tree_src_get_freq_hz(root_clock_src, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &source_freq), TAG, "can't get clock source freq");
|
||||
|
||||
twai_timing_advanced_config_t timing_adv = { .clk_src = root_clock_src, };
|
||||
twai_timing_advanced_config_t *fd_cfg_ptr = NULL, timing_adv_fd = { .clk_src = root_clock_src, };
|
||||
twai_timing_constraint_t hw_const = {
|
||||
.brp_min = SOC_TWAI_BRP_MIN,
|
||||
.brp_max = SOC_TWAI_BRP_MAX,
|
||||
.tseg1_min = TWAI_LL_TSEG1_MIN,
|
||||
.tseg1_max = TWAI_LL_TSEG1_MAX,
|
||||
.tseg2_min = TWAI_LL_TSEG2_MIN,
|
||||
.tseg2_max = TWAI_LL_TSEG2_MAX,
|
||||
.sjw_max = TWAI_LL_SJW_MAX,
|
||||
};
|
||||
uint32_t real_baud = twai_node_timing_calc_param(source_freq, timing, &hw_const, &timing_adv);
|
||||
ESP_LOGD(TAG, "norm src %ld, param %ld %d %d %d %d %d", source_freq, timing_adv.brp, timing_adv.prop_seg, timing_adv.tseg_1, timing_adv.tseg_2, timing_adv.sjw, timing_adv.ssp_offset);
|
||||
ESP_RETURN_ON_FALSE(real_baud, ESP_ERR_INVALID_ARG, TAG, "bitrate can't achieve!");
|
||||
if (timing->bitrate != real_baud) {
|
||||
ESP_LOGW(TAG, "bitrate precision loss, adjust from %ld to %ld", timing->bitrate, real_baud);
|
||||
}
|
||||
#if SOC_TWAI_SUPPORT_FD
|
||||
if (timing_fd->bitrate) {
|
||||
real_baud = twai_node_timing_calc_param(source_freq, timing_fd, &hw_const, &timing_adv_fd);
|
||||
ESP_LOGD(TAG, "fd src %ld, param %ld %d %d %d %d %d", source_freq, timing_adv_fd.brp, timing_adv_fd.prop_seg, timing_adv_fd.tseg_1, timing_adv_fd.tseg_2, timing_adv_fd.sjw, timing_adv_fd.ssp_offset);
|
||||
ESP_RETURN_ON_FALSE(real_baud, ESP_ERR_INVALID_ARG, TAG, "bitrate can't achieve!");
|
||||
if (timing_fd->bitrate != real_baud) {
|
||||
ESP_LOGW(TAG, "bitrate precision loss, adjust from %ld to %ld", timing_fd->bitrate, real_baud);
|
||||
}
|
||||
fd_cfg_ptr = &timing_adv_fd;
|
||||
}
|
||||
#endif
|
||||
ESP_RETURN_ON_ERROR(_node_set_bit_timing(node, &timing_adv, fd_cfg_ptr), TAG, "invalid timing param");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* -------------------------------------------------- Node Control -------------------------------------------------- */
|
||||
|
||||
static esp_err_t _node_enable(twai_node_handle_t node)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||
ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) == TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "node already enabled");
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
//Acquire pm_lock until _node_disable for potential receive
|
||||
if (twai_ctx->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_acquire(twai_ctx->pm_lock), TAG, "acquire power manager failed");
|
||||
}
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
twaifd_ll_enable_hw(twai_ctx->hal.dev, true);
|
||||
twaifd_ll_waiting_state_change(twai_ctx->hal.dev); // waiting hardware ready before resume transaction
|
||||
|
||||
// continuing the transaction if there be
|
||||
if (atomic_load(&twai_ctx->hw_busy) && twaifd_ll_get_fault_state(twai_ctx->hal.dev) != TWAI_ERROR_BUS_OFF) {
|
||||
_node_start_trans(twai_ctx);
|
||||
}
|
||||
// once interrupt enabled, state_change_intr will fire at the moment and update the `twai_ctx->state`
|
||||
ESP_RETURN_ON_ERROR(esp_intr_enable(twai_ctx->intr_hdl), TAG, "enable interrupt failed");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _node_disable(twai_node_handle_t node)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||
ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) != TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "node already disabled");
|
||||
|
||||
esp_intr_disable(twai_ctx->intr_hdl);
|
||||
atomic_store(&twai_ctx->state, TWAI_ERROR_BUS_OFF);
|
||||
twaifd_ll_enable_hw(twai_ctx->hal.dev, false);
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
if (twai_ctx->pm_lock) {
|
||||
ESP_RETURN_ON_ERROR(esp_pm_lock_release(twai_ctx->pm_lock), TAG, "release power manager failed");
|
||||
}
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _node_config_mask_filter(twai_node_handle_t node, uint8_t filter_id, const twai_mask_filter_config_t *mask_cfg)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||
ESP_RETURN_ON_FALSE(filter_id < SOC_TWAI_MASK_FILTER_NUM, ESP_ERR_INVALID_ARG, TAG, "Invalid mask filter id %d", filter_id);
|
||||
ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) == TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "config filter must when node stopped");
|
||||
|
||||
bool full_open = (mask_cfg->mask == 0) && (mask_cfg->id == 0);
|
||||
bool full_close = (mask_cfg->mask == UINT32_MAX) && (mask_cfg->id == UINT32_MAX);
|
||||
bool cc_ext = full_open || (!full_close && mask_cfg->is_ext && !mask_cfg->no_classic);
|
||||
bool fd_ext = full_open || (!full_close && mask_cfg->is_ext && !mask_cfg->no_fd);
|
||||
bool cc_std = full_open || (!full_close && !mask_cfg->is_ext && !mask_cfg->no_classic);
|
||||
bool fd_std = full_open || (!full_close && !mask_cfg->is_ext && !mask_cfg->no_fd);
|
||||
twaifd_ll_filter_enable_basic_ext(twai_ctx->hal.dev, filter_id, false, cc_ext);
|
||||
twaifd_ll_filter_enable_fd_ext(twai_ctx->hal.dev, filter_id, false, fd_ext);
|
||||
twaifd_ll_filter_enable_basic_std(twai_ctx->hal.dev, filter_id, false, cc_std);
|
||||
twaifd_ll_filter_enable_fd_std(twai_ctx->hal.dev, filter_id, false, fd_std);
|
||||
twaifd_ll_filter_set_id_mask(twai_ctx->hal.dev, filter_id, mask_cfg->is_ext, mask_cfg->id, mask_cfg->mask);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if SOC_TWAI_RANGE_FILTER_NUM
|
||||
static esp_err_t _node_config_range_filter(twai_node_handle_t node, uint8_t filter_id, const twai_range_filter_config_t *range_cfg)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||
ESP_RETURN_ON_FALSE(filter_id < SOC_TWAI_RANGE_FILTER_NUM, ESP_ERR_INVALID_ARG, TAG, "Invalid range filter id %d", filter_id);
|
||||
ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) == TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "config filter must when node stopped");
|
||||
|
||||
bool full_open = (range_cfg->range_high == 0) && (range_cfg->range_low == 0);
|
||||
bool full_close = (range_cfg->range_high == UINT32_MAX) && (range_cfg->range_low == UINT32_MAX);
|
||||
bool cc_ext = full_open || (!full_close && range_cfg->is_ext && !range_cfg->no_classic);
|
||||
bool fd_ext = full_open || (!full_close && range_cfg->is_ext && !range_cfg->no_fd);
|
||||
bool cc_std = full_open || (!full_close && !range_cfg->is_ext && !range_cfg->no_classic);
|
||||
bool fd_std = full_open || (!full_close && !range_cfg->is_ext && !range_cfg->no_fd);
|
||||
twaifd_ll_filter_enable_basic_ext(twai_ctx->hal.dev, filter_id, true, cc_ext);
|
||||
twaifd_ll_filter_enable_fd_ext(twai_ctx->hal.dev, filter_id, true, fd_ext);
|
||||
twaifd_ll_filter_enable_basic_std(twai_ctx->hal.dev, filter_id, true, cc_std);
|
||||
twaifd_ll_filter_enable_fd_std(twai_ctx->hal.dev, filter_id, true, fd_std);
|
||||
twaifd_ll_filter_set_range(twai_ctx->hal.dev, 0, range_cfg->is_ext, range_cfg->range_high, range_cfg->range_low);
|
||||
return ESP_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
static esp_err_t _node_recover(twai_node_handle_t node)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||
ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) == TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "node not in bus off");
|
||||
|
||||
// After recover command, the hardware require 128 consecutive occurrences of 11 recessive bits received, so that it can be active again!
|
||||
// Checking `twai_node_status_t::state` Or waiting `on_state_change` callback can know if recover is finish
|
||||
twaifd_ll_set_operate_cmd(twai_ctx->hal.dev, TWAIFD_LL_HW_CMD_RST_ERR_CNT);
|
||||
twai_ctx->history.bus_err_num = 0;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _node_get_status(twai_node_handle_t node, twai_node_status_t *status_ret, twai_node_record_t *record_ret)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||
|
||||
if (status_ret) {
|
||||
status_ret->state = atomic_load(&twai_ctx->state);
|
||||
status_ret->tx_error_count = twai_ctx->tx_error_count;
|
||||
status_ret->rx_error_count = twai_ctx->rx_error_count;
|
||||
}
|
||||
if (record_ret) {
|
||||
*record_ret = twai_ctx->history;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------- Node Communication ----------------------------------------------- */
|
||||
|
||||
static esp_err_t _node_queue_tx(twai_node_handle_t node, const twai_frame_t *frame, int timeout)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||
if (frame->header.dlc && frame->buffer_len) {
|
||||
ESP_RETURN_ON_FALSE(frame->header.dlc == twaifd_len2dlc(frame->buffer_len), ESP_ERR_INVALID_ARG, TAG, "unmatched dlc and buffer_len");
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(frame->buffer_len <= (frame->header.fdf ? TWAIFD_FRAME_MAX_LEN : TWAI_FRAME_MAX_LEN), ESP_ERR_INVALID_ARG, TAG, "illegal transfer length (buffer_len %ld)", frame->buffer_len);
|
||||
ESP_RETURN_ON_FALSE((!frame->header.brs) || (twai_ctx->valid_fd_timing), ESP_ERR_INVALID_ARG, TAG, "brs can't be used without config data_timing");
|
||||
ESP_RETURN_ON_FALSE(!twai_ctx->hal.enable_listen_only, ESP_ERR_NOT_SUPPORTED, TAG, "node is config as listen only");
|
||||
ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) != TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "node is bus off");
|
||||
TickType_t ticks_to_wait = (timeout == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout);
|
||||
|
||||
bool false_var = false;
|
||||
if (atomic_compare_exchange_strong(&twai_ctx->hw_busy, &false_var, true)) {
|
||||
twai_ctx->p_curr_tx = frame;
|
||||
_node_start_trans(twai_ctx);
|
||||
} else {
|
||||
//options in following steps (in_queue->2nd_check->pop_queue) should exec ASAP
|
||||
//within about 50us (minimum time for one msg), to ensure data safe
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(twai_ctx->tx_mount_queue, &frame, ticks_to_wait), ESP_ERR_TIMEOUT, TAG, "tx queue full");
|
||||
false_var = false;
|
||||
if (atomic_compare_exchange_strong(&twai_ctx->hw_busy, &false_var, true)) {
|
||||
if (xQueueReceive(twai_ctx->tx_mount_queue, &twai_ctx->p_curr_tx, 0) != pdTRUE) {
|
||||
assert(false && "should always get frame at this moment");
|
||||
}
|
||||
_node_start_trans(twai_ctx);
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t _node_parse_rx(twai_node_handle_t node, twai_frame_header_t *header, uint8_t *rx_buffer, size_t buf_len, size_t *ret_len)
|
||||
{
|
||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||
ESP_RETURN_ON_FALSE_ISR(atomic_load(&twai_ctx->rx_isr), ESP_ERR_INVALID_STATE, TAG, "rx can only called in `rx_done` callback");
|
||||
|
||||
twaifd_ll_parse_frame_header(&twai_ctx->rcv_buff, header);
|
||||
uint32_t frame_data_len = twaifd_dlc2len(header->dlc);
|
||||
uint8_t final_len = (frame_data_len < buf_len) ? frame_data_len : buf_len;
|
||||
twaifd_ll_parse_frame_data(&twai_ctx->rcv_buff, rx_buffer, final_len);
|
||||
if (ret_len) {
|
||||
*ret_len = frame_data_len;
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* --------------------------------- Public --------------------------------- */
|
||||
esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twai_node_handle_t *node_ret)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_RETURN_ON_FALSE(node_config->tx_queue_depth > 0, ESP_ERR_INVALID_ARG, TAG, "tx_queue_depth at least 1");
|
||||
ESP_RETURN_ON_FALSE(!node_config->intr_priority || (BIT(node_config->intr_priority) & ESP_INTR_FLAG_LOWMED), ESP_ERR_INVALID_ARG, TAG, "Invalid intr_priority level");
|
||||
|
||||
// Allocate TWAI node object memory
|
||||
twai_onchip_ctx_t *node = heap_caps_calloc(1, sizeof(twai_onchip_ctx_t), TWAI_MALLOC_CAPS);
|
||||
ESP_RETURN_ON_FALSE(node, ESP_ERR_NO_MEM, TAG, "No mem");
|
||||
// Acquire controller
|
||||
int ctrlr_id = _ctrlr_acquire(node);
|
||||
ESP_GOTO_ON_FALSE(ctrlr_id != -1, ESP_ERR_NOT_FOUND, ctrlr_err, TAG, "Controller not available");
|
||||
node->ctrlr_id = ctrlr_id;
|
||||
|
||||
// state is in bus_off before enabled
|
||||
atomic_store(&node->state, TWAI_ERROR_BUS_OFF);
|
||||
node->tx_mount_queue = xQueueCreateWithCaps(node_config->tx_queue_depth, sizeof(twai_frame_t *), TWAI_MALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(node->tx_mount_queue, ESP_ERR_NO_MEM, create_err, TAG, "no_mem");
|
||||
uint32_t intr_flags = node_config->intr_priority ? BIT(node_config->intr_priority) | ESP_INTR_FLAG_INTRDISABLED : ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_INTRDISABLED;
|
||||
#if CONFIG_TWAI_ISR_CACHE_SAFE
|
||||
intr_flags |= ESP_INTR_FLAG_IRAM;
|
||||
#endif
|
||||
ESP_GOTO_ON_ERROR(esp_intr_alloc(twai_controller_periph_signals.controllers[ctrlr_id].irq_id, intr_flags, _node_isr_main, (void *)node, &node->intr_hdl),
|
||||
create_err, TAG, "Alloc interrupt failed");
|
||||
|
||||
// Enable bus clock and reset controller
|
||||
_twai_rcc_clock_ctrl(ctrlr_id, true);
|
||||
// Initialize HAL and configure register defaults.
|
||||
twai_hal_config_t hal_config = {
|
||||
.controller_id = node->ctrlr_id,
|
||||
.intr_mask = DRIVER_DEFAULT_INTERRUPTS,
|
||||
.enable_listen_only = node_config->flags.enable_listen_only,
|
||||
};
|
||||
ESP_GOTO_ON_FALSE(twai_hal_init(&node->hal, &hal_config), ESP_ERR_INVALID_STATE, config_err, TAG, "hardware not in reset state");
|
||||
twaifd_ll_set_mode(node->hal.dev, node_config->flags.enable_listen_only, node_config->flags.enable_self_test, node_config->flags.enable_loopback);
|
||||
twaifd_ll_set_tx_retrans_limit(node->hal.dev, node_config->fail_retry_cnt);
|
||||
twaifd_ll_filter_block_rtr(node->hal.dev, node_config->flags.no_receive_rtr);
|
||||
twaifd_ll_enable_filter_mode(node->hal.dev, true); // each filter still has independent enable control
|
||||
twaifd_ll_enable_fd_mode(node->hal.dev, true); // fd frame still controlled by `header.fdf`
|
||||
|
||||
// Configure bus timing
|
||||
ESP_GOTO_ON_ERROR(_node_calc_set_bit_timing(&node->api_base, node_config->clk_src, &node_config->bit_timing, &node_config->data_timing), config_err, TAG, "bitrate error");
|
||||
|
||||
// Configure GPIO
|
||||
ESP_GOTO_ON_ERROR(_node_config_io(node, node_config), config_err, TAG, "gpio config failed");
|
||||
#if CONFIG_PM_ENABLE
|
||||
ESP_GOTO_ON_ERROR(esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, twai_controller_periph_signals.controllers[ctrlr_id].module_name, &node->pm_lock), config_err, TAG, "init power manager failed");
|
||||
#endif //CONFIG_PM_ENABLE
|
||||
|
||||
node->api_base.enable = _node_enable;
|
||||
node->api_base.disable = _node_disable;
|
||||
node->api_base.del = _node_delete;
|
||||
node->api_base.recover = _node_recover;
|
||||
node->api_base.config_mask_filter = _node_config_mask_filter;
|
||||
node->api_base.config_range_filter = _node_config_range_filter;
|
||||
node->api_base.timing_reconfig = _node_set_bit_timing;
|
||||
node->api_base.register_cbs = _node_register_callbacks;
|
||||
node->api_base.transmit = _node_queue_tx;
|
||||
node->api_base.receive_isr = _node_parse_rx;
|
||||
node->api_base.get_info = _node_get_status;
|
||||
|
||||
*node_ret = &node->api_base;
|
||||
return ESP_OK;
|
||||
|
||||
config_err:
|
||||
if (node->intr_hdl) {
|
||||
esp_intr_free(node->intr_hdl);
|
||||
}
|
||||
if (node->tx_mount_queue) {
|
||||
vQueueDeleteWithCaps(node->tx_mount_queue);
|
||||
}
|
||||
create_err:
|
||||
_ctrlr_release(ctrlr_id);
|
||||
ctrlr_err:
|
||||
free(node);
|
||||
return ret;
|
||||
}
|
58
components/esp_driver_twai/onchip/include/esp_twai_onchip.h
Normal file
58
components/esp_driver_twai/onchip/include/esp_twai_onchip.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_twai_types.h"
|
||||
#include "hal/gpio_types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief TWAI on-chip node initialization configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
struct {
|
||||
gpio_num_t tx; /**< GPIO pin for twai TX */
|
||||
gpio_num_t rx; /**< GPIO pin for twai RX */
|
||||
gpio_num_t quanta_clk_out; /**< GPIO pin for quanta clock output, Set -1 to not use */
|
||||
gpio_num_t bus_off_indicator; /**< GPIO pin for bus-off indicator, Set -1 to not use */
|
||||
} io_cfg; /**< I/O configuration */
|
||||
twai_clock_source_t clk_src; /**< Optional, clock source, remain 0 to using TWAI_CLK_SRC_DEFAULT by default */
|
||||
twai_timing_basic_config_t bit_timing; /**< Timing configuration for classic twai and FD arbitration stage */
|
||||
twai_timing_basic_config_t data_timing;/**< Optional, timing configuration for FD data stage */
|
||||
int8_t fail_retry_cnt; /**< Hardware retry limit if failed, range [-1:15], -1 for re-trans forever */
|
||||
uint32_t tx_queue_depth; /**< Depth of the transmit queue */
|
||||
int intr_priority; /**< Interrupt priority, [0:3] */
|
||||
struct {
|
||||
uint32_t enable_self_test: 1; /**< Transmission does not require acknowledgment. Use this mode for self testing */
|
||||
uint32_t enable_loopback: 1; /**< The TWAI controller receive back frames what it send out */
|
||||
uint32_t enable_listen_only: 1; /**< The TWAI controller will not influence the bus (No transmissions or acknowledgments) but can receive messages */
|
||||
uint32_t no_receive_rtr: 1; /**< Don't receive remote frames */
|
||||
} flags;
|
||||
} twai_onchip_node_config_t;
|
||||
|
||||
/**
|
||||
* @brief Allocate a TWAI hardware node by specific init config structure
|
||||
* To delete/free the TWAI, call `twai_node_delete()`
|
||||
*
|
||||
* @param[in] node_config Init config structure
|
||||
* @param[out] node_ret Return driver handle
|
||||
*
|
||||
* @return ESP_OK Allocate success
|
||||
* ESP_ERR_NO_MEM No enough free memory
|
||||
* ESP_ERR_NOT_FOUND No free hardware controller
|
||||
* ESP_ERR_INVALID_ARG Config argument not available
|
||||
* ESP_ERR_INVALID_STATE State error, including hardware state error and driver state error
|
||||
* ESP_FAIL Other reasons
|
||||
*/
|
||||
esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twai_node_handle_t *node_ret);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -26,6 +26,14 @@ extern "C" {
|
||||
|
||||
#define TWAIFD_LL_GET_HW(num) (((num) == 0) ? (&TWAI0) : (&TWAI1))
|
||||
|
||||
#define TWAI_LL_TSEG1_MIN 0
|
||||
#define TWAI_LL_TSEG2_MIN 1
|
||||
#define TWAI_LL_TSEG1_MAX TWAIFD_PH1
|
||||
#define TWAI_LL_TSEG2_MAX TWAIFD_PH2
|
||||
#define TWAI_LL_SJW_MAX TWAIFD_SJW
|
||||
|
||||
#define TWAIFD_IDENTIFIER_BASE_S 18 // Start bit of std_id in IDENTIFIER_W of TX buffer or RX buffer
|
||||
|
||||
#define TWAIFD_LL_ERR_BIT_ERR 0x0 // Bit Error
|
||||
#define TWAIFD_LL_ERR_CRC_ERR 0x1 // CRC Error
|
||||
#define TWAIFD_LL_ERR_FRM_ERR 0x2 // Form Error
|
||||
@@ -44,7 +52,8 @@ extern "C" {
|
||||
#define TWAIFD_LL_HW_CMD_RST_RX_CNT TWAIFD_RXFCRST // Clear RX bus traffic counter
|
||||
#define TWAIFD_LL_HW_CMD_RST_TX_CNT TWAIFD_TXFCRST // Clear TX bus traffic counter
|
||||
|
||||
#define TWAIFD_LL_INTR_TX_DONE TWAIFD_TXI_INT_ST // Transmit Interrupt
|
||||
#define TWAIFD_LL_INTR_TX_DONE TWAIFD_TXBHCI_INT_ST// Transmit finish (ok or error)
|
||||
#define TWAIFD_LL_INTR_TX_SUCCESS TWAIFD_TXI_INT_ST // Transmit success without error
|
||||
#define TWAIFD_LL_INTR_RX_NOT_EMPTY TWAIFD_RBNEI_INT_ST // RX buffer not empty interrupt
|
||||
#define TWAIFD_LL_INTR_RX_FULL TWAIFD_RXFI_INT_ST // RX buffer full interrupt
|
||||
#define TWAIFD_LL_INTR_ERR_WARN TWAIFD_EWLI_INT_ST // Error Interrupt
|
||||
@@ -72,7 +81,7 @@ static inline void twaifd_ll_enable_bus_clock(uint8_t twai_id, bool enable)
|
||||
static inline void twaifd_ll_reset_register(uint8_t twai_id)
|
||||
{
|
||||
PCR.twai[twai_id].twai_conf.twai_rst_en = 1;
|
||||
while (!PCR.twai[twai_id].twai_conf.twai_ready);
|
||||
PCR.twai[twai_id].twai_conf.twai_rst_en = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,6 +104,9 @@ static inline void twaifd_ll_set_clock_source(uint8_t twai_id, twai_clock_source
|
||||
static inline void twaifd_ll_enable_clock(uint8_t twai_id, bool enable)
|
||||
{
|
||||
PCR.twai[twai_id].twai_func_clk_conf.twai_func_clk_en = enable;
|
||||
if (enable) {
|
||||
while (!PCR.twai[twai_id].twai_conf.twai_ready);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -138,14 +150,15 @@ static inline void twaifd_ll_enable_hw(twaifd_dev_t *hw, bool enable)
|
||||
* @param hw Start address of the TWAI registers
|
||||
* @param modes Operating mode
|
||||
*/
|
||||
static inline void twaifd_ll_set_mode(twaifd_dev_t *hw, const twai_mode_t modes)
|
||||
static inline void twaifd_ll_set_mode(twaifd_dev_t *hw, bool listen_only, bool no_ack, bool loopback)
|
||||
{
|
||||
//mode should be changed under disabled
|
||||
HAL_ASSERT(hw->mode_settings.ena == 0);
|
||||
|
||||
twaifd_mode_settings_reg_t opmode = {.val = hw->mode_settings.val};
|
||||
opmode.stm = (modes == TWAI_MODE_NO_ACK);
|
||||
opmode.bmm = (modes == TWAI_MODE_LISTEN_ONLY);
|
||||
opmode.stm = no_ack;
|
||||
opmode.bmm = listen_only;
|
||||
opmode.ilbp = loopback;
|
||||
|
||||
hw->mode_settings.val = opmode.val;
|
||||
}
|
||||
@@ -172,17 +185,6 @@ static inline void twaifd_ll_enable_fd_mode(twaifd_dev_t *hw, bool ena)
|
||||
hw->mode_settings.fde = ena;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable or disable TX loopback
|
||||
*
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @param ena Set to true to enable loopback, false to disable.
|
||||
*/
|
||||
static inline void twaifd_ll_enable_loopback(twaifd_dev_t *hw, bool ena)
|
||||
{
|
||||
hw->mode_settings.ilbp = ena;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable or disable the RX fifo automatic increase when read to register
|
||||
*
|
||||
@@ -213,21 +215,11 @@ static inline void twaifd_ll_enable_filter_mode(twaifd_dev_t* hw, bool enable)
|
||||
* @param hw Pointer to hardware structure.
|
||||
* @param en True to drop, false to Receive to next filter
|
||||
*/
|
||||
static inline void twaifd_ll_filter_drop_remote_frame(twaifd_dev_t* hw, bool en)
|
||||
static inline void twaifd_ll_filter_block_rtr(twaifd_dev_t* hw, bool en)
|
||||
{
|
||||
hw->mode_settings.fdrf = en;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get remote frame filtering behaviour.
|
||||
*
|
||||
* @param hw Pointer to hardware structure.
|
||||
*/
|
||||
static inline bool twaifd_ll_filter_is_drop_remote_frame(twaifd_dev_t* hw)
|
||||
{
|
||||
return hw->mode_settings.fdrf;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable or disable the time-triggered transmission mode for the TWAI-FD peripheral.
|
||||
*
|
||||
@@ -272,6 +264,7 @@ static inline void twaifd_ll_enable_intr(twaifd_dev_t *hw, uint32_t intr_mask)
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @return The current interrupt status as a 32-bit value, used with `TWAIFD_LL_INTR_`.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t twaifd_ll_get_intr_status(twaifd_dev_t *hw)
|
||||
{
|
||||
return hw->int_stat.val;
|
||||
@@ -283,6 +276,7 @@ static inline uint32_t twaifd_ll_get_intr_status(twaifd_dev_t *hw)
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @param intr_mask The interrupt mask specifying which interrupts to clear.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void twaifd_ll_clr_intr_status(twaifd_dev_t *hw, uint32_t intr_mask)
|
||||
{
|
||||
// this register is write to clear
|
||||
@@ -296,7 +290,7 @@ static inline void twaifd_ll_clr_intr_status(twaifd_dev_t *hw, uint32_t intr_mas
|
||||
* @param hw Start address of the TWAI registers
|
||||
* @param timing_param timing params
|
||||
*/
|
||||
static inline void twaifd_ll_set_nominal_bit_rate(twaifd_dev_t *hw, const twai_timing_config_t *timing_param)
|
||||
static inline void twaifd_ll_set_nominal_bitrate(twaifd_dev_t *hw, const twai_timing_advanced_config_t *timing_param)
|
||||
{
|
||||
twaifd_btr_reg_t reg_w = {.val = 0};
|
||||
reg_w.brp = timing_param->brp;
|
||||
@@ -314,7 +308,7 @@ static inline void twaifd_ll_set_nominal_bit_rate(twaifd_dev_t *hw, const twai_t
|
||||
* @param hw Start address of the TWAI registers
|
||||
* @param timing_param_fd FD timing params
|
||||
*/
|
||||
static inline void twaifd_ll_set_fd_bit_rate(twaifd_dev_t *hw, const twai_timing_config_t *timing_param_fd)
|
||||
static inline void twaifd_ll_set_fd_bitrate(twaifd_dev_t *hw, const twai_timing_advanced_config_t *timing_param_fd)
|
||||
{
|
||||
twaifd_btr_fd_reg_t reg_w = {.val = 0};
|
||||
reg_w.brp_fd = timing_param_fd->brp;
|
||||
@@ -331,7 +325,7 @@ static inline void twaifd_ll_set_fd_bit_rate(twaifd_dev_t *hw, const twai_timing
|
||||
*
|
||||
* @param hw Start address of the TWAI registers
|
||||
* @param ssp_src_code Secondary point mode config, see TWAIFD_LL_SSP_SRC_xxx.
|
||||
* @param offset_val Secondary point offset based on Sync_Seg, in clock source freq.
|
||||
* @param offset_val Secondary point offset based on Sync_Seg, in time quanta.
|
||||
*/
|
||||
static inline void twaifd_ll_config_secondary_sample_point(twaifd_dev_t *hw, uint8_t ssp_src_code, uint8_t offset_val)
|
||||
{
|
||||
@@ -463,15 +457,18 @@ static inline uint32_t twaifd_ll_get_tec(twaifd_dev_t *hw)
|
||||
*
|
||||
* @param hw Pointer to the TWAI FD hardware instance
|
||||
* @param filter_id The unique ID of the filter to configure
|
||||
* @param is_range Setting for range filter or mask filter
|
||||
* @param en True to receive, False to drop
|
||||
*/
|
||||
static inline void twaifd_ll_filter_enable_basic_std(twaifd_dev_t* hw, uint8_t filter_id, bool en)
|
||||
static inline void twaifd_ll_filter_enable_basic_std(twaifd_dev_t* hw, uint8_t filter_id, bool is_range, bool en)
|
||||
{
|
||||
HAL_ASSERT(filter_id < (SOC_TWAI_MASK_FILTER_NUM + SOC_TWAI_RANGE_FILTER_NUM));
|
||||
HAL_ASSERT(filter_id < (is_range ? SOC_TWAI_RANGE_FILTER_NUM : SOC_TWAI_MASK_FILTER_NUM));
|
||||
// The hw_filter_id of range_filter is indexed after mask_filter
|
||||
uint8_t hw_filter_id = is_range ? filter_id + SOC_TWAI_MASK_FILTER_NUM : filter_id;
|
||||
if (en) {
|
||||
hw->filter_control_filter_status.val |= TWAIFD_FANB << (filter_id * TWAIFD_FBNB_S);
|
||||
hw->filter_control_filter_status.val |= TWAIFD_FANB << (hw_filter_id * TWAIFD_FBNB_S);
|
||||
} else {
|
||||
hw->filter_control_filter_status.val &= ~(TWAIFD_FANB << (filter_id * TWAIFD_FBNB_S));
|
||||
hw->filter_control_filter_status.val &= ~(TWAIFD_FANB << (hw_filter_id * TWAIFD_FBNB_S));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -480,15 +477,18 @@ static inline void twaifd_ll_filter_enable_basic_std(twaifd_dev_t* hw, uint8_t f
|
||||
*
|
||||
* @param hw Pointer to the TWAI FD hardware instance
|
||||
* @param filter_id The unique ID of the filter to configure
|
||||
* @param is_range Setting for range filter or mask filter
|
||||
* @param en True to receive, False to drop
|
||||
*/
|
||||
static inline void twaifd_ll_filter_enable_basic_ext(twaifd_dev_t* hw, uint8_t filter_id, bool en)
|
||||
static inline void twaifd_ll_filter_enable_basic_ext(twaifd_dev_t* hw, uint8_t filter_id, bool is_range, bool en)
|
||||
{
|
||||
HAL_ASSERT(filter_id < (SOC_TWAI_MASK_FILTER_NUM + SOC_TWAI_RANGE_FILTER_NUM));
|
||||
HAL_ASSERT(filter_id < (is_range ? SOC_TWAI_RANGE_FILTER_NUM : SOC_TWAI_MASK_FILTER_NUM));
|
||||
// The hw_filter_id of range_filter is indexed after mask_filter
|
||||
uint8_t hw_filter_id = is_range ? filter_id + SOC_TWAI_MASK_FILTER_NUM : filter_id;
|
||||
if (en) {
|
||||
hw->filter_control_filter_status.val |= TWAIFD_FANE << (filter_id * TWAIFD_FBNB_S);
|
||||
hw->filter_control_filter_status.val |= TWAIFD_FANE << (hw_filter_id * TWAIFD_FBNB_S);
|
||||
} else {
|
||||
hw->filter_control_filter_status.val &= ~(TWAIFD_FANE << (filter_id * TWAIFD_FBNB_S));
|
||||
hw->filter_control_filter_status.val &= ~(TWAIFD_FANE << (hw_filter_id * TWAIFD_FBNB_S));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -497,15 +497,18 @@ static inline void twaifd_ll_filter_enable_basic_ext(twaifd_dev_t* hw, uint8_t f
|
||||
*
|
||||
* @param hw Pointer to the TWAI FD hardware instance
|
||||
* @param filter_id The unique ID of the filter to configure
|
||||
* @param is_range Setting for range filter or mask filter
|
||||
* @param en True to receive, False to drop
|
||||
*/
|
||||
static inline void twaifd_ll_filter_enable_fd_std(twaifd_dev_t* hw, uint8_t filter_id, bool en)
|
||||
static inline void twaifd_ll_filter_enable_fd_std(twaifd_dev_t* hw, uint8_t filter_id, bool is_range, bool en)
|
||||
{
|
||||
HAL_ASSERT(filter_id < (SOC_TWAI_MASK_FILTER_NUM + SOC_TWAI_RANGE_FILTER_NUM));
|
||||
HAL_ASSERT(filter_id < (is_range ? SOC_TWAI_RANGE_FILTER_NUM : SOC_TWAI_MASK_FILTER_NUM));
|
||||
// The hw_filter_id of range_filter is indexed after mask_filter
|
||||
uint8_t hw_filter_id = is_range ? filter_id + SOC_TWAI_MASK_FILTER_NUM : filter_id;
|
||||
if (en) {
|
||||
hw->filter_control_filter_status.val |= TWAIFD_FAFB << (filter_id * TWAIFD_FBNB_S);
|
||||
hw->filter_control_filter_status.val |= TWAIFD_FAFB << (hw_filter_id * TWAIFD_FBNB_S);
|
||||
} else {
|
||||
hw->filter_control_filter_status.val &= ~(TWAIFD_FAFB << (filter_id * TWAIFD_FBNB_S));
|
||||
hw->filter_control_filter_status.val &= ~(TWAIFD_FAFB << (hw_filter_id * TWAIFD_FBNB_S));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -514,15 +517,18 @@ static inline void twaifd_ll_filter_enable_fd_std(twaifd_dev_t* hw, uint8_t filt
|
||||
*
|
||||
* @param hw Pointer to the TWAI FD hardware instance
|
||||
* @param filter_id The unique ID of the filter to configure
|
||||
* @param is_range Setting for range filter or mask filter
|
||||
* @param en True to receive, False to drop
|
||||
*/
|
||||
static inline void twaifd_ll_filter_enable_fd_ext(twaifd_dev_t* hw, uint8_t filter_id, bool en)
|
||||
static inline void twaifd_ll_filter_enable_fd_ext(twaifd_dev_t* hw, uint8_t filter_id, bool is_range, bool en)
|
||||
{
|
||||
HAL_ASSERT(filter_id < (SOC_TWAI_MASK_FILTER_NUM + SOC_TWAI_RANGE_FILTER_NUM));
|
||||
HAL_ASSERT(filter_id < (is_range ? SOC_TWAI_RANGE_FILTER_NUM : SOC_TWAI_MASK_FILTER_NUM));
|
||||
// The hw_filter_id of range_filter is indexed after mask_filter
|
||||
uint8_t hw_filter_id = is_range ? filter_id + SOC_TWAI_MASK_FILTER_NUM : filter_id;
|
||||
if (en) {
|
||||
hw->filter_control_filter_status.val |= TWAIFD_FAFE << (filter_id * TWAIFD_FBNB_S);
|
||||
hw->filter_control_filter_status.val |= TWAIFD_FAFE << (hw_filter_id * TWAIFD_FBNB_S);
|
||||
} else {
|
||||
hw->filter_control_filter_status.val &= ~(TWAIFD_FAFE << (filter_id * TWAIFD_FBNB_S));
|
||||
hw->filter_control_filter_status.val &= ~(TWAIFD_FAFE << (hw_filter_id * TWAIFD_FBNB_S));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,48 +536,30 @@ static inline void twaifd_ll_filter_enable_fd_ext(twaifd_dev_t* hw, uint8_t filt
|
||||
* @brief Set Bit Acceptance Filter
|
||||
* @param hw Start address of the TWAI registers
|
||||
* @param filter_id Filter number id
|
||||
* @param is_ext Filter for ext_id or std_id
|
||||
* @param code Acceptance Code
|
||||
* @param mask Acceptance Mask
|
||||
*/
|
||||
static inline void twaifd_ll_filter_set_id_mask(twaifd_dev_t* hw, uint8_t filter_id, uint32_t code, uint32_t mask)
|
||||
static inline void twaifd_ll_filter_set_id_mask(twaifd_dev_t* hw, uint8_t filter_id, bool is_ext, uint32_t code, uint32_t mask)
|
||||
{
|
||||
hw->mask_filters[filter_id].filter_mask.bit_mask_val = mask;
|
||||
hw->mask_filters[filter_id].filter_val.bit_val = code;
|
||||
hw->mask_filters[filter_id].filter_mask.bit_mask_val = is_ext ? mask : (mask << TWAIFD_IDENTIFIER_BASE_S);
|
||||
hw->mask_filters[filter_id].filter_val.bit_val = is_ext ? code : (code << TWAIFD_IDENTIFIER_BASE_S);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set Range Acceptance Filter
|
||||
* @param hw Start address of the TWAI registers
|
||||
* @param filter_id Filter number id
|
||||
* @param is_ext Filter for ext_id or std_id
|
||||
* @param high The id range high limit
|
||||
* @param low The id range low limit
|
||||
*/
|
||||
static inline void twaifd_ll_filter_set_range(twaifd_dev_t* hw, uint8_t filter_id, uint32_t high, uint32_t low)
|
||||
static inline void twaifd_ll_filter_set_range(twaifd_dev_t* hw, uint8_t filter_id, bool is_ext, uint32_t high, uint32_t low)
|
||||
{
|
||||
hw->range_filters[filter_id].ran_low.bit_ran_low_val = low;
|
||||
hw->range_filters[filter_id].ran_high.bit_ran_high_val = high;
|
||||
hw->range_filters[filter_id].ran_low.bit_ran_low_val = is_ext ? low : (low << TWAIFD_IDENTIFIER_BASE_S);
|
||||
hw->range_filters[filter_id].ran_high.bit_ran_high_val = is_ext ? high : (high << TWAIFD_IDENTIFIER_BASE_S);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable or disable bit or range frame filtering for a specific filter.
|
||||
*
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @param filter_id The ID of the filter to configure (0-2 for bit filter, 3 for range filter).
|
||||
* @param enable True to enable the filter, false to disable.
|
||||
*/
|
||||
static inline void twaifd_ll_filter_enable(twaifd_dev_t* hw, uint8_t filter_id, bool enable)
|
||||
{
|
||||
HAL_ASSERT(filter_id < (SOC_TWAI_MASK_FILTER_NUM + SOC_TWAI_RANGE_FILTER_NUM));
|
||||
twaifd_filter_control_filter_status_reg_t reg_val = {.val = hw->filter_control_filter_status.val};
|
||||
|
||||
// enable or disable filter selection
|
||||
if (enable) {
|
||||
reg_val.val |= BIT(filter_id + TWAIFD_SFA_S);
|
||||
} else {
|
||||
reg_val.val &= ~BIT(filter_id + TWAIFD_SFA_S);
|
||||
}
|
||||
hw->filter_control_filter_status.val = reg_val.val;
|
||||
}
|
||||
/* ------------------------- TX Buffer Registers ------------------------- */
|
||||
|
||||
/**
|
||||
@@ -610,6 +598,7 @@ static inline uint32_t twaifd_ll_get_tx_buffer_status(twaifd_dev_t *hw, uint8_t
|
||||
* @param buffer_idx
|
||||
* @param cmd The command want to set, see `TWAIFD_LL_TX_CMD_`
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void twaifd_ll_set_tx_cmd(twaifd_dev_t *hw, uint8_t buffer_idx, uint32_t cmd)
|
||||
{
|
||||
hw->tx_command_txtb_info.val = (cmd | BIT(buffer_idx + TWAIFD_TXB1_S));
|
||||
@@ -640,6 +629,7 @@ static inline void twaifd_ll_set_tx_buffer_priority(twaifd_dev_t *hw, uint8_t bu
|
||||
*
|
||||
* @note Call twaifd_ll_format_frame_header() and twaifd_ll_format_frame_data() to format a frame
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void twaifd_ll_mount_tx_buffer(twaifd_dev_t *hw, twaifd_frame_buffer_t *tx_frame, uint8_t buffer_idx)
|
||||
{
|
||||
//Copy formatted frame into TX buffer
|
||||
@@ -667,6 +657,7 @@ static inline uint32_t twaifd_ll_get_rx_buffer_size(twaifd_dev_t *hw)
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @return Number of frames in the RX buffer.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t twaifd_ll_get_rx_frame_count(twaifd_dev_t *hw)
|
||||
{
|
||||
return hw->rx_status_rx_settings.rxfrc;
|
||||
@@ -691,6 +682,7 @@ static inline uint32_t twaifd_ll_is_rx_buffer_empty(twaifd_dev_t *hw)
|
||||
*
|
||||
* @note Call twaifd_ll_parse_frame_header() and twaifd_ll_parse_frame_data() to parse the formatted frame
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void twaifd_ll_get_rx_frame(twaifd_dev_t *hw, twaifd_frame_buffer_t *rx_frame)
|
||||
{
|
||||
// If rx_automatic_mode enabled, hw->rx_data.rx_data should 32bit access
|
||||
@@ -713,6 +705,7 @@ static inline void twaifd_ll_get_rx_frame(twaifd_dev_t *hw, twaifd_frame_buffer_
|
||||
* @param[in] final_dlc data length code of frame.
|
||||
* @param[out] tx_frame Pointer to store formatted frame
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void twaifd_ll_format_frame_header(const twai_frame_header_t *header, uint8_t final_dlc, twaifd_frame_buffer_t *tx_frame)
|
||||
{
|
||||
HAL_ASSERT(final_dlc <= TWAIFD_FRAME_MAX_DLC);
|
||||
@@ -745,6 +738,7 @@ static inline void twaifd_ll_format_frame_header(const twai_frame_header_t *head
|
||||
* @param[in] len data length of data buffer.
|
||||
* @param[out] tx_frame Pointer to store formatted frame
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void twaifd_ll_format_frame_data(const uint8_t *buffer, uint32_t len, twaifd_frame_buffer_t *tx_frame)
|
||||
{
|
||||
HAL_ASSERT(len <= TWAIFD_FRAME_MAX_LEN);
|
||||
@@ -757,6 +751,7 @@ static inline void twaifd_ll_format_frame_data(const uint8_t *buffer, uint32_t l
|
||||
* @param[in] rx_frame Pointer to formatted frame
|
||||
* @param[out] p_frame_header Including DLC, ID, Format, etc.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void twaifd_ll_parse_frame_header(const twaifd_frame_buffer_t *rx_frame, twai_frame_header_t *p_frame_header)
|
||||
{
|
||||
//Copy frame information
|
||||
@@ -786,6 +781,7 @@ static inline void twaifd_ll_parse_frame_header(const twaifd_frame_buffer_t *rx_
|
||||
* @param[out] buffer Pointer to an 8 byte array to save data
|
||||
* @param[in] buffer_len_limit The buffer length limit, If less then frame data length, over length data will dropped
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void twaifd_ll_parse_frame_data(const twaifd_frame_buffer_t *rx_frame, uint8_t *buffer, int len_limit)
|
||||
{
|
||||
memcpy(buffer, rx_frame->data, len_limit);
|
||||
|
@@ -69,6 +69,7 @@ typedef struct {
|
||||
twai_soc_handle_t dev; // TWAI SOC layer handle (i.e. register base address)
|
||||
uint32_t state_flags;
|
||||
uint32_t clock_source_hz;
|
||||
bool enable_listen_only;
|
||||
#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;
|
||||
@@ -81,6 +82,8 @@ typedef struct {
|
||||
typedef struct {
|
||||
int controller_id;
|
||||
uint32_t clock_source_hz;
|
||||
uint32_t intr_mask;
|
||||
bool enable_listen_only;
|
||||
} twai_hal_config_t;
|
||||
|
||||
/**
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -20,8 +20,8 @@ extern "C" {
|
||||
#define TWAI_EXT_ID_MASK 0x1FFFFFFFU /* Mask of the ID fields in an extended frame */
|
||||
|
||||
/* TWAI payload length and DLC definitions */
|
||||
#define TWAI_FRAME_MAX_DLC 8
|
||||
#define TWAI_FRAME_MAX_LEN 8
|
||||
#define TWAI_FRAME_MAX_DLC 8
|
||||
#define TWAI_FRAME_MAX_LEN 8
|
||||
|
||||
/* TWAI FD payload length and DLC definitions */
|
||||
#define TWAIFD_FRAME_MAX_DLC 15
|
||||
@@ -31,21 +31,12 @@ extern "C" {
|
||||
* @brief TWAI error states
|
||||
*/
|
||||
typedef enum {
|
||||
TWAI_ERROR_ACTIVE, /**< Error active state: TEC/REC < 96 */
|
||||
TWAI_ERROR_WARNING, /**< Error warning state: TEC/REC >= 96 and < 128 */
|
||||
TWAI_ERROR_PASSIVE, /**< Error passive state: TEC/REC >= 128 and < 256 */
|
||||
TWAI_ERROR_BUS_OFF, /**< Bus-off state: TEC >= 256 (node disconnected from bus) */
|
||||
TWAI_ERROR_ACTIVE, /**< Error active state: TEC/REC < 96 */
|
||||
TWAI_ERROR_WARNING, /**< Error warning state: TEC/REC >= 96 and < 128 */
|
||||
TWAI_ERROR_PASSIVE, /**< Error passive state: TEC/REC >= 128 and < 256 */
|
||||
TWAI_ERROR_BUS_OFF, /**< Bus-off state: TEC >= 256 (node offline) */
|
||||
} twai_error_state_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI Controller operating modes
|
||||
*/
|
||||
typedef enum {
|
||||
TWAI_MODE_NORMAL, /**< Normal operating mode where TWAI controller can send/receive/acknowledge messages */
|
||||
TWAI_MODE_NO_ACK, /**< Transmission does not require acknowledgment. Use this mode for self testing */
|
||||
TWAI_MODE_LISTEN_ONLY, /**< The TWAI controller will not influence the bus (No transmissions or acknowledgments) but can receive messages */
|
||||
} twai_mode_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI group clock source
|
||||
* @note User should select the clock source based on the power and resolution requirement
|
||||
@@ -57,7 +48,7 @@ typedef int twai_clock_source_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief TWAI baud rate timing config advanced mode
|
||||
* @brief TWAI bitrate timing config advanced mode
|
||||
* @note Setting one of `quanta_resolution_hz` and `brp` is enough, otherwise, `brp` is not used.
|
||||
*/
|
||||
typedef struct {
|
||||
@@ -68,35 +59,33 @@ typedef struct {
|
||||
uint8_t tseg_1; /**< Seg_1 length, in quanta time */
|
||||
uint8_t tseg_2; /**< Seg_2 length, in quanta time */
|
||||
uint8_t sjw; /**< Sync jump width, in quanta time */
|
||||
union {
|
||||
bool en_multi_samp; /**< Enable multi-sampling for one bit to avoid noise and detect errors */
|
||||
bool triple_sampling; /**< Deprecated, in favor of `en_multi_samp` */
|
||||
};
|
||||
uint8_t ssp_offset; /**< Secondary sample point offset refet to Sync seg, in quanta time, set 0 to disable ssp */
|
||||
bool triple_sampling; /**< Deprecated, in favor of `ssp_offset` */
|
||||
} twai_timing_config_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI bitrate timing config advanced mode for esp_driver_twai
|
||||
* @note `quanta_resolution_hz` is not supported in this driver
|
||||
*/
|
||||
typedef twai_timing_config_t twai_timing_advanced_config_t;
|
||||
|
||||
/**
|
||||
* @brief TWAI frame header/format struct type
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
uint32_t ide:1; /**< Extended Frame Format (29bit ID) */
|
||||
uint32_t rtr:1; /**< Message is a Remote Frame */
|
||||
uint32_t fdf:1; /**< TWAI 2.0: Reserved, FD: FD Frames. */
|
||||
uint32_t brs:1; /**< TWAI 2.0: Reserved, FD: Bit Rate Shift. */
|
||||
uint32_t esi:1; /**< Transmit side error indicator for received frame */
|
||||
uint32_t loopback:1; /**< Temporary transmit as loop back for this trans, if setting `TWAI_MODE_LOOP_BACK`, all transmit is loop back */
|
||||
int8_t retrans_count; /**< Re-trans count on transfer fail, -1: infinite, 0: no re-trans, others: re-trans times. */
|
||||
uint32_t reserved:18; /**< Reserved */
|
||||
};
|
||||
uint32_t format_val; /**< Frame format/type integrate value */
|
||||
uint32_t id; /**< message arbitration identification */
|
||||
uint16_t dlc; /**< message data length code */
|
||||
struct {
|
||||
uint32_t ide: 1; /**< Extended Frame Format (29bit ID) */
|
||||
uint32_t rtr: 1; /**< Message is a Remote Frame */
|
||||
uint32_t fdf: 1; /**< Message is FD format, allow max 64 byte of data */
|
||||
uint32_t brs: 1; /**< Transmit message with Bit Rate Shift. */
|
||||
uint32_t esi: 1; /**< Transmit side error indicator for received frame */
|
||||
};
|
||||
union {
|
||||
uint64_t timestamp; /**< Timestamp for received message */
|
||||
uint64_t trigger_time; /**< Trigger time for transmitting message*/
|
||||
};
|
||||
uint32_t id; /**< message arbitration identification */
|
||||
uint8_t dlc; /**< message data length code */
|
||||
} twai_frame_header_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@@ -51,34 +51,34 @@ extern "C" {
|
||||
* @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() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 20000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_5KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 100000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_10KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 200000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_1KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 20000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#define TWAI_TIMING_CONFIG_5KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 100000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#define TWAI_TIMING_CONFIG_10KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 200000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .ssp_offset = 0, .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() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 312500, .brp = 0, .prop_seg = 0, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_16KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 400000, .brp = 0, .prop_seg = 0, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_20KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 400000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_12_5KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 312500, .brp = 0, .prop_seg = 0, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#define TWAI_TIMING_CONFIG_16KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 400000, .brp = 0, .prop_seg = 0, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#define TWAI_TIMING_CONFIG_20KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 400000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#endif // (SOC_TWAI_BRP_MAX > 128) || (CONFIG_ESP32_REV_MIN_FULL >= 200)
|
||||
|
||||
#if SOC_TWAI_CLK_SUPPORT_XTAL
|
||||
#define TWAI_TIMING_CONFIG_25KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 500000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_25KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 500000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#else // APB80M
|
||||
#define TWAI_TIMING_CONFIG_25KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 625000, .brp = 0, .prop_seg = 0, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_25KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 625000, .brp = 0, .prop_seg = 0, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#endif
|
||||
#define TWAI_TIMING_CONFIG_50KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 1000000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_100KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 2000000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_125KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 2000000, .brp = 0, .prop_seg = 0, .tseg_1 = 11, .tseg_2 = 4, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_250KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 4000000, .brp = 0, .prop_seg = 0, .tseg_1 = 11, .tseg_2 = 4, .sjw = 2, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_50KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 1000000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#define TWAI_TIMING_CONFIG_100KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 2000000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#define TWAI_TIMING_CONFIG_125KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 2000000, .brp = 0, .prop_seg = 0, .tseg_1 = 11, .tseg_2 = 4, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#define TWAI_TIMING_CONFIG_250KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 4000000, .brp = 0, .prop_seg = 0, .tseg_1 = 11, .tseg_2 = 4, .sjw = 2, .ssp_offset = 0, .triple_sampling = false}
|
||||
#if SOC_TWAI_CLK_SUPPORT_XTAL && CONFIG_XTAL_FREQ == 40 // TWAI_CLK_SRC_XTAL = 40M
|
||||
#define TWAI_TIMING_CONFIG_500KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 10000000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_800KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 20000000, .brp = 0, .prop_seg = 0, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_1MBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 20000000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_500KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 10000000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#define TWAI_TIMING_CONFIG_800KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 20000000, .brp = 0, .prop_seg = 0, .tseg_1 = 16, .tseg_2 = 8, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#define TWAI_TIMING_CONFIG_1MBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 20000000, .brp = 0, .prop_seg = 0, .tseg_1 = 15, .tseg_2 = 4, .sjw = 3, .ssp_offset = 0, .triple_sampling = false}
|
||||
#else // 32M, 48M, APB80M
|
||||
#define TWAI_TIMING_CONFIG_500KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 8000000, .brp = 0, .prop_seg = 0, .tseg_1 = 11, .tseg_2 = 4, .sjw = 2, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_800KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 8000000, .brp = 0, .prop_seg = 0, .tseg_1 = 6, .tseg_2 = 3, .sjw = 1, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_1MBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 8000000, .brp = 0, .prop_seg = 0, .tseg_1 = 5, .tseg_2 = 2, .sjw = 1, .en_multi_samp = false}
|
||||
#define TWAI_TIMING_CONFIG_500KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 8000000, .brp = 0, .prop_seg = 0, .tseg_1 = 11, .tseg_2 = 4, .sjw = 2, .ssp_offset = 0, .triple_sampling = false}
|
||||
#define TWAI_TIMING_CONFIG_800KBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 8000000, .brp = 0, .prop_seg = 0, .tseg_1 = 6, .tseg_2 = 3, .sjw = 1, .ssp_offset = 0, .triple_sampling = false}
|
||||
#define TWAI_TIMING_CONFIG_1MBITS() {.clk_src = TWAI_CLK_SRC_DEFAULT, .quanta_resolution_hz = 8000000, .brp = 0, .prop_seg = 0, .tseg_1 = 5, .tseg_2 = 2, .sjw = 1, .ssp_offset = 0, .triple_sampling = false}
|
||||
#endif
|
||||
|
||||
/**
|
||||
@@ -87,6 +87,15 @@ extern "C" {
|
||||
#define TWAI_FILTER_CONFIG_ACCEPT_ALL() {.acceptance_code = 0, .acceptance_mask = 0xFFFFFFFF, .single_filter = true}
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief TWAI Controller operating modes
|
||||
*/
|
||||
typedef enum {
|
||||
TWAI_MODE_NORMAL, /**< Normal operating mode where TWAI controller can send/receive/acknowledge messages */
|
||||
TWAI_MODE_NO_ACK, /**< Transmission does not require acknowledgment. Use this mode for self testing */
|
||||
TWAI_MODE_LISTEN_ONLY, /**< The TWAI controller will not influence the bus (No transmissions or acknowledgments) but can receive messages */
|
||||
} twai_mode_t;
|
||||
|
||||
/**
|
||||
* @brief Structure to store a TWAI message
|
||||
*
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -17,6 +17,20 @@
|
||||
|
||||
/* ---------------------------- Init and Config ----------------------------- */
|
||||
|
||||
#if SOC_TWAI_SUPPORT_FD
|
||||
bool twai_hal_init(twai_hal_context_t *hal_ctx, const twai_hal_config_t *config)
|
||||
{
|
||||
hal_ctx->dev = TWAIFD_LL_GET_HW(config->controller_id);
|
||||
hal_ctx->enable_listen_only = config->enable_listen_only;
|
||||
|
||||
twaifd_ll_reset(hal_ctx->dev);
|
||||
//mode should be changed under disabled
|
||||
twaifd_ll_enable_hw(hal_ctx->dev, false);
|
||||
twaifd_ll_enable_rxfifo_auto_incrase(hal_ctx->dev, true);
|
||||
twaifd_ll_enable_intr(hal_ctx->dev, config->intr_mask);
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
bool twai_hal_init(twai_hal_context_t *hal_ctx, const twai_hal_config_t *config)
|
||||
{
|
||||
//Initialize HAL context
|
||||
@@ -99,3 +113,4 @@ void twai_hal_stop(twai_hal_context_t *hal_ctx)
|
||||
//Any TX is immediately halted on entering reset mode
|
||||
TWAI_HAL_CLEAR_BITS(hal_ctx->state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED | TWAI_HAL_STATE_FLAG_RUNNING);
|
||||
}
|
||||
#endif //SOC_TWAI_SUPPORT_FD
|
||||
|
@@ -16,7 +16,7 @@
|
||||
#endif
|
||||
|
||||
/* ----------------------------- Event Handling ----------------------------- */
|
||||
|
||||
#if !SOC_TWAI_SUPPORT_FD
|
||||
/**
|
||||
* Helper functions that can decode what events have been triggered based on
|
||||
* the values of the interrupt, status, TEC and REC registers. The HAL context's
|
||||
@@ -200,3 +200,4 @@ void twai_hal_set_tx_buffer_and_transmit(twai_hal_context_t *hal_ctx, twai_hal_f
|
||||
ESP_COMPILER_DIAGNOSTIC_POP("-Wanalyzer-overlapping-buffers")
|
||||
#endif //defined(CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID) || defined(CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT)
|
||||
}
|
||||
#endif // !SOC_TWAI_SUPPORT_FD
|
||||
|
@@ -39,6 +39,10 @@ config SOC_MCPWM_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_TWAI_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_ETM_SUPPORTED
|
||||
bool
|
||||
default y
|
||||
|
@@ -26,7 +26,7 @@
|
||||
#define SOC_GPTIMER_SUPPORTED 1
|
||||
#define SOC_PCNT_SUPPORTED 1
|
||||
#define SOC_MCPWM_SUPPORTED 1
|
||||
// #define SOC_TWAI_SUPPORTED 1 // TODO: [ESP32C5] IDF-8691
|
||||
#define SOC_TWAI_SUPPORTED 1
|
||||
#define SOC_ETM_SUPPORTED 1
|
||||
#define SOC_PARLIO_SUPPORTED 1
|
||||
#define SOC_ASYNC_MEMCPY_SUPPORTED 1
|
||||
|
@@ -10,6 +10,7 @@
|
||||
const twai_controller_signal_conn_t twai_controller_periph_signals = {
|
||||
.controllers = {
|
||||
[0] = {
|
||||
.module_name = "TWAI0",
|
||||
.irq_id = ETS_TWAI0_INTR_SOURCE,
|
||||
.timer_irq_id = ETS_TWAI0_TIMER_INTR_SOURCE,
|
||||
.tx_sig = TWAI0_TX_IDX,
|
||||
@@ -19,6 +20,7 @@ const twai_controller_signal_conn_t twai_controller_periph_signals = {
|
||||
.stand_by_sig = TWAI0_STANDBY_IDX,
|
||||
},
|
||||
[1] = {
|
||||
.module_name = "TWAI1",
|
||||
.irq_id = ETS_TWAI1_INTR_SOURCE,
|
||||
.timer_irq_id = ETS_TWAI1_TIMER_INTR_SOURCE,
|
||||
.tx_sig = TWAI1_TX_IDX,
|
||||
|
@@ -23,6 +23,7 @@ extern "C" {
|
||||
typedef struct {
|
||||
struct {
|
||||
const periph_module_t module; // peripheral module
|
||||
const char *module_name; // peripheral name
|
||||
const int irq_id; // interrupt source ID
|
||||
#if SOC_TWAI_SUPPORT_TIMESTAMP
|
||||
const int timer_irq_id;
|
||||
|
@@ -481,7 +481,8 @@ examples/peripherals/touch_sensor/touch_sens_basic:
|
||||
|
||||
examples/peripherals/twai/twai_alert_and_recovery:
|
||||
disable:
|
||||
- if: SOC_TWAI_SUPPORTED != 1
|
||||
- if: SOC_TWAI_SUPPORTED != 1 or SOC_TWAI_SUPPORT_FD == 1
|
||||
reason: This example not support FD
|
||||
disable_test:
|
||||
- if: IDF_TARGET not in ["esp32"]
|
||||
temporary: true
|
||||
@@ -489,7 +490,8 @@ examples/peripherals/twai/twai_alert_and_recovery:
|
||||
|
||||
examples/peripherals/twai/twai_network:
|
||||
disable:
|
||||
- if: SOC_TWAI_SUPPORTED != 1
|
||||
- if: SOC_TWAI_SUPPORTED != 1 or SOC_TWAI_SUPPORT_FD == 1
|
||||
reason: This example not support FD
|
||||
disable_test:
|
||||
- if: 1 == 1
|
||||
temporary: true
|
||||
@@ -497,7 +499,8 @@ examples/peripherals/twai/twai_network:
|
||||
|
||||
examples/peripherals/twai/twai_self_test:
|
||||
disable:
|
||||
- if: SOC_TWAI_SUPPORTED != 1
|
||||
- if: SOC_TWAI_SUPPORTED != 1 or SOC_TWAI_SUPPORT_FD == 1
|
||||
reason: This example not support FD
|
||||
disable_test:
|
||||
- if: IDF_TARGET not in ["esp32"]
|
||||
temporary: true
|
||||
|
Reference in New Issue
Block a user