forked from espressif/esp-idf
feat(parlio_tx): support loop transmission
This commit is contained in:
@@ -157,6 +157,7 @@ typedef struct {
|
||||
uint32_t idle_value; /*!< The value on the data line when the parallel IO is in idle state */
|
||||
struct {
|
||||
uint32_t queue_nonblocking : 1; /*!< If set, when the transaction queue is full, driver will not block the thread but return directly */
|
||||
uint32_t loop_transmission : 1; /*!< If set, the transmission will be repeated continuously, until the tx_unit is disabled by `parlio_tx_unit_disable` */
|
||||
} flags; /*!< Transmit specific config flags */
|
||||
} parlio_transmit_config_t;
|
||||
|
||||
|
@@ -4,6 +4,7 @@ entries:
|
||||
if PARLIO_TX_ISR_HANDLER_IN_IRAM = y:
|
||||
parlio_tx: parlio_tx_default_isr (noflash)
|
||||
parlio_tx: parlio_tx_do_transaction (noflash)
|
||||
parlio_tx: parlio_mount_buffer (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)
|
||||
@@ -15,6 +16,7 @@ archive: libesp_hw_support.a
|
||||
entries:
|
||||
if PARLIO_TX_ISR_HANDLER_IN_IRAM = y:
|
||||
gdma_link: gdma_link_mount_buffers (noflash)
|
||||
gdma_link: gdma_link_concat (noflash)
|
||||
gdma_link: gdma_link_get_head_addr (noflash)
|
||||
gdma: gdma_start (noflash)
|
||||
if PARLIO_RX_ISR_HANDLER_IN_IRAM = y:
|
||||
|
@@ -61,6 +61,9 @@ typedef dma_descriptor_align8_t parlio_dma_desc_t;
|
||||
#endif
|
||||
#endif // defined(SOC_GDMA_TRIG_PERIPH_PARLIO0_BUS)
|
||||
|
||||
// loop transmission requires ping-pong link to prevent data tearing.
|
||||
#define PARLIO_DMA_LINK_NUM 2
|
||||
|
||||
#if SOC_PERIPH_CLK_CTRL_SHARED
|
||||
#define PARLIO_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC()
|
||||
#else
|
||||
|
@@ -46,6 +46,10 @@ typedef struct {
|
||||
uint32_t idle_value; // Parallel IO bus idle value
|
||||
const void *payload; // payload to be transmitted
|
||||
size_t payload_bits; // payload size in bits
|
||||
int dma_link_idx; // index of DMA link list
|
||||
struct {
|
||||
uint32_t loop_transmission : 1; // whether the transmission is in loop mode
|
||||
} flags; // Extra configuration flags
|
||||
} parlio_tx_trans_desc_t;
|
||||
|
||||
typedef struct parlio_tx_unit_t {
|
||||
@@ -54,7 +58,7 @@ typedef struct parlio_tx_unit_t {
|
||||
intr_handle_t intr; // allocated interrupt handle
|
||||
esp_pm_lock_handle_t pm_lock; // power management lock
|
||||
gdma_channel_handle_t dma_chan; // DMA channel
|
||||
gdma_link_list_handle_t dma_link; // DMA link list handle
|
||||
gdma_link_list_handle_t dma_link[PARLIO_DMA_LINK_NUM]; // DMA link list handle
|
||||
size_t int_mem_align; // Alignment for internal memory
|
||||
size_t ext_mem_align; // Alignment for external memory
|
||||
#if CONFIG_PM_ENABLE
|
||||
@@ -129,8 +133,10 @@ static esp_err_t parlio_destroy_tx_unit(parlio_tx_unit_t *tx_unit)
|
||||
// de-register from group
|
||||
parlio_unregister_unit_from_group(&tx_unit->base);
|
||||
}
|
||||
if (tx_unit->dma_link) {
|
||||
ESP_RETURN_ON_ERROR(gdma_del_link_list(tx_unit->dma_link), TAG, "delete dma link list failed");
|
||||
for (int i = 0; i < PARLIO_DMA_LINK_NUM; i++) {
|
||||
if (tx_unit->dma_link[i]) {
|
||||
ESP_RETURN_ON_ERROR(gdma_del_link_list(tx_unit->dma_link[i]), TAG, "delete dma link list failed");
|
||||
}
|
||||
}
|
||||
free(tx_unit);
|
||||
return ESP_OK;
|
||||
@@ -204,8 +210,9 @@ static esp_err_t parlio_tx_unit_init_dma(parlio_tx_unit_t *tx_unit, const parlio
|
||||
ESP_RETURN_ON_ERROR(PARLIO_GDMA_NEW_CHANNEL(&dma_chan_config, &tx_unit->dma_chan), TAG, "allocate TX DMA channel failed");
|
||||
gdma_connect(tx_unit->dma_chan, GDMA_MAKE_TRIGGER(GDMA_TRIG_PERIPH_PARLIO, 0));
|
||||
gdma_strategy_config_t gdma_strategy_conf = {
|
||||
.auto_update_desc = true,
|
||||
.owner_check = true,
|
||||
.auto_update_desc = false, // for loop transmission, we have no chance to change the owner
|
||||
.owner_check = false,
|
||||
.eof_till_data_popped = true,
|
||||
};
|
||||
gdma_apply_strategy(tx_unit->dma_chan, &gdma_strategy_conf);
|
||||
|
||||
@@ -227,7 +234,9 @@ static esp_err_t parlio_tx_unit_init_dma(parlio_tx_unit_t *tx_unit, const parlio
|
||||
};
|
||||
|
||||
// throw the error to the caller
|
||||
ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &tx_unit->dma_link), TAG, "create DMA link list failed");
|
||||
for (int i = 0; i < PARLIO_DMA_LINK_NUM; i++) {
|
||||
ESP_RETURN_ON_ERROR(gdma_new_link_list(&dma_link_config, &tx_unit->dma_link[i]), TAG, "create DMA link list failed");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
@@ -316,8 +325,6 @@ esp_err_t parlio_new_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_un
|
||||
// data_width must not conflict with the valid signal
|
||||
ESP_RETURN_ON_FALSE(!(config->valid_gpio_num >= 0 && data_width > PARLIO_LL_TX_DATA_LINE_AS_VALID_SIG),
|
||||
ESP_ERR_INVALID_ARG, TAG, "valid signal conflicts with data signal");
|
||||
ESP_RETURN_ON_FALSE(config->max_transfer_size && config->max_transfer_size <= PARLIO_LL_TX_MAX_BITS_PER_FRAME / 8,
|
||||
ESP_ERR_INVALID_ARG, TAG, "invalid max transfer size");
|
||||
#if SOC_PARLIO_TX_CLK_SUPPORT_GATING
|
||||
// clock gating is controlled by either the MSB bit of data bus or the valid signal
|
||||
ESP_RETURN_ON_FALSE(!(config->flags.clk_gate_en && config->valid_gpio_num < 0 && config->data_width <= PARLIO_LL_TX_DATA_LINE_AS_CLK_GATE),
|
||||
@@ -386,10 +393,8 @@ 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_SIZE_BY_DMA
|
||||
// Always use DATA LEN EOF as the Parlio TX EOF
|
||||
// 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_SIZE_BY_DMA
|
||||
|
||||
// clear any pending interrupt
|
||||
parlio_ll_clear_interrupt_status(hal->regs, PARLIO_LL_EVENT_TX_MASK);
|
||||
@@ -461,10 +466,38 @@ esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_uni
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void parlio_mount_buffer(parlio_tx_unit_t *tx_unit, parlio_tx_trans_desc_t *t)
|
||||
{
|
||||
// DMA transfer data based on bytes not bits, so convert the bit length to bytes, round up
|
||||
gdma_buffer_mount_config_t mount_config = {
|
||||
.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,
|
||||
.mark_final = !t->flags.loop_transmission,
|
||||
}
|
||||
};
|
||||
|
||||
int next_link_idx = t->flags.loop_transmission ? 1 - t->dma_link_idx : t->dma_link_idx;
|
||||
gdma_link_mount_buffers(tx_unit->dma_link[next_link_idx], 0, &mount_config, 1, NULL);
|
||||
|
||||
if (t->flags.loop_transmission) {
|
||||
// concatenate the DMA linked list of the next frame transmission with the DMA linked list of the current frame to realize the reuse of the current transmission transaction
|
||||
gdma_link_concat(tx_unit->dma_link[t->dma_link_idx], -1, tx_unit->dma_link[next_link_idx], 0);
|
||||
t->dma_link_idx = next_link_idx;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
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.
|
||||
@@ -478,21 +511,10 @@ static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_
|
||||
PARLIO_RCC_ATOMIC() {
|
||||
parlio_ll_tx_reset_clock(hal->regs);
|
||||
}
|
||||
|
||||
// DMA transfer data based on bytes not bits, so convert the bit length to bytes, round up
|
||||
gdma_buffer_mount_config_t mount_config = {
|
||||
.buffer = (void *)t->payload,
|
||||
.length = (t->payload_bits + 7) / 8,
|
||||
.flags = {
|
||||
.mark_eof = true,
|
||||
.mark_final = true, // singly link list, mark final descriptor
|
||||
}
|
||||
};
|
||||
// Since the threshold of the clock divider counter is not updated simultaneously with the clock source switching.
|
||||
// The update of the threshold relies on the moment when the counter reaches the threshold each time.
|
||||
// We place gdma_link_mount_buffers between reset clock and disable clock to ensure enough time for updating the threshold of the clock divider counter.
|
||||
gdma_link_mount_buffers(tx_unit->dma_link, 0, &mount_config, 1, NULL);
|
||||
|
||||
// We place parlio_mount_buffer between reset clock and disable clock to ensure enough time for updating the threshold of the clock divider counter.
|
||||
parlio_mount_buffer(tx_unit, t);
|
||||
if (switch_clk) {
|
||||
PARLIO_CLOCK_SRC_ATOMIC() {
|
||||
parlio_ll_tx_set_clock_source(hal->regs, PARLIO_CLK_SRC_EXTERNAL);
|
||||
@@ -506,7 +528,7 @@ static void parlio_tx_do_transaction(parlio_tx_unit_t *tx_unit, parlio_tx_trans_
|
||||
parlio_ll_tx_set_idle_data_value(hal->regs, t->idle_value);
|
||||
parlio_ll_tx_set_trans_bit_len(hal->regs, t->payload_bits);
|
||||
|
||||
gdma_start(tx_unit->dma_chan, gdma_link_get_head_addr(tx_unit->dma_link));
|
||||
gdma_start(tx_unit->dma_chan, gdma_link_get_head_addr(tx_unit->dma_link[t->dma_link_idx]));
|
||||
// wait until the data goes from the DMA to TX unit's FIFO
|
||||
while (parlio_ll_tx_is_ready(hal->regs) == false);
|
||||
// turn on the core clock after we start the TX unit
|
||||
@@ -592,6 +614,10 @@ 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);
|
||||
|
||||
// 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
|
||||
parlio_ll_tx_set_eof_condition(hal->regs, PARLIO_LL_TX_EOF_COND_DATA_LEN);
|
||||
|
||||
// release power management lock
|
||||
if (tx_unit->pm_lock) {
|
||||
esp_pm_lock_release(tx_unit->pm_lock);
|
||||
@@ -612,6 +638,21 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
ESP_RETURN_ON_FALSE((payload_bits % 8) == 0, ESP_ERR_INVALID_ARG, TAG, "payload bit length must be multiple of 8");
|
||||
#endif // !SOC_PARLIO_TRANS_BIT_ALIGN
|
||||
|
||||
#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");
|
||||
}
|
||||
#else
|
||||
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 (!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");
|
||||
}
|
||||
|
||||
size_t cache_line_size = 0;
|
||||
size_t alignment = 0;
|
||||
uint8_t cache_type = 0;
|
||||
@@ -627,39 +668,48 @@ esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *p
|
||||
ESP_CACHE_MSYNC_FLAG_DIR_C2M | ESP_CACHE_MSYNC_FLAG_UNALIGNED), TAG, "cache sync failed");
|
||||
}
|
||||
|
||||
TickType_t queue_wait_ticks = portMAX_DELAY;
|
||||
if (config->flags.queue_nonblocking) {
|
||||
queue_wait_ticks = 0;
|
||||
}
|
||||
parlio_tx_trans_desc_t *t = NULL;
|
||||
// acquire one transaction description from ready queue or complete queue
|
||||
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_READY], &t, 0) != pdTRUE) {
|
||||
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_COMPLETE], &t, queue_wait_ticks) == pdTRUE) {
|
||||
tx_unit->num_trans_inflight--;
|
||||
// check if to start a new transaction or update the current loop transaction
|
||||
bool no_trans_pending_in_queue = uxQueueMessagesWaiting(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS]) == 0;
|
||||
if (tx_unit->cur_trans && tx_unit->cur_trans->flags.loop_transmission && config->flags.loop_transmission && no_trans_pending_in_queue) {
|
||||
tx_unit->cur_trans->payload = payload;
|
||||
tx_unit->cur_trans->payload_bits = payload_bits;
|
||||
parlio_mount_buffer(tx_unit, tx_unit->cur_trans);
|
||||
} else {
|
||||
TickType_t queue_wait_ticks = portMAX_DELAY;
|
||||
if (config->flags.queue_nonblocking) {
|
||||
queue_wait_ticks = 0;
|
||||
}
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(t, ESP_ERR_INVALID_STATE, TAG, "no free transaction descriptor, please consider increasing trans_queue_depth");
|
||||
parlio_tx_trans_desc_t *t = NULL;
|
||||
// acquire one transaction description from ready queue or complete queue
|
||||
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_READY], &t, 0) != pdTRUE) {
|
||||
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_COMPLETE], &t, queue_wait_ticks) == pdTRUE) {
|
||||
tx_unit->num_trans_inflight--;
|
||||
}
|
||||
}
|
||||
ESP_RETURN_ON_FALSE(t, ESP_ERR_INVALID_STATE, TAG, "no free transaction descriptor, please consider increasing trans_queue_depth");
|
||||
|
||||
// fill in the transaction descriptor
|
||||
memset(t, 0, sizeof(parlio_tx_trans_desc_t));
|
||||
t->payload = payload;
|
||||
t->payload_bits = payload_bits;
|
||||
t->idle_value = config->idle_value & tx_unit->idle_value_mask;
|
||||
// fill in the transaction descriptor
|
||||
memset(t, 0, sizeof(parlio_tx_trans_desc_t));
|
||||
t->payload = payload;
|
||||
t->payload_bits = payload_bits;
|
||||
t->idle_value = config->idle_value & tx_unit->idle_value_mask;
|
||||
t->flags.loop_transmission = config->flags.loop_transmission;
|
||||
|
||||
// send the transaction descriptor to progress queue
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE,
|
||||
ESP_ERR_INVALID_STATE, TAG, "failed to send transaction descriptor to progress queue");
|
||||
tx_unit->num_trans_inflight++;
|
||||
// send the transaction descriptor to progress queue
|
||||
ESP_RETURN_ON_FALSE(xQueueSend(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE,
|
||||
ESP_ERR_INVALID_STATE, TAG, "failed to send transaction descriptor to progress queue");
|
||||
tx_unit->num_trans_inflight++;
|
||||
|
||||
// check if we need to start one pending transaction
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) {
|
||||
// check if we need to start one transaction
|
||||
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) {
|
||||
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_RUN);
|
||||
parlio_tx_do_transaction(tx_unit, t);
|
||||
} else {
|
||||
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_ENABLE);
|
||||
// check if we need to start one pending transaction
|
||||
parlio_tx_fsm_t expected_fsm = PARLIO_TX_FSM_ENABLE;
|
||||
if (atomic_compare_exchange_strong(&tx_unit->fsm, &expected_fsm, PARLIO_TX_FSM_RUN_WAIT)) {
|
||||
// check if we need to start one transaction
|
||||
if (xQueueReceive(tx_unit->trans_queues[PARLIO_TX_QUEUE_PROGRESS], &t, 0) == pdTRUE) {
|
||||
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_RUN);
|
||||
parlio_tx_do_transaction(tx_unit, t);
|
||||
} else {
|
||||
atomic_store(&tx_unit->fsm, PARLIO_TX_FSM_ENABLE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -291,7 +291,7 @@ TEST_CASE("parallel_tx_clock_gating", "[paralio_tx]")
|
||||
#endif // SOC_PARLIO_TX_CLK_SUPPORT_GATING
|
||||
|
||||
#if SOC_PSRAM_DMA_CAPABLE
|
||||
TEST_CASE("parlio can transmit PSRAM buffer", "[parlio_tx]")
|
||||
TEST_CASE("parlio_tx_can_transmit_PSRAM_buffer", "[parlio_tx]")
|
||||
{
|
||||
printf("install parlio tx unit\r\n");
|
||||
parlio_tx_unit_handle_t tx_unit = NULL;
|
||||
@@ -447,3 +447,85 @@ TEST_CASE("parallel tx unit use external non-free running clock", "[parlio_tx]")
|
||||
TEST_ESP_OK(gpio_reset_pin(config.data_gpio_nums[i]));
|
||||
}
|
||||
};
|
||||
|
||||
#if SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
TEST_CASE("parlio_tx_loop_transmission", "[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 = 8,
|
||||
.clk_in_gpio_num = -1, // use internal clock source
|
||||
.valid_gpio_num = -1,
|
||||
.clk_out_gpio_num = TEST_CLK_GPIO,
|
||||
.data_gpio_nums = {
|
||||
TEST_DATA0_GPIO,
|
||||
TEST_DATA1_GPIO,
|
||||
TEST_DATA2_GPIO,
|
||||
TEST_DATA3_GPIO,
|
||||
TEST_DATA4_GPIO,
|
||||
TEST_DATA5_GPIO,
|
||||
TEST_DATA6_GPIO,
|
||||
TEST_DATA7_GPIO,
|
||||
},
|
||||
.output_clk_freq_hz = 10 * 1000 * 1000,
|
||||
.trans_queue_depth = 3,
|
||||
.max_transfer_size = 256,
|
||||
.bit_pack_order = PARLIO_BIT_PACK_ORDER_LSB,
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_POS,
|
||||
};
|
||||
TEST_ESP_OK(parlio_new_tx_unit(&config, &tx_unit));
|
||||
TEST_ESP_OK(parlio_tx_unit_enable(tx_unit));
|
||||
|
||||
printf("send packets and check event is fired\r\n");
|
||||
parlio_transmit_config_t transmit_config = {
|
||||
.idle_value = 0x00,
|
||||
.flags.loop_transmission = true,
|
||||
};
|
||||
__attribute__((aligned(64))) uint8_t payload_loop1[256] = {0};
|
||||
__attribute__((aligned(64))) uint8_t payload_loop2[256] = {0};
|
||||
__attribute__((aligned(64))) uint8_t payload_oneshot[256] = {0};
|
||||
for (int i = 0; i < 256; i++) {
|
||||
payload_loop1[i] = i;
|
||||
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
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
#endif // SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -474,12 +474,24 @@ static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t b
|
||||
dev->tx_data_cfg.tx_bitlen = bitlen;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if tx size can be determined by DMA
|
||||
*
|
||||
* @param dev Parallel IO register base address (not used)
|
||||
*/
|
||||
static inline bool parlio_ll_tx_support_dma_eof(parl_io_dev_t *dev)
|
||||
{
|
||||
(void)dev;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the condition to generate the TX EOF event
|
||||
*
|
||||
* @param dev Parallel IO register base address
|
||||
* @param cond TX EOF condition
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_tx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_tx_eof_cond_t cond)
|
||||
{
|
||||
dev->tx_genrl_cfg.tx_eof_gen_sel = cond;
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -44,6 +44,11 @@ typedef enum {
|
||||
PARLIO_LL_RX_EOF_COND_EN_INACTIVE, /*!< RX unit generates EOF event when the external enable signal becomes inactive */
|
||||
} parlio_ll_rx_eof_cond_t;
|
||||
|
||||
typedef enum {
|
||||
PARLIO_LL_TX_EOF_COND_DATA_LEN, /*!< TX unit generates EOF event when it transmits particular data bit length that specified in `tx_bitlen`. */
|
||||
PARLIO_LL_TX_EOF_COND_DMA_EOF, /*!< TX unit generates EOF event when the DMA EOF takes place */
|
||||
} parlio_ll_tx_eof_cond_t;
|
||||
|
||||
/**
|
||||
* @brief Enable or disable the parlio peripheral APB clock
|
||||
*
|
||||
@@ -450,6 +455,19 @@ static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t b
|
||||
HAL_FORCE_MODIFY_U32_REG_FIELD(dev->tx_cfg0, tx_bytelen, bitlen / 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the condition to generate the TX EOF event (this chip does not support)
|
||||
*
|
||||
* @param dev Parallel IO register base address (not used)
|
||||
* @param cond TX EOF condition (not used)
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_tx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_tx_eof_cond_t cond)
|
||||
{
|
||||
(void) dev;
|
||||
HAL_ASSERT(cond == PARLIO_LL_TX_EOF_COND_DATA_LEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to enable the TX clock gating
|
||||
*
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -48,6 +48,11 @@ typedef enum {
|
||||
PARLIO_LL_RX_EOF_COND_EN_INACTIVE, /*!< RX unit generates EOF event when the external enable signal becomes inactive */
|
||||
} parlio_ll_rx_eof_cond_t;
|
||||
|
||||
typedef enum {
|
||||
PARLIO_LL_TX_EOF_COND_DATA_LEN, /*!< TX unit generates EOF event when it transmits particular data bit length that specified in `tx_bitlen`. */
|
||||
PARLIO_LL_TX_EOF_COND_DMA_EOF, /*!< TX unit generates EOF event when the DMA EOF takes place */
|
||||
} parlio_ll_tx_eof_cond_t;
|
||||
|
||||
/**
|
||||
* @brief Enable or disable the parlio peripheral APB clock
|
||||
*
|
||||
@@ -475,6 +480,29 @@ static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t b
|
||||
dev->tx_data_cfg.tx_bitlen = bitlen;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if tx size can be determined by DMA
|
||||
*
|
||||
* @param dev Parallel IO register base address (not used)
|
||||
*/
|
||||
static inline bool parlio_ll_tx_support_dma_eof(parl_io_dev_t *dev)
|
||||
{
|
||||
(void)dev;
|
||||
return ESP_CHIP_REV_ABOVE(efuse_hal_chip_revision(), 102);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the condition to generate the TX EOF event
|
||||
*
|
||||
* @param dev Parallel IO register base address
|
||||
* @param cond TX EOF condition
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_tx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_tx_eof_cond_t cond)
|
||||
{
|
||||
dev->tx_genrl_cfg.tx_eof_gen_sel = cond;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Whether to enable the TX clock gating
|
||||
*
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -523,12 +523,24 @@ static inline void parlio_ll_tx_set_trans_bit_len(parl_io_dev_t *dev, uint32_t b
|
||||
dev->tx_data_cfg.tx_bitlen = bitlen;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if tx size can be determined by DMA
|
||||
*
|
||||
* @param dev Parallel IO register base address (not used)
|
||||
*/
|
||||
static inline bool parlio_ll_tx_support_dma_eof(parl_io_dev_t *dev)
|
||||
{
|
||||
(void)dev;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the condition to generate the TX EOF event
|
||||
*
|
||||
* @param dev Parallel IO register base address
|
||||
* @param cond TX EOF condition
|
||||
*/
|
||||
__attribute__((always_inline))
|
||||
static inline void parlio_ll_tx_set_eof_condition(parl_io_dev_t *dev, parlio_ll_tx_eof_cond_t cond)
|
||||
{
|
||||
dev->tx_genrl_cfg.tx_eof_gen_sel = cond;
|
||||
|
@@ -987,7 +987,7 @@ config SOC_PARLIO_TRANS_BIT_ALIGN
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_PARLIO_TX_SIZE_BY_DMA
|
||||
config SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
bool
|
||||
default y
|
||||
|
||||
|
@@ -385,7 +385,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_SIZE_BY_DMA 1 /*!< Transaction length is controlled by DMA instead of indicated by register */
|
||||
#define SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION 1 /*!< Support loop transmission */
|
||||
#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 */
|
||||
|
||||
|
@@ -947,6 +947,10 @@ config SOC_PARLIO_TRANS_BIT_ALIGN
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_PARLIO_SUPPORT_SLEEP_RETENTION
|
||||
bool
|
||||
default y
|
||||
|
@@ -374,6 +374,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_SUPPORT_SLEEP_RETENTION 1 /*!< Support back up registers before sleep */
|
||||
#define SOC_PARLIO_SUPPORT_SPI_LCD 1 /*!< Support to drive SPI interfaced LCD */
|
||||
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -186,6 +186,14 @@ extern "C" {
|
||||
* Parallel TX general configuration register.
|
||||
*/
|
||||
#define PARL_IO_TX_GENRL_CFG_REG (DR_REG_PARL_IO_BASE + 0x18)
|
||||
/** PARL_IO_TX_EOF_GEN_SEL : R/W; bitpos: [13]; default: 0;
|
||||
* Configures the tx eof generated mechanism. 1'b0: eof generated by data bit length.
|
||||
* 1'b1: eof generated by DMA eof.
|
||||
*/
|
||||
#define PARL_IO_TX_EOF_GEN_SEL (BIT(13))
|
||||
#define PARL_IO_TX_EOF_GEN_SEL_M (PARL_IO_TX_EOF_GEN_SEL_V << PARL_IO_TX_EOF_GEN_SEL_S)
|
||||
#define PARL_IO_TX_EOF_GEN_SEL_V 0x00000001U
|
||||
#define PARL_IO_TX_EOF_GEN_SEL_S 13
|
||||
/** PARL_IO_TX_IDLE_VALUE : R/W; bitpos: [29:14]; default: 0;
|
||||
* Configures bus value of transmitter in IDLE state.
|
||||
*/
|
||||
|
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -179,7 +179,12 @@ typedef union {
|
||||
*/
|
||||
typedef union {
|
||||
struct {
|
||||
uint32_t reserved_0:14;
|
||||
uint32_t reserved_0:13;
|
||||
/** tx_eof_gen_sel : R/W; bitpos: [13]; default: 0;
|
||||
* Configures the tx eof generated mechanism. 1'b0: eof generated by data bit length.
|
||||
* 1'b1: eof generated by DMA eof.
|
||||
*/
|
||||
uint32_t tx_eof_gen_sel:1;
|
||||
/** tx_idle_value : R/W; bitpos: [29:14]; default: 0;
|
||||
* Configures bus value of transmitter in IDLE state.
|
||||
*/
|
||||
|
@@ -1371,7 +1371,7 @@ config SOC_PARLIO_TRANS_BIT_ALIGN
|
||||
bool
|
||||
default y
|
||||
|
||||
config SOC_PARLIO_TX_SIZE_BY_DMA
|
||||
config SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
bool
|
||||
default y
|
||||
|
||||
|
@@ -488,7 +488,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_SIZE_BY_DMA 1 /*!< Transaction length is controlled by DMA instead of indicated by register */
|
||||
#define SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION 1 /*!< Support loop transmission */
|
||||
#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 */
|
||||
|
Reference in New Issue
Block a user