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;
