feat(parlio_tx): support to use dma eof on esp32c5

This commit is contained in:
Chen Jichang
2025-05-06 15:28:19 +08:00
committed by Chen Ji Chang
parent f10dcd6140
commit 00b1d66c6e
8 changed files with 80 additions and 5 deletions

View File

@ -342,8 +342,13 @@ 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);
@ -460,6 +465,7 @@ static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_
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);
}
@ -586,9 +592,11 @@ 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
// 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
@ -620,12 +628,13 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
ESP_RETURN_ON_FALSE(config->flags.loop_transmission == false, ESP_ERR_NOT_SUPPORTED, TAG, "loop transmission is not supported on this chip");
#endif
// check the max payload size if it's not a loop transmission
// workaround for EOF limitation, when DMA EOF issue is fixed, we can remove this check
#if !SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
// check the max payload size if it's not a loop transmission and the DMA EOF is not supported
if (!config->flags.loop_transmission) {
ESP_RETURN_ON_FALSE(tx_unit->max_transfer_bits <= PARLIO_LL_TX_MAX_BITS_PER_FRAME,
ESP_ERR_INVALID_ARG, TAG, "invalid transfer size");
ESP_ERR_INVALID_ARG, TAG, "invalid transfer size, max transfer size should be less than %d", PARLIO_LL_TX_MAX_BITS_PER_FRAME / 8);
}
#endif // !SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
size_t cache_line_size = 0;
size_t alignment = 0;

View File

@ -8,6 +8,7 @@ endif()
if(CONFIG_SOC_BITSCRAMBLER_SUPPORTED)
list(APPEND srcs "test_parlio_bitscrambler.c")
endif()
if(CONFIG_PARLIO_TX_ISR_CACHE_SAFE)
list(APPEND srcs "test_parlio_tx_cache_safe.c")

View File

@ -598,3 +598,51 @@ TEST_CASE("parlio_tx_loop_transmission", "[parlio_tx]")
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
}
#endif // SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
#if SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
TEST_CASE("parlio_tx can transmit buffer larger than max_size decided by datalen_eof", "[parlio_tx]")
{
printf("install parlio tx unit\r\n");
parlio_tx_unit_handle_t tx_unit = NULL;
parlio_tx_unit_config_t config = {
.clk_src = PARLIO_CLK_SRC_DEFAULT,
.data_width = 4,
.clk_in_gpio_num = -1, // use internal clock source
.valid_gpio_num = TEST_VALID_GPIO, // generate the valid signal
.clk_out_gpio_num = TEST_CLK_GPIO,
.data_gpio_nums = {
TEST_DATA0_GPIO,
TEST_DATA1_GPIO,
TEST_DATA2_GPIO,
TEST_DATA3_GPIO,
},
.output_clk_freq_hz = 10 * 1000 * 1000,
.trans_queue_depth = 1,
.max_transfer_size = 100 * 1024,
.bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB,
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
.flags.clk_gate_en = true,
};
TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit));
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
const size_t buffer_size = 100 * 1024; // 100KB, larger than the 65535 bytes limit
uint8_t *buffer = heap_caps_malloc(buffer_size, MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
TEST_ASSERT_NOT_NULL(buffer);
for (int i = 0; i < buffer_size; i++) {
buffer[i] = i;
}
parlio_transmit_config_t transmit_config = {
.idle_value = 0x00,
};
TEST_ESP_OK(parlio_tx_unit_transmit(tx_unit, buffer, buffer_size * 8, &transmit_config));
TEST_ESP_OK(parlio_tx_unit_wait_all_done(tx_unit, -1));
TEST_ESP_OK(parlio_tx_unit_disable(tx_unit));
TEST_ESP_OK(parlio_del_tx_unit(tx_unit));
free(buffer);
}
#endif // SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA

View File

@ -1019,6 +1019,10 @@ config SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
bool
default y
config SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
bool
default y
config SOC_PARLIO_SUPPORT_SLEEP_RETENTION
bool
default y

View File

@ -392,6 +392,7 @@
#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 */
#define SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA 1 /*!< Support to treat DMA EOF as TX unit EOF */
#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 */
#define SOC_PARLIO_SUPPORT_I80_LCD 1 /*!< Support to drive I80 interfaced LCD */

View File

@ -320,6 +320,12 @@ The waveform of the external clock input is shown below:
After writing the BitScrambler program, we can enable it by calling :cpp:func:`parlio_tx_unit_decorate_bitscrambler`. And configure the :cpp:member:`parlio_transmit_config_t::bitscrambler_program` to point to the binary file of the BitScrambler program. Different transmission transactions can use different BitScrambler programs. The binary file must conform to the BitScrambler assembly language specification, and will be loaded into the BitScrambler's instruction memory at runtime. For details on how to write and compile the BitScrambler program, please refer to :doc:`BitScrambler Programming Guide </api-reference/peripherals/bitscrambler>`.
.. only:: not SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
.. note::
Due to hardware limitations, the bitstream generated by the BitScrambler cannot change the length compared to the original bitstream, otherwise transmission blocking or data loss may occur.
:cpp:func:`parlio_tx_unit_decorate_bitscrambler` and :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` need to be used in pairs. When deleting the TX unit, you need to call :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` first to remove the BitScrambler.
Power Management

View File

@ -320,6 +320,12 @@ TX 单元可以选择各种不同的时钟源,其中外部时钟源较为特
编写好比特调节器程序后,通过调用 :cpp:func:`parlio_tx_unit_decorate_bitscrambler` 启用比特调节器。并在 :cpp:member:`parlio_transmit_config_t::bitscrambler_program` 配置本次传输使用比特调节器程序的二进制文件。不同的传输事务可以使用不同的比特调节器程序。该二进制文件必须符合比特调节器的汇编语言规范,并且在运行时会被加载到比特调节器的指令存储器中。如何编写并编译比特调节器程序请参考 :doc:`比特调节器编程指南 </api-reference/peripherals/bitscrambler>`
.. only:: not SOC_PARLIO_TX_SUPPORT_EOF_FROM_DMA
.. note::
由于硬件限制,使用比特调节器生成的比特流与原本比特流相比,长度不能发生变化,否则可能会发生传输阻塞或数据丢失。
:cpp:func:`parlio_tx_unit_decorate_bitscrambler`:cpp:func:`parlio_tx_unit_undecorate_bitscrambler` 需要成对使用。在删除 TX 单元时,需要先调用 :cpp:func:`parlio_tx_unit_undecorate_bitscrambler` 移除比特调节器。
电源管理

View File

@ -126,7 +126,7 @@ void example_init_parlio_panel(esp_lcd_panel_io_handle_t *io_handle)
#endif
},
.data_width = CONFIG_EXAMPLE_LCD_PARLIO_DATA_WIDTH,
.max_transfer_bytes = EXAMPLE_LCD_H_RES * 100 * sizeof(uint16_t),
.max_transfer_bytes = EXAMPLE_LCD_H_RES * EXAMPLE_LVGL_DRAW_BUF_LINES * sizeof(uint16_t),
.dma_burst_size = EXAMPLE_DMA_BURST_SIZE,
.cs_gpio_num = EXAMPLE_PIN_NUM_CS,
.pclk_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,