mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-03 12:44:33 +02:00
Merge branch 'docs/parlio_tx_programming_guide' into 'master'
docs(parlio_tx): add programming guide Closes IDF-11934 See merge request espressif/esp-idf!36298
This commit is contained in:
@@ -255,10 +255,15 @@ esp_err_t parlio_tx_get_alignment_constraints(parlio_tx_unit_t *tx_unit, size_t
|
||||
static esp_err_t parlio_select_periph_clock(parlio_tx_unit_t *tx_unit, const parlio_tx_unit_config_t *config)
|
||||
{
|
||||
parlio_hal_context_t *hal = &tx_unit->base.group->hal;
|
||||
parlio_clock_source_t clk_src = config->clk_in_gpio_num >= 0 ? PARLIO_CLK_SRC_EXTERNAL : config->clk_src;
|
||||
parlio_clock_source_t clk_src = config->clk_src;
|
||||
if (config->clk_in_gpio_num >= 0 && clk_src != PARLIO_CLK_SRC_EXTERNAL) {
|
||||
ESP_LOGW(TAG, "input clock GPIO is set, use external clk src");
|
||||
clk_src = PARLIO_CLK_SRC_EXTERNAL;
|
||||
}
|
||||
uint32_t periph_src_clk_hz = 0;
|
||||
// if the source clock is input from the GPIO, then we're in the slave mode
|
||||
if (clk_src == PARLIO_CLK_SRC_EXTERNAL) {
|
||||
ESP_RETURN_ON_FALSE(config->clk_in_gpio_num >= 0, ESP_ERR_INVALID_ARG, TAG, "invalid input clock GPIO number");
|
||||
periph_src_clk_hz = config->input_clk_src_freq_hz;
|
||||
} else {
|
||||
// get the internal clock source frequency
|
||||
|
@@ -410,7 +410,7 @@ TEST_CASE("parallel tx unit use external non-free running clock", "[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,
|
||||
.clk_src = PARLIO_CLK_SRC_EXTERNAL,
|
||||
.data_width = 8,
|
||||
.clk_in_gpio_num = TEST_EXT_CLK_GPIO,
|
||||
.input_clk_src_freq_hz = 80 * 1000 * 1000, // Note that this is not the real input frequency, we just use it to calculate the clock divider
|
||||
|
35
docs/_static/diagrams/parlio/parlio_tx/external_clock_input_waveform.json
vendored
Normal file
35
docs/_static/diagrams/parlio/parlio_tx/external_clock_input_waveform.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"signal": [
|
||||
{
|
||||
"name": "Input CLK",
|
||||
"wave": "0HlHl.HlHl|"
|
||||
},
|
||||
{
|
||||
"name": "Output CLK",
|
||||
"wave": "0..Hl...Hl|"
|
||||
},
|
||||
{
|
||||
"name": "Valid",
|
||||
"wave": "0..1......|"
|
||||
},
|
||||
{
|
||||
"name": "IO0",
|
||||
"wave": "0..3....4.|", "data": "A0.0 A0.4"
|
||||
},
|
||||
{
|
||||
"name": "IO1",
|
||||
"wave": "0..3....4.|", "data": "A0.1 A0.5"
|
||||
},
|
||||
{
|
||||
"name": "IO2",
|
||||
"wave": "0..3....4.|", "data": "A0.2 A0.6"
|
||||
},
|
||||
{
|
||||
"name": "IO3",
|
||||
"wave": "0..3....4.|", "data": "A0.3 A0.7"
|
||||
}
|
||||
],
|
||||
"head": {
|
||||
"text": "External Clock Input Waveform"
|
||||
}
|
||||
}
|
31
docs/_static/diagrams/parlio/parlio_tx/loop_transmission_waveform.json
vendored
Normal file
31
docs/_static/diagrams/parlio/parlio_tx/loop_transmission_waveform.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"signal": [
|
||||
{
|
||||
"name": "CLK",
|
||||
"wave": "P......|...."
|
||||
},
|
||||
{
|
||||
"name": "CS",
|
||||
"wave": "1.0....|...."
|
||||
},
|
||||
{
|
||||
"name": "IO0",
|
||||
"wave": "0.3456x|783x", "data": "A0.0 A0.4 A1.0 A1.4 A63.0 A63.4 A0.0"
|
||||
},
|
||||
{
|
||||
"name": "IO1",
|
||||
"wave": "0.3456x|783x", "data": "A0.1 A0.5 A1.1 A1.5 A63.1 A63.5 A0.1"
|
||||
},
|
||||
{
|
||||
"name": "IO2",
|
||||
"wave": "0.3456x|783x", "data": "A0.2 A0.6 A1.2 A1.6 A63.2 A63.6 A0.2"
|
||||
},
|
||||
{
|
||||
"name": "IO3",
|
||||
"wave": "0.3456x|783x", "data": "A0.3 A0.7 A1.3 A1.7 A63.3 A63.7 A0.3"
|
||||
}
|
||||
],
|
||||
"head": {
|
||||
"text": "Loop transmission Waveform"
|
||||
}
|
||||
}
|
31
docs/_static/diagrams/parlio/parlio_tx/sim_qpi_waveform.json
vendored
Normal file
31
docs/_static/diagrams/parlio/parlio_tx/sim_qpi_waveform.json
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"signal": [
|
||||
{
|
||||
"name": "CLK",
|
||||
"wave": "P......|.."
|
||||
},
|
||||
{
|
||||
"name": "CS",
|
||||
"wave": "1.0....|.1"
|
||||
},
|
||||
{
|
||||
"name": "IO0",
|
||||
"wave": "0.2345x|.0", "data": "A0.0 A0.4 A1.0 A1.4"
|
||||
},
|
||||
{
|
||||
"name": "IO1",
|
||||
"wave": "0.2345x|.0", "data": "A0.1 A0.5 A1.1 A1.5"
|
||||
},
|
||||
{
|
||||
"name": "IO2",
|
||||
"wave": "0.2345x|.0", "data": "A0.2 A0.6 A1.2 A1.6"
|
||||
},
|
||||
{
|
||||
"name": "IO3",
|
||||
"wave": "0.2345x|.0", "data": "A0.3 A0.7 A1.3 A1.7"
|
||||
}
|
||||
],
|
||||
"head": {
|
||||
"text": "QPI Waveform Simulator"
|
||||
}
|
||||
}
|
@@ -115,7 +115,11 @@ MCPWM_DOCS = ['api-reference/peripherals/mcpwm.rst']
|
||||
|
||||
DEDIC_GPIO_DOCS = ['api-reference/peripherals/dedic_gpio.rst']
|
||||
|
||||
PARLIO_DOCS = ['api-reference/peripherals/parlio.rst']
|
||||
PARLIO_DOCS = [
|
||||
'api-reference/peripherals/parlio/index.rst',
|
||||
'api-reference/peripherals/parlio/parlio_tx.rst',
|
||||
'api-reference/peripherals/parlio/parlio_rx.rst',
|
||||
]
|
||||
|
||||
PCNT_DOCS = ['api-reference/peripherals/pcnt.rst']
|
||||
|
||||
|
@@ -29,7 +29,7 @@ Peripherals API
|
||||
:SOC_GP_LDO_SUPPORTED: ldo_regulator
|
||||
ledc
|
||||
:SOC_MCPWM_SUPPORTED: mcpwm
|
||||
:SOC_PARLIO_SUPPORTED: parlio
|
||||
:SOC_PARLIO_SUPPORTED: parlio/index
|
||||
:SOC_PCNT_SUPPORTED: pcnt
|
||||
:SOC_PPA_SUPPORTED: ppa
|
||||
:SOC_RMT_SUPPORTED: rmt
|
||||
|
@@ -1,29 +0,0 @@
|
||||
Parallel IO
|
||||
===========
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The Parallel IO peripheral is a general purpose parallel interface that can be used to connect to external devices such as LED matrix, LCD display, Printer and Camera. The peripheral has independent TX and RX units. Each unit can have up to 8 or 16 data signals plus 1 or 2 clock signals. [1]_
|
||||
|
||||
The TX and RX driver of Parallel IO peripheral are designed separately, you can include ``driver/parlio_tx.h`` or ``driver/parlio_rx.h`` to use any of them.
|
||||
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
* :example:`peripherals/parlio/parlio_rx/logic_analyzer` demonstrates how to implement a logic analyzer using the Parallel IO RX peripheral, which can sample data on multiple GPIOs in parallel at a high rate, and provides options to probe either internal or external signals and output the raw data via flash or TCP stream.
|
||||
* :example:`peripherals/parlio/parlio_tx/simple_rgb_led_matrix` demonstrates how to use the Parallel IO TX unit of {IDF_TARGET_NAME} to drive a RGB LED Matrix board with a HUB75 interface, using the LVGL library to display simple UI elements.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/parlio_tx.inc
|
||||
.. include-build-file:: inc/parlio_rx.inc
|
||||
.. include-build-file:: inc/components/esp_driver_parlio/include/driver/parlio_types.inc
|
||||
.. include-build-file:: inc/components/hal/include/hal/parlio_types.inc
|
||||
|
||||
.. [1]
|
||||
Different ESP chip series might have different numbers of PARLIO TX/RX instances, and the maximum data bus can also be different. For more details, please refer to **{IDF_TARGET_NAME} Technical Reference Manual** > Chapter **Parallel IO (PARLIO)** [`PDF <{IDF_TARGET_TRM_EN_URL}#parlio>`__]. The driver does not forbid you from applying for more driver objects, but it returns error when all available hardware resources are used up. Please always check the return value when doing resource allocation (e.g., :cpp:func:`parlio_new_tx_unit`).
|
23
docs/en/api-reference/peripherals/parlio/index.rst
Normal file
23
docs/en/api-reference/peripherals/parlio/index.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
Parallel IO
|
||||
===========
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
[`Parallel IO Peripheral <{IDF_TARGET_TRM_EN_URL}#parlio>`__] is a general parallel interface that can be used to connect external devices such as LED matrices, LCD displays, printers, and cameras. This peripheral has independent TX and RX units. Each unit can have up to 8 or 16 data signals and 1 or 2 clock signals.
|
||||
|
||||
The TX and RX drivers of the Parallel IO peripheral are independently designed and can be used by including the header files ``driver/parlio_tx.h`` or ``driver/parlio_rx.h``.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
parlio_tx
|
||||
parlio_rx
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/components/esp_driver_parlio/include/driver/parlio_types.inc
|
||||
.. include-build-file:: inc/components/hal/include/hal/parlio_types.inc
|
22
docs/en/api-reference/peripherals/parlio/parlio_rx.rst
Normal file
22
docs/en/api-reference/peripherals/parlio/parlio_rx.rst
Normal file
@@ -0,0 +1,22 @@
|
||||
Parallel IO RX Driver
|
||||
=====================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
Introduction
|
||||
------------
|
||||
|
||||
The Parallel IO RX unit typically supports the following scenarios:
|
||||
|
||||
- High-speed data acquisition, such as camera and sensor data reading
|
||||
- High-speed parallel communication with other hosts as a slave device
|
||||
|
||||
Application Examples
|
||||
---------------------
|
||||
|
||||
* :example:`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
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/parlio_rx.inc
|
399
docs/en/api-reference/peripherals/parlio/parlio_tx.rst
Normal file
399
docs/en/api-reference/peripherals/parlio/parlio_tx.rst
Normal file
@@ -0,0 +1,399 @@
|
||||
Parallel IO TX Driver
|
||||
=====================
|
||||
|
||||
:link_to_translation:`zh_CN:[中文]`
|
||||
|
||||
This document describes the functionality of the Parallel IO TX driver in ESP-IDF. The table of contents is as follows:
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
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:
|
||||
|
||||
.. code:: c
|
||||
|
||||
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 :cpp:type:`parlio_tx_unit_config_t`. Then call the :cpp:func:`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 :cpp:type:`parlio_tx_unit_handle_t`.
|
||||
|
||||
The following are the configuration parameters of the :cpp:type:`parlio_tx_unit_config_t` structure and their explanations:
|
||||
|
||||
.. list::
|
||||
- :cpp:member:`parlio_tx_unit_config_t::clk_src` Sets the clock source of the TX unit. Available clock sources are listed in :cpp:type:`parlio_clock_source_t`, and only one can be selected. Different clock sources vary in resolution, accuracy, and power consumption.
|
||||
- :cpp:member:`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 internal :cpp:member:`parlio_tx_unit_config_t::clk_src` as the clock source. This option has higher priority than :cpp:member:`parlio_tx_unit_config_t::clk_src`.
|
||||
- :cpp:member:`parlio_tx_unit_config_t::input_clk_src_freq_hz` The frequency of the external input clock source, valid only when :cpp:member:`parlio_tx_unit_config_t::clk_in_gpio_num` is not -1.
|
||||
- :cpp:member:`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.
|
||||
- :cpp:member:`parlio_tx_unit_config_t::clk_out_gpio_num` The GPIO number for the output clock signal.
|
||||
- :cpp:member:`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 {IDF_TARGET_SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH}.
|
||||
- :cpp:member:`parlio_tx_unit_config_t::data_gpio_nums` The GPIO numbers for TX data, unused GPIOs should be set to -1.
|
||||
- :cpp:member:`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 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 is :c:macro:`SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH` / 2.
|
||||
- :cpp:member:`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.
|
||||
- :cpp:member:`parlio_tx_unit_config_t::max_transfer_size` The maximum transfer size per transaction (in bytes).
|
||||
- :cpp:member:`parlio_tx_unit_config_t::dma_burst_size` The DMA burst transfer size (in bytes), must be a power of 2.
|
||||
- :cpp:member:`parlio_tx_unit_config_t::sample_edge` The data sampling edge of the TX unit.
|
||||
- :cpp:member:`parlio_tx_unit_config_t::bit_pack_order` Sets the order of data bits within a byte (valid only when data width < 8).
|
||||
- :cpp:member:`parlio_tx_unit_config_t::flags` Usually used to fine-tune some behaviors of the driver, including the following options
|
||||
- :cpp:member:`parlio_tx_unit_config_t::flags::invert_valid_out` Determines whether to invert the valid signal before sending it to the GPIO pin.
|
||||
:SOC_PARLIO_TX_CLK_SUPPORT_GATING: - :cpp:member:`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 to :cpp:member:`parlio_tx_unit_config_t::data_gpio_nums` [:c:macro:`SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH` - 1] to enable clock output, and a low level to disable it. Note that if both the valid signal output and clock gating are enabled, clock gating can come from the valid signal occupying the MSB data bit, in which case the data bus width can be as long as it is not greater than :c:macro:`SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH` / 2. Otherwise, the data bus width needs to be configured as :c:macro:`SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH`.
|
||||
:SOC_PARLIO_SUPPORT_SLEEP_RETENTION: - :cpp:member:`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 like ``register back up is not supported``.
|
||||
|
||||
.. note::
|
||||
|
||||
If all TX units in the current chip have been requested, the :cpp:func:`parlio_new_tx_unit` function will return the :c:macro:`ESP_ERR_NOT_FOUND` error.
|
||||
|
||||
The TX unit must be enabled before use. The enable function :cpp:func:`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 :cpp:func:`parlio_tx_unit_disable`, which will release all system services.
|
||||
|
||||
.. note::
|
||||
|
||||
When calling the :cpp:func:`parlio_tx_unit_enable` and :cpp:func:`parlio_tx_unit_disable` functions, they need to be used in pairs. This means you cannot call the :cpp:func:`parlio_tx_unit_enable` or :cpp:func:`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 :cpp:func:`parlio_tx_unit_transmit` to start the TX transaction. The following code shows how to initiate a TX unit transmission transaction:
|
||||
|
||||
.. code:: c
|
||||
|
||||
#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 :cpp:func:`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 :cpp:type:`parlio_transmit_config_t`.
|
||||
|
||||
The following are the configuration parameters of the :cpp:type:`parlio_transmit_config_t` structure and their explanations:
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:member:`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 calling :cpp:func:`parlio_tx_unit_disable` to disable the TX unit.
|
||||
- :cpp:member:`parlio_transmit_config_t::flags` Usually used to fine-tune some behaviors of the transmission, including the following options
|
||||
- :cpp:member:`parlio_transmit_config_t::flags::queue_nonblocking` Sets whether the function needs to wait when the transmission queue is full. If this value is set to ``true``, the function will immediately return the error code :c:macro:`ESP_ERR_INVALID_STATE` when the queue is full. Otherwise, the function will block the current thread until there is space in the transmission queue.
|
||||
:SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION: - :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` Setting this to ``true`` enables infinite loop transmission. In this case, the transmission will not stop unless manually calling :cpp:func:`parlio_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.
|
||||
|
||||
:cpp:func:`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 :cpp:func:`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 :cpp:func:`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 :cpp:func:`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:
|
||||
|
||||
.. wavedrom:: /../_static/diagrams/parlio/parlio_tx/sim_qpi_waveform.json
|
||||
|
||||
Registering Event Callbacks
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Since :cpp:func:`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:
|
||||
|
||||
.. code:: c
|
||||
|
||||
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 :cpp:func:`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. :cpp:func:`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 :cpp:type:`parlio_tx_event_callbacks_t`:
|
||||
|
||||
- :cpp:member:`parlio_tx_event_callbacks_t::on_trans_done` Sets the callback function for the "transmission complete" event, with the function prototype declared as :cpp:type:`parlio_tx_done_callback_t`.
|
||||
|
||||
Resource Recycling
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When the TX unit is no longer needed, the :cpp:func:`parlio_del_tx_unit` function should be called to release software and hardware resources. Ensure the TX unit is disabled before deletion.
|
||||
|
||||
.. code:: c
|
||||
|
||||
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 :cpp:member:`parlio_tx_unit_config_t::clk_src`, :cpp:member:`parlio_tx_unit_config_t::clk_in_gpio_num`, and :cpp:member:`parlio_tx_unit_config_t::input_clk_src_freq_hz`:
|
||||
|
||||
.. code-block:: c
|
||||
:emphasize-lines: 3,5,6
|
||||
|
||||
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:
|
||||
|
||||
.. wavedrom:: /../_static/diagrams/parlio/parlio_tx/external_clock_input_waveform.json
|
||||
|
||||
.. note::
|
||||
The ratio of :cpp:member:`parlio_tx_unit_config_t::input_clk_src_freq_hz` to :cpp:member:`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 :cpp:member:`parlio_tx_unit_config_t::input_clk_src_freq_hz`, the actual output clock frequency generated by the TX unit will also change accordingly.
|
||||
|
||||
.. only:: SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
|
||||
Infinite Loop Transmission
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
{IDF_TARGET_NAME} supports infinite loop transmission, where the TX unit can generate periodic sequences without CPU intervention. By configuring :cpp:member:`parlio_transmit_config_t::flags::loop_transmission`, we can enable infinite loop transmission
|
||||
|
||||
.. code-block:: c
|
||||
:emphasize-lines: 32
|
||||
|
||||
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:
|
||||
|
||||
.. wavedrom:: /../_static/diagrams/parlio/parlio_tx/loop_transmission_waveform.json
|
||||
|
||||
In this case, the transmission will not stop unless manually calling :cpp:func:`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 :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` and call :cpp:func:`parlio_tx_unit_transmit` again with a new payload buffer. The driver will switch to the new buffer after the old buffer is completely transmitted. Therefore, users need to maintain two buffers to avoid data inconsistency caused by premature modification or recycling of the old buffer.
|
||||
|
||||
Power Management
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
When power management :ref:`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 :cpp:func:`parlio_tx_unit_enable` and release the lock in :cpp:func:`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.
|
||||
|
||||
.. only:: SOC_PARLIO_SUPPORT_SLEEP_RETENTION
|
||||
|
||||
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 :cpp:member:`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 :ref:`CONFIG_PARLIO_TX_ISR_CACHE_SAFE` option.
|
||||
|
||||
.. note::
|
||||
|
||||
Note that after enabling this option, all interrupt callback functions and their context data **must be stored in internal memory**. Because when the Cache is disabled, the system cannot load data and instructions from Flash.
|
||||
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND or SOC_SPIRAM_XIP_SUPPORTED
|
||||
|
||||
.. note::
|
||||
|
||||
Enable the following options:
|
||||
|
||||
.. list::
|
||||
:SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND: - :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND`
|
||||
:SOC_SPIRAM_XIP_SUPPORTED: - :ref:`CONFIG_SPIRAM_XIP_FROM_PSRAM`
|
||||
|
||||
The Cache will not to be automatically disabled. And it is safe for the CPU to access the Cache while erasing the filesystem. :ref:`CONFIG_PARLIO_TX_ISR_CACHE_SAFE` option has no need to be enabled.
|
||||
|
||||
This way, interrupts can run while the Cache is disabled, but it will also increase IRAM consumption.
|
||||
|
||||
Performance
|
||||
^^^^^^^^^^^
|
||||
|
||||
To improve the real-time response capability of interrupt handling, the TX unit driver provides the :ref:`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 :c:macro:`IRAM_ATTR` and :c:macro:`DRAM_ATTR`.
|
||||
|
||||
Other Kconfig Options
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`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 :doc:`/api-guides/tools/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:
|
||||
- :ref:`CONFIG_PARLIO_TX_ISR_HANDLER_IN_IRAM` - The interrupt handler is not placed in IRAM.
|
||||
- :ref:`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 :cpp:member:`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
|
||||
---------------------
|
||||
|
||||
.. list::
|
||||
|
||||
- :example:`peripherals/parlio/parlio_tx/simple_rgb_led_matrix` demonstrates how to use the TX unit driver of {IDF_TARGET_NAME} to support HUB75 interface RGB LED matrix panels and use the LVGL library to display simple UI elements.
|
||||
:SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION: - :example:`peripherals/parlio/parlio_tx/advanced_rgb_led_matrix` demonstrates how to use the infinite loop transmission feature of the TX unit of {IDF_TARGET_NAME} 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.
|
||||
:SOC_PARLIO_SUPPORT_SPI_LCD: - :example:`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
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/parlio_tx.inc
|
@@ -128,7 +128,7 @@ If the minimum timeout values slightly exceed the requirements, the Interrupt Di
|
||||
|
||||
.. only:: SOC_PARLIO_SUPPORTED
|
||||
|
||||
For even smaller timeout values, for example, to generate or receive waveforms or do bit banging, the resolution of ESP Timer may be insufficient. In this case, it is recommended to use dedicated peripherals, such as :doc:`Parallel IO </api-reference/peripherals/parlio>`, and their DMA features if available.
|
||||
For even smaller timeout values, for example, to generate or receive waveforms or do bit banging, the resolution of ESP Timer may be insufficient. In this case, it is recommended to use dedicated peripherals, such as :doc:`Parallel IO </api-reference/peripherals/parlio/index>`, and their DMA features if available.
|
||||
|
||||
|
||||
Sleep Mode Considerations
|
||||
|
@@ -28,7 +28,7 @@
|
||||
ledc
|
||||
:SOC_MIPI_CSI_SUPPORTED: camera_driver
|
||||
:SOC_MCPWM_SUPPORTED: mcpwm
|
||||
:SOC_PARLIO_SUPPORTED: parlio
|
||||
:SOC_PARLIO_SUPPORTED: parlio/index
|
||||
:SOC_PCNT_SUPPORTED: pcnt
|
||||
:SOC_PPA_SUPPORTED: ppa
|
||||
:SOC_RMT_SUPPORTED: rmt
|
||||
|
@@ -1,13 +1,13 @@
|
||||
Parallel IO 模拟 SPI 或 I80 接口的 LCD
|
||||
并行 IO 模拟 SPI 或 I80 接口的 LCD
|
||||
--------------------------------------------------------------
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
Parallel IO 并不是总线型的外设,驱动直接为 LCD 创建 Parallel IO 设备。目前驱动支持 SPI (1 bit 数据位宽) 和 I80 (8 bit 数据位宽 )模式。
|
||||
并行 IO 并不是总线型的外设,驱动直接为 LCD 创建并行 IO 设备。目前驱动支持 SPI (1 bit 数据位宽) 和 I80 (8 bit 数据位宽 )模式。
|
||||
|
||||
#. 调用 :cpp:func:`esp_lcd_new_panel_io_parl` 创建 Parallel IO 设备。请设置以下参数:
|
||||
#. 调用 :cpp:func:`esp_lcd_new_panel_io_parl` 创建并行 IO 设备。请设置以下参数:
|
||||
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::clk_src` 设置 Parallel IO 设备的时钟源。请注意,不同的 ESP 芯片可能有不同的默认时钟源。
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::clk_src` 设置并行 IO 设备的时钟源。请注意,不同的 ESP 芯片可能有不同的默认时钟源。
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::clk_gpio_num` 设置像素时钟的 GPIO 编号(在某些 LCD 规格书中也被称为 ``WR`` 或者 ``SCLK``)
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::dc_gpio_num` 设置数据或命令选择管脚的 GPIO 编号(在某些 LCD 规格书中也被称为 ``RS``)
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::cs_gpio_num` 设置 CS 信号线的 GPIO 编号。(注意,Parallel IO LCD 驱动仅支持单个 LCD 设备)。
|
||||
@@ -18,7 +18,7 @@ Parallel IO 并不是总线型的外设,驱动直接为 LCD 创建 Parallel IO
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::pclk_hz` 设置像素时钟的频率 (Hz)。较高的像素时钟频率会带来较高的刷新率,但如果 DMA 带宽不足或 LCD 控制器芯片不支持高像素时钟频率,则可能会导致显示异常。
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::dc_levels` 设置 DC 数据选择和命令选择的有效电平。
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::lcd_cmd_bits` 和 :cpp:member:`esp_lcd_panel_io_spi_config_t::lcd_param_bits` 分别设置 LCD 控制器芯片可识别的命令及参数的位宽。不同芯片对位宽要求不同,请提前参阅 LCD 规格书。
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::trans_queue_depth` 设置 Parallel IO 传输队列的深度。该值越大,可以排队的传输越多,但消耗的内存也越多。
|
||||
- :cpp:member:`esp_lcd_panel_io_parl_config_t::trans_queue_depth` 设置并行 IO 传输队列的深度。该值越大,可以排队的传输越多,但消耗的内存也越多。
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
@@ -50,9 +50,9 @@ Parallel IO 并不是总线型的外设,驱动直接为 LCD 创建 Parallel IO
|
||||
|
||||
.. note::
|
||||
|
||||
注意,由于硬件限制,{IDF_TARGET_NAME} 不能通过 Parallel IO 模拟驱动 I80 接口 LCD。
|
||||
注意,由于硬件限制,{IDF_TARGET_NAME} 不能通过并行 IO 模拟驱动 I80 接口 LCD。
|
||||
|
||||
#. 安装 LCD 控制器驱动程序。LCD 控制器驱动程序负责向 LCD 控制器芯片发送命令和参数。在此步骤中,需要指定上一步骤中分配到的 Parallel IO 设备句柄以及一些面板特定配置:
|
||||
#. 安装 LCD 控制器驱动程序。LCD 控制器驱动程序负责向 LCD 控制器芯片发送命令和参数。在此步骤中,需要指定上一步骤中分配到的并行 IO 设备句柄以及一些面板特定配置:
|
||||
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` 设置 LCD 的硬件复位 GPIO 编号。如果 LCD 没有硬件复位管脚,则将此设置为 ``-1``。
|
||||
- :cpp:member:`esp_lcd_panel_dev_config_t::rgb_ele_order` 设置每个颜色数据的 RGB 元素顺序。
|
||||
@@ -67,7 +67,7 @@ Parallel IO 并不是总线型的外设,驱动直接为 LCD 创建 Parallel IO
|
||||
.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
|
||||
.bits_per_pixel = 16,
|
||||
};
|
||||
// 为 ST7789 创建 LCD 面板句柄,并指定 Parallel IO 设备句柄
|
||||
// 为 ST7789 创建 LCD 面板句柄,并指定并行 IO 设备句柄
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &panel_handle));
|
||||
|
||||
API 参考
|
||||
|
@@ -1,29 +0,0 @@
|
||||
并行 IO
|
||||
=======
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
简介
|
||||
----
|
||||
|
||||
并行 IO 外设是一个通用的并行接口,可用于连接外部设备,如 LED 矩阵、LCD 显示器、打印机和摄像头。该外设具有独立的 TX 和 RX 单元。每个单元最多可以具有 8 或 16 个数据信号以及 1 或 2 个时钟信号。[1]_
|
||||
|
||||
并行 IO 外设的 TX 和 RX 驱动程序有各自独立的设计,可分别通过包含头文件 ``driver/parlio_tx.h`` 或 ``driver/parlio_rx.h`` 来使用。
|
||||
|
||||
应用示例
|
||||
--------
|
||||
|
||||
* :example:`peripherals/parlio/parlio_rx/logic_analyzer` 演示了如何使用并行 IO RX 外设来实现逻辑分析仪。该分析仪可以以高频率并行采样多个 GPIO 上的数据,还可以探测内部或外部信号,并通过 flash 或 TCP 流输出原始数据。
|
||||
* :example:`peripherals/parlio/parlio_tx/simple_rgb_led_matrix` 演示了如何使用 {IDF_TARGET_NAME} 的并行 IO TX 单元驱动支持 HUB75 接口的 RGB LED 矩阵板,并使用 LVGL 库来展示简单的 UI 元素。
|
||||
|
||||
|
||||
API 参考
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/parlio_tx.inc
|
||||
.. include-build-file:: inc/parlio_rx.inc
|
||||
.. include-build-file:: inc/components/esp_driver_parlio/include/driver/parlio_types.inc
|
||||
.. include-build-file:: inc/components/hal/include/hal/parlio_types.inc
|
||||
|
||||
.. [1]
|
||||
不同的 ESP 系列芯片可能具有不同数量的并行 IO TX/RX 实例,数据总线的最大宽度也会有所不同。更多信息,请参阅 **{IDF_TARGET_NAME} 技术参考手册** > **并行 IO 控制器 (PARLIO) 章节** [`PDF <{IDF_TARGET_TRM_EN_URL}#parlio>`__]。驱动程序允许你创建更多的驱动程序对象,但当所有可用的硬件资源都被用完时,将会显示返回错误。在进行资源分配时,请时刻注意返回值 (e.g., :cpp:func:`parlio_new_tx_unit`)。
|
23
docs/zh_CN/api-reference/peripherals/parlio/index.rst
Normal file
23
docs/zh_CN/api-reference/peripherals/parlio/index.rst
Normal file
@@ -0,0 +1,23 @@
|
||||
并行 IO
|
||||
=======
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
简介
|
||||
----
|
||||
|
||||
[`并行 IO 外设 <{IDF_TARGET_TRM_CN_URL}#parlio>`__] 是一个通用的并行接口,可用于连接外部设备,如 LED 矩阵、LCD 显示器、打印机和摄像头。该外设具有独立的 TX 和 RX 单元。每个单元最多可以具有 8 或 16 个数据信号以及 1 或 2 个时钟信号。
|
||||
|
||||
并行 IO 外设的 TX 和 RX 驱动程序有各自独立的设计,可分别通过包含头文件 ``driver/parlio_tx.h`` 或 ``driver/parlio_rx.h`` 来使用。
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
parlio_tx
|
||||
parlio_rx
|
||||
|
||||
API 参考
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/components/esp_driver_parlio/include/driver/parlio_types.inc
|
||||
.. include-build-file:: inc/components/hal/include/hal/parlio_types.inc
|
22
docs/zh_CN/api-reference/peripherals/parlio/parlio_rx.rst
Normal file
22
docs/zh_CN/api-reference/peripherals/parlio/parlio_rx.rst
Normal file
@@ -0,0 +1,22 @@
|
||||
并行 IO RX 驱动程序
|
||||
=====================
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
简介
|
||||
----
|
||||
|
||||
并行 IO RX 单元通常支持以下场景:
|
||||
|
||||
- 用于高速数据采集,如摄像头、传感器数据读取
|
||||
- 作为从机与其他主机进行高速并行通信
|
||||
|
||||
应用示例
|
||||
--------
|
||||
|
||||
* :example:`peripherals/parlio/parlio_rx/logic_analyzer` 演示了如何使用并行 IO RX 外设来实现逻辑分析仪。该分析仪可以以高频率并行采样多个 GPIO 上的数据,还可以探测内部或外部信号,并将原始采样数据保存至 Flash 中或者输出到 TCP 流。
|
||||
|
||||
API 参考
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/parlio_rx.inc
|
399
docs/zh_CN/api-reference/peripherals/parlio/parlio_tx.rst
Normal file
399
docs/zh_CN/api-reference/peripherals/parlio/parlio_tx.rst
Normal file
@@ -0,0 +1,399 @@
|
||||
并行 IO TX 驱动程序
|
||||
=====================
|
||||
|
||||
:link_to_translation:`en:[English]`
|
||||
|
||||
本文介绍了 ESP-IDF 中的并行 IO TX 驱动程序的功能,章节目录如下:
|
||||
|
||||
.. contents::
|
||||
:local:
|
||||
:depth: 2
|
||||
|
||||
简介
|
||||
----
|
||||
|
||||
并行 IO TX 单元属于通用的并行接口的一部分,以下简称为 TX 单元。支持通过 GDMA 在并行总线上实现外部设备和内部存储器之间的数据通信,鉴于 IO 数据的灵活性,TX 单元可用作通用接口,连接多种外围设备。该驱动的主要应用场景包括:
|
||||
|
||||
- 用于驱动 LCD、LED 显示屏
|
||||
- 与其他设备进行高速并行通信
|
||||
- 当其他外设数量不够时,使用 TX 单元来模拟该外设的时序。
|
||||
|
||||
快速入门
|
||||
--------
|
||||
|
||||
本节将带你快速了解如何使用 TX 单元驱动。通过简单的示例模拟 QPI (Quad Peripheral Interface) 发送时序,展示如何使用创建 TX 单元并启动它,如何发起传输事务,以及如何注册事件回调函数。一般的使用流程如下:
|
||||
|
||||
创建和使能 TX 单元
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
首先,我们需要创建一个 TX 单元实例。以下代码展示了如何创建一个用于模拟 QPI 的 TX 单元实例:
|
||||
|
||||
.. code:: c
|
||||
|
||||
parlio_tx_unit_handle_t tx_unit = NULL;
|
||||
parlio_tx_unit_config_t config = {
|
||||
.clk_src = PARLIO_CLK_SRC_DEFAULT, // 选择默认的时钟源
|
||||
.data_width = 4, // 数据宽度为 4 位
|
||||
.clk_in_gpio_num = -1, // 不使用外部时钟源
|
||||
.valid_gpio_num = EXAMPLE_PIN_CS, // 使用有效信号作为片选
|
||||
.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, // 输出时钟频率为 10 MHz
|
||||
.trans_queue_depth = 32, // 待处理事务队列深度为 32
|
||||
.max_transfer_size = 256, // 一次传输的最大传输大小为 256 字节
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_NEG, // 在时钟下降沿采样数据
|
||||
.flags = {
|
||||
.invert_valid_out = true, // 有效信号默认高电平有效,通过反转,我们用来模拟 QPI 的时序中的片选信号 CS
|
||||
}
|
||||
};
|
||||
// 创建 TX 单元实例
|
||||
ESP_ERROR_CHECK(parlio_new_tx_unit(&config, &tx_unit));
|
||||
// 使能 TX 单元
|
||||
ESP_ERROR_CHECK(parlio_tx_unit_enable(tx_unit));
|
||||
|
||||
当创建 TX 单元实例时,我们需要通过 :cpp:type:`parlio_tx_unit_config_t` 配置时钟源、数据宽度和输出时钟频率等参数。然后调用 :cpp:func:`parlio_new_tx_unit` 函数创建一个新的 TX 单元实例,该函数将返回一个指向新实例的句柄。实例句柄实际上是一个指向 TX 单元内存对象的指针,类型为 :cpp:type:`parlio_tx_unit_handle_t`。
|
||||
|
||||
以下是 :cpp:type:`parlio_tx_unit_config_t` 结构体的配置参数及其解释:
|
||||
|
||||
.. list::
|
||||
- :cpp:member:`parlio_tx_unit_config_t::clk_src` 设置 TX 单元的时钟源。可用时钟源列在 :cpp:type:`parlio_clock_source_t` 中,只能选择其中一个。不同的时钟源会在分辨率,精度和功耗上有所不同。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::clk_in_gpio_num` 使用外部时钟作为时钟源,设置相应的时钟输入的 GPIO 编号。否则,设置为 -1,驱动程序将使用内部 :cpp:member:`parlio_tx_unit_config_t::clk_src` 作为时钟源。该选项的优先级高于 :cpp:member:`parlio_tx_unit_config_t::clk_src`。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::input_clk_src_freq_hz` 外部输入时钟源的频率,仅当 :cpp:member:`parlio_tx_unit_config_t::clk_in_gpio_num` 不为 -1 时有效。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::output_clk_freq_hz` 设定输出时钟的频率,由内部时钟源或外部时钟源分频得到。注意,并非所有频率都可以得到,当无法得到设置的频率时,驱动会自动调整至接近的频率。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::clk_out_gpio_num` 输出时钟信号的 GPIO 编号。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::data_width` TX 单元数据总线宽度,必须为 2 的幂次方,且不能大于 {IDF_TARGET_SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH}。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::data_gpio_nums` TX 数据 GPIO 编号,未使用的 GPIO 设置为 -1。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::valid_gpio_num` 有效信号的 GPIO 编号,未使用则设置为 -1。有效信号会在 TX 传输数据时保持高电平。注意,启用有效信号会占用 MSB 数据位,导致 TX 单元的最大数据宽度减少 1 位,此时数据总线宽度的最大可配置为 :c:macro:`SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH` / 2。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::trans_queue_depth` 内部事务队列深度。队列越深,在待处理队列中可以准备的事务越多。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::max_transfer_size` 一次传输的最大传输大小(以字节为单位)。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::dma_burst_size` DMA 突发传输大小(以字节为单位),必须为 2 的幂次方。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::sample_edge` TX 单元的数据采样边缘。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::bit_pack_order` 设置字节内数据位出现的顺序(仅当数据宽度 < 8 时有效)。
|
||||
- :cpp:member:`parlio_tx_unit_config_t::flags` 通常用来微调驱动的一些行为,包括以下选项
|
||||
- :cpp:member:`parlio_tx_unit_config_t::flags::invert_valid_out` 决定是否在将 TX 单元有效信号发送到 GPIO 管脚前反转信号。
|
||||
:SOC_PARLIO_TX_CLK_SUPPORT_GATING: - :cpp:member:`parlio_tx_unit_config_t::flags::clk_gate_en` 启用 TX 单元时钟门控,输出时钟将由数据总线的 MSB 位控制,即通过向 :cpp:member:`parlio_tx_unit_config_t::data_gpio_nums` [:c:macro:`SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH` - 1] 写入高电平使能时钟输出,低电平禁用。注意,若有效信号输出和时钟门控同时启用,时钟门控可以来自占用 MSB 数据位的有效信号,此时数据总线宽只要不大于:c:macro:`SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH` / 2 即可,否则需要配置数据总线宽度为 :c:macro:`SOC_PARLIO_TX_UNIT_MAX_DATA_WIDTH`。
|
||||
:SOC_PARLIO_SUPPORT_SLEEP_RETENTION: - :cpp:member:`parlio_tx_unit_config_t::flags::allow_pd` 配置驱动程序是否允许系统在睡眠模式下关闭外设电源。在进入睡眠之前,系统将备份 TX 单元寄存器上下文,当系统退出睡眠模式时,这些上下文将被恢复。关闭外设可以节省更多功耗,但代价是消耗更多内存来保存寄存器上下文。你需要在功耗和内存消耗之间做权衡。此配置选项依赖于特定的硬件功能,如果在不支持的芯片上启用它,你将看到类似 ``register back up is not supported`` 的错误消息。
|
||||
|
||||
.. note::
|
||||
|
||||
如果当前芯片中所有的 TX 单元都已经被申请使用,那么 :cpp:func:`parlio_new_tx_unit` 函数会返回 :c:macro:`ESP_ERR_NOT_FOUND` 错误。
|
||||
|
||||
TX 单元在使用前必须要先使能,使能函数 :cpp:func:`parlio_tx_unit_enable` 可以将驱动的内部状态机切换到激活状态,这里面还会包括一些系统性服务的申请/注册等工作,如申请电源管理锁。与使能函数相对应的是禁用函数 :cpp:func:`parlio_tx_unit_disable`,它会释放所有的系统性服务。
|
||||
|
||||
.. note::
|
||||
|
||||
调用 :cpp:func:`parlio_tx_unit_enable` 和 :cpp:func:`parlio_tx_unit_disable` 函数时,需要成对使用。这意味着,你不能连续调用两次 :cpp:func:`parlio_tx_unit_enable` 或 :cpp:func:`parlio_tx_unit_disable` 函数。这种成对调用的原则确保了资源的正确管理和释放。
|
||||
|
||||
.. note::
|
||||
|
||||
请注意,TX 单元使能后会检查当前的作业队列,一旦队列中存在尚未开始的传输事务,驱动将立即发起一次传输。
|
||||
|
||||
发起 TX 传输事务
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
使能 TX 单元后,我们就可以配置传输的一些参数,并调用 :cpp:func:`parlio_tx_unit_transmit` 启动 TX 事务。以下代码展示了如何发起 TX 单元传输事务:
|
||||
|
||||
.. code:: c
|
||||
|
||||
#define PAYLOAD_SIZE 128
|
||||
|
||||
// 配置 TX 单元传输参数
|
||||
parlio_transmit_config_t transmit_config = {
|
||||
.idle_value = 0x00, // 空闲状态下所有数据线均为低电平
|
||||
};
|
||||
|
||||
// 准备需要发送的数据
|
||||
uint8_t payload[PAYLOAD_SIZE] = {0};
|
||||
for (int i = 0; i < PAYLOAD_SIZE; i++) {
|
||||
payload[i] = i;
|
||||
}
|
||||
|
||||
// 第一次调用 parlio_tx_unit_transmit 时,没有传输事务正在执行,立即开始本次传输事务
|
||||
ESP_ERROR_CHECK(parlio_tx_unit_transmit(tx_unit, payload, PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
|
||||
// 第二次调用 parlio_tx_unit_transmit 时,前一个事务可能还没有完成,驱动将第二个事务发送到作业队列中,并在前一个事务完成后的 ISR 上下文中进行调度
|
||||
ESP_ERROR_CHECK(parlio_tx_unit_transmit(tx_unit, payload, PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
|
||||
// (可选)等待 TX 单元完成所有事务
|
||||
ESP_ERROR_CHECK(parlio_tx_unit_wait_all_done(tx_unit, -1));
|
||||
|
||||
TX 单元以比特为单位进行传输,且传输的比特长度必须配置为对应总线宽度的倍数。调用 :cpp:func:`parlio_tx_unit_transmit` 启动 TX 事务,该函数需要接收传输相关的参数,如单元句柄、payload buffer以及 payload 大小(以 **bit** 为单位)。此外,还需要在 :cpp:type:`parlio_transmit_config_t` 中提供专用于该次传输特定的配置。
|
||||
|
||||
以下是 :cpp:type:`parlio_transmit_config_t` 结构体的配置参数及其解释:
|
||||
|
||||
.. list::
|
||||
|
||||
- :cpp:member:`parlio_transmit_config_t::idle_value` 设置 TX 单元发送完毕后空闲状态时数据线上的值。该值在调用 :cpp:func:`parlio_tx_unit_disable` 禁用 TX 单元后依然会保持。
|
||||
- :cpp:member:`parlio_transmit_config_t::flags` 通常用来微调传输的一些行为,包括以下选项
|
||||
- :cpp:member:`parlio_transmit_config_t::flags::queue_nonblocking` 设置当传输队列满的时候该函数是否需要等待。如果该值设置为 ``true`` 那么当遇到队列满的时候,该函数会立即返回错误代码 :c:macro:`ESP_ERR_INVALID_STATE`。否则,函数会阻塞当前线程,直到传输队列有空档。
|
||||
:SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION: - :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` 设置为 ``true``,会启用无限循环发送机制。此时,除非手动调用 :cpp:func:`parlio_tx_unit_disable`,否则发送不会停止,也不会生成“完成发送”事件。由于循环由 DMA 控制, TX 单元可以在几乎不需要 CPU 干预的情况下,生成周期性序列。
|
||||
|
||||
:cpp:func:`parlio_tx_unit_transmit` 会在内部构建一个事务描述符,并将其发送到作业队列中,该队列通常会在 ISR 上下文中被调度。因此,在 :cpp:func:`parlio_tx_unit_transmit` 返回时,该事务可能尚未启动。注意,你不能在事务结束前就去回收或者修改 payload 中的内容。通过 :cpp:func:`parlio_tx_unit_register_event_callbacks` 来注册事件回调,可以在事务完成的时候被通知。为确保完成所有挂起的事务,你还可以调用 :cpp:func:`parlio_tx_unit_wait_all_done`,这样你就得到了一个带阻塞的发送功能。
|
||||
|
||||
经过简单的配置,我们就可以以 QPI 的格式发送数据,如下图波形所示:
|
||||
|
||||
.. wavedrom:: /../_static/diagrams/parlio/parlio_tx/sim_qpi_waveform.json
|
||||
|
||||
注册事件回调
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
由于 :cpp:func:`parlio_tx_unit_transmit` 是一个异步接口,我们可能会想知道传输事务什么时候完成。以下代码展示了如何注册传输事务完成的事件回调:
|
||||
|
||||
.. code:: c
|
||||
|
||||
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)
|
||||
{
|
||||
// 处理事件回调的一般流程:
|
||||
// 1. 从 user_ctx 中拿到用户上下文数据(需事先从 test_parlio_tx_done_callback 中传入)
|
||||
// 2. 执行用户自定义操作
|
||||
// 3. 返回上述操作期间是否有高优先级的任务被唤醒了,以便通知调度器做切换任务
|
||||
|
||||
BaseType_t high_task_wakeup = pdFalse;
|
||||
// 将 FreeRTOS 任务句柄作为用户上下文
|
||||
QTaskHandle_t task = (TaskHandle_t)user_ctx;
|
||||
// 在传输完成时,我们向指定任务发送任务通知
|
||||
vTaskNotifyGiveFromISR(task, &high_task_wakeup);
|
||||
// 返回高优先级任务是否已被该函数唤醒
|
||||
return (high_task_wakeup == pdTRUE);
|
||||
}
|
||||
|
||||
parlio_tx_event_callbacks_t cbs = {
|
||||
// 将 test_parlio_tx_done_callback 作为传输事务完成的事件回调函数
|
||||
.on_trans_done = test_parlio_tx_done_callback,
|
||||
};
|
||||
ESP_ERROR_CHECK(parlio_tx_unit_register_event_callbacks(tx_unit, &cbs, xTaskGetCurrentTaskHandle()));
|
||||
|
||||
当 TX 单元生成发送完成等事件时,会通过中断告知 CPU。如果需要在发生特定事件时调用函数,可以调用 :cpp:func:`parlio_tx_unit_register_event_callbacks` 向 TX 单元驱动程序的中断服务程序 (ISR) 注册事件回调。由于回调函数是在 ISR 中调用的,因此在回调函数中应该避免执行复杂的操作(包括任何可能导致阻塞的操作),以免影响系统的实时性。:cpp:func:`parlio_tx_unit_register_event_callbacks` 还允许用户传递一个上下文指针,以便在回调函数中访问用户定义的数据。
|
||||
|
||||
有关 TX 单元支持的事件回调,请参阅 :cpp:type:`parlio_tx_event_callbacks_t`:
|
||||
|
||||
- :cpp:member:`parlio_tx_event_callbacks_t::on_trans_done` 为“发送完成”的事件设置回调函数,函数原型声明为 :cpp:type:`parlio_tx_done_callback_t`。
|
||||
|
||||
资源回收
|
||||
^^^^^^^^
|
||||
|
||||
当不再需要使用 TX 单元时,应该调用 :cpp:func:`parlio_del_tx_unit` 函数来释放软硬件资源。删除前请确保 TX 单元已经处于禁用状态。
|
||||
|
||||
.. code:: c
|
||||
|
||||
ESP_ERROR_CHECK(parlio_tx_unit_disable(tx_unit));
|
||||
ESP_ERROR_CHECK(parlio_del_tx_unit(tx_unit));
|
||||
|
||||
进阶功能
|
||||
--------
|
||||
|
||||
在了解了基本用法后,我们可以进一步探索 tx 单元驱动的更多高级玩法。
|
||||
|
||||
使用外部时钟作为 TX 单元的时钟源
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
TX 单元可以选择各种不同的时钟源,其中外部时钟源较为特殊。我们通过配置 :cpp:member:`parlio_tx_unit_config_t::clk_src` , :cpp:member:`parlio_tx_unit_config_t::clk_in_gpio_num` 以及 :cpp:member:`parlio_tx_unit_config_t::input_clk_src_freq_hz` 来启用外部时钟源输入:
|
||||
|
||||
.. code-block:: c
|
||||
:emphasize-lines: 3,5,6
|
||||
|
||||
parlio_tx_unit_handle_t tx_unit = NULL;
|
||||
parlio_tx_unit_config_t config = {
|
||||
.clk_src = PARLIO_CLK_SRC_EXTERNAL, // 选择外部时钟源
|
||||
.data_width = 4, // 数据宽度为 4 位
|
||||
.clk_in_gpio_num = EXAMPLE_PIN_CLK_IN, // 设置外部时钟源输入引脚
|
||||
.input_clk_src_freq_hz = 10 * 1000 * 1000, // 外部时钟源频率为 10 MHz
|
||||
.valid_gpio_num = -1, // 不使用有效信号
|
||||
.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, // 输出时钟频率为 5 MHz。注意,不能超过输入时钟频率
|
||||
.trans_queue_depth = 32,
|
||||
.max_transfer_size = 256,
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_NEG, // 在时钟下降沿采样数据
|
||||
};
|
||||
// 创建 TX 单元实例
|
||||
ESP_ERROR_CHECK(parlio_new_tx_unit(&config, &tx_unit));
|
||||
// 使能 TX 单元
|
||||
ESP_ERROR_CHECK(parlio_tx_unit_enable(tx_unit));
|
||||
|
||||
#define PAYLOAD_SIZE 64
|
||||
|
||||
// 配置 TX 单元传输参数
|
||||
parlio_transmit_config_t transmit_config = {
|
||||
.idle_value = 0x00, // 空闲状态下所有数据线均为低电平
|
||||
};
|
||||
|
||||
// 准备需要发送的数据
|
||||
uint8_t payload[PAYLOAD_SIZE] = {0};
|
||||
for (int i = 0; i < PAYLOAD_SIZE; i++) {
|
||||
payload[i] = i;
|
||||
}
|
||||
|
||||
// 开始传输事务
|
||||
ESP_ERROR_CHECK(parlio_tx_unit_transmit(tx_unit, payload, PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
外部时钟输入的波形如下图所示:
|
||||
|
||||
.. wavedrom:: /../_static/diagrams/parlio/parlio_tx/external_clock_input_waveform.json
|
||||
|
||||
.. note::
|
||||
:cpp:member:`parlio_tx_unit_config_t::input_clk_src_freq_hz` 与 :cpp:member:`parlio_tx_unit_config_t::output_clk_freq_hz` 的比值决定了 TX 单元内部的时钟分频系数。
|
||||
当外部时钟实际频率与 :cpp:member:`parlio_tx_unit_config_t::input_clk_src_freq_hz` 不一致时,TX 单元实际生成的输出时钟频率也会相应改变。
|
||||
|
||||
.. only:: SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION
|
||||
|
||||
无限循环发送
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
{IDF_TARGET_NAME} 支持无限循环发送,即 TX 单元可以在不需要 CPU 干预的情况下,生成周期性序列。通过配置 :cpp:member:`parlio_transmit_config_t::flags::loop_transmission`,我们就可以启用无限循环发送
|
||||
|
||||
.. code-block:: c
|
||||
:emphasize-lines: 32
|
||||
|
||||
parlio_tx_unit_handle_t tx_unit = NULL;
|
||||
parlio_tx_unit_config_t config = {
|
||||
.clk_src = PARLIO_CLK_SRC_DEFAULT, // 选择默认的时钟源
|
||||
.data_width = 4, // 数据宽度为 4 位
|
||||
.clk_in_gpio_num = -1, // 不使用外部时钟源
|
||||
.valid_gpio_num = -1, // 不使用有效信号
|
||||
.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, // 输出时钟频率为 10 MHz
|
||||
.trans_queue_depth = 32,
|
||||
.max_transfer_size = 256,
|
||||
.sample_edge = PARLIO_SAMPLE_EDGE_NEG, // 在时钟下降沿采样数据
|
||||
.flags = {
|
||||
.invert_valid_out = true, // 有效信号默认高电平有效,通过反转,我们用来模拟 QPI 的时序中的片选信号 CS
|
||||
}
|
||||
};
|
||||
// 创建 TX 单元实例
|
||||
ESP_ERROR_CHECK(parlio_new_tx_unit(&config, &tx_unit));
|
||||
// 使能 TX 单元
|
||||
ESP_ERROR_CHECK(parlio_tx_unit_enable(tx_unit));
|
||||
|
||||
#define PAYLOAD_SIZE 64
|
||||
|
||||
// 配置 TX 单元传输参数
|
||||
parlio_transmit_config_t transmit_config = {
|
||||
.idle_value = 0x00, // 空闲状态下所有数据线均为低电平
|
||||
.loop_transmission = true, // 启用无限循环发送
|
||||
};
|
||||
|
||||
// 准备需要发送的数据
|
||||
uint8_t payload[PAYLOAD_SIZE] = {0};
|
||||
for (int i = 0; i < PAYLOAD_SIZE; i++) {
|
||||
payload[i] = i;
|
||||
}
|
||||
|
||||
// 开始循环传输事务
|
||||
ESP_ERROR_CHECK(parlio_tx_unit_transmit(tx_unit, payload, PAYLOAD_SIZE * sizeof(uint8_t) * 8, &transmit_config));
|
||||
|
||||
循环发送波形如下图所示:
|
||||
|
||||
.. wavedrom:: /../_static/diagrams/parlio/parlio_tx/loop_transmission_waveform.json
|
||||
|
||||
此时,除非手动调用 :cpp:func:`parlio_tx_unit_disable`,否则发送不会停止,也不会生成“完成发送”事件。
|
||||
|
||||
.. note::
|
||||
|
||||
如果启用无限循环发送后需要修改发送内容,可以配置 :cpp:member:`parlio_transmit_config_t::flags::loop_transmission` 并再次调用 :cpp:func:`parlio_tx_unit_transmit` 传入新的 payload buffer,驱动会在旧 buffer 完整发送后,切换到新传入的 buffer。因此需要用户自行维护好两块buffer,避免旧 buffer 被提早修改或者回收导致产生数据不连贯的现象。
|
||||
|
||||
电源管理
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
当电源管理 :ref:`CONFIG_PM_ENABLE` 被启用的时候,系统在进入睡眠前可能会调整或禁用时钟源,会导致 TX 单元内部的时间基准无法按预期工作。
|
||||
|
||||
为了防止这种情况发生, TX 单元驱动内部创建了一个电源管理锁。锁的类型会根据不同的时钟源来设置。驱动程序将在 :cpp:func:`parlio_tx_unit_enable` 中拿锁,并在 :cpp:func:`parlio_tx_unit_disable` 中释放锁。这意味着,无论电源管理策略如何,在这两个函数之间系统不会进入睡眠模式,时钟源也不会被禁用或调整频率,任何 TX 事务都可以保证正常工作。
|
||||
|
||||
.. only:: SOC_PARLIO_SUPPORT_SLEEP_RETENTION
|
||||
|
||||
除了关闭时钟源外,系统在进入睡眠模式时还可以关闭 TX 单元的电源以进一步降低功耗。要实现这一点,需要将 :cpp:member:`parlio_tx_unit_config_t::allow_pd` 设置为 ``true``。在系统进入睡眠模式之前,TX 单元的寄存器上下文会被备份到内存中,并在系统唤醒后恢复。请注意,启用此选项虽然可以降低功耗,但会增加内存的使用量。因此,在使用该功能时需要在功耗和内存消耗之间进行权衡。
|
||||
|
||||
关于线程安全
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
驱动使用了临界区保证了对寄存器的原子操作。句柄内部的关键成员也受临界区保护。驱动内部的状态机使用了原子指令保证了线程安全,并且使用线程安全的 FreeRTOS 队列来管理发送事务。因此, TX 单元的 API 可以在多线程环境下使用,无需自行加锁。
|
||||
|
||||
关于 Cache 安全
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
在文件系统进行 Flash 读写操作时,为了避免 Cache 从 Flash 加载指令和数据时出现错误,系统会暂时禁用 Cache 功能。这会导致 TX 单元的中断处理程序在此期间无法响应,从而使用户的回调函数无法及时执行。如果希望在 Cache 被禁用期间,中断处理程序仍能正常运行,可以启用 :ref:`CONFIG_PARLIO_TX_ISR_CACHE_SAFE` 选项。
|
||||
|
||||
.. note::
|
||||
|
||||
请注意,在启用该选项后,所有的中断回调函数及其上下文数据 **必须存放在内部存储空间** 中。因为在 Cache 被禁用时,系统无法从 Flash 中加载数据和指令。
|
||||
|
||||
.. only:: SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND or SOC_SPIRAM_XIP_SUPPORTED
|
||||
|
||||
.. note::
|
||||
|
||||
启用以下选项:
|
||||
|
||||
.. list::
|
||||
:SOC_SPI_MEM_SUPPORT_AUTO_SUSPEND: - :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND`
|
||||
:SOC_SPIRAM_XIP_SUPPORTED: - :ref:`CONFIG_SPIRAM_XIP_FROM_PSRAM`
|
||||
|
||||
可以让 Cache 不被自动禁用, CPU 在擦写文件系统的同时访问 Cache 是安全的,此时 :ref:`CONFIG_PARLIO_TX_ISR_CACHE_SAFE` 选项可以不用打开。
|
||||
|
||||
这样,在 Cache 被禁用时,中断也可运行,但是这也会增加 IRAM 的消耗。
|
||||
|
||||
关于性能
|
||||
^^^^^^^^
|
||||
|
||||
为了提升中断处理的实时响应能力, TX 单元驱动提供了 :ref:`CONFIG_PARLIO_TX_ISR_HANDLER_IN_IRAM` 选项。启用该选项后,中断处理程序将被放置在内部 RAM 中运行,从而减少了从 Flash 加载指令时可能出现的缓存丢失带来的延迟。
|
||||
|
||||
.. note::
|
||||
|
||||
但是,中断处理程序调用的用户回调函数和用户上下文数据仍然可能位于 Flash 中,缓存缺失的问题还是会存在,这需要用户自己将回调函数和数据放入内部 RAM 中,比如使用 :c:macro:`IRAM_ATTR` 和 :c:macro:`DRAM_ATTR`。
|
||||
|
||||
其他 Kconfig 选项
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
- :ref:`CONFIG_PARLIO_ENABLE_DEBUG_LOG` 选项允许强制启用 TX 单元驱动的所有调试日志,无论全局日志级别设置如何。启用此选项可以帮助开发人员在调试过程中获取更详细的日志信息,从而更容易定位和解决问题。此选项与 RX 单元驱动程序共用。
|
||||
|
||||
关于资源消耗
|
||||
^^^^^^^^^^^^
|
||||
|
||||
使用 :doc:`/api-guides/tools/idf-size` 工具可以查看 TX 单元驱动的代码和数据消耗。以下是测试前提条件(以 ESP32-H2 为例):
|
||||
|
||||
- 编译器优化等级设置为 ``-Os``,以确保代码尺寸最小化。
|
||||
- 默认日志等级设置为 ``ESP_LOG_INFO``,以平衡调试信息和性能。
|
||||
- 关闭以下驱动优化选项:
|
||||
- :ref:`CONFIG_PARLIO_TX_ISR_HANDLER_IN_IRAM` - 中断处理程序不放入 IRAM。
|
||||
- :ref:`CONFIG_PARLIO_TX_ISR_CACHE_SAFE` - 不启用 Cache 安全选项。
|
||||
|
||||
**注意,以下数据不是精确值,仅供参考,在不同型号的芯片和不同版本的 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 |
|
||||
+-----------------+------------+-------+------+-------+-------+------------+---------+-------+
|
||||
|
||||
此外,每一个 TX 单元句柄会从 heap 中动态申请约 ``800`` 字节的内存(传输队列深度按 4 计算)。如果还使能了 :cpp:member:`parlio_tx_unit_config_t::flags::allow_pd` 选项,那么每个 TX 单元还会在睡眠期间额外消耗约 ``32`` 字节的内存用于保存寄存器上下文。
|
||||
|
||||
应用示例
|
||||
--------
|
||||
|
||||
.. list::
|
||||
|
||||
- :example:`peripherals/parlio/parlio_tx/simple_rgb_led_matrix` 演示了如何使用 {IDF_TARGET_NAME} 的 TX 单元驱动支持 HUB75 接口的 RGB LED 矩阵板,并使用 LVGL 库来展示简单的 UI 元素。
|
||||
:SOC_PARLIO_TX_SUPPORT_LOOP_TRANSMISSION: - :example:`peripherals/parlio/parlio_tx/advanced_rgb_led_matrix` 演示了如何使用 {IDF_TARGET_NAME} 的 TX 单元的无限循环发送特性支持 HUB75 接口的 RGB LED 矩阵板。相比 simple_rgb_led_matrix 示例,不需要手动执行循环扫描,使用更加灵活。
|
||||
:SOC_PARLIO_SUPPORT_SPI_LCD: - :example:`peripherals/lcd/parlio_simulate` 演示了如何使用并行 IO 外设的 TX 单元驱动 SPI 或 I80 接口的屏幕。
|
||||
|
||||
API 参考
|
||||
-------------
|
||||
|
||||
.. include-build-file:: inc/parlio_tx.inc
|
@@ -128,7 +128,7 @@ CPU 频率越低,最小超时值就越高。一般来说,如果所需的超
|
||||
|
||||
.. only:: SOC_PARLIO_SUPPORTED
|
||||
|
||||
若需要更小的超时值,例如生成或接收波形、进行位操作时,ESP 定时器的分辨率可能不能满足要求。此时建议使用专用外设,例如 :doc:`并行 IO </api-reference/peripherals/parlio>`,以及使用它们的 DMA 功能(如果可用)。
|
||||
若需要更小的超时值,例如生成或接收波形、进行位操作时,ESP 定时器的分辨率可能不能满足要求。此时建议使用专用外设,例如 :doc:`并行 IO </api-reference/peripherals/parlio/index>`,以及使用它们的 DMA 功能(如果可用)。
|
||||
|
||||
|
||||
睡眠模式注意事项
|
||||
|
Reference in New Issue
Block a user