mirror of
https://github.com/espressif/esp-idf.git
synced 2025-10-03 02:20:57 +02:00
Merge branch 'feat/twai_add_wait_done_api' into 'master'
feat(driver_twai): add tx wait all done api Closes IDF-13417 See merge request espressif/esp-idf!40616
This commit is contained in:
@@ -65,7 +65,7 @@ uint32_t twai_node_timing_calc_param(const uint32_t source_freq, const twai_timi
|
|||||||
esp_err_t twai_node_enable(twai_node_handle_t node)
|
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, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null handle");
|
||||||
ESP_RETURN_ON_FALSE(node->enable, ESP_ERR_NOT_SUPPORTED, TAG, "enable func null");
|
ESP_RETURN_ON_FALSE(node->enable, ESP_ERR_NOT_SUPPORTED, TAG, "enable is not supported");
|
||||||
|
|
||||||
return node->enable(node);
|
return node->enable(node);
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ esp_err_t twai_node_enable(twai_node_handle_t node)
|
|||||||
esp_err_t twai_node_disable(twai_node_handle_t 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, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null handle");
|
||||||
ESP_RETURN_ON_FALSE(node->disable, ESP_ERR_NOT_SUPPORTED, TAG, "disable func null");
|
ESP_RETURN_ON_FALSE(node->disable, ESP_ERR_NOT_SUPPORTED, TAG, "disable is not supported");
|
||||||
|
|
||||||
return node->disable(node);
|
return node->disable(node);
|
||||||
}
|
}
|
||||||
@@ -81,7 +81,7 @@ esp_err_t twai_node_disable(twai_node_handle_t node)
|
|||||||
esp_err_t twai_node_delete(twai_node_handle_t 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, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null handle");
|
||||||
ESP_RETURN_ON_FALSE(node->del, ESP_ERR_NOT_SUPPORTED, TAG, "delete func null");
|
ESP_RETURN_ON_FALSE(node->del, ESP_ERR_NOT_SUPPORTED, TAG, "delete is not supported");
|
||||||
|
|
||||||
return node->del(node);
|
return node->del(node);
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ esp_err_t twai_node_delete(twai_node_handle_t 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_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 && 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");
|
ESP_RETURN_ON_FALSE(node->config_mask_filter, ESP_ERR_NOT_SUPPORTED, TAG, "config_mask_filter is not supported");
|
||||||
|
|
||||||
return node->config_mask_filter(node, filter_id, mask_cfg);
|
return node->config_mask_filter(node, filter_id, mask_cfg);
|
||||||
}
|
}
|
||||||
@@ -97,7 +97,7 @@ esp_err_t twai_node_config_mask_filter(twai_node_handle_t node, uint8_t filter_i
|
|||||||
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_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 && 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");
|
ESP_RETURN_ON_FALSE(node->config_range_filter, ESP_ERR_NOT_SUPPORTED, TAG, "config_range_filter is not supported");
|
||||||
|
|
||||||
return node->config_range_filter(node, filter_id, range_cfg);
|
return node->config_range_filter(node, filter_id, range_cfg);
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,7 @@ esp_err_t twai_node_config_range_filter(twai_node_handle_t node, uint8_t filter_
|
|||||||
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_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 && (bit_timing || data_timing), ESP_ERR_INVALID_ARG, TAG, "invalid argument: null");
|
||||||
ESP_RETURN_ON_FALSE(node->reconfig_timing, ESP_ERR_NOT_SUPPORTED, TAG, "reconfig_timing func null");
|
ESP_RETURN_ON_FALSE(node->reconfig_timing, ESP_ERR_NOT_SUPPORTED, TAG, "reconfig_timing is not supported");
|
||||||
|
|
||||||
return node->reconfig_timing(node, bit_timing, data_timing);
|
return node->reconfig_timing(node, bit_timing, data_timing);
|
||||||
}
|
}
|
||||||
@@ -113,7 +113,7 @@ esp_err_t twai_node_reconfig_timing(twai_node_handle_t node, const twai_timing_a
|
|||||||
esp_err_t twai_node_register_event_callbacks(twai_node_handle_t node, const twai_event_callbacks_t *cbs, void *user_data)
|
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 && 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");
|
ESP_RETURN_ON_FALSE(node->register_cbs, ESP_ERR_NOT_SUPPORTED, TAG, "register_cbs is not supported");
|
||||||
|
|
||||||
return node->register_cbs(node, cbs, user_data);
|
return node->register_cbs(node, cbs, user_data);
|
||||||
}
|
}
|
||||||
@@ -121,7 +121,7 @@ esp_err_t twai_node_register_event_callbacks(twai_node_handle_t node, const twai
|
|||||||
esp_err_t twai_node_recover(twai_node_handle_t node)
|
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, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null handle");
|
||||||
ESP_RETURN_ON_FALSE(node->recover, ESP_ERR_NOT_SUPPORTED, TAG, "recover func null");
|
ESP_RETURN_ON_FALSE(node->recover, ESP_ERR_NOT_SUPPORTED, TAG, "recover is not supported");
|
||||||
|
|
||||||
return node->recover(node);
|
return node->recover(node);
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ esp_err_t twai_node_recover(twai_node_handle_t 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_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, 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");
|
ESP_RETURN_ON_FALSE(node->get_info, ESP_ERR_NOT_SUPPORTED, TAG, "get_info is not supported");
|
||||||
|
|
||||||
return node->get_info(node, status_ret, statistics_ret);
|
return node->get_info(node, status_ret, statistics_ret);
|
||||||
}
|
}
|
||||||
@@ -137,16 +137,24 @@ esp_err_t twai_node_get_info(twai_node_handle_t node, twai_node_status_t *status
|
|||||||
esp_err_t twai_node_transmit(twai_node_handle_t node, const twai_frame_t *frame, int timeout_ms)
|
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 && frame, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null");
|
||||||
ESP_RETURN_ON_FALSE(node->transmit, ESP_ERR_NOT_SUPPORTED, TAG, "transmit func null");
|
ESP_RETURN_ON_FALSE(node->transmit, ESP_ERR_NOT_SUPPORTED, TAG, "transmit is not supported");
|
||||||
|
|
||||||
return node->transmit(node, frame, timeout_ms);
|
return node->transmit(node, frame, timeout_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t twai_node_transmit_wait_all_done(twai_node_handle_t node, int timeout_ms)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(node, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null handle");
|
||||||
|
ESP_RETURN_ON_FALSE(node->transmit_wait_done, ESP_ERR_NOT_SUPPORTED, TAG, "transmit_wait_done is not supported");
|
||||||
|
|
||||||
|
return node->transmit_wait_done(node, timeout_ms);
|
||||||
|
}
|
||||||
|
|
||||||
esp_err_t twai_node_receive_from_isr(twai_node_handle_t node, twai_frame_t *rx_frame)
|
esp_err_t twai_node_receive_from_isr(twai_node_handle_t node, twai_frame_t *rx_frame)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE_ISR(node && rx_frame, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null");
|
ESP_RETURN_ON_FALSE_ISR(node && rx_frame, ESP_ERR_INVALID_ARG, TAG, "invalid argument: null");
|
||||||
ESP_RETURN_ON_FALSE_ISR((rx_frame->buffer_len == 0) || esp_ptr_in_dram(rx_frame->buffer) || esp_ptr_external_ram(rx_frame->buffer), ESP_ERR_INVALID_ARG, TAG, "invalid 'rx_frame->buffer' pointer or buffer_len");
|
ESP_RETURN_ON_FALSE_ISR((rx_frame->buffer_len == 0) || esp_ptr_in_dram(rx_frame->buffer) || esp_ptr_external_ram(rx_frame->buffer), ESP_ERR_INVALID_ARG, TAG, "invalid 'rx_frame->buffer' pointer or buffer_len");
|
||||||
ESP_RETURN_ON_FALSE_ISR(node->receive_isr, ESP_ERR_NOT_SUPPORTED, TAG, "receive func null");
|
ESP_RETURN_ON_FALSE_ISR(node->receive_isr, ESP_ERR_NOT_SUPPORTED, TAG, "receive is not supported");
|
||||||
|
|
||||||
return node->receive_isr(node, rx_frame);
|
return node->receive_isr(node, rx_frame);
|
||||||
}
|
}
|
||||||
|
@@ -60,6 +60,8 @@ static void _twai_rcc_clock_sel(uint8_t ctrlr_id, twai_clock_source_t clock)
|
|||||||
}
|
}
|
||||||
#endif //SOC_TWAI_SUPPORT_FD
|
#endif //SOC_TWAI_SUPPORT_FD
|
||||||
|
|
||||||
|
#define TWAI_IDLE_EVENT_BIT BIT0 //event used for tx_wait_all_done
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
struct twai_node_base api_base;
|
struct twai_node_base api_base;
|
||||||
int ctrlr_id;
|
int ctrlr_id;
|
||||||
@@ -67,6 +69,7 @@ typedef struct {
|
|||||||
twai_hal_context_t *hal;
|
twai_hal_context_t *hal;
|
||||||
intr_handle_t intr_hdl;
|
intr_handle_t intr_hdl;
|
||||||
QueueHandle_t tx_mount_queue;
|
QueueHandle_t tx_mount_queue;
|
||||||
|
EventGroupHandle_t event_group;
|
||||||
twai_clock_source_t curr_clk_src;
|
twai_clock_source_t curr_clk_src;
|
||||||
uint32_t valid_fd_timing;
|
uint32_t valid_fd_timing;
|
||||||
twai_event_callbacks_t cbs;
|
twai_event_callbacks_t cbs;
|
||||||
@@ -239,6 +242,7 @@ static void _node_isr_main(void *arg)
|
|||||||
_node_start_trans(twai_ctx);
|
_node_start_trans(twai_ctx);
|
||||||
} else {
|
} else {
|
||||||
atomic_store(&twai_ctx->hw_busy, false);
|
atomic_store(&twai_ctx->hw_busy, false);
|
||||||
|
xEventGroupSetBitsFromISR(twai_ctx->event_group, TWAI_IDLE_EVENT_BIT, &do_yield);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -284,6 +288,10 @@ static void _node_isr_main(void *arg)
|
|||||||
_node_start_trans(twai_ctx);
|
_node_start_trans(twai_ctx);
|
||||||
} else {
|
} else {
|
||||||
atomic_store(&twai_ctx->hw_busy, false);
|
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
|
||||||
|
xEventGroupSetBitsFromISR(twai_ctx->event_group, TWAI_IDLE_EVENT_BIT, &do_yield);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -305,6 +313,9 @@ static void _node_destroy(twai_onchip_ctx_t *twai_ctx)
|
|||||||
if (twai_ctx->tx_mount_queue) {
|
if (twai_ctx->tx_mount_queue) {
|
||||||
vQueueDeleteWithCaps(twai_ctx->tx_mount_queue);
|
vQueueDeleteWithCaps(twai_ctx->tx_mount_queue);
|
||||||
}
|
}
|
||||||
|
if (twai_ctx->event_group) {
|
||||||
|
vEventGroupDeleteWithCaps(twai_ctx->event_group);
|
||||||
|
}
|
||||||
if (twai_ctx->ctrlr_id != -1) {
|
if (twai_ctx->ctrlr_id != -1) {
|
||||||
_ctrlr_release(twai_ctx->ctrlr_id);
|
_ctrlr_release(twai_ctx->ctrlr_id);
|
||||||
}
|
}
|
||||||
@@ -582,6 +593,7 @@ static esp_err_t _node_queue_tx(twai_node_handle_t node, const twai_frame_t *fra
|
|||||||
ESP_RETURN_ON_FALSE(atomic_load(&twai_ctx->state) != TWAI_ERROR_BUS_OFF, ESP_ERR_INVALID_STATE, TAG, "node is bus off");
|
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);
|
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;
|
bool false_var = false;
|
||||||
if (atomic_compare_exchange_strong(&twai_ctx->hw_busy, &false_var, true)) {
|
if (atomic_compare_exchange_strong(&twai_ctx->hw_busy, &false_var, true)) {
|
||||||
twai_ctx->p_curr_tx = frame;
|
twai_ctx->p_curr_tx = frame;
|
||||||
@@ -601,10 +613,28 @@ static esp_err_t _node_queue_tx(twai_node_handle_t node, const twai_frame_t *fra
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static esp_err_t _node_wait_tx_all_done(twai_node_handle_t node, int timeout)
|
||||||
|
{
|
||||||
|
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
||||||
|
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
|
||||||
|
// otherwise, hardware is idle, return immediately
|
||||||
|
if (atomic_load(&twai_ctx->hw_busy) || uxQueueMessagesWaiting(twai_ctx->tx_mount_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static esp_err_t _node_parse_rx(twai_node_handle_t node, twai_frame_t *rx_frame)
|
static esp_err_t _node_parse_rx(twai_node_handle_t node, twai_frame_t *rx_frame)
|
||||||
{
|
{
|
||||||
twai_onchip_ctx_t *twai_ctx = __containerof(node, twai_onchip_ctx_t, api_base);
|
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");
|
ESP_RETURN_ON_FALSE_ISR(atomic_load(&twai_ctx->rx_isr), ESP_ERR_INVALID_STATE, TAG, "rx can only called in `rx_done` callback");
|
||||||
|
assert(xPortInIsrContext() && "should always in rx_done callback");
|
||||||
|
|
||||||
twai_hal_parse_frame(&twai_ctx->rcv_buff, &rx_frame->header, rx_frame->buffer, rx_frame->buffer_len);
|
twai_hal_parse_frame(&twai_ctx->rcv_buff, &rx_frame->header, rx_frame->buffer, rx_frame->buffer_len);
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@@ -630,7 +660,8 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa
|
|||||||
// state is in bus_off before enabled
|
// state is in bus_off before enabled
|
||||||
atomic_store(&node->state, TWAI_ERROR_BUS_OFF);
|
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);
|
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 || node_config->flags.enable_listen_only, ESP_ERR_NO_MEM, 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");
|
||||||
uint32_t intr_flags = TWAI_INTR_ALLOC_FLAGS;
|
uint32_t intr_flags = TWAI_INTR_ALLOC_FLAGS;
|
||||||
intr_flags |= (node_config->intr_priority > 0) ? BIT(node_config->intr_priority) : ESP_INTR_FLAG_LOWMED;
|
intr_flags |= (node_config->intr_priority > 0) ? BIT(node_config->intr_priority) : ESP_INTR_FLAG_LOWMED;
|
||||||
ESP_GOTO_ON_ERROR(esp_intr_alloc(twai_periph_signals[ctrlr_id].irq_id, intr_flags, _node_isr_main, (void *)node, &node->intr_hdl),
|
ESP_GOTO_ON_ERROR(esp_intr_alloc(twai_periph_signals[ctrlr_id].irq_id, intr_flags, _node_isr_main, (void *)node, &node->intr_hdl),
|
||||||
@@ -683,6 +714,7 @@ esp_err_t twai_new_node_onchip(const twai_onchip_node_config_t *node_config, twa
|
|||||||
node->api_base.reconfig_timing = _node_set_bit_timing;
|
node->api_base.reconfig_timing = _node_set_bit_timing;
|
||||||
node->api_base.register_cbs = _node_register_callbacks;
|
node->api_base.register_cbs = _node_register_callbacks;
|
||||||
node->api_base.transmit = _node_queue_tx;
|
node->api_base.transmit = _node_queue_tx;
|
||||||
|
node->api_base.transmit_wait_done = _node_wait_tx_all_done;
|
||||||
node->api_base.receive_isr = _node_parse_rx;
|
node->api_base.receive_isr = _node_parse_rx;
|
||||||
node->api_base.get_info = _node_get_status;
|
node->api_base.get_info = _node_get_status;
|
||||||
|
|
||||||
|
@@ -91,6 +91,18 @@ struct twai_node_base {
|
|||||||
*/
|
*/
|
||||||
esp_err_t (*transmit)(struct twai_node_base *node, const twai_frame_t *frame, int timeout);
|
esp_err_t (*transmit)(struct twai_node_base *node, const twai_frame_t *frame, int timeout);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wait for the TWAI node to finish transmitting
|
||||||
|
*
|
||||||
|
* @param[in] node Pointer to the TWAI node base
|
||||||
|
* @param[in] timeout Timeout in milliseconds
|
||||||
|
* @return esp_err_t
|
||||||
|
* - ESP_OK: Success
|
||||||
|
* - ESP_ERR_TIMEOUT: Waiting timeout
|
||||||
|
* - ESP_ERR_INVALID_STATE: Node is not enabled or already in bus-off state
|
||||||
|
*/
|
||||||
|
esp_err_t (*transmit_wait_done)(struct twai_node_base *node, int timeout);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Receive a TWAI frame through the ISR callback
|
* @brief Receive a TWAI frame through the ISR callback
|
||||||
*
|
*
|
||||||
|
@@ -111,6 +111,15 @@ esp_err_t twai_node_get_info(twai_node_handle_t node, twai_node_status_t *status
|
|||||||
*/
|
*/
|
||||||
esp_err_t twai_node_transmit(twai_node_handle_t node, const twai_frame_t *frame, int timeout_ms);
|
esp_err_t twai_node_transmit(twai_node_handle_t node, const twai_frame_t *frame, int timeout_ms);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Wait for all pending transfers to finish, if bus-off happens during waiting, wait until node recovered and tx is finished, or timeout
|
||||||
|
*
|
||||||
|
* @param[in] node Handle to the TWAI node
|
||||||
|
* @param[in] timeout_ms Maximum wait time for all pending transfers to finish (milliseconds), -1 to wait forever
|
||||||
|
* @return ESP_OK on success, error code otherwise
|
||||||
|
*/
|
||||||
|
esp_err_t twai_node_transmit_wait_all_done(twai_node_handle_t node, int timeout_ms);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Receive a TWAI frame from 'rx_done_cb'
|
* @brief Receive a TWAI frame from 'rx_done_cb'
|
||||||
*
|
*
|
||||||
|
@@ -5,6 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <stdatomic.h>
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include "unity.h"
|
#include "unity.h"
|
||||||
#include "unity_test_utils_cache.h"
|
#include "unity_test_utils_cache.h"
|
||||||
@@ -89,7 +90,9 @@ TEST_CASE("twai install uninstall (loopback)", "[twai]")
|
|||||||
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[SOC_TWAI_CONTROLLER_NUM], &user_cbs, NULL));
|
TEST_ESP_OK(twai_node_register_event_callbacks(node_hdl[SOC_TWAI_CONTROLLER_NUM], &user_cbs, NULL));
|
||||||
TEST_ESP_OK(twai_node_enable(node_hdl[SOC_TWAI_CONTROLLER_NUM]));
|
TEST_ESP_OK(twai_node_enable(node_hdl[SOC_TWAI_CONTROLLER_NUM]));
|
||||||
tx_frame.header.id = 0x100;
|
tx_frame.header.id = 0x100;
|
||||||
|
TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl[SOC_TWAI_CONTROLLER_NUM], 0)); // test wait before transmit
|
||||||
TEST_ESP_OK(twai_node_transmit(node_hdl[SOC_TWAI_CONTROLLER_NUM], &tx_frame, 0));
|
TEST_ESP_OK(twai_node_transmit(node_hdl[SOC_TWAI_CONTROLLER_NUM], &tx_frame, 0));
|
||||||
|
TEST_ESP_ERR(ESP_ERR_TIMEOUT, twai_node_transmit_wait_all_done(node_hdl[SOC_TWAI_CONTROLLER_NUM], 0)); // test return before finish
|
||||||
twai_frame_t rx_frame = {};
|
twai_frame_t rx_frame = {};
|
||||||
printf("Test receive from task\n");
|
printf("Test receive from task\n");
|
||||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_receive_from_isr(node_hdl[SOC_TWAI_CONTROLLER_NUM], &rx_frame));
|
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, twai_node_receive_from_isr(node_hdl[SOC_TWAI_CONTROLLER_NUM], &rx_frame));
|
||||||
@@ -216,9 +219,7 @@ TEST_CASE("twai transmit stop resume (loopback)", "[twai]")
|
|||||||
TEST_ESP_OK(twai_node_enable(node_hdl));
|
TEST_ESP_OK(twai_node_enable(node_hdl));
|
||||||
|
|
||||||
//waiting pkg receive finish
|
//waiting pkg receive finish
|
||||||
while (rx_frame.buffer < recv_pkg_ptr + TEST_TRANS_LEN) {
|
TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl, -1));
|
||||||
vTaskDelay(1);
|
|
||||||
}
|
|
||||||
free(tx_msgs);
|
free(tx_msgs);
|
||||||
|
|
||||||
// check if pkg receive correct
|
// check if pkg receive correct
|
||||||
@@ -244,7 +245,7 @@ static void test_random_trans_generator(twai_node_handle_t node_hdl, uint32_t tr
|
|||||||
tx_msg.header.rtr = !!(tx_cnt % 3);
|
tx_msg.header.rtr = !!(tx_cnt % 3);
|
||||||
tx_msg.buffer_len = tx_cnt % TWAI_FRAME_MAX_LEN;
|
tx_msg.buffer_len = tx_cnt % TWAI_FRAME_MAX_LEN;
|
||||||
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msg, 0));
|
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msg, 0));
|
||||||
vTaskDelay(8); //as async transaction, waiting trans done
|
TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl, 8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,3 +531,49 @@ TEST_CASE("twai bus off recovery (loopback)", "[twai]")
|
|||||||
TEST_ESP_OK(twai_node_disable(node_hdl));
|
TEST_ESP_OK(twai_node_disable(node_hdl));
|
||||||
TEST_ESP_OK(twai_node_delete(node_hdl));
|
TEST_ESP_OK(twai_node_delete(node_hdl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_send_wait_task(void *args)
|
||||||
|
{
|
||||||
|
const char *task_name = pcTaskGetName(NULL);
|
||||||
|
twai_node_handle_t node_hdl = ((twai_node_handle_t *)args)[0];
|
||||||
|
atomic_int *tasks_finished = ((atomic_int **)args)[1];
|
||||||
|
twai_frame_t tx_frame = {};
|
||||||
|
tx_frame.header.rtr = true;
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 10; i++) {
|
||||||
|
tx_frame.header.id = 0x100 + i;
|
||||||
|
printf("%s send %d\n", task_name, i);
|
||||||
|
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_frame, -1));
|
||||||
|
printf("%s wait %d\n", task_name, i);
|
||||||
|
TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl, -1));
|
||||||
|
}
|
||||||
|
printf("%s finished\n", task_name);
|
||||||
|
atomic_fetch_add(tasks_finished, 1);
|
||||||
|
vTaskDelete(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("twai tx_wait_all_done thread safe", "[twai]")
|
||||||
|
{
|
||||||
|
twai_node_handle_t node_hdl;
|
||||||
|
twai_onchip_node_config_t node_config = {};
|
||||||
|
node_config.io_cfg.tx = TEST_TX_GPIO;
|
||||||
|
node_config.io_cfg.rx = TEST_TX_GPIO; //Using same pin for test without transceiver
|
||||||
|
node_config.bit_timing.bitrate = 100000; //slow bitrate to ensure transmite finish during wait_all_done
|
||||||
|
node_config.tx_queue_depth = TEST_FRAME_NUM;
|
||||||
|
node_config.flags.enable_self_test = true;
|
||||||
|
|
||||||
|
TEST_ESP_OK(twai_new_node_onchip(&node_config, &node_hdl));
|
||||||
|
TEST_ESP_OK(twai_node_enable(node_hdl));
|
||||||
|
|
||||||
|
atomic_int tasks_finished = 0;
|
||||||
|
void *args[2] = {node_hdl, &tasks_finished};
|
||||||
|
xTaskCreate(test_send_wait_task, "task_high", 4096, args, 1, NULL);
|
||||||
|
xTaskCreate(test_send_wait_task, "task_low ", 4096, args, 0, NULL);
|
||||||
|
|
||||||
|
vTaskDelay(500); //wait for tasks finished
|
||||||
|
printf("tasks_finished: %d\n", atomic_load(&tasks_finished));
|
||||||
|
TEST_ASSERT_EQUAL(2, atomic_load(&tasks_finished));
|
||||||
|
|
||||||
|
TEST_ESP_OK(twai_node_disable(node_hdl));
|
||||||
|
TEST_ESP_OK(twai_node_delete(node_hdl));
|
||||||
|
}
|
||||||
|
@@ -32,7 +32,7 @@ static void test_random_trans_generator(twai_node_handle_t node_hdl, uint32_t tr
|
|||||||
tx_msg.header.fdf = !!(tx_cnt % 5);
|
tx_msg.header.fdf = !!(tx_cnt % 5);
|
||||||
tx_msg.buffer_len = tx_msg.header.fdf ? (tx_cnt % TWAIFD_FRAME_MAX_LEN) : (tx_cnt % TWAI_FRAME_MAX_LEN);
|
tx_msg.buffer_len = tx_msg.header.fdf ? (tx_cnt % TWAIFD_FRAME_MAX_LEN) : (tx_cnt % TWAI_FRAME_MAX_LEN);
|
||||||
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msg, 0));
|
TEST_ESP_OK(twai_node_transmit(node_hdl, &tx_msg, 0));
|
||||||
vTaskDelay(8); //as async transaction, waiting trans done
|
TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl, -1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -179,9 +179,7 @@ TEST_CASE("twai fd transmit time (loopback)", "[twai]")
|
|||||||
}
|
}
|
||||||
|
|
||||||
//waiting pkg receive finish
|
//waiting pkg receive finish
|
||||||
while (rx_frame.buffer < recv_pkg_ptr + TEST_TRANS_TIME_BUF_LEN) {
|
TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl, -1));
|
||||||
vTaskDelay(1);
|
|
||||||
}
|
|
||||||
time2 = esp_timer_get_time();
|
time2 = esp_timer_get_time();
|
||||||
free(tx_msgs);
|
free(tx_msgs);
|
||||||
|
|
||||||
|
@@ -54,6 +54,7 @@ TEST_CASE("twai_listen_only", "[twai_net]")
|
|||||||
while (!rx_msg_cnt) {
|
while (!rx_msg_cnt) {
|
||||||
vTaskDelay(1);
|
vTaskDelay(1);
|
||||||
}
|
}
|
||||||
|
TEST_ESP_OK(twai_node_transmit_wait_all_done(node_hdl, 0)); // test wait after only receive
|
||||||
ESP_LOGI("Test", "receive with id 0x%lx", rx_frame.header.id);
|
ESP_LOGI("Test", "receive with id 0x%lx", rx_frame.header.id);
|
||||||
ESP_LOG_BUFFER_HEX("Data", rx_frame.buffer, twaifd_dlc2len(rx_frame.header.dlc));
|
ESP_LOG_BUFFER_HEX("Data", rx_frame.buffer, twaifd_dlc2len(rx_frame.header.dlc));
|
||||||
TEST_ASSERT_EQUAL_HEX(0x6688, rx_frame.header.id);
|
TEST_ASSERT_EQUAL_HEX(0x6688, rx_frame.header.id);
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
CONFIG_COMPILER_DUMP_RTL_FILES=y
|
||||||
CONFIG_TWAI_ISR_CACHE_SAFE=y
|
CONFIG_TWAI_ISR_CACHE_SAFE=y
|
||||||
|
CONFIG_FREERTOS_IN_IRAM=y
|
||||||
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
CONFIG_COMPILER_OPTIMIZATION_NONE=y
|
||||||
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
|
CONFIG_COMPILER_OPTIMIZATION_CHECKS_SILENT=y
|
||||||
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y
|
CONFIG_COMPILER_OPTIMIZATION_ASSERTIONS_DISABLE=y
|
||||||
|
@@ -120,6 +120,7 @@ To reduce performance overhead caused by memory copying, the TWAI driver uses po
|
|||||||
.buffer_len = sizeof(send_buff), // Length of data to transmit
|
.buffer_len = sizeof(send_buff), // Length of data to transmit
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(twai_node_transmit(node_hdl, &tx_msg, 0)); // Timeout = 0: returns immediately if queue is full
|
ESP_ERROR_CHECK(twai_node_transmit(node_hdl, &tx_msg, 0)); // Timeout = 0: returns immediately if queue is full
|
||||||
|
ESP_ERROR_CHECK(twai_node_transmit_wait_all_done(node_hdl, -1)); // Wait for transmission to finish
|
||||||
|
|
||||||
In this example, :cpp:member:`twai_frame_t::header::id` specifies the ID of the message as 0x01. Message IDs are typically used to indicate the type of message in an application and also play a role in bus arbitration during transmission—lower values indicate higher priority on the bus. :cpp:member:`twai_frame_t::buffer` points to the memory address where the data to be transmitted is stored, and :cpp:member:`twai_frame_t::buffer_len` specifies the length of that data.
|
In this example, :cpp:member:`twai_frame_t::header::id` specifies the ID of the message as 0x01. Message IDs are typically used to indicate the type of message in an application and also play a role in bus arbitration during transmission—lower values indicate higher priority on the bus. :cpp:member:`twai_frame_t::buffer` points to the memory address where the data to be transmitted is stored, and :cpp:member:`twai_frame_t::buffer_len` specifies the length of that data.
|
||||||
|
|
||||||
|
@@ -120,6 +120,7 @@ TWAI 报文有多种类型,由报头指定。一个典型的数据帧报文主
|
|||||||
.buffer_len = sizeof(send_buff), // 发送数据的长度
|
.buffer_len = sizeof(send_buff), // 发送数据的长度
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(twai_node_transmit(node_hdl, &tx_msg, 0)); // 超时为0,队列满则直接返回超时
|
ESP_ERROR_CHECK(twai_node_transmit(node_hdl, &tx_msg, 0)); // 超时为0,队列满则直接返回超时
|
||||||
|
ESP_ERROR_CHECK(twai_node_transmit_wait_all_done(node_hdl, -1)); // 等待发送完成
|
||||||
|
|
||||||
其中 :cpp:member:`twai_frame_t::header::id` 指示了该文的 ID 为 0x01。报文的 ID 通常用于表示报文在应用中的类型,并在发送过程中起到总线竞争仲裁的作用,其数值越小,在总线上的优先级越高。:cpp:member:`twai_frame_t::buffer` 则指向要发送数据所在的内存地址,并由 :cpp:member:`twai_frame_t::buffer_len` 给出数据长度。
|
其中 :cpp:member:`twai_frame_t::header::id` 指示了该文的 ID 为 0x01。报文的 ID 通常用于表示报文在应用中的类型,并在发送过程中起到总线竞争仲裁的作用,其数值越小,在总线上的优先级越高。:cpp:member:`twai_frame_t::buffer` 则指向要发送数据所在的内存地址,并由 :cpp:member:`twai_frame_t::buffer_len` 给出数据长度。
|
||||||
|
|
||||||
|
@@ -45,17 +45,17 @@ idf.py -p PORT flash monitor
|
|||||||
install twai success
|
install twai success
|
||||||
node started
|
node started
|
||||||
|
|
||||||
sending frame 0
|
sending frame 0, please trigger error during sending
|
||||||
sending frame 1
|
sending frame 1, please trigger error during sending
|
||||||
|
|
||||||
...
|
...
|
||||||
sending frame 4
|
sending frame 4, please trigger error during sending
|
||||||
// Manually trigger error here (disconnect TX/RX or short H/L)
|
// Manually trigger error here (disconnect TX/RX or short H/L)
|
||||||
bus error: 0x2
|
bus error: 0x2
|
||||||
state changed: error_active -> error_warning
|
state changed: error_active -> error_warning
|
||||||
|
|
||||||
...
|
...
|
||||||
sending frame 9
|
sending frame 9, please trigger error during sending
|
||||||
// Trigger error again
|
// Trigger error again
|
||||||
bus error: 0x2
|
bus error: 0x2
|
||||||
state changed: error_passive -> bus_off
|
state changed: error_passive -> bus_off
|
||||||
@@ -66,7 +66,7 @@ waiting ... 0
|
|||||||
state changed: bus_off -> error_active
|
state changed: bus_off -> error_active
|
||||||
node recovered! continue
|
node recovered! continue
|
||||||
|
|
||||||
sending frame 0
|
sending frame 0, please trigger error during sending
|
||||||
```
|
```
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
@@ -89,7 +89,7 @@ void app_main(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ESP_LOGI(TAG, "sending frame %d", i);
|
ESP_LOGI(TAG, "sending frame %d, please trigger error during sending", i);
|
||||||
tx_frame.header.id = i;
|
tx_frame.header.id = i;
|
||||||
ESP_ERROR_CHECK(twai_node_transmit(node_hdl, &tx_frame, 500));
|
ESP_ERROR_CHECK(twai_node_transmit(node_hdl, &tx_frame, 500));
|
||||||
}
|
}
|
||||||
|
@@ -34,13 +34,10 @@ typedef struct {
|
|||||||
// Transmission completion callback
|
// Transmission completion callback
|
||||||
static IRAM_ATTR bool twai_sender_tx_done_callback(twai_node_handle_t handle, const twai_tx_done_event_data_t *edata, void *user_ctx)
|
static IRAM_ATTR bool twai_sender_tx_done_callback(twai_node_handle_t handle, const twai_tx_done_event_data_t *edata, void *user_ctx)
|
||||||
{
|
{
|
||||||
BaseType_t woken;
|
|
||||||
SemaphoreHandle_t *tx_semaphore = (SemaphoreHandle_t *)user_ctx;
|
|
||||||
if (!edata->is_tx_success) {
|
if (!edata->is_tx_success) {
|
||||||
ESP_EARLY_LOGW(TAG, "Failed to transmit message, ID: 0x%X", edata->done_tx_frame->header.id);
|
ESP_EARLY_LOGW(TAG, "Failed to transmit message, ID: 0x%X", edata->done_tx_frame->header.id);
|
||||||
}
|
}
|
||||||
xSemaphoreGiveFromISR(*tx_semaphore, &woken);
|
return false; // No task wake required
|
||||||
return (woken == pdTRUE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bus error callback
|
// Bus error callback
|
||||||
@@ -53,7 +50,6 @@ static IRAM_ATTR bool twai_sender_on_error_callback(twai_node_handle_t handle, c
|
|||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
twai_node_handle_t sender_node = NULL;
|
twai_node_handle_t sender_node = NULL;
|
||||||
SemaphoreHandle_t tx_semaphore = NULL;
|
|
||||||
printf("===================TWAI Sender Example Starting...===================\n");
|
printf("===================TWAI Sender Example Starting...===================\n");
|
||||||
|
|
||||||
// Configure TWAI node
|
// Configure TWAI node
|
||||||
@@ -79,15 +75,10 @@ void app_main(void)
|
|||||||
.on_tx_done = twai_sender_tx_done_callback,
|
.on_tx_done = twai_sender_tx_done_callback,
|
||||||
.on_error = twai_sender_on_error_callback,
|
.on_error = twai_sender_on_error_callback,
|
||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(twai_node_register_event_callbacks(sender_node, &callbacks, &tx_semaphore));
|
ESP_ERROR_CHECK(twai_node_register_event_callbacks(sender_node, &callbacks, NULL));
|
||||||
|
|
||||||
// Enable TWAI node
|
// Enable TWAI node
|
||||||
ESP_ERROR_CHECK(twai_node_enable(sender_node));
|
ESP_ERROR_CHECK(twai_node_enable(sender_node));
|
||||||
|
|
||||||
// Create semaphore for transmission completion
|
|
||||||
tx_semaphore = xSemaphoreCreateCounting(howmany(TWAI_DATA_LEN, TWAI_FRAME_MAX_LEN), 0);
|
|
||||||
assert(tx_semaphore != NULL);
|
|
||||||
|
|
||||||
ESP_LOGI(TAG, "TWAI Sender started successfully");
|
ESP_LOGI(TAG, "TWAI Sender started successfully");
|
||||||
ESP_LOGI(TAG, "Sending messages with IDs: 0x%03X (data), 0x%03X (heartbeat)", TWAI_DATA_ID, TWAI_HEARTBEAT_ID);
|
ESP_LOGI(TAG, "Sending messages with IDs: 0x%03X (data), 0x%03X (heartbeat)", TWAI_DATA_ID, TWAI_HEARTBEAT_ID);
|
||||||
|
|
||||||
@@ -101,7 +92,7 @@ void app_main(void)
|
|||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(twai_node_transmit(sender_node, &tx_frame, 500));
|
ESP_ERROR_CHECK(twai_node_transmit(sender_node, &tx_frame, 500));
|
||||||
ESP_LOGI(TAG, "Sending heartbeat message: %lld", timestamp);
|
ESP_LOGI(TAG, "Sending heartbeat message: %lld", timestamp);
|
||||||
xSemaphoreTake(tx_semaphore, portMAX_DELAY);
|
ESP_ERROR_CHECK(twai_node_transmit_wait_all_done(sender_node, -1)); // -1 means wait forever
|
||||||
|
|
||||||
// Send burst data messages every 10 seconds
|
// Send burst data messages every 10 seconds
|
||||||
if ((timestamp / 1000000) % 10 == 0) {
|
if ((timestamp / 1000000) % 10 == 0) {
|
||||||
@@ -118,9 +109,7 @@ void app_main(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Frames mounted, wait for all frames to be transmitted
|
// Frames mounted, wait for all frames to be transmitted
|
||||||
for (int i = 0; i < num_frames; i++) {
|
ESP_ERROR_CHECK(twai_node_transmit_wait_all_done(sender_node, -1));
|
||||||
xSemaphoreTake(tx_semaphore, portMAX_DELAY);
|
|
||||||
}
|
|
||||||
free(data);
|
free(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,7 +122,6 @@ void app_main(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vSemaphoreDelete(tx_semaphore);
|
|
||||||
ESP_ERROR_CHECK(twai_node_disable(sender_node));
|
ESP_ERROR_CHECK(twai_node_disable(sender_node));
|
||||||
ESP_ERROR_CHECK(twai_node_delete(sender_node));
|
ESP_ERROR_CHECK(twai_node_delete(sender_node));
|
||||||
}
|
}
|
||||||
|
@@ -42,66 +42,13 @@ static struct {
|
|||||||
static bool twai_send_tx_done_cb(twai_node_handle_t handle, const twai_tx_done_event_data_t *event_data, void *user_ctx)
|
static bool twai_send_tx_done_cb(twai_node_handle_t handle, const twai_tx_done_event_data_t *event_data, void *user_ctx)
|
||||||
{
|
{
|
||||||
ESP_UNUSED(handle);
|
ESP_UNUSED(handle);
|
||||||
ESP_UNUSED(event_data);
|
|
||||||
twai_controller_ctx_t *controller = (twai_controller_ctx_t *)user_ctx;
|
twai_controller_ctx_t *controller = (twai_controller_ctx_t *)user_ctx;
|
||||||
|
|
||||||
/* Signal TX completion */
|
|
||||||
if (atomic_load(&controller->send_ctx.is_tx_pending)) {
|
|
||||||
atomic_store(&controller->send_ctx.is_tx_pending, false);
|
|
||||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
|
||||||
xSemaphoreGiveFromISR(controller->send_ctx.tx_done_sem, &xHigherPriorityTaskWoken);
|
|
||||||
return xHigherPriorityTaskWoken == pdTRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initialize the send module for a controller
|
|
||||||
*
|
|
||||||
* @param[in] controller Pointer to the controller context
|
|
||||||
*
|
|
||||||
* @return @c ESP_OK on success, error code on failure
|
|
||||||
*/
|
|
||||||
static esp_err_t twai_send_init_controller(twai_controller_ctx_t *controller)
|
|
||||||
{
|
|
||||||
int controller_id = controller - &g_twai_controller_ctx[0];
|
int controller_id = controller - &g_twai_controller_ctx[0];
|
||||||
|
|
||||||
/* Create TX completion semaphore */
|
if (!event_data->is_tx_success) {
|
||||||
controller->send_ctx.tx_done_sem = xSemaphoreCreateBinary();
|
ESP_EARLY_LOGW(TAG, "Node %d: Bus error when sending frame with ID: 0x%X", controller_id, event_data->done_tx_frame->header.id);
|
||||||
if (controller->send_ctx.tx_done_sem == NULL) {
|
|
||||||
ESP_LOGE(TAG, "Failed to create TX semaphore for controller %d", controller_id);
|
|
||||||
return ESP_ERR_NO_MEM;
|
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
/* Initialize TX pending flag */
|
|
||||||
atomic_init(&controller->send_ctx.is_tx_pending, false);
|
|
||||||
|
|
||||||
/* Register TX done callback */
|
|
||||||
twai_core_ctx_t *core_ctx = &controller->core_ctx;
|
|
||||||
core_ctx->driver_cbs.on_tx_done = twai_send_tx_done_cb;
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Deinitialize the send module for a controller
|
|
||||||
*
|
|
||||||
* @param[in] controller Pointer to the controller context
|
|
||||||
*/
|
|
||||||
static void twai_send_deinit_controller(twai_controller_ctx_t *controller)
|
|
||||||
{
|
|
||||||
/* Clear pending flag */
|
|
||||||
atomic_store(&controller->send_ctx.is_tx_pending, false);
|
|
||||||
|
|
||||||
/* Delete TX completion semaphore */
|
|
||||||
if (controller->send_ctx.tx_done_sem) {
|
|
||||||
vSemaphoreDelete(controller->send_ctx.tx_done_sem);
|
|
||||||
controller->send_ctx.tx_done_sem = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Clear callback */
|
|
||||||
controller->core_ctx.driver_cbs.on_tx_done = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -115,33 +62,21 @@ static void twai_send_deinit_controller(twai_controller_ctx_t *controller)
|
|||||||
*/
|
*/
|
||||||
static esp_err_t send_frame_sync(twai_controller_ctx_t *controller, const twai_frame_t *frame, uint32_t timeout_ms)
|
static esp_err_t send_frame_sync(twai_controller_ctx_t *controller, const twai_frame_t *frame, uint32_t timeout_ms)
|
||||||
{
|
{
|
||||||
if (!controller) {
|
ESP_RETURN_ON_FALSE(controller, ESP_ERR_INVALID_ARG, TAG, "Invalid controller pointer");
|
||||||
ESP_LOGE(TAG, "Invalid controller pointer");
|
|
||||||
return ESP_ERR_INVALID_ARG;
|
|
||||||
}
|
|
||||||
|
|
||||||
int controller_id = controller - &g_twai_controller_ctx[0];
|
int controller_id = controller - &g_twai_controller_ctx[0];
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
twai_core_ctx_t *ctx = &controller->core_ctx;
|
twai_core_ctx_t *ctx = &controller->core_ctx;
|
||||||
|
|
||||||
/* Check if TWAI driver is running */
|
/* Check if TWAI driver is running */
|
||||||
ESP_RETURN_ON_FALSE(atomic_load(&ctx->is_initialized), ESP_ERR_INVALID_STATE, TAG, "TWAI%d not initialized", controller_id);
|
ESP_RETURN_ON_FALSE(atomic_load(&ctx->is_initialized), ESP_ERR_INVALID_STATE, TAG, "TWAI%d not initialized", controller_id);
|
||||||
|
|
||||||
/* Mark TX as pending */
|
|
||||||
atomic_store(&controller->send_ctx.is_tx_pending, true);
|
|
||||||
|
|
||||||
/* Transmit the frame */
|
/* Transmit the frame */
|
||||||
ret = twai_node_transmit(controller->node_handle, frame, timeout_ms);
|
ESP_RETURN_ON_ERROR(twai_node_transmit(controller->node_handle, frame, timeout_ms), TAG, "Node %d: Failed to queue TX frame", controller_id);
|
||||||
ESP_GOTO_ON_ERROR(ret, err, TAG, "Node %d: Failed to queue TX frame: %s", controller_id, esp_err_to_name(ret));
|
|
||||||
|
|
||||||
/* Wait for TX completion or timeout */
|
/* Wait for TX completion or timeout */
|
||||||
ESP_GOTO_ON_FALSE(xSemaphoreTake(controller->send_ctx.tx_done_sem, pdMS_TO_TICKS(timeout_ms)) == pdTRUE, ESP_ERR_TIMEOUT, err, TAG,
|
ESP_RETURN_ON_ERROR(twai_node_transmit_wait_all_done(controller->node_handle, timeout_ms), TAG, "Node %d: TX not completed after %"PRIu32" ms", controller_id, timeout_ms);
|
||||||
"Node %d: TX timed out after %"PRIu32" ms", controller_id, timeout_ms);
|
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
err:
|
|
||||||
atomic_store(&controller->send_ctx.is_tx_pending, false);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -228,10 +163,7 @@ void register_twai_send_commands(void)
|
|||||||
/* Initialize send context for all controllers */
|
/* Initialize send context for all controllers */
|
||||||
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
|
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
|
||||||
twai_controller_ctx_t *controller = &g_twai_controller_ctx[i];
|
twai_controller_ctx_t *controller = &g_twai_controller_ctx[i];
|
||||||
esp_err_t ret = twai_send_init_controller(controller);
|
controller->core_ctx.driver_cbs.on_tx_done = twai_send_tx_done_cb;
|
||||||
if (ret != ESP_OK) {
|
|
||||||
ESP_LOGW(TAG, "Failed to initialize send module for TWAI%d: %s", i, esp_err_to_name(ret));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Register command arguments */
|
/* Register command arguments */
|
||||||
@@ -270,6 +202,6 @@ void unregister_twai_send_commands(void)
|
|||||||
/* Cleanup all controller send modules */
|
/* Cleanup all controller send modules */
|
||||||
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
|
for (int i = 0; i < SOC_TWAI_CONTROLLER_NUM; i++) {
|
||||||
twai_controller_ctx_t *controller = &g_twai_controller_ctx[i];
|
twai_controller_ctx_t *controller = &g_twai_controller_ctx[i];
|
||||||
twai_send_deinit_controller(controller);
|
controller->core_ctx.driver_cbs.on_tx_done = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user