Parallel IO RX Driver
This document describes the functionality of the Parallel IO RX driver in ESP-IDF. The table of contents is as follows:
Introduction
The Parallel IO RX unit is part of the general parallel interface, hereinafter referred to as the RX unit. It supports data reception from external devices via GDMA on a parallel bus. Given the flexibility of IO data, the RX unit can be used as a general interface to connect various peripherals. The main application scenarios of this driver include:
High-speed data acquisition, such as camera and sensor data reading
High-speed parallel communication with other hosts as a slave device
Logic analyzer and signal monitoring applications
Quick Start
This section will quickly guide you on how to use the RX unit driver. Through a simple example demonstrating data reception with a soft delimiter, it shows how to create and start an RX unit, initiate a receive transaction, and register event callback functions. The general usage process is as follows:
Creating and Enabling the RX Unit
First, we need to create an RX unit instance. The following code shows how to create an RX unit instance:
parlio_rx_unit_handle_t rx_unit = NULL;
parlio_rx_unit_config_t config = {
.clk_src = PARLIO_CLK_SRC_DEFAULT, // Select the default clock source
.data_width = 4, // Data width is 4 bits
.clk_in_gpio_num = -1, // Do not use an external clock source
.clk_out_gpio_num = EXAMPLE_PIN_CLK, // Output clock pin
.valid_gpio_num = EXAMPLE_PIN_VALID, // Valid signal pin
.data_gpio_nums = {
EXAMPLE_PIN_DATA0,
EXAMPLE_PIN_DATA1,
EXAMPLE_PIN_DATA2,
EXAMPLE_PIN_DATA3,
[4 ... (PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1)] = -1,
},
.exp_clk_freq_hz = 1 * 1000 * 1000, // Expected clock frequency is 1 MHz
.trans_queue_depth = 10, // Transaction queue depth is 10
.max_recv_size = 1024, // Maximum receive size is 1024 bytes
};
// Create RX unit instance
ESP_ERROR_CHECK(parlio_new_rx_unit(&config, &rx_unit));
// Enable RX unit and reset transaction queue
ESP_ERROR_CHECK(parlio_rx_unit_enable(rx_unit, true));
When creating an RX unit instance, we need to configure parameters such as the clock source, data width, and expected clock frequency through parlio_rx_unit_config_t
. Then call the parlio_new_rx_unit()
function to create a new RX unit instance, which will return a handle pointing to the new instance. The instance handle is essentially a pointer to the RX unit memory object, of type parlio_rx_unit_handle_t
.
The following are the configuration parameters of the parlio_rx_unit_config_t
structure and their explanations:
parlio_rx_unit_config_t::clk_src
Sets the clock source of the RX unit. Available clock sources are listed inparlio_clock_source_t
, and only one can be selected. Different clock sources vary in resolution, accuracy, and power consumption.parlio_rx_unit_config_t::clk_in_gpio_num
Uses an external clock as the clock source, setting the corresponding GPIO number for clock input. Otherwise, set to -1, and the driver will use the internalparlio_rx_unit_config_t::clk_src
as the clock source.parlio_rx_unit_config_t::ext_clk_freq_hz
The frequency of the external input clock source, valid only whenparlio_rx_unit_config_t::clk_in_gpio_num
is not -1.parlio_rx_unit_config_t::exp_clk_freq_hz
Sets the expected sample/bit clock frequency, which is divided from the internal or external clock regarding the clock source.parlio_rx_unit_config_t::clk_out_gpio_num
The GPIO number for the output clock signal (if supported). Set to -1 if not needed.parlio_rx_unit_config_t::data_width
The data bus width of the RX unit, must be a power of 2 and not greater than 8.parlio_rx_unit_config_t::data_gpio_nums
The GPIO numbers for RX data, unused GPIOs should be set to -1.parlio_rx_unit_config_t::valid_gpio_num
The GPIO number for the valid signal, set to -1 if not used. The valid signal indicates whether the data on the data lines are valid.parlio_rx_unit_config_t::trans_queue_depth
The depth of the internal transaction queue. The deeper the queue, the more transactions can be prepared in the pending queue.parlio_rx_unit_config_t::max_recv_size
The maximum receive size per transaction (in bytes). This decides the number of DMA nodes will be used for each transaction.parlio_rx_unit_config_t::flags
Usually used to fine-tune some behaviors of the driver, including the following optionsparlio_rx_unit_config_t::flags::free_clk
Whether the input external clock is a free-running clock. A free-running clock will always keep running (e.g. I2S bclk), a non-free-running clock will start when there are data transporting and stop when the bus idle (e.g. SPI).parlio_rx_unit_config_t::flags::clk_gate_en
Enable RX clock gating, the output clock will be controlled by the valid gpio.parlio_rx_unit_config_t::flags::allow_pd
Set to allow power down. When this flag set, the driver will backup/restore the PARLIO registers before/after entering/exist sleep mode.
Note
If all RX units in the current chip have been requested, the parlio_new_rx_unit()
function will return the ESP_ERR_NOT_FOUND
error.
The RX unit must be enabled before use. The enable function parlio_rx_unit_enable()
can switch the internal state machine of the driver to the active state, which also includes some system service requests/registrations, such as requesting a power management lock and resetting the transaction queue. The corresponding disable function is parlio_rx_unit_disable()
, which will release all system services.
Creating Delimiters
Before initiating receive transactions, we need to create delimiters that define when a frame starts and ends. The RX unit supports three types of delimiters:
Level Delimiter: Uses a level signal to split valid data into frames.
parlio_rx_delimiter_handle_t level_delimiter = NULL;
parlio_rx_level_delimiter_config_t level_config = {
.valid_sig_line_id = 4, // Use data line 4 as valid signal input
.sample_edge = PARLIO_SAMPLE_EDGE_POS, // Sample on positive edge
.bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, // Pack bits from MSB
.eof_data_len = 256, // End of frame interrupt triggers after 256 bytes, if set to 0, the EOF will be triggered when the valid signal is disabled
.timeout_ticks = 1000, // Timeout interrupt triggers after 1000 clock ticks since the valid signal is disabled but no enough data for EOF. If set to 0, the timeout interrupt will not be triggered
.flags = {
.active_low_en = false, // Active high level
},
};
ESP_ERROR_CHECK(parlio_new_rx_level_delimiter(&level_config, &level_delimiter));
Pulse Delimiter: Uses pulse signals to split valid data into frames.
parlio_rx_delimiter_handle_t pulse_delimiter = NULL;
parlio_rx_pulse_delimiter_config_t pulse_config = {
.valid_sig_line_id = 4, // Use data line 4 as valid signal input
.sample_edge = PARLIO_SAMPLE_EDGE_NEG, // Sample on negative edge
.bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, // Pack bits from MSB
.eof_data_len = 128, // End of frame interrupt triggers after 128 bytes, if set to 0, the EOF will be triggered when the valid signal is disabled
.timeout_ticks = 500, // Timeout interrupt triggers after 500 clock ticks since the valid signal is disabled but no enough data for EOF. If set to 0, the timeout interrupt will not be triggered
.flags = {
.start_bit_included = false, // Start bit not included in data
.end_bit_included = false, // End bit not included in data
.has_end_pulse = true, // Has end pulse to terminate
.pulse_invert = false, // Do not invert pulse
},
};
ESP_ERROR_CHECK(parlio_new_rx_pulse_delimiter(&pulse_config, &pulse_delimiter));
Soft Delimiter: Uses software-defined data length to split valid data into frames.
parlio_rx_delimiter_handle_t soft_delimiter = NULL;
parlio_rx_soft_delimiter_config_t soft_config = {
.sample_edge = PARLIO_SAMPLE_EDGE_POS, // Sample on positive edge
.bit_pack_order = PARLIO_BIT_PACK_ORDER_MSB, // Pack bits from MSB
.eof_data_len = 512, // End of frame after 512 bytes, since there is no other end condition, the soft delimiter must set this field
.timeout_ticks = 0, // No timeout
};
ESP_ERROR_CHECK(parlio_new_rx_soft_delimiter(&soft_config, &soft_delimiter));
Initiating RX Receive Transactions
After enabling the RX unit and creating delimiters, we can configure receive parameters and call parlio_rx_unit_receive()
to start the RX transaction. The following code shows how to initiate an RX unit receive transaction:
#define PAYLOAD_SIZE 512
// Allocate DMA compatible buffer
uint8_t *payload = heap_caps_calloc(1, PAYLOAD_SIZE, MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT | MALLOC_CAP_DMA);
// Configure RX unit receive parameters
parlio_receive_config_t receive_config = {
.delimiter = soft_delimiter, // Use the soft delimiter created above
.flags = {
.partial_rx_en = false, // Disable partial receive mode
.indirect_mount = false, // Direct mount to user buffer to DMA
},
};
// Start soft delimiter (required for soft delimiter only)
ESP_ERROR_CHECK(parlio_rx_soft_delimiter_start_stop(rx_unit, soft_delimiter, true));
// Start receive transaction
ESP_ERROR_CHECK(parlio_rx_unit_receive(rx_unit, payload, PAYLOAD_SIZE, &receive_config));
// Wait for receive transaction to complete
ESP_ERROR_CHECK(parlio_rx_unit_wait_all_done(rx_unit, 5000)); // Wait up to 5 seconds
// Stop soft delimiter
ESP_ERROR_CHECK(parlio_rx_soft_delimiter_start_stop(rx_unit, soft_delimiter, false));
The RX unit receives data in bytes, and the received data length depends on the delimiter configuration. Calling parlio_rx_unit_receive()
starts the RX transaction, which requires parameters such as the unit handle, payload buffer, and payload size (in bytes). Additionally, specific configurations for the reception should be provided in parlio_receive_config_t
.
The following are the configuration parameters of the parlio_receive_config_t
structure and their explanations:
parlio_receive_config_t::delimiter
The delimiter to be used for this receive transaction.parlio_receive_config_t::flags
Usually used to fine-tune some behaviors of the reception, including the following optionsparlio_receive_config_t::flags::partial_rx_en
Whether this is an infinite transaction that supposed to receive continuously and partially.parlio_receive_config_t::flags::indirect_mount
Enable this flag to use an INTERNAL DMA buffer instead of the user payload buffer. The data will be copied to the payload in every interrupt.
parlio_rx_unit_receive()
internally constructs a transaction descriptor and sends it to the work queue, which is usually scheduled in the ISR context. Therefore, when parlio_rx_unit_receive()
returns, the transaction may not have started yet. Note that you cannot recycle or modify the contents of the payload before the transaction ends. By registering event callbacks through parlio_rx_unit_register_event_callbacks()
, you can be notified when the transaction is complete. To ensure all pending transactions are completed, you can also call parlio_rx_unit_wait_all_done()
, providing a blocking receive function.
Registering Event Callbacks
Since parlio_rx_unit_receive()
is an asynchronous interface, we may want to know when the receive transaction is complete or when partial data is received. The following code shows how to register event callbacks:
static bool on_partial_receive_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
{
// Called when partial data is received (for infinite transactions). You can do simple processing in the callback, such as queueing, task operations, or copying the received data to the user buffer.
return false; // Return true if high priority task should be woken up
}
static bool on_receive_done_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
{
// Called when receive transaction is complete
BaseType_t high_task_wakeup = pdFalse;
TaskHandle_t task = (TaskHandle_t)user_ctx;
// Notify the waiting task
vTaskNotifyGiveFromISR(task, &high_task_wakeup);
return (high_task_wakeup == pdTRUE);
}
static bool on_timeout_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
{
// Called when receive timeout occurs
return false;
}
parlio_rx_event_callbacks_t cbs = {
.on_partial_receive = on_partial_receive_callback,
.on_receive_done = on_receive_done_callback,
.on_timeout = on_timeout_callback,
};
ESP_ERROR_CHECK(parlio_rx_unit_register_event_callbacks(rx_unit, &cbs, xTaskGetCurrentTaskHandle()));
When the RX unit generates events such as receive done or timeout, it will notify the CPU via interrupts. If you need to call a function when a specific event occurs, you can call parlio_rx_unit_register_event_callbacks()
to register event callbacks to the RX unit driver's interrupt service routine (ISR). Since the callback function is called in the ISR, complex operations (including any operations that may cause blocking) should be avoided in the callback function to avoid affecting the system's real-time performance.
For the event callbacks supported by the RX unit, refer to parlio_rx_event_callbacks_t
:
parlio_rx_event_callbacks_t::on_partial_receive
Sets the callback function for the "partial data received" event, with the function prototype declared asparlio_rx_callback_t
.parlio_rx_event_callbacks_t::on_receive_done
Sets the callback function for the "receive complete" event, with the function prototype declared asparlio_rx_callback_t
.parlio_rx_event_callbacks_t::on_timeout
Sets the callback function for the "receive timeout" event, with the function prototype declared asparlio_rx_callback_t
. The timeout ticks is determined by theparlio_rx_level_delimiter_config_t::timeout_ticks
,parlio_rx_pulse_delimiter_config_t::timeout_ticks
orparlio_rx_soft_delimiter_config_t::timeout_ticks
.
Resource Recycling
When the RX unit is no longer needed, the parlio_del_rx_unit()
function should be called to release software and hardware resources. Ensure the RX unit is disabled before deletion. Also remember to delete the delimiters.
ESP_ERROR_CHECK(parlio_rx_unit_disable(rx_unit));
ESP_ERROR_CHECK(parlio_del_rx_unit(rx_unit));
ESP_ERROR_CHECK(parlio_del_rx_delimiter(soft_delimiter));
free(payload);
Advanced Features
After understanding the basic usage, we can further explore more advanced features of the RX unit driver.
Using an External Clock as the RX Unit Clock Source
The RX unit can choose various clock sources, among which the external clock source is special. We enable the external clock source input by configuring parlio_rx_unit_config_t::clk_src
, parlio_rx_unit_config_t::clk_in_gpio_num
, and parlio_rx_unit_config_t::ext_clk_freq_hz
:
parlio_rx_unit_handle_t rx_unit = NULL;
parlio_rx_unit_config_t config = {
.clk_src = PARLIO_CLK_SRC_EXTERNAL, // Select external clock source
.data_width = 4, // Data width is 4 bits
.clk_in_gpio_num = EXAMPLE_PIN_CLK_IN, // Set external clock source input pin
.ext_clk_freq_hz = 10 * 1000 * 1000, // External clock source frequency is 10 MHz
.exp_clk_freq_hz = 10 * 1000 * 1000, // Expected clock frequency matches external
.valid_gpio_num = EXAMPLE_PIN_VALID, // Valid signal pin
.data_gpio_nums = {
EXAMPLE_PIN_DATA0,
EXAMPLE_PIN_DATA1,
EXAMPLE_PIN_DATA2,
EXAMPLE_PIN_DATA3,
[4 ... (PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1)] = -1,
},
.trans_queue_depth = 10,
.max_recv_size = 1024,
.flags = {
.free_clk = true, // External clock is free-running
},
};
// Create RX unit instance
ESP_ERROR_CHECK(parlio_new_rx_unit(&config, &rx_unit));
// Enable RX unit
ESP_ERROR_CHECK(parlio_rx_unit_enable(rx_unit, true));
Note
When using an external clock source, ensure that parlio_rx_unit_config_t::ext_clk_freq_hz
matches the actual frequency of the external clock for proper operation.
Infinite Receive Transactions
The RX unit supports infinite receive transactions where it continuously receives data in a streaming fashion. This is useful for applications like logic analyzers or continuous data monitoring:
// Configure infinite receive transaction
parlio_receive_config_t receive_config = {
.delimiter = soft_delimiter,
.flags = {
.partial_rx_en = true, // Enable infinite/partial receive mode
.indirect_mount = true, // Use internal buffer to avoid data corruption
},
};
// Start soft delimiter
ESP_ERROR_CHECK(parlio_rx_soft_delimiter_start_stop(rx_unit, soft_delimiter, true));
// Start infinite receive transaction
ESP_ERROR_CHECK(parlio_rx_unit_receive(rx_unit, payload, PAYLOAD_SIZE, &receive_config));
// The transaction will continue indefinitely, with partial receive callbacks being triggered as data is received.
// Use parlio_rx_soft_delimiter_start_stop to stop the transaction when needed.
vTaskDelay(pdMS_TO_TICKS(5000)); // Let it run for 5 seconds
// Stop the infinite transaction
ESP_ERROR_CHECK(parlio_rx_soft_delimiter_start_stop(rx_unit, soft_delimiter, false));
In infinite receive mode, the parlio_rx_event_callbacks_t::on_partial_receive
callback will be triggered each time the internal buffer is filled, and the data will be copied to the user buffer if parlio_receive_config_t::flags::indirect_mount
is enabled.
ISR Context Receive
For applications requiring very low latency, the RX unit driver provides parlio_rx_unit_receive_from_isr()
which can be called from ISR context, such as within event callbacks:
static bool on_receive_done_isr_callback(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_ctx)
{
// Queue another receive transaction immediately from ISR context
parlio_receive_config_t *config = (parlio_receive_config_t *)user_ctx;
uint8_t *next_buffer = get_next_buffer(); // User-defined function
bool hp_task_woken = false;
esp_err_t ret = parlio_rx_unit_receive_from_isr(rx_unit, next_buffer, BUFFER_SIZE, config, &hp_task_woken);
if (ret != ESP_OK) {
// Handle error
}
return hp_task_woken;
}
Power Management
When power management CONFIG_PM_ENABLE is enabled, the system may adjust or disable the clock source before entering sleep, causing the RX unit's internal time base to not work as expected.
To prevent this, the RX unit driver internally creates a power management lock. The type of lock is set according to different clock sources. The driver will acquire the lock in parlio_rx_unit_enable()
and release the lock in parlio_rx_unit_disable()
. This means that regardless of the power management policy, the system will not enter sleep mode, and the clock source will not be disabled or adjusted between these two functions, ensuring that any RX transaction can work normally.
In addition to turning off the clock source, the system can also turn off the RX unit's power to further reduce power consumption when entering sleep mode. To achieve this, set parlio_rx_unit_config_t::allow_pd
to true
. Before the system enters sleep mode, the RX unit's register context will be backed up to memory and restored when the system wakes up. Note that enabling this option can reduce power consumption but will increase memory usage.
Thread Safety
The driver uses critical sections to ensure atomic operations on registers. Key members in the driver handle are also protected by critical sections. The driver's internal state machine uses atomic instructions to ensure thread safety, and uses thread-safe FreeRTOS queues to manage receive transactions. Therefore, RX unit driver APIs can be used in a multi-threaded environment without extra locking.
Cache Safety
When the file system performs Flash read/write operations, the system temporarily disables the Cache function to avoid errors when loading instructions and data from Flash. This will cause the RX unit's interrupt handler to be unresponsive during this period, preventing user callback functions from being executed in time. If you want the interrupt handler to run normally while the Cache is disabled, you can enable the CONFIG_PARLIO_RX_ISR_CACHE_SAFE option.
Note
Note that after enabling this option, all interrupt callback functions and their context data must reside in internal memory. Because when the Cache is disabled, the system cannot load data and instructions from external memory.
Note
When the following options are enabled, the Cache will not be disabled automatically during Flash read/write operations. You don't have to enable the CONFIG_PARLIO_RX_ISR_CACHE_SAFE.
Performance
To improve the real-time response capability of interrupt handling, the RX unit driver provides the CONFIG_PARLIO_RX_ISR_HANDLER_IN_IRAM option. Enabling this option will place the interrupt handler in internal RAM, reducing the latency caused by cache misses when loading instructions from Flash.
Note
However, user callback functions and context data called by the interrupt handler may still be located in Flash, and cache miss issues will still exist. Users need to place callback functions and data in internal RAM, for example, using IRAM_ATTR
and DRAM_ATTR
.
And please also take care that, when the parlio_receive_config_t::flags::indirect_mount
option is enabled, the driver will use an internal DMA buffer instead of the user payload buffer. The data will be copied to the payload in the interrupt. Therefore, using this option will slightly reduce the data throughput efficiency.
Other Kconfig Options
CONFIG_PARLIO_ENABLE_DEBUG_LOG option allows forcing the enablement of all debug logs of the RX unit driver, regardless of the global log level setting. Enabling this option can help developers obtain more detailed log information during debugging, making it easier to locate and solve problems. This option is shared with the TX unit driver.
Resource Consumption
Use the IDF Size tool to view the code and data consumption of the RX unit driver. The following are the test conditions (taking ESP32-H2 as an example):
The compiler optimization level is set to
-Os
to ensure the minimum code size.The default log level is set to
ESP_LOG_INFO
to balance debugging information and performance.- The following driver optimization options are disabled:
CONFIG_PARLIO_RX_ISR_HANDLER_IN_IRAM - The interrupt handler is not placed in IRAM.
CONFIG_PARLIO_RX_ISR_CACHE_SAFE - The Cache safety option is not enabled.
Note that the following data is not precise and is for reference only. The data may vary on different chip models and different versions of IDF.
Component Layer |
Total Size |
DIRAM |
.bss |
.data |
.text |
Flash Code |
.rodata |
.text |
---|---|---|---|---|---|---|---|---|
soc |
100 |
0 |
0 |
0 |
0 |
100 |
0 |
100 |
hal |
18 |
0 |
0 |
0 |
0 |
18 |
0 |
18 |
driver |
9666 |
0 |
0 |
0 |
0 |
9666 |
618 |
9048 |
In addition, each RX unit handle dynamically allocates about 700
bytes of memory from the heap (transaction queue depth is 10). If the parlio_rx_unit_config_t::flags::allow_pd
option is enabled, each RX unit will consume an additional 32
bytes of memory during sleep to save the register context.
Application Examples
peripherals/parlio/parlio_rx/logic_analyzer demonstrates how to use the Parallel IO RX peripheral to implement a logic analyzer. This analyzer can sample data on multiple GPIOs at high frequency, monitor internal or external signals, and save the raw sampled data to Flash or output it through a TCP stream.
API Reference
Header File
This header file can be included with:
#include "driver/parlio_rx.h"
This header file is a part of the API provided by the
esp_driver_parlio
component. To declare that your component depends onesp_driver_parlio
, add the following to your CMakeLists.txt:REQUIRES esp_driver_parlio
or
PRIV_REQUIRES esp_driver_parlio
Functions
-
esp_err_t parlio_new_rx_unit(const parlio_rx_unit_config_t *config, parlio_rx_unit_handle_t *ret_unit)
Create a Parallel IO RX unit.
- Parameters:
config -- [in] Parallel IO RX unit configuration
ret_unit -- [out] Returned Parallel IO RX unit handle
- Returns:
ESP_ERR_INVALID_ARG Invalid arguments in the parameter list or the rx unit configuration
ESP_ERR_NOT_FOUND No available rx unit found
ESP_ERR_NO_MEM No enough memory for the rx unit resources
ESP_OK Success to allocate the rx unit
-
esp_err_t parlio_del_rx_unit(parlio_rx_unit_handle_t rx_unit)
Delete a Parallel IO RX unit.
- Parameters:
rx_unit -- [in] Parallel IO RX unit handle that created by
parlio_new_rx_unit
- Returns:
ESP_ERR_INVALID_ARG rx_unit is NULL
ESP_ERR_INVALID_STATE The rx unit is enabled, can't delete an enabled rx unit
ESP_OK Success to delete the rx unit
-
esp_err_t parlio_new_rx_level_delimiter(const parlio_rx_level_delimiter_config_t *config, parlio_rx_delimiter_handle_t *ret_delimiter)
Create a level delimiter.
Note
This function only allocate the software resources, the hardware configurations will lazy installed while the transaction that using this delimiter start processing
Note
The enable signal must be aligned with the valid data.
Note
There're at most
SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1
IO pins left for RXD- Parameters:
config -- [in] Level delimiter configuration
ret_delimiter -- [out] Returned delimiter handle
- Returns:
ESP_ERR_INVALID_ARG Invalid arguments in the parameter list or the level delimiter configuration
ESP_ERR_NO_MEM No enough memory for the level delimiter resources
ESP_OK Success to allocate the level delimiter
-
esp_err_t parlio_new_rx_pulse_delimiter(const parlio_rx_pulse_delimiter_config_t *config, parlio_rx_delimiter_handle_t *ret_delimiter)
Create a pulse delimiter.
Note
This function only allocate the software resources, the hardware configurations will lazy installed while the transaction that using this delimiter start processing
Note
There're at most
SOC_PARLIO_RX_UNIT_MAX_DATA_WIDTH - 1
IO pins left for RXD- Parameters:
config -- [in] Pulse delimiter configuration
ret_delimiter -- [out] Returned delimiter handle
- Returns:
ESP_ERR_INVALID_ARG Invalid arguments in the parameter list or the pulse delimiter configuration
ESP_ERR_NO_MEM No enough memory for the pulse delimiter resources
ESP_OK Success to allocate the pulse delimiter
-
esp_err_t parlio_new_rx_soft_delimiter(const parlio_rx_soft_delimiter_config_t *config, parlio_rx_delimiter_handle_t *ret_delimiter)
Create a pulse delimiter.
Note
This function only allocate the software resources, the hardware configurations will lazy installed while the transaction that using this delimiter start processing
- Parameters:
config -- [in] Soft delimiter configuration
ret_delimiter -- [out] Returned delimiter handle
- Returns:
ESP_ERR_INVALID_ARG Invalid arguments in the parameter list or the soft delimiter configuration
ESP_ERR_NO_MEM No enough memory for the soft delimiter resources
ESP_OK Success to allocate the soft delimiter
-
esp_err_t parlio_rx_soft_delimiter_start_stop(parlio_rx_unit_handle_t rx_unit, parlio_rx_delimiter_handle_t delimiter, bool start_stop)
Start/stop the soft delimiter.
Note
Soft delimiter need to start or stop manually because it has no validating/enabling signal to indicate the data has started or stopped
- Parameters:
rx_unit -- [in] Parallel IO RX unit handle that created by
parlio_new_rx_unit
delimiter -- [in] Delimiter handle
start_stop -- [in] Set true to start, set false to stop
- Returns:
ESP_ERR_INVALID_ARG Invalid arguments in the parameter list or not soft delimiter
ESP_ERR_INVALID_STATE The rx unit not enabled
ESP_OK Success to start or stop the soft delimiter
-
esp_err_t parlio_del_rx_delimiter(parlio_rx_delimiter_handle_t delimiter)
Delete the delimiter.
Note
To delete the delimiter safely, please delete it after disable all the RX units
- Parameters:
delimiter -- [in] Delimiter handle
- Returns:
ESP_ERR_INVALID_ARG The input delimiter is NULL
ESP_ERR_INVALID_STATE The delimiter is on receiving
ESP_OK Success to delete the delimiter
-
esp_err_t parlio_rx_unit_enable(parlio_rx_unit_handle_t rx_unit, bool reset_queue)
Enable the Parallel IO RX unit.
- Parameters:
rx_unit -- [in] Parallel IO RX unit handle that created by
parlio_new_rx_unit
reset_queue -- [in] Whether to reset the receiving queue. If set to false, the legacy receive transactions in the queue are still available, If set to true, the legacy receive transactions in the queue are dropped.
- Returns:
ESP_ERR_INVALID_ARG The input rx_unit is NULL
ESP_ERR_INVALID_STATE The rx unit has been enabled
ESP_OK Success to enable the rx unit
-
esp_err_t parlio_rx_unit_disable(parlio_rx_unit_handle_t rx_unit)
Disable the Parallel IO RX unit.
- Parameters:
rx_unit -- [in] Parallel IO RX unit handle that created by
parlio_new_rx_unit
- Returns:
ESP_ERR_INVALID_ARG The input rx_unit is NULL
ESP_ERR_INVALID_STATE The rx unit has been disabled
ESP_OK Success to disable the rx unit
-
esp_err_t parlio_rx_unit_receive(parlio_rx_unit_handle_t rx_unit, void *payload, size_t payload_size, const parlio_receive_config_t *recv_cfg)
Receive data by Parallel IO RX unit.
Note
This is a non-blocking and asynchronous function. To block or realize synchronous receive, please call
parlio_rx_unit_wait_all_done
after this functionNote
The receive transaction will start immediately when there is not other transaction on receiving, Otherwise it will be sent to the transaction queue to wait for the bus.
- Parameters:
rx_unit -- [in] Parallel IO RX unit handle that created by
parlio_new_rx_unit
payload -- [in] The payload buffer pointer
payload_size -- [in] The size of the payload buffer, in bytes.
recv_cfg -- [in] The configuration of this receive transaction
- Returns:
ESP_ERR_INVALID_ARG Invalid arguments in the parameter list or the receive configuration
ESP_ERR_NO_MEM No memory for the internal DMA buffer (only when parlio_receive_config_t::indirect_mount enabled)
ESP_ERR_INVALID_STATE Transaction queue is full, failed to queue the current transaction. Or the internal buffer is under using by an infinite transaction, can't allocate a new one
ESP_OK Success to queue the current receiving transaction
-
esp_err_t parlio_rx_unit_receive_from_isr(parlio_rx_unit_handle_t rx_unit, void *payload, size_t payload_size, const parlio_receive_config_t *recv_cfg, bool *hp_task_woken)
Receive data by Parallel IO RX unit in ISR context (e.g. inside a callback function)
Note
This function should only be called in ISR context, and the callback function should not block.
Note
The payload buffer should be accessible in ISR context. The payload buffer will be sent to the tail of the transaction queue.
- Parameters:
rx_unit -- [in] Parallel IO RX unit handle that created by
parlio_new_rx_unit
payload -- [in] The payload buffer pointer
payload_size -- [in] The size of the payload buffer, in bytes.
recv_cfg -- [in] The configuration of this receive transaction
hp_task_woken -- [out] Whether the high priority task is woken (Optional, set NULL if not needed)
- Returns:
ESP_OK: success to queue the transaction
ESP_FAIL: failed to queue the transaction since the queue is full
ESP_ERR_INVALID_ARG: invalid arguments, some conditions are not met
ESP_ERR_INVALID_STATE: invalid state
-
esp_err_t parlio_rx_unit_wait_all_done(parlio_rx_unit_handle_t rx_unit, int timeout_ms)
Wait for all pending RX transactions done.
Note
This function will block until all receiving transactions done or timeout. When timeout occurs, either the timeout limitation too short for all transactions done, or the peripheral got stuck and no more interrupts trigger (e.g., external clock stopped).
- Parameters:
rx_unit -- [in] Parallel IO RX unit handle that created by
parlio_new_rx_unit
timeout_ms -- [in] Timeout in milliseconds,
-1
means to wait forever (software timeout)
- Returns:
ESP_ERR_INVALID_ARG The input rx_unit is NULL
ESP_ERR_TIMEOUT Wait for all transactions done timeout
ESP_OK All transaction done
-
esp_err_t parlio_rx_unit_register_event_callbacks(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_callbacks_t *cbs, void *user_data)
Register event callbacks for Parallel IO RX unit.
- Parameters:
rx_unit -- [in] Parallel IO RX unit handle that created by
parlio_new_rx_unit
cbs -- [in] Callback group, set callback to NULL to deregister the corresponding callback (callback group pointer shouldn't be NULL)
user_data -- [in] User specified data that will be transported to the callbacks
- Returns:
ESP_ERR_INVALID_ARG The input rx_unit is NULL
ESP_ERR_INVALID_STATE The rx unit has been enabled, callback should be registered before enabling the unit
ESP_OK Success to register the callbacks
Structures
-
struct parlio_rx_unit_config_t
Parallel IO RX unit configuration.
Public Members
-
size_t trans_queue_depth
Depth of internal transaction queue
-
size_t max_recv_size
Maximum receive size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction
-
size_t data_width
Parallel IO data width, can set to 1/2/4/8/..., but can't be greater than PARLIO_RX_UNIT_MAX_DATA_WIDTH
-
parlio_clock_source_t clk_src
Parallel IO clock source
-
uint32_t ext_clk_freq_hz
The external source clock frequency. Only be valid when select PARLIO_CLK_SRC_EXTERNAL as clock source
-
uint32_t exp_clk_freq_hz
The expected sample/bit clock frequency, which is divided from the internal or external clock regarding the clock source
-
gpio_num_t clk_in_gpio_num
The the external clock input pin. Only be valid when select PARLIO_CLK_SRC_EXTERNAL as clock source. Set to -1 if not needed
-
gpio_num_t clk_out_gpio_num
The sample/bit clock output pin. Set to -1 if not needed
-
gpio_num_t valid_gpio_num
GPIO number of the valid signal. The signal on this pin is used to indicate whether the data on the data lines are valid. Only takes effect when using level or pulse delimiter, set to
-1
if only use the soft delimiter
-
gpio_num_t data_gpio_nums[PARLIO_RX_UNIT_MAX_DATA_WIDTH]
Parallel IO data GPIO numbers, set to
-1
if it's not used, The driver will take [0 .. (data_width - 1)] as the data pins
-
uint32_t free_clk
Whether the input external clock is a free-running clock. A free-running clock will always keep running (e.g. I2S bclk), a non-free-running clock will start when there are data transporting and stop when the bus idle (e.g. SPI). This flag only takes effect when select PARLIO_CLK_SRC_EXTERNAL as clock source
-
uint32_t clk_gate_en
Enable RX clock gating, only available when the clock direction is output(not supported on ESP32-C6) the output clock will be controlled by the valid gpio, i.e. high level of valid gpio to enable the clock output, low to disable
-
uint32_t allow_pd
Set to allow power down. When this flag set, the driver will backup/restore the PARLIO registers before/after entering/exist sleep mode. By this approach, the system can power off PARLIO's power domain. This can save power, but at the expense of more RAM being consumed.
-
struct parlio_rx_unit_config_t flags
RX driver flags
-
size_t trans_queue_depth
-
struct parlio_rx_level_delimiter_config_t
Configuration of level delimiter.
Public Members
-
uint32_t valid_sig_line_id
The data line id of valid/enable signal. The selected data line will be used as the valid/enable signal (i.e. level signal) in this delimiter. As the line of valid/enable signal is shared with the data line, this line_id will be conflict with the data line if set the id within 'data_width', therefore the range is (data_width, PARLIO_RX_UNIT_MAX_DATA_WIDTH].
-
parlio_sample_edge_t sample_edge
Parallel IO sample edge
-
parlio_bit_pack_order_t bit_pack_order
Set how we pack the bits into one bytes
-
uint32_t eof_data_len
Set the data length to trigger the End Of Frame (EOF, i.e. transaction done) interrupt, if the data length is set to
0
, that mean the EOF will only triggers when the enable signal inactivated
-
uint32_t timeout_ticks
The number of source clock ticks to trigger timeout interrupt. Set 0 to disable the receive timeout interrupt The timeout counter starts when the valid/enable signal is invalid/disabled.
-
uint32_t active_low_en
Set true to set the valid signal active when the level is low, otherwise, the valid signal becomes active when its level is high
-
struct parlio_rx_level_delimiter_config_t flags
Extra flags
-
uint32_t valid_sig_line_id
-
struct parlio_rx_pulse_delimiter_config_t
Configuration of pulse delimiter.
Public Members
-
uint32_t valid_sig_line_id
The data line id of valid/enable signal. The selected data line will be used as the valid/enable signal (i.e. pulse signal) in this delimiter. As the line of valid/enable signal is shared with the data line, this line_id will be conflict with the data line if set the id within 'data_width', therefore the range is (data_width, PARLIO_RX_UNIT_MAX_DATA_WIDTH].
-
parlio_sample_edge_t sample_edge
Parallel IO sample edge
-
parlio_bit_pack_order_t bit_pack_order
Set how we pack the bits into one bytes
-
uint32_t eof_data_len
Set the data length to trigger the End Of Frame (EOF, i.e. transaction done) interrupt, if the data length is set to
0
, that mean the EOF will only triggers when the end pulse detected, please ensure there is an end pulse for a frame andhas_end_pulse
flag is set
-
uint32_t timeout_ticks
The number of source clock ticks to trigger timeout interrupt. Set 0 to disable the receive timeout interrupt The timeout counter starts when the valid/enable signal is invalid/disabled.
-
uint32_t start_bit_included
Whether data bit is included in the start pulse
-
uint32_t end_bit_included
Whether data bit is included in the end pulse, only valid when
has_end_pulse
is true
-
uint32_t has_end_pulse
Whether there's an end pulse to terminate the transaction, if no, the transaction will be terminated by user configured transaction length
-
uint32_t pulse_invert
Whether to invert the pulse
-
struct parlio_rx_pulse_delimiter_config_t flags
Extra flags
-
uint32_t valid_sig_line_id
-
struct parlio_rx_soft_delimiter_config_t
Configuration of soft delimiter.
Public Members
-
parlio_sample_edge_t sample_edge
Parallel IO sample edge
-
parlio_bit_pack_order_t bit_pack_order
Set how we pack the bits into one bytes, set 1 to pack the bits into a byte from LSB, otherwise from MSB
-
uint32_t eof_data_len
Set the data length to trigger the End Of Frame (EOF, i.e. transaction done) interrupt, if the data length is set to
0
, that mean the EOF will only triggers
-
uint32_t timeout_ticks
The number of APB clock ticks to trigger timeout interrupt. Set 0 to disable the receive timeout interrupt The timeout counter starts when soft delimiter is stopped but the data is still not enough for EOF.
-
parlio_sample_edge_t sample_edge
-
struct parlio_receive_config_t
Configuration of a receive transaction.
Public Members
-
parlio_rx_delimiter_handle_t delimiter
The delimiter of this receiving transaction
-
uint32_t partial_rx_en
Whether this is an infinite transaction that supposed to receive continuously and partially
-
uint32_t indirect_mount
This flag only take effect when
partial_rx_en
is enabled. Enable this flag, an INTERNAL DMA buffer will be mounted to the DMA descriptor instead, The data will be copy to the payload in every interrupt. So that to guarantee the payload buffer is valid during theon_receive_done
callback. Eitherpartial_rx_en
orindirect_mount
is disabled, the user given finite payload will be mounted to the DMA descriptor directly. By default, the user given receive payload will be mounted to the DMA descriptor directly.
-
struct parlio_receive_config_t flags
Extra flags
-
parlio_rx_delimiter_handle_t delimiter
-
struct parlio_rx_event_data_t
Event callback data.
Public Members
-
parlio_rx_delimiter_handle_t delimiter
The current delimiter of this receiving event
-
void *data
The data buffer address that just finished receiving
-
size_t recv_bytes
The number of received bytes in the data buffer
-
parlio_rx_delimiter_handle_t delimiter
-
struct parlio_rx_event_callbacks_t
Parallel IO RX event callbacks.
Public Members
-
parlio_rx_callback_t on_partial_receive
Callback of received partial data
-
parlio_rx_callback_t on_receive_done
Callback of receiving transaction done
-
parlio_rx_callback_t on_timeout
Callback of hardware receiving timeout
-
parlio_rx_callback_t on_partial_receive
Type Definitions
-
typedef bool (*parlio_rx_callback_t)(parlio_rx_unit_handle_t rx_unit, const parlio_rx_event_data_t *edata, void *user_data)
The template of the Parallel IO RX callback function.
- Param rx_unit:
[in] Parallel IO RX unit handle that given from ISR
- Param edata:
[in] The event data that given from ISR
- Param user_data:
[in] The user specified data that given while registering the callbacks
- Return:
True: to awoke high priority tasks
False: not to awoke high priority tasks