feat(driver_twai): add new driver based on c5 twaifd

This commit is contained in:
wanckl
2024-09-14 14:24:56 +08:00
parent 37c3ed6320
commit 043c46b4f4
23 changed files with 1561 additions and 135 deletions

View File

@@ -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()

View File

@@ -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:

View 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"
)

View 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

View 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);
}

View 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

View 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

View 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

View 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

View 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)

View 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;
}

View 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

View File

@@ -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);

View File

@@ -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;
/**

View File

@@ -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

View File

@@ -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
*

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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,

View File

@@ -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;

View File

@@ -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