mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-05 05:34:32 +02:00
feat(parlio_tx): support buffer switched callback
This commit is contained in:
committed by
Chen Ji Chang
parent
c5a17106b6
commit
05c2ebcf16
@@ -115,6 +115,7 @@ esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t unit);
|
||||
*/
|
||||
typedef struct {
|
||||
parlio_tx_done_callback_t on_trans_done; /*!< Event callback, invoked when one transmission is finished */
|
||||
parlio_tx_buffer_switched_callback_t on_buffer_switched; /*!< Event callback, invoked when the buffer is switched in loop transmission */
|
||||
} parlio_tx_event_callbacks_t;
|
||||
|
||||
/**
|
||||
|
@@ -37,6 +37,14 @@ typedef struct parlio_rx_delimiter_t *parlio_rx_delimiter_handle_t;
|
||||
typedef struct {
|
||||
} parlio_tx_done_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Type of Parallel IO TX buffer switched event data
|
||||
*/
|
||||
typedef struct {
|
||||
void *old_buffer_addr; /*!< Address of the previous buffer used before switching */
|
||||
void *new_buffer_addr; /*!< Address of the new buffer switched to */
|
||||
} parlio_tx_buffer_switched_event_data_t;
|
||||
|
||||
/**
|
||||
* @brief Prototype of parlio tx event callback
|
||||
* @param[in] tx_unit Parallel IO TX unit that created by `parlio_new_tx_unit`
|
||||
@@ -48,6 +56,17 @@ typedef struct {
|
||||
*/
|
||||
typedef bool (*parlio_tx_done_callback_t)(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx);
|
||||
|
||||
/**
|
||||
* @brief Prototype of parlio tx buffer switched event callback
|
||||
* @param[in] tx_unit Parallel IO TX unit that created by `parlio_new_tx_unit`
|
||||
* @param[in] edata Point to Parallel IO TX event data. The lifecycle of this pointer memory is inside this function,
|
||||
* user should copy it into static memory if used outside this function.
|
||||
* @param[in] user_ctx User registered context, passed from `parlio_tx_unit_register_event_callbacks`
|
||||
*
|
||||
* @return Whether a high priority task has been waken up by this callback function
|
||||
*/
|
||||
typedef bool (*parlio_tx_buffer_switched_callback_t)(parlio_tx_unit_handle_t tx_unit, const parlio_tx_buffer_switched_event_data_t *edata, void *user_ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -5,6 +5,10 @@ entries:
|
||||
parlio_tx: parlio_tx_default_isr (noflash)
|
||||
parlio_tx: parlio_tx_do_transaction (noflash)
|
||||
parlio_tx: parlio_mount_buffer (noflash)
|
||||
|
||||
if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION = y:
|
||||
parlio_tx: parlio_tx_gdma_eof_callback (noflash)
|
||||
|
||||
if PARLIO_RX_ISR_HANDLER_IN_IRAM = y:
|
||||
parlio_rx: parlio_rx_default_eof_callback (noflash)
|
||||
parlio_rx: parlio_rx_default_desc_done_callback (noflash)
|
||||
@@ -18,3 +22,6 @@ entries:
|
||||
gdma_link: gdma_link_mount_buffers (noflash)
|
||||
gdma_link: gdma_link_concat (noflash)
|
||||
gdma_link: gdma_link_get_head_addr (noflash)
|
||||
|
||||
if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION = y:
|
||||
gdma_link: gdma_link_get_buffer (noflash)
|
||||
|
@@ -187,7 +187,9 @@ typedef struct parlio_tx_unit_t {
|
||||
parlio_tx_trans_desc_t *cur_trans; // points to current transaction
|
||||
uint32_t idle_value_mask; // mask of idle value
|
||||
_Atomic parlio_tx_fsm_t fsm; // Driver FSM state
|
||||
_Atomic bool buffer_need_switch; // whether the buffer need to be switched
|
||||
parlio_tx_done_callback_t on_trans_done; // callback function when the transmission is done
|
||||
parlio_tx_buffer_switched_callback_t on_buffer_switched; // callback function when the buffer is switched in loop transmission
|
||||
void *user_data; // user data passed to the callback function
|
||||
bitscrambler_handle_t bs_handle; // bitscrambler handle
|
||||
parlio_tx_bs_enable_fn_t bs_enable_fn; // bitscrambler enable function
|
||||
|
@@ -10,6 +10,9 @@
|
||||
#include "driver/parlio_tx.h"
|
||||
#include "parlio_priv.h"
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
static bool parlio_tx_gdma_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data);
|
||||
#endif
|
||||
static void parlio_tx_default_isr(void *args);
|
||||
|
||||
static esp_err_t parlio_tx_create_trans_queue(parlio_tx_unit_t *tx_unit, const parlio_tx_unit_config_t *config)
|
||||
@@ -342,14 +345,6 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
|
||||
// set sample clock edge
|
||||
parlio_ll_tx_set_sample_clock_edge(hal->regs, config->sample_edge);
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
// always use DMA EOF as the Parlio TX EOF if supported
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DMA_EOF);
|
||||
#else
|
||||
// In default, use DATA LEN EOF as the Parlio TX EOF
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN);
|
||||
#endif // SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
|
||||
// clear any pending interrupt
|
||||
parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_MASK);
|
||||
|
||||
@@ -364,6 +359,7 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
|
||||
|
||||
portMUX_INITIALIZE(&unit->spinlock);
|
||||
atomic_init(&unit->fsm, PARLIO_TX_FSM_INIT);
|
||||
atomic_init(&unit->buffer_need_switch, false);
|
||||
// return TX unit handle
|
||||
*ret_unit = unit;
|
||||
ESP_LOGD(TAG, "new tx unit(%d,%d) at %p, out clk=%"PRIu32"Hz, queue_depth=%zu, idle_mask=%"PRIx32,
|
||||
@@ -426,12 +422,30 @@ esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_uni
|
||||
if (cbs->on_trans_done) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_trans_done), ESP_ERR_INVALID_ARG, TAG, "on_trans_done callback not in IRAM");
|
||||
}
|
||||
if (cbs->on_buffer_switched) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_in_iram(cbs->on_buffer_switched), ESP_ERR_INVALID_ARG, TAG, "on_buffer_switched callback not in IRAM");
|
||||
}
|
||||
if (user_data) {
|
||||
ESP_RETURN_ON_FALSE(esp_ptr_internal(user_data), ESP_ERR_INVALID_ARG, TAG, "user context not in internal RAM");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (cbs->on_buffer_switched) {
|
||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
// workaround for DIG-559
|
||||
ESP_RETURN_ON_FALSE(tx_unit->data_width > 1, ESP_ERR_NOT_SUPPORTED, TAG, "on_buffer_switched callback is not supported for 1-bit data width");
|
||||
|
||||
gdma_tx_event_callbacks_t gdma_cbs = {
|
||||
.on_trans_eof = parlio_tx_gdma_eof_callback,
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(gdma_register_tx_event_callbacks(tx_unit->dma_chan, &gdma_cbs, tx_unit), TAG, "install DMA callback failed");
|
||||
#else
|
||||
ESP_RETURN_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, TAG, "on_buffer_switched callback is not supported");
|
||||
#endif
|
||||
}
|
||||
|
||||
tx_unit->on_trans_done = cbs->on_trans_done;
|
||||
tx_unit->on_buffer_switched = cbs->on_buffer_switched;
|
||||
tx_unit->user_data = user_data;
|
||||
return ESP_OK;
|
||||
}
|
||||
@@ -443,8 +457,8 @@ static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_
|
||||
.buffer = (void *)t->payload,
|
||||
.length = (t->payload_bits + 7) / 8,
|
||||
.flags = {
|
||||
// if transmission is loop, we don't need to generate the EOF, as well as the final mark
|
||||
.mark_eof = !t->flags.loop_transmission,
|
||||
// if transmission is loop, we don't need to generate the EOF for 1-bit data width, DIG-559
|
||||
.mark_eof = tx_unit->data_width == 1 ? !t->flags.loop_transmission : true,
|
||||
.mark_final = !t->flags.loop_transmission,
|
||||
}
|
||||
};
|
||||
@@ -462,13 +476,6 @@ static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_
|
||||
static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_t *t)
|
||||
{
|
||||
parlio_hal_context_t *hal = &tx_unit->base.group->hal;
|
||||
|
||||
if (t->flags.loop_transmission) {
|
||||
// Once a loop transmission is started, it cannot be stopped until it is disabled
|
||||
// If SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA is supported, setting the eof condition to PARLIO_LL_TX_EOF_COND_DMA_EOF again is harmless
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DMA_EOF);
|
||||
}
|
||||
|
||||
tx_unit->cur_trans = t;
|
||||
|
||||
// If the external clock is a non-free-running clock, it needs to be switched to the internal free-running clock first.
|
||||
@@ -497,7 +504,30 @@ static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_
|
||||
// reset tx fifo after disabling tx core clk to avoid unexpected rempty interrupt
|
||||
parlio_ll_tx_reset_fifo(hal->regs);
|
||||
parlio_ll_tx_set_idle_data_value(hal->regs, t->idle_value);
|
||||
parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits);
|
||||
|
||||
// set EOF condition
|
||||
if (t->flags.loop_transmission) {
|
||||
if (tx_unit->data_width == 1) {
|
||||
// for 1-bit data width, we need to set the EOF condition to DMA EOF
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DMA_EOF);
|
||||
} else {
|
||||
// for other data widths, we still use the data length EOF condition,
|
||||
// but let the `bit counter` + `data width` for each cycle is never equal to the configured bit lens.
|
||||
// Thus, we can skip the exact match, prevents EOF
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN);
|
||||
parlio_ll_tx_set_trans_bit_len(hal->regs, 0x01);
|
||||
}
|
||||
} else {
|
||||
// non-loop transmission
|
||||
#if SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
// for DMA EOF supported target, we need to set the EOF condition to DMA EOF
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DMA_EOF);
|
||||
#else
|
||||
// for DMA EOF not supported target, we need to set the bit length to the configured bit lens
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN);
|
||||
parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits);
|
||||
#endif // SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
}
|
||||
|
||||
if (tx_unit->bs_handle) {
|
||||
// load the bitscrambler program and start it
|
||||
@@ -592,12 +622,6 @@ esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t tx_unit)
|
||||
parlio_ll_tx_start(hal->regs, false);
|
||||
parlio_ll_enable_interrupt(hal->regs, PARLIO_LL_EVENT_TX_MASK, false);
|
||||
|
||||
#if !SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
// Once a loop teansmission transaction is started, it can only be stopped in disable function
|
||||
// change the EOF condition to be the data length, so the EOF will be triggered normally in the following transaction
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN);
|
||||
#endif // !SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
|
||||
|
||||
#if CONFIG_PM_ENABLE
|
||||
// release power management lock
|
||||
if (tx_unit->pm_lock) {
|
||||
@@ -622,7 +646,8 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
if (config->flags.loop_transmission) {
|
||||
ESP_RETURN_ON_FALSE(parlio_ll_tx_support_dma_eof(NULL), ESP_ERR_NOT_SUPPORTED, TAG, "loop transmission is not supported by this chip revision");
|
||||
ESP_RETURN_ON_FALSE(parlio_ll_tx_support_dma_eof(NULL) || tx_unit->data_width > 1, ESP_ERR_NOT_SUPPORTED, TAG,
|
||||
"1-bit data width loop transmission is not supported by this chip revision");
|
||||
}
|
||||
#else
|
||||
ESP_RETURN_ON_FALSE(config->flags.loop_transmission == false, ESP_ERR_NOT_SUPPORTED, TAG, "loop transmission is not supported on this chip");
|
||||
@@ -653,6 +678,7 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
tx_unit->cur_trans->payload = payload;
|
||||
tx_unit->cur_trans->payload_bits = payload_bits;
|
||||
parlio_mount_buffer(tx_unit, tx_unit->cur_trans);
|
||||
atomic_store(&tx_unit->buffer_need_switch, true);
|
||||
} else {
|
||||
TickType_t queue_wait_ticks = portMAX_DELAY;
|
||||
if (config->flags.queue_nonblocking) {
|
||||
@@ -701,6 +727,31 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
static bool parlio_tx_gdma_eof_callback(gdma_channel_handle_t dma_chan, gdma_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
parlio_tx_unit_t *tx_unit = (parlio_tx_unit_t *) user_data;
|
||||
bool need_yield = false;
|
||||
bool expected_state = true;
|
||||
// invoke callback to notify the application
|
||||
parlio_tx_buffer_switched_callback_t on_buffer_switched = tx_unit->on_buffer_switched;
|
||||
if (on_buffer_switched) {
|
||||
if (atomic_compare_exchange_strong(&tx_unit->buffer_need_switch, &expected_state, false)) {
|
||||
parlio_tx_buffer_switched_event_data_t edata = {
|
||||
// we use 2 dma links to do the buffer switch in loop transmission
|
||||
.old_buffer_addr = gdma_link_get_buffer(tx_unit->dma_link[1 - tx_unit->cur_trans->dma_link_idx], 0),
|
||||
.new_buffer_addr = gdma_link_get_buffer(tx_unit->dma_link[tx_unit->cur_trans->dma_link_idx], 0),
|
||||
};
|
||||
if (on_buffer_switched(tx_unit, &edata, tx_unit->user_data)) {
|
||||
need_yield = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return need_yield;
|
||||
}
|
||||
#endif // SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
|
||||
static void parlio_tx_default_isr(void *args)
|
||||
{
|
||||
parlio_tx_unit_t *tx_unit = (parlio_tx_unit_t *)args;
|
||||
|
@@ -518,6 +518,22 @@ TEST_CASE("parallel tx unit use external non-free running clock", "[parlio_tx]")
|
||||
};
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
typedef struct {
|
||||
uint32_t switch_count;
|
||||
void *old_buffer_addr[5];
|
||||
void *new_buffer_addr[5];
|
||||
} test_parlio_tx_buffer_switched_context_t;
|
||||
|
||||
TEST_PARLIO_CALLBACK_ATTR
|
||||
static bool test_parlio_tx_buffer_switched_callback(parlio_tx_unit_handle_t tx_unit, const parlio_tx_buffer_switched_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
test_parlio_tx_buffer_switched_context_t *context = (test_parlio_tx_buffer_switched_context_t *)user_ctx;
|
||||
context->old_buffer_addr[context->switch_count] = edata->old_buffer_addr;
|
||||
context->new_buffer_addr[context->switch_count] = edata->new_buffer_addr;
|
||||
context->switch_count++;
|
||||
return false;
|
||||
}
|
||||
|
||||
TEST_CASE("parlio_tx_loop_transmission", "[parlio_tx]")
|
||||
{
|
||||
printf("install parlio tx unit\r\n");
|
||||
@@ -545,6 +561,16 @@ TEST_CASE("parlio_tx_loop_transmission", "[parlio_tx]")
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
|
||||
};
|
||||
TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit));
|
||||
|
||||
printf("register trans_done event callback\r\n");
|
||||
parlio_tx_event_callbacks_t cbs = {
|
||||
.on_buffer_switched = test_parlio_tx_buffer_switched_callback,
|
||||
};
|
||||
test_parlio_tx_buffer_switched_context_t context = {
|
||||
.switch_count = 0,
|
||||
};
|
||||
TEST_ESP_OK(parlio_tx_unit_register_event_callbacks(tx_unit, &cbs, &context));
|
||||
|
||||
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
|
||||
|
||||
printf("send packets and check event is fired\r\n");
|
||||
@@ -560,37 +586,48 @@ TEST_CASE("parlio_tx_loop_transmission", "[parlio_tx]")
|
||||
payload_loop2[i] = 255 - i;
|
||||
payload_oneshot[i] = i * 2 + 1;
|
||||
}
|
||||
if (parlio_ll_tx_support_dma_eof(NULL)) { // for some chips, only support in particular ECO version
|
||||
transmit_config.flags.loop_transmission = true;
|
||||
int lopp_count = 3;
|
||||
while (lopp_count--) {
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// Should be sent after the previous frame has been completely sent
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop2, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
transmit_config.flags.loop_transmission = false;
|
||||
// should be pending in queue
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_oneshot, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
transmit_config.flags.loop_transmission = true;
|
||||
// there is a oneshot trans in queue, should also be pending in queue
|
||||
transmit_config.flags.loop_transmission = true;
|
||||
int lopp_count = 3;
|
||||
while (lopp_count--) {
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ESP_ERR(ESP_ERR_TIMEOUT, parlio_tx_unit_wait_all_done(tx_unit, 50));
|
||||
|
||||
// stop infinite loop transmission
|
||||
parlio_tx_unit_disable(tx_unit);
|
||||
// We should see 1 oneshot frame and 1 loop transmission (both pending in queue)
|
||||
parlio_tx_unit_enable(tx_unit);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// stop the second infinite loop transmission
|
||||
parlio_tx_unit_disable(tx_unit);
|
||||
parlio_tx_unit_enable(tx_unit);
|
||||
} else {
|
||||
TEST_ESP_ERR(ESP_ERR_NOT_SUPPORTED, parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
// Should be sent after the previous frame has been completely sent
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop2, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
}
|
||||
|
||||
transmit_config.flags.loop_transmission = false;
|
||||
// should be pending in queue
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_oneshot, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
transmit_config.flags.loop_transmission = true;
|
||||
// there is a oneshot trans in queue, should also be pending in queue
|
||||
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
TEST_ESP_ERR(ESP_ERR_TIMEOUT, parlio_tx_unit_wait_all_done(tx_unit, 50));
|
||||
|
||||
// stop infinite loop transmission
|
||||
parlio_tx_unit_disable(tx_unit);
|
||||
// We should see 1 oneshot frame and 1 loop transmission (both pending in queue)
|
||||
parlio_tx_unit_enable(tx_unit);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(10));
|
||||
// stop the second infinite loop transmission
|
||||
parlio_tx_unit_disable(tx_unit);
|
||||
parlio_tx_unit_enable(tx_unit);
|
||||
|
||||
// total 5 switch events
|
||||
TEST_ASSERT_EQUAL(5, context.switch_count);
|
||||
for (int i = 0; i < context.switch_count; i++) {
|
||||
void *old_buffer_addr = context.old_buffer_addr[i];
|
||||
void *new_buffer_addr = context.new_buffer_addr[i];
|
||||
if (i % 2 == 0) {
|
||||
TEST_ASSERT_EQUAL(payload_loop1, old_buffer_addr);
|
||||
TEST_ASSERT_EQUAL(payload_loop2, new_buffer_addr);
|
||||
} else {
|
||||
TEST_ASSERT_EQUAL(payload_loop2, old_buffer_addr);
|
||||
TEST_ASSERT_EQUAL(payload_loop1, new_buffer_addr);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, -1));
|
||||
|
@@ -283,3 +283,27 @@ size_t gdma_link_count_buffer_size_till_eof(gdma_link_list_handle_t list, int st
|
||||
}
|
||||
return buf_size;
|
||||
}
|
||||
|
||||
void *gdma_link_get_buffer(gdma_link_list_handle_t list, int item_index)
|
||||
{
|
||||
if (!list) {
|
||||
return NULL;
|
||||
}
|
||||
int num_items = list->num_items;
|
||||
// ensure the item_index is between 0 and `num_items - 1`
|
||||
item_index = (item_index % num_items + num_items) % num_items;
|
||||
gdma_link_list_item_t *lli = (gdma_link_list_item_t *)(list->items_nc + item_index * list->item_size);
|
||||
return lli->buffer;
|
||||
}
|
||||
|
||||
size_t gdma_link_get_length(gdma_link_list_handle_t list, int item_index)
|
||||
{
|
||||
if (!list) {
|
||||
return 0;
|
||||
}
|
||||
int num_items = list->num_items;
|
||||
// ensure the item_index is between 0 and `num_items - 1`
|
||||
item_index = (item_index % num_items + num_items) % num_items;
|
||||
gdma_link_list_item_t *lli = (gdma_link_list_item_t *)(list->items_nc + item_index * list->item_size);
|
||||
return lli->dw0.length;
|
||||
}
|
||||
|
@@ -176,6 +176,24 @@ esp_err_t gdma_link_get_owner(gdma_link_list_handle_t list, int item_index, gdma
|
||||
*/
|
||||
size_t gdma_link_count_buffer_size_till_eof(gdma_link_list_handle_t list, int start_item_index);
|
||||
|
||||
/**
|
||||
* @brief Get the buffer of a DMA link list item
|
||||
*
|
||||
* @param[in] list Link list handle, allocated by `gdma_new_link_list`
|
||||
* @param[in] item_index Index of the link list item
|
||||
* @return Buffer of the link list item
|
||||
*/
|
||||
void* gdma_link_get_buffer(gdma_link_list_handle_t list, int item_index);
|
||||
|
||||
/**
|
||||
* @brief Get the length of the buffer of a DMA link list item
|
||||
*
|
||||
* @param[in] list Link list handle, allocated by `gdma_new_link_list`
|
||||
* @param[in] item_index Index of the link list item
|
||||
* @return Length of the buffer of the link list item
|
||||
*/
|
||||
size_t gdma_link_get_length(gdma_link_list_handle_t list, int item_index);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -376,7 +376,7 @@
|
||||
#define SOC_PARLIO_RX_CLK_SUPPORT_GATING 1 /*!< Support gating RX clock */
|
||||
#define SOC_PARLIO_RX_CLK_SUPPORT_OUTPUT 1 /*!< Support output RX clock to a GPIO */
|
||||
#define SOC_PARLIO_TRANS_BIT_ALIGN 1 /*!< Support bit alignment in transaction */
|
||||
#define SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION 1 /*!< Support loop transmission. Only avliable in chip version above 1.2 */
|
||||
#define SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION 1 /*!< Support loop transmission. Note, 1 data-width loop transmission only avliable in chip version above 1.2 */
|
||||
#define SOC_PARLIO_SUPPORT_SLEEP_RETENTION 1 /*!< Support back up registers before sleep */
|
||||
#define SOC_PARLIO_SUPPORT_SPI_LCD 1 /*!< Support to drive SPI interfaced LCD */
|
||||
|
||||
|
@@ -174,6 +174,7 @@ When the TX unit generates events such as transmission done, it will notify the
|
||||
For the event callbacks supported by the TX unit, refer to :cpp:type:`parlio_tx_event_callbacks_t`:
|
||||
|
||||
- :cpp:member:`parlio_tx_event_callbacks_t::on_trans_done` Sets the callback function for the "transmission complete" event, with the function prototype declared as :cpp:type:`parlio_tx_done_callback_t`.
|
||||
- :cpp:member:`parlio_tx_event_callbacks_t::on_buffer_switched` Sets the callback function for the "buffer switch" event, with the function prototype declared as :cpp:type:`parlio_tx_buffer_switched_callback_t`.
|
||||
|
||||
Resource Recycling
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
@@ -307,7 +308,7 @@ The waveform of the external clock input is shown below:
|
||||
|
||||
.. note::
|
||||
|
||||
If you need to modify the transmission payload after enabling infinite loop transmission, you can configure :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` and call :cpp:func:`parlio_tx_unit_transmit` again with a new payload buffer. The driver will switch to the new buffer after the old buffer is completely transmitted. Therefore, users need to maintain two buffers to avoid data inconsistency caused by premature modification or recycling of the old buffer.
|
||||
If you need to modify the transmission payload after enabling infinite loop transmission, you can configure :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` and call :cpp:func:`parlio_tx_unit_transmit` again with a new payload buffer. The driver will switch to the new buffer after the old buffer is completely transmitted. You can register :cpp:member:`parlio_tx_event_callbacks_t::on_buffer_switched` to set the callback function for the "buffer switch" event, and need to maintain two buffers to avoid data inconsistency caused by premature modification or recycling of the old buffer.
|
||||
|
||||
.. only:: SOC_BITSCRAMBLER_SUPPORTED
|
||||
|
||||
|
@@ -174,6 +174,7 @@ TX 单元以比特为单位进行传输,且传输的比特长度必须配置
|
||||
有关 TX 单元支持的事件回调,请参阅 :cpp:type:`parlio_tx_event_callbacks_t`:
|
||||
|
||||
- :cpp:member:`parlio_tx_event_callbacks_t::on_trans_done` 为“发送完成”的事件设置回调函数,函数原型声明为 :cpp:type:`parlio_tx_done_callback_t`。
|
||||
- :cpp:member:`parlio_tx_event_callbacks_t::on_buffer_switched` 为“缓冲区切换”的事件设置回调函数,函数原型声明为 :cpp:type:`parlio_tx_buffer_switched_callback_t`。
|
||||
|
||||
资源回收
|
||||
^^^^^^^^
|
||||
@@ -307,7 +308,7 @@ TX 单元可以选择各种不同的时钟源,其中外部时钟源较为特
|
||||
|
||||
.. note::
|
||||
|
||||
如果启用无限循环发送后需要修改发送内容,可以配置 :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` 并再次调用 :cpp:func:`parlio_tx_unit_transmit` 传入新的 payload buffer,驱动会在旧 buffer 完整发送后,切换到新传入的 buffer。因此需要用户自行维护好两块buffer,避免旧 buffer 被提早修改或者回收导致产生数据不连贯的现象。
|
||||
如果启用无限循环发送后需要修改发送内容,可以配置 :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` 并再次调用 :cpp:func:`parlio_tx_unit_transmit` 传入新的 payload buffer,驱动会在旧 buffer 完整发送后,切换到新传入的 buffer。可以通过注册 :cpp:member:`parlio_tx_event_callbacks_t::on_buffer_switched` 为“缓冲区切换”的事件设置回调函数,用户需要自行维护好两块buffer,避免旧 buffer 被提早修改或者回收导致产生数据不连贯的现象。
|
||||
|
||||
.. only:: SOC_BITSCRAMBLER_SUPPORTED
|
||||
|
||||
|
@@ -85,6 +85,13 @@ static void merge_two_pixels(lv_color16_t *upper_half, lv_color16_t *lower_half,
|
||||
|
||||
extern void example_lvgl_demo_ui(lv_display_t *disp);
|
||||
|
||||
static IRAM_ATTR bool example_notify_lvgl_flush_ready(parlio_tx_unit_handle_t tx_unit, const parlio_tx_buffer_switched_event_data_t *edata, void *user_ctx)
|
||||
{
|
||||
lv_display_t *display = (lv_display_t *)user_ctx;
|
||||
lv_display_flush_ready(display);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void example_lvgl_flush_cb(lv_display_t *display, const lv_area_t *area, uint8_t *color_map)
|
||||
{
|
||||
static uint8_t buffer_idx = 0;
|
||||
@@ -114,7 +121,6 @@ static void example_lvgl_flush_cb(lv_display_t *display, const lv_area_t *area,
|
||||
parlio_tx_unit_transmit(tx_unit, s_frame_buffer[buffer_idx], (EXAMPLE_LED_MATRIX_V_RES / 2 * (EXAMPLE_LED_MATRIX_H_RES + EXAMPLE_GAP_CYCLE_PER_LINE)) * sizeof(uint16_t) * 8, &transmit_config);
|
||||
// switch to the next frame buffer
|
||||
buffer_idx ^= 0x01;
|
||||
lv_display_flush_ready(display);
|
||||
}
|
||||
|
||||
static IRAM_ATTR void timer_alarm_cb_lvgl_tick(void* arg)
|
||||
@@ -187,6 +193,12 @@ void app_main(void)
|
||||
};
|
||||
lv_display_set_user_data(display, &user_ctx);
|
||||
|
||||
// register the callback to notify LVGL that the buffer is switched
|
||||
parlio_tx_event_callbacks_t callbacks = {
|
||||
.on_buffer_switched = example_notify_lvgl_flush_ready,
|
||||
};
|
||||
parlio_tx_unit_register_event_callbacks(tx_unit, &callbacks, display);
|
||||
|
||||
ESP_LOGI(TAG, "Install LVGL tick timer");
|
||||
// increase the LVGL tick in the esp_timer alarm callback
|
||||
const esp_timer_create_args_t timer_args = {
|
||||
@@ -201,6 +213,9 @@ void app_main(void)
|
||||
|
||||
ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, EXAMPLE_LVGL_TICK_PERIOD_MS * 1000));
|
||||
|
||||
// fake transmit, to trigger the buffer switchd event in the flush callback
|
||||
parlio_tx_unit_transmit(tx_unit, s_frame_buffer[0], (EXAMPLE_LED_MATRIX_V_RES / 2 * (EXAMPLE_LED_MATRIX_H_RES + EXAMPLE_GAP_CYCLE_PER_LINE)) * sizeof(uint16_t) * 8, &transmit_config);
|
||||
|
||||
uint32_t time_till_next_ms = 0;
|
||||
while (1) {
|
||||
if (time_till_next_ms > 500) {
|
||||
|
Reference in New Issue
Block a user