feat(parlio_tx): support buffer switched callback

This commit is contained in:
Chen Jichang
2025-06-12 15:55:30 +08:00
committed by Chen Ji Chang
parent c5a17106b6
commit 05c2ebcf16
12 changed files with 233 additions and 57 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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,7 +586,7 @@ 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--) {
@@ -589,8 +615,19 @@ TEST_CASE("parlio_tx_loop_transmission", "[parlio_tx]")
// 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_ESP_ERR(ESP_ERR_NOT_SUPPORTED, parlio_tx_unit_transmit(tx_unit, payload_loop1, 256 * sizeof(uint8_t) * 8, &transmit_config));
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));

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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