Merge branch 'docs/update_api-reference/peripherals' into 'master'

docs: update format issues for EN and CN files under api-reference/peripherals

Closes DOC-5862

See merge request espressif/esp-idf!25022
This commit is contained in:
Cai Xin Ying 2023-08-30 12:17:09 +08:00
commit b6d4bae4c6
42 changed files with 557 additions and 519 deletions

View File

@ -67,7 +67,7 @@ Further Configurations
- :cpp:func:`ana_cmpr_set_intl_reference` - Specify the internal reference voltage when :cpp:enumerator:`ana_cmpr_ref_source_t::ANA_CMPR_REF_SRC_INTERNAL` is selected as reference source.
It requires :cpp:member:`ana_cmpr_internal_ref_config_t::ref_volt` to specify the voltage. The voltage related to the VDD power supply, which can only support a certain fixed percentage of VDD. Currently on {IDF_TARGET_NAME}, the internal reference voltage can be range to 0~70% VDD with a step 10%.
It requires :cpp:member:`ana_cmpr_internal_ref_config_t::ref_volt` to specify the voltage. The voltage related to the VDD power supply, which can only support a certain fixed percentage of VDD. Currently on {IDF_TARGET_NAME}, the internal reference voltage can be range to 0 ~ 70% VDD with a step 10%.
.. code:: c
@ -80,7 +80,7 @@ It requires :cpp:member:`ana_cmpr_internal_ref_config_t::ref_volt` to specify th
- :cpp:func:`ana_cmpr_set_debounce` - Set the debounce configuration.
It requires :cpp:member:`ana_cmpr_debounce_config_t::wait_us` to set the interrupt waiting time. The interrupt will be disabled temporary for :cpp:member:`ana_cmpr_debounce_config_t::wait_us` micro seconds, so that the frequent triggering can be avoid while the source signal crossing the reference signal. That is, the waiting time is supposed to be inverse ratio to the relative frequency between the source and reference. If the waiting time is set too short, it can't bypass the jitter totally, but if too long, the next crossing interrupt might be missed.
It requires :cpp:member:`ana_cmpr_debounce_config_t::wait_us` to set the interrupt waiting time. The interrupt is disabled temporarily for :cpp:member:`ana_cmpr_debounce_config_t::wait_us` micro seconds, so that the frequent triggering can be avoid while the source signal crossing the reference signal. That is, the waiting time is supposed to be inverse ratio to the relative frequency between the source and reference. If the waiting time is set too short, it can not bypass the jitter totally, but if too long, the next crossing interrupt might be missed.
.. code:: c
@ -133,29 +133,29 @@ Enable and Disable Unit
After the Analog Comparator unit is enabled and the crossing event interrupt is enabled, a power management lock will be acquired if the power management is enabled (see `Power Management <#power-management>`__). Under the **enable** state, only :cpp:func:`ana_cmpr_set_intl_reference` and :cpp:func:`ana_cmpr_set_debounce` can be called, other functions can only be called after the unit is disabled.
Calling :cpp:func:`ana_cmpr_disable` will do the opposite.
Calling :cpp:func:`ana_cmpr_disable` does the opposite.
Power Management
^^^^^^^^^^^^^^^^
When power management is enabled (i.e. :ref:`CONFIG_PM_ENABLE` is on), the system will adjust the APB frequency before going into light sleep, thus potentially changing the resolution of the Analog Comparator.
When power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust the APB frequency before going into light sleep, thus potentially changing the resolution of the Analog Comparator.
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_NO_LIGHT_SLEEP`. Whenever the driver creates a Analog Comparator unit instance that has selected the clock source like :cpp:enumerator:`ANA_CMPR_CLK_SRC_DEFAULT` or :cpp:enumerator:`ANA_CMPR_CLK_SRC_XTAL` as its clock source, the driver will guarantee that the power management lock is acquired when enable the channel by :cpp:func:`ana_cmpr_enable`. Likewise, the driver releases the lock when :cpp:func:`ana_cmpr_disable` is called for that channel.
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_NO_LIGHT_SLEEP`. Whenever the driver creates a Analog Comparator unit instance that has selected the clock source like :cpp:enumerator:`ANA_CMPR_CLK_SRC_DEFAULT` or :cpp:enumerator:`ANA_CMPR_CLK_SRC_XTAL` as its clock source, the driver guarantees that the power management lock is acquired when enable the channel by :cpp:func:`ana_cmpr_enable`. Likewise, the driver releases the lock when :cpp:func:`ana_cmpr_disable` is called for that channel.
IRAM Safe
^^^^^^^^^
By default, the Analog Comparator interrupt will be deferred when the Cache is disabled for reasons like programming/erasing Flash. Thus the alarm interrupt will not get executed in time, which is not expected in a real-time application.
There's a Kconfig option :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` that will:
There is a Kconfig option :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` that:
1. Enable the interrupt being serviced even when cache is disabled
2. Place all functions that used by the ISR into IRAM [1]_
3. Place driver object into DRAM (in case it's allocated on PSRAM)
1. Enables the interrupt being serviced even when cache is disabled
2. Places all functions that used by the ISR into IRAM [1]_
3. Places driver object into DRAM (in case it is allocated on PSRAM)
This will allow the interrupt to run while the cache is disabled but will come at the cost of increased IRAM consumption.
This allows the interrupt to run while the cache is disabled but comes at the cost of increased IRAM consumption.
There's a Kconfig option :ref:`CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM` that can put commonly used IO control functions into IRAM as well. So that these functions can also be executable when the cache is disabled. These IO control functions are listed as follows:
There is a Kconfig option :ref:`CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM` that can put commonly used IO control functions into IRAM as well. So that these functions can also be executable when the cache is disabled. These IO control functions are listed as follows:
- :cpp:func:`ana_cmpr_set_internal_reference`
- :cpp:func:`ana_cmpr_set_debounce`
@ -178,7 +178,7 @@ Kconfig Options
- :ref:`CONFIG_ANA_CMPR_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled, see `IRAM Safe <#iram-safe>`__ for more information.
- :ref:`CONFIG_ANA_CMPR_CTRL_FUNC_IN_IRAM` controls where to place the Analog Comparator control functions (IRAM or Flash), see `IRAM Safe <#iram-safe>`__ for more information.
- :ref:`CONFIG_ANA_CMPR_ENABLE_DEBUG_LOG` is used to enabled the debug log output. Enabling this option will increase the firmware binary size.
- :ref:`CONFIG_ANA_CMPR_ENABLE_DEBUG_LOG` is used to enabled the debug log output. Enabling this option increases the firmware binary size.
Application Example
-------------------

View File

@ -16,8 +16,8 @@ Overview
The DAC peripheral supports outputting analog signal in the following ways:
1. Outputting a voltage directly. The DAC channel will keep outputting a specified voltage.
2. Outputting continuous analog signal by DMA. The DAC will convert the data in a buffer at a specified frequency.
1. Outputting a voltage directly. The DAC channel keeps outputting a specified voltage.
2. Outputting continuous analog signal by DMA. The DAC converts the data in a buffer at a specified frequency.
3. Outputting a cosine wave by the cosine wave generator. The DAC channel can output a cosine wave with specified frequency and amplitude.
For other analog output options, see :doc:`Sigma-Delta Modulation <sdm>` and :doc:`LED Control <ledc>`. Both modules produce high-frequency PWM/PDM output, which can be hardware low-pass filtered in order to generate a lower frequency analog output.
@ -54,14 +54,14 @@ The DAC on {IDF_TARGET_NAME} has two channels. The channels have separate softwa
Direct Voltage Output (One-shot/Direct Mode)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The DAC channels in the group can convert an 8-bit digital value into the analog when :cpp:func:`dac_oneshot_output_voltage` is called (it can be called in ISR). The analog voltage will be kept on the DAC channel until the next conversion starts. To start the voltage conversion, the DAC channels need to be enabled first through registering by :cpp:func:`dac_oneshot_new_channel`.
The DAC channels in the group can convert an 8-bit digital value into the analog when :cpp:func:`dac_oneshot_output_voltage` is called (it can be called in ISR). The analog voltage is kept on the DAC channel until the next conversion starts. To start the voltage conversion, the DAC channels need to be enabled first through registering by :cpp:func:`dac_oneshot_new_channel`.
Continuous Wave Output (Continuous/DMA Mode)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
DAC channels can convert digital data continuously via the DMA. There are three ways to write the DAC data:
1. Normal writing (synchronous): Data can be transmitted at one time and kept blocked until all the data has been loaded into the DMA buffer, and the voltage will be kept as the last conversion value while no more data is inputted. It is usually used to transport a long signal like an audio. To convert data continuously, the continuous channel handle need to be allocated by calling :cpp:func:`dac_continuous_new_channels` and the DMA conversion should be enabled by calling :cpp:func:`dac_continuous_enable`. Then data can be written by :cpp:func:`dac_continuous_write` synchronously. Refer to :example:`peripherals/dac/dac_continuous/dac_audio` for examples.
1. Normal writing (synchronous): Data can be transmitted at one time and kept blocked until all the data has been loaded into the DMA buffer, and the voltage is kept as the last conversion value while no more data is inputted. It is usually used to transport a long signal like an audio. To convert data continuously, the continuous channel handle need to be allocated by calling :cpp:func:`dac_continuous_new_channels` and the DMA conversion should be enabled by calling :cpp:func:`dac_continuous_enable`. Then data can be written by :cpp:func:`dac_continuous_write` synchronously. Refer to :example:`peripherals/dac/dac_continuous/dac_audio` for examples.
2. Cyclical writing: A piece of data can be converted cyclically without blocking, and no more operation is needed after the data are loaded into the DMA buffer. But note that the inputted buffer size is limited by the number of descriptors and the DMA buffer size. It is usually used to transport short signals that need to be repeated, e.g., a sine wave. To achieve cyclical writing, call :cpp:func:`dac_continuous_write_cyclically` after the DAC continuous mode is enabled. Refer to :example:`peripherals/dac/dac_continuous/signal_generator` for examples.
3. Asynchronous writing: Data can be transmitted asynchronously based on the event callback. :cpp:member:`dac_event_callbacks_t::on_convert_done` must be registered to use asynchronous mode. Users can get the :cpp:type:`dac_event_data_t` in the callback which contains the DMA buffer address and length, allowing them to load the data into the buffer directly. To use the asynchronous writing, call :cpp:func:`dac_continuous_register_event_callback` to register the :cpp:member:`dac_event_callbacks_t::on_convert_done` before enabling, and then :cpp:func:`dac_continuous_start_async_writing` to start the asynchronous writing. Note that once the asynchronous writing is started, the callback function will be triggered continuously. Call :cpp:func:`dac_continuous_write_asynchronously` to load the data either in a separate task or in the callback directly. Refer to :example:`peripherals/dac/dac_continuous/dac_audio` for examples.
@ -96,20 +96,20 @@ Power Management
When the power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust or stop the clock source of DAC before entering Light-sleep mode, thus potential influence to the DAC signals may lead to false data conversion.
When using DAC driver in continuous mode, it can prevent the system from changing or stopping the clock source in DMA or cosine mode by acquiring a power management lock. When the clock source is generated from APB, the lock type will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`. When the clock source is APLL (only in DMA mode), it will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`. Whenever the DAC is converting (i.e., DMA or cosine wave generator is working), the driver will guarantee that the power management lock is acquired after calling :cpp:func:`dac_continuous_enable`. Likewise, the driver will release the lock when :cpp:func:`dac_continuous_disable` is called.
When using DAC driver in continuous mode, it can prevent the system from changing or stopping the clock source in DMA or cosine mode by acquiring a power management lock. When the clock source is generated from APB, the lock type will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`. When the clock source is APLL (only in DMA mode), it will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`. Whenever the DAC is converting (i.e., DMA or cosine wave generator is working), the driver guarantees that the power management lock is acquired after calling :cpp:func:`dac_continuous_enable`. Likewise, the driver will release the lock when :cpp:func:`dac_continuous_disable` is called.
IRAM Safe
^^^^^^^^^
By default, the DAC DMA interrupt will be deferred when the cache is disabled for reasons like writing/erasing Flash. Thus the DMA EOF interrupt will not get executed in time.
To avoid such case in real-time applications, you can enable the Kconfig option :ref:`CONFIG_DAC_ISR_IRAM_SAFE` which will:
To avoid such case in real-time applications, you can enable the Kconfig option :ref:`CONFIG_DAC_ISR_IRAM_SAFE` which:
1. Enable the interrupt being serviced even when cache is disabled;
1. Enables the interrupt being serviced even when cache is disabled;
2. Place driver object into DRAM (in case it is linked to PSRAM by accident).
2. Places driver object into DRAM (in case it is linked to PSRAM by accident).
This will allow the interrupt to run while the cache is disabled but will come at the cost of increased IRAM consumption.
This allows the interrupt to run while the cache is disabled but comes at the cost of increased IRAM consumption.
Thread Safety
^^^^^^^^^^^^^
@ -121,11 +121,11 @@ Kconfig Options
- :ref:`CONFIG_DAC_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled. See `IRAM Safe <#iram-safe>`__ for more information.
- :ref:`CONFIG_DAC_SUPPRESS_DEPRECATE_WARN` controls whether to suppress the warning message compilation while using the legacy DAC driver.
- :ref:`CONFIG_DAC_ENABLE_DEBUG_LOG` is used to enable the debug log output. Enable this option will increase the firmware binary size.
- :ref:`CONFIG_DAC_ENABLE_DEBUG_LOG` is used to enable the debug log output. Enable this option increases the firmware binary size.
.. only:: esp32
- :ref:`CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` will auto expand the 8-bit data to 16-bit data in the driver to satisfy the I2S DMA format.
- :ref:`CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` auto expands the 8-bit data to 16-bit data in the driver to satisfy the I2S DMA format.
Application Example
-------------------

View File

@ -15,6 +15,7 @@ Create/Destroy GPIO Bundle
A GPIO bundle is a group of GPIOs, which can be manipulated at the same time in one CPU cycle. The maximal number of GPIOs that a bundle can contain is limited by each CPU. What's more, the GPIO bundle has a strong relevance to the CPU which it derives from. **Any operations on the GPIO bundle should be put inside a task which is running on the same CPU core to the GPIO bundle belongs to.** Likewise, only those ISRs who are installed on the same CPU core are allowed to do operations on that GPIO bundle.
.. note::
Dedicated GPIO is more of a CPU peripheral, so it has a strong relationship with CPU core. It's highly recommended to install and operate GPIO bundle in a pin-to-core task. For example, if GPIOA is connected to CPU0, and the dedicated GPIO instruction is issued from CPU1, then it's impossible to control GPIOA.
To install a GPIO bundle, one needs to call :cpp:func:`dedic_gpio_new_bundle` to allocate the software resources and connect the dedicated channels to user selected GPIOs. Configurations for a GPIO bundle are covered in :cpp:type:`dedic_gpio_bundle_config_t` structure:
@ -55,7 +56,8 @@ The following code shows how to install a output only GPIO bundle:
To uninstall the GPIO bundle, one needs to call :cpp:func:`dedic_gpio_del_bundle`.
.. note::
:cpp:func:`dedic_gpio_new_bundle` doesn't cover any GPIO pad configuration (e.g. pull up/down, drive ability, output/input enable), so before installing a dedicated GPIO bundle, you have to configure the GPIO separately using GPIO driver API (e.g. :cpp:func:`gpio_config`). For more information about GPIO driver, please refer to :doc:`GPIO API Reference <gpio>`.
:cpp:func:`dedic_gpio_new_bundle` doesn't cover any GPIO pad configuration (e.g., pull up/down, drive ability, output/input enable), so before installing a dedicated GPIO bundle, you have to configure the GPIO separately using GPIO driver API (e.g., :cpp:func:`gpio_config`). For more information about GPIO driver, please refer to :doc:`GPIO API Reference <gpio>`.
GPIO Bundle Operations
@ -75,6 +77,7 @@ GPIO Bundle Operations
- :cpp:func:`dedic_gpio_bundle_read_in`
.. note::
Using the above functions might not get a high GPIO flip speed because of the overhead of function calls and the bit operations involved inside. Users can try :ref:`manipulate_gpios_by_writing_assembly_code` instead to reduce the overhead but should take care of the thread safety by themselves.
.. _manipulate_gpios_by_writing_assembly_code:
@ -86,7 +89,7 @@ For advanced users, they can always manipulate the GPIOs by writing assembly cod
1. Allocate a GPIO bundle: :cpp:func:`dedic_gpio_new_bundle`
2. Query the mask occupied by that bundle: :cpp:func:`dedic_gpio_get_out_mask` or/and :cpp:func:`dedic_gpio_get_in_mask`
3. Call CPU LL apis (e.g. `dedic_gpio_cpu_ll_write_mask`) or write assembly code with that mask
3. Call CPU LL apis (e.g., `dedic_gpio_cpu_ll_write_mask`) or write assembly code with that mask
4. The fasted way of toggling IO is to use the dedicated "set/clear" instructions:
.. only:: esp32s2 or esp32s3
@ -103,21 +106,22 @@ For advanced users, they can always manipulate the GPIOs by writing assembly cod
.. only:: esp32s2
For details of supported dedicated GPIO instructions, please refer to *{IDF_TARGET_NAME} Technical Reference Manual* > *IO MUX and GPIO Matrix (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
For details of supported dedicated GPIO instructions, please refer to **{IDF_TARGET_NAME} Technical Reference Manual** > **IO MUX and GPIO Matrix (GPIO, IO_MUX)** [`PDF <{IDF_TARGET_TRM_EN_URL}#iomuxgpio>`__].
.. only:: esp32s3
For details of supported dedicated GPIO instructions, please refer to *{IDF_TARGET_NAME} Technical Reference Manual* > *Processor Instruction Extensions (PIE) (to be added later)* [`PDF <{IDF_TARGET_TRM_EN_URL}#pie>`__].
For details of supported dedicated GPIO instructions, please refer to **{IDF_TARGET_NAME} Technical Reference Manual** > **Processor Instruction Extensions (PIE) (to be added later)** [`PDF <{IDF_TARGET_TRM_EN_URL}#pie>`__].
.. only:: esp32c2 or esp32c3 or esp32c6
Code examples for manipulating dedicated GPIOs from assembly are provided in the :example:`peripherals/dedicated_gpio` directory of ESP-IDF examples. These examples show how to emulate a UART, an I2C and an SPI bus in assembly thanks to dedicated GPIOs.
For details of supported dedicated GPIO instructions, please refer to *{IDF_TARGET_NAME} Technical Reference Manual* > *ESP-RISC-V CPU* [`PDF <{IDF_TARGET_TRM_EN_URL}#riscvcpu>`__].
For details of supported dedicated GPIO instructions, please refer to **{IDF_TARGET_NAME} Technical Reference Manual** > **ESP-RISC-V CPU** [`PDF <{IDF_TARGET_TRM_EN_URL}#riscvcpu>`__].
Some of the dedicated CPU instructions are also wrapped inside ``hal/dedic_gpio_cpu_ll.h`` as helper inline functions.
.. note::
Writing assembly code in application could make your code hard to port between targets, because those customized instructions are not guaranteed to remain the same format on different targets.
.. only:: SOC_DEDIC_GPIO_HAS_INTERRUPT
@ -142,7 +146,7 @@ Some of the dedicated CPU instructions are also wrapped inside ``hal/dedic_gpio_
return high_task_wakeup == pdTRUE;
}
// enable positive edge interrupt on the second GPIO in the bundle (i.e. index 1)
// enable positive edge interrupt on the second GPIO in the bundle (i.e., index 1)
ESP_ERROR_CHECK(dedic_gpio_bundle_set_interrupt_and_callback(bundle, BIT(1), DEDIC_GPIO_INTR_POS_EDGE, dedic_gpio_isr_callback, sem));
// wait for done semaphore

View File

@ -39,10 +39,10 @@ GPIO Summary
{IDF_TARGET_FLEX_GLITCH_FILTER_NUM:default="8"}
{IDF_TARGET_NAME} provides {IDF_TARGET_FLEX_GLITCH_FILTER_NUM} flexible glitch filters, whose duration is configurable. We refer to this kind of filter as ``flex flitch filter``. Each of them can be applied to any input GPIO. However, applying multiple filters to the same GPIO doesn't make difference from one. You can create the filter handle by calling :cpp:func:`gpio_new_flex_glitch_filter`. All the configurations for a flexible glitch filter are listed in the :cpp:type:`gpio_flex_glitch_filter_config_t` structure.
{IDF_TARGET_NAME} provides {IDF_TARGET_FLEX_GLITCH_FILTER_NUM} flexible glitch filters, whose duration is configurable. We refer to this kind of filter as ``flex flitch filter``. Each of them can be applied to any input GPIO. However, applying multiple filters to the same GPIO does not make difference from one. You can create the filter handle by calling :cpp:func:`gpio_new_flex_glitch_filter`. All the configurations for a flexible glitch filter are listed in the :cpp:type:`gpio_flex_glitch_filter_config_t` structure.
- :cpp:member:`gpio_flex_glitch_filter_config_t::gpio_num` sets the GPIO that will be applied to the flex glitch filter.
- :cpp:member:`gpio_flex_glitch_filter_config_t::window_width_ns` and :cpp:member:`gpio_flex_glitch_filter_config_t::window_thres_ns` are the key parameters of the glitch filter. During :cpp:member:`gpio_flex_glitch_filter_config_t::window_width_ns`, any pulse whose width is shorter than :cpp:member:`gpio_flex_glitch_filter_config_t::window_thres_ns` will be discarded. Please note that, you can't set :cpp:member:`gpio_flex_glitch_filter_config_t::window_thres_ns` bigger than :cpp:member:`gpio_flex_glitch_filter_config_t::window_width_ns`.
- :cpp:member:`gpio_flex_glitch_filter_config_t::window_width_ns` and :cpp:member:`gpio_flex_glitch_filter_config_t::window_thres_ns` are the key parameters of the glitch filter. During :cpp:member:`gpio_flex_glitch_filter_config_t::window_width_ns`, any pulse whose width is shorter than :cpp:member:`gpio_flex_glitch_filter_config_t::window_thres_ns` will be discarded. Please note that, you can not set :cpp:member:`gpio_flex_glitch_filter_config_t::window_thres_ns` bigger than :cpp:member:`gpio_flex_glitch_filter_config_t::window_width_ns`.
.. only:: SOC_GPIO_SUPPORT_PIN_GLITCH_FILTER and SOC_GPIO_FLEX_GLITCH_FILTER_NUM

View File

@ -8,9 +8,9 @@ GPTimer (General Purpose Timer) is the driver of {IDF_TARGET_NAME} Timer Group p
Typically, a general purpose timer can be used in scenarios like:
- Free running as a wall clock, fetching a high-resolution timestamp at any time and any places
- Generate period alarms, trigger events periodically
- Generate one-shot alarm, respond in target time
- Free running as a wall clock, fetching a high-resolution timestamp at any time and any places
- Generate period alarms, trigger events periodically
- Generate one-shot alarm, respond in target time
Functional Overview
-------------------
@ -38,15 +38,15 @@ Resource Allocation
Different ESP chips might have different numbers of independent timer groups, and within each group, there could also be several independent timers. [1]_
A GPTimer instance is represented by :cpp:type:`gptimer_handle_t`. The driver behind will manage all available hardware resources in a pool, so that you do not need to care about which timer and which group it belongs to.
A GPTimer instance is represented by :cpp:type:`gptimer_handle_t`. The driver behind manages all available hardware resources in a pool, so that you do not need to care about which timer and which group it belongs to.
To install a timer instance, there is a configuration structure that needs to be given in advance: :cpp:type:`gptimer_config_t`:
- :cpp:member:`gptimer_config_t::clk_src` selects the source clock for the timer. The available clocks are listed in :cpp:type:`gptimer_clock_source_t`, you can only pick one of them. For the effect on power consumption of different clock source, please refer to Section :ref:`gptimer-power-management`.
- :cpp:member:`gptimer_config_t::clk_src` selects the source clock for the timer. The available clocks are listed in :cpp:type:`gptimer_clock_source_t`, you can only pick one of them. For the effect on power consumption of different clock source, please refer to Section :ref:`gptimer-power-management`.
- :cpp:member:`gptimer_config_t::direction` sets the counting direction of the timer, supported directions are listed in :cpp:type:`gptimer_count_direction_t`, you can only pick one of them.
- :cpp:member:`gptimer_config_t::direction` sets the counting direction of the timer, supported directions are listed in :cpp:type:`gptimer_count_direction_t`, you can only pick one of them.
- :cpp:member:`gptimer_config_t::resolution_hz` sets the resolution of the internal counter. Each count step is equivalent to **1 / resolution_hz** seconds.
- :cpp:member:`gptimer_config_t::resolution_hz` sets the resolution of the internal counter. Each count step is equivalent to **1 / resolution_hz** seconds.
- :cpp:member:`gptimer_config::intr_priority` sets the priority of the timer interrupt. If it is set to ``0``, the driver will allocate an interrupt with a default priority. Otherwise, the driver will use the given priority.
@ -54,12 +54,12 @@ To install a timer instance, there is a configuration structure that needs to be
With all the above configurations set in the structure, the structure can be passed to :cpp:func:`gptimer_new_timer` which will instantiate the timer instance and return a handle of the timer.
The function can fail due to various errors such as insufficient memory, invalid arguments, etc. Specifically, when there are no more free timers (i.e. all hardware resources have been used up), then :c:macro:`ESP_ERR_NOT_FOUND` will be returned. The total number of available timers is represented by the :c:macro:`SOC_TIMER_GROUP_TOTAL_TIMERS` and its value will depend on the ESP chip.
The function can fail due to various errors such as insufficient memory, invalid arguments, etc. Specifically, when there are no more free timers (i.e., all hardware resources have been used up), then :c:macro:`ESP_ERR_NOT_FOUND` will be returned. The total number of available timers is represented by the :c:macro:`SOC_TIMER_GROUP_TOTAL_TIMERS` and its value depends on the ESP chip.
If a previously created GPTimer instance is no longer required, you should recycle the timer by calling :cpp:func:`gptimer_del_timer`. This will allow the underlying HW timer to be used for other purposes. Before deleting a GPTimer handle, please disable it by :cpp:func:`gptimer_disable` in advance or make sure it has not enabled yet by :cpp:func:`gptimer_enable`.
If a previously created GPTimer instance is no longer required, you should recycle the timer by calling :cpp:func:`gptimer_del_timer`. This allows the underlying HW timer to be used for other purposes. Before deleting a GPTimer handle, please disable it by :cpp:func:`gptimer_disable` in advance or make sure it has not enabled yet by :cpp:func:`gptimer_enable`.
Creating a GPTimer Handle with Resolution of 1 MHz
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code:: c
@ -87,11 +87,11 @@ Set up Alarm Action
For most of the use cases of GPTimer, you should set up the alarm action before starting the timer, except for the simple wall-clock scenario, where a free running timer is enough. To set up the alarm action, you should configure several members of :cpp:type:`gptimer_alarm_config_t` based on how you make use of the alarm event:
- :cpp:member:`gptimer_alarm_config_t::alarm_count` sets the target count value that will trigger the alarm event. You should also take the counting direction into consideration when setting the alarm value. Specially, :cpp:member:`gptimer_alarm_config_t::alarm_count` and :cpp:member:`gptimer_alarm_config_t::reload_count` cannot be set to the same value when :cpp:member:`gptimer_alarm_config_t::auto_reload_on_alarm` is true, as keeping reload with a target alarm count is meaningless.
- :cpp:member:`gptimer_alarm_config_t::alarm_count` sets the target count value that triggers the alarm event. You should also take the counting direction into consideration when setting the alarm value. Specially, :cpp:member:`gptimer_alarm_config_t::alarm_count` and :cpp:member:`gptimer_alarm_config_t::reload_count` cannot be set to the same value when :cpp:member:`gptimer_alarm_config_t::auto_reload_on_alarm` is true, as keeping reload with a target alarm count is meaningless.
- :cpp:member:`gptimer_alarm_config_t::reload_count` sets the count value to be reloaded when the alarm event happens. This configuration only takes effect when :cpp:member:`gptimer_alarm_config_t::auto_reload_on_alarm` is set to true.
- :cpp:member:`gptimer_alarm_config_t::reload_count` sets the count value to be reloaded when the alarm event happens. This configuration only takes effect when :cpp:member:`gptimer_alarm_config_t::auto_reload_on_alarm` is set to true.
- :cpp:member:`gptimer_alarm_config_t::auto_reload_on_alarm` flag sets whether to enable the auto-reload feature. If enabled, the hardware timer will reload the value of :cpp:member:`gptimer_alarm_config_t::reload_count` into counter immediately when an alarm event happens.
- :cpp:member:`gptimer_alarm_config_t::auto_reload_on_alarm` flag sets whether to enable the auto-reload feature. If enabled, the hardware timer will reload the value of :cpp:member:`gptimer_alarm_config_t::reload_count` into counter immediately when an alarm event happens.
To make the alarm configurations take effect, you should call :cpp:func:`gptimer_set_alarm_action`. Especially, if :cpp:type:`gptimer_alarm_config_t` is set to ``NULL``, the alarm function will be disabled.
@ -104,26 +104,26 @@ To make the alarm configurations take effect, you should call :cpp:func:`gptimer
Register Event Callbacks
^^^^^^^^^^^^^^^^^^^^^^^^
After the timer starts up, it can generate a specific event (e.g. the "Alarm Event") dynamically. If you have some functions that should be called when the event happens, please hook your function to the interrupt service routine by calling :cpp:func:`gptimer_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`gptimer_event_callbacks_t`:
After the timer starts up, it can generate a specific event (e.g., the "Alarm Event") dynamically. If you have some functions that should be called when the event happens, please hook your function to the interrupt service routine by calling :cpp:func:`gptimer_register_event_callbacks`. All supported event callbacks are listed in :cpp:type:`gptimer_event_callbacks_t`:
- :cpp:member:`gptimer_event_callbacks_t::on_alarm` sets a callback function for alarm events. As this function is called within the ISR context, you must ensure that the function does not attempt to block (e.g., by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function). The function prototype is declared in :cpp:type:`gptimer_alarm_cb_t`.
You can save your own context to :cpp:func:`gptimer_register_event_callbacks` as well, via the parameter ``user_data``. The user data will be directly passed to the callback function.
This function will lazy install the interrupt service for the timer but not enable it. So please call this function before :cpp:func:`gptimer_enable`, otherwise the :c:macro:`ESP_ERR_INVALID_STATE` error will be returned. See Section :ref:`enable-and-disable-timer` for more information.
This function lazy installs the interrupt service for the timer but not enable it. So please call this function before :cpp:func:`gptimer_enable`, otherwise the :c:macro:`ESP_ERR_INVALID_STATE` error will be returned. See Section :ref:`enable-and-disable-timer` for more information.
.. _enable-and-disable-timer:
Enable and Disable Timer
^^^^^^^^^^^^^^^^^^^^^^^^
Before doing IO control to the timer, you needs to enable the timer first, by calling :cpp:func:`gptimer_enable`. This function will:
Before doing IO control to the timer, you needs to enable the timer first, by calling :cpp:func:`gptimer_enable`. This function:
* Switch the timer driver state from **init** to **enable**.
* Enable the interrupt service if it has been lazy installed by :cpp:func:`gptimer_register_event_callbacks`.
* Acquire a proper power management lock if a specific clock source (e.g. APB clock) is selected. See Section :ref:`gptimer-power-management` for more information.
* Switches the timer driver state from **init** to **enable**.
* Enables the interrupt service if it has been lazy installed by :cpp:func:`gptimer_register_event_callbacks`.
* Acquires a proper power management lock if a specific clock source (e.g., APB clock) is selected. See Section :ref:`gptimer-power-management` for more information.
Calling :cpp:func:`gptimer_disable` will do the opposite, that is, put the timer driver back to the **init** state, disable the interrupts service and release the power management lock.
Calling :cpp:func:`gptimer_disable` does the opposite, that is, put the timer driver back to the **init** state, disable the interrupts service and release the power management lock.
.. _start-and-stop-timer:
@ -131,7 +131,8 @@ Start and Stop Timer
^^^^^^^^^^^^^^^^^^^^
The basic IO operation of a timer is to start and stop. Calling :cpp:func:`gptimer_start` can make the internal counter work, while calling :cpp:func:`gptimer_stop` can make the counter stop working. The following illustrates how to start a timer with or without an alarm event.
Calling :cpp:func:`gptimer_start` will transit the driver state from **enable** to **run**, and vice versa. You need to make sure the start and stop functions are used in pairs, otherwise, the functions may return :c:macro:`ESP_ERR_INVALID_STATE`. Most of the time, this error means that the timer is already stopped or in the "start protection" state (i.e. :cpp:func:`gptimer_start` is called but not finished).
Calling :cpp:func:`gptimer_start` transits the driver state from **enable** to **run**, and vice versa. You need to make sure the start and stop functions are used in pairs, otherwise, the functions may return :c:macro:`ESP_ERR_INVALID_STATE`. Most of the time, this error means that the timer is already stopped or in the "start protection" state (i.e., :cpp:func:`gptimer_start` is called but not finished).
Start Timer as a Wall Clock
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -283,9 +284,9 @@ Alarm value can be updated dynamically inside the ISR handler callback, by chang
Power Management
^^^^^^^^^^^^^^^^
There're some power management strategies, which might turn off or change the frequency of GPTimer's source clock to save power consumption. For example, during DFS, APB clock will be scaled down. If light-sleep is also enabled, PLL and XTAL clocks will be powered off. Both of them can result in an inaccurate time keeping.
There are some power management strategies, which might turn off or change the frequency of GPTimer's source clock to save power consumption. For example, during DFS, APB clock will be scaled down. If light-sleep is also enabled, PLL and XTAL clocks will be powered off. Both of them can result in an inaccurate time keeping.
The driver can prevent the above situation from happening by creating different power management lock according to different clock source. The driver will increase the reference count of that power management lock in the :cpp:func:`gptimer_enable` and decrease it in the :cpp:func:`gptimer_disable`. So we can ensure the clock source is stable between :cpp:func:`gptimer_enable` and :cpp:func:`gptimer_disable`.
The driver can prevent the above situation from happening by creating different power management lock according to different clock source. The driver increases the reference count of that power management lock in the :cpp:func:`gptimer_enable` and decrease it in the :cpp:func:`gptimer_disable`. So we can ensure the clock source is stable between :cpp:func:`gptimer_enable` and :cpp:func:`gptimer_disable`.
.. _gptimer-iram-safe:
@ -294,13 +295,13 @@ IRAM Safe
By default, the GPTimer interrupt will be deferred when the cache is disabled because of writing or erasing the flash. Thus the alarm interrupt will not get executed in time, which is not expected in a real-time application.
There is a Kconfig option :ref:`CONFIG_GPTIMER_ISR_IRAM_SAFE` that will:
There is a Kconfig option :ref:`CONFIG_GPTIMER_ISR_IRAM_SAFE` that:
- Enable the interrupt being serviced even when the cache is disabled
- Place all functions that used by the ISR into IRAM [2]_
- Place driver object into DRAM (in case it is mapped to PSRAM by accident)
- Enables the interrupt being serviced even when the cache is disabled
- Places all functions that used by the ISR into IRAM [2]_
- Places driver object into DRAM (in case it is mapped to PSRAM by accident)
This will allow the interrupt to run while the cache is disabled, but will come at the cost of increased IRAM consumption.
This allows the interrupt to run while the cache is disabled, but comes at the cost of increased IRAM consumption.
There is another Kconfig option :ref:`CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM` that can put commonly used IO control functions into IRAM as well. So, these functions can also be executable when the cache is disabled. These IO control functions are as follows:
@ -351,7 +352,7 @@ API Reference
.. include-build-file:: inc/timer_types.inc
.. [1]
Different ESP chip series might have different numbers of GPTimer instances. For more details, please refer to *{IDF_TARGET_NAME} Technical Reference Manual* > Chapter *Timer Group (TIMG)* [`PDF <{IDF_TARGET_TRM_EN_URL}#timg>`__]. The driver will not forbid you from applying for more timers, but it will return error when all available hardware resources are used up. Please always check the return value when doing resource allocation (e.g. :cpp:func:`gptimer_new_timer`).
Different ESP chip series might have different numbers of GPTimer instances. For more details, please refer to **{IDF_TARGET_NAME} Technical Reference Manual** > Chapter **Timer Group (TIMG)** [`PDF <{IDF_TARGET_TRM_EN_URL}#timg>`__]. The driver does forbid you from applying for more timers, 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:`gptimer_new_timer`).
.. [2]
:cpp:member:`gptimer_event_callbacks_t::on_alarm` callback and the functions invoked by the callback should also be placed in IRAM, please take care of them by yourself.

View File

@ -118,7 +118,7 @@ When :cpp:member:`i2c_config_t::clk_flags` is 0, the clock allocator will select
.. note::
A clock is not a valid option, if it doesn't meet the requested capabilities, i.e. any bit of requested capabilities (clk_flags) is 0 in the clock's capabilities.
A clock is not a valid option, if it does not meet the requested capabilities, i.e., any bit of requested capabilities (clk_flags) is 0 in the clock's capabilities.
.. only:: esp32
@ -208,9 +208,9 @@ Explanations for :cpp:member:`i2c_config_t::clk_flags` are as follows:
.. note::
The clock frequency of SCL will be influenced by the pull-up resistors and wire capacitance (or might slave capacitance) together. Therefore, users need to choose correct pull-up resistors by themselves to make the frequency accurate. It is recommended by I2C protocol that the pull-up resistors commonly range from 1KOhms to 10KOhms, but different frequencies need different resistors.
The clock frequency of SCL is influenced by the pull-up resistors and wire capacitance (or might slave capacitance) together. Therefore, users need to choose correct pull-up resistors by themselves to make the frequency accurate. It is recommended by I2C protocol that the pull-up resistors commonly range from 1KOhms to 10KOhms, but different frequencies need different resistors.
Generally speaking, the higher frequency is selected, the smaller resistor should be used (but not less than 1KOhms). This is because high resistor will decline the current, which will lengthen the rising time and reduce the frequency. Usually, range 2KOhms to 5KOhms is what we recommend, but users also might need to make some adjustment depends on their reality.
Generally speaking, the higher frequency is selected, the smaller resistor should be used (but not less than 1KOhms). This is because high resistor declines the current, which will lengthen the rising time and reduce the frequency. Usually, range 2KOhms to 5KOhms is what we recommend, but users also might need to make some adjustment depends on their reality.
.. _i2c-api-install-driver:
@ -224,7 +224,7 @@ After the I2C driver is configured, install it by calling the function :cpp:func
.. only:: SOC_I2C_SUPPORT_SLAVE
- (Slave only) Size of buffers to allocate for sending and receiving data. As I2C is a master-centric bus, data can only go from the slave to the master at the master's request. Therefore, the slave will usually have a send buffer where the slave application writes data. The data remains in the send buffer to be read by the master at the master's own discretion.
- (Slave only) Size of buffers to allocate for sending and receiving data. As I2C is a master-centric bus, data can only go from the slave to the master at the master's request. Therefore, the slave usually has a send buffer where the slave application writes data. The data remains in the send buffer to be read by the master at the master's own discretion.
- Flags for allocating the interrupt (see ESP_INTR_FLAG_* values in :component_file:`esp_hw_support/include/esp_intr_alloc.h`)
@ -287,7 +287,7 @@ Indicating Write or Read
After sending a slave address (see Step 3 on both diagrams above), the master either writes or reads from the slave.
The information on what the master will actually do is hidden in the least significant bit of the slave's address.
The information on what the master actually does is hidden in the least significant bit of the slave's address.
For this reason, the command link sent by the master to write data to the slave contains the address ``(ESP_SLAVE_ADDR << 1) | I2C_MASTER_WRITE`` and looks as follows:
@ -315,11 +315,11 @@ Likewise, the command link to read from the slave looks as follows:
- :cpp:func:`i2c_slave_read_buffer`
Whenever the master writes data to the slave, the slave will automatically store it in the receive buffer. This allows the slave application to call the function :cpp:func:`i2c_slave_read_buffer` at its own discretion. This function also has a parameter to specify block time if no data is in the receive buffer. This will allow the slave application to wait with a specified timeout for data to arrive to the buffer.
Whenever the master writes data to the slave, the slave automatically stores it in the receive buffer. This allows the slave application to call the function :cpp:func:`i2c_slave_read_buffer` at its own discretion. This function also has a parameter to specify block time if no data is in the receive buffer. This allows the slave application to wait with a specified timeout for data to arrive to the buffer.
- :cpp:func:`i2c_slave_write_buffer`
The send buffer is used to store all the data that the slave wants to send to the master in FIFO order. The data stays there until the master requests for it. The function :cpp:func:`i2c_slave_write_buffer` has a parameter to specify block time if the send buffer is full. This will allow the slave application to wait with a specified timeout for the adequate amount of space to become available in the send buffer.
The send buffer is used to store all the data that the slave wants to send to the master in FIFO order. The data stays there until the master requests for it. The function :cpp:func:`i2c_slave_write_buffer` has a parameter to specify block time if the send buffer is full. This allows the slave application to wait with a specified timeout for the adequate amount of space to become available in the send buffer.
A code example showing how to use these functions can be found in :example:`peripherals/i2c`.

View File

@ -1,5 +1,6 @@
Inter-IC Sound (I2S)
====================
:link_to_translation:`zh_CN:[中文]`
{IDF_TARGET_I2S_NUM:default="one", esp32="two", esp32s3="two"}
@ -186,7 +187,7 @@ In standard mode, there are always two sound channels, i.e., the left and right
.. wavedrom:: /../_static/diagrams/i2s/tdm_pcm_short.json
- **PCM Long Format**: Data has one-bit shift and the WS signal lasts one-slot bit width for every frame. For example, the duty of WS will be 25% if there are four slots enabled, and 20% if there are five slots.
- **PCM Long Format**: Data has one-bit shift and the WS signal lasts one-slot bit width for every frame. For example, the duty of WS will be 25% if there are four slots enabled, and 20% if there are five slots.
.. wavedrom:: /../_static/diagrams/i2s/tdm_pcm_long.json
@ -195,7 +196,7 @@ In standard mode, there are always two sound channels, i.e., the left and right
LCD/Camera Mode
^^^^^^^^^^^^^^^
LCD/Camera mode is only supported on I2S0 over a parallel bus. For LCD mode, I2S0 should work at master TX mode. For camera mode, I2S0 should work at slave RX mode. These two modes are not implemented by the I2S driver. Please refer to :doc:`/api-reference/peripherals/lcd` for details about the LCD implementation. For more information, see *{IDF_TARGET_NAME} Technical Reference Manual* > *I2S Controller (I2S)* > LCD Mode [`PDF <{IDF_TARGET_TRM_EN_URL}#camlcdctrl>`__].
LCD/Camera mode is only supported on I2S0 over a parallel bus. For LCD mode, I2S0 should work at master TX mode. For camera mode, I2S0 should work at slave RX mode. These two modes are not implemented by the I2S driver. Please refer to :doc:`/api-reference/peripherals/lcd` for details about the LCD implementation. For more information, see **{IDF_TARGET_NAME} Technical Reference Manual** > **I2S Controller (I2S)** > LCD Mode [`PDF <{IDF_TARGET_TRM_EN_URL}#camlcdctrl>`__].
.. only:: SOC_I2S_SUPPORTS_ADC_DAC
@ -225,7 +226,7 @@ Power Management
When the power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust or stop the source clock of I2S before entering Light-sleep, thus potentially changing the I2S signals and leading to transmitting or receiving invalid data.
The I2S driver can prevent the system from changing or stopping the source clock by acquiring a power management lock. When the source clock is generated from APB, the lock type will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX` and when the source clock is APLL (if supported), it will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`. Whenever the user is reading or writing via I2S (i.e., calling :cpp:func:`i2s_channel_read` or :cpp:func:`i2s_channel_write`), the driver will guarantee that the power management lock is acquired. Likewise, the driver releases the lock after the reading or writing finishes.
The I2S driver can prevent the system from changing or stopping the source clock by acquiring a power management lock. When the source clock is generated from APB, the lock type will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX` and when the source clock is APLL (if supported), it will be set to :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`. Whenever the user is reading or writing via I2S (i.e., calling :cpp:func:`i2s_channel_read` or :cpp:func:`i2s_channel_write`), the driver guarantees that the power management lock is acquired. Likewise, the driver releases the lock after the reading or writing finishes.
Finite State Machine
^^^^^^^^^^^^^^^^^^^^
@ -245,7 +246,7 @@ Data Transport
The data transport of the I2S peripheral, including sending and receiving, is realized by DMA. Before transporting data, please call :cpp:func:`i2s_channel_enable` to enable the specific channel. When the sent or received data reaches the size of one DMA buffer, the ``I2S_OUT_EOF`` or ``I2S_IN_SUC_EOF`` interrupt will be triggered. Note that the DMA buffer size is not equal to :cpp:member:`i2s_chan_config_t::dma_frame_num`. One frame here refers to all the sampled data in one WS circle. Therefore, ``dma_buffer_size = dma_frame_num * slot_num * slot_bit_width / 8``. For the data transmitting, users can input the data by calling :cpp:func:`i2s_channel_write`. This function helps users to copy the data from the source buffer to the DMA TX buffer and wait for the transmission to finish. Then it will repeat until the sent bytes reach the given size. For the data receiving, the function :cpp:func:`i2s_channel_read` waits to receive the message queue which contains the DMA buffer address. It helps users copy the data from the DMA RX buffer to the destination buffer.
Both :cpp:func:`i2s_channel_write` and :cpp:func:`i2s_channel_read` are blocking functions. They keeps waiting until the whole source buffer is sent or the whole destination buffer is loaded, unless they exceed the max blocking time, where the error code `ESP_ERR_TIMEOUT` returns. To send or receive data asynchronously, callbacks can be registered by :cpp:func:`i2s_channel_register_event_callback`. Users are able to access the DMA buffer directly in the callback function instead of transmitting or receiving by the two blocking functions. However, please be aware that it is an interrupt callback, so do not add complex logic, run floating operation, or call non-reentrant functions in the callback.
Both :cpp:func:`i2s_channel_write` and :cpp:func:`i2s_channel_read` are blocking functions. They keeps waiting until the whole source buffer is sent or the whole destination buffer is loaded, unless they exceed the max blocking time, where the error code ``ESP_ERR_TIMEOUT`` returns. To send or receive data asynchronously, callbacks can be registered by :cpp:func:`i2s_channel_register_event_callback`. Users are able to access the DMA buffer directly in the callback function instead of transmitting or receiving by the two blocking functions. However, please be aware that it is an interrupt callback, so do not add complex logic, run floating operation, or call non-reentrant functions in the callback.
Configuration
^^^^^^^^^^^^^
@ -257,13 +258,13 @@ IRAM Safe
By default, the I2S interrupt will be deferred when the cache is disabled for reasons like writing/erasing flash. Thus the EOF interrupt will not get executed in time.
To avoid such case in real-time applications, you can enable the Kconfig option :ref:`CONFIG_I2S_ISR_IRAM_SAFE` that will:
To avoid such case in real-time applications, you can enable the Kconfig option :ref:`CONFIG_I2S_ISR_IRAM_SAFE` that:
1. Keep the interrupt being serviced even when the cache is disabled.
1. Keeps the interrupt being serviced even when the cache is disabled.
2. Place driver object into DRAM (in case it is linked to PSRAM by accident).
2. Places driver object into DRAM (in case it is linked to PSRAM by accident).
This will allow the interrupt to run while the cache is disabled, but will come at the cost of increased IRAM consumption.
This allows the interrupt to run while the cache is disabled, but comes at the cost of increased IRAM consumption.
Thread Safety
^^^^^^^^^^^^^
@ -275,7 +276,7 @@ Kconfig Options
- :ref:`CONFIG_I2S_ISR_IRAM_SAFE` controls whether the default ISR handler can work when the cache is disabled. See `IRAM Safe <#iram-safe>`__ for more information.
- :ref:`CONFIG_I2S_SUPPRESS_DEPRECATE_WARN` controls whether to suppress the compiling warning message while using the legacy I2S driver.
- :ref:`CONFIG_I2S_ENABLE_DEBUG_LOG` is used to enable the debug log output. Enable this option will increase the firmware binary size.
- :ref:`CONFIG_I2S_ENABLE_DEBUG_LOG` is used to enable the debug log output. Enable this option increases the firmware binary size.
Application Example
-------------------
@ -634,7 +635,7 @@ Here is the table of the data received in the buffer with different :cpp:member:
.. only:: SOC_I2S_SUPPORTS_PDM_RX
PDM RX usage
PDM RX Usage
^^^^^^^^^^^^
For PDM mode in RX channel, the slot configuration helper macro is:
@ -735,9 +736,9 @@ Here is the table of the data received in the buffer with different :cpp:member:
.. note::
Due to hardware limitation, when setting the clock configuration for a slave role, please be aware that :cpp:member:`i2s_tdm_clk_config_t::bclk_div` should not be smaller than 8. Increasing this field can reduce the lagging of the data sent from the slave. In the high sample rate case, the data might lag behind for more than one BCLK which will lead to data malposition. Users may gradually increase :cpp:member:`i2s_tdm_clk_config_t::bclk_div` to correct it.
Due to hardware limitation, when setting the clock configuration for a slave role, please be aware that :cpp:member:`i2s_tdm_clk_config_t::bclk_div` should not be smaller than 8. Increasing this field can reduce the lagging of the data sent from the slave. In the high sample rate case, the data might lag behind for more than one BCLK which leads to data malposition. Users may gradually increase :cpp:member:`i2s_tdm_clk_config_t::bclk_div` to correct it.
As :cpp:member:`i2s_tdm_clk_config_t::bclk_div` is the division of MCLK to BCLK, increasing it will also increase the MCLK frequency. Therefore, the clock calculation may fail if MCLK is too high to divide from the source clock. This means that a larger value for :cpp:member:`i2s_tdm_clk_config_t::bclk_div` is not necessarily better.
As :cpp:member:`i2s_tdm_clk_config_t::bclk_div` is the division of MCLK to BCLK, increasing it also increases the MCLK frequency. Therefore, the clock calculation may fail if MCLK is too high to divide from the source clock. This means that a larger value for :cpp:member:`i2s_tdm_clk_config_t::bclk_div` is not necessarily better.
TDM TX Mode
~~~~~~~~~~~

View File

@ -13,11 +13,11 @@ In ``esp_lcd``, an LCD panel is represented by :cpp:type:`esp_lcd_panel_handle_t
.. list::
- Controller based LCD driver involves multiple steps to get a panel handle, like bus allocation, IO device registration and controller driver install. The frame buffer is located in the controller's internal GRAM (Graphical RAM). ESP-IDF provides only a limited number of LCD controller drivers out of the box (e.g. ST7789, SSD1306), :ref:`more_controller_based_lcd_drivers` are maintained in the `Espressif Component Registry <https://components.espressif.com/>_`.
- Controller based LCD driver involves multiple steps to get a panel handle, like bus allocation, IO device registration and controller driver install. The frame buffer is located in the controller's internal GRAM (Graphical RAM). ESP-IDF provides only a limited number of LCD controller drivers out of the box (e.g., ST7789, SSD1306), :ref:`more_controller_based_lcd_drivers` are maintained in the `Espressif Component Registry <https://components.espressif.com/>_`.
- :ref:`spi_lcd_panel` describes the steps to install the SPI LCD IO driver and then get the panel handle.
- :ref:`i2c_lcd_panel` describes the steps to install the I2C LCD IO driver and then get the panel handle.
:SOC_LCD_I80_SUPPORTED: - :ref:`i80_lcd_panel` describes the steps to install the I80 LCD IO driver and then get the panel handle.
:SOC_LCD_RGB_SUPPORTED: - :ref:`rgb_lcd_panel` - is based on a group of specific synchronous signals indicating where to start and stop a frame. The frame buffer is allocated on the ESP side. The driver install steps are much simplified because we don't need to install any IO interface driver in this case.
:SOC_LCD_RGB_SUPPORTED: - :ref:`rgb_lcd_panel` - is based on a group of specific synchronous signals indicating where to start and stop a frame. The frame buffer is allocated on the ESP side. The driver install steps are much simplified because we do not need to install any IO interface driver in this case.
- :ref:`lcd_panel_operations` - provides a set of APIs to operate the LCD panel, like turning on/off the display, setting the orientation, etc. These operations are common for either controller-based LCD panel driver or RGB LCD panel driver.
.. _spi_lcd_panel:
@ -41,10 +41,10 @@ SPI Interfaced LCD
#. Allocate an LCD IO device handle from the SPI bus. In this step, you need to provide the following information:
- :cpp:member:`esp_lcd_panel_io_spi_config_t::dc_gpio_num`: Sets the gpio number for the DC signal line (some LCD calls this ``RS`` line). The LCD driver will use this GPIO to switch between sending command and sending data.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::cs_gpio_num`: Sets the gpio number for the CS signal line. The LCD driver will use this GPIO to select the LCD chip. If the SPI bus only has one device attached (i.e. this LCD), you can set the gpio number to ``-1`` to occupy the bus exclusively.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::dc_gpio_num`: Sets the gpio number for the DC signal line (some LCD calls this ``RS`` line). The LCD driver uses this GPIO to switch between sending command and sending data.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::cs_gpio_num`: Sets the gpio number for the CS signal line. The LCD driver uses this GPIO to select the LCD chip. If the SPI bus only has one device attached (i.e., this LCD), you can set the gpio number to ``-1`` to occupy the bus exclusively.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::pclk_hz` sets the frequency of the pixel clock, in Hz. The value should not exceed the range recommended in the LCD spec.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::spi_mode` sets the SPI mode. The LCD driver will use this mode to communicate with the LCD. For the meaning of the SPI mode, please refer to the :doc:`SPI Master API doc </api-reference/peripherals/spi_master>`.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::spi_mode` sets the SPI mode. The LCD driver uses this mode to communicate with the LCD. For the meaning of the SPI mode, please refer to the :doc:`SPI Master API doc </api-reference/peripherals/spi_master>`.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_panel_io_spi_config_t::lcd_param_bits` set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
- :cpp:member:`esp_lcd_panel_io_spi_config_t::trans_queue_depth` sets the depth of the SPI transaction queue. A bigger value means more transactions can be queued up, but it also consumes more memory.
@ -67,7 +67,7 @@ SPI Interfaced LCD
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` sets the LCD's hardware reset GPIO number. If the LCD does not have a hardware reset pin, set this to ``-1``.
- :cpp:member:`esp_lcd_panel_dev_config_t::rgb_endian` sets the endian of the RGB color data.
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver uses this value to calculate the number of bytes to send to the LCD controller chip.
.. code-block:: c
@ -102,7 +102,7 @@ I2C Interfaced LCD
#. Allocate an LCD IO device handle from the I2C bus. In this step, you need to provide the following information:
- :cpp:member:`esp_lcd_panel_io_i2c_config_t::dev_addr` sets the I2C device address of the LCD controller chip. The LCD driver will use this address to communicate with the LCD controller chip.
- :cpp:member:`esp_lcd_panel_io_i2c_config_t::dev_addr` sets the I2C device address of the LCD controller chip. The LCD driver uses this address to communicate with the LCD controller chip.
- :cpp:member:`esp_lcd_panel_io_i2c_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_panel_io_i2c_config_t::lcd_param_bits` set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
.. code-block:: c
@ -120,7 +120,7 @@ I2C Interfaced LCD
#. Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the I2C IO device handle that allocated in the last step, and some panel specific configurations:
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` sets the LCD's hardware reset GPIO number. If the LCD does not have a hardware reset pin, set this to ``-1``.
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver uses this value to calculate the number of bytes to send to the LCD controller chip.
.. code-block:: c
@ -174,7 +174,7 @@ I2C Interfaced LCD
#. Allocate an LCD IO device handle from the I80 bus. In this step, you need to provide the following information:
- :cpp:member:`esp_lcd_panel_io_i80_config_t::cs_gpio_num` sets the GPIO number of the chip select pin.
- :cpp:member:`esp_lcd_panel_io_i80_config_t::pclk_hz` sets the pixel clock frequency in Hz. Higher pixel clock frequency will result in higher refresh rate, but may cause flickering if the DMA bandwidth is not sufficient or the LCD controller chip does not support high pixel clock frequency.
- :cpp:member:`esp_lcd_panel_io_i80_config_t::pclk_hz` sets the pixel clock frequency in Hz. Higher pixel clock frequency results in higher refresh rate, but may cause flickering if the DMA bandwidth is not sufficient or the LCD controller chip does not support high pixel clock frequency.
- :cpp:member:`esp_lcd_panel_io_i80_config_t::lcd_cmd_bits` and :cpp:member:`esp_lcd_panel_io_i80_config_t::lcd_param_bits` set the bit width of the command and parameter that recognized by the LCD controller chip. This is chip specific, you should refer to your LCD spec in advance.
- :cpp:member:`esp_lcd_panel_io_i80_config_t::trans_queue_depth` sets the maximum number of transactions that can be queued in the LCD IO device. A bigger value means more transactions can be queued up, but it also consumes more memory.
@ -198,7 +198,7 @@ I2C Interfaced LCD
#. Install the LCD controller driver. The LCD controller driver is responsible for sending the commands and parameters to the LCD controller chip. In this step, you need to specify the I80 IO device handle that allocated in the last step, and some panel specific configurations:
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver will use this value to calculate the number of bytes to send to the LCD controller chip.
- :cpp:member:`esp_lcd_panel_dev_config_t::bits_per_pixel` sets the bit width of the pixel color data. The LCD driver uses this value to calculate the number of bytes to send to the LCD controller chip.
- :cpp:member:`esp_lcd_panel_dev_config_t::reset_gpio_num` sets the GPIO number of the reset pin. If the LCD controller chip does not have a reset pin, you can set this value to ``-1``.
- :cpp:member:`esp_lcd_panel_dev_config_t::rgb_endian` sets the endian of the pixel color data.
@ -220,7 +220,7 @@ I2C Interfaced LCD
More Controller Based LCD Drivers
---------------------------------
More LCD panel drivers and touch drivers are available in `IDF Component Registry <https://components.espressif.com/search/lcd>`_. The list of available and planned drivers with links is in this `table <https://github.com/espressif/esp-bsp/blob/master/LCD.md>`_.
More LCD panel drivers and touch drivers are available in `ESP-IDF Component Registry <https://components.espressif.com/search/lcd>`_. The list of available and planned drivers with links is in this `table <https://github.com/espressif/esp-bsp/blob/master/LCD.md>`_.
.. only:: SOC_LCD_RGB_SUPPORTED
@ -233,13 +233,13 @@ More LCD panel drivers and touch drivers are available in `IDF Component Registr
- :cpp:member:`esp_lcd_rgb_panel_config_t::clk_src` selects the clock source for the RGB LCD controller. The available clock sources are listed in :cpp:type:`lcd_clock_source_t`.
- :cpp:member:`esp_lcd_rgb_panel_config_t::data_width` set number of data lines used by the RGB interface. Currently, the supported value can be 8 or 16.
- :cpp:member:`esp_lcd_rgb_panel_config_t::bits_per_pixel` set the number of bits per pixel. This is different from :cpp:member:`esp_lcd_rgb_panel_config_t::data_width`. By default, if you set this field to 0, the driver will automatically adjust the bpp to the :cpp:member:`esp_lcd_rgb_panel_config_t::data_width`. But in some cases, these two value must be different. For example, a Serial RGB interface LCD only needs ``8`` data lines, but the color width can reach to ``RGB888``, i.e. the :cpp:member:`esp_lcd_rgb_panel_config_t::bits_per_pixel` should be set to ``24``.
- :cpp:member:`esp_lcd_rgb_panel_config_t::bits_per_pixel` set the number of bits per pixel. This is different from :cpp:member:`esp_lcd_rgb_panel_config_t::data_width`. By default, if you set this field to 0, the driver will automatically adjust the bpp to the :cpp:member:`esp_lcd_rgb_panel_config_t::data_width`. But in some cases, these two value must be different. For example, a Serial RGB interface LCD only needs ``8`` data lines, but the color width can reach to ``RGB888``, i.e., the :cpp:member:`esp_lcd_rgb_panel_config_t::bits_per_pixel` should be set to ``24``.
- :cpp:member:`esp_lcd_rgb_panel_config_t::hsync_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::vsync_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::de_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::pclk_gpio_num`, :cpp:member:`esp_lcd_rgb_panel_config_t::disp_gpio_num` and :cpp:member:`esp_lcd_rgb_panel_config_t::data_gpio_nums` are the GPIO pins used by the RGB LCD controller. If some of them are not used, please set it to `-1`.
- :cpp:member:`esp_lcd_rgb_panel_config_t::sram_trans_align` and :cpp:member:`esp_lcd_rgb_panel_config_t::psram_trans_align` set the alignment of the allocated frame buffer. Internally, the DMA transfer ability will adjust against these alignment values. A higher alignment value can lead to a bigger DMA burst size. Please note, the alignment value must be a power of 2.
- :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px` set the size of bounce buffer. This is only necessary for a so-called "bounce buffer" mode. Please refer to :ref:`bounce_buffer_with_single_psram_frame_buffer` for more information.
- :cpp:member:`esp_lcd_rgb_panel_config_t::timings` sets the LCD panel specific timing parameters. All required parameters are listed in the :cpp:type:`esp_lcd_rgb_timing_t`, including the LCD resolution and blanking porches. Please fill them according to the datasheet of your LCD.
- :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` sets whether to allocate the frame buffer from PSRAM or not. Please refer to :ref:`single_frame_buffer_in_psram` for more information.
- :cpp:member:`esp_lcd_rgb_panel_config_t::num_fbs` sets the number of frame buffers allocated by the driver. For backward compatibility, ``0`` means to allocate ``one`` frame buffer. Please use :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` if you don't want to allocate any frame buffer.
- :cpp:member:`esp_lcd_rgb_panel_config_t::num_fbs` sets the number of frame buffers allocated by the driver. For backward compatibility, ``0`` means to allocate ``one`` frame buffer. Please use :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` if you do not want to allocate any frame buffer.
- :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` if sets, no frame buffer will be allocated. This is also called the :ref:`bounce_buffer_only` mode.
RGB LCD Frame Buffer Operation Modes
@ -250,7 +250,7 @@ More LCD panel drivers and touch drivers are available in `IDF Component Registr
Single Frame Buffer in Internal Memory
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This is the default and simplest and you don't have to specify flags or bounce buffer options. A frame buffer is allocated from the internal memory. The frame data is read out by DMA to the LCD verbatim. It needs no CPU intervention to function, but it has the downside that it uses up a fair bit of the limited amount of internal memory.
This is the default and simplest and you do not have to specify flags or bounce buffer options. A frame buffer is allocated from the internal memory. The frame data is read out by DMA to the LCD verbatim. It needs no CPU intervention to function, but it has the downside that it uses up a fair bit of the limited amount of internal memory.
.. code:: c
@ -291,11 +291,11 @@ More LCD panel drivers and touch drivers are available in `IDF Component Registr
Single Frame Buffer in PSRAM
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you have PSRAM and want to store the frame buffer there rather than in the limited internal memory, the LCD peripheral will use EDMA to fetch frame data directly from the PSRAM, bypassing the internal cache. You can enable this feature by setting the :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` to ``true``. The downside of this is that when both the CPU as well as EDMA need access to the PSRAM, the bandwidth will be **shared** between them, that is, EDMA gets half and the CPUs get the other half. If there're other peripherals using EDMA as well, with a high enough pixel clock this can lead to starvation of the LCD peripheral, leading to display corruption. However, if the pixel clock is low enough for this not to be an issue, this is a solution that uses almost no CPU intervention.
If you have PSRAM and want to store the frame buffer there rather than in the limited internal memory, the LCD peripheral will use EDMA to fetch frame data directly from the PSRAM, bypassing the internal cache. You can enable this feature by setting the :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` to ``true``. The downside of this is that when both the CPU as well as EDMA need access to the PSRAM, the bandwidth will be **shared** between them, that is, EDMA gets half and the CPUs get the other half. If there are other peripherals using EDMA as well, with a high enough pixel clock this can lead to starvation of the LCD peripheral, leading to display corruption. However, if the pixel clock is low enough for this not to be an issue, this is a solution that uses almost no CPU intervention.
.. only:: esp32s3
The PSRAM shares the same SPI bus with the main Flash (the one stores your firmware binary). At one time, there only be one consumer of the SPI bus. When you also use the main flash to serve your file system (e.g. :doc:`SPIFFS </api-reference/storage/spiffs>`), the bandwidth of the underlying SPI bus will also be shared, leading to display corruption. You can use :cpp:func:`esp_lcd_rgb_panel_set_pclk` to update the pixel clock frequency to a lower value.
The PSRAM shares the same SPI bus with the main Flash (the one stores your firmware binary). At one time, there only be one consumer of the SPI bus. When you also use the main flash to serve your file system (e.g., :doc:`SPIFFS </api-reference/storage/spiffs>`), the bandwidth of the underlying SPI bus will also be shared, leading to display corruption. You can use :cpp:func:`esp_lcd_rgb_panel_set_pclk` to update the pixel clock frequency to a lower value.
.. code:: c
@ -381,15 +381,15 @@ More LCD panel drivers and touch drivers are available in `IDF Component Registr
Bounce Buffer with Single PSRAM Frame Buffer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This mode allocates two so-called ``bounce buffers`` from the internal memory, and a main frame buffer that is still in PSRAM. This mode is selected by setting the :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` flag and additionally specifying a non-zero :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px` value. The bounce buffers only need to be large enough to hold a few lines of display data, which is significantly less than the main frame buffer. The LCD peripheral will use DMA to read data from one of the bounce buffers, and meanwhile an interrupt routine will use the CPU DCache to copy data from the main PSRAM frame buffer into the other bounce buffer. Once the LCD peripheral has finished reading the bounce buffer, the two buffers change place and the CPU can fill the others. The advantage of this mode is that, you can achieve higher pixel clock frequency. As the bounce buffers are larger than the FIFOs in the EDMA path, this method is also more robust against short bandwidth spikes. The downside is a major increase in CPU use and the LCD **CAN'T** work if we disable the cache of the external memory, via e.g. OTA or NVS write to the main flash.
This mode allocates two so-called ``bounce buffers`` from the internal memory, and a main frame buffer that is still in PSRAM. This mode is selected by setting the :cpp:member:`esp_lcd_rgb_panel_config_t::fb_in_psram` flag and additionally specifying a non-zero :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px` value. The bounce buffers only need to be large enough to hold a few lines of display data, which is significantly less than the main frame buffer. The LCD peripheral uses DMA to read data from one of the bounce buffers, and meanwhile an interrupt routine uses the CPU DCache to copy data from the main PSRAM frame buffer into the other bounce buffer. Once the LCD peripheral has finished reading the bounce buffer, the two buffers change place and the CPU can fill the others. The advantage of this mode is that, you can achieve higher pixel clock frequency. As the bounce buffers are larger than the FIFOs in the EDMA path, this method is also more robust against short bandwidth spikes. The downside is a major increase in CPU use and the LCD **CAN NOT** work if we disable the cache of the external memory, via e.g., OTA or NVS write to the main flash.
.. note::
It's highly recommended to turn on the "PSRAM XIP (Execute In Place)" feature in this mode by enabling the Kconfig options: :ref:`CONFIG_SPIRAM_FETCH_INSTRUCTIONS` and :ref:`CONFIG_SPIRAM_RODATA`, which allows the CPU to fetch instructions and readonly data from the PSRAM instead of the main flash. What's more, the external memory cache won't be disabled even if you attempt to write to the main flash through SPI1. This makes it possible to display an OTA progress bar for your application.
It is highly recommended to turn on the "PSRAM XIP (Execute In Place)" feature in this mode by enabling the Kconfig options: :ref:`CONFIG_SPIRAM_FETCH_INSTRUCTIONS` and :ref:`CONFIG_SPIRAM_RODATA`, which allows the CPU to fetch instructions and readonly data from the PSRAM instead of the main flash. What is more, the external memory cache will not be disabled even if you attempt to write to the main flash through SPI1. This makes it possible to display an OTA progress bar for your application.
.. note::
This mode still has another problem which is also caused by insufficient PSRAM bandwidth. e.g. when your draw buffers are allocated from PSRAM, and their contents are copied into the internal frame buffer on CPU core 1. On CPU core 0, there's another memory copy happening in the DMA EOF ISR. In this situation, both CPUs are accessing the PSRAM by cache and sharing the bandwidth of the PSRAM. This increases the memory copy time that spent in the DMA EOF ISR significantly. The driver can't switch the bounce buffer in time, thus leading to a shift on the LCD screen. Although the driver can detect such a condition and perform a restart in the LCD's VSYNC interrupt handler, you still can see a flickering on the screen.
This mode still has another problem which is also caused by insufficient PSRAM bandwidth. e.g., when your draw buffers are allocated from PSRAM, and their contents are copied into the internal frame buffer on CPU core 1. On CPU core 0, there is another memory copy happening in the DMA EOF ISR. In this situation, both CPUs are accessing the PSRAM by cache and sharing the bandwidth of the PSRAM. This increases the memory copy time that spent in the DMA EOF ISR significantly. The driver can not switch the bounce buffer in time, thus leading to a shift on the LCD screen. Although the driver can detect such a condition and perform a restart in the LCD's VSYNC interrupt handler, you still can see a flickering on the screen.
.. code:: c
@ -427,19 +427,19 @@ More LCD panel drivers and touch drivers are available in `IDF Component Registr
};
ESP_ERROR_CHECK(esp_lcd_new_rgb_panel(&panel_config, &panel_handle));
Note that this mode also allows for a :cpp:member:`esp_lcd_rgb_panel_config_t::bb_invalidate_cache` flag to be set. Enabling this frees up the cache lines after they're used to read out the frame buffer data from PSRAM, but it may lead to slight corruption if the other core writes data to the frame buffer at the exact time the cache lines are freed up. (Technically, a write to the frame buffer can be ignored if it falls between the cache writeback and the cache invalidate calls.)
Note that this mode also allows for a :cpp:member:`esp_lcd_rgb_panel_config_t::bb_invalidate_cache` flag to be set. Enabling this frees up the cache lines after they are used to read out the frame buffer data from PSRAM, but it may lead to slight corruption if the other core writes data to the frame buffer at the exact time the cache lines are freed up. (Technically, a write to the frame buffer can be ignored if it falls between the cache writeback and the cache invalidate calls.)
.. _bounce_buffer_only:
Bounce Buffer Only
~~~~~~~~~~~~~~~~~~
This mode is similar to the :ref:`bounce_buffer_with_single_psram_frame_buffer`, but there is no PSRAM frame buffer initialized by the LCD driver. Instead, the user supplies a callback function that is responsible for filling the bounce buffers. As this driver does not care where the written pixels come from, this allows for the callback doing e.g. on-the-fly conversion from a smaller, 8-bit-per-pixel PSRAM frame buffer to an 16-bit LCD, or even procedurally-generated frame-buffer-less graphics. This option is selected by setting the :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` flag and supplying a :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px` value. And then register the :cpp:member:`esp_lcd_rgb_panel_event_callbacks_t::on_bounce_empty` callback by calling :cpp:func:`esp_lcd_rgb_panel_register_event_callbacks`.
This mode is similar to the :ref:`bounce_buffer_with_single_psram_frame_buffer`, but there is no PSRAM frame buffer initialized by the LCD driver. Instead, the user supplies a callback function that is responsible for filling the bounce buffers. As this driver does not care where the written pixels come from, this allows for the callback doing e.g., on-the-fly conversion from a smaller, 8-bit-per-pixel PSRAM frame buffer to an 16-bit LCD, or even procedurally-generated frame-buffer-less graphics. This option is selected by setting the :cpp:member:`esp_lcd_rgb_panel_config_t::no_fb` flag and supplying a :cpp:member:`esp_lcd_rgb_panel_config_t::bounce_buffer_size_px` value. And then register the :cpp:member:`esp_lcd_rgb_panel_event_callbacks_t::on_bounce_empty` callback by calling :cpp:func:`esp_lcd_rgb_panel_register_event_callbacks`.
.. note::
It should never happen in a well-designed embedded application, but it can in theory be possible that the DMA cannot deliver data as fast as the LCD consumes it. In the {IDF_TARGET_NAME} hardware, this leads to the LCD simply outputting dummy bytes while DMA waits for data. If we were to run DMA in a stream fashion, this would mean a de-sync between the LCD address the DMA reads the data for and the LCD address the LCD peripheral thinks it outputs data for, leading to a **permanently** shifted image.
In order to stop this from happening, you can either enable the :ref:`CONFIG_LCD_RGB_RESTART_IN_VSYNC` option, so the driver can restart the DMA in the VBlank interrupt automatically or call :cpp:func:`esp_lcd_rgb_panel_restart` to restart the DMA manually. Note :cpp:func:`esp_lcd_rgb_panel_restart` doesn't restart the DMA immediately, the DMA will still be restarted in the next VSYNC event.
In order to stop this from happening, you can either enable the :ref:`CONFIG_LCD_RGB_RESTART_IN_VSYNC` option, so the driver can restart the DMA in the VBlank interrupt automatically or call :cpp:func:`esp_lcd_rgb_panel_restart` to restart the DMA manually. Note :cpp:func:`esp_lcd_rgb_panel_restart` does not restart the DMA immediately, the DMA is still restarted in the next VSYNC event.
.. _lcd_panel_operations:
@ -451,23 +451,23 @@ LCD Panel IO Operations
-----------------------
* :cpp:func:`esp_lcd_panel_reset` can reset the LCD panel.
* :cpp:func:`esp_lcd_panel_init` will perform a basic initialization of the panel. To perform more manufacture specific initialization, please go to :ref:`steps_add_manufacture_init`.
* :cpp:func:`esp_lcd_panel_init` performs a basic initialization of the panel. To perform more manufacture specific initialization, please go to :ref:`steps_add_manufacture_init`.
* Through combined use of :cpp:func:`esp_lcd_panel_swap_xy` and :cpp:func:`esp_lcd_panel_mirror`, you can rotate the LCD screen.
* :cpp:func:`esp_lcd_panel_disp_on_off` can turn on or off the LCD screen (different from LCD backlight).
* :cpp:func:`esp_lcd_panel_draw_bitmap` is the most significant function, that will do the magic to draw the user provided color buffer to the LCD screen, where the draw window is also configurable.
* :cpp:func:`esp_lcd_panel_draw_bitmap` is the most significant function, which does the magic to draw the user provided color buffer to the LCD screen, where the draw window is also configurable.
.. _steps_add_manufacture_init:
Steps to Add Manufacture Specific Initialization
-------------------------------------------------
The LCD controller drivers (e.g. st7789) in esp-idf only provide basic initialization in the :cpp:func:`esp_lcd_panel_init`, leaving the vast majority of settings to the default values. Some LCD modules needs to set a bunch of manufacture specific configurations before it can display normally. These configurations usually include gamma, power voltage and so on. If you want to add manufacture specific initialization, please follow the steps below:
The LCD controller drivers (e.g., st7789) in esp-idf only provide basic initialization in the :cpp:func:`esp_lcd_panel_init`, leaving the vast majority of settings to the default values. Some LCD modules needs to set a bunch of manufacture specific configurations before it can display normally. These configurations usually include gamma, power voltage and so on. If you want to add manufacture specific initialization, please follow the steps below:
.. code:: c
esp_lcd_panel_reset(panel_handle);
esp_lcd_panel_init(panel_handle);
// set extra configurations e.g. gamma control
// set extra configurations e.g., gamma control
// with the underlying IO handle
// please consult your manufacture for special commands and corresponding values
esp_lcd_panel_io_tx_param(io_handle, GAMMA_CMD, (uint8_t[]) {

View File

@ -8,8 +8,7 @@ LED Control (LEDC)
Introduction
------------
The LED control (LEDC) peripheral is primarily designed to control the intensity of LEDs, although it can also be used to generate PWM signals for other purposes.
It has {IDF_TARGET_SOC_LEDC_CHANNEL_NUM} channels which can generate independent waveforms that can be used, for example, to drive RGB LED devices.
The LED control (LEDC) peripheral is primarily designed to control the intensity of LEDs, although it can also be used to generate PWM signals for other purposes. It has {IDF_TARGET_SOC_LEDC_CHANNEL_NUM} channels which can generate independent waveforms that can be used, for example, to drive RGB LED devices.
.. only:: esp32
@ -87,7 +86,7 @@ The source clock can also limit the PWM frequency. The higher the source clock f
- High / Low
- Dynamic Frequency Scaling compatible
* - RC_FAST_CLK
- ~8 MHz
- ~ 8 MHz
- Low
- Dynamic Frequency Scaling compatible, Light sleep compatible
@ -107,7 +106,7 @@ The source clock can also limit the PWM frequency. The higher the source clock f
- 1 MHz
- Dynamic Frequency Scaling compatible
* - RC_FAST_CLK
- ~8 MHz
- ~ 8 MHz
- Dynamic Frequency Scaling compatible, Light sleep compatible
* - XTAL_CLK
- 40 MHz
@ -126,7 +125,7 @@ The source clock can also limit the PWM frequency. The higher the source clock f
- 80 MHz
- /
* - RC_FAST_CLK
- ~20 MHz
- ~ 20 MHz
- Dynamic Frequency Scaling compatible, Light sleep compatible
* - XTAL_CLK
- 40 MHz
@ -145,7 +144,7 @@ The source clock can also limit the PWM frequency. The higher the source clock f
- 60 MHz
- /
* - RC_FAST_CLK
- ~20 MHz
- ~ 20 MHz
- Dynamic Frequency Scaling compatible, Light sleep compatible
* - XTAL_CLK
- 40 MHz
@ -164,7 +163,7 @@ The source clock can also limit the PWM frequency. The higher the source clock f
- 80 MHz
- /
* - RC_FAST_CLK
- ~20 MHz
- ~ 20 MHz
- Dynamic Frequency Scaling compatible, Light sleep compatible
* - XTAL_CLK
- 40 MHz
@ -183,7 +182,7 @@ The source clock can also limit the PWM frequency. The higher the source clock f
- 96 MHz
- /
* - RC_FAST_CLK
- ~8 MHz
- ~ 8 MHz
- Dynamic Frequency Scaling compatible, Light sleep compatible
* - XTAL_CLK
- 32 MHz
@ -247,10 +246,10 @@ To set the duty cycle, use the dedicated function :cpp:func:`ledc_set_duty`. Aft
Another way to set the duty cycle, as well as some other channel parameters, is by calling :cpp:func:`ledc_channel_config` covered in Section :ref:`ledc-api-configure-channel`.
The range of the duty cycle values passed to functions depends on selected ``duty_resolution`` and should be from ``0`` to ``(2 ** duty_resolution) - 1``. For example, if the selected duty resolution is 10, then the duty cycle values can range from 0 to 1023. This provides the resolution of ~0.1%.
The range of the duty cycle values passed to functions depends on selected ``duty_resolution`` and should be from ``0`` to ``(2 ** duty_resolution) - 1``. For example, if the selected duty resolution is 10, then the duty cycle values can range from 0 to 1023. This provides the resolution of ~ 0.1%.
Change PWM Duty Cycle using Hardware
Change PWM Duty Cycle Using Hardware
""""""""""""""""""""""""""""""""""""
The LEDC hardware provides the means to gradually transition from one duty cycle value to another. To use this functionality, enable fading with :cpp:func:`ledc_fade_func_install` and then configure it by calling one of the available fading functions:
@ -320,7 +319,7 @@ For registration of a handler to address this interrupt, call :cpp:func:`ledc_is
High speed mode enables a glitch-free changeover of timer settings. This means that if the timer settings are modified, the changes will be applied automatically on the next overflow interrupt of the timer. In contrast, when updating the low-speed timer, the change of settings should be explicitly triggered by software. The LEDC driver handles it in the background, e.g., when :cpp:func:`ledc_timer_config` or :cpp:func:`ledc_timer_set` is called.
For additional details regarding speed modes, see *{IDF_TARGET_NAME} Technical Reference Manual* > *LED PWM Controller (LEDC)* [`PDF <{IDF_TARGET_TRM_EN_URL}#ledpwm>`__].
For additional details regarding speed modes, see **{IDF_TARGET_NAME} Technical Reference Manual** > **LED PWM Controller (LEDC)** [`PDF <{IDF_TARGET_TRM_EN_URL}#ledpwm>`__].
.. _ledc-api-supported-range-frequency-duty-resolution:
@ -331,11 +330,11 @@ For registration of a handler to address this interrupt, call :cpp:func:`ledc_is
Supported Range of Frequency and Duty Resolutions
-------------------------------------------------
The LED PWM Controller is designed primarily to drive LEDs. It provides a large flexibility of PWM duty cycle settings. For instance, the PWM frequency of 5 kHz can have the maximum duty resolution of 13 bits. This means that the duty can be set anywhere from 0 to 100% with a resolution of ~0.012% (2 ** 13 = 8192 discrete levels of the LED intensity). Note, however, that these parameters depend on the clock signal clocking the LED PWM Controller timer which in turn clocks the channel (see :ref:`timer configuration<ledc-api-configure-timer>` and the *{IDF_TARGET_NAME} Technical Reference Manual* > *LED PWM Controller (LEDC)* [`PDF <{IDF_TARGET_TRM_EN_URL}#ledpwm>`__]).
The LED PWM Controller is designed primarily to drive LEDs. It provides a large flexibility of PWM duty cycle settings. For instance, the PWM frequency of 5 kHz can have the maximum duty resolution of 13 bits. This means that the duty can be set anywhere from 0 to 100% with a resolution of ~ 0.012% (2 ** 13 = 8192 discrete levels of the LED intensity). Note, however, that these parameters depend on the clock signal clocking the LED PWM Controller timer which in turn clocks the channel (see :ref:`timer configuration <ledc-api-configure-timer>` and the **{IDF_TARGET_NAME} Technical Reference Manual** > **LED PWM Controller (LEDC)** [`PDF <{IDF_TARGET_TRM_EN_URL}#ledpwm>`__]).
The LEDC can be used for generating signals at much higher frequencies that are sufficient enough to clock other devices, e.g., a digital camera module. In this case, the maximum available frequency is 40 MHz with duty resolution of 1 bit. This means that the duty cycle is fixed at 50% and cannot be adjusted.
The LEDC API is designed to report an error when trying to set a frequency and a duty resolution that exceed the range of LEDC's hardware. For example, an attempt to set the frequency to 20 MHz and the duty resolution to 3 bits will result in the following error reported on a serial monitor:
The LEDC API is designed to report an error when trying to set a frequency and a duty resolution that exceed the range of LEDC's hardware. For example, an attempt to set the frequency to 20 MHz and the duty resolution to 3 bits results in the following error reported on a serial monitor:
.. highlight:: none
@ -343,9 +342,9 @@ The LEDC API is designed to report an error when trying to set a frequency and a
E (196) ledc: requested frequency and duty resolution cannot be achieved, try reducing freq_hz or duty_resolution. div_param=128
In such a situation, either the duty resolution or the frequency must be reduced. For example, setting the duty resolution to 2 will resolve this issue and will make it possible to set the duty cycle at 25% steps, i.e., at 25%, 50% or 75%.
In such a situation, either the duty resolution or the frequency must be reduced. For example, setting the duty resolution to 2 resolves this issue and makes it possible to set the duty cycle at 25% steps, i.e., at 25%, 50% or 75%.
The LEDC driver will also capture and report attempts to configure frequency / duty resolution combinations that are below the supported minimum, e.g.:
The LEDC driver also captures and reports attempts to configure frequency/duty resolution combinations that are below the supported minimum, e.g.,:
::

View File

@ -6,7 +6,7 @@ Motor Control Pulse Width Modulator (MCPWM)
The MCPWM peripheral is a versatile PWM generator, which contains various submodules to make it a key element in power electronic applications like motor control, digital power, and so on. Typically, the MCPWM peripheral can be used in the following scenarios:
- Digital motor control, e.g. brushed/brushless DC motor, RC servo motor
- Digital motor control, e.g., brushed/brushless DC motor, RC servo motor
- Switch mode-based digital power conversion
- Power DAC, where the duty cycle is equivalent to a DAC analog value
- Calculate external pulse width, and convert it into other analog values like speed, distance
@ -27,7 +27,7 @@ The main submodules are listed in the following diagram:
- **Dead Time**: This submodule is used to insert extra delay to the existing PWM edges generated in the previous steps.
- **Carrier Modulation**: The carrier submodule can modulate a high-frequency carrier signal into PWM waveforms by the generator and dead time submodules. This capability is mandatory for controlling the power-switching elements.
- **Brake**: MCPWM operator can set how to brake the generators when a particular fault is detected. We can shut down the PWM output immediately or regulate the PWM output cycle by cycle, depending on how critical the fault is.
- **MCPWM Capture**: This is a standalone submodule that can work even without the above MCPWM operators. The capture consists one dedicated timer and several independent channels, with each channel connected to the GPIO. A pulse on the GPIO will trigger the capture timer to store the time-base count value and then notify you by an interrupt. Using this feature, we can measure a pulse width precisely. What's more, the capture timer can also be synchronized by the MCPWM Sync submodule.
- **MCPWM Capture**: This is a standalone submodule that can work even without the above MCPWM operators. The capture consists one dedicated timer and several independent channels, with each channel connected to the GPIO. A pulse on the GPIO triggers the capture timer to store the time-base count value and then notify you by an interrupt. Using this feature, we can measure a pulse width precisely. What is more, the capture timer can also be synchronized by the MCPWM Sync submodule.
Functional Overview
-------------------
@ -46,7 +46,7 @@ Description of the MCPWM functionality is divided into the following sections:
- :ref:`mcpwm-generator-force-actions` - describes how to control the generator output level asynchronously in a forceful way.
- :ref:`mcpwm-synchronization` - describes how to synchronize the MCPWM timers and get a fixed phase difference between the generated PWM signals.
- :ref:`mcpwm-capture` - describes how to use the MCPWM capture module to measure the pulse width of a signal.
- :ref:`mcpwm-power-management` - describes how different source clocks will affect power consumption.
- :ref:`mcpwm-power-management` - describes how different source clocks affects power consumption.
- :ref:`mcpwm-iram-safe` - describes tips on how to make the RMT interrupt work better along with a disabled cache.
- :ref:`mcpwm-thread-safety` - lists which APIs are guaranteed to be thread-safe by the driver.
- :ref:`mcpwm-kconfig-options` - lists the supported Kconfig options that can bring different effects to the driver.
@ -67,7 +67,7 @@ You can allocate a MCPWM timer object by calling :cpp:func:`mcpwm_new_timer` fun
- :cpp:member:`mcpwm_timer_config_t::group_id` specifies the MCPWM group ID. The ID should belong to [0, :c:macro:`SOC_MCPWM_GROUPS` - 1] range. Please note, timers located in different groups are totally independent.
- :cpp:member:`mcpwm_timer_config_t::intr_priority` sets the priority of the interrupt. If it is set to ``0``, the driver will allocate an interrupt with a default priority. Otherwise, the driver will use the given priority.
- :cpp:member:`mcpwm_timer_config_t::clk_src` sets the clock source of the timer.
- :cpp:member:`mcpwm_timer_config_t::resolution_hz` sets the expected resolution of the timer. The driver internally will set a proper divider based on the clock source and the resolution.
- :cpp:member:`mcpwm_timer_config_t::resolution_hz` sets the expected resolution of the timer. The driver internally sets a proper divider based on the clock source and the resolution.
- :cpp:member:`mcpwm_timer_config_t::count_mode` sets the count mode of the timer.
- :cpp:member:`mcpwm_timer_config_t::period_ticks` sets the period of the timer, in ticks (the tick resolution is set in the :cpp:member:`mcpwm_timer_config_t::resolution_hz`).
- :cpp:member:`mcpwm_timer_config_t::update_period_on_empty` sets whether to update the period value when the timer counts to zero.
@ -142,7 +142,7 @@ The :cpp:func:`mcpwm_new_gpio_fault` will return a pointer to the allocated faul
Software fault object can be used to trigger a fault by calling the function :cpp:func:`mcpwm_soft_fault_activate` instead of waiting for a real fault signal on the GPIO. A software fault object can be allocated by calling the :cpp:func:`mcpwm_new_soft_fault` function, with configuration structure :cpp:type:`mcpwm_soft_fault_config_t` as the parameter. Currently, this configuration structure is left for future purposes.
The :cpp:func:`mcpwm_new_soft_fault` function will return a pointer to the allocated fault object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there is no memory left for the fault object, this function will return the :c:macro:`ESP_ERR_NO_MEM` error. Although the software fault and GPIO fault are of different types, the returned fault handle is of the same type.
The :cpp:func:`mcpwm_new_soft_fault` function will return a pointer to the allocated fault object if the allocation succeeds. Otherwise, it will return an error code. Specifically, when there is no memory left for the fault object, this function will return the :c:macro:`ESP_ERR_NO_MEM` error. Although the software fault and GPIO fault are of different types, the returned fault handle is of the same type.
On the contrary, calling the :cpp:func:`mcpwm_del_fault` function will free the allocated fault object, this function works for both software and GPIO fault.
@ -153,7 +153,7 @@ The sync source is what can be used to synchronize the MCPWM timer and MCPWM cap
To allocate a GPIO sync source, you can call the :cpp:func:`mcpwm_new_gpio_sync_src` function, with configuration structure :cpp:type:`mcpwm_gpio_sync_src_config_t` as the parameter. The configuration structure is defined as:
- :cpp:member:`mcpwm_gpio_sync_src_config_t::group_id` sets the MCPWM group ID. The ID should belong to [0, :c:macro:`SOC_MCPWM_GROUPS` - 1] range. Please note, the GPIO sync sources located in different groups are totally independent, i.e. GPIO sync source in group 0 can not be detected by the timers in group 1.
- :cpp:member:`mcpwm_gpio_sync_src_config_t::group_id` sets the MCPWM group ID. The ID should belong to [0, :c:macro:`SOC_MCPWM_GROUPS` - 1] range. Please note, the GPIO sync sources located in different groups are totally independent, i.e., GPIO sync source in group 0 can not be detected by the timers in group 1.
- :cpp:member:`mcpwm_gpio_sync_src_config_t::gpio_num` sets the GPIO number used by the sync source.
- :cpp:member:`mcpwm_gpio_sync_src_config_t::active_neg` sets whether the sync signal is active on negative edges.
- :cpp:member:`mcpwm_gpio_sync_src_config_t::pull_up` and :cpp:member:`mcpwm_gpio_sync_src_config_t::pull_down` set whether to pull up and/or pull down the GPIO internally.
@ -164,7 +164,7 @@ The :cpp:func:`mcpwm_new_gpio_sync_src` will return a pointer to the allocated s
To allocate a timer event sync source, you can call the :cpp:func:`mcpwm_new_timer_sync_src` function, with configuration structure :cpp:type:`mcpwm_timer_sync_src_config_t` as the parameter. The configuration structure is defined as:
- :cpp:member:`mcpwm_timer_sync_src_config_t::timer_event` specifies on what timer event to generate the sync signal.
- :cpp:member:`mcpwm_timer_sync_src_config_t::propagate_input_sync` sets whether to propagate the input sync signal (i.e. the input sync signal will be routed to its sync output).
- :cpp:member:`mcpwm_timer_sync_src_config_t::propagate_input_sync` sets whether to propagate the input sync signal (i.e., the input sync signal will be routed to its sync output).
The :cpp:func:`mcpwm_new_timer_sync_src` will return a pointer to the allocated sync source object if the allocation succeeds. Otherwise, it will return an error code. Specifically, if a sync source has been allocated from the same timer before, this function will return the :c:macro:`ESP_ERR_INVALID_STATE` error.
@ -226,25 +226,25 @@ The MCPWM timer can generate different events at runtime. If you have some funct
The callback functions above are called within the ISR context, so they should **not** attempt to block. For example, you may make sure that only FreeRTOS APIs with the ``ISR`` suffix are called within the function.
The parameter ``user_data`` of the :cpp:func:`mcpwm_timer_register_event_callbacks` function is used to save your own context. It will be passed to each callback function directly.
The parameter ``user_data`` of the :cpp:func:`mcpwm_timer_register_event_callbacks` function is used to save your own context. It is passed to each callback function directly.
This function will lazy the install interrupt service for the MCPWM timer without enabling it. It is only allowed to be called before :cpp:func:`mcpwm_timer_enable`, otherwise the :c:macro:`ESP_ERR_INVALID_STATE` error will be returned. See also `Enable and Disable timer <#enable-and-disable-timer>`__ for more information.
Enable and Disable Timer
~~~~~~~~~~~~~~~~~~~~~~~~
Before doing IO control to the timer, you need to enable the timer first, by calling :cpp:func:`mcpwm_timer_enable`. This function will:
Before doing IO control to the timer, you need to enable the timer first, by calling :cpp:func:`mcpwm_timer_enable`. This function:
* switch the timer state from **init** to **enable**.
* enable the interrupt service if it has been lazy installed by :cpp:func:`mcpwm_timer_register_event_callbacks`.
* acquire a proper power management lock if a specific clock source (e.g. PLL_160M clock) is selected. See also `Power management <#power-management>`__ for more information.
* switches the timer state from **init** to **enable**.
* enables the interrupt service if it has been lazy installed by :cpp:func:`mcpwm_timer_register_event_callbacks`.
* acquire a proper power management lock if a specific clock source (e.g., PLL_160M clock) is selected. See also `Power management <#power-management>`__ for more information.
On the contrary, calling :cpp:func:`mcpwm_timer_disable` will put the timer driver back to the **init** state, disable the interrupt service and release the power management lock.
Start and Stop Timer
~~~~~~~~~~~~~~~~~~~~
The basic IO operation of a timer is to start and stop. Calling :cpp:func:`mcpwm_timer_start_stop` with different :cpp:type:`mcpwm_timer_start_stop_cmd_t` commands can start the timer immediately or stop the timer at a specific event. What's more, you can even start the timer for only one round, which means, the timer will count to peak value or zero, and then stop itself.
The basic IO operation of a timer is to start and stop. Calling :cpp:func:`mcpwm_timer_start_stop` with different :cpp:type:`mcpwm_timer_start_stop_cmd_t` commands can start the timer immediately or stop the timer at a specific event. What is more, you can even start the timer for only one round, which means, the timer will count to peak value or zero, and then stop itself.
Connect Timer with Operator
~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -264,9 +264,9 @@ The MCPWM comparator can inform you when the timer counter equals the compare va
- :cpp:member:`mcpwm_comparator_event_callbacks_t::on_reach` sets the callback function for the comparator when the timer counter equals the compare value.
The callback function will provide event-specific data of type :cpp:type:`mcpwm_compare_event_data_t` to you. The callback function is called within the ISR context, so it should **not** attempt to block. For example, you may make sure that only FreeRTOS APIs with the ``ISR`` suffix are called within the function.
The callback function provides event-specific data of type :cpp:type:`mcpwm_compare_event_data_t` to you. The callback function is called within the ISR context, so it should **not** attempt to block. For example, you may make sure that only FreeRTOS APIs with the ``ISR`` suffix are called within the function.
The parameter ``user_data`` of :cpp:func:`mcpwm_comparator_register_event_callbacks` function is used to save your own context. It will be passed to the callback function directly.
The parameter ``user_data`` of :cpp:func:`mcpwm_comparator_register_event_callbacks` function is used to save your own context. It is passed to the callback function directly.
This function will lazy the installation of interrupt service for the MCPWM comparator, whereas the service can only be removed in :cpp:type:`mcpwm_del_comparator`.
@ -277,7 +277,7 @@ You can set the compare value for the MCPWM comparator at runtime by calling :cp
- A new compare value might not take effect immediately. The update time for the compare value is set by :cpp:member:`mcpwm_comparator_config_t::update_cmp_on_tez` or :cpp:member:`mcpwm_comparator_config_t::update_cmp_on_tep` or :cpp:member:`mcpwm_comparator_config_t::update_cmp_on_sync`.
- Make sure the operator has connected to one MCPWM timer already by :cpp:func:`mcpwm_operator_connect_timer`. Otherwise, it will return the error code :c:macro:`ESP_ERR_INVALID_STATE`.
- The compare value shouldn't exceed the timer's count peak, otherwise, the compare event will never get triggered.
- The compare value should not exceed the timer's count peak, otherwise, the compare event will never get triggered.
.. _mcpwm-generator-actions-on-events:
@ -294,7 +294,7 @@ One generator can set multiple actions on different timer events, by calling :cp
- :cpp:member:`mcpwm_gen_timer_event_action_t::event` specifies the timer event. The supported timer events are listed in :cpp:type:`mcpwm_timer_event_t`.
- :cpp:member:`mcpwm_gen_timer_event_action_t::action` specifies the generator action to be taken. The supported actions are listed in :cpp:type:`mcpwm_generator_action_t`.
There's a helper macro :c:macro:`MCPWM_GEN_TIMER_EVENT_ACTION` to simplify the construction of a timer event action entry.
There is a helper macro :c:macro:`MCPWM_GEN_TIMER_EVENT_ACTION` to simplify the construction of a timer event action entry.
Please note, the argument list of :cpp:func:`mcpwm_generator_set_actions_on_timer_event` **must** be terminated by :c:macro:`MCPWM_GEN_TIMER_EVENT_ACTION_END`.
@ -309,7 +309,7 @@ One generator can set multiple actions on different compare events, by calling :
- :cpp:member:`mcpwm_gen_compare_event_action_t::comparator` specifies the comparator handle. See `MCPWM Comparators <#mcpwm-comparators>`__ for how to allocate a comparator.
- :cpp:member:`mcpwm_gen_compare_event_action_t::action` specifies the generator action to be taken. The supported actions are listed in :cpp:type:`mcpwm_generator_action_t`.
There's a helper macro :c:macro:`MCPWM_GEN_COMPARE_EVENT_ACTION` to simplify the construction of a compare event action entry.
There is a helper macro :c:macro:`MCPWM_GEN_COMPARE_EVENT_ACTION` to simplify the construction of a compare event action entry.
Please note, the argument list of :cpp:func:`mcpwm_generator_set_actions_on_compare_event` **must** be terminated by :c:macro:`MCPWM_GEN_COMPARE_EVENT_ACTION_END`.
@ -328,7 +328,7 @@ When no free trigger slot is left in the operator to which the generator belongs
The trigger only support GPOI fault. when the input is not a GPIO fault, this function will return the :c:macro:`ESP_ERR_NOT_SUPPORTED` error.
There's a helper macro :c:macro:`MCPWM_GEN_FAULT_EVENT_ACTION` to simplify the construction of a trigger event action entry.
There is a helper macro :c:macro:`MCPWM_GEN_FAULT_EVENT_ACTION` to simplify the construction of a trigger event action entry.
Please note, fault event does not have variadic function like :cpp:func:`mcpwm_generator_set_actions_on_fault_event`.
@ -345,7 +345,7 @@ When no free trigger slot is left in the operator to which the generator belongs
The trigger only support one sync action, regardless of the kinds. When set sync actions more than once, this function will return the :c:macro:`ESP_ERR_INVALID_STATE` error.
There's a helper macro :c:macro:`MCPWM_GEN_SYNC_EVENT_ACTION` to simplify the construction of a trigger event action entry.
There is a helper macro :c:macro:`MCPWM_GEN_SYNC_EVENT_ACTION` to simplify the construction of a trigger event action entry.
Please note, sync event does not have variadic function like :cpp:func:`mcpwm_generator_set_actions_on_sync_event`.
@ -481,9 +481,9 @@ Dual Edge Symmetric Waveform - Complementary
Dead Time
^^^^^^^^^
In power electronics, the rectifier and inverter are commonly used. This requires the use of a rectifier bridge and an inverter bridge. Each bridge arm has two power electronic devices, such as MOSFET, IGBT, etc. The two MOSFETs on the same arm can't conduct at the same time, otherwise there will be a short circuit. The fact is that, although the PWM wave shows it is turning off the switch, the MOSFET still needs a small time window to make that happen. This requires an extra delay to be added to the existing PWM wave generated by setting `Generator Actions on Events <#generator-actions-on-events>`__.
In power electronics, the rectifier and inverter are commonly used. This requires the use of a rectifier bridge and an inverter bridge. Each bridge arm has two power electronic devices, such as MOSFET, IGBT, etc. The two MOSFETs on the same arm can not conduct at the same time, otherwise there will be a short circuit. The fact is that, although the PWM wave shows it is turning off the switch, the MOSFET still needs a small time window to make that happen. This requires an extra delay to be added to the existing PWM wave generated by setting `Generator Actions on Events <#generator-actions-on-events>`__.
The dead time driver works like a *decorator*. This is also reflected in the function parameters of :cpp:func:`mcpwm_generator_set_dead_time`, where it takes the primary generator handle (``in_generator``), and returns a new generator (``out_generator``) after applying the dead time. Please note, if the ``out_generator`` and ``in_generator`` are the same, it means we are adding the time delay to the PWM waveform in an "in-place" fashion. In turn, if the ``out_generator`` and ``in_generator`` are different, it means we're deriving a new PWM waveform from the existing ``in_generator``.
The dead time driver works like a **decorator**. This is also reflected in the function parameters of :cpp:func:`mcpwm_generator_set_dead_time`, where it takes the primary generator handle (``in_generator``), and returns a new generator (``out_generator``) after applying the dead time. Please note, if the ``out_generator`` and ``in_generator`` are the same, it means we are adding the time delay to the PWM waveform in an "in-place" fashion. In turn, if the ``out_generator`` and ``in_generator`` are different, it means we are deriving a new PWM waveform from the existing ``in_generator``.
Dead time specific configuration is listed in the :cpp:type:`mcpwm_dead_time_config_t` structure:
@ -492,7 +492,7 @@ Dead time specific configuration is listed in the :cpp:type:`mcpwm_dead_time_con
.. warning::
Due to the hardware limitation, one delay module (either `posedge delay` or `negedge delay`) can't be applied to multiple MCPWM generators at the same time. e.g. the following configuration is **invalid**:
Due to the hardware limitation, one delay module (either ``posedge delay`` or ``negedge delay``) can not be applied to multiple MCPWM generators at the same time. e.g., the following configuration is **invalid**:
.. code:: c
@ -501,10 +501,10 @@ Dead time specific configuration is listed in the :cpp:type:`mcpwm_dead_time_con
};
// Set posedge delay to generator A
mcpwm_generator_set_dead_time(mcpwm_gen_a, mcpwm_gen_a, &dt_config);
// NOTE: This is invalid, you can't apply the posedge delay to another generator
// NOTE: This is invalid, you can not apply the posedge delay to another generator
mcpwm_generator_set_dead_time(mcpwm_gen_b, mcpwm_gen_b, &dt_config);
However, you can apply `posedge delay` to generator A and `negedge delay` to generator B. You can also set both `posedge delay` and `negedge delay` for generator A, while letting generator B bypass the dead time module.
However, you can apply ``posedge delay`` to generator A and ``negedge delay`` to generator B. You can also set both ``posedge delay`` and ``negedge delay`` for generator A, while letting generator B bypass the dead time module.
.. note::
@ -516,7 +516,7 @@ Dead time specific configuration is listed in the :cpp:type:`mcpwm_dead_time_con
Dead Time Configurations for Classical PWM Waveforms
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This section will demonstrate the classical PWM waveforms that can be generated by the dead time submodule. The code snippet that is used to generate the waveforms is also provided below the diagram.
This section demonstrates the classical PWM waveforms that can be generated by the dead time submodule. The code snippet that is used to generate the waveforms is also provided below the diagram.
Active High Complementary
~~~~~~~~~~~~~~~~~~~~~~~~~
@ -734,14 +734,14 @@ Rising and Falling Delay on PWMB and Bypass Dead Time for PWMA
Carrier Modulation
^^^^^^^^^^^^^^^^^^
The MCPWM operator has a carrier submodule that can be used if galvanic isolation from the motor driver is required (e.g. isolated digital power application) by passing the PWM output signals through transformers. Any of the PWM output signals may be at 100% duty and not changing whenever a motor is required to run steadily at the full load. Coupling with non-alternating signals with a transformer is problematic, so the signals are modulated by the carrier submodule to create an AC waveform, to make the coupling possible.
The MCPWM operator has a carrier submodule that can be used if galvanic isolation from the motor driver is required (e.g., isolated digital power application) by passing the PWM output signals through transformers. Any of the PWM output signals may be at 100% duty and not changing whenever a motor is required to run steadily at the full load. Coupling with non-alternating signals with a transformer is problematic, so the signals are modulated by the carrier submodule to create an AC waveform, to make the coupling possible.
To configure the carrier submodule, you can call :cpp:func:`mcpwm_operator_apply_carrier`, and provide configuration structure :cpp:type:`mcpwm_carrier_config_t`:
- :cpp:member:`mcpwm_carrier_config_t::clk_src` sets the clock source of the carrier.
- :cpp:member:`mcpwm_carrier_config_t::frequency_hz` indicates carrier frequency in Hz.
- :cpp:member:`mcpwm_carrier_config_t::duty_cycle` indicates the duty cycle of the carrier. Note that, the supported choices of the duty cycle are discrete, the driver will search for the nearest one based on your configuration.
- :cpp:member:`mcpwm_carrier_config_t::first_pulse_duration_us` indicates the duration of the first pulse in microseconds. The resolution of the first pulse duration is determined by the carrier frequency you set in the :cpp:member:`mcpwm_carrier_config_t::frequency_hz`. The first pulse duration can't be zero, and it has to be at least one period of the carrier. A longer pulse width can help conduct the inductance quicker.
- :cpp:member:`mcpwm_carrier_config_t::duty_cycle` indicates the duty cycle of the carrier. Note that, the supported choices of the duty cycle are discrete, the driver searches for the nearest one based on your configuration.
- :cpp:member:`mcpwm_carrier_config_t::first_pulse_duration_us` indicates the duration of the first pulse in microseconds. The resolution of the first pulse duration is determined by the carrier frequency you set in the :cpp:member:`mcpwm_carrier_config_t::frequency_hz`. The first pulse duration can not be zero, and it has to be at least one period of the carrier. A longer pulse width can help conduct the inductance quicker.
- :cpp:member:`mcpwm_carrier_config_t::invert_before_modulate` and :cpp:member:`mcpwm_carrier_config_t::invert_after_modulate` set whether to invert the carrier output before and after modulation.
Specifically, the carrier submodule can be disabled by calling :cpp:func:`mcpwm_operator_apply_carrier` with a ``NULL`` configuration.
@ -762,7 +762,7 @@ Set Operator Brake Mode on Fault
The way that MCPWM operator reacts to the fault is called **Brake**. The MCPWM operator can be configured to perform different brake modes for each fault object by calling :cpp:func:`mcpwm_operator_set_brake_on_fault`. Specific brake configuration is passed as a structure :cpp:type:`mcpwm_brake_config_t`:
- :cpp:member:`mcpwm_brake_config_t::fault` sets which fault the operator should react to.
- :cpp:member:`mcpwm_brake_config_t::brake_mode` sets the brake mode that should be used for the fault. The supported brake modes are listed in the :cpp:type:`mcpwm_operator_brake_mode_t`. For :cpp:enumerator:`MCPWM_OPER_BRAKE_MODE_CBC` mode, the operator will recover itself automatically as long as the fault disappears. You can specify the recovery time in :cpp:member:`mcpwm_brake_config_t::cbc_recover_on_tez` and :cpp:member:`mcpwm_brake_config_t::cbc_recover_on_tep`. For :cpp:enumerator:`MCPWM_OPER_BRAKE_MODE_OST` mode, the operator can't recover even though the fault disappears. You have to call :cpp:func:`mcpwm_operator_recover_from_fault` to manually recover it.
- :cpp:member:`mcpwm_brake_config_t::brake_mode` sets the brake mode that should be used for the fault. The supported brake modes are listed in the :cpp:type:`mcpwm_operator_brake_mode_t`. For :cpp:enumerator:`MCPWM_OPER_BRAKE_MODE_CBC` mode, the operator recovers itself automatically as long as the fault disappears. You can specify the recovery time in :cpp:member:`mcpwm_brake_config_t::cbc_recover_on_tez` and :cpp:member:`mcpwm_brake_config_t::cbc_recover_on_tep`. For :cpp:enumerator:`MCPWM_OPER_BRAKE_MODE_OST` mode, the operator can not recover even though the fault disappears. You have to call :cpp:func:`mcpwm_operator_recover_from_fault` to manually recover it.
Set Generator Action on Brake Event
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -773,7 +773,7 @@ One generator can set multiple actions on different brake events, by calling :cp
- :cpp:member:`mcpwm_gen_brake_event_action_t::brake_mode` specifies the brake mode. The supported brake modes are listed in the :cpp:type:`mcpwm_operator_brake_mode_t`.
- :cpp:member:`mcpwm_gen_brake_event_action_t::action` specifies the generator action to be taken. The supported actions are listed in :cpp:type:`mcpwm_generator_action_t`.
There's a helper macro :c:macro:`MCPWM_GEN_BRAKE_EVENT_ACTION` to simplify the construction of a brake event action entry.
There is a helper macro :c:macro:`MCPWM_GEN_BRAKE_EVENT_ACTION` to simplify the construction of a brake event action entry.
Please note, the argument list of :cpp:func:`mcpwm_generator_set_actions_on_brake_event` **must** be terminated by :c:macro:`MCPWM_GEN_BRAKE_EVENT_ACTION_END`.
@ -789,7 +789,7 @@ The MCPWM fault detector can inform you when it detects a valid fault or a fault
The callback function is called within the ISR context, so it should **not** attempt to block. For example, you may make sure that only FreeRTOS APIs with the ``ISR`` suffix are called within the function.
The parameter ``user_data`` of :cpp:func:`mcpwm_fault_register_event_callbacks` function is used to save your own context. It will be passed to the callback function directly.
The parameter ``user_data`` of :cpp:func:`mcpwm_fault_register_event_callbacks` function is used to save your own context. It is passed to the callback function directly.
This function will lazy the install interrupt service for the MCPWM fault, whereas the service can only be removed in :cpp:type:`mcpwm_del_fault`.
@ -798,8 +798,8 @@ Register Brake Event Callbacks
The MCPWM operator can inform you when it is going to take a brake action. If you have some function that should be called when this event happens, you should hook your function to the interrupt service routine by calling :cpp:func:`mcpwm_operator_register_event_callbacks`. The callback function prototype is declared in :cpp:type:`mcpwm_brake_event_cb_t`. All supported event callbacks are listed in the :cpp:type:`mcpwm_operator_event_callbacks_t`:
- :cpp:member:`mcpwm_operator_event_callbacks_t::on_brake_cbc` sets the callback function that will be called when the operator is going to take a *CBC* action.
- :cpp:member:`mcpwm_operator_event_callbacks_t::on_brake_ost` sets the callback function that will be called when the operator is going to take an *OST* action.
- :cpp:member:`mcpwm_operator_event_callbacks_t::on_brake_cbc` sets the callback function that will be called when the operator is going to take a **CBC** action.
- :cpp:member:`mcpwm_operator_event_callbacks_t::on_brake_ost` sets the callback function that will be called when the operator is going to take an **OST** action.
The callback function is called within the ISR context, so it should **not** attempt to block. For example, you may make sure that only FreeRTOS APIs with the ``ISR`` suffix are called within the function.
@ -813,7 +813,7 @@ This function will lazy the install interrupt service for the MCPWM operator, wh
Generator Force Actions
^^^^^^^^^^^^^^^^^^^^^^^
Software can override generator output level at runtime, by calling :cpp:func:`mcpwm_generator_set_force_level`. The software force level always has a higher priority than other event actions set in e.g. :cpp:func:`mcpwm_generator_set_actions_on_timer_event`.
Software can override generator output level at runtime, by calling :cpp:func:`mcpwm_generator_set_force_level`. The software force level always has a higher priority than other event actions set in e.g., :cpp:func:`mcpwm_generator_set_actions_on_timer_event`.
- Set the ``level`` to -1 means to disable the force action, and the generator's output level will be controlled by the event actions again.
- Set the ``hold_on`` to true, and the force output level will keep alive until it is removed by assigning ``level`` to -1.
@ -890,15 +890,15 @@ The capture timer is usually connected to several capture channels. Please refer
Register Capture Event Callbacks
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The MCPWM capture channel can inform you when there's a valid edge detected on the signal. You have to register a callback function to get the timer count value of the captured moment, by calling :cpp:func:`mcpwm_capture_channel_register_event_callbacks`. The callback function prototype is declared in :cpp:type:`mcpwm_capture_event_cb_t`. All supported capture callbacks are listed in the :cpp:type:`mcpwm_capture_event_callbacks_t`:
The MCPWM capture channel can inform you when there is a valid edge detected on the signal. You have to register a callback function to get the timer count value of the captured moment, by calling :cpp:func:`mcpwm_capture_channel_register_event_callbacks`. The callback function prototype is declared in :cpp:type:`mcpwm_capture_event_cb_t`. All supported capture callbacks are listed in the :cpp:type:`mcpwm_capture_event_callbacks_t`:
- :cpp:member:`mcpwm_capture_event_callbacks_t::on_cap` sets the callback function for the capture channel when a valid edge is detected.
The callback function will provide event-specific data of type :cpp:type:`mcpwm_capture_event_data_t`, so that you can get the edge of the capture signal in :cpp:member:`mcpwm_capture_event_data_t::cap_edge` and the count value of that moment in :cpp:member:`mcpwm_capture_event_data_t::cap_value`. To convert the capture count into a timestamp, you need to know the resolution of the capture timer by calling :cpp:func:`mcpwm_capture_timer_get_resolution`.
The callback function provides event-specific data of type :cpp:type:`mcpwm_capture_event_data_t`, so that you can get the edge of the capture signal in :cpp:member:`mcpwm_capture_event_data_t::cap_edge` and the count value of that moment in :cpp:member:`mcpwm_capture_event_data_t::cap_value`. To convert the capture count into a timestamp, you need to know the resolution of the capture timer by calling :cpp:func:`mcpwm_capture_timer_get_resolution`.
The callback function is called within the ISR context, so it should **not** attempt to block. For example, you may make sure that only FreeRTOS APIs with the ``ISR`` suffix are called within the function.
The parameter ``user_data`` of :cpp:func:`mcpwm_capture_channel_register_event_callbacks` function is used to save your context. It will be passed to the callback function directly.
The parameter ``user_data`` of :cpp:func:`mcpwm_capture_channel_register_event_callbacks` function is used to save your context. It is passed to the callback function directly.
This function will lazy install interrupt service for the MCPWM capture channel, whereas the service can only be removed in :cpp:type:`mcpwm_del_capture_channel`.
@ -910,10 +910,10 @@ The capture channel is not enabled after allocation by :cpp:func:`mcpwm_new_capt
Enable and Disable Capture Timer
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before doing IO control to the capture timer, you need to enable the timer first, by calling :cpp:func:`mcpwm_capture_timer_enable`. Internally, this function will:
Before doing IO control to the capture timer, you need to enable the timer first, by calling :cpp:func:`mcpwm_capture_timer_enable`. Internally, this function:
* switch the capture timer state from **init** to **enable**.
* acquire a proper power management lock if a specific clock source (e.g. APB clock) is selected. See also `Power management <#power-management>`__ for more information.
* switches the capture timer state from **init** to **enable**.
* acquires a proper power management lock if a specific clock source (e.g., APB clock) is selected. See also `Power management <#power-management>`__ for more information.
On the contrary, calling :cpp:func:`mcpwm_capture_timer_disable` will put the timer driver back to **init** state, and release the power management lock.
@ -925,7 +925,7 @@ The basic IO operation of a capture timer is to start and stop. Calling :cpp:fun
Trigger a Software Capture Event
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes, the software also wants to trigger a "fake" capture event. The :cpp:func:`mcpwm_capture_channel_trigger_soft_catch` is provided for that purpose. Please note that, even though it's a "fake" capture event, it can still cause an interrupt, thus your capture event callback function will get invoked as well.
Sometimes, the software also wants to trigger a "fake" capture event. The :cpp:func:`mcpwm_capture_channel_trigger_soft_catch` is provided for that purpose. Please note that, even though it is a "fake" capture event, it can still cause an interrupt, thus your capture event callback function gets invoked as well.
.. _mcpwm-power-management:
@ -935,9 +935,9 @@ Power Management
When power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust the PLL and APB frequency before going into Light-sleep, thus potentially changing the period of an MCPWM timers' counting step and leading to inaccurate time-keeping.
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever the driver creates an MCPWM timer instance that has selected :cpp:enumerator:`MCPWM_TIMER_CLK_SRC_PLL160M` as its clock source, the driver will guarantee that the power management lock is acquired when enabling the timer by :cpp:func:`mcpwm_timer_enable`. On the contrary, the driver releases the lock when :cpp:func:`mcpwm_timer_disable` is called for that timer.
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever the driver creates an MCPWM timer instance that has selected :cpp:enumerator:`MCPWM_TIMER_CLK_SRC_PLL160M` as its clock source, the driver guarantees that the power management lock is acquired when enabling the timer by :cpp:func:`mcpwm_timer_enable`. On the contrary, the driver releases the lock when :cpp:func:`mcpwm_timer_disable` is called for that timer.
Likewise, whenever the driver creates an MCPWM capture timer instance that has selected :cpp:enumerator:`MCPWM_CAPTURE_CLK_SRC_APB` as its clock source, the driver will guarantee that the power management lock is acquired when enabling the timer by :cpp:func:`mcpwm_capture_timer_enable`. And will release the lock in :cpp:func:`mcpwm_capture_timer_disable`.
Likewise, whenever the driver creates an MCPWM capture timer instance that has selected :cpp:enumerator:`MCPWM_CAPTURE_CLK_SRC_APB` as its clock source, the driver guarantees that the power management lock is acquired when enabling the timer by :cpp:func:`mcpwm_capture_timer_enable`. And releases the lock in :cpp:func:`mcpwm_capture_timer_disable`.
.. _mcpwm-iram-safe:
@ -947,13 +947,13 @@ IRAM Safe
By default, the MCPWM interrupt will be deferred when the Cache is disabled for reasons like writing/erasing Flash. Thus the event callback functions will not get executed in time, which is not expected in a real-time application.
There's a Kconfig option :ref:`CONFIG_MCPWM_ISR_IRAM_SAFE` that will:
There is a Kconfig option :ref:`CONFIG_MCPWM_ISR_IRAM_SAFE` that:
* enable the interrupt to be serviced even when the cache is disabled
* place all functions used by the ISR into IRAM [2]_
* place the driver object into DRAM (in case it's mapped to PSRAM by accident)
* enables the interrupt to be serviced even when the cache is disabled
* places all functions used by the ISR into IRAM [2]_
* places the driver object into DRAM (in case it is mapped to PSRAM by accident)
This will allow the interrupt to run while the cache is disabled but will come at the cost of increased IRAM consumption.
This allows the interrupt to run while the cache is disabled but comes at the cost of increased IRAM consumption.
There is another Kconfig option :ref:`CONFIG_MCPWM_CTRL_FUNC_IN_IRAM` that can put commonly used IO control functions into IRAM as well. So, these functions can also be executable when the cache is disabled. The IO control function is as follows:
@ -1008,7 +1008,7 @@ API Reference
.. [1]
Different ESP chip series might have a different number of MCPWM resources (e.g. groups, timers, comparators, operators, generators, triggers and so on). Please refer to the [`TRM <{IDF_TARGET_TRM_EN_URL}#mcpwm>`__] for details. The driver won't forbid you from applying for more MCPWM resources, but it will return an error when there are no hardware resources available. Please always check the return value when doing :ref:`mcpwm-resource-allocation-and-initialization`.
Different ESP chip series might have a different number of MCPWM resources (e.g., groups, timers, comparators, operators, generators, triggers and so on). Please refer to the [`TRM <{IDF_TARGET_TRM_EN_URL}#mcpwm>`__] for details. The driver does not forbid you from applying for more MCPWM resources, but it returns an error when there are no hardware resources available. Please always check the return value when doing :ref:`mcpwm-resource-allocation-and-initialization`.
.. [2]
The callback function and the sub-functions invoked by itself should also be placed in IRAM. You need to take care of this by yourself.

View File

@ -7,6 +7,7 @@ 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]_
.. warning::
At the moment, the Parallel IO driver only supports TX mode. The RX feature is still working in progress.
@ -24,4 +25,4 @@ API Reference
.. 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 will not forbid you from applying for more driver objects, but it will return 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`).
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`).

View File

@ -12,8 +12,8 @@ Besides that, PCNT unit is equipped with a separate glitch filter, which is help
Typically, a PCNT module can be used in scenarios like:
- Calculate periodic signal's frequency by counting the pulse numbers within a time slice
- Decode quadrature signals into speed and direction
- Calculate periodic signal's frequency by counting the pulse numbers within a time slice
- Decode quadrature signals into speed and direction
Functional Overview
-------------------
@ -45,7 +45,7 @@ The PCNT unit and channel are represented by :cpp:type:`pcnt_unit_handle_t` and
Install PCNT Unit
~~~~~~~~~~~~~~~~~
To install a PCNT unit, there's a configuration structure that needs to be given in advance: :cpp:type:`pcnt_unit_config_t`:
To install a PCNT unit, there is a configuration structure that needs to be given in advance: :cpp:type:`pcnt_unit_config_t`:
- :cpp:member:`pcnt_unit_config_t::low_limit` and :cpp:member:`pcnt_unit_config_t::high_limit` specify the range for the internal hardware counter. The counter will reset to zero automatically when it crosses either the high or low limit.
- :cpp:member:`pcnt_unit_config_t::accum_count` sets whether to create an internal accumulator for the counter. This is helpful when you want to extend the counter's width, which by default is 16bit at most, defined in the hardware. See also :ref:`pcnt-compensate-overflow-loss` for how to use this feature to compensate the overflow loss.
@ -57,7 +57,7 @@ To install a PCNT unit, there's a configuration structure that needs to be given
Unit allocation and initialization is done by calling a function :cpp:func:`pcnt_new_unit` with :cpp:type:`pcnt_unit_config_t` as an input parameter. The function will return a PCNT unit handle only when it runs correctly. Specifically, when there are no more free PCNT units in the pool (i.e. unit resources have been used up), then this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. The total number of available PCNT units is recorded by :c:macro:`SOC_PCNT_UNITS_PER_GROUP` for reference.
If a previously created PCNT unit is no longer needed, it's recommended to recycle the resource by calling :cpp:func:`pcnt_del_unit`. Which in return allows the underlying unit hardware to be used for other purposes. Before deleting a PCNT unit, one should ensure the following prerequisites:
If a previously created PCNT unit is no longer needed, it is recommended to recycle the resource by calling :cpp:func:`pcnt_del_unit`. Which in return allows the underlying unit hardware to be used for other purposes. Before deleting a PCNT unit, one should ensure the following prerequisites:
- The unit is in the init state, in other words, the unit is either disabled by :cpp:func:`pcnt_unit_disable` or not enabled yet.
- The attached PCNT channels are all removed by :cpp:func:`pcnt_del_channel`.
@ -79,14 +79,14 @@ Install PCNT Channel
To install a PCNT channel, you must initialize a :cpp:type:`pcnt_chan_config_t` structure in advance, and then call :cpp:func:`pcnt_new_channel`. The configuration fields of the :cpp:type:`pcnt_chan_config_t` structure are described below:
- :cpp:member:`pcnt_chan_config_t::edge_gpio_num` and :cpp:member:`pcnt_chan_config_t::level_gpio_num` specify the GPIO numbers used by **edge** type signal and **level** type signal. Please note, either of them can be assigned to `-1` if it's not actually used, and thus it will become a **virtual IO**. For some simple pulse counting applications where one of the level/edge signals is fixed (i.e., never changes), you can reclaim a GPIO by setting the signal as a virtual IO on channel allocation. Setting the level/edge signal as a virtual IO will cause that signal to be internally routed to a fixed High/Low logic level, thus allowing you to save a GPIO for other purposes.
- :cpp:member:`pcnt_chan_config_t::virt_edge_io_level` and :cpp:member:`pcnt_chan_config_t::virt_level_io_level` specify the virtual IO level for **edge** and **level** input signal, to ensure a deterministic state for such control signal. Please note, they are only valid when either :cpp:member:`pcnt_chan_config_t::edge_gpio_num` or :cpp:member:`pcnt_chan_config_t::level_gpio_num` is assigned to `-1`.
- :cpp:member:`pcnt_chan_config_t::invert_edge_input` and :cpp:member:`pcnt_chan_config_t::invert_level_input` are used to decide whether to invert the input signals before they going into PCNT hardware. The invert is done by GPIO matrix instead of PCNT hardware.
- :cpp:member:`pcnt_chan_config_t::io_loop_back` is for debug only, which enables both the GPIO's input and output paths. This can help to simulate the pulse signals by function :cpp:func:`gpio_set_level` on the same GPIO.
- :cpp:member:`pcnt_chan_config_t::edge_gpio_num` and :cpp:member:`pcnt_chan_config_t::level_gpio_num` specify the GPIO numbers used by **edge** type signal and **level** type signal. Please note, either of them can be assigned to ``-1`` if it is not actually used, and thus it will become a **virtual IO**. For some simple pulse counting applications where one of the level/edge signals is fixed (i.e., never changes), you can reclaim a GPIO by setting the signal as a virtual IO on channel allocation. Setting the level/edge signal as a virtual IO causes that signal to be internally routed to a fixed High/Low logic level, thus allowing you to save a GPIO for other purposes.
- :cpp:member:`pcnt_chan_config_t::virt_edge_io_level` and :cpp:member:`pcnt_chan_config_t::virt_level_io_level` specify the virtual IO level for **edge** and **level** input signal, to ensure a deterministic state for such control signal. Please note, they are only valid when either :cpp:member:`pcnt_chan_config_t::edge_gpio_num` or :cpp:member:`pcnt_chan_config_t::level_gpio_num` is assigned to ``-1``.
- :cpp:member:`pcnt_chan_config_t::invert_edge_input` and :cpp:member:`pcnt_chan_config_t::invert_level_input` are used to decide whether to invert the input signals before they going into PCNT hardware. The invert is done by GPIO matrix instead of PCNT hardware.
- :cpp:member:`pcnt_chan_config_t::io_loop_back` is for debug only, which enables both the GPIO's input and output paths. This can help to simulate the pulse signals by function :cpp:func:`gpio_set_level` on the same GPIO.
Channel allocating and initialization is done by calling a function :cpp:func:`pcnt_new_channel` with the above :cpp:type:`pcnt_chan_config_t` as an input parameter plus a PCNT unit handle returned from :cpp:func:`pcnt_new_unit`. This function will return a PCNT channel handle if it runs correctly. Specifically, when there are no more free PCNT channel within the unit (i.e. channel resources have been used up), then this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. The total number of available PCNT channels within the unit is recorded by :c:macro:`SOC_PCNT_CHANNELS_PER_UNIT` for reference. Note that, when install a PCNT channel for a specific unit, one should ensure the unit is in the init state, otherwise this function will return :c:macro:`ESP_ERR_INVALID_STATE` error.
Channel allocating and initialization is done by calling a function :cpp:func:`pcnt_new_channel` with the above :cpp:type:`pcnt_chan_config_t` as an input parameter plus a PCNT unit handle returned from :cpp:func:`pcnt_new_unit`. This function will return a PCNT channel handle if it runs correctly. Specifically, when there are no more free PCNT channel within the unit (i.e., channel resources have been used up), then this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. The total number of available PCNT channels within the unit is recorded by :c:macro:`SOC_PCNT_CHANNELS_PER_UNIT` for reference. Note that, when install a PCNT channel for a specific unit, one should ensure the unit is in the init state, otherwise this function will return :c:macro:`ESP_ERR_INVALID_STATE` error.
If a previously created PCNT channel is no longer needed, it's recommended to recycle the resources by calling :cpp:func:`pcnt_del_channel`. Which in return allows the underlying channel hardware to be used for other purposes.
If a previously created PCNT channel is no longer needed, it is recommended to recycle the resources by calling :cpp:func:`pcnt_del_channel`. Which in return allows the underlying channel hardware to be used for other purposes.
.. code:: c
@ -107,8 +107,8 @@ Set Up Channel Actions
The PCNT will increase/decrease/hold its internal count value when the input pulse signal toggles. You can set different actions for edge signal and/or level signal.
- :cpp:func:`pcnt_channel_set_edge_action` function is to set specific actions for rising and falling edge of the signal attached to the :cpp:member:`pcnt_chan_config_t::edge_gpio_num`. Supported actions are listed in :cpp:type:`pcnt_channel_edge_action_t`.
- :cpp:func:`pcnt_channel_set_level_action` function is to set specific actions for high and low level of the signal attached to the :cpp:member:`pcnt_chan_config_t::level_gpio_num`. Supported actions are listed in :cpp:type:`pcnt_channel_level_action_t`. This function is not mandatory if the :cpp:member:`pcnt_chan_config_t::level_gpio_num` is set to `-1` when allocating PCNT channel by :cpp:func:`pcnt_new_channel`.
- :cpp:func:`pcnt_channel_set_edge_action` function is to set specific actions for rising and falling edge of the signal attached to the :cpp:member:`pcnt_chan_config_t::edge_gpio_num`. Supported actions are listed in :cpp:type:`pcnt_channel_edge_action_t`.
- :cpp:func:`pcnt_channel_set_level_action` function is to set specific actions for high and low level of the signal attached to the :cpp:member:`pcnt_chan_config_t::level_gpio_num`. Supported actions are listed in :cpp:type:`pcnt_channel_level_action_t`. This function is not mandatory if the :cpp:member:`pcnt_chan_config_t::level_gpio_num` is set to ``-1`` when allocating PCNT channel by :cpp:func:`pcnt_new_channel`.
.. code:: c
@ -122,9 +122,9 @@ The PCNT will increase/decrease/hold its internal count value when the input pul
Watch Points
^^^^^^^^^^^^
Each PCNT unit can be configured to watch several different values that you're interested in. The value to be watched is also called **Watch Point**. The watch point itself can't exceed the range set in :cpp:type:`pcnt_unit_config_t` by :cpp:member:`pcnt_unit_config_t::low_limit` and :cpp:member:`pcnt_unit_config_t::high_limit`. When the counter reaches either watch point, a watch event will be triggered and notify you by interrupt if any watch event callback has ever registered in :cpp:func:`pcnt_unit_register_event_callbacks`. See :ref:`pcnt-register-event-callbacks` for how to register event callbacks.
Each PCNT unit can be configured to watch several different values that you are interested in. The value to be watched is also called **Watch Point**. The watch point itself can not exceed the range set in :cpp:type:`pcnt_unit_config_t` by :cpp:member:`pcnt_unit_config_t::low_limit` and :cpp:member:`pcnt_unit_config_t::high_limit`. When the counter reaches either watch point, a watch event will be triggered and notify you by interrupt if any watch event callback has ever registered in :cpp:func:`pcnt_unit_register_event_callbacks`. See :ref:`pcnt-register-event-callbacks` for how to register event callbacks.
The watch point can be added and removed by :cpp:func:`pcnt_unit_add_watch_point` and :cpp:func:`pcnt_unit_remove_watch_point`. The commonly used watch points are: **zero cross**, **maximum / minimum count** and other threshold values. The number of available watch point is limited, :cpp:func:`pcnt_unit_add_watch_point` will return error :c:macro:`ESP_ERR_NOT_FOUND` if it can't find any free hardware resource to save the watch point. You can't add the same watch point for multiple times, otherwise it will return error :c:macro:`ESP_ERR_INVALID_STATE`.
The watch point can be added and removed by :cpp:func:`pcnt_unit_add_watch_point` and :cpp:func:`pcnt_unit_remove_watch_point`. The commonly-used watch points are: **zero cross**, **maximum/minimum count** and other threshold values. The number of available watch point is limited, :cpp:func:`pcnt_unit_add_watch_point` will return error :c:macro:`ESP_ERR_NOT_FOUND` if it can not find any free hardware resource to save the watch point. You can not add the same watch point for multiple times, otherwise it will return error :c:macro:`ESP_ERR_INVALID_STATE`.
It is recommended to remove the unused watch point by :cpp:func:`pcnt_unit_remove_watch_point` to recycle the watch point resources.
@ -148,16 +148,16 @@ Register Event Callbacks
When PCNT unit reaches any enabled watch point, specific event will be generated and notify the CPU by interrupt. If you have some function that want to get executed when event happens, you should hook your function to the interrupt service routine by calling :cpp:func:`pcnt_unit_register_event_callbacks`. All supported event callbacks are listed in the :cpp:type:`pcnt_event_callbacks_t`:
- :cpp:member:`pcnt_event_callbacks_t::on_reach` sets a callback function for watch point event. As this function is called within the ISR context, you must ensure that the function doesn't attempt to block (e.g., by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function). The function prototype is declared in :cpp:type:`pcnt_watch_cb_t`.
- :cpp:member:`pcnt_event_callbacks_t::on_reach` sets a callback function for watch point event. As this function is called within the ISR context, you must ensure that the function does not attempt to block (e.g., by making sure that only FreeRTOS APIs with ``ISR`` suffix are called from within the function). The function prototype is declared in :cpp:type:`pcnt_watch_cb_t`.
You can save their own context to :cpp:func:`pcnt_unit_register_event_callbacks` as well, via the parameter ``user_ctx``. This user data will be directly passed to the callback functions.
In the callback function, the driver will fill in the event data of specific event. For example, the watch point event data is declared as :cpp:type:`pcnt_watch_event_data_t`:
- :cpp:member:`pcnt_watch_event_data_t::watch_point_value` saves the watch point value that triggers the event.
- :cpp:member:`pcnt_watch_event_data_t::zero_cross_mode` saves how the PCNT unit crosses the zero point in the latest time. The possible zero cross modes are listed in the :cpp:type:`pcnt_unit_zero_cross_mode_t`. Usually different zero cross mode means different **counting direction** and **counting step size**.
- :cpp:member:`pcnt_watch_event_data_t::watch_point_value` saves the watch point value that triggers the event.
- :cpp:member:`pcnt_watch_event_data_t::zero_cross_mode` saves how the PCNT unit crosses the zero point in the latest time. The possible zero cross modes are listed in the :cpp:type:`pcnt_unit_zero_cross_mode_t`. Usually different zero cross mode means different **counting direction** and **counting step size**.
Registering callback function will result in lazy installation of interrupt service, thus this function should only be called before the unit is enabled by :cpp:func:`pcnt_unit_enable`. Otherwise, it can return :c:macro:`ESP_ERR_INVALID_STATE` error.
Registering callback function results in lazy installation of interrupt service, thus this function should only be called before the unit is enabled by :cpp:func:`pcnt_unit_enable`. Otherwise, it can return :c:macro:`ESP_ERR_INVALID_STATE` error.
.. code:: c
@ -184,15 +184,15 @@ Set Glitch Filter
The PCNT unit features filters to ignore possible short glitches in the signals. The parameters that can be configured for the glitch filter are listed in :cpp:type:`pcnt_glitch_filter_config_t`:
- :cpp:member:`pcnt_glitch_filter_config_t::max_glitch_ns` sets the maximum glitch width, in nano seconds. If a signal pulse's width is smaller than this value, then it will be treated as noise and won't increase/decrease the internal counter.
- :cpp:member:`pcnt_glitch_filter_config_t::max_glitch_ns` sets the maximum glitch width, in nano seconds. If a signal pulse's width is smaller than this value, then it will be treated as noise and will not increase/decrease the internal counter.
You can enable the glitch filter for PCNT unit by calling :cpp:func:`pcnt_unit_set_glitch_filter` with the filter configuration provided above. Particularly, you can disable the glitch filter later by calling :cpp:func:`pcnt_unit_set_glitch_filter` with a `NULL` filter configuration.
You can enable the glitch filter for PCNT unit by calling :cpp:func:`pcnt_unit_set_glitch_filter` with the filter configuration provided above. Particularly, you can disable the glitch filter later by calling :cpp:func:`pcnt_unit_set_glitch_filter` with a ``NULL`` filter configuration.
This function should be called when the unit is in the init state. Otherwise, it will return :c:macro:`ESP_ERR_INVALID_STATE` error.
.. note::
The glitch filter is clocked from APB. For the counter not to miss any pulses, the maximum glitch width should be longer than one APB_CLK cycle (usually 12.5 ns if APB equals 80MHz). As the APB frequency would be changed after DFS (Dynamic Frequency Scaling) enabled, which means the filter won't work as expect in that case. So the driver will install a PM lock for PCNT unit during the first time you enable the glitch filter. For more information related to power management strategy used in PCNT driver, please see :ref:`pcnt-power-management`.
The glitch filter is clocked from APB. For the counter not to miss any pulses, the maximum glitch width should be longer than one APB_CLK cycle (usually 12.5 ns if APB equals 80MHz). As the APB frequency would be changed after DFS (Dynamic Frequency Scaling) enabled, which means the filter does not work as expect in that case. So the driver installs a PM lock for PCNT unit during the first time you enable the glitch filter. For more information related to power management strategy used in PCNT driver, please see :ref:`pcnt-power-management`.
.. code:: c
@ -232,11 +232,11 @@ This function should be called when the unit is in the init state. Otherwise, it
Enable and Disable Unit
^^^^^^^^^^^^^^^^^^^^^^^
Before doing IO control to the PCNT unit, you need to enable it first, by calling :cpp:func:`pcnt_unit_enable`. Internally, this function will:
Before doing IO control to the PCNT unit, you need to enable it first, by calling :cpp:func:`pcnt_unit_enable`. Internally, this function:
* switch the PCNT driver state from **init** to **enable**.
* enable the interrupt service if it has been lazy installed in :cpp:func:`pcnt_unit_register_event_callbacks`.
* acquire a proper power management lock if it has been lazy installed in :cpp:func:`pcnt_unit_set_glitch_filter`. See also :ref:`pcnt-power-management` for more information.
* switches the PCNT driver state from **init** to **enable**.
* enables the interrupt service if it has been lazy installed in :cpp:func:`pcnt_unit_register_event_callbacks`.
* acquires a proper power management lock if it has been lazy installed in :cpp:func:`pcnt_unit_set_glitch_filter`. See also :ref:`pcnt-power-management` for more information.
On the contrary, calling :cpp:func:`pcnt_unit_disable` will do the opposite, that is, put the PCNT driver back to the **init** state, disable the interrupts service and release the power management lock.
@ -252,7 +252,7 @@ Unit IO Control
Start/Stop and Clear
~~~~~~~~~~~~~~~~~~~~
Calling :cpp:func:`pcnt_unit_start` will make the PCNT unit start to work, increase or decrease counter according to pulse signals. On the contrary, calling :cpp:func:`pcnt_unit_stop` will stop the PCNT unit but retain current count value. Instead, clearing counter can only be done by calling :cpp:func:`pcnt_unit_clear_count`.
Calling :cpp:func:`pcnt_unit_start` makes the PCNT unit start to work, increase or decrease counter according to pulse signals. On the contrary, calling :cpp:func:`pcnt_unit_stop` will stop the PCNT unit but retain current count value. Instead, clearing counter can only be done by calling :cpp:func:`pcnt_unit_clear_count`.
Note, :cpp:func:`pcnt_unit_start` and :cpp:func:`pcnt_unit_stop` should be called when the unit has been enabled by :cpp:func:`pcnt_unit_enable`. Otherwise, it will return :c:macro:`ESP_ERR_INVALID_STATE` error.
@ -284,16 +284,16 @@ The internal hardware counter will be cleared to zero automatically when it reac
.. note::
:cpp:func:`pcnt_unit_clear_count` will reset the accumulated count value as well.
:cpp:func:`pcnt_unit_clear_count` resets the accumulated count value as well.
.. _pcnt-power-management:
Power Management
^^^^^^^^^^^^^^^^
When power management is enabled (i.e. :ref:`CONFIG_PM_ENABLE` is on), the system will adjust the APB frequency before going into light sleep, thus potentially changing the behavior of PCNT glitch filter and leading to valid signal being treated as noise.
When power management is enabled (i.e., :ref:`CONFIG_PM_ENABLE` is on), the system will adjust the APB frequency before going into light sleep, thus potentially changing the behavior of PCNT glitch filter and leading to valid signal being treated as noise.
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever you enable the glitch filter by :cpp:func:`pcnt_unit_set_glitch_filter`, the driver will guarantee that the power management lock is acquired after the PCNT unit is enabled by :cpp:func:`pcnt_unit_enable`. Likewise, the driver releases the lock after :cpp:func:`pcnt_unit_disable` is called.
However, the driver can prevent the system from changing APB frequency by acquiring a power management lock of type :cpp:enumerator:`ESP_PM_APB_FREQ_MAX`. Whenever you enable the glitch filter by :cpp:func:`pcnt_unit_set_glitch_filter`, the driver guarantees that the power management lock is acquired after the PCNT unit is enabled by :cpp:func:`pcnt_unit_enable`. Likewise, the driver releases the lock after :cpp:func:`pcnt_unit_disable` is called.
.. _pcnt-iram-safe:
@ -302,15 +302,15 @@ IRAM Safe
By default, the PCNT interrupt will be deferred when the Cache is disabled for reasons like writing/erasing Flash. Thus the alarm interrupt will not get executed in time, which is not expected in a real-time application.
There's a Kconfig option :ref:`CONFIG_PCNT_ISR_IRAM_SAFE` that will:
There is a Kconfig option :ref:`CONFIG_PCNT_ISR_IRAM_SAFE` that:
1. Enable the interrupt being serviced even when cache is disabled
2. Place all functions that used by the ISR into IRAM [2]_
3. Place driver object into DRAM (in case it's mapped to PSRAM by accident)
1. Enables the interrupt being serviced even when cache is disabled
2. Places all functions that used by the ISR into IRAM [2]_
3. Places driver object into DRAM (in case it is mapped to PSRAM by accident)
This will allow the interrupt to run while the cache is disabled but will come at the cost of increased IRAM consumption.
This allows the interrupt to run while the cache is disabled but comes at the cost of increased IRAM consumption.
There's another Kconfig option :ref:`CONFIG_PCNT_CTRL_FUNC_IN_IRAM` that can put commonly used IO control functions into IRAM as well. So that these functions can also be executable when the cache is disabled. These IO control functions are as follows:
There is another Kconfig option :ref:`CONFIG_PCNT_CTRL_FUNC_IN_IRAM` that can put commonly used IO control functions into IRAM as well. So that these functions can also be executable when the cache is disabled. These IO control functions are as follows:
- :cpp:func:`pcnt_unit_start`
- :cpp:func:`pcnt_unit_stop`
@ -323,6 +323,7 @@ Thread Safety
^^^^^^^^^^^^^
The factory functions :cpp:func:`pcnt_new_unit` and :cpp:func:`pcnt_new_channel` are guaranteed to be thread safe by the driver, which means, you can call them from different RTOS tasks without protection by extra locks.
The following functions are allowed to run under ISR context, the driver uses a critical section to prevent them being called concurrently in both task and ISR.
- :cpp:func:`pcnt_unit_start`
@ -339,7 +340,7 @@ Kconfig Options
- :ref:`CONFIG_PCNT_CTRL_FUNC_IN_IRAM` controls where to place the PCNT control functions (IRAM or Flash), see :ref:`pcnt-iram-safe` for more information.
- :ref:`CONFIG_PCNT_ISR_IRAM_SAFE` controls whether the default ISR handler can work when cache is disabled, see :ref:`pcnt-iram-safe` for more information.
- :ref:`CONFIG_PCNT_ENABLE_DEBUG_LOG` is used to enabled the debug log output. Enable this option will increase the firmware binary size.
- :ref:`CONFIG_PCNT_ENABLE_DEBUG_LOG` is used to enabled the debug log output. Enabling this option increases the firmware binary size.
Application Examples
--------------------
@ -354,7 +355,7 @@ API Reference
.. include-build-file:: inc/pcnt_types.inc
.. [1]
Different ESP chip series might have different number of PCNT units and channels. Please refer to the [`TRM <{IDF_TARGET_TRM_EN_URL}#pcnt>`__] for details. The driver won't forbid you from applying for more PCNT units and channels, but it will return error when all available hardware resources are used up. Please always check the return value when doing resource allocation (e.g. :cpp:func:`pcnt_new_unit`).
Different ESP chip series might have different number of PCNT units and channels. Please refer to the [`TRM <{IDF_TARGET_TRM_EN_URL}#pcnt>`__] for details. The driver does not forbid you from applying for more PCNT units and channels, 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:`pcnt_new_unit`).
.. [2]
:cpp:member:`pcnt_event_callbacks_t::on_reach` callback and the functions invoked by itself should also be placed in IRAM, you need to take care of them by themselves.

View File

@ -1,7 +1,7 @@
SD Pull-up Requirements
=======================
Espressif hardware products are designed for multiple use cases which may require different pull states on pins. For this reason, the pull state of particular pins on certain products will need to be adjusted to provide the pull-ups required in the SD bus.
Espressif hardware products are designed for multiple use cases which may require different pull states on pins. For this reason, the pull state of particular pins on certain products needs to be adjusted to provide the pull-ups required in the SD bus.
SD pull-up requirements apply to cases where {IDF_TARGET_NAME} uses the SPI or SDMMC controller to communicate with SD cards. When an SD card is operating in SPI mode or 1-bit SD mode, the CMD and DATA (DAT0 - DAT3) lines of the SD bus must be pulled up by 10 kOhm resistors. SD cards and SDIO devices should also have pull-ups on all above-mentioned lines (regardless of whether these lines are connected to the host) in order to prevent them from entering a wrong state.
@ -193,7 +193,7 @@ If you use a development board without pull-ups, you can do the following:
To resolve the conflict, you have the following options:
1. (Recommended) Burn the flash voltage selection eFuses. This will permanently configure the internal regulator's output voltage to 3.3 V, and GPIO12 will not be used as a bootstrapping pin. After that, connect a pull-up resistor to GPIO12.
1. (Recommended) Burn the flash voltage selection eFuses. This permanently configures the internal regulator's output voltage to 3.3 V, and GPIO12 will not be used as a bootstrapping pin. After that, connect a pull-up resistor to GPIO12.
.. warning::
@ -205,7 +205,7 @@ If you use a development board without pull-ups, you can do the following:
components/esptool_py/esptool/espefuse.py set_flash_voltage 3.3V
This command will burn the `XPD_SDIO_TIEH`, `XPD_SDIO_FORCE`, and `XPD_SDIO_REG` eFuses. After all the three eFuses are burned to value 1, the internal VDD_SDIO flash voltage regulator will be permanently set to 3.3 V. You will see the following log if the burning succeeds:
This command burns the ``XPD_SDIO_TIEH``, ``XPD_SDIO_FORCE``, and ``XPD_SDIO_REG`` eFuses. After all the three eFuses are burned to value 1, the internal VDD_SDIO flash voltage regulator is permanently set to 3.3 V. You will see the following log if the burning succeeds:
.. code-block:: bash
@ -225,12 +225,12 @@ If you use a development board without pull-ups, you can do the following:
If running from an automated flashing script, ``espefuse.py`` has an option ``--do-not-confirm``.
For more details, see *{IDF_TARGET_NAME} Technical Reference Manual* [`PDF <{IDF_TARGET_TRM_EN_URL}#efuse>`__].
For more details, see **{IDF_TARGET_NAME} Technical Reference Manual** [`PDF <{IDF_TARGET_TRM_EN_URL}#efuse>`__].
2. **If using 1-bit SD mode or SPI mode**, disconnect the DAT2 pin and make sure it is pulled high. For this, do one the following:
- Leave the host's DAT2 floating and directly connect the slave's DAT2 to VDD.
- For a slave device, build a firmware with the option ``SDIO_SLAVE_FLAG_DAT2_DISABLED`` and re-flash your device. This option will help avoid slave detecting on the DAT2 line. Note that 4-bit SD mode will no longer be supported by the standard Card Common Control Register (CCCR); however, the host will not be aware of that. The use of 4-bit SD mode will have to be disabled on the host's side.
- For a slave device, build a firmware with the option ``SDIO_SLAVE_FLAG_DAT2_DISABLED`` and re-flash your device. This option helps avoid slave detecting on the DAT2 line. Note that 4-bit SD mode is no longer supported by the standard Card Common Control Register (CCCR); however, the host is not aware of that. The use of 4-bit SD mode has to be disabled on the host's side.
.. _no_pull-up_on_gpio12:
@ -250,7 +250,7 @@ If you use a development board without pull-ups, you can do the following:
There are the following solutions:
- For boards that require shorting the GPIO0 and GPIO2 pins with a jumper, put the jumper in place, and the auto-reset circuit will pull GPIO2 low along with GPIO0 before entering Download mode.
- For boards that require shorting the GPIO0 and GPIO2 pins with a jumper, put the jumper in place, and the auto-reset circuit pulls GPIO2 low along with GPIO0 before entering Download mode.
- For boards with components attached to their GPIO2 pin (such as pull-down resistors and/or LEDs), check the schematic of your development board for anything connected to GPIO2.
- **LEDs** would not affect operation in most cases.
@ -271,7 +271,7 @@ Related Information
MTDI Strapping Pin
^^^^^^^^^^^^^^^^^^
MTDI (GPIO12) is used as a bootstrapping pin to select the output voltage of an internal regulator (VDD_SDIO) which powers the flash chip. This pin has an internal pull-down, so, if left unconnected, it will read low level at startup, which will lead to selecting the default 3.3 V operation.
MTDI (GPIO12) is used as a bootstrapping pin to select the output voltage of an internal regulator (VDD_SDIO) which powers the flash chip. This pin has an internal pull-down, so, if left unconnected, it will read low level at startup, which leads to selecting the default 3.3 V operation.
All ESP32-WROVER modules, excluding ESP32-WROVER-B, use 1.8 V flash and have internal pull-ups on GPIO12. Other modules that use 3.3 V flash have no pull-ups on the GPIO12 pin, and this pin is slightly pulled down internally.
@ -287,7 +287,7 @@ Related Information
Using external resistors is always preferable. However, Espressif's products have internal weak pull-up and pull-down resistors which can be enabled and used instead of external ones. Please keep in mind that this solution cannot guarantee reliable SDIO communication.
With that said, the information about these internal pull-ups and strapping requirements can still be useful. Espressif hardware products have different weak internal pull-ups / pull-downs connected to CMD and DATA pins. The table below shows the default pull-up and pull-down states of the CMD and DATA pins.
With that said, the information about these internal pull-ups and strapping requirements can still be useful. Espressif hardware products have different weak internal pull-ups/pull-downs connected to CMD and DATA pins. The table below shows the default pull-up and pull-down states of the CMD and DATA pins.
The following abbreviations are used in the table:

View File

@ -15,7 +15,7 @@ To realize the multiplexing of different devices from different drivers, includi
Each bus lock is initialized with a BG (background) service registered. All devices that request transactions on the bus should wait until the BG is successfully disabled.
- For the SPI1 bus, the BG is the cache. The bus lock will disable the cache before device operations start, and enable it again after the device releases the lock. No devices on SPI1 are allowed to use ISR, since it is meaningless for the task to yield to other tasks when the cache is disabled.
- For the SPI1 bus, the BG is the cache. The bus lock disables the cache before device operations start, and enables it again after the device releases the lock. No devices on SPI1 are allowed to use ISR, since it is meaningless for the task to yield to other tasks when the cache is disabled.
.. only:: esp32
@ -23,6 +23,6 @@ Each bus lock is initialized with a BG (background) service registered. All devi
.. only:: not esp32
The SPI Master driver hasn't supported SPI1 bus. Only the SPI Flash driver can attach to the bus.
The SPI Master driver has not supported SPI1 bus. Only the SPI Flash driver can attach to the bus.
- For other buses, the driver can register the ISR as a BG. If a device task requests exclusive bus access, the bus lock will block the task, disable the ISR, and then unblock the task. After the task releases the lock, the lock will try to re-enable the ISR if there are still pending transactions in the ISR.

View File

@ -11,6 +11,7 @@ The spi_flash component contains API functions related to reading, writing, eras
For higher-level API functions which work with partitions defined in the :doc:`partition table </api-guides/partition-tables>`, see :doc:`/api-reference/storage/partition`
.. note::
``esp_partition_*`` APIs are recommended to be used instead of the lower level ``esp_flash_*`` API functions when accessing the main SPI flash chip, since they conduct bounds checking and are guaranteed to calculate correct offsets in flash based on the information in the partition table. ``esp_flash_*`` functions can still be used directly when accessing an external (secondary) SPI flash chip.
Different from the API before ESP-IDF v4.0, the functionality of ``esp_flash_*`` APIs is not limited to the "main" SPI flash chip (the same SPI flash chip from which program runs). With different chip pointers, you can access external flash chips connected to not only SPI0/1 but also other SPI buses like SPI2.
@ -23,7 +24,7 @@ Different from the API before ESP-IDF v4.0, the functionality of ``esp_flash_*``
.. note::
Flash APIs after ESP-IDF v4.0 are no longer *atomic*. If a write operation occurs during another on-going read operation, and the flash addresses of both operations overlap, the data returned from the read operation may contain both old data and new data (that was updated written by the write operation).
Flash APIs after ESP-IDF v4.0 are no longer **atomic**. If a write operation occurs during another on-going read operation, and the flash addresses of both operations overlap, the data returned from the read operation may contain both old data and new data (that was updated written by the write operation).
.. note::
@ -35,7 +36,7 @@ Support for Features of Flash Chips
Quad/Dual Mode Chips
^^^^^^^^^^^^^^^^^^^^
Features of different flashes are implemented in different ways and thus need special support. The fast/slow read and Dual mode (DOUT/DIO) of almost all flashes with 24-bit address are supported, because they don't need any vendor-specific commands.
Features of different flashes are implemented in different ways and thus need special support. The fast/slow read and Dual mode (DOUT/DIO) of almost all flashes with 24-bit address are supported, because they do not need any vendor-specific commands.
Quad mode (QIO/QOUT) is supported on the following chip types:
@ -69,7 +70,7 @@ There are some features that are not supported by all flash chips, or not suppor
.. only:: esp32s3
- High performance mode (HPM) - means that flash works under high frequency which is higher than 80MHz.
- High performance mode (HPM) - means that flash works under high frequency which is higher than 80 MHz.
- Flash unique ID - means that flash supports its unique 64-bit ID.
@ -95,7 +96,7 @@ To use the ``esp_flash_*`` APIs, you need to initialise a flash chip on a certai
2. Call :cpp:func:`spi_bus_add_flash_device` to attach the flash device to the bus. This function allocates memory and fills the members for the ``esp_flash_t`` structure. The CS I/O is also initialized here.
3. Call :cpp:func:`esp_flash_init` to actually communicate with the chip. This will also detect the chip type, and influence the following operations.
3. Call :cpp:func:`esp_flash_init` to actually communicate with the chip. This also detects the chip type, and influence the following operations.
.. note:: Multiple flash chips can be attached to the same bus now.
@ -117,7 +118,7 @@ SPI Flash Size
The SPI flash size is configured by writing a field in the software bootloader image header, flashed at offset 0x1000.
By default, the SPI flash size is detected by esptool.py when this bootloader is written to flash, and the header is updated with the correct size. Alternatively, it is possible to generate a fixed flash size by setting :envvar:`CONFIG_ESPTOOLPY_FLASHSIZE` in the project configuration.
By default, the SPI flash size is detected by ``esptool.py`` when this bootloader is written to flash, and the header is updated with the correct size. Alternatively, it is possible to generate a fixed flash size by setting :envvar:`CONFIG_ESPTOOLPY_FLASHSIZE` in the project configuration.
If it is necessary to override the configured flash size at runtime, it is possible to set the ``chip_size`` member of the ``g_rom_flashchip`` structure. This size is used by ``esp_flash_*`` functions (in both software & ROM) to check the bounds.
@ -131,7 +132,7 @@ Concurrency Constraints for Flash on SPI1
.. attention::
The SPI0/1 bus is shared between the instruction & data cache (for firmware execution) and the SPI1 peripheral (controlled by the drivers including this SPI flash driver). Hence, calling SPI Flash API on SPI1 bus (including the main flash) will cause significant influence to the whole system. See :doc:`spi_flash_concurrency` for more details.
The SPI0/1 bus is shared between the instruction & data cache (for firmware execution) and the SPI1 peripheral (controlled by the drivers including this SPI flash driver). Hence, calling SPI Flash API on SPI1 bus (including the main flash) causes significant influence to the whole system. See :doc:`spi_flash_concurrency` for more details.
SPI Flash Encryption
@ -144,7 +145,7 @@ Refer to the :doc:`Flash Encryption documentation </security/flash-encryption>`
Memory Mapping API
------------------
{IDF_TARGET_CACHE_SIZE:default="64 KB",esp32c2=16~64 KB}
{IDF_TARGET_CACHE_SIZE:default="64 KB", esp32c2=16 ~ 64 KB}
{IDF_TARGET_NAME} features memory hardware which allows regions of flash memory to be mapped into instruction and data address spaces. This mapping works only for read operations. It is not possible to modify contents of flash memory by writing to a mapped memory region.
@ -163,11 +164,12 @@ Memory mapping API are declared in ``spi_flash_mmap.h`` and ``esp_partition.h``:
Differences between :cpp:func:`spi_flash_mmap` and :cpp:func:`esp_partition_mmap` are as follows:
- :cpp:func:`spi_flash_mmap` must be given a {IDF_TARGET_CACHE_SIZE} aligned physical address.
- :cpp:func:`esp_partition_mmap` may be given any arbitrary offset within the partition. It will adjust the returned pointer to mapped memory as necessary.
- :cpp:func:`esp_partition_mmap` may be given any arbitrary offset within the partition. It adjusts the returned pointer to mapped memory as necessary.
Note that since memory mapping happens in pages, it may be possible to read data outside of the partition provided to ``esp_partition_mmap``, regardless of the partition boundary.
.. note::
mmap is supported by cache, so it can only be used on main flash.
SPI Flash Implementation
@ -177,7 +179,7 @@ The ``esp_flash_t`` structure holds chip data as well as three important parts o
1. The host driver, which provides the hardware support to access the chip;
2. The chip driver, which provides compatibility service to different chips;
3. The OS functions, provide support of some OS functions (e.g. lock, delay) in different stages (1st/2nd boot, or the app).
3. The OS functions, provide support of some OS functions (e.g., lock, delay) in different stages (1st/2nd boot, or the app).
Host Driver
^^^^^^^^^^^
@ -212,7 +214,7 @@ The lock (see :ref:`spi_bus_lock`) is used to resolve the conflicts among the ac
2. On the other buses, the flash driver needs to disable the ISR registered by SPI Master driver, to avoid conflicts.
3. Some devices of SPI Master driver may require to use the bus monopolized during a period (especially when the device doesn't have a CS wire, or the wire is controlled by software like SDSPI driver).
3. Some devices of SPI Master driver may require to use the bus monopolized during a period (especially when the device does not have a CS wire, or the wire is controlled by software like SDSPI driver).
The delay is used by some long operations which requires the master to wait or polling periodically.
@ -220,14 +222,14 @@ The top API wraps these the chip driver and OS functions into an entire componen
OS functions can also help to avoid a watchdog timeout when erasing large flash areas. During this time, the CPU is occupied with the flash erasing task. This stops other tasks from being executed. Among these tasks is the idle task to feed the watchdog timer (WDT). If the configuration option :ref:`CONFIG_ESP_TASK_WDT_PANIC` is selected and the flash operation time is longer than the watchdog timeout period, the system will reboot.
It's pretty hard to totally eliminate this risk, because the erasing time varies with different flash chips, making it hard to be compatible in flash drivers. Therefore, users need to pay attention to it. Please use the following guidelines:
It is pretty hard to totally eliminate this risk, because the erasing time varies with different flash chips, making it hard to be compatible in flash drivers. Therefore, users need to pay attention to it. Please use the following guidelines:
1. It is recommended to enable the :ref:`CONFIG_SPI_FLASH_YIELD_DURING_ERASE` option to allow the scheduler to re-schedule during erasing flash memory. Besides, following parameters can also be used.
- Increase :ref:`CONFIG_SPI_FLASH_ERASE_YIELD_TICKS` or decrease :ref:`CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS` in menuconfig.
- You can also increase :ref:`CONFIG_ESP_TASK_WDT_TIMEOUT_S` in menuconfig for a larger watchdog timeout period. However, with larger watchdog timeout period, previously detected timeouts may no longer be detected.
2. Please be aware of the consequences of enabling the :ref:`CONFIG_ESP_TASK_WDT_PANIC` option when doing long-running SPI flash operations which will trigger the panic handler when it times out. However, this option can also help dealing with unexpected exceptions in your application. Please decide whether this is needed to be enabled according to actual condition.
2. Please be aware of the consequences of enabling the :ref:`CONFIG_ESP_TASK_WDT_PANIC` option when doing long-running SPI flash operations which triggers the panic handler when it times out. However, this option can also help dealing with unexpected exceptions in your application. Please decide whether this is needed to be enabled according to actual condition.
3. During your development, please carefully review the actual flash operation according to the specific requirements and time limits on erasing flash memory of your projects. Always allow reasonable redundancy based on your specific product requirements when configuring the flash erasing timeout threshold, thus improving the reliability of your product.

View File

@ -34,27 +34,27 @@ Under this condition, all CPUs should always execute code and access data from i
.. note::
When :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` is enabled, these APIs won't disable the caches. The hardware will handle the arbitration between them.
When :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` is enabled, these APIs will not disable the caches. The hardware will handle the arbitration between them.
.. only:: SOC_SPIRAM_XIP_SUPPORTED
.. note::
When :ref:`CONFIG_SPIRAM_FETCH_INSTRUCTIONS` and :ref:`CONFIG_SPIRAM_RODATA` are both enabled, these APIs won't disable the caches.
When :ref:`CONFIG_SPIRAM_FETCH_INSTRUCTIONS` and :ref:`CONFIG_SPIRAM_RODATA` are both enabled, these APIs will not disable the caches.
.. only:: not CONFIG_FREERTOS_UNICORE
The way that these APIs disable the caches will suspend all the other tasks. Besides, all non-IRAM-safe interrupts will be disabled. The other core will be polling in a busy loop. These will be restored until the Flash operation completes.
The way that these APIs disable the caches suspends all the other tasks. Besides, all non-IRAM-safe interrupts will be disabled. The other core will be polling in a busy loop. These will be restored until the Flash operation completes.
.. only:: CONFIG_FREERTOS_UNICORE
The way that these APIs disable the caches will also disable non-IRAM-safe interrupts. These will be restored until the Flash operation completes.
The way that these APIs disable the caches also disables non-IRAM-safe interrupts. These will be restored until the Flash operation completes.
See also :ref:`esp_flash_os_func` and :ref:`spi_bus_lock`.
There are no such constraints and impacts for flash chips on other SPI buses than SPI0/1.
For differences between internal RAM (e.g. IRAM, DRAM) and flash cache, please refer to the :ref:`application memory layout <memory-layout>` documentation.
For differences between internal RAM (e.g., IRAM, DRAM) and flash cache, please refer to the :ref:`application memory layout <memory-layout>` documentation.
.. _iram-safe-interrupt-handlers:
@ -75,7 +75,7 @@ If a function or symbol is not correctly put into IRAM/DRAM, and the interrupt h
Non-IRAM-Safe Interrupt Handlers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If the ``ESP_INTR_FLAG_IRAM`` flag is not set when registering, the interrupt handler will not get executed when the caches are disabled. Once the caches are restored, the non-IRAM-safe interrupts will be re-enabled. After this moment, the interrupt handler will run normally again. This means that as long as caches are disabled, users won't see the corresponding hardware event happening.
If the ``ESP_INTR_FLAG_IRAM`` flag is not set when registering, the interrupt handler will not get executed when the caches are disabled. Once the caches are restored, the non-IRAM-safe interrupts will be re-enabled. After this moment, the interrupt handler will run normally again. This means that as long as caches are disabled, users will not see the corresponding hardware event happening.
.. only:: esp32c3 or esp32c2 or esp32s3

View File

@ -1,13 +1,13 @@
SPI Flash API ESP-IDF version vs Chip-ROM version
=================================================
SPI Flash API ESP-IDF Version vs Chip-ROM Version
===================================================
.. toctree::
:maxdepth: 1
There is a set of SPI Flash drivers in Chip-ROM which you can use by enabling :ref:`CONFIG_SPI_FLASH_ROM_IMPL`. Most of the ESP-IDF SPI Flash driver code are in internal RAM, therefore enabling this option will free some internal RAM usage. Note if you enable this option, this means some SPI Flash driver features and bugfixes that are done in ESP-IDF might not be included in the Chip-ROM version.
There is a set of SPI Flash drivers in Chip-ROM which you can use by enabling :ref:`CONFIG_SPI_FLASH_ROM_IMPL`. Most of the ESP-IDF SPI Flash driver code are in internal RAM, therefore enabling this option frees some internal RAM usage. Note if you enable this option, this means some SPI Flash driver features and bugfixes that are done in ESP-IDF might not be included in the Chip-ROM version.
Feature Supported by ESP-IDF but not in Chip-ROM
Feature Supported by ESP-IDF but Not in Chip-ROM
------------------------------------------------
.. list::
@ -17,13 +17,13 @@ Feature Supported by ESP-IDF but not in Chip-ROM
- TH Flash chip support.
- Kconfig option :ref:`CONFIG_SPI_FLASH_CHECK_ERASE_TIMEOUT_DISABLED`.
- :ref:`CONFIG_SPI_FLASH_VERIFY_WRITE`, enabling this option helps you detect bad writing.
- :ref:`CONFIG_SPI_FLASH_LOG_FAILED_WRITE`, enabling this option will print the bad writing.
- :ref:`CONFIG_SPI_FLASH_WARN_SETTING_ZERO_TO_ONE`, enabling this option will check if you're writing zero to one.
- :ref:`CONFIG_SPI_FLASH_DANGEROUS_WRITE`, enabling this option will check for flash programming to certain protected regions like bootloader, partition table or application itself.
- :ref:`CONFIG_SPI_FLASH_LOG_FAILED_WRITE`, enabling this option prints the bad writing.
- :ref:`CONFIG_SPI_FLASH_WARN_SETTING_ZERO_TO_ONE`, enabling this option checks if you are writing zero to one.
- :ref:`CONFIG_SPI_FLASH_DANGEROUS_WRITE`, enabling this option checks for flash programming to certain protected regions like bootloader, partition table or application itself.
- :ref:`CONFIG_SPI_FLASH_ENABLE_COUNTERS`, enabling this option to collect performance data for ESP-IDF SPI Flash driver APIs.
- :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND`, enabling this option to automatically suspend / resume a long Flash operation when short Flash operation happens. Note this feature is an optional feature, please do read :ref:`auto-suspend-intro` for more limitations.
Bugfixes Introduced in ESP-IDF but not in Chip-ROM
Bugfixes Introduced in ESP-IDF but Not in Chip-ROM
--------------------------------------------------
.. list::

View File

@ -15,7 +15,7 @@ Some features are not supported on all ESP chips and Flash chips. You can check
.. note::
- The features listed above needs to be supported by both esp chips and flash chips.
- The features listed above needs to be supported by both ESP chips and flash chips.
- If you are using an official Espressif modules/SiP. Some of the modules/SiPs always support the feature, in this case you can see these features listed in the datasheet. Otherwise please contact `Espressif's business team <https://www.espressif.com/en/contact-us/sales-questions>`_ to know if we can supply such products for you.
@ -23,7 +23,7 @@ Some features are not supported on all ESP chips and Flash chips. You can check
.. attention::
This document only shows that IDF code has supported the features of those flash chips. It's not a list of stable flash chips certified by Espressif. If you build your own hardware from flash chips with your own brought flash chips (even with flash listed in this page), you need to validate the reliability of flash chips yourself.
This document only shows that ESP-IDF code has supported the features of those flash chips. It is not a list of stable flash chips certified by Espressif. If you build your own hardware from flash chips with your own brought flash chips (even with flash listed in this page), you need to validate the reliability of flash chips yourself.
.. _auto-suspend-intro:
@ -49,7 +49,7 @@ Flash Chips List:
There are multiple limitations about the auto-suspend feature, please do read :ref:`auto-suspend` for more information before you enable this feature.
Flash unique ID
Flash Unique ID
---------------
Unique ID is not flash id, which means flash has 64-Bit unique ID for each device. The instruction to read the unique ID (4Bh) accesses a factory-set read-only 64-bit number that is unique to each flash device. This ID number helps you to recognize each single device. Not all flash vendors support this feature. If you try to read the unique ID on a chip which does not have this feature, the behavior is not determined. The support list is as follows.
@ -70,16 +70,16 @@ Flash Chips List:
.. _hpm-doc:
High performance mode
High Performance Mode
---------------------
.. note::
This section is provided for Dual mode (DOUT/DIO) and Quad mode (QIO/QOUT) flash chips. Octal flash used on ESP-chips support High performance mode by default so far, you can refer to the octal flash support list below.
High performance mode (HPM) means that the SPI1 and flash chip works under high frequency. Usually, when the operating frequency of the flash is greater than 80MHz, it is considered that the flash works under HPM. As far as we acknowledged, flash chips have more than two different coping strategies when flash work under HPM. For some flash chips, HPM is controlled by high performance flag (HPF) in status register and for some flash chips, HPM is controlled by dummy cycle bit.
High performance mode (HPM) means that the SPI1 and flash chip works under high frequency. Usually, when the operating frequency of the flash is greater than 80 MHz, it is considered that the flash works under HPM. As far as we acknowledged, flash chips have more than two different coping strategies when flash work under HPM. For some flash chips, HPM is controlled by high performance flag (HPF) in status register and for some flash chips, HPM is controlled by dummy cycle bit.
For following conditions IDF start code deals with HPM internally.
For following conditions, ESP-IDF start code deals with HPM internally.
ESP Chips List:
@ -97,7 +97,7 @@ Flash Chips (name & ID) List:
.. _oct-flash-doc:
OPI flash support
OPI flash Support
-----------------
OPI flash means that the flash chip supports octal peripheral interface, which has octal I/O pins. Different octal flash has different configurations and different commands. Hence, it is necessary to carefully check the support list.
@ -135,8 +135,8 @@ Flash Chips List:
.. important::
Over 16 MBytes space on flash mentioned above can be only used for `data saving`, like file system. If your data / instructions over 16 MBytes spaces need to be mapped to MMU (so as to be accessed by the CPU), please enable the config `IDF_EXPERIMENTAL_FEATURES` and `CONFIG_SPI_FLASH_32BIT_ADDRESS` and read the limitations following:
Over 16 MBytes space on flash mentioned above can be only used for ``data saving``, like file system. If your data/instructions over 16 MBytes spaces need to be mapped to MMU (so as to be accessed by the CPU), please enable the config ``IDF_EXPERIMENTAL_FEATURES`` and ``CONFIG_SPI_FLASH_32BIT_ADDRESS`` and read the limitations following:
1. This option only valid for 4-line flash. Octal flash doesn't need this.
2. Only MMU on ESP chip that supports mapping to a range over 16MB memory supports this config. (Only ESP32S3 supports this up to now)
3. This option is experimental, which means it can't use on all flash chips stable, for more information, please contact Espressif Business support.
1. This option only valid for 4-line flash. Octal flash does not need this.
2. Only MMU on ESP chip that supports mapping to a range over 16 MB memory supports this config. (Only ESP32S3 supports this up to now)
3. This option is experimental, which means it can not use on all flash chips stable, for more information, please contact Espressif Business support.

View File

@ -2,43 +2,45 @@ Overriding Default Chip Drivers
===============================
.. warning::
Customizing SPI Flash Chip Drivers is considered an "expert" feature. Users should only do so at their own risk. (See the notes below)
During the SPI Flash driver's initialization (i.e., :cpp:func:`esp_flash_init`), there is a chip detection step during which the driver will iterate through a Default Chip Driver List and determine which chip driver can properly support the currently connected flash chip. The Default Chip Drivers are provided by the IDF, thus are updated in together with each IDF version. However IDF also allows users to customize their own chip drivers.
During the SPI Flash driver's initialization (i.e., :cpp:func:`esp_flash_init`), there is a chip detection step during which the driver iterates through a Default Chip Driver List and determine which chip driver can properly support the currently connected flash chip. The Default Chip Drivers are provided by the ESP-IDF, thus are updated in together with each ESP-IDF version. However ESP-IDF also allows users to customize their own chip drivers.
Users should note the following when customizing chip drivers:
1. You may need to rely on some non-public IDF functions, which have slight possibility to change between IDF versions. On the one hand, these changes may be useful bug fixes for your driver, on the other hand, they may also be breaking changes (i.e., breaks your code).
2. Some IDF bug fixes to other chip drivers will not be automatically applied to your own custom chip drivers.
1. You may need to rely on some non-public ESP-IDF functions, which have slight possibility to change between ESP-IDF versions. On the one hand, these changes may be useful bug fixes for your driver, on the other hand, they may also be breaking changes (i.e., breaks your code).
2. Some ESP-IDF bug fixes to other chip drivers are not automatically applied to your own custom chip drivers.
3. If the protection of flash is not handled properly, there may be some random reliability issues.
4. If you update to a newer IDF version that has support for more chips, you will have to manually add those new chip drivers into your custom chip driver list. Otherwise the driver will only search for the drivers in custom list you provided.
4. If you update to a newer ESP-IDF version that has support for more chips, you will have to manually add those new chip drivers into your custom chip driver list. Otherwise the driver will only search for the drivers in custom list you provided.
Steps For Creating Custom Chip Drivers and Overriding the IDF Default Driver List
---------------------------------------------------------------------------------
Steps For Creating Custom Chip Drivers and Overriding the ESP-IDF Default Driver List
-------------------------------------------------------------------------------------
.. highlight: cmake
1. Enable the :ref:`CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST` config option. This will prevent compilation and linking of the Default Chip Driver List (`default_registered_chips`) provided by IDF. Instead, the linker will search for the structure of the same name (`default_registered_chips`) that must be provided by the user.
2. Add a new component in your project, e.g. `custom_chip_driver`.
3. Copy the necessary chip driver files from the `spi_flash` component in IDF. This may include:
1. Enable the :ref:`CONFIG_SPI_FLASH_OVERRIDE_CHIP_DRIVER_LIST` config option. This prevents compilation and linking of the Default Chip Driver List (``default_registered_chips``) provided by ESP-IDF. Instead, the linker searches for the structure of the same name (``default_registered_chips``) that must be provided by the user.
2. Add a new component in your project, e.g., ``custom_chip_driver``.
3. Copy the necessary chip driver files from the ``spi_flash`` component in ESP-IDF. This may include:
- `spi_flash_chip_drivers.c` (to provide the `default_registered_chips` structure)
- Any of the `spi_flash_chip_*.c` files that matches your own flash model best
- `CMakeLists.txt` and `linker.lf` files
- ``spi_flash_chip_drivers.c`` (to provide the ``default_registered_chips`` structure)
- Any of the ``spi_flash_chip_*.c`` files that matches your own flash model best
- ``CMakeLists.txt`` and ``linker.lf`` files
Modify the files above properly. Including:
- Change the ``default_registered_chips`` variable to non-static and remove the #ifdef logic around it.
- Update `linker.lf` file to rename the fragment header and the library name to match the new component.
- Update ``linker.lf`` file to rename the fragment header and the library name to match the new component.
- If reusing other drivers, some header names need prefixing with ``spi_flash/`` when included from outside spi_flash component.
.. note::
- When writing your own flash chip driver, you can set your flash chip capabilities through `spi_flash_chip_***(vendor)_get_caps` and points the function pointer `get_chip_caps` for protection to the `spi_flash_chip_***_get_caps` function. The steps are as follows.
- When writing your own flash chip driver, you can set your flash chip capabilities through ``spi_flash_chip_***(vendor)_get_caps`` and points the function pointer ``get_chip_caps`` for protection to the ``spi_flash_chip_***_get_caps`` function. The steps are as follows.
1. Please check whether your flash chip have the capabilities listed in `spi_flash_caps_t` by checking the flash datasheet.
2. Write a function named `spi_flash_chip_***(vendor)_get_caps`. Take the example below as a reference. (if the flash support `suspend` and `read unique id`).
3. Points the pointer `get_chip_caps` (in `spi_flash_chip_t`) to the function mentioned above.
1. Please check whether your flash chip have the capabilities listed in ``spi_flash_caps_t`` by checking the flash datasheet.
2. Write a function named ``spi_flash_chip_***(vendor)_get_caps``. Take the example below as a reference. (if the flash support ``suspend`` and ``read unique id``).
3. Points the pointer ``get_chip_caps`` (in ``spi_flash_chip_t``) to the function mentioned above.
.. code-block:: c
@ -62,7 +64,7 @@ Steps For Creating Custom Chip Drivers and Overriding the IDF Default Driver Lis
- You also can see how to implement this in the example :example:`storage/custom_flash_driver`.
4. Write a new `CMakeLists.txt` file for the `custom_chip_driver` component, including an additional line to add a linker dependency from `spi_flash` to `custom_chip_driver`::
4. Write a new ``CMakeLists.txt`` file for the ``custom_chip_driver`` component, including an additional line to add a linker dependency from ``spi_flash`` to ``custom_chip_driver``::
idf_component_register(SRCS "spi_flash_chip_drivers.c"
"spi_flash_chip_mychip.c" # modify as needed
@ -73,7 +75,7 @@ Steps For Creating Custom Chip Drivers and Overriding the IDF Default Driver Lis
- An example of this component CMakeLists.txt can be found in :example_file:`storage/custom_flash_driver/components/custom_chip_driver/CMakeLists.txt`
5. The `linker.lf` is used to put every chip driver that you are going to use whilst cache is disabled into internal RAM. See :doc:`/api-guides/linker-script-generation` for more details. Make sure this file covers all the source files that you add.
5. The ``linker.lf`` is used to put every chip driver that you are going to use whilst cache is disabled into internal RAM. See :doc:`/api-guides/linker-script-generation` for more details. Make sure this file covers all the source files that you add.
6. Build your project, and you will see the new flash driver is used.

View File

@ -1,5 +1,6 @@
SPI Master Driver
=================
:link_to_translation:`zh_CN:[中文]`
SPI Master driver is a program that controls {IDF_TARGET_NAME}'s General Purpose SPI (GP-SPI) peripheral(s) when it functions as a master.
@ -10,7 +11,7 @@ SPI Master driver is a program that controls {IDF_TARGET_NAME}'s General Purpose
SPI1 is not a GP-SPI. SPI Master driver also supports SPI1 but with quite a few limitations, see :ref:`spi_master_on_spi1_bus`.
For more hardware information about the GP-SPI peripheral(s), see *{IDF_TARGET_NAME} Technical Reference Manual* > *SPI Controller* [`PDF <{IDF_TARGET_TRM_EN_URL}#spi>`__].
For more hardware information about the GP-SPI peripheral(s), see **{IDF_TARGET_NAME} Technical Reference Manual** > **SPI Controller** [`PDF <{IDF_TARGET_TRM_EN_URL}#spi>`__].
Terminology
-----------
@ -56,9 +57,9 @@ The terms used in relation to the SPI Master driver are given in the table below
* - Transaction
- One instance of a Host asserting a CS line, transferring data to and from a Device, and de-asserting the CS line. Transactions are atomic, which means they can never be interrupted by another transaction.
* - Launch Edge
- Edge of the clock at which the source register *launches* the signal onto the line.
- Edge of the clock at which the source register **launches** the signal onto the line.
* - Latch Edge
- Edge of the clock at which the destination register *latches in* the signal.
- Edge of the clock at which the destination register **latches in** the signal.
Driver Features
@ -125,7 +126,7 @@ In half-duplex transactions, the Read and Write phases are not simultaneous (one
The Command and Address phases are optional, as not every SPI Device requires a command and/or address. This is reflected in the Device's configuration: if :cpp:member:`spi_device_interface_config_t::command_bits` and/or :cpp:member:`spi_device_interface_config_t::address_bits` are set to zero, no Command or Address phase will occur.
The Read and Write phases can also be optional, as not every transaction requires both writing and reading data. If :cpp:member:`spi_transaction_t::rx_buffer` is NULL and :c:macro:`SPI_TRANS_USE_RXDATA` is not set, the Read phase is skipped. If :cpp:member:`spi_transaction_t::tx_buffer` is NULL and :c:macro:`SPI_TRANS_USE_TXDATA` is not set, the Write phase is skipped.
The Read and Write phases can also be optional, as not every transaction requires both writing and reading data. If :cpp:member:`spi_transaction_t::rx_buffer` is ``NULL`` and :c:macro:`SPI_TRANS_USE_RXDATA` is not set, the Read phase is skipped. If :cpp:member:`spi_transaction_t::tx_buffer` is ``NULL`` and :c:macro:`SPI_TRANS_USE_TXDATA` is not set, the Write phase is skipped.
The driver supports two types of transactions: interrupt transactions and polling transactions. The programmer can choose to use a different transaction type per Device. If your Device requires both transaction types, see :ref:`mixed_transactions`.
@ -135,9 +136,9 @@ The driver supports two types of transactions: interrupt transactions and pollin
Interrupt Transactions
^^^^^^^^^^^^^^^^^^^^^^
Interrupt transactions will block the transaction routine until the transaction completes, thus allowing the CPU to run other tasks.
Interrupt transactions blocks the transaction routine until the transaction completes, thus allowing the CPU to run other tasks.
An application task can queue multiple transactions, and the driver will automatically handle them one by one in the interrupt service routine (ISR). It allows the task to switch to other procedures until all the transactions are complete.
An application task can queue multiple transactions, and the driver automatically handles them one by one in the interrupt service routine (ISR). It allows the task to switch to other procedures until all the transactions are complete.
.. _polling_transactions:
@ -147,7 +148,7 @@ Polling Transactions
Polling transactions do not use interrupts. The routine keeps polling the SPI Host's status bit until the transaction is finished.
All the tasks that use interrupt transactions can be blocked by the queue. At this point, they will need to wait for the ISR to run twice before the transaction is finished. Polling transactions save time otherwise spent on queue handling and context switching, which results in smaller transaction duration. The disadvantage is that the CPU is busy while these transactions are in progress.
All the tasks that use interrupt transactions can be blocked by the queue. At this point, they need to wait for the ISR to run twice before the transaction is finished. Polling transactions save time otherwise spent on queue handling and context switching, which results in smaller transaction duration. The disadvantage is that the CPU is busy while these transactions are in progress.
The :cpp:func:`spi_device_polling_end` routine needs an overhead of at least 1 µs to unblock other tasks when the transaction is finished. It is strongly recommended to wrap a series of polling transactions using the functions :cpp:func:`spi_device_acquire_bus` and :cpp:func:`spi_device_release_bus` to avoid the overhead. For more information, see :ref:`bus_acquiring`.
@ -276,9 +277,9 @@ If the Command and Address phase need to have the same number of lines as the da
Write and Read Phases
^^^^^^^^^^^^^^^^^^^^^
Normally, the data that needs to be transferred to or from a Device will be read from or written to a chunk of memory indicated by the members :cpp:member:`spi_transaction_t::rx_buffer` and :cpp:member:`spi_transaction_t::tx_buffer`. If DMA is enabled for transfers, the buffers are required to be:
Normally, the data that needs to be transferred to or from a Device is read from or written to a chunk of memory indicated by the members :cpp:member:`spi_transaction_t::rx_buffer` and :cpp:member:`spi_transaction_t::tx_buffer`. If DMA is enabled for transfers, the buffers are required to be:
1. Allocated in DMA-capable internal memory (MALLOC_CAP_DMA), see :ref:`DMA-Capable Memory<dma-capable-memory>`.
1. Allocated in DMA-capable internal memory (MALLOC_CAP_DMA), see :ref:`DMA-Capable Memory <dma-capable-memory>`.
2. 32-bit aligned (starting from a 32-bit boundary and having a length of multiples of 4 bytes).
If these requirements are not satisfied, the transaction efficiency will be affected due to the allocation and copying of temporary buffers.
@ -390,7 +391,7 @@ To have better control of the calling sequence of functions, send mixed transact
.. note::
Though the :ref:`spi_bus_lock` feature makes it possible to use SPI Master driver on the SPI1 bus, it's still tricky and needs a lot of special treatment. It's a feature for advanced developers.
Though the :ref:`spi_bus_lock` feature makes it possible to use SPI Master driver on the SPI1 bus, it is still tricky and needs a lot of special treatment. It is a feature for advanced developers.
To use SPI Master driver on SPI1 bus, you have to take care of two problems:
@ -481,7 +482,7 @@ GPIO Matrix and IO_MUX
Most of the chip's peripheral signals have a direct connection to their dedicated IO_MUX pins. However, the signals can also be routed to any other available pins using the less direct GPIO matrix. If at least one signal is routed through the GPIO matrix, then all signals will be routed through it.
When an SPI Host is set to 80MHz or lower frequencies, routing SPI pins via the GPIO matrix will behave the same compared to routing them via IOMUX.
When an SPI Host is set to 80 MHz or lower frequencies, routing SPI pins via the GPIO matrix will behave the same compared to routing them via IOMUX.
The IO_MUX pins for SPI buses are given below.
@ -549,9 +550,10 @@ SPI Clock Frequency
^^^^^^^^^^^^^^^^^^^
The clock source of the GPSPI peripherals can be selected by setting :cpp:member:`spi_device_handle_t::cfg::clock_source`. You can refer to :cpp:type:`spi_clock_source_t` to know the supported clock sources.
By default driver will set :cpp:member:`spi_device_handle_t::cfg::clock_source` to ``SPI_CLK_SRC_DEFAULT``. This usually stands for the highest frequency among GPSPI clock sources. Its value will be different among chips.
The actual clock frequency of a Device may not be exactly equal to the number you set, it will be re-calculated by the driver to the nearest hardware-compatible number, and not larger than the clock frequency of the clock source. You can call :cpp:func:`spi_device_get_actual_freq` to know the actual frequency computed by the driver.
By default driver sets :cpp:member:`spi_device_handle_t::cfg::clock_source` to ``SPI_CLK_SRC_DEFAULT``. This usually stands for the highest frequency among GPSPI clock sources. Its value is different among chips.
The actual clock frequency of a Device may not be exactly equal to the number you set, it is re-calculated by the driver to the nearest hardware-compatible number, and not larger than the clock frequency of the clock source. You can call :cpp:func:`spi_device_get_actual_freq` to know the actual frequency computed by the driver.
The theoretical maximum transfer speed of the Write or Read phase can be calculated according to the table below:
@ -587,7 +589,7 @@ The theoretical maximum transfer speed of the Write or Read phase can be calcula
* - 8-Line
- *SPI Frequency*
The transfer speed calculation of other phases(Command, Address, Dummy) is similar.
The transfer speed calculation of other phases (Command, Address, Dummy) is similar.
.. only:: esp32
@ -596,13 +598,13 @@ The transfer speed calculation of other phases(Command, Address, Dummy) is simil
Cache Missing
^^^^^^^^^^^^^
The default config puts only the ISR into the IRAM. Other SPI-related functions, including the driver itself and the callback, might suffer from cache misses and will need to wait until the code is read from flash. Select :ref:`CONFIG_SPI_MASTER_IN_IRAM` to put the whole SPI driver into IRAM and put the entire callback(s) and its callee functions into IRAM to prevent cache missing.
The default config puts only the ISR into the IRAM. Other SPI-related functions, including the driver itself and the callback, might suffer from cache misses and need to wait until the code is read from flash. Select :ref:`CONFIG_SPI_MASTER_IN_IRAM` to put the whole SPI driver into IRAM and put the entire callback(s) and its callee functions into IRAM to prevent cache missing.
.. note::
SPI driver implementation is based on FreeRTOS APIs, to use :ref:`CONFIG_SPI_MASTER_IN_IRAM`, you should not enable :ref:`CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH`.
For an interrupt transaction, the overall cost is *20+8n/Fspi[MHz]* [µs] for n bytes transferred in one transaction. Hence, the transferring speed is: *n/(20+8n/Fspi)*. An example of transferring speed at 8 MHz clock speed is given in the following table.
For an interrupt transaction, the overall cost is **20+8n/Fspi[MHz]** [µs] for n bytes transferred in one transaction. Hence, the transferring speed is: **n/(20+8n/Fspi)**. An example of transferring speed at 8 MHz clock speed is given in the following table.
.. list-table::
:widths: 30 45 40 30 30
@ -641,7 +643,7 @@ For an interrupt transaction, the overall cost is *20+8n/Fspi[MHz]* [µs] for n
When a transaction length is short, the cost of the transaction interval is high. If possible, try to squash several short transactions into one transaction to achieve a higher transfer speed.
Please note that the ISR is disabled during flash operation by default. To keep sending transactions during flash operations, enable :ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM` and set :c:macro:`ESP_INTR_FLAG_IRAM` in the member :cpp:member:`spi_bus_config_t::intr_flags`. In this case, all the transactions queued before starting flash operations will be handled by the ISR in parallel. Also note that the callback of each Device and their callee functions should be in IRAM, or your callback will crash due to cache missing. For more details, see :ref:`iram-safe-interrupt-handlers`.
Please note that the ISR is disabled during flash operation by default. To keep sending transactions during flash operations, enable :ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM` and set :c:macro:`ESP_INTR_FLAG_IRAM` in the member :cpp:member:`spi_bus_config_t::intr_flags`. In this case, all the transactions queued before starting flash operations are handled by the ISR in parallel. Also note that the callback of each Device and their ``callee`` functions should be in IRAM, or your callback will crash due to cache missing. For more details, see :ref:`iram-safe-interrupt-handlers`.
.. only:: esp32
@ -664,7 +666,7 @@ Please note that the ISR is disabled during flash operation by default. To keep
- :cpp:member:`spi_device_interface_config_t::input_delay_ns` - maximum data valid time on the MISO bus after a clock cycle on SCLK starts
- If the IO_MUX pin or the GPIO Matrix is used
When the GPIO matrix is used, the maximum allowed frequency is reduced to about 33~77% in comparison to the existing *input delay*. To retain a higher frequency, you have to use the IO_MUX pins or the *dummy bit workaround*. You can obtain the maximum reading frequency of the master by using the function :cpp:func:`spi_get_freq_limit`.
When the GPIO matrix is used, the maximum allowed frequency is reduced to about 33 ~ 77% in comparison to the existing **input delay**. To retain a higher frequency, you have to use the IO_MUX pins or the **dummy bit workaround**. You can obtain the maximum reading frequency of the master by using the function :cpp:func:`spi_get_freq_limit`.
.. _dummy_bit_workaround:
@ -693,7 +695,7 @@ Please note that the ISR is disabled during flash operation by default. To keep
- Yes
- Half-duplex, no DMA allowed
If the Host only writes data, the *dummy bit workaround* and the frequency check can be disabled by setting the bit ``SPI_DEVICE_NO_DUMMY`` in the member :cpp:member:`spi_device_interface_config_t::flags`. When disabled, the output frequency can be 80 MHz, even if the GPIO matrix is used.
If the Host only writes data, the **dummy bit workaround** and the frequency check can be disabled by setting the bit ``SPI_DEVICE_NO_DUMMY`` in the member :cpp:member:`spi_device_interface_config_t::flags`. When disabled, the output frequency can be 80 MHz, even if the GPIO matrix is used.
:cpp:member:`spi_device_interface_config_t::flags`
@ -719,9 +721,9 @@ Please note that the ISR is disabled during flash operation by default. To keep
* - ESP32 slave using IO_MUX
- 50
* - ESP32 slave using GPIO_MATRIX
- 75
- 75
The MISO path delay (valid time) consists of a slave's *input delay* plus the master's *GPIO matrix delay*. The delay determines the above frequency limit for full-duplex transfers. Once exceeding, full-duplex transfers will not work as well as the half-duplex transactions that use dummy bits. The frequency limit is:
The MISO path delay (valid time) consists of a slave's **input delay** plus the master's **GPIO matrix delay**. The delay determines the above frequency limit for full-duplex transfers. Once exceeding, full-duplex transfers will not work as well as the half-duplex transactions that use dummy bits. The frequency limit is:
*Freq limit [MHz] = 80 / (floor(MISO delay[ns]/12.5) + 1)*
@ -729,7 +731,7 @@ Please note that the ISR is disabled during flash operation by default. To keep
.. image:: /../_static/spi_master_freq_tv.png
Corresponding frequency limits for different Devices with different *input delay* times are shown in the table below.
Corresponding frequency limits for different Devices with different **input delay** times are shown in the table below.
When the master is IO_MUX (0 ns):
@ -788,7 +790,7 @@ Please note that the ISR is disabled during flash operation by default. To keep
This can prohibit you from transmitting and receiving data longer than 64 bytes.
3. Try using the command and address fields to replace the Write phase.
2. Full-duplex transactions are not compatible with the *dummy bit workaround*, hence the frequency is limited. See :ref:`dummy bit speed-up workaround <dummy_bit_workaround>`.
2. Full-duplex transactions are not compatible with the **dummy bit workaround**, hence the frequency is limited. See :ref:`dummy bit speed-up workaround <dummy_bit_workaround>`.
3. ``dummy_bits`` in :cpp:type:`spi_device_interface_config_t` and :cpp:type:`spi_transaction_ext_t` are not available when SPI Read and Write phases are both enabled (regardless of full duplex or half duplex mode).

View File

@ -5,7 +5,7 @@ SPI Slave Driver
SPI Slave driver is a program that controls {IDF_TARGET_NAME}'s General Purpose SPI (GP-SPI) peripheral(s) when it functions as a slave.
For more hardware information about the GP-SPI peripheral(s), see *{IDF_TARGET_NAME} Technical Reference Manual* > *SPI Controller* [`PDF <{IDF_TARGET_TRM_EN_URL}#spi>`__].
For more hardware information about the GP-SPI peripheral(s), see **{IDF_TARGET_NAME} Technical Reference Manual** > **SPI Controller** [`PDF <{IDF_TARGET_TRM_EN_URL}#spi>`__].
Terminology
-----------
@ -37,13 +37,13 @@ The terms used in relation to the SPI slave driver are given in the table below.
* - QUADHD
- Hold signal. Only used for 4-bit (qio/qout) transactions.
* - Assertion
- The action of activating a line. The opposite action of returning the line back to inactive (back to idle) is called *de-assertion*.
- The action of activating a line. The opposite action of returning the line back to inactive (back to idle) is called **de-assertion**.
* - Transaction
- One instance of a Host asserting a CS line, transferring data to and from a Device, and de-asserting the CS line. Transactions are atomic, which means they can never be interrupted by another transaction.
* - Launch Edge
- Edge of the clock at which the source register *launches* the signal onto the line.
- Edge of the clock at which the source register **launches** the signal onto the line.
* - Latch Edge
- Edge of the clock at which the destination register *latches in* the signal.
- Edge of the clock at which the destination register **latches in** the signal.
Driver Features
@ -62,7 +62,7 @@ A full-duplex SPI transaction begins when the Host asserts the CS line and start
The attributes of a transaction are determined by the configuration structure for an SPI peripheral acting as a slave device :cpp:type:`spi_slave_interface_config_t`, and transaction configuration structure :cpp:type:`spi_slave_transaction_t`.
As not every transaction requires both writing and reading data, you can choose to configure the :cpp:type:`spi_transaction_t` structure for TX only, RX only, or TX and RX transactions. If :cpp:member:`spi_slave_transaction_t::rx_buffer` is set to NULL, the read phase will be skipped. Similarly, if :cpp:member:`spi_slave_transaction_t::tx_buffer` is set to NULL, the write phase will be skipped.
As not every transaction requires both writing and reading data, you can choose to configure the :cpp:type:`spi_transaction_t` structure for TX only, RX only, or TX and RX transactions. If :cpp:member:`spi_slave_transaction_t::rx_buffer` is set to ``NULL``, the read phase will be skipped. Similarly, if :cpp:member:`spi_slave_transaction_t::tx_buffer` is set to ``NULL``, the write phase will be skipped.
.. note::
@ -215,10 +215,10 @@ The SPI slaves are designed to operate at up to {IDF_TARGET_MAX_FREQ} MHz. The d
- Freq. limit (MHz)
* - IO_MUX
- 43.75
- <11.4
- < 11.4
* - GPIO matrix
- 68.75
- <7.2
- < 7.2
Note:
1. If the frequency reaches the maximum limitation, random errors may occur.

View File

@ -1,5 +1,6 @@
Touch Sensor
============
:link_to_translation:`zh_CN:[中文]`
{IDF_TARGET_TOUCH_SENSOR_VERSION:default="v2", esp32="v1"}
@ -7,11 +8,11 @@ Touch Sensor
Introduction
------------
A touch sensor system is built on a substrate which carries electrodes and relevant connections under a protective flat surface. When a user touches the surface, the capacitance variation is used to evaluate if the touch was valid.
A touch sensor system is built on a substrate which carries electrodes and relevant connections under a protective flat surface. When the surface is touched, the capacitance variation is used to evaluate if the touch was valid.
The sensing pads can be arranged in different combinations (e.g., matrix, slider), so that a larger area or more points can be detected. The touch pad sensing process is under the control of a hardware-implemented finite-state machine (FSM) which is initiated by software or a dedicated hardware timer.
For design, operation, and control registers of a touch sensor, see *{IDF_TARGET_NAME} Technical Reference Manual* > *On-Chip Sensors and Analog Signal Processing* [`PDF <{IDF_TARGET_TRM_EN_URL}#sensor>`__].
For design, operation, and control registers of a touch sensor, see **{IDF_TARGET_NAME} Technical Reference Manual** > **On-Chip Sensors and Analog Signal Processing** [`PDF <{IDF_TARGET_TRM_EN_URL}#sensor>`__].
In-depth design details of touch sensors and firmware development guidelines for {IDF_TARGET_NAME} are available in `Touch Sensor Application Note <https://github.com/espressif/esp-iot-solution/blob/release/v1.0/documents/touch_pad_solution/touch_sensor_design_en.md>`_.
@ -38,7 +39,7 @@ For detailed description of a particular function, please go to Section :ref:`to
Initialization
^^^^^^^^^^^^^^
Before using a touch pad, you need to initialize the touch pad driver by calling the function :cpp:func:`touch_pad_init`. This function sets several ``.._DEFAULT`` driver parameters listed in :ref:`touch_pad-api-reference` under *Macros*. It also removes the information about which pads have been touched before, if any, and disables interrupts.
Before using a touch pad, you need to initialize the touch pad driver by calling the function :cpp:func:`touch_pad_init`. This function sets several ``.._DEFAULT`` driver parameters listed in :ref:`touch_pad-api-reference` under **Macros**. It also removes the information about which pads have been touched before, if any, and disables interrupts.
If the driver is not required anymore, deinitialize it by calling :cpp:func:`touch_pad_deinit`.
@ -152,7 +153,7 @@ Method of Measurements
.. only:: SOC_TOUCH_VERSION_1
The touch sensor will count the number of charge/discharge cycles over a fixed period of time (specified by :cpp:func:`touch_pad_set_measurement_clock_cycles`). The count result is the raw data that read from :cpp:func:`touch_pad_read_raw_data`. After finishing one measurement, the touch sensor will sleep until the next measurement start, this interval between two measurements can be set by :cpp:func:`touch_pad_set_measurement_interval`.
The touch sensor counts the number of charge/discharge cycles over a fixed period of time (specified by :cpp:func:`touch_pad_set_measurement_clock_cycles`). The count result is the raw data that read from :cpp:func:`touch_pad_read_raw_data`. After finishing one measurement, the touch sensor sleeps until the next measurement start, this interval between two measurements can be set by :cpp:func:`touch_pad_set_measurement_interval`.
.. note::
@ -160,7 +161,7 @@ Method of Measurements
.. only:: SOC_TOUCH_VERSION_2
The touch sensor will record the period of time (i.e. the number of clock cycles) over a fixed charge/discharge cycles (specified by :cpp:func:`touch_pad_set_charge_discharge_times`). The count result is the raw data that read from :cpp:func:`touch_pad_read_raw_data`. After finishing one measurement, the touch sensor will sleep until the next measurement start, this interval between two measurements can be set by :cpp:func:`touch_pad_set_measurement_interval`.
The touch sensor records the period of time (i.e., the number of clock cycles) over a fixed charge/discharge cycles (specified by :cpp:func:`touch_pad_set_charge_discharge_times`). The count result is the raw data that read from :cpp:func:`touch_pad_read_raw_data`. After finishing one measurement, the touch sensor sleeps until the next measurement start, this interval between two measurements can be set by :cpp:func:`touch_pad_set_measurement_interval`.
.. note::
@ -169,7 +170,7 @@ Method of Measurements
Optimization of Measurements
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A touch sensor has several configurable parameters to match the characteristics of a particular touch pad design. For instance, to sense smaller capacity changes, it is possible to narrow down the reference voltage range within which the touch pads are charged / discharged. The high and low reference voltages are set using the function :cpp:func:`touch_pad_set_voltage`.
A touch sensor has several configurable parameters to match the characteristics of a particular touch pad design. For instance, to sense smaller capacity changes, it is possible to narrow down the reference voltage range within which the touch pads are charged/discharged. The high and low reference voltages are set using the function :cpp:func:`touch_pad_set_voltage`.
.. only:: SOC_TOUCH_VERSION_1
@ -205,7 +206,7 @@ Relationship between the voltage range (high/low reference voltages), speed (slo
Touch pad - relationship between measurement parameters
The last chart *Output* represents the touch sensor reading, i.e., the count of pulses collected within the measurement time.
The last chart **Output** represents the touch sensor reading, i.e., the count of pulses collected within the measurement time.
.. only:: SOC_TOUCH_VERSION_2
@ -216,9 +217,9 @@ Relationship between the voltage range (high/low reference voltages), speed (slo
Touch pad - relationship between measurement parameters
The last chart *Output* represents the touch sensor reading, i.e., the time taken to accumulate the fixed number of cycles.
The last chart **Output** represents the touch sensor reading, i.e., the time taken to accumulate the fixed number of cycles.
All functions are provided in pairs to *set* a specific parameter and to *get* the current parameter's value, e.g., :cpp:func:`touch_pad_set_voltage` and :cpp:func:`touch_pad_get_voltage`.
All functions are provided in pairs to **set** a specific parameter and to **get** the current parameter's value, e.g., :cpp:func:`touch_pad_set_voltage` and :cpp:func:`touch_pad_get_voltage`.
.. _touch_pad-api-filtering-of-measurements:
@ -237,6 +238,7 @@ Filtering of Measurements
If measurements are noisy, you can filter them with provided API functions. The {IDF_TARGET_NAME}'s touch functionality provide two sets of APIs for doing this.
There is an internal touch channel that is not connected to any external GPIO. The measurements from this denoise pad can be used to filters out interference introduced on all channels, such as noise introduced by the power supply and external EMI.
The denoise paramaters are set with the function :cpp:func:`touch_pad_denoise_set_config` and started by with :cpp:func:`touch_pad_denoise_enable`
There is also a configurable hardware implemented IIR-filter (infinite impulse response). This IIR-filter is configured with the function :cpp:func:`touch_pad_filter_set_config` and enabled by calling :cpp:func:`touch_pad_filter_enable`
@ -248,7 +250,7 @@ Touch detection is implemented in ESP32's hardware based on the user-configured
Hardware touch detection can also be wired to interrupts. This is described in the next section.
If measurements are noisy and capacity changes are small, hardware touch detection might be unreliable. To resolve this issue, instead of using hardware detection / provided interrupts, implement measurement filtering and perform touch detection in your own application. For sample implementation of both methods of touch detection, see :example:`peripherals/touch_sensor/touch_sensor_{IDF_TARGET_TOUCH_SENSOR_VERSION}/touch_pad_interrupt`.
If measurements are noisy and capacity changes are small, hardware touch detection might be unreliable. To resolve this issue, instead of using hardware detection/provided interrupts, implement measurement filtering and perform touch detection in your own application. For sample implementation of both methods of touch detection, see :example:`peripherals/touch_sensor/touch_sensor_{IDF_TARGET_TOUCH_SENSOR_VERSION}/touch_pad_interrupt`.
Touch Triggered Interrupts
^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -272,7 +274,7 @@ When interrupts are operational, you can obtain the information from which parti
.. note::
Interrupts on touch detection operate on raw / unfiltered measurements checked against user established threshold and are implemented in hardware. Enabling the software filtering API (see :ref:`touch_pad-api-filtering-of-measurements`) does not affect this process.
Interrupts on touch detection operate on raw/unfiltered measurements checked against user established threshold and are implemented in hardware. Enabling the software filtering API (see :ref:`touch_pad-api-filtering-of-measurements`) does not affect this process.
.. only:: SOC_TOUCH_VERSION_1
@ -304,7 +306,8 @@ API Reference
GPIO Lookup Macros
^^^^^^^^^^^^^^^^^^
Some useful macros can be used to specified the GPIO number of a touch pad channel, or vice versa. e.g.
Some useful macros can be used to specified the GPIO number of a touch pad channel, or vice versa. e.g.,
1. ``TOUCH_PAD_NUM5_GPIO_NUM`` is the GPIO number of channel 5 (12);
2. ``TOUCH_PAD_GPIO4_CHANNEL`` is the channel number of GPIO 4 (channel 0).

View File

@ -6,6 +6,7 @@ Two-Wire Automotive Interface (TWAI)
.. only:: esp32c6
.. warning::
{IDF_TARGET_NAME} has {IDF_TARGET_SOC_TWAI_CONTROLLER_NUM} TWAI controllers, but at the moment, the driver can only support ``TWAI0`` due to the limitation of the driver structure.
Overview
@ -14,6 +15,7 @@ Overview
The Two-Wire Automotive Interface (TWAI) is a real-time serial communication protocol suited for automotive and industrial applications. It is compatible with ISO11898-1 Classical frames, thus can support Standard Frame Format (11-bit ID) and Extended Frame Format (29-bit ID). The {IDF_TARGET_NAME} contains {IDF_TARGET_CONFIG_SOC_TWAI_CONTROLLER_NUM} TWAI controller(s) that can be configured to communicate on a TWAI bus via an external transceiver.
.. warning::
The TWAI controller is not compatible with ISO11898-1 FD Format frames, and will interpret such frames as errors.
This programming guide is split into the following sections:
@ -34,7 +36,7 @@ The TWAI is a multi-master, multi-cast, asynchronous, serial communication proto
**Asynchronous:** The bus does not contain a clock signal. All nodes on the bus operate at the same bit rate and synchronize using the edges of the bits transmitted on the bus.
**Error Detection and Signalling:** Every node will constantly monitor the bus. When any node detects an error, it will signal the detection by transmitting an error frame. Other nodes will receive the error frame and transmit their own error frames in response. This will result in an error detection being propagated to all nodes on the bus.
**Error Detection and Signaling:** Every node constantly monitors the bus. When any node detects an error, it signals the detection by transmitting an error frame. Other nodes will receive the error frame and transmit their own error frames in response. This results in an error detection being propagated to all nodes on the bus.
**Message Priorities:** Messages contain an ID field. If two or more nodes attempt to transmit simultaneously, the node transmitting the message with the lower ID value will win arbitration of the bus. All other nodes will become receivers ensuring that there is at most one transmitter at any time.
@ -63,7 +65,7 @@ The TWAI protocol implements a feature known as "fault confinement" where a pers
Signals Lines and Transceiver
-----------------------------
The TWAI controller does not contain a integrated transceiver. Therefore, to connect the TWAI controller to a TWAI bus, **an external transceiver is required**. The type of external transceiver used should depend on the application's physical layer specification (e.g. using SN65HVD23x transceivers for ISO 11898-2 compatibility).
The TWAI controller does not contain a integrated transceiver. Therefore, to connect the TWAI controller to a TWAI bus, **an external transceiver is required**. The type of external transceiver used should depend on the application's physical layer specification (e.g., using SN65HVD23x transceivers for ISO 11898-2 compatibility).
The TWAI controller's interface consists of 4 signal lines known as **TX, RX, BUS-OFF, and CLKOUT**. These four signal lines can be routed through the GPIO Matrix to the {IDF_TARGET_NAME}'s GPIO pads.
@ -71,13 +73,14 @@ The TWAI controller's interface consists of 4 signal lines known as **TX, RX, BU
:caption: Signal lines of the TWAI controller
:align: center
**TX and RX:** The TX and RX signal lines are required to interface with an external transceiver. Both signal lines represent/interpret a dominant bit as a low logic level (0V), and a recessive bit as a high logic level (3.3V).
**TX and RX:** The TX and RX signal lines are required to interface with an external transceiver. Both signal lines represent/interpret a dominant bit as a low logic level (0 V), and a recessive bit as a high logic level (3.3 V).
**BUS-OFF:** The BUS-OFF signal line is **optional** and is set to a low logic level (0V) whenever the TWAI controller reaches a bus-off state. The BUS-OFF signal line is set to a high logic level (3.3V) otherwise.
**BUS-OFF:** The BUS-OFF signal line is **optional** and is set to a low logic level (0 V) whenever the TWAI controller reaches a bus-off state. The BUS-OFF signal line is set to a high logic level (3.3 V) otherwise.
**CLKOUT:** The CLKOUT signal line is **optional** and outputs a prescaled version of the controller's source clock.
.. note::
An external transceiver **must internally loopback the TX to RX** such that a change in logic level to the TX signal line can be observed on the RX line. Failing to do so will cause the TWAI controller to interpret differences in logic levels between the two signal lines as a loss in arbitration or a bit error.
@ -97,7 +100,7 @@ The TWAI driver supports the following modes of operations:
**No Ack Mode:** The No Acknowledgement mode is similar to normal mode, however acknowledgements are not required for a message transmission to be considered successful. This mode is useful when self testing the TWAI controller (loopback of transmissions).
**Listen Only Mode:** This mode will prevent the TWAI controller from influencing the bus. Therefore, transmission of messages/acknowledgement/error frames will be disabled. However the TWAI controller will still be able to receive messages but will not acknowledge the message. This mode is suited for bus monitor applications.
**Listen Only Mode:** This mode prevents the TWAI controller from influencing the bus. Therefore, transmission of messages/acknowledgement/error frames will be disabled. However the TWAI controller is still able to receive messages but will not acknowledge the message. This mode is suited for bus monitor applications.
Alerts
^^^^^^
@ -140,18 +143,21 @@ The TWAI driver contains an alert feature that is used to notify the application
- Bus-off condition occurred. TWAI controller can no longer influence bus
.. note::
The TWAI controller's **error warning limit** is used to preemptively warn the application of bus errors before the error passive state is reached. By default, the TWAI driver sets the **error warning limit** to **96**. The ``TWAI_ALERT_ABOVE_ERR_WARN`` is raised when the TEC or REC becomes larger then or equal to the error warning limit. The ``TWAI_ALERT_BELOW_ERR_WARN`` is raised when both TEC and REC return back to values below **96**.
.. note::
When enabling alerts, the ``TWAI_ALERT_AND_LOG`` flag can be used to cause the TWAI driver to log any raised alerts to UART. However, alert logging is disabled and ``TWAI_ALERT_AND_LOG`` if the :ref:`CONFIG_TWAI_ISR_IN_IRAM` option is enabled (see :ref:`placing-isr-into-iram`).
.. note::
The ``TWAI_ALERT_ALL`` and ``TWAI_ALERT_NONE`` macros can also be used to enable/disable all alerts during configuration/reconfiguration.
Bit Timing
^^^^^^^^^^
The operating bit rate of the TWAI driver is configured using the :cpp:type:`twai_timing_config_t` structure. The period of each bit is made up of multiple **time quanta**, and the period of a **time quantum** is determined by a prescaled version of the TWAI controller's source clock. A single bit contains the following segments in the following order:
The operating bit rate of the TWAI driver is configured using the :cpp:type:`twai_timing_config_t` structure. The period of each bit is made up of multiple **time quanta**, and the period of a **time quantum** is determined by a pre-scaled version of the TWAI controller's source clock. A single bit contains the following segments in the following order:
1. The **Synchronization Segment** consists of a single time quantum
2. **Timing Segment 1** consists of 1 to 16 time quanta before sample point
@ -159,7 +165,7 @@ The operating bit rate of the TWAI driver is configured using the :cpp:type:`twa
{IDF_TARGET_MAX_BRP:default="128", esp32="128", esp32s2="32768", esp32s3="16384", esp32c3="16384", esp32c6="32768", esp32h2="32768"}
The **Baudrate Prescaler** is used to determine the period of each time quantum by dividing the TWAI controller's source clock. On the {IDF_TARGET_NAME}, the ``brp`` can be **any even number from 2 to {IDF_TARGET_MAX_BRP}**. Alternatively, you can decide the resolution of each quantum, by setting :cpp:member:`twai_timing_config_t::quanta_resolution_hz` to a non-zero value. In this way, the driver can calculate the underlying ``brp`` value for you. It's useful when you set different clock sources but want the bitrate to keep the same.
The **Baudrate Prescaler** is used to determine the period of each time quantum by dividing the TWAI controller's source clock. On the {IDF_TARGET_NAME}, the ``brp`` can be **any even number from 2 to {IDF_TARGET_MAX_BRP}**. Alternatively, you can decide the resolution of each quantum, by setting :cpp:member:`twai_timing_config_t::quanta_resolution_hz` to a non-zero value. In this way, the driver can calculate the underlying ``brp`` value for you. It is useful when you set different clock sources but want the bitrate to keep the same.
Supported clock source for a TWAI controller is listed in the :cpp:type:`twai_clock_source_t` and can be specified in :cpp:member:`twai_timing_config_t::clk_src`.
@ -171,11 +177,12 @@ Supported clock source for a TWAI controller is listed in the :cpp:type:`twai_cl
:caption: Bit timing configuration for 500kbit/s given BRP = 8, clock source frequency is 80MHz
:align: center
The sample point of a bit is located on the intersection of Timing Segment 1 and 2. Enabling **Triple Sampling** will cause 3 time quanta to be sampled per bit instead of 1 (extra samples are located at the tail end of Timing Segment 1).
The sample point of a bit is located on the intersection of Timing Segment 1 and 2. Enabling **Triple Sampling** causes 3 time quanta to be sampled per bit instead of 1 (extra samples are located at the tail end of Timing Segment 1).
The **Synchronization Jump Width** is used to determine the maximum number of time quanta a single bit time can be lengthened/shortened for synchronization purposes. ``sjw`` can **range from 1 to 4**.
.. note::
Multiple combinations of ``brp``, ``tseg_1``, ``tseg_2``, and ``sjw`` can achieve the same bit rate. Users should tune these values to the physical characteristics of their bus by taking into account factors such as **propagation delay, node information processing time, and phase errors**.
Bit timing **macro initializers** are also available for commonly used bit rates. The following macro initializers are provided by the TWAI driver.
@ -208,17 +215,17 @@ Bit timing **macro initializers** are also available for commonly used bit rates
Acceptance Filter
^^^^^^^^^^^^^^^^^
The TWAI controller contains a hardware acceptance filter which can be used to filter messages of a particular ID. A node that filters out a message **will not receive the message, but will still acknowledge it**. Acceptance filters can make a node more efficient by filtering out messages sent over the bus that are irrelevant to the node. The acceptance filter is configured using two 32-bit values within :cpp:type:`twai_filter_config_t` known as the **acceptance code** and the **acceptance mask**.
The TWAI controller contains a hardware acceptance filter which can be used to filter messages of a particular ID. A node that filters out a message **does not receive the message, but will still acknowledge it**. Acceptance filters can make a node more efficient by filtering out messages sent over the bus that are irrelevant to the node. The acceptance filter is configured using two 32-bit values within :cpp:type:`twai_filter_config_t` known as the **acceptance code** and the **acceptance mask**.
The **acceptance code** specifies the bit sequence which a message's ID, RTR, and data bytes must match in order for the message to be received by the TWAI controller. The **acceptance mask** is a bit sequence specifying which bits of the acceptance code can be ignored. This allows for a messages of different IDs to be accepted by a single acceptance code.
The acceptance filter can be used under **Single or Dual Filter Mode**. Single Filter Mode will use the acceptance code and mask to define a single filter. This allows for the first two data bytes of a standard frame to be filtered, or the entirety of an extended frame's 29-bit ID. The following diagram illustrates how the 32-bit acceptance code and mask will be interpreted under Single Filter Mode (Note: The yellow and blue fields represent standard and extended frame formats respectively).
The acceptance filter can be used under **Single or Dual Filter Mode**. Single Filter Mode uses the acceptance code and mask to define a single filter. This allows for the first two data bytes of a standard frame to be filtered, or the entirety of an extended frame's 29-bit ID. The following diagram illustrates how the 32-bit acceptance code and mask are interpreted under Single Filter Mode (Note: The yellow and blue fields represent standard and extended frame formats respectively).
.. packetdiag:: ../../../_static/diagrams/twai/acceptance_filter_single.diag
:caption: Bit layout of single filter mode (Right side MSBit)
:align: center
**Dual Filter Mode** will use the acceptance code and mask to define two separate filters allowing for increased flexibility of ID's to accept, but does not allow for all 29-bits of an extended ID to be filtered. The following diagram illustrates how the 32-bit acceptance code and mask will be interpreted under **Dual Filter Mode** (Note: The yellow and blue fields represent standard and extended frame formats respectively).
**Dual Filter Mode** uses the acceptance code and mask to define two separate filters allowing for increased flexibility of ID's to accept, but does not allow for all 29-bits of an extended ID to be filtered. The following diagram illustrates how the 32-bit acceptance code and mask are interpreted under **Dual Filter Mode** (Note: The yellow and blue fields represent standard and extended frame formats respectively).
.. packetdiag:: ../../../_static/diagrams/twai/acceptance_filter_dual.diag
:caption: Bit layout of dual filter mode (Right side MSBit)
@ -227,14 +234,14 @@ The acceptance filter can be used under **Single or Dual Filter Mode**. Single F
Disabling TX Queue
^^^^^^^^^^^^^^^^^^
The TX queue can be disabled during configuration by setting the ``tx_queue_len`` member of :cpp:type:`twai_general_config_t` to ``0``. This will allow applications that do not require message transmission to save a small amount of memory when using the TWAI driver.
The TX queue can be disabled during configuration by setting the ``tx_queue_len`` member of :cpp:type:`twai_general_config_t` to ``0``. This allows applications that do not require message transmission to save a small amount of memory when using the TWAI driver.
.. _placing-isr-into-iram:
Placing ISR into IRAM
^^^^^^^^^^^^^^^^^^^^^
The TWAI driver's ISR (Interrupt Service Routine) can be placed into IRAM so that the ISR can still run whilst the cache is disabled. Placing the ISR into IRAM may be necessary to maintain the TWAI driver's functionality during lengthy cache disabling operations (such as SPI Flash writes, OTA updates etc). Whilst the cache is disabled, the ISR will continue to:
The TWAI driver's ISR (Interrupt Service Routine) can be placed into IRAM so that the ISR can still run whilst the cache is disabled. Placing the ISR into IRAM may be necessary to maintain the TWAI driver's functionality during lengthy cache disabling operations (such as SPI Flash writes, OTA updates etc). Whilst the cache is disabled, the ISR continues to:
- Read received messages from the RX buffer and place them into the driver's RX queue.
- Load messages pending transmission from the driver's TX queue and write them into the TX buffer.
@ -242,9 +249,10 @@ The TWAI driver's ISR (Interrupt Service Routine) can be placed into IRAM so tha
To place the TWAI driver's ISR, users must do the following:
- Enable the :ref:`CONFIG_TWAI_ISR_IN_IRAM` option using ``idf.py menuconfig``.
- When calling :cpp:func:`twai_driver_install`, the `intr_flags` member of :cpp:type:`twai_general_config_t` should set the :c:macro:`ESP_INTR_FLAG_IRAM` set.
- When calling :cpp:func:`twai_driver_install`, the ``intr_flags`` member of :cpp:type:`twai_general_config_t` should set the :c:macro:`ESP_INTR_FLAG_IRAM` set.
.. note::
When the :ref:`CONFIG_TWAI_ISR_IN_IRAM` option is enabled, the TWAI driver will no longer log any alerts (i.e., the ``TWAI_ALERT_AND_LOG`` flag will not have any effect).
.. only:: esp32
@ -258,7 +266,7 @@ To place the TWAI driver's ISR, users must do the following:
- The TWAI driver can occasionally drop some received messages.
- The TWAI driver can be unresponsive for a short period of time (i.e., will not transmit or ACK for 11 bit times or longer).
- If :ref:`CONFIG_TWAI_ISR_IN_IRAM` is enabled, the workarounds will increase IRAM usage by approximately 1KB.
- If :ref:`CONFIG_TWAI_ISR_IN_IRAM` is enabled, the workarounds will increase IRAM usage by approximately 1 KB.
The software workarounds are enabled by default and it is recommended that users keep this workarounds enabled.
@ -281,28 +289,28 @@ The TWAI driver is designed with distinct states and strict rules regarding the
- Transition
- Action/Condition
* - A
- Uninstalled -> Stopped
- Uninstalled > Stopped
- :cpp:func:`twai_driver_install`
* - B
- Stopped -> Uninstalled
- Stopped > Uninstalled
- :cpp:func:`twai_driver_uninstall`
* - C
- Stopped -> Running
- Stopped > Running
- :cpp:func:`twai_start`
* - D
- Running -> Stopped
- Running > Stopped
- :cpp:func:`twai_stop`
* - E
- Running -> Bus-Off
- Running > Bus-Off
- Transmit Error Counter >= 256
* - F
- Bus-Off -> Uninstalled
- Bus-Off > Uninstalled
- :cpp:func:`twai_driver_uninstall`
* - G
- Bus-Off -> Recovering
- Bus-Off > Recovering
- :cpp:func:`twai_initiate_recovery`
* - H
- Recovering -> Stopped
- Recovering > Stopped
- 128 occurrences of 11 consecutive recessive bits.
@ -311,20 +319,20 @@ Driver States
**Uninstalled**: In the uninstalled state, no memory is allocated for the driver and the TWAI controller is powered OFF.
**Stopped**: In this state, the TWAI controller is powered ON and the TWAI driver has been installed. However the TWAI controller will be unable to take part in any bus activities such as transmitting, receiving, or acknowledging messages.
**Stopped**: In this state, the TWAI controller is powered ON and the TWAI driver has been installed. However the TWAI controller is unable to take part in any bus activities such as transmitting, receiving, or acknowledging messages.
**Running**: In the running state, the TWAI controller is able to take part in bus activities. Therefore messages can be transmitted/received/acknowledged. Furthermore the TWAI controller will be able to transmit error frames upon detection of errors on the bus.
**Running**: In the running state, the TWAI controller is able to take part in bus activities. Therefore messages can be transmitted/received/acknowledged. Furthermore, the TWAI controller is able to transmit error frames upon detection of errors on the bus.
**Bus-Off**: The bus-off state is automatically entered when the TWAI controller's Transmit Error Counter becomes greater than or equal to 256. The bus-off state indicates the occurrence of severe errors on the bus or in the TWAI controller. Whilst in the bus-off state, the TWAI controller will be unable to take part in any bus activities. To exit the bus-off state, the TWAI controller must undergo the bus recovery process.
**Bus-Off**: The bus-off state is automatically entered when the TWAI controller's Transmit Error Counter becomes greater than or equal to 256. The bus-off state indicates the occurrence of severe errors on the bus or in the TWAI controller. Whilst in the bus-off state, the TWAI controller is unable to take part in any bus activities. To exit the bus-off state, the TWAI controller must undergo the bus recovery process.
**Recovering**: The recovering state is entered when the TWAI controller undergoes bus recovery. The TWAI controller/TWAI driver will remain in the recovering state until the 128 occurrences of 11 consecutive recessive bits is observed on the bus.
**Recovering**: The recovering state is entered when the TWAI controller undergoes bus recovery. The TWAI controller/TWAI driver remains in the recovering state until the 128 occurrences of 11 consecutive recessive bits is observed on the bus.
Message Fields and Flags
^^^^^^^^^^^^^^^^^^^^^^^^
The TWAI driver distinguishes different types of messages by using the various bit field members of the :cpp:type:`twai_message_t` structure. These bit field members determine whether a message is in standard or extended format, a remote frame, and the type of transmission to use when transmitting such a message.
These bit field members can also be toggled using the `flags` member of :cpp:type:`twai_message_t` and the following message flags:
These bit field members can also be toggled using the ``flags`` member of :cpp:type:`twai_message_t` and the following message flags:
.. list-table::
:widths: 30 70
@ -502,7 +510,7 @@ The following code demonstrates how to stop and uninstall the TWAI driver via th
Multiple ID Filter Configuration
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The acceptance mask in :cpp:type:`twai_filter_config_t` can be configured such that two or more IDs will be accepted for a single filter. For a particular filter to accept multiple IDs, the conflicting bit positions amongst the IDs must be set in the acceptance mask. The acceptance code can be set to any one of the IDs.
The acceptance mask in :cpp:type:`twai_filter_config_t` can be configured such that two or more IDs are accepted for a single filter. For a particular filter to accept multiple IDs, the conflicting bit positions amongst the IDs must be set in the acceptance mask. The acceptance code can be set to any one of the IDs.
The following example shows how the calculate the acceptance mask given multiple IDs::

View File

@ -14,7 +14,7 @@ Each UART controller is independently configurable with parameters such as baud
.. only:: SOC_UART_LP_NUM
Additionally, the {IDF_TARGET_NAME} chip has one low-power (LP) UART controller. It is the cut-down version of regular UART. Usually, the LP UART controller only support basic UART functionality with a much smaller RAM size, and does not support IrDA or RS485 protocols. For a full list of difference between UART and LP UART, please refer to the *{IDF_TARGET_NAME} Technical Reference Manual* > *UART Controller (UART)* > *Features* [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__]).
Additionally, the {IDF_TARGET_NAME} chip has one low-power (LP) UART controller. It is the cut-down version of regular UART. Usually, the LP UART controller only support basic UART functionality with a much smaller RAM size, and does not support IrDA or RS485 protocols. For a full list of difference between UART and LP UART, please refer to the **{IDF_TARGET_NAME} Technical Reference Manual** > **UART Controller (UART)** > **Features** [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__]).
Functional Overview
-------------------
@ -94,7 +94,7 @@ Each of the above functions has a ``_get_`` counterpart to check the currently s
Set Communication Pins
^^^^^^^^^^^^^^^^^^^^^^^^^^
After setting communication parameters, configure the physical GPIO pins to which the other UART device will be connected. For this, call the function :cpp:func:`uart_set_pin` and specify the GPIO pin numbers to which the driver should route the Tx, Rx, RTS, and CTS signals. If you want to keep a currently allocated pin number for a specific signal, pass the macro :c:macro:`UART_PIN_NO_CHANGE`.
After setting communication parameters, configure the physical GPIO pins to which the other UART device will be connected. For this, call the function :cpp:func:`uart_set_pin` and specify the GPIO pin numbers to which the driver should route the TX, RX, RTS, and CTS signals. If you want to keep a currently allocated pin number for a specific signal, pass the macro :c:macro:`UART_PIN_NO_CHANGE`.
The same macro :c:macro:`UART_PIN_NO_CHANGE` should be specified for pins that will not be used.
@ -110,12 +110,12 @@ Install Drivers
Once the communication pins are set, install the driver by calling :cpp:func:`uart_driver_install` and specify the following parameters:
- Size of Tx ring buffer
- Size of Rx ring buffer
- Size of TX ring buffer
- Size of RX ring buffer
- Event queue handle and size
- Flags to allocate an interrupt
The function will allocate the required internal resources for the UART driver.
The function allocates the required internal resources for the UART driver.
.. code-block:: c
@ -138,23 +138,23 @@ Serial communication is controlled by each UART controller's finite state machin
The process of sending data involves the following steps:
1. Write data into Tx FIFO buffer
1. Write data into TX FIFO buffer
2. FSM serializes the data
3. FSM sends the data out
The process of receiving data is similar, but the steps are reversed:
1. FSM processes an incoming serial stream and parallelizes it
2. FSM writes the data into Rx FIFO buffer
3. Read the data from Rx FIFO buffer
2. FSM writes the data into RX FIFO buffer
3. Read the data from RX FIFO buffer
Therefore, an application will only write and read data from a specific buffer using :cpp:func:`uart_write_bytes` and :cpp:func:`uart_read_bytes` respectively, and the FSM will do the rest.
Therefore, an application only writes and reads data from a specific buffer using :cpp:func:`uart_write_bytes` and :cpp:func:`uart_read_bytes` respectively, and the FSM does the rest.
Transmit Data
"""""""""""""
After preparing the data for transmission, call the function :cpp:func:`uart_write_bytes` and pass the data buffer's address and data length to it. The function will copy the data to the Tx ring buffer (either immediately or after enough space is available), and then exit. When there is free space in the Tx FIFO buffer, an interrupt service routine (ISR) moves the data from the Tx ring buffer to the Tx FIFO buffer in the background. The code below demonstrates the use of this function.
After preparing the data for transmission, call the function :cpp:func:`uart_write_bytes` and pass the data buffer's address and data length to it. The function copies the data to the TX ring buffer (either immediately or after enough space is available), and then exit. When there is free space in the TX FIFO buffer, an interrupt service routine (ISR) moves the data from the TX ring buffer to the TX FIFO buffer in the background. The code below demonstrates the use of this function.
.. code-block:: c
@ -162,16 +162,16 @@ After preparing the data for transmission, call the function :cpp:func:`uart_wri
char* test_str = "This is a test string.\n";
uart_write_bytes(uart_num, (const char*)test_str, strlen(test_str));
The function :cpp:func:`uart_write_bytes_with_break` is similar to :cpp:func:`uart_write_bytes` but adds a serial break signal at the end of the transmission. A 'serial break signal' means holding the Tx line low for a period longer than one data frame.
The function :cpp:func:`uart_write_bytes_with_break` is similar to :cpp:func:`uart_write_bytes` but adds a serial break signal at the end of the transmission. A 'serial break signal' means holding the TX line low for a period longer than one data frame.
.. code-block:: c
// Write data to UART, end with a break signal.
uart_write_bytes_with_break(uart_num, "test break\n",strlen("test break\n"), 100);
Another function for writing data to the Tx FIFO buffer is :cpp:func:`uart_tx_chars`. Unlike :cpp:func:`uart_write_bytes`, this function will not block until space is available. Instead, it will write all data which can immediately fit into the hardware Tx FIFO, and then return the number of bytes that were written.
Another function for writing data to the TX FIFO buffer is :cpp:func:`uart_tx_chars`. Unlike :cpp:func:`uart_write_bytes`, this function does not block until space is available. Instead, it writes all data which can immediately fit into the hardware TX FIFO, and then return the number of bytes that were written.
There is a 'companion' function :cpp:func:`uart_wait_tx_done` that monitors the status of the Tx FIFO buffer and returns once it is empty.
There is a 'companion' function :cpp:func:`uart_wait_tx_done` that monitors the status of the TX FIFO buffer and returns once it is empty.
.. code-block:: c
@ -183,7 +183,7 @@ There is a 'companion' function :cpp:func:`uart_wait_tx_done` that monitors the
Receive Data
""""""""""""
Once the data is received by the UART and saved in the Rx FIFO buffer, it needs to be retrieved using the function :cpp:func:`uart_read_bytes`. Before reading data, you can check the number of bytes available in the Rx FIFO buffer by calling :cpp:func:`uart_get_buffered_data_len`. An example of using these functions is given below.
Once the data is received by the UART and saved in the RX FIFO buffer, it needs to be retrieved using the function :cpp:func:`uart_read_bytes`. Before reading data, you can check the number of bytes available in the RX FIFO buffer by calling :cpp:func:`uart_get_buffered_data_len`. An example of using these functions is given below.
.. code-block:: c
@ -194,7 +194,7 @@ Once the data is received by the UART and saved in the Rx FIFO buffer, it needs
ESP_ERROR_CHECK(uart_get_buffered_data_len(uart_num, (size_t*)&length));
length = uart_read_bytes(uart_num, data, length, 100);
If the data in the Rx FIFO buffer is no longer needed, you can clear the buffer by calling :cpp:func:`uart_flush`.
If the data in the RX FIFO buffer is no longer needed, you can clear the buffer by calling :cpp:func:`uart_flush`.
Software Flow Control
@ -206,7 +206,7 @@ If the hardware flow control is disabled, you can manually set the RTS and DTR s
Communication Mode Selection
""""""""""""""""""""""""""""
The UART controller supports a number of communication modes. A mode can be selected using the function :cpp:func:`uart_set_mode`. Once a specific mode is selected, the UART driver will handle the behavior of a connected UART device accordingly. As an example, it can control the RS485 driver chip using the RTS line to allow half-duplex RS485 communication.
The UART controller supports a number of communication modes. A mode can be selected using the function :cpp:func:`uart_set_mode`. Once a specific mode is selected, the UART driver handles the behavior of a connected UART device accordingly. As an example, it can control the RS485 driver chip using the RTS line to allow half-duplex RS485 communication.
.. code-block:: bash
@ -221,13 +221,13 @@ Use Interrupts
There are many interrupts that can be generated depending on specific UART states or detected errors. The full list of available interrupts is provided in *{IDF_TARGET_NAME} Technical Reference Manual* > *UART Controller (UART)* > *UART Interrupts* and *UHCI Interrupts* [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__]. You can enable or disable specific interrupts by calling :cpp:func:`uart_enable_intr_mask` or :cpp:func:`uart_disable_intr_mask` respectively.
The :cpp:func:`uart_driver_install` function installs the driver's internal interrupt handler to manage the Tx and Rx ring buffers and provides high-level API functions like events (see below).
The :cpp:func:`uart_driver_install` function installs the driver's internal interrupt handler to manage the TX and RX ring buffers and provides high-level API functions like events (see below).
The API provides a convenient way to handle specific interrupts discussed in this document by wrapping them into dedicated functions:
- **Event detection**: There are several events defined in :cpp:type:`uart_event_type_t` that may be reported to a user application using the FreeRTOS queue functionality. You can enable this functionality when calling :cpp:func:`uart_driver_install` described in :ref:`uart-api-driver-installation`. An example of using Event detection can be found in :example:`peripherals/uart/uart_events`.
- **FIFO space threshold or transmission timeout reached**: The Tx and Rx FIFO buffers can trigger an interrupt when they are filled with a specific number of characters, or on a timeout of sending or receiving data. To use these interrupts, do the following:
- **FIFO space threshold or transmission timeout reached**: The TX and RX FIFO buffers can trigger an interrupt when they are filled with a specific number of characters, or on a timeout of sending or receiving data. To use these interrupts, do the following:
- Configure respective threshold values of the buffer length and timeout by entering them in the structure :cpp:type:`uart_intr_config_t` and calling :cpp:func:`uart_intr_config`
- Enable the interrupts using the functions :cpp:func:`uart_enable_tx_intr` and :cpp:func:`uart_enable_rx_intr`
@ -258,7 +258,7 @@ Overview of RS485 Specific Communication 0ptions
.. note::
The following section will use ``[UART_REGISTER_NAME].[UART_FIELD_BIT]`` to refer to UART register fields/bits. For more information on a specific option bit, see *{IDF_TARGET_NAME} Technical Reference Manual* > *UART Controller (UART)* > *Register Summary* [`PDF <{IDF_TARGET_TRM_EN_URL}#uart-reg-summ>`__]. Use the register name to navigate to the register description and then find the field/bit.
The following section uses ``[UART_REGISTER_NAME].[UART_FIELD_BIT]`` to refer to UART register fields/bits. For more information on a specific option bit, see **{IDF_TARGET_NAME} Technical Reference Manual** > **UART Controller (UART)** > **Register Summary** [`PDF <{IDF_TARGET_TRM_EN_URL}#uart-reg-summ>`__]. Use the register name to navigate to the register description and then find the field/bit.
- ``UART_RS485_CONF_REG.UART_RS485_EN``: setting this bit enables RS485 communication mode support.
- ``UART_RS485_CONF_REG.UART_RS485TX_RX_EN``: if this bit is set, the transmitter's output signal loops back to the receiver's input signal.
@ -272,7 +272,7 @@ The collision detection feature can work with circuit A and circuit C (see Secti
The {IDF_TARGET_NAME} UART controllers themselves do not support half-duplex communication as they cannot provide automatic control of the RTS pin connected to the RE/DE input of RS485 bus driver. However, half-duplex communication can be achieved via software control of the RTS pin by the UART driver. This can be enabled by selecting the :cpp:enumerator:`UART_MODE_RS485_HALF_DUPLEX` mode when calling :cpp:func:`uart_set_mode`.
Once the host starts writing data to the Tx FIFO buffer, the UART driver automatically asserts the RTS pin (logic 1); once the last bit of the data has been transmitted, the driver de-asserts the RTS pin (logic 0). To use this mode, the software would have to disable the hardware flow control function. This mode works with all the used circuits shown below.
Once the host starts writing data to the TX FIFO buffer, the UART driver automatically asserts the RTS pin (logic 1); once the last bit of the data has been transmitted, the driver de-asserts the RTS pin (logic 0). To use this mode, the software would have to disable the hardware flow control function. This mode works with all the used circuits shown below.
Interface Connection Options
@ -394,7 +394,7 @@ API Reference
GPIO Lookup Macros
^^^^^^^^^^^^^^^^^^
The UART peripherals have dedicated IO_MUX pins to which they are connected directly. However, signals can also be routed to other pins using the less direct GPIO matrix. To use direct routes, you need to know which pin is a dedicated IO_MUX pin for a UART channel. GPIO Lookup Macros simplify the process of finding and assigning IO_MUX pins. You choose a macro based on either the IO_MUX pin number, or a required UART channel name, and the macro will return the matching counterpart for you. See some examples below.
The UART peripherals have dedicated IO_MUX pins to which they are connected directly. However, signals can also be routed to other pins using the less direct GPIO matrix. To use direct routes, you need to know which pin is a dedicated IO_MUX pin for a UART channel. GPIO Lookup Macros simplify the process of finding and assigning IO_MUX pins. You choose a macro based on either the IO_MUX pin number, or a required UART channel name, and the macro returns the matching counterpart for you. See some examples below.
.. note::

View File

@ -37,7 +37,7 @@ The layers of the Host Stack are described in the following table. The layers ar
- ``usb_host.h``, ``usb_host.c``
- The USB Host Library layer is the lowest public API layer of the Host Stack and presents the concept of USB Host Clients. The abstraction of clients allows for multiple class drivers to coexist simultaneously (where each class roughly maps to a single client) and also acts as a mechanism for division of labor (where each client is responsible for its own processing and event handling).
* - Host Class Drivers
- See the `IDF Extra Components repository <https://github.com/espressif/idf-extra-components>`_ or the USB Host examples in ESP-IDF (via :example:`peripherals/usb/host`).
- See the `ESP-IDF Extra Components repository <https://github.com/espressif/idf-extra-components>`_ or the USB Host examples in ESP-IDF (via :example:`peripherals/usb/host`).
- The Host Class Drivers implement the host side of a particular device class (e.g., CDC, MSC, HID). The exposed API is specific to each class driver.
Layer Dependencies

View File

@ -28,23 +28,23 @@ DMA Support
**Requirement: The Host Stack must support DMA.**
The Host Stack must support DMA in order to reduce CPU's workload. DMA support will allow the automatic copying of USB transfer data to/from the Host Controller without CPU intervention. This is especially critical given the embedded nature of the CPU (i.e., lower CPU frequencies) and large maximum data payloads of USB transfers (e.g., 1023 bytes for isochronous transfers).
The Host Stack must support DMA in order to reduce CPU's workload. DMA support allows the automatic copying of USB transfer data to/from the Host Controller without CPU intervention. This is especially critical given the embedded nature of the CPU (i.e., lower CPU frequencies) and large maximum data payloads of USB transfers (e.g., 1023 bytes for isochronous transfers).
Minimize Memory Copies
^^^^^^^^^^^^^^^^^^^^^^
**Requirement: The Host Stack should minimize the amount of memory copies when passing data between layers.**
Various data and objects (e.g., USB transfers) will need to be passed between multiple layers of the Host Stack. The Host Stack should minimize the amount of memory copies that occur between layers by allocating the data's/object's memory once, and simply passing a pointer to that data/object between the layers. Therefore, the Host Stack will require some standardized data types shared across multiple layers (see USB2.0 Section 10.3.4).
Various data and objects (e.g., USB transfers) need to be passed between multiple layers of the Host Stack. The Host Stack should minimize the amount of memory copies that occur between layers by allocating the data's/object's memory once, and simply passing a pointer to that data/object between the layers. Therefore, the Host Stack requires some standardized data types shared across multiple layers (see USB2.0 Section 10.3.4).
Event Driven
^^^^^^^^^^^^
**Requirement: The Host Stack must allow for event driven operation (i.e., the Host Stack's API must not be polling).**
The Host Stack will need to support some CPU intensive use application scenarios such as video streaming (i.e., UVC class). Therefore, the Host Stack should minimize CPU usage by allowing for completely event driven operation, thus reserving the majority of CPU time for the application itself (i.e., video encoding/decoding in this case).
The Host Stack needs to support some CPU intensive use application scenarios such as video streaming (i.e., UVC class). Therefore, the Host Stack should minimize CPU usage by allowing for completely event driven operation, thus reserving the majority of CPU time for the application itself (i.e., video encoding/decoding in this case).
The Host Stack will need to communicate events across the layers using interrupts, callbacks, and FreeRTOS synchronization primitives (e.g., queues and semaphores).
The Host Stack needs to communicate events across the layers using interrupts, callbacks, and FreeRTOS synchronization primitives (e.g., queues and semaphores).
No Task Creation
^^^^^^^^^^^^^^^^
@ -54,9 +54,9 @@ No Task Creation
Task stacks are generally one of the most memory intensive parts of an ESP-IDF applications. Given the wide range of applications scenarios, the number of tasks created (and their stack sizes) can vary greatly. For example...
- applications that require low latency or high throughput (such as isochronous transfers) may choose to create a dedicated task to handle those transfers in order to minimize latency.
- applications that don't have strict latency requirements (such as bulk transfers) may choose to handle those transfers from a shared task in order to save some memory.
- applications that do not have strict latency requirements (such as bulk transfers) may choose to handle those transfers from a shared task in order to save some memory.
Therefore, all layers of the Host Stack below (and including) the Host Library layer **must not** create any tasks.Instead, these layers should expose handlers functions to be called from tasks created by the upper layers. Task creation will be delegated to the class driver or application layer.
Therefore, all layers of the Host Stack below (and including) the Host Library layer **must not** create any tasks. Instead, these layers should expose handlers functions to be called from tasks created by the upper layers. Task creation will be delegated to the class driver or application layer.
Operable at Different Layers
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -66,7 +66,7 @@ Given the wide range of use case complexities, the Host Stack must be operable a
Being operable at different layers allows the users to decide on the appropriate trade-off between convenience, control, and optimization for their application when using the Host Stack. For example...
- Host Stack applications that support a dedicated custom device may want to use a lower level of abstraction for better optimization, control, and simpler API.
- Host Stack applications that need to support a wide range of device classes will require the full Host Stack so that device enumeration is automatically handled.
- Host Stack applications that need to support a wide range of device classes requires the full Host Stack so that device enumeration is automatically handled.
Coding Conventions
------------------

View File

@ -58,7 +58,7 @@ The destination FIFO depends on the direction and transfer type of the channel:
.. note::
The separation of non-periodic and periodic OUT channels to the NPTX and PTX FIFOs is due to the periodic nature of Interrupt and Isochronous endpoints (specified by the ``bInterval`` value of the endpoint). The DWC_OTG will automatically schedule these periodic transfers, thus a separate PTX FIFO allows these periodic transfers to be staged separately.
The separation of non-periodic and periodic OUT channels to the NPTX and PTX FIFOs is due to the periodic nature of Interrupt and Isochronous endpoints (specified by the ``bInterval`` value of the endpoint). The DWC_OTG automatically schedules these periodic transfers, thus a separate PTX FIFO allows these periodic transfers to be staged separately.
DMA Engine
^^^^^^^^^^
@ -75,7 +75,7 @@ The DMA engine is responsible for copying data between the FIFOs and main memory
- A 32-bit Buffer Status Quadlet specifying details of the transfer, and also reports the status of the transfer on completion. The Buffer Status Quadlet has bits to specify whether the QTD should generate an interrupt and/or halt the channel on completion.
- A 32-bit pointer to the data buffer containing the data payload for OUT transfers, or an empty buffer used to store the data payload for IN transfers.
- The data payload of each QTD can be larger than the MPS (Maximum Packet Size) of its target endpoint. The DWC_OTG hardware will automatically handle splitting of the transfer into multiple transactions.
- The data payload of each QTD can be larger than the MPS (Maximum Packet Size) of its target endpoint. The DWC_OTG hardware automatically handles splitting of the transfer into multiple transactions.
- Multiple QTDs can be placed into a single QTD List. A channel will then execute each QTD in the list automatically, and optionally loop back around if configured to do so.
- Before a channel starts data transfer, it is configured with a QTD list (and QTD list length). Once the channel is enabled, USB transfers are executed automatically by the hardware.
- A channel can generate interrupts (configurable) on completion of a particular QTD, or an entire QTD list.
@ -128,17 +128,17 @@ However, there are some minor differences in channel operation and QTD list usag
Bulk
^^^^
Bulk transfers are a simplest. Each QTD represents a bulk transfer of a particular direction, where the DWC_OTG will automatically split a particular QTD into multiple MPS sized transactions. Thus it is possible to fill a QTD list with multiple bulk transfers, and have the entire list executed automatically (i.e., only interrupt on completion of the last QTD).
Bulk transfers are a simplest. Each QTD represents a bulk transfer of a particular direction, where the DWC_OTG automatically splits a particular QTD into multiple MPS sized transactions. Thus it is possible to fill a QTD list with multiple bulk transfers, and have the entire list executed automatically (i.e., only interrupt on completion of the last QTD).
Control
^^^^^^^
Control transfers are more complicated as they are bi-directional (i.e., each control transfer stage can have a different direction). Thus, a separate QTD is required for each stage, and each QTD must halt the channel on completion. Halting the channel after each QTD allows changing the channel's direction to be changed by reconfiguring the channel's CSRs. Thus a typical control transfer will consist of 3 QTDs (one for each stage).
Control transfers are more complicated as they are bi-directional (i.e., each control transfer stage can have a different direction). Thus, a separate QTD is required for each stage, and each QTD must halt the channel on completion. Halting the channel after each QTD allows changing the channel's direction to be changed by reconfiguring the channel's CSRs. Thus a typical control transfer consists of 3 QTDs (one for each stage).
Interrupt
^^^^^^^^^
In accordance with the USB2.0 specification, interrupt transfers will execute transactions at the endpoints specified service period (i.e., ``bInterval``). A particular interrupt endpoint may not execute more than one interrupt transaction within a service period. The service period is specified in number of (micro)frames, thus a particular interrupt endpoint will generally execute one transaction every Nth (micro)frame until the transfer is complete. For interrupt channels, the service period of a particular channel (i.e., ``bInterval``) is specified via the Host Frame List (see section 6.5 of programming guide for more details).
In accordance with the USB2.0 specification, interrupt transfers executes transactions at the endpoints specified service period (i.e., ``bInterval``). A particular interrupt endpoint may not execute more than one interrupt transaction within a service period. The service period is specified in number of (micro)frames, thus a particular interrupt endpoint will generally execute one transaction every Nth (micro)frame until the transfer is complete. For interrupt channels, the service period of a particular channel (i.e., ``bInterval``) is specified via the Host Frame List (see section 6.5 of programming guide for more details).
.. note::
@ -149,8 +149,8 @@ Thus, interrupt transfers in Host Mode Scatter/Gather DMA have the following pec
- If a QTD payload is larger than the endpoint's MPS, the channel will automatically split the transfer into multiple MPS sized transactions (similar to bulk transfers). However, each transaction **is executed at endpoint's specified service period** (i.e., one transaction per ``bInterval``) until the transfer completes.
- For Interrupt IN transfers, if a short packet is received (i.e., transaction's data payload is < MPS), this indicates that the endpoint has no more data to send. In this case:
- the channel will generate an extra channel interrupt even if the transfer's QTD did not set the IOC (interrupt on complete) bit.
- however, the channel will not be halted even if this extra channel interrupt is generated.
- the channel generates an extra channel interrupt even if the transfer's QTD did not set the IOC (interrupt on complete) bit.
- however, the channel is not halted even if this extra channel interrupt is generated.
- software must then use this extra interrupt to manually halt the interrupt channel (thus canceling any remaining QTDs in the QTD list).
@ -161,9 +161,9 @@ Thus, interrupt transfers in Host Mode Scatter/Gather DMA have the following pec
Isochronous
^^^^^^^^^^^
In accordance with the USB2.0 specification, isochronous transfers will execute transactions at the endpoints specified service period (i.e., ``bInterval``) in order to achieve a constant rate of data transfer. A particular isochronous endpoint may not execute more than one isochronous transaction within a service period. The service period is specified in number of (micro)frames, thus a particular isochronous endpoint will generally execute one transaction every Nth (micro)frame until the transfer is complete. For isochronous channels, the service period of a particular channel (i.e., ``bInterval``) is specified via the Host Frame List (see section 6.5 of programming guide for more details).
In accordance with the USB2.0 specification, isochronous transfers executes transactions at the endpoints specified service period (i.e., ``bInterval``) in order to achieve a constant rate of data transfer. A particular isochronous endpoint may not execute more than one isochronous transaction within a service period. The service period is specified in number of (micro)frames, thus a particular isochronous endpoint will generally execute one transaction every Nth (micro)frame until the transfer is complete. For isochronous channels, the service period of a particular channel (i.e., ``bInterval``) is specified via the Host Frame List (see section 6.5 of programming guide for more details).
However, unlike interrupt transactions, isochronous transactions will not be retried on failure (or NAK), due to the need to maintain the constant data rate.
However, unlike interrupt transactions, isochronous transactions are not retried on failure (or NAK), due to the need to maintain the constant data rate.
.. note::
@ -171,7 +171,7 @@ However, unlike interrupt transactions, isochronous transactions will not be ret
Thus, isochronous transfers in Host Mode Scatter/Gather DMA have the following peculiarities:
- A QTD must be allocated for each (micro)frame. However, non-service service period QTDs should be left blank (i.e., only ever Nth QTD should be filled if the channel's service period is every Nth (micro)frame).
- A QTD must be allocated for each (micro) frame. However, non-service service period QTDs should be left blank (i.e., only ever Nth QTD should be filled if the channel's service period is every Nth (micro)frame).
- **Each filled QTD must represent a single transaction instead of a transfer**.
- Because isochronous transactions are not retried on failure, the status each completed QTD must be checked.
@ -185,8 +185,8 @@ Port Errors Do Not Trigger a Channel Interrupt
If a port error occurs (such as a sudden disconnection or port over-current) while there are one or more active channels...
- The active channels will remain active (i.e., ``HCCHAR.ChEna`` will remain set) and no channel interrupts are generated.
- Channels could in theory be disabled by setting ``HCCHAR.ChDis``, but this will not work for Isochronous channels as the channel disabled interrupt is never generated
- The active channels remains active (i.e., ``HCCHAR.ChEna`` remains set) and no channel interrupts are generated.
- Channels could in theory be disabled by setting ``HCCHAR.ChDis``, but this does not work for Isochronous channels as the channel disabled interrupt is never generated.
Therefore, on port errors, a controller soft reset should be used to ensure all channels are disabled.

View File

@ -4,6 +4,7 @@ USB Host Maintainers Notes (Introduction)
This document contains information regarding the implementation details of the USB Host stack. This document is intended for the maintainers and third-party contributors of the USB Host stack. Users of the USB Host stack should refer to :doc:`../usb_host` instead.
.. warning::
The implementations details of the USB Host stack is categorized as private API. Thus, all layers (other than the USB Host Library) do not adhere to :ref:`ESP-IDF's versioning scheme <versioning-scheme>` (i.e., breaking changes are permitted).
.. figure:: ../../../../_static/usb_host/stack-overview.png

View File

@ -12,9 +12,10 @@
创建/销毁 GPIO 捆绑包
--------------------------
GPIO 捆绑包是一组 GPIO该组 GPIO 可以在一个 CPU 周期内同时操作。一个包能够包含 GPIO 的最大数量受每个 CPU 的限制。另外GPIO 捆绑包与派生它的 CPU 有很强的相关性。 **注意,任何对 GPIO 捆绑包操作的任务都必须运行在 GPIO 捆绑包所属的 CPU 内核。** 同理,只有那些安装在同一个 CPU 内核上的 ISR 才允许对该 GPIO 捆绑包进行操作。
GPIO 捆绑包是一组 GPIO该组 GPIO 可以在一个 CPU 周期内同时操作。一个包能够包含 GPIO 的最大数量受每个 CPU 的限制。另外GPIO 捆绑包与派生它的 CPU 有很强的相关性。**注意,任何对 GPIO 捆绑包操作的任务都必须运行在 GPIO 捆绑包所属的 CPU 内核。** 同理,只有那些安装在同一个 CPU 内核上的 ISR 才允许对该 GPIO 捆绑包进行操作。
.. note::
专用 GPIO 更像是 CPU 外设,因此与 CPU 内核关系密切。强烈建议在 pin-to-core 任务中安装和操作 GPIO 捆绑包。例如,如果 GPIOA 连接到了 CPU0而专用的 GPIO 指令却是从 CPU1 发出的,那么就无法控制 GPIOA。
安装 GPIO 捆绑包需要调用 :cpp:func:`dedic_gpio_new_bundle` 来分配软件资源并将专用通道连接到用户选择的 GPIO。GPIO 捆绑包的配置在 :cpp:type:`dedic_gpio_bundle_config_t` 结构体中:
@ -55,7 +56,8 @@ GPIO 捆绑包是一组 GPIO该组 GPIO 可以在一个 CPU 周期内同时
如需卸载 GPIO 捆绑包,可调用 :cpp:func:`dedic_gpio_del_bundle`
.. note::
:cpp:func:`dedic_gpio_new_bundle` 不包含任何 GPIO pad 配置(例如上拉/下拉、驱动能力、输出/输入使能)。因此,在安装专用 GPIO 捆绑包之前,您必须使用 GPIO 驱动程序 API:cpp:func:`gpio_config`)单独配置 GPIO。更多关于 GPIO 驱动的信息,请参考 :doc:`GPIO API 参考 <gpio>`
:cpp:func:`dedic_gpio_new_bundle` 不包含任何 GPIO pad 配置(例如上拉/下拉、驱动能力、输出/输入使能)。因此,在安装专用 GPIO 捆绑包之前,必须使用 GPIO 驱动程序 API:cpp:func:`gpio_config`)单独配置 GPIO。更多关于 GPIO 驱动的信息,请参考 :doc:`GPIO API 参考 <gpio>`
GPIO 捆绑包操作
@ -75,6 +77,7 @@ GPIO 捆绑包操作
- :cpp:func:`dedic_gpio_bundle_read_in`
.. note::
由于函数调用的开销和内部涉及的位操作,使用上述函数可能无法获得较高的 GPIO 翻转速度。用户可以尝试 :ref:`manipulate_gpios_by_writing_assembly_code` 来减少开销,但应自行注意线程安全。
.. _manipulate_gpios_by_writing_assembly_code:
@ -103,21 +106,22 @@ GPIO 捆绑包操作
.. only:: esp32s2
有关支持的专用 GPIO 指令的详细信息,请参考 *{IDF_TARGET_NAME} 技术参考手册* > *IO MUX 和 GPIO 矩阵 (GPIO, IO_MUX)* [`PDF <{IDF_TARGET_TRM_CN_URL}#iomuxgpio>`__].
有关支持的专用 GPIO 指令的详细信息,请参考 **{IDF_TARGET_NAME} 技术参考手册** > **IO MUX 和 GPIO 矩阵 (GPIO, IO_MUX)** [`PDF <{IDF_TARGET_TRM_CN_URL}#iomuxgpio>`__].
.. only:: esp32s3
有关支持的专用 GPIO 指令的详细信息,请参考 *{IDF_TARGET_NAME} 技术参考手册* > *处理器指令拓展 (PIE)(稍后发布)* [`PDF <{IDF_TARGET_TRM_CN_URL}#pie>`__].
有关支持的专用 GPIO 指令的详细信息,请参考 **{IDF_TARGET_NAME} 技术参考手册** > **处理器指令拓展 (PIE)(稍后发布)** [`PDF <{IDF_TARGET_TRM_CN_URL}#pie>`__].
.. only:: esp32c2 or esp32c3 or esp32c6
通过汇编操作专用 GPIO 的示例代码存放在 ESP-IDF 示例项目的 :example:`peripherals/dedicated_gpio` 目录下。示例演示了如何通过汇编操作专用 GPIO 来模拟 UART、I2C 和 SPI 总线。
有关支持的专用 GPIO 指令的详细信息,请参考 *{IDF_TARGET_NAME} 技术参考手册* > *ESP-RISC-V CPU* [`PDF <{IDF_TARGET_TRM_CN_URL}#riscvcpu>`__]。
有关支持的专用 GPIO 指令的详细信息,请参考 **{IDF_TARGET_NAME} 技术参考手册** > **ESP-RISC-V CPU** [`PDF <{IDF_TARGET_TRM_CN_URL}#riscvcpu>`__]。
一些专用的 CPU 指令也包含在 `hal/dedic_gpio_cpu_ll.h` 中,作为辅助内联函数。
一些专用的 CPU 指令也包含在 ``hal/dedic_gpio_cpu_ll.h`` 中,作为辅助内联函数。
.. note::
由于自定义指令在不同目标上可能会有不同的格式,在应用程序中编写汇编代码可能会让代码难以在不同的芯片架构之间移植。
.. only:: SOC_DEDIC_GPIO_HAS_INTERRUPT

View File

@ -38,7 +38,7 @@
不同的 ESP 芯片可能有不同数量的独立定时器组,每组内也可能有若干个独立定时器。[1]_
通用定时器实例由 :cpp:type:`gptimer_handle_t` 表示。后台驱动会在资源池中管理所有可用的硬件资源,这样您便无需考虑硬件所属的定时器以及定时器组。
通用定时器实例由 :cpp:type:`gptimer_handle_t` 表示。可用硬件资源汇集在资源池内,由后台驱动程序管理,无需考虑硬件所属的定时器以及定时器组。
要安装一个定时器实例,需要提前提供配置结构体 :cpp:type:`gptimer_config_t`
@ -108,7 +108,7 @@
- :cpp:member:`gptimer_event_callbacks_t::on_alarm` 设置警报事件的回调函数。由于此函数在 ISR 上下文中调用,必须确保该函数不会试图阻塞(例如,确保仅从函数内调用具有 ``ISR`` 后缀的 FreeRTOS API。函数原型在 :cpp:type:`gptimer_alarm_cb_t` 中有所声明。
也可以通过参数 ``user_data`` 将自己的上下文保存到 :cpp:func:`gptimer_register_event_callbacks` 中。用户数据将直接传递给回调函数。
也可以通过参数 ``user_data``,将自己的上下文保存到 :cpp:func:`gptimer_register_event_callbacks` 中。用户数据将直接传递给回调函数。
此功能将为定时器延迟安装中断服务,但不使能中断服务。所以,请在 :cpp:func:`gptimer_enable` 之前调用这一函数,否则将返回 :c:macro:`ESP_ERR_INVALID_STATE` 错误。了解详细信息,请查看章节 :ref:`enable-and-disable-timer`
@ -131,7 +131,8 @@
^^^^^^^^^^^^^^^^
启动和停止是定时器的基本 IO 操作。调用 :cpp:func:`gptimer_start` 可以使内部计数器开始工作,而 :cpp:func:`gptimer_stop` 可以使计数器停止工作。下文说明了如何在存在或不存在警报事件的情况下启动定时器。
调用 :cpp:func:`gptimer_start` 将使驱动程序状态从 enable 转换为 run, 反之亦然。您需要确保 start 和 stop 函数成对使用,否则,函数可能返回 :c:macro:`ESP_ERR_INVALID_STATE`
调用 :cpp:func:`gptimer_start` 将使驱动程序状态从 enable 转换为 run, 反之亦然。注意确保 start 和 stop 函数成对使用,否则,函数可能返回 :c:macro:`ESP_ERR_INVALID_STATE`
将定时器作为挂钟启动
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -315,7 +316,7 @@ IRAM 安全
线程安全
^^^^^^^^
驱动提供的所有 API 都是线程安全的,这意味着您可以从不同的 RTOS 任务中调用这些函数,而无需额外的互斥锁去保护。以下这些函数还被允许在中断上下文中运行。
驱动提供的所有 API 都是线程安全的。使用时,可以直接从不同的 RTOS 任务中调用此类函数,无需额外锁保护。以下这些函数还支持在中断上下文中运行。
- :cpp:func:`gptimer_start`
- :cpp:func:`gptimer_stop`
@ -351,7 +352,7 @@ API 参考
.. include-build-file:: inc/timer_types.inc
.. [1]
不同 ESP 芯片系列的通用定时器实例数量可能不同。了解详细信息,请参考《{IDF_TARGET_NAME} 技术参考手册》 > 章节定时器组 (TIMG) [`PDF <{IDF_TARGET_TRM_CN_URL}#timg>`__]。驱动程序不会禁止您申请更多的定时器,但是当所有可用的硬件资源用完时将会返回错误。在分配资源时,请务必检查返回值(例如 :cpp:func:`gptimer_new_timer`)。
不同 ESP 芯片系列的通用定时器实例数量可能不同。了解详细信息,请参考《{IDF_TARGET_NAME} 技术参考手册》 > 章节定时器组 (TIMG) [`PDF <{IDF_TARGET_TRM_CN_URL}#timg>`__]。驱动程序对通道申请数量不做限制,但当硬件资源用尽时,驱动程序将返回错误。在分配资源时,请务必检查返回值(例如 :cpp:func:`gptimer_new_timer`)。
.. [2]
:cpp:member:`gptimer_event_callbacks_t::on_alarm` 回调函数和这一函数调用的函数也需放在 IRAM 中,请自行处理。

View File

@ -31,7 +31,7 @@ I2C 驱动程序管理在 I2C 总线上设备的通信,该驱动程序具备
{IDF_TARGET_I2C_ROLE:default="主机或从机", esp32c2="主机"}
以下部分将指导您完成 I2C 驱动程序配置和工作的基本步骤:
I2C 驱动程序配置和工作的基本步骤如下
1. :ref:`i2c-api-configure-driver` - 设置初始化参数如主机模式或从机模式SDA 和 SCL 使用的 GPIO 管脚,时钟速度等)
2. :ref:`i2c-api-install-driver`- 激活一个 I2C 控制器的驱动,该控制器可为主机也可为从机

View File

@ -1,5 +1,6 @@
I2S
===
:link_to_translation:`en:[English]`
{IDF_TARGET_I2S_NUM:default="1", esp32="2", esp32s3="2"}
@ -195,7 +196,7 @@ ESP32-H2 I2S 0 I2S 0 无 I2S 0 无 无
LCD/摄像头模式
^^^^^^^^^^^^^^^
LCD/摄像头模式只支持在 I2S0 上通过并行总线运行。在 LCD 模式下I2S0 应当设置为主机 TX 模式在摄像头模式下I2S0 应当设置为从机 RX 模式。这两种模式不是由 I2S 驱动实现的,关于 LCD 模式的实现,请参阅 :doc:`/api-reference/peripherals/lcd`。更多信息请参考 *{IDF_TARGET_NAME} 技术参考手册* > *I2S 控制器 (I2S)* > LCD 模式 [`PDF <{IDF_TARGET_TRM_EN_URL}#camlcdctrl>`__]。
LCD/摄像头模式只支持在 I2S0 上通过并行总线运行。在 LCD 模式下I2S0 应当设置为主机 TX 模式在摄像头模式下I2S0 应当设置为从机 RX 模式。这两种模式不是由 I2S 驱动实现的,关于 LCD 模式的实现,请参阅 :doc:`/api-reference/peripherals/lcd`。更多信息请参考 **{IDF_TARGET_NAME} 技术参考手册** > **I2S 控制器 (I2S)** > LCD 模式 [`PDF <{IDF_TARGET_TRM_EN_URL}#camlcdctrl>`__]。
.. only:: SOC_I2S_SUPPORTS_ADC_DAC
@ -245,7 +246,7 @@ I2S 通道有三种状态,分别为 ``registered已注册``、 ``ready
I2S 的数据传输(包括数据发送和接收)由 DMA 实现。在传输数据之前,请调用 :cpp:func:`i2s_channel_enable` 来启用特定的通道。发送或接收的数据达到 DMA 缓冲区的大小时,将触发 ``I2S_OUT_EOF````I2S_IN_SUC_EOF`` 中断。注意DMA 缓冲区的大小不等于 :cpp:member:`i2s_chan_config_t::dma_frame_num`,这里的一帧是指一个 WS 周期内的所有采样数据。因此, ``dma_buffer_size = dma_frame_num * slot_num * slot_bit_width / 8``。传输数据时,可以调用 :cpp:func:`i2s_channel_write` 来输入数据,并把数据从源缓冲区复制到 DMA TX 缓冲区等待传输完成。此过程将重复进行,直到发送的字节数达到配置的大小。接收数据时,用户可以调用函数 :cpp:func:`i2s_channel_read` 来等待接收包含 DMA 缓冲区地址的消息队列,从而将数据从 DMA RX 缓冲区复制到目标缓冲区。
:cpp:func:`i2s_channel_write`:cpp:func:`i2s_channel_read` 都是阻塞函数,在源缓冲区的数据发送完毕前,或是整个目标缓冲区都被加载数据占用时,它们会一直保持等待状态。在等待时间达到最大阻塞时间时,返回 `ESP_ERR_TIMEOUT` 错误。要实现异步发送或接收数据,可以通过 :cpp:func:`i2s_channel_register_event_callback` 注册回调,随即便可在回调函数中直接访问 DMA 缓冲区,无需通过这两个阻塞函数来发送或接收数据。但请注意,该回调是一个中断回调,不要在该回调中添加复杂的逻辑、进行浮点运算或调用不可重入函数。
:cpp:func:`i2s_channel_write`:cpp:func:`i2s_channel_read` 都是阻塞函数,在源缓冲区的数据发送完毕前,或是整个目标缓冲区都被加载数据占用时,它们会一直保持等待状态。在等待时间达到最大阻塞时间时,返回 ``ESP_ERR_TIMEOUT`` 错误。要实现异步发送或接收数据,可以通过 :cpp:func:`i2s_channel_register_event_callback` 注册回调,随即便可在回调函数中直接访问 DMA 缓冲区,无需通过这两个阻塞函数来发送或接收数据。但请注意,该回调是一个中断回调,不要在该回调中添加复杂的逻辑、进行浮点运算或调用不可重入函数。
配置
^^^^

View File

@ -8,8 +8,7 @@ LED PWM 控制器
概述
------------
LED 控制器 (LEDC) 主要用于控制 LED也可产生 PWM 信号用于其他设备的控制。
该控制器有 {IDF_TARGET_SOC_LEDC_CHANNEL_NUM} 路通道,可以产生独立的波形来驱动 RGB LED 等设备。
LED 控制器 (LEDC) 主要用于控制 LED也可产生 PWM 信号用于其他设备的控制。该控制器有 {IDF_TARGET_SOC_LEDC_CHANNEL_NUM} 路通道,可以产生独立的波形,驱动 RGB LED 等设备。
.. only:: esp32
@ -85,11 +84,11 @@ LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实
* - REF_TICK
- 1 MHz
- 高速 / 低速
- 支持动态调频DFS功能
- 支持动态调频 (DFS) 功能
* - RC_FAST_CLK
- ~8 MHz
- ~ 8 MHz
- 低速
- 支持动态调频DFS功能支持Light-sleep模式
- 支持动态调频 (DFS) 功能支持Light-sleep模式
.. only:: esp32s2
@ -105,13 +104,13 @@ LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实
- /
* - REF_TICK
- 1 MHz
- 支持动态调频DFS功能
- 支持动态调频 (DFS) 功能
* - RC_FAST_CLK
- ~8 MHz
- 支持动态调频DFS功能支持Light-sleep模式
- ~ 8 MHz
- 支持动态调频 (DFS) 功能,支持 Light-sleep 模式
* - XTAL_CLK
- 40 MHz
- 支持动态调频DFS功能
- 支持动态调频 (DFS) 功能
.. only:: esp32s3 or esp32c3
@ -126,8 +125,8 @@ LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实
- 80 MHz
- /
* - RC_FAST_CLK
- ~20 MHz
- 支持动态调频DFS功能支持Light-sleep模式
- ~ 20 MHz
- 支持动态调频DFS功能支持 Light-sleep 模式
* - XTAL_CLK
- 40 MHz
- 支持动态调频DFS功能
@ -145,11 +144,11 @@ LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实
- 60 MHz
- /
* - RC_FAST_CLK
- ~20 MHz
- 支持动态调频DFS功能支持Light-sleep模式
- ~ 20 MHz
- 支持动态调频 (DFS) 功能支持Light-sleep模式
* - XTAL_CLK
- 40 MHz
- 支持动态调频DFS功能
- 支持动态调频 (DFS) 功能
.. only:: esp32c6
@ -164,11 +163,11 @@ LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实
- 80 MHz
- /
* - RC_FAST_CLK
- ~20 MHz
- 支持动态调频DFS功能支持Light-sleep模式
- ~ 20 MHz
- 支持动态调频 (DFS) 功能,支持 Light-sleep 模式
* - XTAL_CLK
- 40 MHz
- 支持动态调频DFS功能
- 支持动态调频 (DFS) 功能
.. only:: esp32h2
@ -183,21 +182,21 @@ LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实
- 96 MHz
- /
* - RC_FAST_CLK
- ~8 MHz
- 支持动态调频DFS功能支持Light-sleep模式
- ~ 8 MHz
- 支持动态调频 (DFS) 功能,支持 Light-sleep 模式
* - XTAL_CLK
- 32 MHz
- 支持动态调频DFS功能
- 支持动态调频 (DFS) 功能
.. note::
.. only:: SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
1. 如果 {IDF_TARGET_NAME} 的定时器选用了RC_FAST_CLK作为其时钟源驱动会通过内部校准来得知这个时钟源的实际频率。这样确保了输出PWM信号频率的精准性。
1. 如果 {IDF_TARGET_NAME} 的定时器选用了 ``RC_FAST_CLK`` 作为其时钟源,驱动会通过内部校准来得知这个时钟源的实际频率。这样确保了输出 PWM 信号频率的精准性。
.. only:: not SOC_CLK_RC_FAST_SUPPORT_CALIBRATION
1. 如果 {IDF_TARGET_NAME} 的定时器选用了RC_FAST_CLK作为其时钟源LEDC的输出PWM信号频率可能会与设定值有一定偏差。由于{IDF_TARGET_NAME} 的硬件限制,驱动无法通过内部校准得知这个时钟源的实际频率。因此驱动默认使用其理论频率进行计算。
1. 如果 {IDF_TARGET_NAME} 的定时器选用了 ``RC_FAST_CLK`` 作为其时钟源LEDC的输出 PWM 信号频率可能会与设定值有一定偏差。由于 {IDF_TARGET_NAME} 的硬件限制,驱动无法通过内部校准得知这个时钟源的实际频率。因此驱动默认使用其理论频率进行计算。
.. only:: not SOC_LEDC_HAS_TIMER_SPECIFIC_MUX
@ -247,7 +246,7 @@ LED PWM 控制器可在无需 CPU 干预的情况下自动改变占空比,实
另外一种设置占空比和其他通道参数的方式是调用 :ref:`ledc-api-configure-channel` 一节提到的函数 :cpp:func:`ledc_channel_config`
传递给函数的占空比数值范围取决于选定的 ``duty_resolution``,应为 ``0````(2 ** duty_resolution) - 1``。例如,如选定的占空比分辨率为 10则占空比的数值范围为 0 至 1023。此时分辨率为 ~0.1%。
传递给函数的占空比数值范围取决于选定的 ``duty_resolution``,应为 ``0````(2 ** duty_resolution) - 1``。例如,如选定的占空比分辨率为 10则占空比的数值范围为 0 至 1023。此时分辨率为 ~ 0.1%。
使用硬件改变 PWM 占空比
@ -320,7 +319,7 @@ LED PWM 控制器 API 有多种方式即时改变 PWM 频率:
高速模式的优点是可平稳地改变定时器设置。也就是说高速模式下如定时器设置改变此变更会自动应用于定时器的下一次溢出中断。而更新低速定时器时设置变更应由软件显式触发。LED PWM 驱动的设置将在硬件层面被修改,比如在调用函数 :cpp:func:`ledc_timer_config`:cpp:func:`ledc_timer_set` 时。
更多关于速度模式的详细信息请参阅 *{IDF_TARGET_NAME} 技术参考手册* > *LED PWM 控制器 (LEDC)* [`PDF <{IDF_TARGET_TRM_EN_URL}#ledpwm>`__]。
更多关于速度模式的详细信息请参阅 **{IDF_TARGET_NAME} 技术参考手册** > **LED PWM 控制器 (LEDC)** [`PDF <{IDF_TARGET_TRM_EN_URL}#ledpwm>`__]。
.. _ledc-api-supported-range-frequency-duty-resolution:
@ -331,7 +330,7 @@ LED PWM 控制器 API 有多种方式即时改变 PWM 频率:
频率和占空比分辨率支持范围
-------------------------------------------------
LED PWM 控制器主要用于驱动 LED。该控制器 PWM 占空比设置的分辨率范围较广。比如PWM 频率为 5 kHz 时,占空比分辨率最大可为 13 位。这意味着占空比可为 0 至 100% 之间的任意值,分辨率为 ~0.012%2 ** 13 = 8192 LED 亮度的离散电平)。然而,这些参数取决于为 LED PWM 控制器定时器计时的时钟信号LED PWM 控制器为通道提供时钟(具体可参考 :ref:`定时器配置 <ledc-api-configure-timer>` 和 *{IDF_TARGET_NAME} 技术参考手册* > *LED PWM 计时器 (LEDC)* [`PDF <{IDF_TARGET_TRM_EN_URL}#ledpwm>`__])。
LED PWM 控制器主要用于驱动 LED。该控制器 PWM 占空比设置的分辨率范围较广。比如PWM 频率为 5 kHz 时,占空比分辨率最大可为 13 位。这意味着占空比可为 0 至 100% 之间的任意值,分辨率为 ~0.012%2 ** 13 = 8192 LED 亮度的离散电平)。然而,这些参数取决于为 LED PWM 控制器定时器计时的时钟信号LED PWM 控制器为通道提供时钟(具体可参考 :ref:`定时器配置 <ledc-api-configure-timer>` 和 **{IDF_TARGET_NAME} 技术参考手册** > **LED PWM 计时器 (LEDC)** [`PDF <{IDF_TARGET_TRM_EN_URL}#ledpwm>`__])。
LED PWM 控制器可用于生成频率较高的信号,足以为数码相机模组等其他设备提供时钟。此时,最大频率可为 40 MHz占空比分辨率为 1 位。也就是说,占空比固定为 50%,无法调整。

View File

@ -237,7 +237,7 @@ MCPWM 定时器运行时会生成不同的事件。若有函数需在特定事
* 将定时器的状态从 **init** 切换到 **enable**。
* 若中断服务此前已通过 :cpp:func:`mcpwm_timer_register_event_callbacks` 函数延迟安装,则启用中断服务。
* 若选择了特定时钟源(例如 PLL_160M 时钟),则获取相应的电源管理锁。更多信息请参见 `电源管理`_
* 若选择了特定时钟源(例如 PLL_160M 时钟),则获取相应的电源管理锁。更多信息请参见 :ref:`mcpwm-power-management`
反之,调用 :cpp:func:`mcpwm_timer_disable` 会将定时器切换回 **init** 状态、禁用中断服务并释放电源管理锁。
@ -483,7 +483,7 @@ MCPWM 比较器可以在定时器计数器等于比较值时发送通知。若
在电力电子学中,常常会用到整流器和逆变器,这就涉及到了整流桥和逆变桥的应用。每个桥臂配有两个功率电子器件,例如 MOSFET、IGBT 等。同一桥臂上的两个 MOSFET 不能同时导通,否则会造成短路。实际应用中,在 PWM 波形显示 MOSFET 开关已关闭后,仍需要一段时间窗口才能完全关闭 MOSFET。因此需要设置 :ref:`mcpwm-generator-actions-on-events`,在已生成的 PWM 波形上添加额外延迟。
死区驱动器的工作方式与 *装饰器* 类似。在 :cpp:func:`mcpwm_generator_set_dead_time` 函数的参数中,驱动接收主要生成器句柄 (``in_generator``),并在应用死区后返回一个新的生成器 (``out_generator``)。需注意,如果 ``out_generator````in_generator`` 相同,这表示 PWM 波形中的时间延迟是以“就地”的方式添加的。反之,如果 ``out_generator````in_generator`` 不同,则代表在原 ``in_generator`` 的基础上派生出了一个新的 PWM 波形。
死区驱动器的工作方式与 **装饰器** 类似。在 :cpp:func:`mcpwm_generator_set_dead_time` 函数的参数中,驱动接收主要生成器句柄 (``in_generator``),并在应用死区后返回一个新的生成器 (``out_generator``)。需注意,如果 ``out_generator````in_generator`` 相同,这表示 PWM 波形中的时间延迟是以“就地”的方式添加的。反之,如果 ``out_generator````in_generator`` 不同,则代表在原 ``in_generator`` 的基础上派生出了一个新的 PWM 波形。
结构体 :cpp:type:`mcpwm_dead_time_config_t` 中列出了死区相关的具体配置:
@ -492,7 +492,7 @@ MCPWM 比较器可以在定时器计数器等于比较值时发送通知。若
.. warning::
由于硬件限制,同一种 delay 模块(`posedge delay` 或者 `negedge delay`)不能同时被应用在不同的 MCPWM 生成器中。例如,以下配置是无效的:
由于硬件限制,同一种 delay 模块(``posedge delay`` 或者 ``negedge delay``)不能同时被应用在不同的 MCPWM 生成器中。例如,以下配置是无效的:
.. code:: c
@ -504,7 +504,7 @@ MCPWM 比较器可以在定时器计数器等于比较值时发送通知。若
// NOTE: 下面的操作是无效的,不能将同一种 delay 应用于不同的 generator 上
mcpwm_generator_set_dead_time(mcpwm_gen_b, mcpwm_gen_b, &dt_config);
然而,您可以为生成器 A 设置 `posedge delay`,为生成器 B 设置 `negedge delay`。另外,您也可以为生成器 A 同时设置 `posedge delay``negedge delay`,而让生成器 B 绕过死区模块。
然而,你可以为生成器 A 设置 ``posedge delay``,为生成器 B 设置 ``negedge delay``。另外,也可以为生成器 A 同时设置 ``posedge delay````negedge delay``,而让生成器 B 绕过死区模块。
.. note::
@ -798,8 +798,8 @@ MCPWM 故障检测器支持在检测到实际故障或故障信号消失时发
MCPWM 操作器支持在进行制动操作前发送通知。若有函数需在特定事件发生时调用,则应预先调用 :cpp:func:`mcpwm_operator_register_event_callbacks`,将所需函数挂载至中断服务程序 (ISR) 中。驱动中制动事件回调函数原型声明为 :cpp:type:`mcpwm_brake_event_cb_t`,其所支持的事件回调类型则列在 :cpp:type:`mcpwm_operator_event_callbacks_t` 中:
- :cpp:member:`mcpwm_operator_event_callbacks_t::on_brake_cbc` 设置操作器进行 *逐周期 (CBC)* 操作前调用的回调函数。
- :cpp:member:`mcpwm_operator_event_callbacks_t::on_brake_ost` 设置操作器进行 *一次性 (OST)* 操作前调用的回调函数。
- :cpp:member:`mcpwm_operator_event_callbacks_t::on_brake_cbc` 设置操作器进行 **逐周期 (CBC)** 操作前调用的回调函数。
- :cpp:member:`mcpwm_operator_event_callbacks_t::on_brake_ost` 设置操作器进行 **一次性 (OST)** 操作前调用的回调函数。
由于上述回调函数在 ISR 中调用,因此,这些函数 **不应** 涉及 block 操作。可以检查调用 API 的后缀,确保在函数中只调用了后缀为 ``ISR`` 的 FreeRTOS API。

View File

@ -122,9 +122,9 @@ PCNT 单元和通道分别用 :cpp:type:`pcnt_unit_handle_t` 与 :cpp:type:`pcnt
PCNT 观察点
^^^^^^^^^^^
PCNT 单元可被设置为观察几个特定的数值,这些被观察的数值被称为 **观察点**。观察点不能超过 :cpp:type:`pcnt_unit_config_t` 设置的范围,最小值和最大值分别为 :cpp:member:`pcnt_unit_config_t::low_limit`:cpp:member:`pcnt_unit_config_t::high_limit`。当计数器到达任一观察点时,会触发一个观察事件,如果在 :cpp:func:`pcnt_unit_register_event_callbacks` 注册过事件回调函数,该事件就会通过中断通知。关于如何注册事件回调函数,请参考 :ref:`pcnt-register-event-callbacks`
PCNT 单元可被设置为观察几个特定的数值,这些被观察的数值被称为 **观察点**。观察点不能超过 :cpp:type:`pcnt_unit_config_t` 设置的范围,最小值和最大值分别为 :cpp:member:`pcnt_unit_config_t::low_limit`:cpp:member:`pcnt_unit_config_t::high_limit`。当计数器到达任一观察点时,会触发一个观察事件,如果在 :cpp:func:`pcnt_unit_register_event_callbacks` 注册过事件回调函数,该事件就会通过中断发送通知。关于如何注册事件回调函数,请参考 :ref:`pcnt-register-event-callbacks`
观察点分别可以通过 :cpp:func:`pcnt_unit_add_watch_point`:cpp:func:`pcnt_unit_remove_watch_point` 进行添加和删除。常用的观察点包括 **过零**, **最大/最小计数** 以及其他的阈值。可用的观察点是有限的,如果 :cpp:func:`pcnt_unit_add_watch_point` 无法获得空闲硬件资源来存储观察点,会返回错误 :c:macro:`ESP_ERR_NOT_FOUND`。不能多次添加同一个观察点,否则将返回错误 :c:macro:`ESP_ERR_INVALID_STATE`
观察点分别可以通过 :cpp:func:`pcnt_unit_add_watch_point`:cpp:func:`pcnt_unit_remove_watch_point` 进行添加和删除。常用的观察点包括 **过零****最大/最小计数** 以及其他的阈值。可用的观察点是有限的,如果 :cpp:func:`pcnt_unit_add_watch_point` 无法获得空闲硬件资源来存储观察点,会返回错误 :c:macro:`ESP_ERR_NOT_FOUND`。不能多次添加同一个观察点,否则将返回错误 :c:macro:`ESP_ERR_INVALID_STATE`
建议通过 :cpp:func:`pcnt_unit_remove_watch_point` 删除未使用的观察点来回收资源。
@ -146,9 +146,9 @@ PCNT 单元可被设置为观察几个特定的数值,这些被观察的数值
注册事件回调函数
^^^^^^^^^^^^^^^^^^^^
当 PCNT 单元的数值达到任一使能的观察点的数值时,会触发相应的事件并通过中断通知 CPU。如果您想在事件触发时执行相关函数,可通过调用 :cpp:func:`pcnt_unit_register_event_callbacks` 将函数挂载到中断服务程序 (ISR) 上。:cpp:type:`pcnt_event_callbacks_t` 列出了所有支持的事件回调函数:
当 PCNT 单元的数值达到任一使能的观察点的数值时,会触发相应的事件并通过中断通知 CPU。如果在事件触发时执行相关函数,可通过调用 :cpp:func:`pcnt_unit_register_event_callbacks` 将函数挂载到中断服务程序 (ISR) 上。:cpp:type:`pcnt_event_callbacks_t` 列出了所有支持的事件回调函数:
- :cpp:member:`pcnt_event_callbacks_t::on_reach` 用于为观察点事件设置回调函数。由于该回调函数是在 ISR 的上下文中被调用的,必须确保该函数不会阻塞调用的任务,(例如,可确保只有以 ``ISR`` 为后缀的 FreeRTOS API 才能在函数中调用):cpp:type:`pcnt_watch_cb_t` 中声明了该回调函数的原型。
- :cpp:member:`pcnt_event_callbacks_t::on_reach` 用于为观察点事件设置回调函数。由于该回调函数是在 ISR 的上下文中被调用的,必须确保该函数不会阻塞调用的任务,例如,可确保只有以 ``ISR`` 为后缀的 FreeRTOS API 才能在函数中调用:cpp:type:`pcnt_watch_cb_t` 中声明了该回调函数的原型。
可通过 ``user_ctx`` 将函数上下文保存到 :cpp:func:`pcnt_unit_register_event_callbacks` 中,这些数据会直接传递给回调函数。
@ -322,7 +322,8 @@ Konfig 选项 :ref:`CONFIG_PCNT_ISR_IRAM_SAFE` 可以实现以下功能:
支持线程安全
^^^^^^^^^^^^^
驱动保证工厂函数 :cpp:func:`pcnt_new_unit`:cpp:func:`pcnt_new_channel` 是线程安全的,因此您可以从 RTOS 任务中调用这些函数而无需使用额外的电源管理锁。
驱动保证工厂函数 :cpp:func:`pcnt_new_unit`:cpp:func:`pcnt_new_channel` 是线程安全的,因此可以从 RTOS 任务中调用这些函数,而无需使用额外的电源管理锁。
以下函数可以在 ISR 上下文中运行,驱动可以防止这些函数在任务和 ISR 中同时被调用。
- :cpp:func:`pcnt_unit_start`
@ -354,7 +355,7 @@ API 参考
.. include-build-file:: inc/pcnt_types.inc
.. [1]
在不同的 ESP 芯片系列中PCNT 单元和通道的数量可能会有差异,具体信息请参考 [`TRM <{IDF_TARGET_TRM_CN_URL}#pcnt>`__]。驱动不会禁止用户申请更多的 PCNT 单元和通道,但是当单元和通道资源全部被占用时,再调用单元和通道会返回错误。因此分配资源时,应注意检查返回值,如 :cpp:func:`pcnt_new_unit`
在不同的 ESP 芯片系列中PCNT 单元和通道的数量可能会有差异,具体信息请参考 [`TRM <{IDF_TARGET_TRM_CN_URL}#pcnt>`__]。驱动程序对通道申请数量不做限制,但当硬件资源用尽时,驱动程序将返回错误。因此分配资源时,应注意检查返回值,如 :cpp:func:`pcnt_new_unit`
.. [2]
:cpp:member:`pcnt_event_callbacks_t::on_reach` 回调函数和其调用的函数也应该放在 IRAM 中。

View File

@ -54,7 +54,7 @@ RMT 接收器可以对输入信号采样,将其转换为 RMT 数据格式,
下文将分节概述 RMT 的功能:
- :ref:`rmt-resource-allocation` - 介绍如何分配和正确配置 RMT 通道,以及如何回收闲置道及其他资源。
- :ref:`rmt-resource-allocation` - 介绍如何分配和正确配置 RMT 通道,以及如何回收闲置道及其他资源。
- :ref:`rmt-carrier-modulation-and demodulation` - 介绍如何调制和解调用于 TX 和 RX 通道的载波信号。
- :ref:`rmt-register-event-callbacks` - 介绍如何注册用户提供的事件回调函数以接收 RMT 通道事件。
- :ref:`rmt-enable-and-disable-channel` - 介绍如何启用和禁用 RMT 通道。
@ -80,7 +80,7 @@ RMT 接收器可以对输入信号采样,将其转换为 RMT 数据格式,
要安装 RMT TX 通道,应预先提供配置结构体 :cpp:type:`rmt_tx_channel_config_t`。以下列表介绍了配置结构体中的各个部分。
- :cpp:member:`rmt_tx_channel_config_t::gpio_num` 设置发射器使用的 GPIO 编号。
- :cpp:member:`rmt_tx_channel_config_t::clk_src` 选择 RMT 通道的时钟源。:cpp:type:`rmt_clock_source_t` 中列出了可用的时钟源。注意,其他道将使用同一所选时钟源,因此,应确保分配的任意 TX 或 RX 通道都享有相同的配置。有关不同时钟源对功耗的影响,请参阅 :ref:`rmt-power-management`
- :cpp:member:`rmt_tx_channel_config_t::clk_src` 选择 RMT 通道的时钟源。:cpp:type:`rmt_clock_source_t` 中列出了可用的时钟源。注意,其他道将使用同一所选时钟源,因此,应确保分配的任意 TX 或 RX 通道都享有相同的配置。有关不同时钟源对功耗的影响,请参阅 :ref:`rmt-power-management`
- :cpp:member:`rmt_tx_channel_config_t::resolution_hz` 设置内部滴答计数器的分辨率。基于此 **滴答**,可以计算 RMT 信号的定时参数。
- 在启用 DMA 后端和未启用 DMA 后端的情况下,:cpp:member:`rmt_tx_channel_config_t::mem_block_symbols` 字段含义稍有不同。
@ -115,7 +115,7 @@ RMT 接收器可以对输入信号采样,将其转换为 RMT 数据格式,
要安装 RMT RX 通道,应预先提供配置结构体 :cpp:type:`rmt_rx_channel_config_t`。以下列表介绍了配置结构体中的各个部分。
- :cpp:member:`rmt_rx_channel_config_t::gpio_num` 设置接收器使用的 GPIO 编号。
- :cpp:member:`rmt_rx_channel_config_t::clk_src` 选择 RMT 通道的时钟源。:cpp:type:`rmt_clock_source_t` 中列出了可用的时钟源。注意,其他道将使用同一所选时钟源,因此,应确保分配的任意 TX 或 RX 通道都享有相同的配置。有关不同时钟源对功耗的影响,请参阅 :ref:`rmt-power-management`
- :cpp:member:`rmt_rx_channel_config_t::clk_src` 选择 RMT 通道的时钟源。:cpp:type:`rmt_clock_source_t` 中列出了可用的时钟源。注意,其他道将使用同一所选时钟源,因此,应确保分配的任意 TX 或 RX 通道都享有相同的配置。有关不同时钟源对功耗的影响,请参阅 :ref:`rmt-power-management`
- :cpp:member:`rmt_rx_channel_config_t::resolution_hz` 设置内部滴答计数器的分辨率。基于此 **滴答**,可以计算 RMT 信号的定时参数。
- 在启用 DMA 后端和未启用 DMA 后端的情况下,:cpp:member:`rmt_rx_channel_config_t::mem_block_symbols` 字段含义稍有不同。
@ -191,7 +191,7 @@ RMT 发射器可以生成载波信号,并将其调制到消息信号上。载
注册事件回调
^^^^^^^^^^^^^^^^^^^^^^^^
当 RMT 道生成发送或接收完成等事件时,会通过中断告知 CPU。如果需要在发生特定事件时调用函数可以为 TX 和 RX 道分别调用 :cpp:func:`rmt_tx_register_event_callbacks`:cpp:func:`rmt_rx_register_event_callbacks`,向 RMT 驱动程序的中断服务程序 (ISR) 注册事件回调。由于上述回调函数是在 ISR 中调用的,因此,这些函数不应涉及 block 操作。可以检查调用 API 的后缀,确保在函数中只调用了后缀为 ISR 的 FreeRTOS API。回调函数具有布尔返回值指示回调是否解除了更高优先级任务的阻塞状态。
当 RMT 道生成发送或接收完成等事件时,会通过中断告知 CPU。如果需要在发生特定事件时调用函数可以为 TX 和 RX 道分别调用 :cpp:func:`rmt_tx_register_event_callbacks`:cpp:func:`rmt_rx_register_event_callbacks`,向 RMT 驱动程序的中断服务程序 (ISR) 注册事件回调。由于上述回调函数是在 ISR 中调用的,因此,这些函数不应涉及 block 操作。可以检查调用 API 的后缀,确保在函数中只调用了后缀为 ISR 的 FreeRTOS API。回调函数具有布尔返回值指示回调是否解除了更高优先级任务的阻塞状态。
有关 TX 通道支持的事件回调,请参阅 :cpp:type:`rmt_tx_event_callbacks_t`
@ -319,7 +319,7 @@ RMT 是一种特殊的通信外设,无法像 SPI 和 I2C 那样发送原始字
发起 RX 事务
^^^^^^^^^^^^^^^^^^^^^^^
:ref:`rmt-enable-and-disable-channel` 一节所述,仅调用 :cpp:func:`rmt_enable`RX 道无法接收 RMT 符号。为此,应在 :cpp:type:`rmt_receive_config_t` 中指明传入信号的基本特征:
:ref:`rmt-enable-and-disable-channel` 一节所述,仅调用 :cpp:func:`rmt_enable`RX 道无法接收 RMT 符号。为此,应在 :cpp:type:`rmt_receive_config_t` 中指明传入信号的基本特征:
- :cpp:member:`rmt_receive_config_t::signal_range_min_ns` 指定高电平或低电平有效脉冲的最小持续时间。如果脉冲宽度小于指定值,硬件会将其视作干扰信号并忽略。
- :cpp:member:`rmt_receive_config_t::signal_range_max_ns` 指定高电平或低电平有效脉冲的最大持续时间。如果脉冲宽度大于指定值,接收器会将其视作 **停止信号**,并立即生成接收完成事件。
@ -598,7 +598,7 @@ API 参考
.. [1]
不同 ESP 芯片系列可能具有不同数量的 RMT 通道,详情请参阅 [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__]。驱动程序不会禁止申请更多 RMT 通道,但会在可用硬件资源不足时报错。在进行 :ref:`rmt-resource-allocation` 时,请持续检查返回值。
不同 ESP 芯片系列可能具有不同数量的 RMT 通道,详情请参阅 [`TRM <{IDF_TARGET_TRM_EN_URL}#rmt>`__]。驱动程序对通道申请数量不做限制,但当硬件资源用尽时,驱动程序将返回错误。因此,每次进行 :ref:`rmt-resource-allocation` 时,请注意检查返回值。
.. [2]
回调函数,如 :cpp:member:`rmt_tx_event_callbacks_t::on_trans_done` 及回调函数所调用的函数也应位于 IRAM 中,用户需自行留意这一问题
注意,回调函数(如 :cpp:member:`rmt_tx_event_callbacks_t::on_trans_done`及回调函数所调用的函数也应位于 IRAM 中。

View File

@ -11,9 +11,10 @@ spi_flash 组件提供外部 flash 数据读取、写入、擦除和内存映射
关于更多高层次的用于访问分区(分区表定义于 :doc:`分区表 </api-guides/partition-tables>`)的 API 函数,参见 :doc:`/api-reference/storage/partition`
.. note::
访问主 flash 芯片时,建议使用上述 ``esp_partition_*`` API 函数,而非低层级的 ``esp_flash_*`` API 函数。分区表 API 函数根据存储在分区表中的数据,进行边界检查并计算在 flash 中的正确偏移量。不过,您仍可以使用 ``esp_flash_*`` 函数直接访问外部(额外)的 SPI flash 芯片。
与 ESP-IDF v4.0 之前的 API 不同,这一版 ``esp_flash_*`` API 功能并不局限于主 SPI flash 芯片(即运行程序的 SPI flash 芯片)。使用不同的芯片指针,您可以访问连接到 SPI0/1 或 SPI2 总线的外部 flash 芯片。
访问主 flash 芯片时,建议使用上述 ``esp_partition_*`` API 函数,而非低层级的 ``esp_flash_*`` API 函数。分区表 API 函数根据存储在分区表中的数据,进行边界检查并计算在 flash 中的正确偏移量。不过,仍支持使用 ``esp_flash_*`` 函数直接访问外部(额外)的 SPI flash 芯片。
与 ESP-IDF v4.0 之前的 API 不同,这一版 ``esp_flash_*`` API 功能并不局限于主 SPI flash 芯片(即运行程序的 SPI flash 芯片)。通过使用不同的芯片指针,可以访问连接到 SPI0/1 或 SPI2 总线的外部 flash 芯片。
.. note::
@ -23,7 +24,7 @@ spi_flash 组件提供外部 flash 数据读取、写入、擦除和内存映射
.. note::
ESP-IDF v4.0 之后的 flash API 不再是 *原子* 的。因此,如果读操作执行过程中发生写操作,且读操作和写操作的 flash 地址出现重叠,读操作返回的数据可能会包含旧数据和新数据(新数据为写操作更新产生的数据)。
ESP-IDF v4.0 之后的 flash API 不再是 **原子** 的。因此,如果读操作执行过程中发生写操作,且读操作和写操作的 flash 地址出现重叠,读操作返回的数据可能会包含旧数据和新数据(新数据为写操作更新产生的数据)。
.. note::
@ -77,9 +78,9 @@ Flash 可选的功能
- 暂停与恢复 - 表示 flash 可以在读/写的过程中接受暂停/恢复的命令。{IDF_TARGET_NAME} 可以在 flash 正在写/擦除的过程中保持 cache 开启,并能随机读取 flash 中的内容。
如果想使用这些功能,则需保证 {IDF_TARGET_NAME} 支持这些功能,且产品里所使用的 flash 芯片也要支持这些功能。请参阅 :doc:`spi_flash_optional_feature`,查看更多信息。
如果想使用这些功能,则需保证 {IDF_TARGET_NAME} 支持这些功能,且产品里所使用的 flash 芯片也要支持这些功能。请参阅 :doc:`spi_flash_optional_feature`,查看更多信息。
也可以自定义 flash 芯片驱动。请参阅 :doc:`spi_flash_override_driver`,查看详细信息。
也可以自定义 flash 芯片驱动。请参阅 :doc:`spi_flash_override_driver`,查看详细信息。
.. toctree::
:hidden:
@ -89,7 +90,7 @@ Flash 可选的功能
初始化 Flash 设备
---------------------------
在使用 ``esp_flash_*`` API 之前,需要在 SPI 总线上初始化芯片,步骤如下:
在使用 ``esp_flash_*`` API 之前,需要在 SPI 总线上初始化芯片,步骤如下:
1. 调用 :cpp:func:`spi_bus_initialize` 初始化 SPI 总线。此函数将初始化总线上设备间共享的资源,如 I/O、DMA、中断等。
@ -117,7 +118,7 @@ SPI Flash 容量
SPI flash 容量由引导加载程序镜像头部(烧录偏移量为 0x1000的一个字段进行配置。
默认情况下,引导程序被写入 flash 时esptool.py 会自动检测 SPI flash 容量,同时使用正确容量更新引导程序的头部。也可以在工程配置中设置 :envvar:`CONFIG_ESPTOOLPY_FLASHSIZE`,生成固定的 flash 容量。
默认情况下,引导程序被写入 flash 时,``esptool.py`` 会自动检测 SPI flash 容量,同时使用正确容量更新引导程序的头部。也可以在工程配置中设置 :envvar:`CONFIG_ESPTOOLPY_FLASHSIZE`,生成固定的 flash 容量。
如需在运行时覆盖已配置的 flash 容量,请配置 ``g_rom_flashchip`` 结构中的 ``chip_size````esp_flash_*`` 函数使用此容量(于软件和 ROM 中)进行边界检查。
@ -137,7 +138,7 @@ SPI1 Flash 并发约束
SPI Flash 加密
--------------------
您可以对 SPI flash 内容进行加密,并在硬件层对其进行透明解密。
SPI flash 内容支持加密,并在硬件层进行透明解密。
请参阅 :doc:`flash 加密 </security/flash-encryption>`,查看详细信息。
@ -168,6 +169,7 @@ Flash 在 {IDF_TARGET_CACHE_SIZE} 页进行映射。内存映射硬件既可将
内存映射以页为单位,即使传递给 ``esp_partition_mmap`` 的是一个分区,分区外的数据也是也是可以被读取到的,不会受到分区边界的影响。
.. note::
由于 mmap 是由 cache 支持的因此mmap 也仅能用在主 flash 上。
SPI Flash 实现
@ -186,7 +188,7 @@ SPI Flash 实现
在 SPI HAL 文件中,有些函数是基于现有的 {IDF_TARGET_NAME} memory-spi 来实现的。但是,由于 {IDF_TARGET_NAME} 的速度限制HAL 层无法提供某些读命令的高速实现(所以这些命令根本没有在 HAL 的文件中被实现)。``memspi_host_driver.h````.c`` 文件使用 HAL 提供的 ``common_command`` 函数实现上述读命令的高速版本,并将所有它实现的以及 HAL 函数封装为 ``spi_flash_host_driver_t`` 供更上层调用。
您甚至可以仅通过 GPIO 来实现自己的主机驱动。只要实现了 ``spi_flash_host_driver_t`` 中所有函数不管底层硬件是什么esp_flash API 都可以访问 flash。
仅通过 GPIO也可实现自己的主机驱动。只要实现了 ``spi_flash_host_driver_t`` 中所有函数不管底层硬件是什么esp_flash API 都可以访问 flash。
芯片驱动
^^^^^^^^^^^
@ -220,16 +222,16 @@ OS 函数层目前支持访问锁和延迟的方法。
使用 OS 函数还可以在一定程度上避免在擦除大块 flash 区域时出现看门狗超时的情况。在这段时间内CPU 将被 flash 擦除任务占用,从而阻止其他任务的执行,包括为看门狗定时器 (WDT) 供电的空闲任务。若已选中配置选项 :ref:`CONFIG_ESP_TASK_WDT_PANIC`,并且 flash 操作时间长于看门狗的超时时间,系统将重新启动。
不过,由于不同的 flash 芯片擦除时间不同flash 驱动几乎无法兼容,很难完全规避超时的风险。因此,您需要格外注意这一点。请遵照以下指南:
不过,由于不同的 flash 芯片擦除时间不同flash 驱动几乎无法兼容,很难完全规避超时的风险,这一点需要格外注意。请遵照以下指南:
1. 建议启用 :ref:`CONFIG_SPI_FLASH_YIELD_DURING_ERASE` 选项,允许调度器在擦除 flash 时进行重新调度。此外,还可以使用下列参数。
- 在 menuconfig 中增加 :ref:`CONFIG_SPI_FLASH_ERASE_YIELD_TICKS` 或减少 :ref:`CONFIG_SPI_FLASH_ERASE_YIELD_DURATION_MS` 的时间。
- 您也可以在 menuconfig 中增加 :ref:`CONFIG_ESP_TASK_WDT_TIMEOUT_S` 的时间以设置更长的看门狗超时周期。然而,看门狗超时周期拉长后,可能无法再检测到以前可检测到的超时。
- 在 menuconfig 中增加 :ref:`CONFIG_ESP_TASK_WDT_TIMEOUT_S` 的时间以设置更长的看门狗超时周期。然而,看门狗超时周期拉长后,可能无法再检测到以前可检测到的超时。
2. 请注意,在进行长时间的 SPI flash 操作时,启用 :ref:`CONFIG_ESP_TASK_WDT_PANIC` 选项将会在超时时触发恐慌处理程序。不过,启用该选项也可以帮助处理应用程序中的意外异常,您可以根据实际情况决定是否需要启用这个选项。
1. 请注意,在进行长时间的 SPI flash 操作时,启用 :ref:`CONFIG_ESP_TASK_WDT_PANIC` 选项将会在超时时触发紧急处理程序。不过,启用该选项也可以帮助处理应用程序中的意外异常,请根据实际情况决定是否需要启用这个选项。
3. 在开发过程中,请根据项目对擦除 flash 的具体要求和时间限制,谨慎进行 flash 操作。在配置 flash 擦除超时周期时,请在实际产品要求的基础上留出合理的冗余时间,从而提高产品的可靠性。
2. 在开发过程中,请根据项目对擦除 flash 的具体要求和时间限制,谨慎进行 flash 操作。在配置 flash 擦除超时周期时,请在实际产品要求的基础上留出合理的冗余时间,从而提高产品的可靠性。
.. _spi-flash-implementation-details:
@ -249,7 +251,7 @@ Flash 操作完成后CPU A 上的函数将设置另一标志位,即 ``s_fla
另外,所有 API 函数均受互斥量 ``s_flash_op_mutex`` 保护。
在单核环境中(启用 :ref:`CONFIG_FREERTOS_UNICORE`您需要禁用上述两个 cache 以防发生 CPU 间通信。
在单核环境中(启用 :ref:`CONFIG_FREERTOS_UNICORE`需要禁用上述两个 cache以防发生 CPU 间通信。
相关文档
-----------------

View File

@ -9,46 +9,46 @@ SPI1 flash 并发约束
.. only:: not (esp32c3 or SOC_SPIRAM_XIP_SUPPORTED)
在 {IDF_TARGET_NAME} 上flash 读取/写入/擦除时cache 必须被禁用
在 {IDF_TARGET_NAME} 上flash 读取/写入/擦除时,必须禁用 cache。
.. only:: esp32c3
在 {IDF_TARGET_NAME} 上,默认启用的配置选项 :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` 允许 flash/PSRAM 的 cache 访问和 SPI1 的操作并发执行。请参阅 :ref:`auto-suspend`,查看详细信息。
在该选项被禁用的情况下,读取/写入/擦除 flash 时cache 必须被禁用。使用驱动访问 SPI1 的相关约束参见 :ref:`impact_disabled_cache`。这些约束会带来更多的 IRAM/DRAM 消耗。
禁用该选项时,在读取/写入/擦除 flash 期间,必须禁用 cache。使用驱动访问 SPI1 的相关约束参见 :ref:`impact_disabled_cache`。这些约束会带来更多的 IRAM/DRAM 消耗。
.. only:: SOC_SPIRAM_XIP_SUPPORTED
在 {IDF_TARGET_NAME} 上,启用配置选项 :ref:`CONFIG_SPIRAM_FETCH_INSTRUCTIONS` (默认禁用)和 :ref:`CONFIG_SPIRAM_RODATA` (默认禁用)后将允许 flash/PSRAM 的 cache 访问和 SPI1 的操作并发执行。请参阅 :ref:`xip_from_psram`,查看详细信息。
在上述选项被禁用的情况下,读取/写入/擦除 flash 时cache 必须被禁用。使用驱动访问 SPI1 的相关约束参见 :ref:`impact_disabled_cache`。这些约束会带来更多的 IRAM/DRAM 消耗。
禁用该选项时,在读取/写入/擦除 flash 期间,必须禁用 cache。使用驱动访问 SPI1 的相关约束参见 :ref:`impact_disabled_cache`。这些约束会带来更多的 IRAM/DRAM 消耗。
.. _impact_disabled_cache:
当 cache 被禁用
禁用 cache
----------------------------
此时,在 flash 擦写操作中,所有的 CPU 都只能执行 IRAM 中的代码,而且必须从 DRAM 中读取数据。如果使用本文档中 API 函数,上述限制将自动生效且透明(无需额外关注),但这些限制可能会影响系统中的其他任务的性能。
此时,在 flash 擦写操作中,所有的 CPU 都只能执行 IRAM 中的代码,而且必须从 DRAM 中读取数据。如果使用本文档中 API 函数,上述限制将自动生效且透明(无需额外关注),但这些限制可能会影响系统中的其他任务的性能。
.. only:: esp32c3
.. note::
启用 :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND`cache 不会被禁用,其中的操作将通过硬件仲裁器来协调。
启用 :ref:`CONFIG_SPI_FLASH_AUTO_SUSPEND` 时,不会禁用 cache其中的操作将通过硬件仲裁器来协调。
.. only:: SOC_SPIRAM_XIP_SUPPORTED
.. note::
同时启用 :ref:`CONFIG_SPIRAM_FETCH_INSTRUCTIONS`:ref:`CONFIG_SPIRAM_RODATA` 选项后cache 不会被禁用
同时启用 :ref:`CONFIG_SPIRAM_FETCH_INSTRUCTIONS`:ref:`CONFIG_SPIRAM_RODATA` 选项后,不会禁用 cache。
.. only:: not CONFIG_FREERTOS_UNICORE
为避免意外读取 flash cache一个 CPU 在启动 flash 写入或擦除操作时,另一个 CPU 将阻塞。在 flash 操作完成前,所有 CPU 上,所有的非 IRAM 安全的中断都会被禁用
为避免意外读取 flash cache一个 CPU 在启动 flash 写入或擦除操作时,另一个 CPU 将阻塞。在 flash 操作完成前,会禁用所有 CPU 上非 IRAM 安全的中断。
.. only:: CONFIG_FREERTOS_UNICORE
为避免意外读取 flash cache在 flash 操作完成前,所有 CPU 上,所有的非 IRAM 安全的中断都会被禁用
为避免意外读取 flash cache在 flash 操作完成前,所有 CPU 上,会禁用所有在 CPU 上非 IRAM 安全的中断
另请参阅 :ref:`esp_flash_os_func`:ref:`spi_bus_lock`
@ -62,20 +62,20 @@ SPI1 flash 并发约束
IRAM 安全中断处理程序
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
如果需要在 flash 操作期间运行中断处理程序(比如低延迟操作),请在 :doc:`注册中断处理程序 </api-reference/system/intr_alloc>` 时设置 ``ESP_INTR_FLAG_IRAM``
如果需要在 flash 操作期间运行中断处理程序(比如低延迟操作),请在 :doc:`注册中断处理程序 </api-reference/system/intr_alloc>` 时设置 ``ESP_INTR_FLAG_IRAM``
请确保中断处理程序访问的所有数据和函数(包括其调用的数据和函数)都存储在 IRAM 或 DRAM 中。参见 :ref:`how-to-place-code-in-iram`
在函数或符号未被正确放入 IRAM/DRAM 的情况下,中断处理程序在 flash 操作期间从 flash cache 中读取数据时,会导致程序崩溃。这可能是因为代码未被正确放入 IRAM 而产生非法指令异常,也可能是因为常数未被正确放入 DRAM 而读取到垃圾数据。
未将函数或符号正确放入 IRAM/DRAM 的情况下,在 flash 操作期间,中断处理程序从 flash cache 中读取数据时,会导致程序崩溃。这可能是因为代码未正确放入 IRAM产生了非法指令异常也可能是因为常数未正确放入 DRAM读取到了垃圾数据。
.. note::
在 ISRs 中处理字符串时,不建议使用 `printf` 和其他输出函数。为了方便调试,在从 ISRs 中获取数据时,请使用 :cpp:func:`ESP_DRAM_LOGE` 和类似的宏。请确保 ``TAG`` 和格式字符串都放置于 ``DRAM`` 中。
在 ISRs 中处理字符串时,不建议使用 ``printf`` 和其他输出函数。为了方便调试,在从 ISRs 中获取数据时,请使用 :cpp:func:`ESP_DRAM_LOGE` 和类似的宏。请确保 ``TAG`` 和格式字符串都放置于 ``DRAM`` 中。
非 IRAM 安全中断处理程序
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
如果在注册时没有设置 `ESP_INTR_FLAG_IRAM` 标志,当 cache 被禁用时,将不会执行中断处理程序。一旦 cache 恢复,非 IRAM 安全的中断将重新启用,中断处理程序随即再次正常运行。这意味着,只要 cache 被禁用,将不会发生相应的硬件事件。
如果在注册时没有设置 ``ESP_INTR_FLAG_IRAM`` 标志,当禁用 cache 时,将不会执行中断处理程序。一旦 cache 恢复,非 IRAM 安全的中断将重新启用,中断处理程序随即再次正常运行。这意味着,只要禁用了 cache不会发生相应的硬件事件。
.. only:: esp32c3 or esp32c2 or esp32s3

View File

@ -1,8 +1,9 @@
SPI 主机驱动程序
===================
:link_to_translation:`en:[English]`
SPI 主机驱动程序是用于控制 {IDF_TARGET_NAME} 的通用 SPI (GP-SPI) 外设作为 SPI 主机的软件程序
SPI 主机驱动程序是一个软件程序,用于在 {IDF_TARGET_NAME} 的通用 SPI (GP-SPI) 外设工作在主控模式时,对其进行控制
.. only:: esp32
@ -10,7 +11,7 @@ SPI 主机驱动程序是用于控制 {IDF_TARGET_NAME} 的通用 SPI (GP-SPI)
SPI1 不属于 GP-SPI。SPI 主机驱动程序也支持 SPI1。但在 SPI1 总线上使用 SPI 主机驱动程序存在诸多限制,请参阅 :ref:`spi_master_on_spi1_bus`
有关 GP-SPI 硬件相关信息,请参考 *{IDF_TARGET_NAME} 技术参考手册* > *SPI 控制器* [`PDF <{IDF_TARGET_TRM_CN_URL}#spi>`__]。
有关 GP-SPI 硬件相关信息,请参考 **{IDF_TARGET_NAME} 技术参考手册** > **SPI 控制器** [`PDF <{IDF_TARGET_TRM_CN_URL}#spi>`__]。
术语
--------
@ -56,9 +57,9 @@ SPI 主机驱动程序是用于控制 {IDF_TARGET_NAME} 的通用 SPI (GP-SPI)
* - 传输事务 (Transaction)
- 即主机断言设备的 CS 线,向设备发送数据/从设备读取数据,接着去断言 CS 线的过程。传输事务为原子操作,不可打断。
* - 发射沿 (Launch Edge)
- 源寄存器将信号 *发射* 到线路上的时钟边沿。
- 源寄存器将信号 **发射** 到线路上的时钟边沿。
* - 锁存沿 (Latch Edge)
- 目的寄存器 *锁存* 信号的时钟边沿。
- 目的寄存器 **锁存** 信号的时钟边沿。
主机驱动特性
@ -125,7 +126,7 @@ SPI 总线传输事务由五个阶段构成,详见下表(任意阶段均可
并非每个 SPI 设备都要求命令和/或地址,因此命令阶段和地址阶段为可选项。这反映在设备的配置中:如果 :cpp:member:`spi_device_interface_config_t::command_bits` 和/或 :cpp:member:`spi_device_interface_config_t::address_bits` 被设置为零,则不会唤起命令或地址阶段。
并非每个传输事务都需要写入和读取数据,因此读取和写入阶段也是可选项。如果将 :cpp:member:`spi_transaction_t::rx_buffer` 设置为 NULL且未设置 :c:macro:`SPI_TRANS_USE_RXDATA`,读取阶段将被跳过。如果将 :cpp:member:`spi_transaction_t::tx_buffer` 设置为 NULL且未设置 :c:macro:`SPI_TRANS_USE_TXDATA`,写入阶段将被跳过。
并非每个传输事务都需要写入和读取数据,因此读取和写入阶段也是可选项。如果将 :cpp:member:`spi_transaction_t::rx_buffer` 设置为 ``NULL``,且未设置 :c:macro:`SPI_TRANS_USE_RXDATA`,读取阶段将被跳过。如果将 :cpp:member:`spi_transaction_t::tx_buffer` 设置为 ``NULL``,且未设置 :c:macro:`SPI_TRANS_USE_TXDATA`,写入阶段将被跳过。
主机驱动程序支持两种类型的传输事务:中断传输事务和轮询传输事务。用户可以选择在不同设备上使用不同的传输事务类型。若设备需要同时使用两种传输事务类型,请参阅 :ref:`mixed_transactions`
@ -156,7 +157,7 @@ SPI 总线传输事务由五个阶段构成,详见下表(任意阶段均可
传输线模式配置
^^^^^^^^^^^^^^^^^^^^^
{IDF_TARGET_NAME} 支持的线路模式如下。要使用这些模式,请在结构体 :cpp:type:`spi_transaction_t` 中设置 ``flags``,如 ``传输事务标志信号`` 一栏所示。要检查相应的 IO 管脚是否被设置,请在 :cpp:type:`spi_bus_config_t` 中设置 ``flags``,如 `总线 IO 设置标志信号` 一栏所示。
{IDF_TARGET_NAME} 支持的线路模式如下。要使用这些模式,请在结构体 :cpp:type:`spi_transaction_t` 中设置 ``flags``,如 ``传输事务标志信号`` 一栏所示。要检查相应的 IO 管脚是否被设置,请在 :cpp:type:`spi_bus_config_t` 中设置 ``flags``,如 ``总线 IO 设置标志信号`` 一栏所示。
.. only:: not SOC_SPI_SUPPORT_OCT
@ -283,7 +284,7 @@ SPI 总线传输事务由五个阶段构成,详见下表(任意阶段均可
若未满足以上要求,传输事务效率将受到临时缓冲区分配和复制的影响。
如果使用多条数据线传输,请在结构体 :cpp:type:`spi_device_interface_config_t` 中的 `flags` 设置 ``SPI_DEVICE_HALFDUPLEX`` 标志信号。结构体 :cpp:type:`spi_transaction_t` 中的 ``flags`` 应按照 :ref:`transaction-line-mode` 中的描述设置。
如果使用多条数据线传输,请在结构体 :cpp:type:`spi_device_interface_config_t` 中的 ``flags`` 设置 ``SPI_DEVICE_HALFDUPLEX`` 标志信号。结构体 :cpp:type:`spi_transaction_t` 中的 ``flags`` 应按照 :ref:`transaction-line-mode` 中的描述设置。
.. only:: esp32
@ -549,6 +550,7 @@ SPI 时钟频率
^^^^^^^^^^^^^^^^^^^
GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_handle_t::cfg::clock_source` 选择,可用的时钟源请参阅 :cpp:type:`spi_clock_source_t`
默认情况下,驱动程序将把 :cpp:member:`spi_device_handle_t::cfg::clock_source` 设置为 ``SPI_CLK_SRC_DEFAULT``。这往往代表 GPSPI 时钟源中的最高频率,在不同的芯片中这一数值会有所不同。
设备的实际时钟频率可能不完全等于所设置的数字,驱动会将其重新计算为与硬件兼容的最接近的数字,并且不超过时钟源的时钟频率。调用函数 :cpp:func:`spi_device_get_actual_freq` 以了解驱动计算的实际频率。
@ -602,7 +604,7 @@ GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_handle_t::cfg
SPI 驱动是基于 FreeRTOS 的 API 实现的,在使用 :ref:`CONFIG_SPI_MASTER_IN_IRAM` 时,不得启用 :ref:`CONFIG_FREERTOS_PLACE_FUNCTIONS_INTO_FLASH`
单个中断传输事务传输 n 字节的总成本为 *20+8n/Fspi[MHz]* [µs],故传输速度为 *n/(20+8n/Fspi)*。8 MHz 时钟速度的传输速度见下表。
单个中断传输事务传输 n 字节的总成本为 **20+8n/Fspi[MHz]** [µs],故传输速度为 **n/(20+8n/Fspi)**。8 MHz 时钟速度的传输速度见下表。
.. list-table::
:widths: 30 45 40 30 30
@ -641,7 +643,7 @@ GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_handle_t::cfg
传输事务长度较短时将提高传输事务间隔成本,因此应尽可能将几个短传输事务压缩成一个传输事务,以提升传输速度。
注意ISR 在 flash 操作期间默认处于禁用状态。要在 flash 操作期间继续发送传输事务,请启用 :ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM`,并在 :cpp:member:`spi_bus_config_t::intr_flags` 中设置 :c:macro:`ESP_INTR_FLAG_IRAM`。此时flash 操作前列队的传输事务将由 ISR 并行处理。此外,每个设备的回调和它们的 callee 函数都应该在 IRAM 中,避免回调因缓存丢失而崩溃。详情请参阅 :ref:`iram-safe-interrupt-handlers`
注意ISR 在 flash 操作期间默认处于禁用状态。要在 flash 操作期间继续发送传输事务,请启用 :ref:`CONFIG_SPI_MASTER_ISR_IN_IRAM`,并在 :cpp:member:`spi_bus_config_t::intr_flags` 中设置 :c:macro:`ESP_INTR_FLAG_IRAM`。此时flash 操作前列队的传输事务将由 ISR 并行处理。此外,每个设备的回调和它们的 ``callee`` 函数都应该在 IRAM 中,避免回调因缓存丢失而崩溃。详情请参阅 :ref:`iram-safe-interrupt-handlers`
.. only:: esp32
@ -664,7 +666,7 @@ GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_handle_t::cfg
- :cpp:member:`spi_device_interface_config_t::input_delay_ns` - SCLK 上的一个时钟周期开始后 MISO 总线上的最大数据有效时间
- 是否使用了 IO_MUX 管脚或 GPIO 矩阵
使用 GPIO 矩阵时,最大有效频率降至现有 *输入延迟* 的 33~77%。使用 IO_MUX 管脚或 *dummy 位* 可以保留较高的频率,调用函数 :cpp:func:`spi_get_freq_limit` 能够获取主机的最大读取频率。
使用 GPIO 矩阵时,最大有效频率降至现有 **输入延迟** 的 33 ~ 77%。使用 IO_MUX 管脚或 **dummy 位** 可以保留较高的频率,调用函数 :cpp:func:`spi_get_freq_limit` 能够获取主机的最大读取频率。
.. _dummy_bit_workaround:
@ -693,7 +695,7 @@ GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_handle_t::cfg
- 是
- 半双工,禁用 DMA
如果主机仅写入数据,可以通过设置 :cpp:member:`spi_device_interface_config_t::flags` 中的 ``SPI_DEVICE_NO_DUMMY`` 位来禁用 *Dummy 位* 和频率检查机制。禁用 *Dummy 位* 和频率检查机制后,即便在使用 GPIO 矩阵的情况下,输出频率也可达 80 MHz。
如果主机仅写入数据,可以通过设置 :cpp:member:`spi_device_interface_config_t::flags` 中的 ``SPI_DEVICE_NO_DUMMY`` 位来禁用 **Dummy 位** 和频率检查机制。禁用 **Dummy 位** 和频率检查机制后,即便在使用 GPIO 矩阵的情况下,输出频率也可达 80 MHz。
:cpp:member:`spi_device_interface_config_t::flags`
@ -721,7 +723,7 @@ GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_handle_t::cfg
* - 使用 GPIO_MATRIX 的 ESP32 从机设备
- 75
MISO 路径延迟(有效时间)由从机的 *输入延迟* 与主机的 *GPIO 矩阵延迟* 组成。该延迟决定了频率限制,超过这个频率的全双工传输将无法如半双工交易中使用的 Dummy 位一样工作。该频率限制的计算方法为:
MISO 路径延迟(有效时间)由从机的 **输入延迟** 与主机的 **GPIO 矩阵延迟** 组成。该延迟决定了频率限制,超过这个频率的全双工传输将无法如半双工交易中使用的 Dummy 位一样工作。该频率限制的计算方法为:
*Freq limit [MHz] = 80 / (floor(MISO delay[ns]/12.5) + 1)*
@ -729,7 +731,7 @@ GPSPI 外设的时钟源可以通过设置 :cpp:member:`spi_device_handle_t::cfg
.. image:: /../_static/spi_master_freq_tv.png
不同设备在不同 *输入延迟* 时间下对应的频率限制如下表所示。
不同设备在不同 **输入延迟** 时间下对应的频率限制如下表所示。
当主机驱动为 IO_MUX (0 ns) 时:

View File

@ -5,7 +5,7 @@ SPI 从机驱动程序
SPI 从机驱动程序控制在 {IDF_TARGET_NAME} 中作为从机的 GP-SPI 外设。
有关 GP-SPI 硬件相关信息,请参考 *{IDF_TARGET_NAME} 技术参考手册* > *SPI 控制器* [`PDF <{IDF_TARGET_TRM_CN_URL}#spi>`__]。
有关 GP-SPI 硬件相关信息,请参考 **{IDF_TARGET_NAME} 技术参考手册** > **SPI 控制器** [`PDF <{IDF_TARGET_TRM_CN_URL}#spi>`__]。
术语
-----------
@ -37,13 +37,13 @@ SPI 从机驱动程序控制在 {IDF_TARGET_NAME} 中作为从机的 GP-SPI 外
* - QUADHD
- 保持信号。只用于 4 位 (qio/qout) 传输。
* - 断言 (Assertion)
- 指激活一条线的操作。反之,将线路恢复到非活动状态(回到空闲状态)的操作则称为 *去断言*。
- 指激活一条线的操作。反之,将线路恢复到非活动状态(回到空闲状态)的操作则称为 **去断言**。
* - 传输事务 (Transaction)
- 即主机断言从机设备的 CS 线,向从机设备传输数据,接着去断言 CS 线的过程。传输事务为原子操作,不可打断。
* - 发射沿 (Launch Edge)
- 源寄存器将信号 *发射* 到线路上的时钟边沿。
- 源寄存器将信号 **发射** 到线路上的时钟边沿。
* - 锁存沿 (Latch Edge)
- 目的寄存器 *锁存* 信号的时钟边沿。
- 目的寄存器 **锁存** 信号的时钟边沿。
驱动程序的功能
@ -53,7 +53,7 @@ SPI 从机驱动程序控制在 {IDF_TARGET_NAME} 中作为从机的 GP-SPI 外
SPI 从机驱动程序允许将 SPI 外设作为全双工设备使用。驱动程序可以发送/接收长度不超过 {IDF_TARGET_MAX_DATA_BUF} 字节的传输事务,或者利用 DMA 来发送/接收更长的传输事务。然而,存在一些与 DMA 有关的 :ref:`已知问题 <spi_dma_known_issues>`
SPI 从机驱动程序支持将 SPI ISR 注册至指定 CPU 内核。如果多个任务同时尝试访问一个 SPI 设备,建议重构应用程序,以使每个 SPI 外设一次只由一个任务访问。此外,请使用 :cpp:member:`spi_bus_config_t::isr_cpu_id` 将 SPI ISR 注册至与 SPI 外设相关任务相同的内核,确保线程安全。
SPI 从机驱动程序支持将 SPI ISR 注册至指定 CPU 内核。如果多个任务同时尝试访问一个 SPI 设备,建议重构应用程序,以使每个 SPI 外设一次只由一个任务访问。此外,请使用 :cpp:member:`spi_bus_config_t::isr_cpu_id` 将 SPI ISR 注册至与 SPI 外设相关任务相同的内核,确保线程安全。
SPI 传输事务
----------------
@ -62,7 +62,7 @@ SPI 传输事务
传输事务的属性由作为从机设备的 SPI 外设的配置结构体 :cpp:type:`spi_slave_interface_config_t` 和传输事务配置结构体 :cpp:type:`spi_slave_transaction_t` 决定。
由于并非每次传输事务都需要写入和读取数据,可以选择配置 :cpp:type:`spi_transaction_t` 为仅 TX、仅 RX 或同时 TX 和 RX 传输事务。如果将 :cpp:member:`spi_slave_transaction_t::rx_buffer` 设置为 NULL读取阶段将被跳过。与之类似如果将 :cpp:member:`spi_slave_transaction_t::tx_buffer` 设置为 NULL则写入阶段将被跳过。
由于并非每次传输事务都需要写入和读取数据,可以选择配置 :cpp:type:`spi_transaction_t` 为仅 TX、仅 RX 或同时 TX 和 RX 传输事务。如果将 :cpp:member:`spi_slave_transaction_t::rx_buffer` 设置为 ``NULL``,读取阶段将被跳过。与之类似,如果将 :cpp:member:`spi_slave_transaction_t::tx_buffer` 设置为 ``NULL``,则写入阶段将被跳过。
.. note::
@ -186,7 +186,7 @@ GPIO 交换矩阵和 IO_MUX
解决方案为,首先使用函数 :cpp:func:`spi_slave_queue_trans`,然后使用 :cpp:func:`spi_slave_get_trans_result`,来代替 :cpp:func:`spi_slave_transmit`。由此一来,可使从机设备的响应速度提高一倍。
也可以配置一个 GPIO 管脚,当从机设备开始新一次传输事务前,它将通过该管脚向主机发出信号。示例代码存放在 :example:`peripherals/spi_slave` 目录下。
也可以配置一个 GPIO 管脚,当从机设备开始新一次传输事务前,它将通过该管脚向主机发出信号。示例代码存放在 :example:`peripherals/spi_slave` 目录下。
时钟频率要求
@ -215,10 +215,10 @@ SPI 从机的工作频率最高可达 {IDF_TARGET_MAX_FREQ} MHz。如果时钟
- 频率限制 (MHz)
* - IO_MUX
- 43.75
- <11.4
- < 11.4
* - GPIO 交换矩阵
- 68.75
- <7.2
- < 7.2
注:
1. 如果频率达到上限,会导致随机误差。
@ -243,7 +243,7 @@ SPI 从机的工作频率最高可达 {IDF_TARGET_MAX_FREQ} MHz。如果时钟
.. wavedrom:: /../_static/diagrams/spi/spi_slave_miso_dma.json
如果启用 DMA从机设备的发射沿会比正常时间提前半个 SPI 时钟周期,变为主机的实际锁存沿。在这种情况下,如果 GPIO 交换矩阵被绕过,数据采样的保持时间将是 68.75 ns而非半个 SPI 时钟周期。如果使用了 GPIO 交换矩阵,保持时间将增加到 93.75 ns。主机应在锁存沿立即采样数据或在 SPI 模式 1 或模式 3 中进行通信。如果您的主机无法满足上述时间要求,请在没有 DMA 的情况下初始化从机设备。
如果启用 DMA从机设备的发射沿会比正常时间提前半个 SPI 时钟周期,变为主机的实际锁存沿。在这种情况下,如果 GPIO 交换矩阵被绕过,数据采样的保持时间将是 68.75 ns而非半个 SPI 时钟周期。如果使用了 GPIO 交换矩阵,保持时间将增加到 93.75 ns。主机应在锁存沿立即采样数据或在 SPI 模式 1 或模式 3 中进行通信。如果主机无法满足上述时间要求,请在没有 DMA 的情况下初始化从机设备。
应用示例

View File

@ -1,5 +1,6 @@
触摸传感器
============
:link_to_translation:`en:[English]`
{IDF_TARGET_TOUCH_SENSOR_VERSION:default="v2", esp32="v1"}
@ -7,7 +8,7 @@
概述
------------
触摸传感器系统由保护覆盖层、触摸电极、绝缘基板和走线组成,保护覆盖层位于最上层,绝缘基板上设有电极及走线。用户触摸覆盖层将产生电容变化,根据电容变化判断此次触摸是否为有效触摸行为。
触摸传感器系统由保护覆盖层、触摸电极、绝缘基板和走线组成,保护覆盖层位于最上层,绝缘基板上设有电极及走线。触摸覆盖层将引起电容变化,根据电容变化,可以判断此次触摸是否为有效触摸行为。
触摸传感器可以以矩阵或滑条等方式组合使用,从而覆盖更大触感区域及更多触感点。触摸传感由软件或专用硬件计时器发起,由有限状态机 (FSM) 硬件控制。
@ -22,7 +23,7 @@
功能介绍
----------------------
下面将 API 分解成几个函数组进行介绍,帮助用户快速了解以下功能
下面将 API 分解成几个函数组进行介绍:
- 初始化触摸传感器驱动程序
- 配置触摸传感器 GPIO 管脚
@ -169,7 +170,7 @@
优化测量
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
触摸传感器设有数个可配置参数,以适应触摸传感器设计特点。例如,如果需要感知较细微的电容变化,则可以缩小触摸传感器充放电的参考电压范围。用户可以使用 :cpp:func:`touch_pad_set_voltage` 函数设置电压参考低值和参考高值。
触摸传感器设有数个可配置参数,以适应触摸传感器设计特点。例如,如果需要感知较细微的电容变化,则可以缩小触摸传感器充放电的参考电压范围。使用 :cpp:func:`touch_pad_set_voltage` 函数,可以设置电压参考低值和参考高值。
.. only:: SOC_TOUCH_VERSION_1
@ -205,7 +206,7 @@
触摸传感器 - 测量参数之间的关系
上图中的 *Output* 代表触摸传感器读值,即一个测量周期内测得的脉冲计数值。
上图中的 **Output** 代表触摸传感器读值,即一个测量周期内测得的脉冲计数值。
.. only:: SOC_TOUCH_VERSION_2
@ -216,7 +217,7 @@
触摸传感器 - 测量参数之间的关系
上图中的 *Output* 代表触摸传感器读值,即固定充放电次数所需的时间。
上图中的 **Output** 代表触摸传感器读值,即固定充放电次数所需的时间。
所有函数均成对出现,用于设定某一特定参数,并获取当前参数值。例如::cpp:func:`touch_pad_set_voltage`:cpp:func:`touch_pad_get_voltage`
@ -228,7 +229,7 @@
如果测量中存在噪声,可以使用提供的 API 函数对采样进行滤波。使用滤波器之前,请先调用 :cpp:func:`touch_pad_filter_start` 启动该滤波器。
滤波器类型为 IIR无限脉冲响应滤波器用户可以调用 :cpp:func:`touch_pad_set_filter_period` 配置此类滤波器的采样周期。
滤波器类型为 IIR无限脉冲响应滤波器可以调用 :cpp:func:`touch_pad_set_filter_period` 配置此类滤波器的采样周期。
如需停止滤波器,请调用 :cpp:func:`touch_pad_filter_stop` 函数。如果不再使用该滤波器,请调用 :cpp:func:`touch_pad_filter_delete` 删除此滤波器。
@ -237,6 +238,7 @@
如果测量中存在噪声,可以使用提供的 API 函数对采样进行滤波。{IDF_TARGET_NAME} 的触摸功能提供了两套 API 可实现此功能。
一个是内部触摸通道,它没有连接到任何外部 GPIO。该降噪板的测量值可用于过滤所有通道上的干扰如来自电源和外部 EMI 的噪声。
降噪参数由 :cpp:func:`touch_pad_denoise_set_config` 设置并由 :cpp:func:`touch_pad_denoise_enable` 启动。
另一是可配置的硬件实现 IIR-滤波器(无限脉冲响应滤波器),该滤波器可通过调用 :cpp:func:`touch_pad_filter_set_config` 函数进行配置,调用 :cpp:func:`touch_pad_filter_enable` 函数启用。
@ -244,44 +246,44 @@
触摸监测
^^^^^^^^^^^^^^^
触摸监测基于用户配置的阈值和 FSM 执行的原始测量,并由 ESP32 硬件实现。用户可以调用 :cpp:func:`touch_pad_get_status` 查看被触碰的触摸传感器,或调用 :cpp:func:`touch_pad_clear_status` 清除触摸状态信息。
触摸监测基于配置的阈值和 FSM 执行的原始测量,并由 ESP32 硬件实现。可以调用 :cpp:func:`touch_pad_get_status` 查看被触碰的触摸传感器,或调用 :cpp:func:`touch_pad_clear_status` 清除触摸状态信息。
用户也可以将硬件触摸监测连接至中断,详细介绍见下一章节。
也可以将硬件触摸监测连接至中断,详细介绍见下一章节。
如果测量中存在噪声,且电容变化幅度较小,硬件触摸监测结果可能就不太理想。如需解决这一问题,不建议使用硬件监测或中断信号,建议用户在自己的应用程序中进行采样滤波,并执行触摸监测。请参考 :example:`peripherals/touch_sensor/touch_sensor_{IDF_TARGET_TOUCH_SENSOR_VERSION}/touch_pad_interrupt`,查看以上两种触摸监测的实现方式。
如果测量中存在噪声,且电容变化幅度较小,硬件触摸监测结果可能就不太理想。如需解决这一问题,不建议使用硬件监测或中断信号,建议在自己的应用程序中进行采样滤波,并执行触摸监测。请参考 :example:`peripherals/touch_sensor/touch_sensor_{IDF_TARGET_TOUCH_SENSOR_VERSION}/touch_pad_interrupt`,查看以上两种触摸监测的实现方式。
中断触发
^^^^^^^^^^^^^^^^^^^^^^^^^^
在对触摸监测启用中断之前,请先设置一个触摸监测阈值。然后使用 `触摸状态测量`_ 中所述的函数读取并显示触摸和释放触摸传感器时测得的结果。如果测量中存在噪声且相对电容变化较小,请使用滤波器。用户也可以根据应用程序和环境条件,测试温度和电源电压变化对测量值的影响。
在对触摸监测启用中断之前,请先设置一个触摸监测阈值。然后使用 `触摸状态测量`_ 中所述的函数读取并显示触摸和释放触摸传感器时测得的结果。如果测量中存在噪声且相对电容变化较小,请使用滤波器。也可以根据应用程序和环境条件,测试温度和电源电压变化对测量值的影响。
确定监测阈值后就可以在初始化时调用 :cpp:func:`touch_pad_config` 设置此阈值,或在运行时调用 :cpp:func:`touch_pad_set_thresh` 设置此阈值。
.. only:: SOC_TOUCH_VERSION_1
下一步就是设置如何触发中断。用户可以设置在阈值以下或以上触发中断,具体触发模式由函数 :cpp:func:`touch_pad_set_trigger_mode` 设置。
下一步就是设置如何触发中断。可以设置在阈值以下或以上触发中断,具体触发模式由函数 :cpp:func:`touch_pad_set_trigger_mode` 设置。
最后用户可以使用以下函数配置和管理中断调用:
最后可以使用以下函数配置和管理中断调用:
* :cpp:func:`touch_pad_isr_register` / :cpp:func:`touch_pad_isr_deregister`
* :cpp:func:`touch_pad_intr_enable` / :cpp:func:`touch_pad_intr_disable`
中断配置完成后,用户可以调用 :cpp:func:`touch_pad_get_status` 查看中断信号来自哪个触摸传感器,也可以调用 :cpp:func:`touch_pad_clear_status` 清除触摸传感器状态信息。
中断配置完成后,可以调用 :cpp:func:`touch_pad_get_status` 查看中断信号来自哪个触摸传感器,也可以调用 :cpp:func:`touch_pad_clear_status` 清除触摸传感器状态信息。
.. only:: SOC_TOUCH_VERSION_1
.. note::
触摸监测中的中断信号基于原始/未经滤波的采样(对比用户设置的阈值),并在硬件中实现。启用软件滤波 API (请参考 :ref:`touch_pad-api-filtering-of-measurements`)并不会影响这一过程。
触摸监测中的中断信号基于原始/未经滤波的采样(对比设置的阈值),并在硬件中实现。启用软件滤波 API请参考 :ref:`touch_pad-api-filtering-of-measurements`)并不会影响这一过程。
.. only:: SOC_TOUCH_VERSION_1
从睡眠模式唤醒
^^^^^^^^^^^^^^^^^^^^^^
如果使用触摸传感器中断将芯片从睡眠模式唤醒,用户可以选择配置一些触摸传感器,例如 SET1 或 SET1 和 SET2触摸这些触摸传感器将触发中断并唤醒芯片。请调用 :cpp:func:`touch_pad_set_trigger_source` 实现上述操作。
如果使用触摸传感器中断将芯片从睡眠模式唤醒,可以选择配置一些触摸传感器,例如 SET1 或 SET1 和 SET2触摸这些触摸传感器将触发中断并唤醒芯片。请调用 :cpp:func:`touch_pad_set_trigger_source` 实现上述操作。
用户可以使用以下函数管理 'SET' 中触摸传感器所需的位模式配置:
可以使用以下函数管理 'SET' 中触摸传感器所需的位模式配置:
* :cpp:func:`touch_pad_set_group_mask` / :cpp:func:`touch_pad_get_group_mask`
* :cpp:func:`touch_pad_clear_group_mask`
@ -304,7 +306,8 @@ API 参考
GPIO 宏查找表
^^^^^^^^^^^^^^^^^^
用户可以使用宏定义某一触摸传感器通道的 GPIO或定义某一 GPIO 的通道。例如:
可以使用宏定义某一触摸传感器通道的 GPIO或定义某一 GPIO 的通道。例如:
1. ``TOUCH_PAD_NUM5_GPIO_NUM`` 定义了通道 5 的 GPIO即 GPIO 12
2. ``TOUCH_PAD_GPIO4_CHANNEL`` 定义了 GPIO 4 的通道(即通道 0

View File

@ -14,7 +14,7 @@
.. only:: SOC_UART_LP_NUM
此外,{IDF_TARGET_NAME} 芯片还有一个满足低功耗需求的 LP UART 控制器。LP UART 是原 UART 的功能剪裁版本。它只支持基础 UART 功能,不支持 IrDA 或 RS485 协议,并且只有一块较小的 RAM 存储空间。想要全面了解的 UART 及 LP UART 功能区别,请参考 *{IDF_TARGET_NAME} 技术参考手册* > UART 控制器 (UART) > 主要特性 [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__]。
此外,{IDF_TARGET_NAME} 芯片还有一个满足低功耗需求的 LP UART 控制器。LP UART 是原 UART 的功能剪裁版本。它只支持基础 UART 功能,不支持 IrDA 或 RS485 协议,并且只有一块较小的 RAM 存储空间。想要全面了解的 UART 及 LP UART 功能区别,请参考 **{IDF_TARGET_NAME} 技术参考手册** > UART 控制器 (UART) > 主要特性 [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__]。
功能概述
-------------------
@ -126,7 +126,7 @@ UART 通信参数可以在一个步骤中完成全部配置,也可以在多个
ESP_ERROR_CHECK(uart_driver_install({IDF_TARGET_UART_EXAMPLE_PORT}, uart_buffer_size, \
uart_buffer_size, 10, &uart_queue, 0));
此步骤完成后,可连接外部 UART 设备检查通信。
此步骤完成后,可连接外部 UART 设备检查通信。
.. _uart-api-running-uart-communication:
@ -183,7 +183,7 @@ UART 通信参数可以在一个步骤中完成全部配置,也可以在多个
接收数据
"""""""""""""""""
一旦数据被 UART 接收并保存在 Rx FIFO 缓冲区中,就需要使用函数 :cpp:func:`uart_read_bytes` 检索数据。读取数据之前,调用 :cpp:func:`uart_get_buffered_data_len` 能够查看 Rx FIFO 缓冲区中可用的字节数。请参考以下示例。
一旦 UART 接收了数据,将其保存在 Rx FIFO 缓冲区中,就需要使用函数 :cpp:func:`uart_read_bytes` 检索数据。读取数据之前,调用 :cpp:func:`uart_get_buffered_data_len` 能够查看 Rx FIFO 缓冲区中可用的字节数。请参考以下示例。
.. code-block:: c
@ -200,7 +200,7 @@ UART 通信参数可以在一个步骤中完成全部配置,也可以在多个
软件流控
""""""""""""""""""""""
如果硬件流控被禁用,您可使用函数 :cpp:func:`uart_set_rts`:cpp:func:`uart_set_dtr` 分别手动设置 RTS 和 DTR 信号电平。
如果硬件流控处于禁用状态,可使用函数 :cpp:func:`uart_set_rts`:cpp:func:`uart_set_dtr` 分别手动设置 RTS 和 DTR 信号电平。
通信方式选择
@ -219,13 +219,13 @@ UART 控制器支持多种通信模式,使用函数 :cpp:func:`uart_set_mode`
使用中断
^^^^^^^^^^^^^^^^^
根据特定的 UART 状态或检测到的错误,可以生成许多不同的中断。*{IDF_TARGET_NAME} 技术参考手册* > UART 控制器 (UART) > UART 中断 和 UHCI 中断 [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__] 中提供了可用中断的完整列表。调用 :cpp:func:`uart_enable_intr_mask`:cpp:func:`uart_disable_intr_mask` 能够分别启用或禁用特定中断。
根据特定的 UART 状态或检测到的错误,可以生成许多不同的中断。**{IDF_TARGET_NAME} 技术参考手册** > UART 控制器 (UART) > UART 中断 和 UHCI 中断 [`PDF <{IDF_TARGET_TRM_EN_URL}#uart>`__] 中提供了可用中断的完整列表。调用 :cpp:func:`uart_enable_intr_mask`:cpp:func:`uart_disable_intr_mask` 能够分别启用或禁用特定中断。
调用 :cpp:func:`uart_driver_install` 函数可以安装驱动程序的内部中断处理程序,用以管理 Tx 和 Rx 环形缓冲区,并提供事件等高级 API 函数(见下文)。
API 提供了一种便利的方法来处理本文所讨论的特定中断,即用专用函数包装中断:
- **事件检测**:cpp:type:`uart_event_type_t` 定义了多个事件,使用 FreeRTOS 队列功能能够将其报告给用户应用程序。您可调用 :ref:`uart-api-driver-installation` 中的 :cpp:func:`uart_driver_install` 函数启用此功能,请参考 :example:`peripherals/uart/uart_events` 中使用事件检测的示例。
- **事件检测**:cpp:type:`uart_event_type_t` 定义了多个事件,使用 FreeRTOS 队列功能能够将其报告给用户应用程序。调用 :ref:`uart-api-driver-installation` 中的 :cpp:func:`uart_driver_install` 函数,可以启用此功能,请参考 :example:`peripherals/uart/uart_events` 中使用事件检测的示例。
- **达到 FIFO 空间阈值或传输超时**Tx 和 Rx FIFO 缓冲区在填充特定数量的字符和在发送或接收数据超时的情况下将会触发中断。如要使用此类中断,请执行以下操作:
@ -258,7 +258,7 @@ RS485 特定通信模式简介
.. note::
下文将使用 ``[UART_REGISTER_NAME].[UART_FIELD_BIT]`` 指代 UART 寄存器字段/位。了解特定模式位的更多信息,请参考 *{IDF_TARGET_NAME} 技术参考手册* > UART 控制器 (UART) > 寄存器摘要 [`PDF <{IDF_TARGET_TRM_EN_URL}#uart-reg-summ>`__]。您可搜索寄存器名称导航至寄存器描述,找到相应字段/位。
下文将使用 ``[UART_REGISTER_NAME].[UART_FIELD_BIT]`` 指代 UART 寄存器字段/位。了解特定模式位的更多信息,请参考 **{IDF_TARGET_NAME} 技术参考手册** > UART 控制器 (UART) > 寄存器摘要 [`PDF <{IDF_TARGET_TRM_EN_URL}#uart-reg-summ>`__]。搜索寄存器名称导航至寄存器描述,找到相应字段/位。
- ``UART_RS485_CONF_REG.UART_RS485_EN``:设置此位将启用 RS485 通信模式支持。
- ``UART_RS485_CONF_REG.UART_RS485TX_RX_EN``:设置此位,发送器的输出信号将环回到接收器的输入信号。
@ -394,7 +394,7 @@ API 参考
GPIO 查找宏指令
^^^^^^^^^^^^^^^^^^^^^^^^^^^
UART 外设有供直接连接的专用 IO_MUX 管脚,但也可用非直接的 GPIO 矩阵将信号配置到其他管脚。如要直接连接,需要知道哪一管脚为 UART 通道的专用 IO_MUX 管脚。GPIO 查找宏简化了查找和分配 IO_MUX 管脚的过程,可根据 IO_MUX 管脚编号或所需 UART 通道名称选择一个宏,该宏将返回匹配的对应项。请查看下列示例。
UART 外设有供直接连接的专用 IO_MUX 管脚,但也可用非直接的 GPIO 矩阵将信号配置到其他管脚。如要直接连接,需要知道哪一管脚为 UART 通道的专用 IO_MUX 管脚。GPIO 查找宏简化了查找和分配 IO_MUX 管脚的过程,可根据 IO_MUX 管脚编号或所需 UART 通道名称选择一个宏,该宏将返回匹配的对应项。请查看下列示例。
.. note::