diff --git a/docs/en/api-reference/peripherals/dac.rst b/docs/en/api-reference/peripherals/dac.rst index 202e8882e2a5..82c81a57d258 100644 --- a/docs/en/api-reference/peripherals/dac.rst +++ b/docs/en/api-reference/peripherals/dac.rst @@ -1,23 +1,25 @@ Digital To Analog Converter (DAC) ================================= +:link_to_translation:`zh_CN:[中文]` + {IDF_TARGET_DAC_CH_1: default = "GPIO25", esp32 = "GPIO25", esp32s2 = "GPIO17"} {IDF_TARGET_DAC_CH_2: default = "GPIO26", esp32 = "GPIO26", esp32s2 = "GPIO18"} Overview -------- -{IDF_TARGET_NAME} has two 8-bit DAC (digital to analog converter) channels, connected to {IDF_TARGET_DAC_CH_1} (Channel 1) and {IDF_TARGET_DAC_CH_2} (Channel 2). Which means each channel of DAC can convert digital value 0~255 to the analog voltage 0~Vref, the output voltage can be calculate by:: +{IDF_TARGET_NAME} has two 8-bit DAC (digital to analog converter) channels respectively connected to {IDF_TARGET_DAC_CH_1} (Channel 1) and {IDF_TARGET_DAC_CH_2} (Channel 2). Each DAC channel can convert the digital value 0~255 to the analog voltage 0~Vref. The output voltage can be calculated as the following:: out_voltage = Vref * digi_val / 255 -The DAC peripheral supports outputting analog signal in following ways: +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 the specified frequency. +2. Outputting continuous analog signal by DMA. The DAC will convert 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 the :doc:`Sigma-delta Modulation module ` and the :doc:`LED Control module `. Both modules produce high-frequency PWM/PDM output, which can be hardware low-pass filtered in order to generate a lower frequency analog output. +For other analog output options, see :doc:`Sigma-Delta Modulation ` and :doc:`LED Control `. Both modules produce high-frequency PWM/PDM output, which can be hardware low-pass filtered in order to generate a lower frequency analog output. DAC File Structure ------------------ @@ -26,17 +28,19 @@ DAC File Structure :align: center :alt: DAC file structure + DAC File Structure + -**Public headers that need to be included in the DAC application** +**Public headers that need to be included in the DAC application are listed as follows:** -- ``dac.h``: The top header file of legacy DAC driver, only included in the apps which use legacy driver API -- ``dac_oneshot.h``: The top header file of new DAC driver, should be included in the apps which use the new driver API with oneshot mode. -- ``dac_cosine.h``: The top header file of new DAC driver, should be included in the apps which use the new driver API with cosine mode. -- ``dac_continuous.h``: The top header file of new DAC driver, should be included in the apps which use the new driver API with continuous mode. +- ``dac.h``: The top header file of the legacy DAC driver, which should be only included in the apps which use the legacy driver API. +- ``dac_oneshot.h``: The top header file of the new DAC driver, which should be included in the apps which use the new driver API with one-shot mode. +- ``dac_cosine.h``: The top header file of the new DAC driver, which should be included in the apps which use the new driver API with cosine mode. +- ``dac_continuous.h``: The top header file of the new DAC driver, which should be included in the apps which use the new driver API with continuous mode. .. note:: - The legacy driver can't coexist with the new driver. Including ``dac.h`` to use the legacy driver or ``dac_oneshot.h``, ``dac_cosine.h`` and ``dac_continuous.h`` to use the new driver. The legacy driver might be removed in future. + The legacy driver cannot coexist with the new driver. Include ``dac.h`` to use the legacy driver or ``dac_oneshot.h``, ``dac_cosine.h``, and ``dac_continuous.h`` to use the new driver. The legacy driver might be removed in the future. Functional Overview ------------------- @@ -44,79 +48,79 @@ Functional Overview Resources Management ^^^^^^^^^^^^^^^^^^^^ -The DAC on {IDF_TARGET_NAME} has two channels, due to the software resources are separate, they could be managed by the :cpp:type:`dac_oneshot_handle_t`, :cpp:type:`dac_cosine_handle_t`:cpp:type:`dac_continuous_handle_t` according to the usage. Of cause, registering different modes on a same DAC channel is not allowed. +The DAC on {IDF_TARGET_NAME} has two channels. The channels have separate software resources and can be managed by :cpp:type:`dac_oneshot_handle_t`, :cpp:type:`dac_cosine_handle_t`, or :cpp:type:`dac_continuous_handle_t` according to the usage. Registering different modes on a same DAC channel is not allowed. Direct Voltage Output (One-shot/Direct Mode) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The DAC channels in the group can convert a 8-bit digital value into the analog every time calling :cpp:func:`dac_oneshot_output_voltage` (it can be called in ISR), and then the analog voltage will be kept on the DAC channel until next conversion start. To start to convert the voltage, the DAC channels need to be registered by :cpp:func:`dac_oneshot_new_channel` first, and the channel will be enabled after it is registered. +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`. Continuous Wave Output (Continuous/DMA Mode) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -DAC channels can convert digital data continuously via the DMA. There are three ways to writing DAC data: +DAC channels can convert digital data continuously via the DMA. There are three ways to write the DAC data: - 1. Normal writing (synchronous): It can transmit the data one time and keep blocking until all data has been loaded into the DMA buffer, and the voltage will be kept according to the last conversion value while no more data inputted, usually it is 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 :cpp:func:`dac_continuous_enable`, then data can be written by :cpp:func:`dac_continuous_write` synchronously. Referring to :example:`peripherals/dac/dac_continuous/dac_audio` for example. - 2. Cyclical writing: It can convert a piece of data cyclically without blocking, no more operation needed after the data are loaded into the DMA buffer,but note that the inputted buffer size is limited by the descriptor number and the DMA buffer size, usually it is used to transport some short signal that need to be repeated, for example, a sine wave. To achieve cyclical writing, :cpp:func:`dac_continuous_write_cyclically` can be called after the DAC continuous mode is enabled. For the cyclical writing example, please refer to :example:`peripherals/dac/dac_continuous/signal_generator` - 3. Asynchronous writing: It can transmit the data asynchronously based on the event callback. Thus :cpp:member:`dac_event_callbacks_t::on_convert_done` must be registered to use asynchronous mode, and then users can get the :cpp:type:`dac_event_data_t` in the callback which contains the DMA buffer address and length, allowing user to load the data into it directly. As mentioned, to use the asynchronous writing, :cpp:func:`dac_continuous_register_event_callback` need to be called to register the :cpp:member:`dac_event_callbacks_t::on_convert_done` before enabling, and then calling :cpp:func:`dac_continuous_start_async_writing` to start the asynchronous writing, note that once the asynchronous writing started, the callback function will be triggered continuously, :cpp:func:`dac_continuous_write_asynchronously` can help to load the data either in a separate task or the callback directly. For the asynchronous example, please refer to :example:`peripherals/dac/dac_continuous/dac_audio` as well. + 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. + 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. .. only:: esp32 - On ESP32, DAC digital controller can be connected internally to the I2S0 and use its DMA for continuous conversion. Although the DAC only needs 8-bit data for conversion, it has to be left shifted 8 bits (i.e. the high 8 bits in 16-bit slot) to satisfy the I2S communication format. But the driver can help to expand automatically, if you want to expand manually, please disable :ref:`CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` in the menuconfig. + On ESP32, the DAC digital controller can be connected internally to the I2S0 and use its DMA for continuous conversion. Although the DAC only needs 8-bit data for conversion, it has to be the left-shifted 8 bits (i.e., the high 8 bits in a 16-bit slot) to satisfy the I2S communication format. By default, the driver helps to expand the data to 16-bit wide automatically. To expand manually, please disable :ref:`CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` in the menuconfig. - The clock of DAC digital controller comes from I2S0 as well, so there are two kinds of clock source can be selected: + The clock of the DAC digital controller comes from I2S0 as well, so there are two clock sources for selection: - - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_PLL_D2` can support frequency between 19.6 KHz to several MHz. It is the default clock which can also be selected by :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT`. - - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APLL` can support frequency between 648 Hz to several MHz, however, it might be occupied by other peripherals, then it may not provide the required frequency. But it doesn't mean APLL is not available in this case, it can still work as long as it can be divided to the target DAC DMA frequency correctly. + - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_PLL_D2` supports frequency between 19.6 KHz to several MHz. It is the default clock which can also be selected by :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT`. + - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APLL` supports frequency between 648 Hz to several MHz. However, it might be occupied by other peripherals, thus not providing the required frequency. In such case, this clock source is available only if APLL still can be correctly divided into the target DAC DMA frequency. .. only:: esp32s2 - On ESP32-S2, DAC digital controller can be connected internally to the SPI3 and use its DMA for continuous conversion. + On ESP32-S2, the DAC digital controller can be connected internally to the SPI3 and use its DMA for continuous conversion. - The clock source of DAC digital controller are: + The clock sources of the DAC digital controller include: - - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APB` can support frequency between 77 Hz to several MHz. It is the default clock which can also be selected by :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT`. - - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APLL` can support frequency between 6 Hz to several MHz, however, it might be occupied by other peripherals, then it may not provide the required frequency. But it doesn't mean APLL is not available in this case, it can still work as long as it can be divided to the target DAC DMA frequency correctly. + - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APB` supports frequency between 77 Hz to several MHz. It is the default clock which can also be selected by :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT`. + - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APLL` supports frequency between 6 Hz to several MHz. However, it might be occupied by other peripherals, thus not providing the required frequency. In such case, this clock source is available only if APLL still can be correctly divided into the target DAC DMA frequency. Cosine Wave Output (Cosine Mode) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The DAC peripheral has one cosine wave generator, it can generate cosine wave on the channels, users can specify the frequency, amplitude and phase of the cosine wave. To output the cosine wave, please acquire the DAC to cosine mode handle by :cpp:func:`dac_cosine_new_channel` first, and then start the cosine wave generator by :cpp:func:`dac_cosine_start`. +The DAC peripheral has a cosine wave generator, which can generate cosine wave on the channels. Users can specify the frequency, amplitude, and phase of the cosine wave. To output the cosine wave, please acquire the DAC to cosine mode using :cpp:func:`dac_cosine_new_channel`, and then start the cosine wave generator by :cpp:func:`dac_cosine_start`. -Currently, the source clock of the cosine wave generator only comes from ``RTC_FAST`` which can be chosen by :cpp:enumerator:`dac_cosine_clk_src_t::DAC_COSINE_CLK_SRC_RTC_FAST`, it is also the default clock source which is same as :cpp:enumerator:`dac_cosine_clk_src_t::DAC_COSINE_CLK_SRC_RTC_DEFAULT`. +Currently, the clock source of the cosine wave generator only comes from ``RTC_FAST`` which can be selected by :cpp:enumerator:`dac_cosine_clk_src_t::DAC_COSINE_CLK_SRC_RTC_FAST`. It is also the default clock source which is the same as :cpp:enumerator:`dac_cosine_clk_src_t::DAC_COSINE_CLK_SRC_RTC_DEFAULT`. 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 DAC before going into light sleep, thus potentially influence to the DAC signals may lead the data conversion goes wrong. +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 source clock in DMA or cosine wave mode 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 (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 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. 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, which is not expected in a real-time application. +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. -There's a Kconfig option :ref:`CONFIG_DAC_ISR_IRAM_SAFE` that will: +To avoid such case in real-time applications, you can enable the Kconfig option :ref:`CONFIG_DAC_ISR_IRAM_SAFE` which will: -1. Enable the interrupt being serviced even when cache is disabled +1. Enable the interrupt being serviced even when cache is disabled; -2. Place driver object into DRAM (in case it's linked to PSRAM by accident) +2. Place 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. Thread Safety ^^^^^^^^^^^^^ -All the public DAC APIs are guaranteed to be thread safe by the driver, which means, users can call them from different RTOS tasks without protection by extra locks. Notice that DAC driver uses mutex lock to ensure the thread safety, thus the APIs except :cpp:func:`dac_oneshot_output_voltage` are not allowed to be used in ISR. +All the public DAC APIs are guaranteed to be thread safe by the driver, which means users can call them from different RTOS tasks without protection by extra locks. Notice that the DAC driver uses mutex lock to ensure the thread safety, thus the APIs except :cpp:func:`dac_oneshot_output_voltage` are not allowed to be used in ISR. 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 compiling warning message while using the legacy DAC driver. -- :ref:`CONFIG_DAC_ENABLE_DEBUG_LOG` is used to enabled the debug log output. Enable this option will increase the firmware binary size. +- :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. .. only:: esp32 @@ -125,7 +129,7 @@ Kconfig Options Application Example ------------------- -The basic examples for the ``One-shot Mode``, ``Continuous Mode`` and ``Cosine Mode`` can be found in: +The basic examples for the ``One-shot Mode``, ``Continuous Mode``, and ``Cosine Mode`` can be found in: - :example:`peripherals/dac/dac_oneshot` - :example:`peripherals/dac/dac_continuous` diff --git a/docs/zh_CN/api-reference/peripherals/dac.rst b/docs/zh_CN/api-reference/peripherals/dac.rst index f0f7f7166328..b20233297241 100644 --- a/docs/zh_CN/api-reference/peripherals/dac.rst +++ b/docs/zh_CN/api-reference/peripherals/dac.rst @@ -1 +1,145 @@ -.. include:: ../../../en/api-reference/peripherals/dac.rst \ No newline at end of file +数模转换器 (DAC) +================= + +:link_to_translation:`en:[English]` + +{IDF_TARGET_DAC_CH_1: default = "GPIO25", esp32 = "GPIO25", esp32s2 = "GPIO17"} +{IDF_TARGET_DAC_CH_2: default = "GPIO26", esp32 = "GPIO26", esp32s2 = "GPIO18"} + +概况 +---- + +{IDF_TARGET_NAME} 有两个 8 位数模转换器 (DAC) 通道,分别连接到 {IDF_TARGET_DAC_CH_1}(通道 1)和 {IDF_TARGET_DAC_CH_2}(通道 2)。每个 DAC 通道可以将数字值 0~255 转换成模拟电压 0~Vref。输出电压可按以下方式计算:: + + out_voltage = Vref * digi_val / 255 + +DAC 外设支持以下列方式输出模拟信号: + +1. 直接输出电压。DAC 通道持续输出某一指定电压。 +2. 通过 DMA 输出连续模拟信号。DAC 以某一特定频率转换缓冲器中的数据。 +3. 通过余弦波发生器输出余弦波。DAC 通道可以输出特定频率和振幅的余弦波。 + +其他模拟输出选项可参考 :doc:`Sigma-Delta 调制 ` 和 :doc:`LED PWM 控制器 `。这两个模块均输出高频的 PWM/PDM 信号,也可借助硬件低通滤波输出较低频率的模拟信号。 + +DAC 文件结构 +------------ + +.. figure:: ../../../_static/diagrams/dac/dac_file_structure.png + :align: center + :alt: DAC 文件结构 + + DAC 文件结构 + + +**需包含在 DAC 应用程序中的公共头文件包括:** + +- ``dac.h``:原有 DAC 驱动的最上层头文件,只包含在使用原有驱动 API 的应用程序中。 +- ``dac_oneshot.h``:新 DAC 驱动的最上层头文件,应包含在使用新驱动 API(单次模式)的应用程序中。 +- ``dac_cosine.h``:新 DAC 驱动的最上层头文件,应包含在使用新驱动 API(余弦模式)的应用程序中。 +- ``dac_continuous.h``:新 DAC 驱动的最上层头文件,应包含在使用新驱动 API(连续模式)的应用程序中。 + +.. note:: + + 原有驱动程序与新驱动程序无法共存。使用原有驱动需包含 ``dac.h``,使用新驱动需包含 ``dac_oneshot.h``、 ``dac_cosine.h`` 和 ``dac_continuous.h``。后续更新或将移除原有驱动程序。 + +功能概览 +-------- + +资源管理 +^^^^^^^^ + +{IDF_TARGET_NAME} 有两个 DAC 通道。通道的软件资源互相独立,用户可以根据具体情况调用 :cpp:type:`dac_oneshot_handle_t`、 :cpp:type:`dac_cosine_handle_t` 或 :cpp:type:`dac_continuous_handle_t` 来管理不同通道,但不支持在同一个通道上注册不同模式。 + +电压直接输出(单次/直接模式) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +在这种模式下,DAC 通道每次调用 :cpp:func:`dac_oneshot_output_voltage` (可在 ISR 中调用)时都可以将一个 8 位数字转换为模拟值。直至下一次转换开始前,DAC 通道上都将维持该模拟电压。开始转换电压前,需要首先调用 :cpp:func:`dac_oneshot_new_channel` 来启用该 DAC 通道。 + +连续波输出(连续/DMA 模式) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +DAC 通道可以通过 DMA 连续转换数字信号,这种模式下有三种写入 DAC 数据的方法: + + 1. 常规写入(同步):一次性传输所有数据并在所有数据均已载入 DMA 缓冲区前保持阻塞状态。如果不再继续输入数据,电压将维持在最后的转换值。该模式通常用于传输音频等长信号。要连续转换数据,需要调用 :cpp:func:`dac_continuous_new_channels` 来分配连续通道句柄,调用 :cpp:func:`dac_continuous_enable` 来启用 DMA 转换,然后调用 :cpp:func:`dac_continuous_write` 来同步写入数据。示例可参考 :example:`peripherals/dac/dac_continuous/dac_audio`。 + 2. 循环写入:在数据载入 DMA 缓冲区后,缓冲区中的数据将以非阻塞的方式被循环转换。但要注意,输入的缓冲区大小受 DMA 描述符数量和 DMA 缓冲区大小的限制。该模式通常用于传输如正弦波等需要重复的短信号。为了启用循环写入,需要在启用 DAC 连续模式后调用 :cpp:func:`dac_continuous_write_cyclically`。示例可参考 :example:`peripherals/dac/dac_continuous/signal_generator`。 + 3. 异步写入。可根据事件回调异步传输数据。需要调用 :cpp:member:`dac_event_callbacks_t::on_convert_done` 以启用异步模式。用户在回调中可得到 :cpp:type:`dac_event_data_t`,其中包含 DMA 缓冲区的地址和长度,即允许用户直接将数据载入 DMA 缓冲区。启用异步写入前需要调用 :cpp:func:`dac_continuous_register_event_callback`、 :cpp:member:`dac_event_callbacks_t::on_convert_done` 和 :cpp:func:`dac_continuous_start_async_writing`。注意,异步写入一旦开始,回调函数将被持续触发。调用 :cpp:func:`dac_continuous_write_asynchronously` 可以在某个单独任务中或直接在回调函数中载入数据。示例可参考 :example:`peripherals/dac/dac_continuous/dac_audio`。 + +.. only:: esp32 + + 在 ESP32 上,DAC 的数字控制器可以在内部连接到 I2S0,并借用其 DMA 进行连续转换。虽然 DAC 转换仅需 8 位数据,但它必须是左移的 8 位(即 16 位中的高 8 位),以满足 I2S 通信格式。默认状态下驱动程序将自动扩充数据至 16 位,如需手动扩充,请在 menuconfig 中禁用 :ref:`CONFIG_DAC_DMA_AUTO_16BIT_ALIGN`。 + + DAC 的数字控制器的时钟也来自 I2S0,有以下两种时钟源可选: + + - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_PLL_D2` 支持 19.6 KHz 到若干 MHz 之间的频率。该时钟源为默认时钟源,也可通过选择 :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT` 来启用该时钟源。 + - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APLL` 支持 648 Hz 到若干 MHz 之间的频率。该时钟源可能会被其他外设占用而导致频率无法更改,此时除非 APLL 仍能准确分频得到 DAC DMA 的目标频率,否则将无法使用该时钟源。 + +.. only:: esp32s2 + + 在 ESP32-S2 上,DAC 的数字控制器可以在内部连接到 SPI3,并借用其 DMA 进行连续转换。 + + DAC 的数字控制器的时钟源包括: + + - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APB` 支持 77 Hz 到若干 MHz 之间的频率。该时钟源为默认时钟源,也可通过选择 :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_DEFAULT` 来启用该时钟源。 + - :cpp:enumerator:`dac_continuous_digi_clk_src_t::DAC_DIGI_CLK_SRC_APLL` 支持 6 Hz 到若干 MHz 之间的频率。该时钟源可能会被其他外设占用,导致无法提供所需频率。该时钟源可能会被其他外设占用而导致频率无法更改,此时除非 APLL 仍能准确分频得到 DAC DMA 的目标频率,否则将无法使用该时钟源。 + + +余弦波输出(余弦模式) +^^^^^^^^^^^^^^^^^^^^^^^^ + +DAC 外设中包含一个余弦波发生器,可以在通道上产生余弦波。用户可以配置余弦波的频率、振幅和相位。启用该模式需要先调用 :cpp:func:`dac_cosine_new_channel` 将 DAC 转换成余弦模式,然后调用 :cpp:func:`dac_cosine_start` 启动余弦波发生器。 + +目前,余弦波发生器仅有 ``RTC_FAST`` 一个时钟源,可通过选择 :cpp:enumerator:`dac_cosine_clk_src_t::DAC_COSINE_CLK_SRC_RTC_FAST` 来启用该时钟源。该时钟源为默认时钟源,与 :cpp:enumerator:`dac_cosine_clk_src_t::DAC_COSINE_CLK_SRC_RTC_DEFAULT` 相同。 + +电源管理 +^^^^^^^^ + +启用电源管理时(即开启 :ref:`CONFIG_PM_ENABLE`),系统会在进入 Light-sleep 模式前调整或停止 DAC 时钟源,这可能会影响 DAC 信号,从而导致数据无法正确转换。 + +在连续模式下使用 DAC 驱动时,可以通过获取电源管理锁来防止系统在 DMA 或余弦波模式下改变或停止时钟源。时钟源为 APB 时,锁的类型将被设置为 :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_APB_FREQ_MAX`。时钟源为 APLL 时(仅在 DMA 模式下),锁的类型将被设置为 :cpp:enumerator:`esp_pm_lock_type_t::ESP_PM_NO_LIGHT_SLEEP`。在进行 DAC 转换时(即 DMA 或余弦波发生器运行时),驱动程序会保证在调用 :cpp:func:`dac_continuous_enable` 后获取电源管理锁。同样地,在调用 :cpp:func:`dac_continuous_disable` 时,驱动程序会释放锁。 + +IRAM 安全 +^^^^^^^^^ + +默认情况下,由于写入或擦除 flash 等原因导致 cache 被禁用时,DAC 的 DMA 中断将产生延迟,无法及时执行 DMA EOF 中断。 + +在实时应用中,可通过启用 Kconfig 选项 :ref:`CONFIG_DAC_ISR_IRAM_SAFE` 来避免此种情况发生,启用后: + +1. 即使在 cache 被禁用的情况下,也可以启用中断服务。 + +2. 驱动对象会被放入 DRAM(以防其被意外链接到 PSRAM)。 + +此时在 cache 被禁用时仍可以运行中断,但会增加 IRAM 内存消耗。 + +线程安全 +^^^^^^^^ + +驱动程序可保证所有公共 DAC API 的线程安全,用户可以从不同的 RTOS 任务中调用这些 API,而不需要额外的锁来保护。注意,DAC 驱动使用 mutex 锁来保证线程安全,因此不允许在 ISR 中使用除了 :cpp:func:`dac_oneshot_output_voltage` 之外的 API。 + +Kconfig 选项 +^^^^^^^^^^^^^ + +- :ref:`CONFIG_DAC_ISR_IRAM_SAFE` 控制默认 ISR 处理程序在 cache 被禁用时能否继续运行。更多信息可参考 `IRAM 安全 <#iram-safe>`__。 +- :ref:`CONFIG_DAC_SUPPRESS_DEPRECATE_WARN` 控制是否在使用原有 DAC 驱动时关闭警告信息。 +- :ref:`CONFIG_DAC_ENABLE_DEBUG_LOG` 用于启用调试日志输出。启用该选项将增加固件的二进制文件大小。 + +.. only:: esp32 + + - :ref:`CONFIG_DAC_DMA_AUTO_16BIT_ALIGN` 会在驱动中自动将 8 位数据扩展为 16 位数据,以满足 I2S DMA 格式。 + +应用示例 +-------- + +``单次模式``、 ``连续模式`` 和 ``余弦模式`` 的基本示例如下所示: + +- :example:`peripherals/dac/dac_oneshot` +- :example:`peripherals/dac/dac_continuous` +- :example:`peripherals/dac/dac_cosine_wave` + +API 参考 +-------- + +.. include-build-file:: inc/dac_oneshot.inc +.. include-build-file:: inc/dac_cosine.inc +.. include-build-file:: inc/dac_continuous.inc +.. include-build-file:: inc/components/driver/dac/include/driver/dac_types.inc +.. include-build-file:: inc/components/hal/include/hal/dac_types.inc