Parallel IO TX Driver
This document describes the functionality of the Parallel IO TX driver in ESP-IDF. The table of contents is as follows:
Introduction
The Parallel IO TX unit is part of the general parallel interface, hereinafter referred to as the TX unit. It supports data communication between external devices and internal memory via GDMA on a parallel bus. Given the flexibility of IO data, the TX unit can be used as a general interface to connect various peripherals. The main application scenarios of this driver include:
Driving LCD, LED displays
High-speed parallel communication with other devices
Simulating the timing of other peripherals when the number of peripherals is insufficient.
Quick Start
This section will quickly guide you on how to use the TX unit driver. Through a simple example simulating QPI (Quad Peripheral Interface) transmission timing, it demonstrates how to create and start a TX unit, initiate a transmission transaction, and register event callback functions. The general usage process is as follows:
Creating and Enabling the TX Unit
First, we need to create a TX unit instance. The following code shows how to create a TX unit instance to simulate QPI:
parlio_tx_unit_handle_t tx_unit = NULL;
parlio_tx_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
.valid_gpio_num = EXAMPLE_PIN_CS, // Use the valid signal as chip select
.clk_out_gpio_num = EXAMPLE_PIN_CLK,
.data_gpio_nums = {
EXAMPLE_PIN_DATA0,
EXAMPLE_PIN_DATA1,
EXAMPLE_PIN_DATA2,
EXAMPLE_PIN_DATA3,
},
.output_clk_freq_hz = 10 * 1000 * 1000, // Output clock frequency is 10 MHz
.trans_queue_depth = 32, // Transaction queue depth is 32
.max_transfer_size = 256, // Maximum transfer size is 256 bytes
.sample_edge = PARLIO_SAMPLE_EDGE_NEG, // Sample data on the falling edge of the clock
.flags = {
.invert_valid_out = true, // The valid signal is high by default, inverted to simulate the chip select signal CS in QPI timing
}
};
// Create TX unit instance
ESP_ERROR_CHECK(parlio_new_tx_unit(&config, &tx_unit));
// Enable TX unit
ESP_ERROR_CHECK(parlio_tx_unit_enable(tx_unit));
When creating a TX unit instance, we need to configure parameters such as the clock source, data width, and output clock frequency through parlio_tx_unit_config_t
. Then call the parlio_new_tx_unit()
function to create a new TX unit instance, which will return a handle pointing to the new instance. The instance handle is essentially a pointer to the TX unit memory object, of type parlio_tx_unit_handle_t
.
The following are the configuration parameters of the parlio_tx_unit_config_t
structure and their explanations:
parlio_tx_unit_config_t::clk_src
Sets the clock source of the TX 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_tx_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_tx_unit_config_t::clk_src
as the clock source. This option has higher priority thanparlio_tx_unit_config_t::clk_src
.parlio_tx_unit_config_t::input_clk_src_freq_hz
The frequency of the external input clock source, valid only whenparlio_tx_unit_config_t::clk_in_gpio_num
is not -1.parlio_tx_unit_config_t::output_clk_freq_hz
Sets the frequency of the output clock, derived from the internal or external clock source. Note that not all frequencies can be achieved, and the driver will automatically adjust to the nearest frequency when the set frequency cannot be achieved.parlio_tx_unit_config_t::clk_out_gpio_num
The GPIO number for the output clock signal.parlio_tx_unit_config_t::data_width
The data bus width of the TX unit, must be a power of 2 and not greater than 16.parlio_tx_unit_config_t::data_gpio_nums
The GPIO numbers for TX data, unused GPIOs should be set to -1.parlio_tx_unit_config_t::valid_gpio_num
The GPIO number for the valid signal, set to -1 if not used. The valid signal stays high level when the TX unit is transmitting data. Note that enabling the valid signal in some specific chips will occupy the MSB data bit, reducing the maximum data width of the TX unit by 1 bit. In this case, the maximum configurable data bus width isSOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH
/ 2. Please check the return value ofparlio_new_tx_unit()
.parlio_tx_unit_config_t::valid_start_delay
The number of clock cycles the valid signal will stay high level before the TX unit starts transmitting data. This configuration option depends on specific hardware features, and if enabled on unsupported chips or configured with invalid values, you will see an error message likeinvalid valid delay
.parlio_tx_unit_config_t::valid_stop_delay
The number of clock cycles the valid signal will stay high level after the TX unit finishes transmitting data. This configuration option depends on specific hardware features, and if enabled on unsupported chips or configured with invalid values, you will see an error message likeinvalid valid delay
.parlio_tx_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_tx_unit_config_t::max_transfer_size
The maximum transfer size per transaction (in bytes).parlio_tx_unit_config_t::dma_burst_size
The DMA burst transfer size (in bytes), must be a power of 2.parlio_tx_unit_config_t::sample_edge
The data sampling edge of the TX unit.parlio_tx_unit_config_t::bit_pack_order
Sets the order of data bits within a byte (valid only when data width < 8).parlio_tx_unit_config_t::flags
Usually used to fine-tune some behaviors of the driver, including the following optionsparlio_tx_unit_config_t::flags::invert_valid_out
Determines whether to invert the valid signal before sending it to the GPIO pin.parlio_tx_unit_config_t::flags::clk_gate_en
Enables TX unit clock gating, the output clock will be controlled by the MSB bit of the data bus, i.e., by writing a high level toparlio_tx_unit_config_t::data_gpio_nums
[SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH
- 1] to enable clock output, and a low level to disable it. In this case, the data bus width needs to be configured asSOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH
. Note that if both the valid signal output and clock gating are enabled, clock gating can come from the valid signal. there is no limit on the data bus width. (Note that in some chips, the valid signal occupies the MSB data bit, so the maximum configurable data bus width isSOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH
/ 2)parlio_tx_unit_config_t::flags::allow_pd
Configures whether the driver allows the system to turn off the peripheral power in sleep mode. Before entering sleep, the system will back up the TX unit register context, and these contexts will be restored when the system exits sleep mode. Turning off the peripheral can save more power, but at the cost of consuming more memory to save the register context. You need to balance power consumption and memory usage. This configuration option depends on specific hardware features, and if enabled on unsupported chips, you will see an error message likeregister back up is not supported
.
Note
If all TX units in the current chip have been requested, the parlio_new_tx_unit()
function will return the ESP_ERR_NOT_FOUND
error.
The TX unit must be enabled before use. The enable function parlio_tx_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. The corresponding disable function is parlio_tx_unit_disable()
, which will release all system services.
Note
When calling the parlio_tx_unit_enable()
and parlio_tx_unit_disable()
functions, they need to be used in pairs. This means you cannot call the parlio_tx_unit_enable()
or parlio_tx_unit_disable()
function twice in a row. This paired calling principle ensures the correct management and release of resources.
Note
Please note that after the TX unit is enabled, it will check the current work queue. If there are pending transmission transactions in the queue, the driver will immediately initiate a transmission.
Initiating TX Transmission Transactions
After enabling the TX unit, we can configure some parameters for the transmission and call the parlio_tx_unit_transmit()
to start the TX transaction. The following code shows how to initiate a TX unit transmission transaction:
#define PAYLOAD_SIZE 128
// Configure TX unit transmission parameters
parlio_transmit_config_t transmit_config = {
.idle_value = 0x00, // All data lines are low in idle state
};
// Prepare the data to be sent
uint8_t payload[PAYLOAD_SIZE] = {0};
for (int i = 0; i < PAYLOAD_SIZE; i++) {
payload[i] = i;
}
// The first call to parlio_tx_unit_transmit will start the transmission immediately as there is no ongoing transaction
ESP_ERROR_CHECK(parlio_tx_unit_transmit(tx_unit, payload, PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
// The second call to parlio_tx_unit_transmit may queue the transaction if the previous one is not completed, and it will be scheduled in the ISR context after the previous transaction is completed
ESP_ERROR_CHECK(parlio_tx_unit_transmit(tx_unit, payload, PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
// (Optional) Wait for the TX unit to complete all transactions
ESP_ERROR_CHECK(parlio_tx_unit_wait_all_done(tx_unit, -1));
The TX unit transmits data in bits, and the transmission bit length must be a multiple of the corresponding bus width. Calling parlio_tx_unit_transmit()
to start the TX transaction, which requires parameters such as the unit handle, payload buffer, and payload size (in bits). Additionally, specific configurations for the transmission should be provided in parlio_transmit_config_t
.
The following are the configuration parameters of the parlio_transmit_config_t
structure and their explanations:
parlio_transmit_config_t::idle_value
Sets the value on the data lines when the TX unit is idle after transmission. This value will remain even after callingparlio_tx_unit_disable()
to disable the TX unit.parlio_transmit_config_t::bitscrambler_program
The pointer to the bitscrambler program binary file. Set toNULL
if the bitscrambler is not used in this transmission.parlio_transmit_config_t::flags
Usually used to fine-tune some behaviors of the transmission, including the following optionsparlio_transmit_config_t::flags::queue_nonblocking
Sets whether the function needs to wait when the transmission queue is full. If this value is set totrue
, the function will immediately return the error codeESP_ERR_INVALID_STATE
when the queue is full. Otherwise, the function will block the current thread until there is space in the transmission queue.parlio_transmit_config_t::flags::loop_transmission
Setting this totrue
enables infinite loop transmission. In this case, the transmission will not stop unless manually callingparlio_tx_unit_disable()
, and no "trans_done" event will be generated. Since the loop is controlled by DMA, the TX unit can generate periodic sequences with minimal CPU intervention.
parlio_tx_unit_transmit()
internally constructs a transaction descriptor and sends it to the work queue, which is usually scheduled in the ISR context. Therefore, when parlio_tx_unit_transmit()
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_tx_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_tx_unit_wait_all_done()
, providing a blocking send function.
With simple configuration, we can send data in QPI format, as shown in the waveform below:
Registering Event Callbacks
Since parlio_tx_unit_transmit()
is an asynchronous interface, we may want to know when the transmission transaction is complete. The following code shows how to register an event callback for the transmission transaction done:
static bool test_parlio_tx_done_callback(parlio_tx_unit_handle_t tx_unit, const parlio_tx_done_event_data_t *edata, void *user_ctx)
{
// General process for handling event callbacks:
// 1. Retrieve user context data from user_ctx (passed in from test_parlio_tx_done_callback)
// 2. Perform user-defined operations
// 3. Return whether a high-priority task was woken up during the above operations to notify the scheduler to switch tasks
BaseType_t high_task_wakeup = pdFalse;
// Use FreeRTOS task handle as user context
TaskHandle_t task = (TaskHandle_t)user_ctx;
// Send task notification to the specified task upon transmission done
vTaskNotifyGiveFromISR(task, &high_task_wakeup);
// Return whether a high-priority task was woken up by this function
return (high_task_wakeup == pdTRUE);
}
parlio_tx_event_callbacks_t cbs = {
// Set test_parlio_tx_done_callback as the event callback function for transmission done
.on_trans_done = test_parlio_tx_done_callback,
};
ESP_ERROR_CHECK(parlio_tx_unit_register_event_callbacks(tx_unit, &cbs, xTaskGetCurrentTaskHandle()));
When the TX unit generates events such as transmission done, it will notify the CPU via interrupts. If you need to call a function when a specific event occurs, you can call parlio_tx_unit_register_event_callbacks()
to register event callbacks to the TX 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. parlio_tx_unit_register_event_callbacks()
also allows users to pass a context pointer to access user-defined data in the callback function.
For the event callbacks supported by the TX unit, refer to parlio_tx_event_callbacks_t
:
parlio_tx_event_callbacks_t::on_trans_done
Sets the callback function for the "transmission complete" event, with the function prototype declared asparlio_tx_done_callback_t
.parlio_tx_event_callbacks_t::on_buffer_switched
Sets the callback function for the "buffer switch" event, with the function prototype declared asparlio_tx_buffer_switched_callback_t
.
Resource Recycling
When the TX unit is no longer needed, the parlio_del_tx_unit()
function should be called to release software and hardware resources. Ensure the TX unit is disabled before deletion.
ESP_ERROR_CHECK(parlio_tx_unit_disable(tx_unit));
ESP_ERROR_CHECK(parlio_del_tx_unit(tx_unit));
Advanced Features
After understanding the basic usage, we can further explore more advanced features of the TX unit driver.
Using an External Clock as the TX Unit Clock Source
The TX unit can choose various clock sources, among which the external clock source is special. We enable the external clock source input by configuring parlio_tx_unit_config_t::clk_src
, parlio_tx_unit_config_t::clk_in_gpio_num
, and parlio_tx_unit_config_t::input_clk_src_freq_hz
:
parlio_tx_unit_handle_t tx_unit = NULL;
parlio_tx_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
.input_clk_src_freq_hz = 10 * 1000 * 1000, // External clock source frequency is 10 MHz
.valid_gpio_num = -1, // Do not use valid signal
.clk_out_gpio_num = EXAMPLE_PIN_CLK_OUT,
.data_gpio_nums = {
EXAMPLE_PIN_DATA0,
EXAMPLE_PIN_DATA1,
EXAMPLE_PIN_DATA2,
EXAMPLE_PIN_DATA3,
},
.output_clk_freq_hz = 5 * 1000 * 1000, // Output clock frequency is 5 MHz. Note that it cannot exceed the input clock frequency
.trans_queue_depth = 32,
.max_transfer_size = 256,
.sample_edge = PARLIO_SAMPLE_EDGE_NEG, // Sample data on the falling edge of the clock
};
// Create TX unit instance
ESP_ERROR_CHECK(parlio_new_tx_unit(&config, &tx_unit));
// Enable TX unit
ESP_ERROR_CHECK(parlio_tx_unit_enable(tx_unit));
#define PAYLOAD_SIZE 64
// Configure TX unit transmission parameters
parlio_transmit_config_t transmit_config = {
.idle_value = 0x00, // All data lines are low in idle state
};
// Prepare the data to be sent
uint8_t payload[PAYLOAD_SIZE] = {0};
for (int i = 0; i < PAYLOAD_SIZE; i++) {
payload[i] = i;
}
// Start transmission transaction
ESP_ERROR_CHECK(parlio_tx_unit_transmit(tx_unit, payload, PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
The waveform of the external clock input is shown below:
Note
The ratio of parlio_tx_unit_config_t::input_clk_src_freq_hz
to parlio_tx_unit_config_t::output_clk_freq_hz
determines the internal clock division factor of the TX unit.
When the actual frequency of the external clock differs from parlio_tx_unit_config_t::input_clk_src_freq_hz
, the actual output clock frequency generated by the TX unit will also change accordingly.
Infinite Loop Transmission
ESP32-P4 supports infinite loop transmission, where the TX unit can generate periodic sequences without CPU intervention. By configuring parlio_transmit_config_t::flags::loop_transmission
, we can enable infinite loop transmission
parlio_tx_unit_handle_t tx_unit = NULL;
parlio_tx_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
.valid_gpio_num = -1, // Do not use valid signal
.clk_out_gpio_num = EXAMPLE_PIN_CLK,
.data_gpio_nums = {
EXAMPLE_PIN_DATA0,
EXAMPLE_PIN_DATA1,
EXAMPLE_PIN_DATA2,
EXAMPLE_PIN_DATA3,
},
.output_clk_freq_hz = 10 * 1000 * 1000, // Output clock frequency is 10 MHz
.trans_queue_depth = 32,
.max_transfer_size = 256,
.sample_edge = PARLIO_SAMPLE_EDGE_NEG, // Sample data on the falling edge of the clock
.flags = {
.invert_valid_out = true, // The valid signal is high by default, inverted to simulate the chip select signal CS in QPI timing
}
};
// Create TX unit instance
ESP_ERROR_CHECK(parlio_new_tx_unit(&config, &tx_unit));
// Enable TX unit
ESP_ERROR_CHECK(parlio_tx_unit_enable(tx_unit));
#define PAYLOAD_SIZE 64
// Configure TX unit transmission parameters
parlio_transmit_config_t transmit_config = {
.idle_value = 0x00, // All data lines are low in idle state
.loop_transmission = true, // Enable infinite loop transmission
};
// Prepare the data to be sent
uint8_t payload[PAYLOAD_SIZE] = {0};
for (int i = 0; i < PAYLOAD_SIZE; i++) {
payload[i] = i;
}
// Start loop transmission transaction
ESP_ERROR_CHECK(parlio_tx_unit_transmit(tx_unit, payload, PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
The waveform of the loop transmission is shown below:
In this case, the transmission will not stop unless manually calling parlio_tx_unit_disable()
, and no "trans_done" event will be generated.
Note
If you need to modify the transmission payload after enabling infinite loop transmission, you can configure parlio_transmit_config_t::flags::loop_transmission
and call 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 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.
Custom Bitstream Generation with BitScrambler
We can use the BitScrambler assembly code to control the data on the DMA path, thereby implementing some simple encoding work. Compared to using the CPU for encoding, the BitScrambler has higher performance and does not consume CPU resources, but is limited by the limited instruction memory of the BitScrambler, so it cannot implement complex encoding work.
After writing the BitScrambler program, we can enable it by calling parlio_tx_unit_decorate_bitscrambler()
. And configure the 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 BitScrambler Programming Guide.
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.
parlio_tx_unit_decorate_bitscrambler()
and parlio_tx_unit_undecorate_bitscrambler()
need to be used in pairs. When deleting the TX unit, you need to call parlio_tx_unit_undecorate_bitscrambler()
first to remove the BitScrambler.
Power Management
When power management CONFIG_PM_ENABLE is enabled, the system may adjust or disable the clock source before entering sleep, causing the TX unit's internal time base to not work as expected.
To prevent this, the TX 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_tx_unit_enable()
and release the lock in parlio_tx_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 TX transaction can work normally.
In addition to turning off the clock source, the system can also turn off the TX unit's power to further reduce power consumption when entering sleep mode. To achieve this, set parlio_tx_unit_config_t::allow_pd
to true
. Before the system enters sleep mode, the TX 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. Therefore, when using this feature, you need to balance power consumption and 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 use thread-safe FreeRTOS queues to manage transmit transactions. Therefore, TX 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 TX 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_TX_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_TX_ISR_CACHE_SAFE.
Performance
To improve the real-time response capability of interrupt handling, the TX unit driver provides the CONFIG_PARLIO_TX_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
.
Other Kconfig Options
CONFIG_PARLIO_ENABLE_DEBUG_LOG option allows forcing the enablement of all debug logs of the TX 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 RX unit driver.
Resource Consumption
Use the IDF Size tool to view the code and data consumption of the TX 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_TX_ISR_HANDLER_IN_IRAM - The interrupt handler is not placed in IRAM.
CONFIG_PARLIO_TX_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 |
92 |
0 |
0 |
0 |
0 |
92 |
0 |
92 |
hal |
18 |
0 |
0 |
0 |
0 |
18 |
0 |
18 |
driver |
6478 |
12 |
12 |
0 |
0 |
6466 |
586 |
5880 |
In addition, each TX unit handle dynamically allocates about 800
bytes of memory from the heap (transmission queue depth is 4). If the parlio_tx_unit_config_t::flags::allow_pd
option is enabled, each TX unit will consume an additional 32
bytes of memory during sleep to save the register context.
Application Examples
peripherals/parlio/parlio_tx/simple_rgb_led_matrix demonstrates how to use the TX unit driver of ESP32-P4 to support HUB75 interface RGB LED matrix panels and use the LVGL library to display simple UI elements.
peripherals/parlio/parlio_tx/advanced_rgb_led_matrix demonstrates how to use the infinite loop transmission feature of the TX unit of ESP32-P4 to support HUB75 interface RGB LED matrix panels. Compared to the simple_rgb_led_matrix example, it does not require manual loop scanning and is more flexible.
peripherals/lcd/parlio_simulate demonstrates how to use the TX unit driver of the parallel IO peripheral to drive screens with SPI or I80 interfaces.
API Reference
Header File
This header file can be included with:
#include "driver/parlio_tx.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_tx_unit(const parlio_tx_unit_config_t *config, parlio_tx_unit_handle_t *ret_unit)
Create a Parallel IO TX unit.
- Parameters:
config -- [in] Parallel IO TX unit configuration
ret_unit -- [out] Returned Parallel IO TX unit handle
- Returns:
ESP_OK: Create Parallel IO TX unit successfully
ESP_ERR_INVALID_ARG: Create Parallel IO TX unit failed because of invalid argument
ESP_ERR_NO_MEM: Create Parallel IO TX unit failed because of out of memory
ESP_ERR_NOT_FOUND: Create Parallel IO TX unit failed because all TX units are used up and no more free one
ESP_ERR_NOT_SUPPORTED: Create Parallel IO TX unit failed because some feature is not supported by hardware, e.g. clock gating
ESP_FAIL: Create Parallel IO TX unit failed because of other error
-
esp_err_t parlio_del_tx_unit(parlio_tx_unit_handle_t unit)
Delete a Parallel IO TX unit.
- Parameters:
unit -- [in] Parallel IO TX unit that created by
parlio_new_tx_unit
- Returns:
ESP_OK: Delete Parallel IO TX unit successfully
ESP_ERR_INVALID_ARG: Delete Parallel IO TX unit failed because of invalid argument
ESP_ERR_INVALID_STATE: Delete Parallel IO TX unit failed because it is still in working
ESP_FAIL: Delete Parallel IO TX unit failed because of other error
-
esp_err_t parlio_tx_unit_enable(parlio_tx_unit_handle_t unit)
Enable the Parallel IO TX unit.
Note
This function will transit the driver state from init to enable
Note
This function will acquire a PM lock that might be installed during channel allocation
Note
If there're transaction pending in the queue, this function will pick up the first one and start the transfer
- Parameters:
unit -- [in] Parallel IO TX unit that created by
parlio_new_tx_unit
- Returns:
ESP_OK: Enable Parallel IO TX unit successfully
ESP_ERR_INVALID_ARG: Enable Parallel IO TX unit failed because of invalid argument
ESP_ERR_INVALID_STATE: Enable Parallel IO TX unit failed because it is already enabled
ESP_FAIL: Enable Parallel IO TX unit failed because of other error
-
esp_err_t parlio_tx_unit_disable(parlio_tx_unit_handle_t unit)
Disable the Parallel IO TX unit.
Note
This function will transit the driver state from enable to init
Note
This function will release the PM lock that might be installed during channel allocation
Note
If one transaction is undergoing, this function will terminate it immediately
- Parameters:
unit -- [in] Parallel IO TX unit that created by
parlio_new_tx_unit
- Returns:
ESP_OK: Disable Parallel IO TX unit successfully
ESP_ERR_INVALID_ARG: Disable Parallel IO TX unit failed because of invalid argument
ESP_ERR_INVALID_STATE: Disable Parallel IO TX unit failed because it's not enabled yet
ESP_FAIL: Disable Parallel IO TX unit failed because of other error
-
esp_err_t parlio_tx_unit_register_event_callbacks(parlio_tx_unit_handle_t tx_unit, const parlio_tx_event_callbacks_t *cbs, void *user_data)
Set event callbacks for Parallel IO TX unit.
Note
User can deregister a previously registered callback by calling this function and setting the callback member in the
cbs
structure to NULL.Note
When CONFIG_PARLIO_TX_ISR_CACHE_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. The variables used in the function should be in the SRAM as well. The
user_data
should also reside in SRAM.- Parameters:
tx_unit -- [in] Parallel IO TX unit that created by
parlio_new_tx_unit
cbs -- [in] Group of callback functions
user_data -- [in] User data, which will be passed to callback functions directly
- Returns:
ESP_OK: Set event callbacks successfully
ESP_ERR_INVALID_ARG: Set event callbacks failed because of invalid argument
ESP_FAIL: Set event callbacks failed because of other error
-
esp_err_t parlio_tx_unit_transmit(parlio_tx_unit_handle_t tx_unit, const void *payload, size_t payload_bits, const parlio_transmit_config_t *config)
Transmit data on by Parallel IO TX unit.
Note
After the function returns, it doesn't mean the transaction is finished. This function only constructs a transaction structure and push into a queue.
- Parameters:
tx_unit -- [in] Parallel IO TX unit that created by
parlio_new_tx_unit
payload -- [in] Pointer to the data to be transmitted
payload_bits -- [in] Length of the data to be transmitted, in bits
config -- [in] Transmit configuration
- Returns:
ESP_OK: Transmit data successfully
ESP_ERR_INVALID_ARG: Transmit data failed because of invalid argument
ESP_ERR_INVALID_STATE: Transmit data failed because the Parallel IO TX unit is not enabled
ESP_FAIL: Transmit data failed because of other error
-
esp_err_t parlio_tx_unit_wait_all_done(parlio_tx_unit_handle_t tx_unit, int timeout_ms)
Wait for all pending TX transactions done.
- Parameters:
tx_unit -- [in] Parallel IO TX unit that created by
parlio_new_tx_unit
timeout_ms -- [in] Timeout in milliseconds,
-1
means to wait forever
- Returns:
ESP_OK: All pending TX transactions is finished and recycled
ESP_ERR_INVALID_ARG: Wait for all pending TX transactions done failed because of invalid argument
ESP_ERR_TIMEOUT: Wait for all pending TX transactions done timeout
ESP_FAIL: Wait for all pending TX transactions done failed because of other error
Structures
-
struct parlio_tx_unit_config_t
Parallel IO TX unit configuration.
Public Members
-
parlio_clock_source_t clk_src
Parallel IO internal clock source
-
gpio_num_t clk_in_gpio_num
If the clock source is input from external, set the corresponding GPIO number. Otherwise, set to
-1
and the driver will use the internalclk_src
as clock source. This option has higher priority thanclk_src
-
uint32_t input_clk_src_freq_hz
Frequency of the input clock source, valid only if
clk_in_gpio_num
is not-1
-
uint32_t output_clk_freq_hz
Frequency of the output clock. It's divided from either internal
clk_src
or external clock source
-
size_t data_width
Parallel IO data width, can set to 1/2/4/8/..., but can't bigger than PARLIO_TX_UNIT_MAX_DATA_WIDTH
-
gpio_num_t data_gpio_nums[PARLIO_TX_UNIT_MAX_DATA_WIDTH]
Parallel IO data GPIO numbers, if any GPIO is not used, you can set it to
-1
-
gpio_num_t clk_out_gpio_num
GPIO number of the output clock signal, the clock is synced with TX data
-
gpio_num_t valid_gpio_num
GPIO number of the valid signal, which stays high when transferring data. Note that, the valid signal will always occupy the MSB data bit
-
uint16_t valid_start_delay
The clock cycles that the valid signal becomes active before data start
-
uint16_t valid_stop_delay
The clock cycles that the valid signal keeps active after data end
-
size_t trans_queue_depth
Depth of internal transaction queue
-
size_t max_transfer_size
Maximum transfer size in one transaction, in bytes. This decides the number of DMA nodes will be used for each transaction
-
size_t dma_burst_size
DMA burst size, in bytes
-
parlio_sample_edge_t sample_edge
Parallel IO sample edge
-
parlio_bit_pack_order_t bit_pack_order
Set the order of packing the bits into bytes (only works when
data_width
< 8)
-
uint32_t clk_gate_en
Enable TX clock gating, the output clock will be controlled by the MSB bit of the data bus, i.e. by data_gpio_nums[PARLIO_TX_UNIT_MAX_DATA_WIDTH-1]. High level 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.
-
uint32_t invert_valid_out
Invert the output valid signal
-
struct parlio_tx_unit_config_t flags
Extra configuration flags
-
parlio_clock_source_t clk_src
-
struct parlio_tx_event_callbacks_t
Group of Parallel IO TX callbacks.
Note
The callbacks are all running under ISR environment
Note
When CONFIG_PARLIO_TX_ISR_CACHE_SAFE is enabled, the callback itself and functions called by it should be placed in IRAM. The variables used in the function should be in the SRAM as well.
Public Members
-
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_done_callback_t on_trans_done
-
struct parlio_transmit_config_t
Parallel IO transmit configuration.
Public Members
-
uint32_t idle_value
The value on the data line when the parallel IO is in idle state
-
const void *bitscrambler_program
BitScrambler program binary, NULL if not use BitScrambler
-
uint32_t queue_nonblocking
If set, when the transaction queue is full, driver will not block the thread but return directly
-
uint32_t loop_transmission
If set, the transmission will be repeated continuously, until the tx_unit is disabled by
parlio_tx_unit_disable
-
struct parlio_transmit_config_t flags
Transmit specific config flags
-
uint32_t idle_value