mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
Merge branch 'fix/twaifd_assert_fail_v6.0' into 'release/v6.0'
fix(driver_twai): fix driver assert fail when send frame (v6.0) See merge request espressif/esp-idf!49941
This commit is contained in:
@@ -243,8 +243,8 @@ static void twai_intr_handler_main(void *arg)
|
||||
//Note: This event will never occur if there is a periph reset event
|
||||
twai_handle_rx_buffer_frames(p_twai_obj, &task_woken, &alert_req);
|
||||
}
|
||||
if (events & TWAI_HAL_EVENT_TX_BUFF_FREE) {
|
||||
twai_handle_tx_buffer_frame(p_twai_obj, (events & TWAI_HAL_EVENT_TX_SUCCESS), &task_woken, &alert_req);
|
||||
if (events & TWAI_HAL_EVENT_TX0_DONE) {
|
||||
twai_handle_tx_buffer_frame(p_twai_obj, (events & TWAI_HAL_EVENT_TX0_SUCCESS), &task_woken, &alert_req);
|
||||
}
|
||||
|
||||
//Handle events that only require alerting (i.e. no handler)
|
||||
|
||||
@@ -9,7 +9,7 @@ set(public_include "include")
|
||||
set(priv_req esp_driver_gpio esp_pm esp_timer)
|
||||
|
||||
if(CONFIG_SOC_TWAI_SUPPORTED)
|
||||
list(APPEND srcs "esp_twai_onchip.c")
|
||||
list(APPEND srcs "esp_twai_onchip.c" "twai_frame_queue.c")
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2024-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -33,7 +33,7 @@ uint32_t twai_node_timing_calc_param(const uint32_t source_freq, const twai_timi
|
||||
if (total_div != tseg * pre_div) {
|
||||
continue; // no integer tseg
|
||||
}
|
||||
if ((tseg <= (hw_limit->tseg1_max + hw_limit->tseg2_max + 1)) && (tseg >= (hw_limit->tseg1_min + hw_limit->tseg2_min))) {
|
||||
if ((tseg <= (hw_limit->tseg1_max + hw_limit->tseg2_max + hw_limit->prop_max + 1)) && (tseg >= (hw_limit->tseg1_min + hw_limit->tseg2_min + 1))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -44,20 +44,21 @@ uint32_t twai_node_timing_calc_param(const uint32_t source_freq, const twai_timi
|
||||
uint16_t default_point = (in_param->bitrate >= 800000) ? 750 : ((in_param->bitrate >= 500000) ? 800 : 875);
|
||||
uint16_t sample_point = in_param->sp_permill ? in_param->sp_permill : default_point; // default sample point based on bitrate if not configured
|
||||
uint16_t tseg_1 = (tseg * sample_point) / 1000 - 1;
|
||||
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));
|
||||
uint16_t prop = MAX(1, tseg_1 / 4); // prop_seg is usually shorter than tseg_1 and at least 1
|
||||
tseg_1 -= prop;
|
||||
tseg_1 = MAX(hw_limit->tseg1_min, MIN(tseg_1, hw_limit->tseg1_max + hw_limit->prop_max));
|
||||
uint16_t phase_seg2 = tseg - tseg_1 - 1;
|
||||
phase_seg2 = MAX(hw_limit->tseg2_min, MIN(phase_seg2, hw_limit->tseg2_max));
|
||||
uint16_t phase_seg1 = (tseg_1 * 3) / 4; // phase_seg1 is usually larger than prop_seg
|
||||
phase_seg1 = MAX(hw_limit->tseg1_min, MIN(phase_seg1, hw_limit->tseg1_max));
|
||||
uint16_t prop = tseg_1 - phase_seg1;
|
||||
|
||||
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->tseg_1 = phase_seg1;
|
||||
out_param->tseg_2 = phase_seg2;
|
||||
out_param->sjw = MAX(1, MIN(phase_seg2 >> 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));
|
||||
return source_freq / (pre_div * (prop + phase_seg1 + phase_seg2 + 1));
|
||||
}
|
||||
|
||||
esp_err_t twai_node_enable(twai_node_handle_t node)
|
||||
|
||||
@@ -7,9 +7,10 @@
|
||||
#include "esp_timer.h"
|
||||
#include "esp_twai.h"
|
||||
#include "esp_twai_onchip.h"
|
||||
#include "twai_private.h"
|
||||
#include "esp_private/twai_interface.h"
|
||||
#include "esp_private/twai_utils.h"
|
||||
#include "twai_private.h"
|
||||
#include "esp_private/twai_frame_queue.h"
|
||||
#include "hal/twai_periph.h"
|
||||
#include "hal/twai_hal.h"
|
||||
#if SOC_HAS(TWAI_FD)
|
||||
@@ -54,7 +55,7 @@ typedef struct {
|
||||
twai_hal_context_t *hal;
|
||||
intr_handle_t intr_hdl;
|
||||
intr_handle_t timer_intr_hdl;
|
||||
QueueHandle_t tx_mount_queue;
|
||||
twai_frame_queue_t tx_queue;
|
||||
EventGroupHandle_t event_group;
|
||||
twai_clock_source_t curr_clk_src;
|
||||
uint32_t src_freq_hz;
|
||||
@@ -68,10 +69,11 @@ typedef struct {
|
||||
|
||||
_Atomic twai_error_state_t state;
|
||||
twai_node_record_t history;
|
||||
uint8_t tx_slot_num;
|
||||
|
||||
atomic_bool hw_busy;
|
||||
atomic_bool rx_isr;
|
||||
const twai_frame_t *p_curr_tx;
|
||||
const twai_frame_t *p_curr_tx[TWAI_HAL_TX_BUFFER_SLOT_NUM];
|
||||
twai_hal_frame_t rcv_buff;
|
||||
} twai_onchip_ctx_t;
|
||||
|
||||
@@ -162,9 +164,9 @@ static void _node_release_io(twai_onchip_ctx_t *node)
|
||||
}
|
||||
}
|
||||
|
||||
static void _node_start_trans(twai_onchip_ctx_t *node)
|
||||
static void _node_start_trans(twai_onchip_ctx_t *node, uint8_t buffer_idx)
|
||||
{
|
||||
const twai_frame_t *frame = node->p_curr_tx;
|
||||
const twai_frame_t *frame = node->p_curr_tx[buffer_idx];
|
||||
twai_hal_context_t *hal = node->hal;
|
||||
|
||||
twai_hal_frame_t hal_buf = {};
|
||||
@@ -180,8 +182,40 @@ static void _node_start_trans(twai_onchip_ctx_t *node)
|
||||
},
|
||||
};
|
||||
twai_hal_format_frame(&hal_trans, &hal_buf);
|
||||
//TODO: utilize all txt buffers
|
||||
twai_hal_set_tx_buffer_and_transmit(hal, &hal_buf, 0);
|
||||
twai_hal_set_tx_buffer_and_transmit(hal, &hal_buf, buffer_idx);
|
||||
}
|
||||
|
||||
static uint8_t _node_start_tx_batch_from_isr(twai_onchip_ctx_t *node, BaseType_t *yield_required)
|
||||
{
|
||||
uint8_t tx_idx = 0;
|
||||
// Note: hardware slot has default priority (like 0>1>2...),
|
||||
// we should fill slot with same order to avoid data inverse
|
||||
while (tx_idx < node->tx_slot_num) { // try best to fill all tx slots
|
||||
const twai_frame_t *frame = NULL;
|
||||
if (twai_frame_queue_pop_from_isr(node->tx_queue, &frame, (bool *)yield_required) != ESP_OK) {
|
||||
break;
|
||||
}
|
||||
node->p_curr_tx[tx_idx] = frame;
|
||||
_node_start_trans(node, tx_idx);
|
||||
tx_idx++;
|
||||
}
|
||||
return tx_idx;
|
||||
}
|
||||
|
||||
static void _node_mark_tx_idle(twai_onchip_ctx_t *node)
|
||||
{
|
||||
memset(node->p_curr_tx, 0, sizeof(node->p_curr_tx));
|
||||
atomic_store(&node->hw_busy, false);
|
||||
}
|
||||
|
||||
static inline bool _node_is_tx_all_done(twai_onchip_ctx_t *node)
|
||||
{
|
||||
for (uint8_t tx_idx = 0; tx_idx < node->tx_slot_num; tx_idx++) {
|
||||
if (node->p_curr_tx[tx_idx]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static void _node_isr_main(void *arg)
|
||||
@@ -229,11 +263,10 @@ static void _node_isr_main(void *arg)
|
||||
}
|
||||
// node recover from busoff, restart remain tx transaction
|
||||
if ((e_data.old_sta == TWAI_ERROR_BUS_OFF) && (e_data.new_sta == TWAI_ERROR_ACTIVE)) {
|
||||
if (xQueueReceiveFromISR(twai_ctx->tx_mount_queue, &twai_ctx->p_curr_tx, &do_yield)) {
|
||||
if (_node_start_tx_batch_from_isr(twai_ctx, &do_yield)) {
|
||||
atomic_store(&twai_ctx->hw_busy, true);
|
||||
_node_start_trans(twai_ctx);
|
||||
} else {
|
||||
atomic_store(&twai_ctx->hw_busy, false);
|
||||
_node_mark_tx_idle(twai_ctx);
|
||||
xEventGroupSetBitsFromISR(twai_ctx->event_group, TWAI_IDLE_EVENT_BIT, &do_yield);
|
||||
}
|
||||
}
|
||||
@@ -265,23 +298,27 @@ static void _node_isr_main(void *arg)
|
||||
}
|
||||
|
||||
// deal TX event
|
||||
if (events & TWAI_HAL_EVENT_TX_BUFF_FREE) {
|
||||
if (twai_ctx->cbs.on_tx_done) {
|
||||
twai_tx_done_event_data_t tx_ev = {
|
||||
.is_tx_success = (events & TWAI_HAL_EVENT_TX_SUCCESS), // find 'on_error_cb' if not success
|
||||
.done_tx_frame = twai_ctx->p_curr_tx,
|
||||
};
|
||||
do_yield |= twai_ctx->cbs.on_tx_done(&twai_ctx->api_base, &tx_ev, twai_ctx->user_data);
|
||||
if (events & TWAI_HAL_EVENT_TX_DONE_MASK) {
|
||||
uint32_t tx_done_events = (events & TWAI_HAL_EVENT_TX_DONE_MASK) >> __builtin_ctz(TWAI_HAL_EVENT_TX0_DONE);
|
||||
while (tx_done_events) {
|
||||
uint32_t slot_event = tx_done_events & -tx_done_events; // get the lowest event bit
|
||||
uint8_t tx_idx = __builtin_ctz(slot_event) / 2;
|
||||
assert((tx_idx < twai_ctx->tx_slot_num) && twai_ctx->p_curr_tx[tx_idx]);
|
||||
if (twai_ctx->cbs.on_tx_done) {
|
||||
twai_tx_done_event_data_t tx_ev = {
|
||||
.is_tx_success = (events & TWAI_HAL_EVENT_TX_SUCC_SLOT(tx_idx)), // find 'on_error_cb' if not success
|
||||
.done_tx_frame = twai_ctx->p_curr_tx[tx_idx],
|
||||
};
|
||||
do_yield |= twai_ctx->cbs.on_tx_done(&twai_ctx->api_base, &tx_ev, twai_ctx->user_data);
|
||||
}
|
||||
twai_ctx->p_curr_tx[tx_idx] = NULL;
|
||||
tx_done_events &= ~slot_event;
|
||||
}
|
||||
// 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);
|
||||
if (atomic_load(&twai_ctx->state) != TWAI_ERROR_BUS_OFF) {
|
||||
// only when node is not in busoff here, means tx is finished
|
||||
|
||||
// start a new TX batch only when all hardware TX buffers from this batch are done
|
||||
if (_node_is_tx_all_done(twai_ctx) && (atomic_load(&twai_ctx->state) != TWAI_ERROR_BUS_OFF)) {
|
||||
if (!_node_start_tx_batch_from_isr(twai_ctx, &do_yield)) {
|
||||
_node_mark_tx_idle(twai_ctx);
|
||||
xEventGroupSetBitsFromISR(twai_ctx->event_group, TWAI_IDLE_EVENT_BIT, &do_yield);
|
||||
}
|
||||
}
|
||||
@@ -315,9 +352,7 @@ static void _node_destroy(twai_onchip_ctx_t *twai_ctx)
|
||||
if (twai_ctx->timer_intr_hdl) {
|
||||
esp_intr_free(twai_ctx->timer_intr_hdl);
|
||||
}
|
||||
if (twai_ctx->tx_mount_queue) {
|
||||
vQueueDeleteWithCaps(twai_ctx->tx_mount_queue);
|
||||
}
|
||||
twai_frame_queue_del(twai_ctx->tx_queue);
|
||||
if (twai_ctx->event_group) {
|
||||
vEventGroupDeleteWithCaps(twai_ctx->event_group);
|
||||
}
|
||||
@@ -386,6 +421,7 @@ static esp_err_t _node_calc_set_bit_timing(twai_node_handle_t node, const twai_t
|
||||
twai_timing_constraint_t hw_const = {
|
||||
.brp_min = TWAI_LL_BRP_MIN,
|
||||
.brp_max = TWAI_LL_BRP_MAX,
|
||||
.prop_max = TWAI_LL_PROP_MAX,
|
||||
.tseg1_min = TWAI_LL_TSEG1_MIN,
|
||||
.tseg1_max = TWAI_LL_TSEG1_MAX,
|
||||
.tseg2_min = TWAI_LL_TSEG2_MIN,
|
||||
@@ -404,6 +440,7 @@ static esp_err_t _node_calc_set_bit_timing(twai_node_handle_t node, const twai_t
|
||||
twai_timing_advanced_config_t timing_adv_fd = {};
|
||||
if (timing_fd->bitrate) {
|
||||
hw_const.brp_max = TWAI_LL_BRP_MAX_FD;
|
||||
hw_const.prop_max = TWAI_LL_PROP_MAX_FD;
|
||||
hw_const.tseg1_max = TWAI_LL_TSEG1_MAX_FD;
|
||||
hw_const.tseg2_max = TWAI_LL_TSEG2_MAX_FD;
|
||||
hw_const.sjw_max = TWAI_LL_SJW_MAX_FD;
|
||||
@@ -456,7 +493,11 @@ static esp_err_t _node_enable(twai_node_handle_t node)
|
||||
atomic_store(&twai_ctx->state, hw_state);
|
||||
// continuing the transaction if there be
|
||||
if (atomic_load(&twai_ctx->hw_busy) && hw_state != TWAI_ERROR_BUS_OFF) {
|
||||
_node_start_trans(twai_ctx);
|
||||
for (uint8_t tx_idx = 0; tx_idx < twai_ctx->tx_slot_num; tx_idx++) {
|
||||
if (twai_ctx->p_curr_tx[tx_idx]) {
|
||||
_node_start_trans(twai_ctx, tx_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_intr_enable(twai_ctx->intr_hdl), TAG, "enable interrupt failed");
|
||||
return ESP_OK;
|
||||
@@ -547,7 +588,7 @@ static esp_err_t _node_get_status(twai_node_handle_t node, twai_node_status_t *s
|
||||
status_ret->state = atomic_load(&twai_ctx->state);
|
||||
status_ret->tx_error_count = twai_hal_get_tec(twai_ctx->hal);
|
||||
status_ret->rx_error_count = twai_hal_get_rec(twai_ctx->hal);
|
||||
status_ret->tx_queue_remaining = uxQueueSpacesAvailable(twai_ctx->tx_mount_queue);
|
||||
status_ret->tx_queue_remaining = twai_frame_queue_get_free_space(twai_ctx->tx_queue);
|
||||
}
|
||||
if (record_ret) {
|
||||
*record_ret = twai_ctx->history;
|
||||
@@ -571,46 +612,25 @@ static esp_err_t _node_queue_tx(twai_node_handle_t node, const twai_frame_t *fra
|
||||
ESP_RETURN_ON_FALSE_ISR((!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_ISR(!twai_ctx->hal->enable_listen_only, ESP_ERR_NOT_SUPPORTED, TAG, "node is config as listen only");
|
||||
ESP_RETURN_ON_FALSE_ISR(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);
|
||||
|
||||
xEventGroupClearBits(twai_ctx->event_group, TWAI_IDLE_EVENT_BIT); //going to send, clear the idle event
|
||||
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);
|
||||
twai_ctx->p_curr_tx[0] = frame; // here only has one frame, using slot 0
|
||||
_node_start_trans(twai_ctx, 0);
|
||||
} else {
|
||||
// Hardware busy, need to queue the frame
|
||||
BaseType_t is_isr_context = xPortInIsrContext();
|
||||
BaseType_t yield_required = pdFALSE;
|
||||
|
||||
if (is_isr_context) {
|
||||
// In ISR context - use ISR-safe queue operations
|
||||
ESP_RETURN_ON_FALSE_ISR(xQueueSendFromISR(twai_ctx->tx_mount_queue, &frame, &yield_required), ESP_ERR_TIMEOUT, TAG, "tx queue full");
|
||||
} else {
|
||||
// In task context - use normal queue operations
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(twai_ctx->tx_mount_queue, &frame, ticks_to_wait), ESP_ERR_TIMEOUT, TAG, "tx queue full");
|
||||
}
|
||||
ESP_RETURN_ON_ERROR_ISR(twai_frame_queue_push_safe(twai_ctx->tx_queue, frame, frame->tx_queue_priority, timeout), TAG, "tx queue full");
|
||||
|
||||
// Second chance check for hardware availability
|
||||
false_var = false;
|
||||
if (atomic_compare_exchange_strong(&twai_ctx->hw_busy, &false_var, true)) {
|
||||
BaseType_t dequeue_result;
|
||||
if (is_isr_context) {
|
||||
dequeue_result = xQueueReceiveFromISR(twai_ctx->tx_mount_queue, &twai_ctx->p_curr_tx, &yield_required);
|
||||
if (twai_frame_queue_pop_safe(twai_ctx->tx_queue, &twai_ctx->p_curr_tx[0]) == ESP_OK) {
|
||||
_node_start_trans(twai_ctx, 0);
|
||||
} else {
|
||||
dequeue_result = xQueueReceive(twai_ctx->tx_mount_queue, &twai_ctx->p_curr_tx, 0);
|
||||
// any reason here means frame already taken and maybe finished by fast hardware, so back `hw_busy` to false
|
||||
atomic_store(&twai_ctx->hw_busy, false);
|
||||
}
|
||||
|
||||
if (dequeue_result == pdTRUE) {
|
||||
_node_start_trans(twai_ctx);
|
||||
} else {
|
||||
assert(false && "should always get frame at this moment");
|
||||
}
|
||||
}
|
||||
|
||||
// Handle ISR yield if required
|
||||
if (is_isr_context && yield_required) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
return ESP_OK;
|
||||
@@ -622,9 +642,9 @@ static esp_err_t _node_wait_tx_all_done(twai_node_handle_t node, int timeout)
|
||||
TickType_t ticks_to_wait = (timeout == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout);
|
||||
ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) != TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "node is bus off");
|
||||
|
||||
// either hw_busy or tx_mount_queue is not empty, means tx is not finished
|
||||
// either hw_busy or tx_queue is not empty, means tx is not finished
|
||||
// otherwise, hardware is idle, return immediately
|
||||
if (atomic_load(&twai_ctx->hw_busy) || uxQueueMessagesWaiting(twai_ctx->tx_mount_queue)) {
|
||||
if (atomic_load(&twai_ctx->hw_busy) || twai_frame_queue_get_count(twai_ctx->tx_queue)) {
|
||||
//wait for idle event bit but without clear it, every tasks block here can be waked up
|
||||
if (TWAI_IDLE_EVENT_BIT != xEventGroupWaitBits(twai_ctx->event_group, TWAI_IDLE_EVENT_BIT, pdFALSE, pdFALSE, ticks_to_wait)) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
@@ -680,9 +700,11 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa
|
||||
|
||||
// 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);
|
||||
if (!node_config->flags.enable_listen_only) {
|
||||
ESP_GOTO_ON_ERROR(twai_frame_queue_new(&node->tx_queue, node_config->tx_queue_depth, TWAI_MALLOC_CAPS), err, TAG, "no_mem");
|
||||
}
|
||||
node->event_group = xEventGroupCreateWithCaps(TWAI_MALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE((node->tx_mount_queue && node->event_group) || node_config->flags.enable_listen_only, ESP_ERR_NO_MEM, err, TAG, "no_mem");
|
||||
ESP_GOTO_ON_FALSE(node->event_group, ESP_ERR_NO_MEM, err, TAG, "no_mem");
|
||||
uint32_t intr_flags = TWAI_INTR_ALLOC_FLAGS;
|
||||
intr_flags |= (node_config->intr_priority > 0) ? BIT(node_config->intr_priority) : ESP_INTR_FLAG_LOWMED;
|
||||
_lock_acquire(&s_platform.intr_mutex); // lock to prevent twai_intr and timer_intr registered to different cpu then triggered at the same time
|
||||
@@ -756,6 +778,11 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa
|
||||
.enable_loopback = node_config->flags.enable_loopback,
|
||||
};
|
||||
ESP_GOTO_ON_FALSE(twai_hal_init(node->hal, &hal_config), ESP_ERR_INVALID_STATE, err, TAG, "hardware not in reset state");
|
||||
node->tx_slot_num = twai_hal_get_tx_slot_num(node->hal);
|
||||
if (node->tx_slot_num > TWAI_HAL_TX_BUFFER_SLOT_NUM) {
|
||||
ESP_LOGW(TAG, "HW TX slot num (%d) is greater than supported, only using %d slots", node->tx_slot_num, TWAI_HAL_TX_BUFFER_SLOT_NUM);
|
||||
node->tx_slot_num = TWAI_HAL_TX_BUFFER_SLOT_NUM;
|
||||
}
|
||||
// Configure bus timing
|
||||
ESP_GOTO_ON_ERROR(_node_calc_set_bit_timing(&node->api_base, &node_config->bit_timing, &node_config->data_timing), err, TAG, "bitrate error");
|
||||
// Configure GPIO
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_twai_types.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// !! This queue is ONLY for TWAI driver internal use //
|
||||
////////////////////////////////////////////////////////////////////
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct twai_frame_queue_s *twai_frame_queue_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize a TWAI frame priority queue.
|
||||
*
|
||||
* Items are ordered by priority first. For equal priority values, items are popped in push order.
|
||||
*/
|
||||
esp_err_t twai_frame_queue_new(twai_frame_queue_t *queue, size_t capacity, uint32_t mem_caps);
|
||||
|
||||
/**
|
||||
* @brief Release resources used by a TWAI frame priority queue.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Queue was released successfully
|
||||
* - ESP_ERR_INVALID_ARG: Queue is invalid
|
||||
*/
|
||||
esp_err_t twai_frame_queue_del(twai_frame_queue_t queue);
|
||||
|
||||
/**
|
||||
* @brief Push an item with priority from task context.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Item was queued successfully
|
||||
* - ESP_ERR_INVALID_ARG: Queue is invalid
|
||||
* - ESP_ERR_TIMEOUT: No free slot became available before timeout
|
||||
*/
|
||||
esp_err_t twai_frame_queue_push(twai_frame_queue_t queue, const twai_frame_t *data, uint32_t priority, int timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Push an item with priority from ISR context.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Item was queued successfully
|
||||
* - ESP_ERR_INVALID_ARG: Queue is invalid
|
||||
* - ESP_ERR_TIMEOUT: Queue is full
|
||||
*/
|
||||
esp_err_t twai_frame_queue_push_from_isr(twai_frame_queue_t queue, const twai_frame_t *data, uint32_t priority, bool *task_woken);
|
||||
|
||||
/**
|
||||
* @brief Push an item with priority from task or ISR context.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Item was queued successfully
|
||||
* - ESP_ERR_INVALID_ARG: Queue is invalid
|
||||
* - ESP_ERR_TIMEOUT: Queue is full or no free slot became available before timeout
|
||||
*/
|
||||
esp_err_t twai_frame_queue_push_safe(twai_frame_queue_t queue, const twai_frame_t *data, uint32_t priority, int timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Pop the highest-priority item from task context.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Item was popped successfully
|
||||
* - ESP_ERR_INVALID_ARG: Queue is invalid
|
||||
* - ESP_ERR_NOT_FOUND: Queue is empty
|
||||
*/
|
||||
esp_err_t twai_frame_queue_pop(twai_frame_queue_t queue, const twai_frame_t **data);
|
||||
|
||||
/**
|
||||
* @brief Pop the highest-priority item from ISR context.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Item was popped successfully
|
||||
* - ESP_ERR_INVALID_ARG: Queue is invalid
|
||||
* - ESP_ERR_NOT_FOUND: Queue is empty
|
||||
*/
|
||||
esp_err_t twai_frame_queue_pop_from_isr(twai_frame_queue_t queue, const twai_frame_t **data, bool *task_woken);
|
||||
|
||||
/**
|
||||
* @brief Pop the highest-priority item from task or ISR context.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Item was popped successfully
|
||||
* - ESP_ERR_INVALID_ARG: Queue is invalid
|
||||
* - ESP_ERR_NOT_FOUND: Queue is empty
|
||||
*/
|
||||
esp_err_t twai_frame_queue_pop_safe(twai_frame_queue_t queue, const twai_frame_t **data);
|
||||
|
||||
/**
|
||||
* @brief Get the current number of queued items.
|
||||
*/
|
||||
size_t twai_frame_queue_get_count(twai_frame_queue_t queue);
|
||||
|
||||
/**
|
||||
* @brief Get the number of available queue slots.
|
||||
*/
|
||||
size_t twai_frame_queue_get_free_space(twai_frame_queue_t queue);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -19,6 +19,7 @@ extern "C" {
|
||||
typedef struct {
|
||||
uint32_t brp_min; /* Bit-rate prescaler */
|
||||
uint32_t brp_max;
|
||||
uint8_t prop_max; /* Propagation segment */
|
||||
uint8_t tseg1_min; /* Time segment 1 = prop_seg + phase_seg1 */
|
||||
uint8_t tseg1_max;
|
||||
uint8_t tseg2_min; /* Time segment 2 = phase_seg2 */
|
||||
|
||||
@@ -33,6 +33,7 @@ typedef struct {
|
||||
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.*/
|
||||
uint8_t tx_queue_priority; /**< Frame priority, range [0, 255], a frame with higher priority value will be picked first from the queue */
|
||||
} twai_frame_t;
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,10 +6,20 @@ entries:
|
||||
esp_twai_onchip: _node_isr_main (noflash)
|
||||
esp_twai_onchip: _node_start_trans (noflash)
|
||||
esp_twai_onchip: _node_parse_rx (noflash)
|
||||
esp_twai_onchip: _node_start_tx_batch_from_isr (noflash)
|
||||
esp_twai_onchip: _node_mark_tx_idle (noflash)
|
||||
esp_twai_onchip: _node_is_tx_all_done (noflash)
|
||||
twai_frame_queue: twai_frame_queue_pop_from_isr (noflash)
|
||||
|
||||
if TWAI_IO_FUNC_IN_IRAM = y:
|
||||
esp_twai_onchip: _node_queue_tx (noflash)
|
||||
esp_twai: twai_node_transmit (noflash)
|
||||
twai_frame_queue: twai_frame_queue_push (noflash)
|
||||
twai_frame_queue: twai_frame_queue_push_from_isr (noflash)
|
||||
twai_frame_queue: twai_frame_queue_push_safe (noflash)
|
||||
twai_frame_queue: twai_frame_queue_pop (noflash)
|
||||
twai_frame_queue: twai_frame_queue_pop_from_isr (noflash)
|
||||
twai_frame_queue: twai_frame_queue_pop_safe (noflash)
|
||||
|
||||
[mapping:twai_hal]
|
||||
archive: libesp_hal_twai.a
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
set(srcs "test_app_main.c")
|
||||
|
||||
if(CONFIG_SOC_TWAI_SUPPORTED)
|
||||
list(APPEND srcs "test_twai_common.cpp" "test_twai_network.cpp")
|
||||
list(APPEND srcs "test_twai_common.cpp" "test_twai_network.cpp" "test_twai_queue.cpp")
|
||||
if(CONFIG_SOC_LIGHT_SLEEP_SUPPORTED)
|
||||
list(APPEND srcs "test_twai_sleep.c")
|
||||
endif()
|
||||
|
||||
@@ -23,13 +23,8 @@
|
||||
#include "esp_private/gpio.h"
|
||||
#include "driver/uart.h" // for baudrate detection
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32H4
|
||||
#define TEST_TX_GPIO GPIO_NUM_2
|
||||
#define TEST_RX_GPIO GPIO_NUM_3
|
||||
#else
|
||||
#define TEST_TX_GPIO GPIO_NUM_4
|
||||
#define TEST_RX_GPIO GPIO_NUM_5
|
||||
#endif
|
||||
#define TEST_TWAI_QUEUE_DEPTH 5
|
||||
#define TEST_TRANS_LEN 100
|
||||
#define TEST_FRAME_LEN 7
|
||||
@@ -57,7 +52,7 @@ TEST_CASE("twai install uninstall (loopback)", "[twai]")
|
||||
node_config.io_cfg.rx = TEST_TX_GPIO; // Using same pin for test without transceiver
|
||||
node_config.io_cfg.quanta_clk_out = GPIO_NUM_NC;
|
||||
node_config.io_cfg.bus_off_indicator = GPIO_NUM_NC;
|
||||
node_config.bit_timing.bitrate = 1000000;
|
||||
node_config.bit_timing.bitrate = 100000;
|
||||
node_config.tx_queue_depth = TEST_TWAI_QUEUE_DEPTH;
|
||||
node_config.flags.enable_self_test = true;
|
||||
node_config.flags.enable_loopback = true;
|
||||
@@ -153,7 +148,7 @@ static void test_twai_baudrate_correctness(twai_clock_source_t clk_src, uint32_t
|
||||
TEST_ESP_OK(uart_detect_bitrate_stop(UART_NUM_1, true, &measure_result));
|
||||
uint32_t bitrate_measured = measure_result.clk_freq_hz * 4 / (measure_result.pos_period + measure_result.neg_period);
|
||||
printf("TWAI bitrate measured: %" PRIu32 "\r\n", bitrate_measured);
|
||||
TEST_ASSERT_INT_WITHIN(1000, test_bitrate, bitrate_measured); // 1k tolerance
|
||||
TEST_ASSERT_INT_WITHIN((test_bitrate / 100), test_bitrate, bitrate_measured); // 1% tolerance
|
||||
|
||||
TEST_ESP_OK(twai_node_disable(twai_node));
|
||||
TEST_ESP_OK(twai_node_delete(twai_node));
|
||||
@@ -875,18 +870,17 @@ TEST_CASE("twai rx timestamp", "[twai]")
|
||||
node_config.flags.enable_loopback = true;
|
||||
node_config.flags.enable_self_test = true;
|
||||
|
||||
bool hw_timer = false;
|
||||
#if TWAI_LL_SUPPORT(TIMESTAMP)
|
||||
hw_timer = true;
|
||||
#endif
|
||||
for (uint32_t resolution = 1000; resolution <= 10000000; resolution *= 100) {
|
||||
#if CONFIG_IDF_TARGET_ESP32H4
|
||||
if (resolution > 1000000) {
|
||||
continue; // h4 clk_src [32M, 96M] can't accurate support resolutions > 1MHz
|
||||
}
|
||||
#endif
|
||||
node_config.timestamp_resolution_hz = resolution;
|
||||
printf("\nTesting resolution %ld\n", resolution);
|
||||
if (((resolution < 2000) && hw_timer) || ((resolution > 1000000) && !hw_timer)) {
|
||||
TEST_ESP_ERR(twai_new_node_onchip(&node_config, &node_hdl), ESP_ERR_INVALID_ARG);
|
||||
if (ESP_OK != twai_new_node_onchip(&node_config, &node_hdl)) {
|
||||
continue;
|
||||
}
|
||||
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl));
|
||||
|
||||
uint8_t rx_buffer[TWAI_FRAME_MAX_LEN] = {0};
|
||||
twai_frame_t rx_frame = {};
|
||||
@@ -911,8 +905,8 @@ TEST_CASE("twai rx timestamp", "[twai]")
|
||||
|
||||
time_now = MS_TO_TWAI_TICK(esp_timer_get_time() / 1000, resolution);
|
||||
printf("esp tick now %llu, diff %u\n", time_now, abs(time_now - rx_frame.header.timestamp));
|
||||
TEST_ASSERT_INT32_WITHIN(MAX(resolution / 100, 5), time_now, rx_frame.header.timestamp);
|
||||
TEST_ASSERT_INT32_WITHIN(MAX(resolution / 100, 5), rx_frame.header.timestamp - time_last, MS_TO_TWAI_TICK(i * 100, resolution));
|
||||
TEST_ASSERT_INT32_WITHIN(MAX(resolution / 100, 5), rx_frame.header.timestamp, time_now);
|
||||
TEST_ASSERT_INT32_WITHIN(MAX(resolution / 100, 5), MS_TO_TWAI_TICK(i * 100, resolution), rx_frame.header.timestamp - time_last);
|
||||
time_last = rx_frame.header.timestamp;
|
||||
}
|
||||
|
||||
@@ -923,7 +917,7 @@ TEST_CASE("twai rx timestamp", "[twai]")
|
||||
TEST_ESP_OK(twai_node_enable(node_hdl));
|
||||
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_frame, 100));
|
||||
TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl, 100));
|
||||
TEST_ASSERT_INT32_WITHIN(MAX(resolution / 100, 5), rx_frame.header.timestamp - time_last, MS_TO_TWAI_TICK(1000, resolution));
|
||||
TEST_ASSERT_INT32_WITHIN(MAX(resolution / 100, 5), MS_TO_TWAI_TICK(1000, resolution), rx_frame.header.timestamp - time_last);
|
||||
|
||||
TEST_ESP_OK(twai_node_disable(node_hdl));
|
||||
TEST_ESP_OK(twai_node_delete(node_hdl));
|
||||
|
||||
@@ -195,6 +195,12 @@ TEST_CASE("twai fd transmit time (loopback)", "[twai]")
|
||||
uint64_t predict_time_ms = (uint64_t)trans_num * arb_bits * 1000 / node_config.bit_timing.bitrate;
|
||||
predict_time_ms += (uint64_t)trans_num * data_bits * 1000 / node_config.data_timing.bitrate;
|
||||
predict_time_ms += (trans_num * 10) / 1000; // add about 10 us interrupt overhead per frame
|
||||
#if CONFIG_COMPILER_OPTIMIZATION_NONE
|
||||
predict_time_ms += (trans_num * 5) / 1000; // non optimized slow code
|
||||
#endif
|
||||
#if CONFIG_PM_DFS_INIT_AUTO
|
||||
predict_time_ms += (trans_num * 10) / 1000; // slow cpu
|
||||
#endif
|
||||
|
||||
//waiting pkg receive finish
|
||||
TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl, -1));
|
||||
@@ -211,7 +217,7 @@ TEST_CASE("twai fd transmit time (loopback)", "[twai]")
|
||||
(unsigned long long)predict_time_ms,
|
||||
memcmp(recv_pkg_ptr, send_pkg_ptr, TEST_TRANS_TIME_BUF_LEN) ? "failed" : "ok");
|
||||
TEST_ASSERT_EQUAL_HEX8_ARRAY(send_pkg_ptr, recv_pkg_ptr, TEST_TRANS_TIME_BUF_LEN);
|
||||
TEST_ASSERT_LESS_THAN((predict_time_ms / 10), abs((time2 - time1) / 1000 - predict_time_ms));
|
||||
TEST_ASSERT_LESS_THAN((predict_time_ms * 15 / 100), abs((time2 - time1) / 1000 - predict_time_ms));
|
||||
}
|
||||
printf("-----------------------------------------------------------------------------------------\n");
|
||||
|
||||
|
||||
@@ -0,0 +1,282 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdatomic.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_twai_types.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "unity.h"
|
||||
#include "esp_private/twai_frame_queue.h"
|
||||
|
||||
typedef struct {
|
||||
int value;
|
||||
uint32_t priority;
|
||||
} twai_frame_queue_test_item_t;
|
||||
|
||||
static void test_twai_frame_queue_print_header(const char *title)
|
||||
{
|
||||
printf("\n%s\n", title);
|
||||
printf("+-------+----------+\n");
|
||||
printf("| value | priority |\n");
|
||||
printf("+-------+----------+\n");
|
||||
}
|
||||
|
||||
static void test_twai_frame_queue_print_row(const twai_frame_queue_test_item_t *item)
|
||||
{
|
||||
printf("| %5d | %8" PRIu32 " |\n", item->value, item->priority);
|
||||
}
|
||||
|
||||
static void test_twai_frame_queue_print_footer(void)
|
||||
{
|
||||
printf("+-------+----------+\n");
|
||||
}
|
||||
|
||||
static void test_twai_frame_queue_pop_in_order(twai_frame_queue_t queue, size_t expected_count, const char *title)
|
||||
{
|
||||
const twai_frame_t *item = NULL;
|
||||
const twai_frame_queue_test_item_t *last_item = NULL;
|
||||
|
||||
test_twai_frame_queue_print_header(title);
|
||||
for (size_t i = 0; i < expected_count; i++) {
|
||||
TEST_ESP_OK(twai_frame_queue_pop(queue, &item));
|
||||
const twai_frame_queue_test_item_t *cur_item = (const twai_frame_queue_test_item_t *) item;
|
||||
|
||||
test_twai_frame_queue_print_row(cur_item);
|
||||
if (last_item) {
|
||||
TEST_ASSERT_LESS_OR_EQUAL(last_item->priority, cur_item->priority);
|
||||
if (cur_item->priority == last_item->priority) {
|
||||
TEST_ASSERT_GREATER_THAN(last_item->value, cur_item->value);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(expected_count - i - 1, twai_frame_queue_get_count(queue));
|
||||
last_item = cur_item;
|
||||
}
|
||||
test_twai_frame_queue_print_footer();
|
||||
}
|
||||
|
||||
TEST_CASE("test twai frame priority queue", "[twai]")
|
||||
{
|
||||
twai_frame_queue_t test_q = NULL;
|
||||
const twai_frame_t *item = NULL;
|
||||
twai_frame_queue_test_item_t test_items[] = {
|
||||
{ 0, 1 }, { 1, 4 }, { 2, 2 }, { 3, 5 },
|
||||
{ 4, 3 }, { 5, 5 }, { 6, 1 }, { 7, 4 },
|
||||
{ 8, 2 }, { 9, 3 }, { 10, 5 }, { 11, 0 },
|
||||
{ 12, 4 }, { 13, 2 }, { 14, 3 }, { 15, 1 },
|
||||
};
|
||||
twai_frame_queue_test_item_t overflow = { 16, 6 };
|
||||
const size_t capacity = sizeof(test_items) / sizeof(test_items[0]);
|
||||
|
||||
TEST_ESP_OK(twai_frame_queue_new(&test_q, capacity, MALLOC_CAP_DEFAULT));
|
||||
TEST_ASSERT_EQUAL(0, twai_frame_queue_get_count(test_q));
|
||||
TEST_ASSERT_EQUAL(capacity, twai_frame_queue_get_free_space(test_q));
|
||||
|
||||
test_twai_frame_queue_print_header("Push all test data");
|
||||
for (size_t i = 0; i < capacity; i++) {
|
||||
TEST_ESP_OK(twai_frame_queue_push(test_q, (const twai_frame_t *) &test_items[i], test_items[i].priority, 0));
|
||||
test_twai_frame_queue_print_row(&test_items[i]);
|
||||
TEST_ASSERT_EQUAL(i + 1, twai_frame_queue_get_count(test_q));
|
||||
TEST_ASSERT_EQUAL(capacity - i - 1, twai_frame_queue_get_free_space(test_q));
|
||||
}
|
||||
test_twai_frame_queue_print_footer();
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_TIMEOUT, twai_frame_queue_push(test_q, (const twai_frame_t *) &overflow, 6, 0));
|
||||
TEST_ASSERT_EQUAL(capacity, twai_frame_queue_get_count(test_q));
|
||||
TEST_ASSERT_EQUAL(0, twai_frame_queue_get_free_space(test_q));
|
||||
|
||||
test_twai_frame_queue_pop_in_order(test_q, capacity, "Pop all test data");
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, twai_frame_queue_pop(test_q, &item));
|
||||
|
||||
TEST_ASSERT_EQUAL(0, twai_frame_queue_get_count(test_q));
|
||||
TEST_ASSERT_EQUAL(capacity, twai_frame_queue_get_free_space(test_q));
|
||||
TEST_ESP_OK(twai_frame_queue_del(test_q));
|
||||
}
|
||||
|
||||
TEST_CASE("test twai queue mixed pop and push", "[twai]")
|
||||
{
|
||||
twai_frame_queue_t test_q = NULL;
|
||||
const twai_frame_t *item = NULL;
|
||||
twai_frame_queue_test_item_t test_items[] = {
|
||||
{ 0, 1 },
|
||||
{ 1, 5 },
|
||||
{ 2, 3 },
|
||||
{ 3, 5 },
|
||||
{ 4, 2 },
|
||||
{ 5, 4 },
|
||||
{ 6, 6 },
|
||||
{ 7, 3 },
|
||||
{ 8, 4 },
|
||||
};
|
||||
const size_t first_push_count = 6;
|
||||
const size_t capacity = 8;
|
||||
const size_t remaining_count = sizeof(test_items) / sizeof(test_items[0]) - 2;
|
||||
|
||||
TEST_ESP_OK(twai_frame_queue_new(&test_q, capacity, MALLOC_CAP_DEFAULT));
|
||||
|
||||
test_twai_frame_queue_print_header("Initial push before refill");
|
||||
for (size_t i = 0; i < first_push_count; i++) {
|
||||
TEST_ESP_OK(twai_frame_queue_push(test_q, (const twai_frame_t *) &test_items[i], test_items[i].priority, 0));
|
||||
test_twai_frame_queue_print_row(&test_items[i]);
|
||||
}
|
||||
test_twai_frame_queue_print_footer();
|
||||
TEST_ASSERT_EQUAL(first_push_count, twai_frame_queue_get_count(test_q));
|
||||
TEST_ASSERT_EQUAL(capacity - first_push_count, twai_frame_queue_get_free_space(test_q));
|
||||
|
||||
test_twai_frame_queue_print_header("Pop before refill");
|
||||
TEST_ESP_OK(twai_frame_queue_pop(test_q, &item));
|
||||
test_twai_frame_queue_print_row((const twai_frame_queue_test_item_t *) item);
|
||||
TEST_ASSERT_EQUAL_PTR(&test_items[1], item);
|
||||
TEST_ESP_OK(twai_frame_queue_pop(test_q, &item));
|
||||
test_twai_frame_queue_print_row((const twai_frame_queue_test_item_t *) item);
|
||||
TEST_ASSERT_EQUAL_PTR(&test_items[3], item);
|
||||
test_twai_frame_queue_print_footer();
|
||||
TEST_ASSERT_EQUAL(4, twai_frame_queue_get_count(test_q));
|
||||
TEST_ASSERT_EQUAL(4, twai_frame_queue_get_free_space(test_q));
|
||||
|
||||
test_twai_frame_queue_print_header("Push after partial pop");
|
||||
for (size_t i = first_push_count; i < sizeof(test_items) / sizeof(test_items[0]); i++) {
|
||||
TEST_ESP_OK(twai_frame_queue_push(test_q, (const twai_frame_t *) &test_items[i], test_items[i].priority, 0));
|
||||
test_twai_frame_queue_print_row(&test_items[i]);
|
||||
}
|
||||
test_twai_frame_queue_print_footer();
|
||||
TEST_ASSERT_EQUAL(remaining_count, twai_frame_queue_get_count(test_q));
|
||||
TEST_ASSERT_EQUAL(1, twai_frame_queue_get_free_space(test_q));
|
||||
|
||||
test_twai_frame_queue_pop_in_order(test_q, remaining_count, "Pop after refill");
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, twai_frame_queue_pop(test_q, &item));
|
||||
TEST_ASSERT_EQUAL(0, twai_frame_queue_get_count(test_q));
|
||||
TEST_ASSERT_EQUAL(capacity, twai_frame_queue_get_free_space(test_q));
|
||||
TEST_ESP_OK(twai_frame_queue_del(test_q));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
twai_frame_queue_t queue;
|
||||
twai_frame_queue_test_item_t *items;
|
||||
bool *seen;
|
||||
size_t total;
|
||||
atomic_int push_tasks_done;
|
||||
atomic_int pop_count;
|
||||
atomic_bool failed;
|
||||
} twai_frame_queue_concurrent_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
twai_frame_queue_concurrent_ctx_t *ctx;
|
||||
size_t begin;
|
||||
size_t end;
|
||||
} twai_frame_queue_push_args_t;
|
||||
|
||||
static void twai_frame_queue_push_task(void *arg)
|
||||
{
|
||||
twai_frame_queue_push_args_t *args = (twai_frame_queue_push_args_t *) arg;
|
||||
twai_frame_queue_concurrent_ctx_t *ctx = args->ctx;
|
||||
|
||||
printf("%s started\n", pcTaskGetName(NULL));
|
||||
for (size_t i = args->begin; i < args->end; i++) {
|
||||
if (atomic_load(&ctx->failed)) {
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
TEST_ESP_OK(twai_frame_queue_push(ctx->queue, (const twai_frame_t *) &ctx->items[i], ctx->items[i].priority, portMAX_DELAY));
|
||||
vTaskDelay(1);
|
||||
}
|
||||
atomic_fetch_add(&ctx->push_tasks_done, 1);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void twai_frame_queue_pop_task(void *arg)
|
||||
{
|
||||
twai_frame_queue_concurrent_ctx_t *ctx = (twai_frame_queue_concurrent_ctx_t *) arg;
|
||||
|
||||
printf("%s started\n", pcTaskGetName(NULL));
|
||||
const twai_frame_t *item = NULL;
|
||||
|
||||
while (atomic_load(&ctx->pop_count) < (int) ctx->total) {
|
||||
if (twai_frame_queue_pop(ctx->queue, &item) == ESP_OK) {
|
||||
const twai_frame_queue_test_item_t *cur = (const twai_frame_queue_test_item_t *) item;
|
||||
int value = cur->value;
|
||||
|
||||
if (value < 0 || (size_t) value >= ctx->total || ctx->seen[value]) {
|
||||
atomic_store(&ctx->failed, true);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
ctx->seen[value] = true;
|
||||
atomic_fetch_add(&ctx->pop_count, 1);
|
||||
} else if (atomic_load(&ctx->push_tasks_done) == 2 && twai_frame_queue_get_count(ctx->queue) == 0) {
|
||||
break;
|
||||
} else {
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("test twai queue concurrent push and pop", "[twai]")
|
||||
{
|
||||
twai_frame_queue_t test_q = NULL;
|
||||
const size_t item_count = 500;
|
||||
const size_t capacity = 4;
|
||||
twai_frame_queue_test_item_t *test_items = (twai_frame_queue_test_item_t *) malloc(item_count * sizeof(twai_frame_queue_test_item_t));
|
||||
bool seen[item_count] = {};
|
||||
twai_frame_queue_concurrent_ctx_t ctx = {};
|
||||
twai_frame_queue_push_args_t push_args0 = {};
|
||||
twai_frame_queue_push_args_t push_args1 = {};
|
||||
|
||||
for (size_t i = 0; i < item_count; i++) {
|
||||
test_items[i].value = (int) i;
|
||||
test_items[i].priority = (uint32_t)((i * 7 + 3) % 6);
|
||||
}
|
||||
|
||||
ctx.items = test_items;
|
||||
ctx.seen = seen;
|
||||
ctx.total = item_count;
|
||||
|
||||
push_args0.ctx = &ctx;
|
||||
push_args0.begin = 0;
|
||||
push_args0.end = item_count / 2;
|
||||
|
||||
push_args1.ctx = &ctx;
|
||||
push_args1.begin = item_count / 2;
|
||||
push_args1.end = item_count;
|
||||
|
||||
TEST_ESP_OK(twai_frame_queue_new(&test_q, capacity, MALLOC_CAP_DEFAULT));
|
||||
ctx.queue = test_q;
|
||||
|
||||
TEST_ASSERT_EQUAL(pdPASS, xTaskCreate(twai_frame_queue_pop_task, "twai_q_pop", 4096, &ctx, 5, NULL));
|
||||
TEST_ASSERT_EQUAL(pdPASS, xTaskCreate(twai_frame_queue_push_task, "twai_q_push0", 4096, &push_args0, 4, NULL));
|
||||
TEST_ASSERT_EQUAL(pdPASS, xTaskCreate(twai_frame_queue_push_task, "twai_q_push1", 4096, &push_args1, 4, NULL));
|
||||
|
||||
TickType_t start = xTaskGetTickCount();
|
||||
while (atomic_load(&ctx.push_tasks_done) < 2 || atomic_load(&ctx.pop_count) < (int) item_count) {
|
||||
TEST_ASSERT_FALSE(atomic_load(&ctx.failed));
|
||||
if ((xTaskGetTickCount() - start) > pdMS_TO_TICKS(5000)) {
|
||||
TEST_FAIL_MESSAGE("concurrent push/pop timed out");
|
||||
}
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
TEST_ASSERT_FALSE(atomic_load(&ctx.failed));
|
||||
TEST_ASSERT_EQUAL(2, atomic_load(&ctx.push_tasks_done));
|
||||
printf("pop %d items\n", atomic_load(&ctx.pop_count));
|
||||
TEST_ASSERT_EQUAL((int) item_count, atomic_load(&ctx.pop_count));
|
||||
TEST_ASSERT_EQUAL(0, twai_frame_queue_get_count(test_q));
|
||||
TEST_ASSERT_EQUAL(capacity, twai_frame_queue_get_free_space(test_q));
|
||||
for (size_t i = 0; i < item_count; i++) {
|
||||
TEST_ASSERT_TRUE(seen[i]);
|
||||
}
|
||||
|
||||
printf("test finished\n");
|
||||
free(test_items);
|
||||
TEST_ESP_OK(twai_frame_queue_del(test_q));
|
||||
vTaskDelay(10); // wait for tasks to be deleted
|
||||
}
|
||||
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_attr.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_private/twai_frame_queue.h"
|
||||
|
||||
/**
|
||||
* @brief Priority queue item for TWAI frame pointers.
|
||||
*
|
||||
* The queue stores TWAI frame pointers and orders them by priority.
|
||||
* Higher priority values are popped first; equal priorities keep FIFO order.
|
||||
*/
|
||||
typedef struct {
|
||||
const twai_frame_t *data; /**< TWAI frame pointer stored in the queue */
|
||||
uint32_t priority; /**< Higher value is dequeued first */
|
||||
uint64_t seq; /**< Sequence number used to keep FIFO order for equal priority */
|
||||
} twai_frame_queue_item_t;
|
||||
|
||||
/**
|
||||
* @brief Priority queue used by the TWAI driver to schedule queued frames.
|
||||
*/
|
||||
struct twai_frame_queue_s {
|
||||
portMUX_TYPE spinlock; /**< Protects heap storage in task and ISR context */
|
||||
SemaphoreHandle_t spaces_sem; /**< Counts available queue slots */
|
||||
twai_frame_queue_item_t *items; /**< Binary heap storage */
|
||||
size_t capacity; /**< Maximum number of items */
|
||||
size_t count; /**< Current number of queued items */
|
||||
uint64_t next_seq; /**< Next sequence number assigned on push */
|
||||
};
|
||||
|
||||
static inline IRAM_ATTR bool twai_frame_queue_item_greater(const twai_frame_queue_item_t *a, const twai_frame_queue_item_t *b)
|
||||
{
|
||||
if (a->priority != b->priority) {
|
||||
return a->priority > b->priority;
|
||||
}
|
||||
return a->seq < b->seq;
|
||||
}
|
||||
|
||||
static inline IRAM_ATTR void twai_frame_queue_swap_item(twai_frame_queue_item_t *a, twai_frame_queue_item_t *b)
|
||||
{
|
||||
twai_frame_queue_item_t tmp = *a;
|
||||
*a = *b;
|
||||
*b = tmp;
|
||||
}
|
||||
|
||||
static IRAM_ATTR void twai_frame_queue_sift_up(twai_frame_queue_t queue, size_t index)
|
||||
{
|
||||
while (index > 0) {
|
||||
size_t parent = (index - 1) / 2;
|
||||
if (!twai_frame_queue_item_greater(&queue->items[index], &queue->items[parent])) {
|
||||
break;
|
||||
}
|
||||
twai_frame_queue_swap_item(&queue->items[index], &queue->items[parent]);
|
||||
index = parent;
|
||||
}
|
||||
}
|
||||
|
||||
static IRAM_ATTR void twai_frame_queue_sift_down(twai_frame_queue_t queue, size_t index)
|
||||
{
|
||||
while (true) {
|
||||
size_t left = index * 2 + 1;
|
||||
size_t right = left + 1;
|
||||
size_t highest = index;
|
||||
|
||||
if ((left < queue->count) && twai_frame_queue_item_greater(&queue->items[left], &queue->items[highest])) {
|
||||
highest = left;
|
||||
}
|
||||
if ((right < queue->count) && twai_frame_queue_item_greater(&queue->items[right], &queue->items[highest])) {
|
||||
highest = right;
|
||||
}
|
||||
if (highest == index) {
|
||||
break;
|
||||
}
|
||||
twai_frame_queue_swap_item(&queue->items[index], &queue->items[highest]);
|
||||
index = highest;
|
||||
}
|
||||
}
|
||||
|
||||
static inline IRAM_ATTR void twai_frame_queue_push_locked(twai_frame_queue_t queue, const twai_frame_t *data, uint32_t priority)
|
||||
{
|
||||
size_t index = queue->count++;
|
||||
queue->items[index] = (twai_frame_queue_item_t) {
|
||||
.data = data,
|
||||
.priority = priority,
|
||||
.seq = queue->next_seq++,
|
||||
};
|
||||
twai_frame_queue_sift_up(queue, index);
|
||||
}
|
||||
|
||||
static inline IRAM_ATTR bool twai_frame_queue_pop_locked(twai_frame_queue_t queue, const twai_frame_t **data)
|
||||
{
|
||||
if (queue->count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*data = queue->items[0].data;
|
||||
queue->count--;
|
||||
if (queue->count > 0) {
|
||||
queue->items[0] = queue->items[queue->count];
|
||||
twai_frame_queue_sift_down(queue, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
esp_err_t twai_frame_queue_new(twai_frame_queue_t *queue, size_t capacity, uint32_t mem_caps)
|
||||
{
|
||||
if (!queue || !capacity) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
twai_frame_queue_t q_ctx = heap_caps_calloc(1, sizeof(struct twai_frame_queue_s), mem_caps);
|
||||
if (!q_ctx) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
q_ctx->items = heap_caps_calloc(capacity, sizeof(twai_frame_queue_item_t), mem_caps);
|
||||
if (!q_ctx->items) {
|
||||
heap_caps_free(q_ctx);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
q_ctx->spaces_sem = xSemaphoreCreateCountingWithCaps(capacity, capacity, mem_caps);
|
||||
if (!q_ctx->spaces_sem) {
|
||||
heap_caps_free(q_ctx->items);
|
||||
heap_caps_free(q_ctx);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
q_ctx->spinlock = (portMUX_TYPE) portMUX_INITIALIZER_UNLOCKED;
|
||||
q_ctx->capacity = capacity;
|
||||
|
||||
*queue = q_ctx;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t twai_frame_queue_del(twai_frame_queue_t queue)
|
||||
{
|
||||
if (!queue) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (queue->spaces_sem) {
|
||||
vSemaphoreDeleteWithCaps(queue->spaces_sem);
|
||||
}
|
||||
if (queue->items) {
|
||||
heap_caps_free(queue->items);
|
||||
}
|
||||
heap_caps_free(queue);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t twai_frame_queue_push(twai_frame_queue_t queue, const twai_frame_t *data, uint32_t priority, int timeout_ms)
|
||||
{
|
||||
if (!queue || !queue->spaces_sem) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
TickType_t ticks_to_wait = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
if (xSemaphoreTake(queue->spaces_sem, ticks_to_wait) != pdTRUE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&queue->spinlock);
|
||||
twai_frame_queue_push_locked(queue, data, priority);
|
||||
portEXIT_CRITICAL(&queue->spinlock);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t twai_frame_queue_push_from_isr(twai_frame_queue_t queue, const twai_frame_t *data, uint32_t priority, bool *task_woken)
|
||||
{
|
||||
if (!queue || !queue->spaces_sem) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
if (xSemaphoreTakeFromISR(queue->spaces_sem, (BaseType_t *)task_woken) != pdTRUE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
portENTER_CRITICAL_ISR(&queue->spinlock);
|
||||
twai_frame_queue_push_locked(queue, data, priority);
|
||||
portEXIT_CRITICAL_ISR(&queue->spinlock);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t twai_frame_queue_push_safe(twai_frame_queue_t queue, const twai_frame_t *data, uint32_t priority, int timeout_ms)
|
||||
{
|
||||
if (xPortInIsrContext()) {
|
||||
bool task_woken = false;
|
||||
esp_err_t ret = twai_frame_queue_push_from_isr(queue, data, priority, &task_woken);
|
||||
if (task_woken) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
TickType_t ticks_to_wait = (timeout_ms == -1) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
return twai_frame_queue_push(queue, data, priority, ticks_to_wait);
|
||||
}
|
||||
|
||||
esp_err_t twai_frame_queue_pop(twai_frame_queue_t queue, const twai_frame_t **data)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
if (!queue || !queue->spaces_sem) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&queue->spinlock);
|
||||
ret = twai_frame_queue_pop_locked(queue, data);
|
||||
portEXIT_CRITICAL(&queue->spinlock);
|
||||
|
||||
if (ret) {
|
||||
xSemaphoreGive(queue->spaces_sem);
|
||||
}
|
||||
return ret ? ESP_OK : ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t twai_frame_queue_pop_from_isr(twai_frame_queue_t queue, const twai_frame_t **data, bool *task_woken)
|
||||
{
|
||||
bool ret;
|
||||
|
||||
if (!queue || !queue->spaces_sem) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
portENTER_CRITICAL_ISR(&queue->spinlock);
|
||||
ret = twai_frame_queue_pop_locked(queue, data);
|
||||
portEXIT_CRITICAL_ISR(&queue->spinlock);
|
||||
|
||||
if (ret) {
|
||||
xSemaphoreGiveFromISR(queue->spaces_sem, (BaseType_t *)task_woken);
|
||||
}
|
||||
return ret ? ESP_OK : ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
esp_err_t twai_frame_queue_pop_safe(twai_frame_queue_t queue, const twai_frame_t **data)
|
||||
{
|
||||
if (xPortInIsrContext()) {
|
||||
bool task_woken = false;
|
||||
esp_err_t ret = twai_frame_queue_pop_from_isr(queue, data, &task_woken);
|
||||
if (task_woken) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return twai_frame_queue_pop(queue, data);
|
||||
}
|
||||
|
||||
size_t twai_frame_queue_get_count(twai_frame_queue_t queue)
|
||||
{
|
||||
size_t count;
|
||||
|
||||
if (!queue || !queue->spaces_sem) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&queue->spinlock);
|
||||
count = queue->count;
|
||||
portEXIT_CRITICAL(&queue->spinlock);
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t twai_frame_queue_get_free_space(twai_frame_queue_t queue)
|
||||
{
|
||||
size_t count;
|
||||
|
||||
if (!queue || !queue->spaces_sem) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&queue->spinlock);
|
||||
count = queue->count;
|
||||
portEXIT_CRITICAL(&queue->spinlock);
|
||||
return queue->capacity - count;
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <string.h>
|
||||
#include <sys/cdefs.h>
|
||||
#include <sys/lock.h>
|
||||
#include <sys/param.h>
|
||||
#include <stdatomic.h>
|
||||
#include "sdkconfig.h"
|
||||
#if CONFIG_TWAI_ENABLE_DEBUG_LOG
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -61,6 +61,7 @@ static uint32_t twai_ll_get_brp_max(void);
|
||||
#define TWAI_LL_BRP_DIV_THRESH 128
|
||||
#define TWAI_LL_TSEG1_MIN 1
|
||||
#define TWAI_LL_TSEG2_MIN 1
|
||||
#define TWAI_LL_PROP_MAX 0 //hardware don't support prop_seg
|
||||
#define TWAI_LL_TSEG1_MAX 16 //the max register value
|
||||
#define TWAI_LL_TSEG2_MAX 8
|
||||
#define TWAI_LL_SJW_MAX 4
|
||||
@@ -700,6 +701,17 @@ static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_
|
||||
|
||||
/* ------------------------- TX/RX Buffer Registers ------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Get the number of TX buffers that are preset in the hardware.
|
||||
*
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @return The number of TX buffers available.
|
||||
*/
|
||||
static inline uint32_t twai_ll_get_tx_buffer_total(twai_dev_t *hw)
|
||||
{
|
||||
return 1; // only one TX buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a formatted TWAI frame into TX buffer for transmission
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -38,6 +38,7 @@ extern "C" {
|
||||
#define TWAI_LL_BRP_MAX 16384
|
||||
#define TWAI_LL_TSEG1_MIN 1
|
||||
#define TWAI_LL_TSEG2_MIN 1
|
||||
#define TWAI_LL_PROP_MAX 0 //hardware don't support prop_seg
|
||||
#define TWAI_LL_TSEG1_MAX 16 //the max register value
|
||||
#define TWAI_LL_TSEG2_MAX 8
|
||||
#define TWAI_LL_SJW_MAX 4
|
||||
@@ -626,6 +627,17 @@ static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_
|
||||
|
||||
/* ------------------------- TX/RX Buffer Registers ------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Get the number of TX buffers that are preset in the hardware.
|
||||
*
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @return The number of TX buffers available.
|
||||
*/
|
||||
static inline uint32_t twai_ll_get_tx_buffer_total(twai_dev_t *hw)
|
||||
{
|
||||
return 1; // only one TX buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a formatted TWAI frame into TX buffer for transmission
|
||||
*
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
#define TWAIFD_LL_TX_CMD_READY TWAIFD_TXCR // Set tx buffer to "Ready" state
|
||||
#define TWAIFD_LL_TX_CMD_ABORT TWAIFD_TXCA // Set tx buffer to "Aborted" state
|
||||
|
||||
#define TWAIFD_LL_TX_STATUS_SUCCESS 0x4 // TX buffer transmitted successfully
|
||||
#define TWAIFD_LL_TX_STATUS_FAILED 0x6 // TX buffer transmission failed
|
||||
#define TWAIFD_LL_TX_STATUS_ABORTED 0x7 // TX buffer transmission aborted
|
||||
|
||||
#define TWAIFD_LL_HW_CMD_RST_ERR_CNT TWAIFD_ERCRST // Error Counters Reset
|
||||
#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
|
||||
@@ -665,11 +669,10 @@ static inline uint32_t twaifd_ll_get_tx_buffer_total(twaifd_dev_t *hw)
|
||||
* @param buffer_idx Index of the TX buffer (0-7).
|
||||
* @return The status of the selected TX buffer.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t twaifd_ll_get_tx_buffer_status(twaifd_dev_t *hw, uint8_t buffer_idx)
|
||||
{
|
||||
HAL_ASSERT(buffer_idx < twaifd_ll_get_tx_buffer_total(hw)); // Ensure buffer index is valid
|
||||
uint32_t reg_val = hw->tx_status.val;
|
||||
return reg_val & (TWAIFD_TX2S_V << (TWAIFD_TX2S_S * buffer_idx)); // Get status for buffer
|
||||
return (hw->tx_status.val >> (TWAIFD_TX2S_S * buffer_idx)) & TWAIFD_TX2S_V;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -700,7 +703,6 @@ static inline void twaifd_ll_set_tx_buffer_cmd(twaifd_dev_t *hw, uint8_t buffer_
|
||||
*/
|
||||
static inline void twaifd_ll_set_tx_buffer_priority(twaifd_dev_t *hw, uint8_t buffer_idx, uint32_t priority)
|
||||
{
|
||||
HAL_ASSERT(buffer_idx < twaifd_ll_get_tx_buffer_total(hw)); // Ensure buffer index is valid
|
||||
uint32_t reg_val = hw->tx_priority.val;
|
||||
reg_val &= ~(TWAIFD_TXT1P_V << (TWAIFD_TXT2P_S * buffer_idx)); // Clear old priority
|
||||
reg_val |= priority << (TWAIFD_TXT2P_S * buffer_idx); // Set new priority
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -33,6 +33,7 @@ extern "C" {
|
||||
#define TWAI_LL_BRP_MAX 32768
|
||||
#define TWAI_LL_TSEG1_MIN 1
|
||||
#define TWAI_LL_TSEG2_MIN 1
|
||||
#define TWAI_LL_PROP_MAX 0 //hardware don't support prop_seg
|
||||
#define TWAI_LL_TSEG1_MAX (TWAI_TIME_SEGMENT1 + 1)
|
||||
#define TWAI_LL_TSEG2_MAX (TWAI_TIME_SEGMENT2 + 1)
|
||||
#define TWAI_LL_SJW_MAX (TWAI_SYNC_JUMP_WIDTH + 1)
|
||||
@@ -636,6 +637,17 @@ static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_
|
||||
|
||||
/* ------------------------- TX/RX Buffer Registers ------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Get the number of TX buffers that are preset in the hardware.
|
||||
*
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @return The number of TX buffers available.
|
||||
*/
|
||||
static inline uint32_t twai_ll_get_tx_buffer_total(twai_dev_t *hw)
|
||||
{
|
||||
return 1; // only one TX buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a formatted TWAI frame into TX buffer for transmission
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -33,6 +33,7 @@ extern "C" {
|
||||
#define TWAI_LL_BRP_MAX 32768
|
||||
#define TWAI_LL_TSEG1_MIN 1
|
||||
#define TWAI_LL_TSEG2_MIN 1
|
||||
#define TWAI_LL_PROP_MAX 0 //hardware don't support prop_seg
|
||||
#define TWAI_LL_TSEG1_MAX (TWAI_TIME_SEGMENT1 + 1)
|
||||
#define TWAI_LL_TSEG2_MAX (TWAI_TIME_SEGMENT2 + 1)
|
||||
#define TWAI_LL_SJW_MAX (TWAI_SYNC_JUMP_WIDTH + 1)
|
||||
@@ -614,6 +615,17 @@ static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_
|
||||
|
||||
/* ------------------------- TX/RX Buffer Registers ------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Get the number of TX buffers that are preset in the hardware.
|
||||
*
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @return The number of TX buffers available.
|
||||
*/
|
||||
static inline uint32_t twai_ll_get_tx_buffer_total(twai_dev_t *hw)
|
||||
{
|
||||
return 1; // only one TX buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a formatted TWAI frame into TX buffer for transmission
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -33,6 +33,7 @@ extern "C" {
|
||||
#define TWAI_LL_BRP_MAX 32768
|
||||
#define TWAI_LL_TSEG1_MIN 1
|
||||
#define TWAI_LL_TSEG2_MIN 1
|
||||
#define TWAI_LL_PROP_MAX 0 //hardware don't support prop_seg
|
||||
#define TWAI_LL_TSEG1_MAX (TWAI_TIME_SEGMENT1 + 1)
|
||||
#define TWAI_LL_TSEG2_MAX (TWAI_TIME_SEGMENT2 + 1)
|
||||
#define TWAI_LL_SJW_MAX (TWAI_SYNC_JUMP_WIDTH + 1)
|
||||
@@ -614,6 +615,17 @@ static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_
|
||||
|
||||
/* ------------------------- TX/RX Buffer Registers ------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Get the number of TX buffers that are preset in the hardware.
|
||||
*
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @return The number of TX buffers available.
|
||||
*/
|
||||
static inline uint32_t twai_ll_get_tx_buffer_total(twai_dev_t *hw)
|
||||
{
|
||||
return 1; // only one TX buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a formatted TWAI frame into TX buffer for transmission
|
||||
*
|
||||
|
||||
@@ -48,6 +48,10 @@
|
||||
#define TWAIFD_LL_TX_CMD_READY TWAIFD_TXCR // Set tx buffer to "Ready" state
|
||||
#define TWAIFD_LL_TX_CMD_ABORT TWAIFD_TXCA // Set tx buffer to "Aborted" state
|
||||
|
||||
#define TWAIFD_LL_TX_STATUS_SUCCESS 0x4 // TX buffer transmitted successfully
|
||||
#define TWAIFD_LL_TX_STATUS_FAILED 0x6 // TX buffer transmission failed
|
||||
#define TWAIFD_LL_TX_STATUS_ABORTED 0x7 // TX buffer transmission aborted
|
||||
|
||||
#define TWAIFD_LL_HW_CMD_RST_ERR_CNT TWAIFD_ERCRST // Error Counters Reset
|
||||
#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
|
||||
@@ -664,11 +668,10 @@ static inline uint32_t twaifd_ll_get_tx_buffer_total(twaifd_dev_t *hw)
|
||||
* @param buffer_idx Index of the TX buffer (0-7).
|
||||
* @return The status of the selected TX buffer.
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t twaifd_ll_get_tx_buffer_status(twaifd_dev_t *hw, uint8_t buffer_idx)
|
||||
{
|
||||
HAL_ASSERT(buffer_idx < twaifd_ll_get_tx_buffer_total(hw)); // Ensure buffer index is valid
|
||||
uint32_t reg_val = hw->tx_status.val;
|
||||
return reg_val & (TWAIFD_TX2S_V << (TWAIFD_TX2S_S * buffer_idx)); // Get status for buffer
|
||||
return (hw->tx_status.val >> (TWAIFD_TX2S_S * buffer_idx)) & TWAIFD_TX2S_V;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -699,7 +702,6 @@ static inline void twaifd_ll_set_tx_buffer_cmd(twaifd_dev_t *hw, uint8_t buffer_
|
||||
*/
|
||||
static inline void twaifd_ll_set_tx_buffer_priority(twaifd_dev_t *hw, uint8_t buffer_idx, uint32_t priority)
|
||||
{
|
||||
HAL_ASSERT(buffer_idx < twaifd_ll_get_tx_buffer_total(hw)); // Ensure buffer index is valid
|
||||
uint32_t reg_val = hw->tx_priority.val;
|
||||
reg_val &= ~(TWAIFD_TXT1P_V << (TWAIFD_TXT2P_S * buffer_idx)); // Clear old priority
|
||||
reg_val |= priority << (TWAIFD_TXT2P_S * buffer_idx); // Set new priority
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -33,6 +33,7 @@ extern "C" {
|
||||
#define TWAI_LL_BRP_MAX 32768
|
||||
#define TWAI_LL_TSEG1_MIN 1
|
||||
#define TWAI_LL_TSEG2_MIN 1
|
||||
#define TWAI_LL_PROP_MAX 0 //hardware don't support prop_seg
|
||||
#define TWAI_LL_TSEG1_MAX (TWAI_TIME_SEGMENT1 + 1)
|
||||
#define TWAI_LL_TSEG2_MAX (TWAI_TIME_SEGMENT2 + 1)
|
||||
#define TWAI_LL_SJW_MAX (TWAI_SYNC_JUMP_WIDTH + 1)
|
||||
@@ -679,6 +680,17 @@ static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_
|
||||
|
||||
/* ------------------------- TX/RX Buffer Registers ------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Get the number of TX buffers that are preset in the hardware.
|
||||
*
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @return The number of TX buffers available.
|
||||
*/
|
||||
static inline uint32_t twai_ll_get_tx_buffer_total(twai_dev_t *hw)
|
||||
{
|
||||
return 1; // only one TX buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a formatted TWAI frame into TX buffer for transmission
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -38,6 +38,7 @@ extern "C" {
|
||||
#define TWAI_LL_BRP_MAX 32768
|
||||
#define TWAI_LL_TSEG1_MIN 1
|
||||
#define TWAI_LL_TSEG2_MIN 1
|
||||
#define TWAI_LL_PROP_MAX 0 //hardware don't support prop_seg
|
||||
#define TWAI_LL_TSEG1_MAX 16 //the max register value
|
||||
#define TWAI_LL_TSEG2_MAX 8
|
||||
#define TWAI_LL_SJW_MAX 4
|
||||
@@ -629,6 +630,17 @@ static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_
|
||||
|
||||
/* ------------------------- TX/RX Buffer Registers ------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Get the number of TX buffers that are preset in the hardware.
|
||||
*
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @return The number of TX buffers available.
|
||||
*/
|
||||
static inline uint32_t twai_ll_get_tx_buffer_total(twai_dev_t *hw)
|
||||
{
|
||||
return 1; // only one TX buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a formatted TWAI frame into TX buffer for transmission
|
||||
*
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -38,6 +38,7 @@ extern "C" {
|
||||
#define TWAI_LL_BRP_MAX 16384
|
||||
#define TWAI_LL_TSEG1_MIN 1
|
||||
#define TWAI_LL_TSEG2_MIN 1
|
||||
#define TWAI_LL_PROP_MAX 0 //hardware don't support prop_seg
|
||||
#define TWAI_LL_TSEG1_MAX 16 //the max register value
|
||||
#define TWAI_LL_TSEG2_MAX 8
|
||||
#define TWAI_LL_SJW_MAX 4
|
||||
@@ -626,6 +627,17 @@ static inline void twai_ll_set_acc_filter(twai_dev_t *hw, uint32_t code, uint32_
|
||||
|
||||
/* ------------------------- TX/RX Buffer Registers ------------------------- */
|
||||
|
||||
/**
|
||||
* @brief Get the number of TX buffers that are preset in the hardware.
|
||||
*
|
||||
* @param hw Pointer to the TWAI-FD device hardware.
|
||||
* @return The number of TX buffers available.
|
||||
*/
|
||||
static inline uint32_t twai_ll_get_tx_buffer_total(twai_dev_t *hw)
|
||||
{
|
||||
return 1; // only one TX buffer
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy a formatted TWAI frame into TX buffer for transmission
|
||||
*
|
||||
|
||||
@@ -50,9 +50,29 @@ typedef union twai_ll_frame_buffer_t twai_hal_frame_t;
|
||||
#define TWAI_HAL_EVENT_BUS_ERR (1 << 7)
|
||||
#define TWAI_HAL_EVENT_ARB_LOST (1 << 8)
|
||||
#define TWAI_HAL_EVENT_RX_BUFF_FRAME (1 << 9)
|
||||
#define TWAI_HAL_EVENT_TX_BUFF_FREE (1 << 10)
|
||||
#define TWAI_HAL_EVENT_NEED_PERIPH_RESET (1 << 11)
|
||||
#define TWAI_HAL_EVENT_TX_SUCCESS (1 << 12)
|
||||
#define TWAI_HAL_EVENT_NEED_PERIPH_RESET (1 << 10)
|
||||
#define TWAI_HAL_EVENT_TX0_DONE (1 << 11)
|
||||
#define TWAI_HAL_EVENT_TX0_SUCCESS (1 << 12)
|
||||
#define TWAI_HAL_EVENT_TX1_DONE (1 << 13)
|
||||
#define TWAI_HAL_EVENT_TX1_SUCCESS (1 << 14)
|
||||
#define TWAI_HAL_EVENT_TX2_DONE (1 << 15)
|
||||
#define TWAI_HAL_EVENT_TX2_SUCCESS (1 << 16)
|
||||
#define TWAI_HAL_EVENT_TX3_DONE (1 << 17)
|
||||
#define TWAI_HAL_EVENT_TX3_SUCCESS (1 << 18)
|
||||
#define TWAI_HAL_EVENT_TX4_DONE (1 << 19)
|
||||
#define TWAI_HAL_EVENT_TX4_SUCCESS (1 << 20)
|
||||
#define TWAI_HAL_EVENT_TX5_DONE (1 << 21)
|
||||
#define TWAI_HAL_EVENT_TX5_SUCCESS (1 << 22)
|
||||
#define TWAI_HAL_EVENT_TX6_DONE (1 << 23)
|
||||
#define TWAI_HAL_EVENT_TX6_SUCCESS (1 << 24)
|
||||
#define TWAI_HAL_EVENT_TX7_DONE (1 << 25)
|
||||
#define TWAI_HAL_EVENT_TX7_SUCCESS (1 << 26)
|
||||
#define TWAI_HAL_TX_BUFFER_SLOT_NUM 8 // support up to 8 TX slots in hal layer
|
||||
|
||||
#define TWAI_HAL_EVENT_TX_DONE_MASK (TWAI_HAL_EVENT_TX0_DONE | TWAI_HAL_EVENT_TX1_DONE | TWAI_HAL_EVENT_TX2_DONE | TWAI_HAL_EVENT_TX3_DONE | \
|
||||
TWAI_HAL_EVENT_TX4_DONE | TWAI_HAL_EVENT_TX5_DONE | TWAI_HAL_EVENT_TX6_DONE | TWAI_HAL_EVENT_TX7_DONE)
|
||||
#define TWAI_HAL_EVENT_TX_DONE_SLOT(buffer_idx) (TWAI_HAL_EVENT_TX0_DONE << ((buffer_idx) * 2))
|
||||
#define TWAI_HAL_EVENT_TX_SUCC_SLOT(buffer_idx) (TWAI_HAL_EVENT_TX0_SUCCESS << ((buffer_idx) * 2))
|
||||
|
||||
typedef struct {
|
||||
twai_soc_handle_t dev; // TWAI SOC layer handle (i.e. register base address)
|
||||
@@ -61,6 +81,7 @@ typedef struct {
|
||||
uint32_t timer_overflow_cnt;
|
||||
twai_error_flags_t errors;
|
||||
uint8_t sja1000_filter_id_type; // hardware don't check id type, check in software, 0:no_filter, 1: std_id_only, 2: ext_id_only
|
||||
uint8_t tx_buffer_num;
|
||||
int8_t retry_cnt;
|
||||
bool enable_self_test;
|
||||
bool enable_loopback;
|
||||
@@ -296,6 +317,14 @@ static inline twai_error_flags_t twai_hal_get_err_flags(twai_hal_context_t *hal_
|
||||
*/
|
||||
uint32_t twai_hal_get_rx_msg_count(twai_hal_context_t *hal_ctx);
|
||||
|
||||
/**
|
||||
* @brief Get the number of TX buffers supported by the hardware
|
||||
*
|
||||
* @param hal_ctx Context of the HAL layer
|
||||
* @return TX buffer count
|
||||
*/
|
||||
#define twai_hal_get_tx_slot_num(hal_ctx) (hal_ctx->tx_buffer_num)
|
||||
|
||||
/**
|
||||
* @brief TWAI hal transaction description type
|
||||
*/
|
||||
|
||||
@@ -50,6 +50,7 @@ bool twai_hal_init(twai_hal_context_t *hal_ctx, const twai_hal_config_t *config)
|
||||
if (!twai_ll_is_in_reset_mode(hal_ctx->dev)) { //Must enter reset mode to write to config registers
|
||||
return false;
|
||||
}
|
||||
hal_ctx->tx_buffer_num = twai_ll_get_tx_buffer_total(hal_ctx->dev);
|
||||
#if TWAI_LL_HAS_RX_FRAME_ISSUE || TWAI_LL_HAS_RX_FIFO_ISSUE
|
||||
hal_ctx->errata_ctx = (twai_hal_errata_ctx_t *)(hal_ctx + 1); //errata context is place at end of hal_ctx
|
||||
#endif
|
||||
@@ -238,10 +239,10 @@ static inline uint32_t twai_hal_decode_interrupt(twai_hal_context_t *hal_ctx)
|
||||
#else
|
||||
if (interrupts & TWAI_LL_INTR_TI) {
|
||||
#endif
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_TX_BUFF_FREE);
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_TX0_DONE);
|
||||
TWAI_HAL_CLEAR_BITS(state_flags, TWAI_HAL_STATE_FLAG_TX_BUFF_OCCUPIED);
|
||||
if (status & TWAI_LL_STATUS_TCS) {
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_TX_SUCCESS);
|
||||
TWAI_HAL_SET_BITS(events, TWAI_HAL_EVENT_TX0_SUCCESS);
|
||||
}
|
||||
}
|
||||
//Error Passive Interrupt on transition from error active to passive or vice versa
|
||||
|
||||
@@ -22,6 +22,7 @@ bool twai_hal_init(twai_hal_context_t *hal_ctx, const twai_hal_config_t *config)
|
||||
hal_ctx->enable_listen_only = config->enable_listen_only;
|
||||
|
||||
twaifd_ll_reset(hal_ctx->dev);
|
||||
hal_ctx->tx_buffer_num = twaifd_ll_get_tx_buffer_total(hal_ctx->dev);
|
||||
twaifd_ll_enable_hw(hal_ctx->dev, false); //mode should be changed under disabled
|
||||
twaifd_ll_set_mode(hal_ctx->dev, config->enable_listen_only, config->enable_self_test, config->enable_loopback);
|
||||
twaifd_ll_set_tx_retrans_limit(hal_ctx->dev, config->retry_cnt);
|
||||
@@ -222,9 +223,20 @@ uint32_t twai_hal_get_events(twai_hal_context_t *hal_ctx)
|
||||
hal_ctx->timer_overflow_cnt ++;
|
||||
}
|
||||
if (int_stat & (TWAIFD_LL_INTR_TX_DONE)) {
|
||||
hal_events |= TWAI_HAL_EVENT_TX_BUFF_FREE;
|
||||
if (int_stat & TWAIFD_LL_INTR_TX_FRAME) {
|
||||
hal_events |= TWAI_HAL_EVENT_TX_SUCCESS;
|
||||
for (uint32_t i = 0; i < MIN(hal_ctx->tx_buffer_num, TWAI_HAL_TX_BUFFER_SLOT_NUM); i++) {
|
||||
uint32_t tx_status = twaifd_ll_get_tx_buffer_status(hal_ctx->dev, i);
|
||||
switch (tx_status) {
|
||||
case TWAIFD_LL_TX_STATUS_SUCCESS:
|
||||
hal_events |= TWAI_HAL_EVENT_TX_SUCC_SLOT(i);
|
||||
__attribute__((fallthrough)); // success event must be a done event, just fallthrough
|
||||
case TWAIFD_LL_TX_STATUS_FAILED:
|
||||
case TWAIFD_LL_TX_STATUS_ABORTED:
|
||||
hal_events |= TWAI_HAL_EVENT_TX_DONE_SLOT(i);
|
||||
twaifd_ll_set_tx_buffer_cmd(hal_ctx->dev, i, TWAIFD_LL_TX_CMD_EMPTY); // clear buffer to empty state
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (int_stat & TWAIFD_LL_INTR_RX_NOT_EMPTY) {
|
||||
|
||||
Reference in New Issue
Block a user