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 409b195b039ae782ff1982e331e0096b94a6fdf5
Author: Darryl Ring <[email protected]>
AuthorDate: Wed Oct 7 10:18:48 2026 -0700

    arch/arm/stm32h5: Add DAC support
    
    Copy DAC support from STM32H7 with no DMA or timer control and
    accounting for the differences between the H7 and H5
    
    Assisted-by: Claude:claude-sonnet-5-5
    Signed-off-by: Darryl Ring <[email protected]>
---
 Documentation/platforms/arm/stm32h5/index.rst      |   2 +-
 arch/arm/src/common/stm32/Kconfig.dac              |   4 +-
 arch/arm/src/stm32h5/CMakeLists.txt                |   4 +
 arch/arm/src/stm32h5/Kconfig                       |   1 +
 arch/arm/src/stm32h5/Make.defs                     |   4 +
 arch/arm/src/stm32h5/hardware/stm32_dac.h          | 158 +++++++
 arch/arm/src/stm32h5/hardware/stm32h50xxx_pinmap.h |   5 +
 arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h |   5 +
 arch/arm/src/stm32h5/stm32.h                       |   1 +
 arch/arm/src/stm32h5/stm32_dac.c                   | 483 +++++++++++++++++++++
 arch/arm/src/stm32h5/stm32_dac.h                   | 124 ++++++
 arch/arm/src/stm32h5/stm32h5xx_rcc.c               |  12 +-
 12 files changed, 799 insertions(+), 4 deletions(-)

diff --git a/Documentation/platforms/arm/stm32h5/index.rst 
b/Documentation/platforms/arm/stm32h5/index.rst
index 5f1bd78c45a..5cd754c7890 100644
--- a/Documentation/platforms/arm/stm32h5/index.rst
+++ b/Documentation/platforms/arm/stm32h5/index.rst
@@ -54,7 +54,7 @@ CEC         No
 CORDIC      No
 CRC         Yes
 CRS         No
-DAC         No
+DAC         Yes
 DBG         No
 DCACHE      No
 DCMI        No
diff --git a/arch/arm/src/common/stm32/Kconfig.dac 
b/arch/arm/src/common/stm32/Kconfig.dac
index 5dc6d55ea30..6b38c01469d 100644
--- a/arch/arm/src/common/stm32/Kconfig.dac
+++ b/arch/arm/src/common/stm32/Kconfig.dac
@@ -83,7 +83,7 @@ config STM32_DAC1CH1_MODE
 
 config STM32_DAC1CH1_DMA
        bool "DAC1CH1 DMA"
-       depends on STM32_DAC1CH1
+       depends on STM32_DAC1CH1 && !ARCH_CHIP_STM32H5
        default n
        ---help---
                If DMA is selected, then a timer and output frequency must also 
be
@@ -165,7 +165,7 @@ config STM32_DAC1CH2_MODE
 
 config STM32_DAC1CH2_DMA
        bool "DAC1CH2 DMA"
-       depends on STM32_DAC1CH2
+       depends on STM32_DAC1CH2 && !ARCH_CHIP_STM32H5
        default n
        ---help---
                If DMA is selected, then a timer and output frequency must also 
be
diff --git a/arch/arm/src/stm32h5/CMakeLists.txt 
b/arch/arm/src/stm32h5/CMakeLists.txt
index cecfaacbc03..6d7386c87a5 100644
--- a/arch/arm/src/stm32h5/CMakeLists.txt
+++ b/arch/arm/src/stm32h5/CMakeLists.txt
@@ -44,6 +44,10 @@ if(CONFIG_ADC)
   list(APPEND SRCS stm32_adc.c)
 endif()
 
+if(CONFIG_STM32_DAC)
+  list(APPEND SRCS stm32_dac.c)
+endif()
+
 if(CONFIG_STM32_FDCAN_CHARDRIVER)
   list(APPEND SRCS stm32_fdcan.c)
 endif()
diff --git a/arch/arm/src/stm32h5/Kconfig b/arch/arm/src/stm32h5/Kconfig
index 4cddcdffc46..a3236fdbdae 100644
--- a/arch/arm/src/stm32h5/Kconfig
+++ b/arch/arm/src/stm32h5/Kconfig
@@ -34,6 +34,7 @@ config STM32_H5_PERIPHERALS
        select STM32_HAVE_USBFS_MODE
        select STM32_HAVE_USBDRD_HOST
        select STM32_HAVE_ADC_H5
+       select STM32_HAVE_DAC1
        select STM32_HAVE_USART_H5
        select STM32_HAVE_USART_WUS
        select STM32_HAVE_I2C_H5
diff --git a/arch/arm/src/stm32h5/Make.defs b/arch/arm/src/stm32h5/Make.defs
index 1793dc386cf..99525ed6615 100644
--- a/arch/arm/src/stm32h5/Make.defs
+++ b/arch/arm/src/stm32h5/Make.defs
@@ -52,6 +52,10 @@ ifeq ($(CONFIG_ADC),y)
 CHIP_CSRCS += stm32_adc.c
 endif
 
+ifeq ($(CONFIG_STM32_DAC),y)
+CHIP_CSRCS += stm32_dac.c
+endif
+
 ifeq ($(CONFIG_STM32_FDCAN_CHARDRIVER),y)
 CHIP_CSRCS += stm32_fdcan.c
 endif
diff --git a/arch/arm/src/stm32h5/hardware/stm32_dac.h 
b/arch/arm/src/stm32h5/hardware/stm32_dac.h
new file mode 100644
index 00000000000..73b32e22a89
--- /dev/null
+++ b/arch/arm/src/stm32h5/hardware/stm32_dac.h
@@ -0,0 +1,158 @@
+/****************************************************************************
+ * arch/arm/src/stm32h5/hardware/stm32_dac.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_DAC_H
+#define __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_DAC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Register Offsets *********************************************************/
+
+#define STM32_DAC1_CR_OFFSET        0x0000  /* control register */
+#define STM32_DAC1_SWTRGR_OFFSET    0x0004  /* software trigger register */
+#define STM32_DAC1_DHR12R1_OFFSET   0x0008  /* ch1 12-bit right aligned data 
hold register */
+#define STM32_DAC1_DHR12L1_OFFSET   0x000C  /* ch1 12-bit left aligned data 
hold register */
+#define STM32_DAC1_DHR8R1_OFFSET    0x0010  /* ch1 8-bit right aligned data 
hold register */
+#define STM32_DAC1_DHR12R2_OFFSET   0x0014  /* ch2 12-bit right aligned data 
hold register */
+#define STM32_DAC1_DHR12L2_OFFSET   0x0018  /* ch2 12-bit left aligned data 
hold register */
+#define STM32_DAC1_DHR8R2_OFFSET    0x001C  /* ch2 8-bit right aligned data 
hold register */
+#define STM32_DAC1_DHR12RD_OFFSET   0x0020  /* dual 12-bit right aligned data 
hold register */
+#define STM32_DAC1_DHR12LD_OFFSET   0x0024  /* dual 2 12-bit left aligned data 
hold register */
+#define STM32_DAC1_DHR8RD_OFFSET    0x0028  /* dual 2 8-bit right aligned data 
hold register */
+#define STM32_DAC1_DOR1_OFFSET      0x002C  /* ch1 data output register */
+#define STM32_DAC1_DOR2_OFFSET      0x0030  /* ch2 data output register */
+#define STM32_DAC1_SR_OFFSET        0x0034  /* status register */
+#define STM32_DAC1_CCR_OFFSET       0x0038  /* calibration control register */
+#define STM32_DAC1_MCR_OFFSET       0x003C  /* mode control register */
+#define STM32_DAC1_SHSR1_OFFSET     0x0040  /* ch1 sample and hold sample time 
register */
+#define STM32_DAC1_SHSR2_OFFSET     0x0044  /* ch2 sample and hold sample time 
register */
+#define STM32_DAC1_SHHR_OFFSET      0x0048  /* sample and hold time register */
+#define STM32_DAC1_SHRR_OFFSET      0x004C  /* sample and hold refresh time 
register */
+
+/* Register Addresses *******************************************************/
+
+#define STM32_DAC1_CR               (STM32_DAC1_BASE + STM32_DAC1_CR_OFFSET)
+#define STM32_DAC1_SWTRGR           (STM32_DAC1_BASE + 
STM32_DAC1_SWTRGR_OFFSET)
+#define STM32_DAC1_DHR12R1          (STM32_DAC1_BASE + 
STM32_DAC1_DHR12R1_OFFSET)
+#define STM32_DAC1_DHR12L1          (STM32_DAC1_BASE + 
STM32_DAC1_DHR12L1_OFFSET)
+#define STM32_DAC1_DHR8R1           (STM32_DAC1_BASE + 
STM32_DAC1_DHR8R1_OFFSET)
+#define STM32_DAC1_DHR12R2          (STM32_DAC1_BASE + 
STM32_DAC1_DHR12R2_OFFSET)
+#define STM32_DAC1_DHR12L2          (STM32_DAC1_BASE + 
STM32_DAC1_DHR12L2_OFFSET)
+#define STM32_DAC1_DHR8R2           (STM32_DAC1_BASE + 
STM32_DAC1_DHR8R2_OFFSET)
+#define STM32_DAC1_DHR12RD          (STM32_DAC1_BASE + 
STM32_DAC1_DHR12RD_OFFSET)
+#define STM32_DAC1_DHR12LD          (STM32_DAC1_BASE + 
STM32_DAC1_DHR12LD_OFFSET)
+#define STM32_DAC1_DHR8RD           (STM32_DAC1_BASE + 
STM32_DAC1_DHR8RD_OFFSET)
+#define STM32_DAC1_DOR1             (STM32_DAC1_BASE + STM32_DAC1_DOR1_OFFSET)
+#define STM32_DAC1_DOR2             (STM32_DAC1_BASE + STM32_DAC1_DOR2_OFFSET)
+#define STM32_DAC1_SR               (STM32_DAC1_BASE + STM32_DAC1_SR_OFFSET)
+#define STM32_DAC1_CCR              (STM32_DAC1_BASE + STM32_DAC1_CCR_OFFSET)
+#define STM32_DAC1_MCR              (STM32_DAC1_BASE + STM32_DAC1_MCR_OFFSET)
+#define STM32_DAC1_SHSR1            (STM32_DAC1_BASE + STM32_DAC1_SHSR1_OFFSET)
+#define STM32_DAC1_SHSR2            (STM32_DAC1_BASE + STM32_DAC1_SHSR2_OFFSET)
+#define STM32_DAC1_SHHR             (STM32_DAC1_BASE + STM32_DAC1_SHHR_OFFSET)
+#define STM32_DAC1_SHRR             (STM32_DAC1_BASE + STM32_DAC1_SHRR_OFFSET)
+
+/* Register Bitfield Definitions ********************************************/
+
+/* DAC control register */
+
+#define DAC_CR_EN1                  (1 << 0)  /* Bit 0: ch1 enable */
+#define DAC_CR_TEN1                 (1 << 1)  /* Bit 1: ch1 trigger enable */
+#define DAC_CR_TSEL1_SHIFT          (2)       /* Bits 5-2: ch1 trigger sel */
+#define DAC_CR_TSEL1_MASK           (15 << DAC_CR_TSEL1_SHIFT)
+#define DAC_CR_WAVE1_SHIFT          (6)       /* Bits 7-6: ch1 wave enable */
+#define DAC_CR_WAVE1_MASK           (3 << DAC_CR_WAVE1_SHIFT)
+#define DAC_CR_MAMP1_SHIFT          (8)       /* Bits 11-8: ch1 mask/amplitude 
*/
+#define DAC_CR_MAMP1_MASK           (15 << DAC_CR_MAMP1_SHIFT)
+#define DAC_CR_DMAEN1               (1 << 12)  /* Bit 12: ch1 dma enable */
+#define DAC_CR_DMAUDRIE1            (1 << 13)  /* Bit 13: ch1 dma underrun 
interrupt enable */
+#define DAC_CR_CEN1                 (1 << 14)  /* Bit 14: ch1 calibration 
enable */
+#define DAC_CR_EN2                  (1 << 16)  /* Bit 16: ch2 enable */
+#define DAC_CR_TEN2                 (1 << 17)  /* Bit 17: ch2 trigger enable */
+#define DAC_CR_TSEL2_SHIFT          (18)       /* Bits 21-18: ch2 trigger sel 
*/
+#define DAC_CR_TSEL2_MASK           (15 << DAC_CR_TSEL2_SHIFT)
+#define DAC_CR_WAVE2_SHIFT          (22)       /* Bits 23-22: ch2 wave enable 
*/
+#define DAC_CR_WAVE2_MASK           (3 << DAC_CR_WAVE2_SHIFT)
+#define DAC_CR_MAMP2_SHIFT          (24)       /* Bits 27-24: ch2 
mask/amplitude */
+#define DAC_CR_MAMP2_MASK           (15 << DAC_CR_MAMP2_SHIFT)
+#define DAC_CR_DMAEN2               (1 << 28) /* Bit 28: ch2 dma enable */
+#define DAC_CR_DMAUDRIE2            (1 << 29) /* Bit 29: ch2 dma underrun 
interrupt enable */
+#define DAC_CR_CEN2                 (1 << 30) /* Bit 30: ch2 calibration 
enable */
+
+/* DAC software trigger register */
+
+#define DAC_SWTRGR_SWTRIG1          (1 << 0)  /* Bit 0: ch1 software trigger */
+#define DAC_SWTRGR_SWTRIG2          (1 << 1)  /* Bit 1: ch2 software trigger */
+
+/* DAC status register */
+
+#define DAC_SR_DMAUDR1              (1 << 13)  /* Bit 13: ch1 dma underrun 
flag */
+#define DAC_SR_CAL_FLAG1            (1 << 14)  /* Bit 14: ch1 calibration 
status */
+#define DAC_SR_BWST1                (1 << 15)  /* Bit 15: ch1 busy writing */
+#define DAC_SR_DMAUDR2              (1 << 29)  /* Bit 29: ch2 dma underrun 
flag */
+#define DAC_SR_CAL_FLAG2            (1 << 30)  /* Bit 30: ch2 calibration 
status */
+#define DAC_SR_BWST2                (1 << 31)  /* Bit 31: ch2 busy writing */
+
+/* DAC mode control register: MODE1 is bits 2-0, MODE2 is bits 18-16 */
+
+#define DAC_MCR_MODE1_SHIFT        (0)
+#define DAC_MCR_MODE1_MASK         (7 << DAC_MCR_MODE1_SHIFT)
+#  define DAC_MCR_MODE1_NORM_EXT_BUF       (0 << DAC_MCR_MODE1_SHIFT) /* 
Normal, pin, buffer on */
+#  define DAC_MCR_MODE1_NORM_EXT_CHIP_BUF  (1 << DAC_MCR_MODE1_SHIFT) /* 
Normal, pin + on-chip, buffer on */
+#  define DAC_MCR_MODE1_NORM_EXT_NOBUF     (2 << DAC_MCR_MODE1_SHIFT) /* 
Normal, pin, buffer off */
+#  define DAC_MCR_MODE1_NORM_CHIP_NOBUF    (3 << DAC_MCR_MODE1_SHIFT) /* 
Normal, on-chip, buffer off */
+#  define DAC_MCR_MODE1_SH_EXT_BUF         (4 << DAC_MCR_MODE1_SHIFT) /* 
Sample&hold, pin, buffer on */
+#  define DAC_MCR_MODE1_SH_EXT_CHIP_BUF    (5 << DAC_MCR_MODE1_SHIFT) /* 
Sample&hold, pin + on-chip, buffer on */
+#  define DAC_MCR_MODE1_SH_EXT_CHIP_NOBUF  (6 << DAC_MCR_MODE1_SHIFT) /* 
Sample&hold, pin + on-chip, buffer off */
+#  define DAC_MCR_MODE1_SH_CHIP_NOBUF      (7 << DAC_MCR_MODE1_SHIFT) /* 
Sample&hold, on-chip, buffer off */
+#define DAC_MCR_MODE2_SHIFT        (16)
+#define DAC_MCR_MODE2_MASK         (7 << DAC_MCR_MODE2_SHIFT)
+#  define DAC_MCR_MODE2_NORM_EXT_BUF       (0 << DAC_MCR_MODE2_SHIFT)
+#  define DAC_MCR_MODE2_NORM_EXT_CHIP_BUF  (1 << DAC_MCR_MODE2_SHIFT)
+#  define DAC_MCR_MODE2_NORM_EXT_NOBUF     (2 << DAC_MCR_MODE2_SHIFT)
+#  define DAC_MCR_MODE2_NORM_CHIP_NOBUF    (3 << DAC_MCR_MODE2_SHIFT)
+#  define DAC_MCR_MODE2_SH_EXT_BUF         (4 << DAC_MCR_MODE2_SHIFT)
+#  define DAC_MCR_MODE2_SH_EXT_CHIP_BUF    (5 << DAC_MCR_MODE2_SHIFT)
+#  define DAC_MCR_MODE2_SH_EXT_CHIP_NOBUF  (6 << DAC_MCR_MODE2_SHIFT)
+#  define DAC_MCR_MODE2_SH_CHIP_NOBUF      (7 << DAC_MCR_MODE2_SHIFT)
+
+/* DAC mode control register: high-frequency interface mode, which must
+ * match the AHB (HCLK) frequency.
+ */
+
+#define DAC_MCR_HFSEL_SHIFT        (14)
+#define DAC_MCR_HFSEL_MASK         (3 << DAC_MCR_HFSEL_SHIFT)
+#  define DAC_MCR_HFSEL_DISABLED   (0 << DAC_MCR_HFSEL_SHIFT) /* HCLK <= 80 
MHz */
+#  define DAC_MCR_HFSEL_AHB_80MHZ  (1 << DAC_MCR_HFSEL_SHIFT) /* HCLK > 80 MHz 
*/
+#  define DAC_MCR_HFSEL_AHB_160MHZ (2 << DAC_MCR_HFSEL_SHIFT) /* HCLK > 160 
MHz */
+
+#endif /* __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32_DAC_H */
diff --git a/arch/arm/src/stm32h5/hardware/stm32h50xxx_pinmap.h 
b/arch/arm/src/stm32h5/hardware/stm32h50xxx_pinmap.h
index 9422be00725..e4d36129a10 100644
--- a/arch/arm/src/stm32h5/hardware/stm32h50xxx_pinmap.h
+++ b/arch/arm/src/stm32h5/hardware/stm32h50xxx_pinmap.h
@@ -360,6 +360,11 @@
 #define GPIO_ADC1_INN18_0      (GPIO_ANALOG | GPIO_PORTA | GPIO_PIN5)
 #define GPIO_ADC1_INP19_0      (GPIO_ANALOG | GPIO_PORTA | GPIO_PIN5)
 
+/* DAC1 */
+
+#define GPIO_DAC1_OUT1_0       (GPIO_ANALOG | GPIO_PORTA | GPIO_PIN4)
+#define GPIO_DAC1_OUT2_0       (GPIO_ANALOG | GPIO_PORTA | GPIO_PIN5)
+
 /* USB */
 
 #define GPIO_USB_DM            
(GPIO_ALT|GPIO_AF10|GPIO_PUSHPULL|GPIO_FLOAT|GPIO_PORTA|GPIO_PIN11)
diff --git a/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h 
b/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h
index 55d6d9ead54..d88cd64d27c 100644
--- a/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h
+++ b/arch/arm/src/stm32h5/hardware/stm32h56xxx_pinmap.h
@@ -797,5 +797,10 @@
 #define GPIO_ADC2_INN18_0  (GPIO_ANALOG | GPIO_PORTA | GPIO_PIN5)
 #define GPIO_ADC2_INP19_0  (GPIO_ANALOG | GPIO_PORTA | GPIO_PIN5)
 
+/* DAC1 */
+
+#define GPIO_DAC1_OUT1_0       (GPIO_ANALOG | GPIO_PORTA | GPIO_PIN4)
+#define GPIO_DAC1_OUT2_0       (GPIO_ANALOG | GPIO_PORTA | GPIO_PIN5)
+
 #endif /* STM32H53xxx, STM32H562xx, STM32H563xx or STM32H57xxx */
 #endif /* __ARCH_ARM_SRC_STM32H5_HARDWARE_STM32H56XXX_PINMAP_H */
diff --git a/arch/arm/src/stm32h5/stm32.h b/arch/arm/src/stm32h5/stm32.h
index 03b72afd2c5..f4f92422f02 100644
--- a/arch/arm/src/stm32h5/stm32.h
+++ b/arch/arm/src/stm32h5/stm32.h
@@ -38,6 +38,7 @@
 
 #include "chip.h"
 #include "stm32_adc.h"
+#include "stm32_dac.h"
 #include "stm32_dbgmcu.h"
 #include "stm32_dma.h"
 #include "stm32_dts.h"
diff --git a/arch/arm/src/stm32h5/stm32_dac.c b/arch/arm/src/stm32h5/stm32_dac.c
new file mode 100644
index 00000000000..2d3bdaf8866
--- /dev/null
+++ b/arch/arm/src/stm32h5/stm32_dac.c
@@ -0,0 +1,483 @@
+/****************************************************************************
+ * arch/arm/src/stm32h5/stm32_dac.c
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include <sys/types.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <nuttx/debug.h>
+
+#include <arch/board/board.h>
+#include <nuttx/arch.h>
+#include <nuttx/irq.h>
+#include <nuttx/analog/dac.h>
+
+#include "arm_internal.h"
+#include "chip.h"
+#include "stm32.h"
+#include "stm32_dac.h"
+#include "stm32_rcc.h"
+
+#ifdef CONFIG_DAC
+
+/****************************************************************************
+ * Pre-processor Definitions
+ ****************************************************************************/
+
+/* Only DAC1 is supported, but it has two channels */
+
+#if !defined(CONFIG_STM32_DAC1)
+#  error "DAC1 must be enabled"
+#endif
+
+#if !defined(CONFIG_STM32_DAC1CH1) && !defined(CONFIG_STM32_DAC1CH2)
+#  error "At least one DAC1 channel must be enabled"
+#endif
+
+/* Helper macros for register bits */
+
+#define DAC_CR_EN(ch)         ((ch) == 1 ? DAC_CR_EN1 : DAC_CR_EN2)
+#define DAC_CR_TSEL_MASK(ch)  ((ch) == 1 ? DAC_CR_TSEL1_MASK : 
DAC_CR_TSEL2_MASK)
+#define DAC_CR_WAVE_MASK(ch)  ((ch) == 1 ? DAC_CR_WAVE1_MASK : 
DAC_CR_WAVE2_MASK)
+#define DAC_CR_MAMP_MASK(ch)  ((ch) == 1 ? DAC_CR_MAMP1_MASK : 
DAC_CR_MAMP2_MASK)
+#define DAC_MCR_MODE_MASK(ch) ((ch) == 1 ? DAC_MCR_MODE1_MASK : 
DAC_MCR_MODE2_MASK)
+#define DAC_MCR_MODE_EXT(ch)  ((ch) == 1 ? DAC_MCR_MODE1_NORM_EXT_BUF : \
+                               DAC_MCR_MODE2_NORM_EXT_BUF)
+
+/* The high-frequency interface mode must match the AHB clock frequency */
+
+#if STM32_HCLK_FREQUENCY > 160000000
+#  define DAC_MCR_HFSEL       DAC_MCR_HFSEL_AHB_160MHZ
+#elif STM32_HCLK_FREQUENCY > 80000000
+#  define DAC_MCR_HFSEL       DAC_MCR_HFSEL_AHB_80MHZ
+#else
+#  define DAC_MCR_HFSEL       DAC_MCR_HFSEL_DISABLED
+#endif
+
+/* Time to wait after enabling a channel before it accepts data (tWAKEUP is
+ * at most a few microseconds; leave some margin).
+ */
+
+#define DAC_WAKEUP_USEC       15
+
+/* Data holding register (12-bit, right aligned) for each channel */
+
+#define DAC_DHR12R(ch)        ((ch) == 1 ? STM32_DAC1_DHR12R1 : 
STM32_DAC1_DHR12R2)
+
+/****************************************************************************
+ * Private Types
+ ****************************************************************************/
+
+/* This structure represents the internal state of the DAC block (DAC1) */
+
+struct stm32_dac_s
+{
+  uint8_t init : 1; /* True, the DAC block has been initialized */
+};
+
+/* This structure represents the internal state of one DAC channel */
+
+struct stm32_chan_s
+{
+#ifdef CONFIG_STM32_DAC_LL_OPS
+  const struct stm32_dac_ops_s *llops; /* Low-level DAC ops */
+#endif
+  uint8_t inuse  : 1;   /* True, the driver is in use */
+  uint8_t ch;           /* Channel number (1 or 2) */
+  uint32_t pin;         /* Pin configuration */
+  uint32_t dro;         /* Data holding register address */
+};
+
+/****************************************************************************
+ * Private Function Prototypes
+ ****************************************************************************/
+
+static void stm32_dac_dumpregs(struct stm32_chan_s *chan);
+static void stm32_dac_enable(struct stm32_chan_s *chan);
+
+static void stm32_dac_reset(struct dac_dev_s *dev);
+static int  stm32_dac_setup(struct dac_dev_s *dev);
+static void stm32_dac_shutdown(struct dac_dev_s *dev);
+static void stm32_dac_txint(struct dac_dev_s *dev, bool enable);
+static int  stm32_dac_send(struct dac_dev_s *dev, struct dac_msg_s *msg);
+static int  stm32_dac_ioctl(struct dac_dev_s *dev, int cmd,
+                            unsigned long arg);
+
+static int  stm32_dac_chaninit(struct stm32_chan_s *chan);
+static void stm32_dac_blockinit(void);
+
+#ifdef CONFIG_STM32_DAC_LL_OPS
+static void stm32_dac_llops_enable(struct stm32_dac_dev_s *dev,
+                                   bool enabled);
+static void stm32_dac_llops_writedro(struct stm32_dac_dev_s *dev,
+                                     uint16_t data);
+static void stm32_dac_llops_dumpregs(struct stm32_dac_dev_s *dev);
+#endif
+
+/****************************************************************************
+ * Private Data
+ ****************************************************************************/
+
+static const struct dac_ops_s g_dacops =
+{
+  .ao_reset    = stm32_dac_reset,
+  .ao_setup    = stm32_dac_setup,
+  .ao_shutdown = stm32_dac_shutdown,
+  .ao_txint    = stm32_dac_txint,
+  .ao_send     = stm32_dac_send,
+  .ao_ioctl    = stm32_dac_ioctl,
+};
+
+#ifdef CONFIG_STM32_DAC_LL_OPS
+static const struct stm32_dac_ops_s g_dac_llops =
+{
+  .enable        = stm32_dac_llops_enable,
+  .write_dro     = stm32_dac_llops_writedro,
+  .dump_regs     = stm32_dac_llops_dumpregs
+};
+#endif
+
+#ifdef CONFIG_STM32_DAC1CH1
+/* DAC1 channel 1 */
+
+static struct stm32_chan_s g_dac1ch1priv =
+{
+#ifdef CONFIG_STM32_DAC_LL_OPS
+  .llops      = &g_dac_llops,
+#endif
+  .ch         = 1,
+  .pin        = GPIO_DAC1_OUT1_0,
+  .dro        = DAC_DHR12R(1),
+};
+
+static struct dac_dev_s g_dac1ch1dev =
+{
+  .ad_ops  = &g_dacops,
+  .ad_priv = &g_dac1ch1priv,
+};
+#endif /* CONFIG_STM32_DAC1CH1 */
+
+#ifdef CONFIG_STM32_DAC1CH2
+/* DAC1 channel 2 */
+
+static struct stm32_chan_s g_dac1ch2priv =
+{
+#ifdef CONFIG_STM32_DAC_LL_OPS
+  .llops      = &g_dac_llops,
+#endif
+  .ch         = 2,
+  .pin        = GPIO_DAC1_OUT2_0,
+  .dro        = DAC_DHR12R(2),
+};
+
+static struct dac_dev_s g_dac1ch2dev =
+{
+  .ad_ops  = &g_dacops,
+  .ad_priv = &g_dac1ch2priv,
+};
+#endif /* CONFIG_STM32_DAC1CH2 */
+
+static struct stm32_dac_s g_dacblock;
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_dac_dumpregs
+ ****************************************************************************/
+
+static void stm32_dac_dumpregs(struct stm32_chan_s *chan)
+{
+  UNUSED(chan);
+
+  ainfo("CR:   0x%08" PRIx32 "  SR:  0x%08" PRIx32 "  MCR: 0x%08" PRIx32
+        "\n",
+        getreg32(STM32_DAC1_CR), getreg32(STM32_DAC1_SR),
+        getreg32(STM32_DAC1_MCR));
+  ainfo("DOR1: 0x%08" PRIx32 "  DOR2: 0x%08" PRIx32 "\n",
+        getreg32(STM32_DAC1_DOR1), getreg32(STM32_DAC1_DOR2));
+}
+
+/****************************************************************************
+ * Name: stm32_dac_enable
+ *
+ * Description:
+ *   Enable a channel and wait for it to wake up.  Data written to the
+ *   holding register while the channel is still waking up can be lost, so
+ *   this must complete before any data is written.
+ *
+ ****************************************************************************/
+
+static void stm32_dac_enable(struct stm32_chan_s *chan)
+{
+  if ((getreg32(STM32_DAC1_CR) & DAC_CR_EN(chan->ch)) == 0)
+    {
+      modifyreg32(STM32_DAC1_CR, 0, DAC_CR_EN(chan->ch));
+      up_udelay(DAC_WAKEUP_USEC);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_dac_reset
+ ****************************************************************************/
+
+static void stm32_dac_reset(struct dac_dev_s *dev)
+{
+  struct stm32_chan_s *chan = dev->ad_priv;
+
+  /* Disable the channel and clear its trigger, waveform and mode setup */
+
+  modifyreg32(STM32_DAC1_CR, DAC_CR_EN(chan->ch) |
+              DAC_CR_TSEL_MASK(chan->ch) | DAC_CR_WAVE_MASK(chan->ch) |
+              DAC_CR_MAMP_MASK(chan->ch), 0);
+  modifyreg32(STM32_DAC1_MCR, DAC_MCR_MODE_MASK(chan->ch), 0);
+}
+
+/****************************************************************************
+ * Name: stm32_dac_setup
+ ****************************************************************************/
+
+static int stm32_dac_setup(struct dac_dev_s *dev)
+{
+  struct stm32_chan_s *chan = dev->ad_priv;
+  int ret;
+
+  /* Configure the channel if not already configured */
+
+  if (!chan->inuse)
+    {
+      ret = stm32_dac_chaninit(chan);
+      if (ret < 0)
+        {
+          aerr("ERROR: DAC channel init failed: %d\n", ret);
+          return ret;
+        }
+    }
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_dac_shutdown
+ ****************************************************************************/
+
+static void stm32_dac_shutdown(struct dac_dev_s *dev)
+{
+  /* Leave the channel enabled so that the last value written keeps being
+   * driven after the device is closed.  Applications such as the dac
+   * example open, write and close the device for every update, and
+   * disabling the channel here would put the output back into high
+   * impedance.  stm32_dac_reset() is what turns the channel off.
+   */
+}
+
+/****************************************************************************
+ * Name: stm32_dac_txint
+ ****************************************************************************/
+
+static void stm32_dac_txint(struct dac_dev_s *dev, bool enable)
+{
+  /* No interrupt support for DAC */
+}
+
+/****************************************************************************
+ * Name: stm32_dac_send
+ ****************************************************************************/
+
+static int stm32_dac_send(struct dac_dev_s *dev, struct dac_msg_s *msg)
+{
+  struct stm32_chan_s *chan = dev->ad_priv;
+
+  /* The trigger is disabled, so the value is transferred to the output
+   * as soon as it is written to the holding register.  The register must
+   * be written with a 32-bit access; a halfword access faults on the H5.
+   */
+
+  stm32_dac_enable(chan);
+  putreg32(msg->am_data & 0xfff, chan->dro);
+  dac_txdone(dev);
+
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_dac_ioctl
+ ****************************************************************************/
+
+static int stm32_dac_ioctl(struct dac_dev_s *dev, int cmd, unsigned long arg)
+{
+  return -ENOTTY;
+}
+
+/****************************************************************************
+ * Name: stm32_dac_chaninit
+ ****************************************************************************/
+
+static int stm32_dac_chaninit(struct stm32_chan_s *chan)
+{
+  if (chan->inuse)
+    {
+      return -EBUSY;
+    }
+
+  stm32_configgpio(chan->pin);
+
+  /* Disable the channel before configuration.  Leave the trigger disabled
+   * (TEN=0) with no wave generation so that writes to the holding register
+   * update the output directly.
+   */
+
+  modifyreg32(STM32_DAC1_CR, DAC_CR_EN(chan->ch) |
+              DAC_CR_TSEL_MASK(chan->ch) | DAC_CR_WAVE_MASK(chan->ch) |
+              DAC_CR_MAMP_MASK(chan->ch), 0);
+
+  /* Normal mode, output to the external pin with the buffer enabled.  The
+   * high-frequency interface mode is shared by both channels.
+   */
+
+  modifyreg32(STM32_DAC1_MCR, DAC_MCR_MODE_MASK(chan->ch) |
+              DAC_MCR_HFSEL_MASK, DAC_MCR_MODE_EXT(chan->ch) |
+              DAC_MCR_HFSEL);
+
+  /* Enable the channel now so that it has woken up by the first write */
+
+  stm32_dac_enable(chan);
+
+  chan->inuse = 1;
+  return OK;
+}
+
+/****************************************************************************
+ * Name: stm32_dac_blockinit
+ ****************************************************************************/
+
+static void stm32_dac_blockinit(void)
+{
+  irqstate_t flags;
+
+  if (g_dacblock.init)
+    {
+      return;
+    }
+
+  flags = enter_critical_section();
+
+  /* Enable the DAC1 clock (AHB2) and pulse its reset */
+
+  modifyreg32(STM32_RCC_AHB2ENR, 0, RCC_AHB2ENR_DAC1EN);
+  modifyreg32(STM32_RCC_AHB2RSTR, 0, RCC_AHB2RSTR_DACRST);
+  modifyreg32(STM32_RCC_AHB2RSTR, RCC_AHB2RSTR_DACRST, 0);
+
+  g_dacblock.init = 1;
+  leave_critical_section(flags);
+}
+
+#ifdef CONFIG_STM32_DAC_LL_OPS
+
+/****************************************************************************
+ * Name: stm32_dac_llops_enable
+ ****************************************************************************/
+
+static void stm32_dac_llops_enable(struct stm32_dac_dev_s *dev, bool enabled)
+{
+  struct stm32_chan_s *chan = (struct stm32_chan_s *)dev;
+
+  if (enabled)
+    {
+      stm32_dac_enable(chan);
+    }
+  else
+    {
+      modifyreg32(STM32_DAC1_CR, DAC_CR_EN(chan->ch), 0);
+    }
+}
+
+/****************************************************************************
+ * Name: stm32_dac_llops_writedro
+ ****************************************************************************/
+
+static void stm32_dac_llops_writedro(struct stm32_dac_dev_s *dev,
+                                     uint16_t data)
+{
+  struct stm32_chan_s *chan = (struct stm32_chan_s *)dev;
+
+  putreg32(data & 0xfff, chan->dro);
+}
+
+/****************************************************************************
+ * Name: stm32_dac_llops_dumpregs
+ ****************************************************************************/
+
+static void stm32_dac_llops_dumpregs(struct stm32_dac_dev_s *dev)
+{
+  stm32_dac_dumpregs((struct stm32_chan_s *)dev);
+}
+
+#endif /* CONFIG_STM32_DAC_LL_OPS */
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: stm32_dacinitialize
+ *
+ * Description:
+ *   Initialize the DAC.  The interface number corresponds to the channel:
+ *   0 = DAC1 channel 1, 1 = DAC1 channel 2.
+ *
+ ****************************************************************************/
+
+struct dac_dev_s *stm32_dacinitialize(int intf)
+{
+  struct dac_dev_s *dev;
+
+  switch (intf)
+    {
+#ifdef CONFIG_STM32_DAC1CH1
+      case 0:
+        dev = &g_dac1ch1dev;
+        break;
+#endif
+#ifdef CONFIG_STM32_DAC1CH2
+      case 1:
+        dev = &g_dac1ch2dev;
+        break;
+#endif
+      default:
+        aerr("ERROR: Invalid DAC interface: %d\n", intf);
+        return NULL;
+    }
+
+  stm32_dac_blockinit();
+
+  return dev;
+}
+
+#endif /* CONFIG_DAC */
diff --git a/arch/arm/src/stm32h5/stm32_dac.h b/arch/arm/src/stm32h5/stm32_dac.h
new file mode 100644
index 00000000000..0029d9a7041
--- /dev/null
+++ b/arch/arm/src/stm32h5/stm32_dac.h
@@ -0,0 +1,124 @@
+/****************************************************************************
+ * arch/arm/src/stm32h5/stm32_dac.h
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.  The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ ****************************************************************************/
+
+#ifndef __ARCH_ARM_SRC_STM32H5_STM32_DAC_H
+#define __ARCH_ARM_SRC_STM32H5_STM32_DAC_H
+
+/****************************************************************************
+ * Included Files
+ ****************************************************************************/
+
+#include <nuttx/config.h>
+
+#include "chip.h"
+#include "hardware/stm32_dac.h"
+
+#include <nuttx/analog/dac.h>
+
+/****************************************************************************
+ * Pre-processor definitions
+ ****************************************************************************/
+
+/* Low-level ops helpers ****************************************************/
+
+#define DAC_ENABLE(dac,d)                            \
+        (dac)->llops->enable(dac,d)
+#define DAC_WRITE_DRO(dac,d)                         \
+        (dac)->llops->write_dro(dac,d)
+#define DAC_DUMP_REGS(dac)                           \
+        (dac)->llops->dump_regs(dac)
+
+/****************************************************************************
+ * Public Types
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32_DAC_LL_OPS
+
+/* This structure provides the publicly visible representation of the
+ * "lower-half" DAC driver structure.
+ */
+
+struct stm32_dac_dev_s
+{
+  /* Publicly visible portion of the "lower-half" DAC driver structure */
+
+  const struct stm32_dac_ops_s *llops;
+
+  /* Require cast-compatibility with private "lower-half" DAC structure */
+};
+
+/* Low-level operations for DAC */
+
+struct stm32_dac_ops_s
+{
+  /* Enable / Disable DAC */
+
+  void (*enable)(struct stm32_dac_dev_s *dev, bool enabled);
+
+  /* Write DRO */
+
+  void (*write_dro)(struct stm32_dac_dev_s *dev, uint16_t data);
+
+  /* Dump DAC regs */
+
+  void (*dump_regs)(struct stm32_dac_dev_s *dev);
+};
+
+#endif /* CONFIG_STM32_DAC_LL_OPS */
+
+/****************************************************************************
+ * Public Function Prototypes
+ ****************************************************************************/
+
+#ifndef __ASSEMBLY__
+#ifdef __cplusplus
+#define EXTERN extern "C"
+extern "C"
+{
+#else
+#define EXTERN extern
+#endif
+
+/****************************************************************************
+ * Name: stm32_dacinitialize
+ *
+ * Description:
+ *   Initialize the DAC.  Interface 0 is DAC1 channel 1 and interface 1 is
+ *   DAC1 channel 2.
+ *
+ * Input Parameters:
+ *   intf - The DAC interface number.
+ *
+ * Returned Value:
+ *   Valid dac device structure reference on success; a NULL on failure
+ *
+ ****************************************************************************/
+
+struct dac_dev_s;
+struct dac_dev_s *stm32_dacinitialize(int intf);
+
+#undef EXTERN
+#ifdef __cplusplus
+}
+#endif
+#endif /* __ASSEMBLY__ */
+#endif /* __ARCH_ARM_SRC_STM32H5_STM32_DAC_H */
diff --git a/arch/arm/src/stm32h5/stm32h5xx_rcc.c 
b/arch/arm/src/stm32h5/stm32h5xx_rcc.c
index 8252df5cc77..683f1c67477 100644
--- a/arch/arm/src/stm32h5/stm32h5xx_rcc.c
+++ b/arch/arm/src/stm32h5/stm32h5xx_rcc.c
@@ -1229,9 +1229,19 @@ void stm32_stdclockconfig(void)
 
 #endif /* STM32_USE_LSE */
 
-      /* Configure ADC source clock */
+      /* Configure ADC source clock.  The DAC shares this kernel clock, and
+       * its output voltage is invalid unless the clock is HCLK or SYSCLK
+       * (see the "Invalid DAC output voltage for several DAC kernel clocks"
+       * erratum).
+       */
 
 #if defined(STM32_RCC_CCIPR5_ADCDACSEL)
+#  if defined(CONFIG_STM32_DAC1) && \
+      STM32_RCC_CCIPR5_ADCDACSEL != RCC_CCIPR5_ADCDACSEL_RCCHCLK && \
+      STM32_RCC_CCIPR5_ADCDACSEL != RCC_CCIPR5_ADCDACSEL_SYSCK
+#    error "The DAC needs STM32_RCC_CCIPR5_ADCDACSEL set to HCLK or SYSCLK"
+#  endif
+
       regval = getreg32(STM32_RCC_CCIPR5);
       regval &= ~RCC_CCIPR5_ADCDACSEL_MASK;
       regval |= STM32_RCC_CCIPR5_ADCDACSEL;

Reply via email to