This is an automated email from the ASF dual-hosted git repository.

acassis pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 990825110d433ea944ff81f800b18c47a9ad9040
Author: Andrey Sobol <[email protected]>
AuthorDate: Sat Jul 25 22:02:50 2026 +0300

    Documentation/dac: add STM32H7 on-chip DAC driver docs
    
    Covers basic write mode, DMA circular and stream modes with code examples,
    data structures, ioctl commands, and Kconfig configuration.
    
    Signed-off-by: Andrey Sobol <[email protected]>
---
 .../drivers/character/analog/dac/index.rst         |   8 +
 .../drivers/character/analog/dac/stm32h7/index.rst | 395 +++++++++++++++++++++
 2 files changed, 403 insertions(+)

diff --git a/Documentation/components/drivers/character/analog/dac/index.rst 
b/Documentation/components/drivers/character/analog/dac/index.rst
index 65eba090570..1812e7a2c29 100644
--- a/Documentation/components/drivers/character/analog/dac/index.rst
+++ b/Documentation/components/drivers/character/analog/dac/index.rst
@@ -83,3 +83,11 @@ SPI-based DACs:
 
   dac7554/index.rst
   mcp48xx/index.rst
+
+On-Chip DAC Drivers
+===================
+
+.. toctree::
+  :maxdepth: 1
+
+  stm32h7/index.rst
diff --git 
a/Documentation/components/drivers/character/analog/dac/stm32h7/index.rst 
b/Documentation/components/drivers/character/analog/dac/stm32h7/index.rst
new file mode 100644
index 00000000000..ffdf2c26c66
--- /dev/null
+++ b/Documentation/components/drivers/character/analog/dac/stm32h7/index.rst
@@ -0,0 +1,395 @@
+====================
+STM32H7 DAC Driver
+====================
+
+.. tags:: chip:stm32h7, arch:arm, vendor:st, peripheral:dac
+
+The STM32H7 DAC is a 12-bit, voltage-output, dual-channel digital-to-analog
+converter embedded in the STM32H7 MCU. The driver supports two operating modes:
+
+  *Basic (direct write) mode* — the application writes individual samples
+  via the standard POSIX ``write()`` call.
+
+  *DMA mode* — the DAC outputs samples from an internal DMA buffer at a
+  rate determined by a timer. DMA mode itself has two sub-modes: circular
+  (fixed buffer replayed periodically) and stream (double-buffered with
+  half-transfer interrupts for real-time refill).
+
+Hardware Features
+=================
+
+- 12-bit resolution
+- 2 independent channels (DAC1_CH1 on PA4, DAC1_CH2 on PA5)
+- Built-in output buffer for each channel
+- DMA with timer-triggered transfers
+- Configurable DMA buffer size per channel 
(``CONFIG_STM32_DACxCHy_DMA_BUFFER_SIZE``)
+- Stream mode with half-transfer interrupts for double-buffering
+- Timer range on H7: 1–15, with configurable output frequency
+
+Driver Interface
+================
+
+The STM32H7 DAC driver follows the NuttX upper-half/lower-half DAC
+architecture.  Board-level initialization calls ``stm32_dacinitialize()``
+and registers the device with ``dac_register()``:
+
+.. code-block:: c
+
+   #include <nuttx/config.h>
+   #include <nuttx/analog/dac.h>
+   #include "stm32_dac.h"
+
+   int board_dac_initialize(void)
+   {
+     struct dac_dev_s *dac;
+
+   #ifdef CONFIG_STM32_DAC1CH1
+     dac = stm32_dacinitialize(0);
+     if (dac == NULL)
+       return -ENODEV;
+
+     ret = dac_register("/dev/dac0", dac);
+     if (ret < 0)
+       return ret;
+   #endif
+
+     return OK;
+   }
+
+The DAC is accessed through a character device in ``/dev`` (e.g. 
``/dev/dac0``).
+Standard file operations and driver-specific ``ioctl`` commands are used for
+control and data transfer.
+
+Operating Modes
+===============
+
+Basic Mode (direct write)
+-------------------------
+
+In basic mode the application calls ``write()`` with a ``dac_msg_s``
+structure for each sample.  The upper-half driver queues the message
+and the lower-half interrupt handler writes it to the DAC data register.
+
+.. code-block:: c
+
+   #include <nuttx/analog/dac.h>
+   #include <fcntl.h>
+
+   int fd = open("/dev/dac0", O_WRONLY);
+   if (fd < 0)
+     return ERROR;
+
+   struct dac_msg_s msg;
+   msg.am_channel = 0;          /* channel 1 */
+   msg.am_data    = 2048;       /* mid-scale (0–4095) */
+
+   write(fd, &msg, sizeof(msg));
+
+   close(fd);
+
+Each ``write()`` call delivers one sample.  For continuous waveforms a
+timer or loop is needed; for higher update rates use DMA mode.
+
+DMA Mode
+--------
+
+In DMA mode a timer periodically triggers the DAC to read the next sample
+from the DMA buffer.  The buffer is split into two halves for stream mode
+(double-buffering).  The ``ANIOC_DAC_INFO`` command returns the buffer
+configuration:
+
+.. code-block:: c
+
+   #include <nuttx/analog/dac.h>
+   #include <sys/ioctl.h>
+
+   struct dac_info_s info;
+   ioctl(fd, ANIOC_DAC_INFO, (unsigned long)&info);
+
+   /* info.dma_buffer_size  — total DMA buffer in samples
+    * info.dma_timer_frequency — timer frequency in Hz
+    * The half length is dma_buffer_size / 2.
+    * The per-sample rate is dma_timer_frequency / dma_buffer_size.
+    */
+
+
+Circular sub-mode (``halfint = 0``)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The DMA engine cycles through the entire buffer endlessly.  Only
+transfer-complete interrupts are enabled; no events are delivered
+to the application.  The application must pre-fill the **entire**
+buffer before starting.
+
+**Example:** fill the full DMA buffer with a sawtooth and start circular
+output:
+
+.. code-block:: c
+
+   #include <nuttx/analog/dac.h>
+   #include <sys/ioctl.h>
+
+   struct dac_info_s info;
+   ioctl(fd, ANIOC_DAC_INFO, (unsigned long)&info);
+
+   uint16_t *buf = malloc(info.dma_buffer_size * sizeof(uint16_t));
+
+   for (int i = 0; i < info.dma_buffer_size; i++)
+     buf[i] = (uint16_t)((uint32_t)i * 4095 / info.dma_buffer_size);
+
+   /* Copy the whole buffer into the internal DMA buffer */
+   ioctl(fd, ANIOC_DAC_DMABUFF_INIT, (unsigned long)buf);
+
+   /* Start circular output */
+   struct dac_dma_start_s start;
+   start.halfint = 0;
+   ioctl(fd, ANIOC_DAC_DMA_START, (unsigned long)&start);
+
+   /* Let it run ... */
+   sleep(5);
+
+   ioctl(fd, ANIOC_DAC_DMA_STOP, 0);
+   close(fd);
+   free(buf);
+
+The total DMA buffer length is configured by
+``CONFIG_STM32_DACxCHy_DMA_BUFFER_SIZE`` (default 256).  The
+``DMAHBUF_WRITE`` command can also be used to write each half
+separately instead of ``DMABUFF_INIT``:
+
+.. code-block:: c
+
+   /* Equivalent to one DMABUFF_INIT using two half writes */
+   uint32_t half = info.dma_buffer_size / 2;
+
+   struct dac_dma_event_s ev;
+   ev.buffer = first_half_data;   /* half 0 */
+   ev.half   = 0;
+   ioctl(fd, ANIOC_DAC_DMAHBUF_WRITE, (unsigned long)&ev);
+
+   ev.buffer = second_half_data;  /* half 1 */
+   ev.half   = 1;
+   ioctl(fd, ANIOC_DAC_DMAHBUF_WRITE, (unsigned long)&ev);
+
+Stream sub-mode (``halfint = 1``)
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Both half-transfer (HTIF) and transfer-complete (TCIF) interrupts are
+enabled.  Each time a half-transfer finishes the driver posts an event
+to a semaphore.  The application calls ``GET_EVENT`` (blocking) to
+obtain the index of the completed half, writes new data into that half
+via ``DMAHBUF_WRITE``, and the DMA engine continues filling the other
+half — enabling continuous real-time waveform synthesis.
+
+**Initial fill:** both halves must be written before starting the DMA.
+
+**Refill:** after each ``GET_EVENT`` call, write **only one half**
+(the half whose index was returned).
+
+**Example:** DDS sine wave generation using stream mode:
+
+.. code-block:: c
+
+   #include <nuttx/analog/dac.h>
+   #include <sys/ioctl.h>
+
+   struct dac_info_s info;
+   ioctl(fd, ANIOC_DAC_INFO, (unsigned long)&info);
+
+   uint32_t half_len = info.dma_buffer_size / 2;
+   uint16_t *workbuf = malloc(half_len * sizeof(uint16_t));
+   int phase_acc = 0;
+
+   /* Fill both halves with the initial sine data */
+
+   for (int j = 0; j < 2; j++)
+     {
+       for (int i = 0; i < half_len; i++)
+         {
+           workbuf[i] = sine_table[phase_acc >> 12];
+           phase_acc  = (phase_acc + phase_step) & (1024 * 4096 - 1);
+         }
+       struct dac_dma_event_s ev;
+       ev.buffer = workbuf;
+       ev.half   = j;
+       ioctl(fd, ANIOC_DAC_DMAHBUF_WRITE, (unsigned long)&ev);
+     }
+
+   /* Start stream mode */
+   struct dac_dma_start_s start;
+   start.halfint = 1;
+   ioctl(fd, ANIOC_DAC_DMA_START, (unsigned long)&start);
+
+   /* Refill loop — write one half per event */
+   volatile int stop = 0;
+
+   while (!stop)
+     {
+       struct dac_dma_event_s ev;
+       int ret = ioctl(fd, ANIOC_DAC_DMA_GET_EVENT,
+                       (unsigned long)&ev);
+       if (ret < 0)
+         break;                 /* e.g. -EINTR from signal */
+
+       /* Fill the completed half with new sine samples */
+       for (int i = 0; i < half_len; i++)
+         {
+           workbuf[i] = sine_table[phase_acc >> 12];
+           phase_acc  = (phase_acc + phase_step) & (1024 * 4096 - 1);
+         }
+
+       ev.buffer = workbuf;      /* write to the half returned by GET_EVENT */
+       ioctl(fd, ANIOC_DAC_DMAHBUF_WRITE, (unsigned long)&ev);
+     }
+
+   ioctl(fd, ANIOC_DAC_DMA_STOP, 0);
+   close(fd);
+   free(workbuf);
+
+DMA Structures
+==============
+
+These structures are defined in ``include/nuttx/analog/dac.h``.
+
+.. c:struct:: dac_dma_start_s
+
+Describes the DMA start configuration:
+
+.. code-block:: c
+
+   struct dac_dma_start_s
+   {
+     uint8_t halfint; /* 0 = circular (TC only),
+                       * 1 = stream (HT + TC) */
+   };
+
+.. c:struct:: dac_dma_event_s
+
+Carries a half-buffer pointer and half index for stream mode:
+
+.. code-block:: c
+
+   struct dac_dma_event_s
+   {
+     FAR uint16_t *buffer; /* data to write (DMAHBUF_WRITE) */
+     int half;             /* 0 = first half, 1 = second half
+                            * (returned by GET_EVENT,
+                            *  set by caller for DMAHBUF_WRITE) */
+   };
+
+.. c:struct:: dac_info_s
+
+Contains DAC capabilities and runtime state:
+
+.. code-block:: c
+
+   struct dac_info_s
+   {
+     uint8_t  sample_bits;          /* DAC resolution (12 bits) */
+     uint8_t  dma_enabled;          /* 1 if DMA is currently running */
+     uint8_t  halfint_enabled;      /* 1 if stream (HT) mode is active */
+     uint32_t dma_buffer_size;      /* total DMA buffer length in samples */
+     uint32_t dma_timer_frequency;  /* timer output frequency in Hz */
+   };
+
+DMA ioctl Commands
+==================
+
+These commands are defined in ``include/nuttx/analog/ioctl.h``.
+
+The following commands are available when DMA is enabled
+(``CONFIG_STM32_DAC1CH1_DMA`` or ``CONFIG_STM32_DAC1CH2_DMA``).
+For the standard DAC ioctl commands see
+:doc:`/components/drivers/character/analog/dac/index`.
+
+.. c:macro:: ANIOC_DAC_DMABUFF_INIT
+
+Copy the **entire** user buffer into the internal DMA buffer
+(``dma_buffer_size`` samples).  The caller must provide a buffer
+of exactly ``dma_buffer_size * sizeof(uint16_t)`` bytes.
+
+   *IN*: ``uint16_t *`` — source buffer
+   *OUT*: None
+
+.. c:macro:: ANIOC_DAC_DMA_START
+
+Start the DMA transfer.  The argument is a ``struct dac_dma_start_s *``.
+
+   *IN*: ``struct dac_dma_start_s *``
+   *OUT*: None
+
+.. c:macro:: ANIOC_DAC_DMA_STOP
+
+Stop the DMA transfer and the associated timer.
+
+   *IN*: None
+   *OUT*: None
+
+.. c:macro:: ANIOC_DAC_DMA_GET_EVENT
+
+Wait (blocking) for a half-transfer DMA event.  Returns the index of
+the completed half (0 or 1) in ``dac_dma_event_s::half``.
+Returns ``-EINTR`` if interrupted by a signal (e.g. Ctrl+C).
+
+   *IN*: None
+   *OUT*: ``struct dac_dma_event_s *`` — ``half`` set to the completed half
+
+.. c:macro:: ANIOC_DAC_DMAHBUF_WRITE
+
+Write data into **one half** of the internal DMA buffer.  The
+``buffer`` field points to the user data; ``half`` selects which half
+(0 = first, 1 = second) to overwrite.  The data length is always
+``(dma_buffer_size / 2) * sizeof(uint16_t)`` bytes.
+
+   *IN*: ``struct dac_dma_event_s *``
+   *OUT*: None
+
+.. c:macro:: ANIOC_DAC_INFO
+
+Query DAC capabilities and current runtime state.  Returns the values
+in a ``struct dac_info_s *``.  This command does not require DMA to be
+enabled; it always reports the fixed resolution (12 bits) and,
+depending on configuration, may return zero for DMA-related fields.
+
+   *IN*: None
+   *OUT*: ``struct dac_info_s *``
+
+Configuration
+=============
+
+Each channel has its own Kconfig options:
+
+.. list-table:: DAC Channel Configuration Options
+   :widths: auto
+
+   * - Name
+     - Description
+   * - ``CONFIG_STM32_DAC1CH1`` / ``CONFIG_STM32_DAC1CH2``
+     - Enable DAC1 channel 1 / channel 2
+   * - ``CONFIG_STM32_DAC1CH1_DMA`` / ``CONFIG_STM32_DAC1CH2_DMA``
+     - Enable DMA for the channel
+   * - ``CONFIG_STM32_DAC1CH1_DMA_BUFFER_SIZE``
+     - DMA buffer size in samples (default 256)
+   * - ``CONFIG_STM32_DAC1CH2_DMA_BUFFER_SIZE``
+     - DMA buffer size in samples (default 256)
+   * - ``CONFIG_STM32_DAC1CH1_DMA_PRIORITY_{LOW,MEDIUM,HIGH,VERYHIGH}``
+     - DMA stream priority (default Medium)
+   * - ``CONFIG_STM32_DAC1CH2_DMA_PRIORITY_{LOW,MEDIUM,HIGH,VERYHIGH}``
+     - DMA stream priority (default Medium)
+   * - ``CONFIG_STM32_DAC1CH1_TIMER``
+     - Timer number for DMA trigger (range 1–15 on H7)
+   * - ``CONFIG_STM32_DAC1CH2_TIMER``
+     - Timer number for DMA trigger (range 1–15 on H7)
+   * - ``CONFIG_STM32_DAC1CH1_TIMER_FREQUENCY``
+     - Timer output frequency in Hz
+   * - ``CONFIG_STM32_DAC1CH2_TIMER_FREQUENCY``
+     - Timer output frequency in Hz
+
+The corresponding timer peripheral must also be enabled:
+
+.. code-block::
+
+   CONFIG_STM32_TIM<n>=y
+
+where ``<n>`` matches the timer number set in ``STM32_DAC1CH1_TIMER``
+(or ``STM32_DAC1CH2_TIMER``).

Reply via email to