diff --git a/arch/arm/src/common/stm32/Kconfig.dac b/arch/arm/src/common/stm32/Kconfig.dac index 92a88c2e601..fecbcaf23c8 100644 --- a/arch/arm/src/common/stm32/Kconfig.dac +++ b/arch/arm/src/common/stm32/Kconfig.dac @@ -116,6 +116,7 @@ endif # STM32_HRTIM_DAC config STM32_DAC1CH1_TIMER int "DAC1CH1 timer" depends on !STM32_DAC1CH1_DMA_EXTERNAL + range 1 15 if ARCH_CHIP_STM32H7 range 2 8 config STM32_DAC1CH1_TIMER_FREQUENCY @@ -177,6 +178,7 @@ endif # STM32_HRTIM_DAC config STM32_DAC1CH2_TIMER int "DAC1CH2 timer" depends on !STM32_DAC1CH2_DMA_EXTERNAL + range 1 15 if ARCH_CHIP_STM32H7 range 2 8 config STM32_DAC1CH2_TIMER_FREQUENCY diff --git a/arch/arm/src/common/stm32/Kconfig.have b/arch/arm/src/common/stm32/Kconfig.have index d6cd3b31e07..73927364718 100644 --- a/arch/arm/src/common/stm32/Kconfig.have +++ b/arch/arm/src/common/stm32/Kconfig.have @@ -946,6 +946,7 @@ config STM32_HAVE_TIM_DAC_TRIGGER default y if STM32_COMMON_LEGACY default y if ARCH_CHIP_STM32F7 default y if STM32_COMMON_L4_L5_U5 + default y if ARCH_CHIP_STM32H7 config STM32_HAVE_TIM1 bool diff --git a/arch/arm/src/stm32h7/Kconfig b/arch/arm/src/stm32h7/Kconfig index 8712d4e652c..70112131708 100644 --- a/arch/arm/src/stm32h7/Kconfig +++ b/arch/arm/src/stm32h7/Kconfig @@ -51,6 +51,7 @@ config STM32_H7_PERIPHERALS select STM32_HAVE_USART_RXFIFO_THRESHOLD select STM32_HAVE_FDCAN_H7 select STM32_HAVE_IP_WDG_M3M4_V1 + select STM32_HAVE_DAC1 choice prompt "STM32 H7 Chip Selection" diff --git a/arch/arm/src/stm32h7/Make.defs b/arch/arm/src/stm32h7/Make.defs index e1f6fd958b9..bb3817171c7 100644 --- a/arch/arm/src/stm32h7/Make.defs +++ b/arch/arm/src/stm32h7/Make.defs @@ -75,6 +75,10 @@ ifeq ($(CONFIG_STM32_ADC),y) CHIP_CSRCS += stm32_adc.c endif +ifeq ($(CONFIG_STM32_DAC),y) +CHIP_CSRCS += stm32_dac.c +endif + ifeq ($(CONFIG_STM32_FDCAN),y) CHIP_CSRCS += stm32_fdcan_sock.c endif diff --git a/arch/arm/src/stm32h7/hardware/stm32_dac.h b/arch/arm/src/stm32h7/hardware/stm32_dac.h index ba4a7dc199e..03959f6466f 100644 --- a/arch/arm/src/stm32h7/hardware/stm32_dac.h +++ b/arch/arm/src/stm32h7/hardware/stm32_dac.h @@ -87,22 +87,22 @@ #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 (1 << 2) /* Bits 5-2: ch1 trigger sel */ +#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 (1 << 6) /* Bits 7-6: ch1 wave enable */ +#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 (1 << 8) /* Bits 11-8: ch1 mask/amplitude */ +#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 (1 << 18) /* Bits 28-21: ch2 trigger sel */ +#define DAC_CR_TSEL2_SHIFT (8) /* Bits 28-21: ch2 trigger sel */ #define DAC_CR_TSEL2_MASK (15 << DAC_CR_TSEL2_SHIFT) -#define DAC_CR_WAVE2_SHIFT (1 << 22) /* Bits 23-22: ch2 wave enable */ +#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 (1 << 24) /* Bits 27-24: ch2 mask/amplitude */ +#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 */ @@ -123,19 +123,19 @@ #define DAC_SR_BWST2 (1 << 31) /* Bit 31: ch2 busy writing */ /* DAC mode control register */ -#define DAC_MCR_MODE1_SHIFT (1 << 0) /* Bits 2-0: ch1 mode */ +#define DAC_MCR_MODE1_SHIFT (0) /* Bits 2-0: ch1 mode */ #define DAC_MCR_MODE1_MASK (7 << DAC_MCR_MODE1_SHIFT) #define DAC_MCR_MODE1_NORM (4 << DAC_MCR_MODE1_SHIFT) /* ch1 normal mode */ #define DAC_MCR_MODE1_SAMPLE_HOLD (0 << DAC_MCR_MODE1_SHIFT) /* ch1 sample and hold mode */ #define DAC_MCR_MODE1_DIS_BUFFER (2 << DAC_MCR_MODE1_SHIFT) /* ch1 buffer disabled */ -#define DAC_MCR_MODE1_EXT (0 << DAC_MCR_MODE1_SHIFT) /* ch1 to external pin */ -#define DAC_MCR_MODE1_ON_CHIP (1 << DAC_MCR_MODE1_SHIFT) /* ch1 to on chip and external */ -#define DAC_MCR_MODE2_SHIFT (1 << 16) /* Bits 18-16: ch2 mode */ +#define DAC_MCR_MODE1_EXT (1 << DAC_MCR_MODE1_SHIFT) /* ch1 to external pin */ +#define DAC_MCR_MODE1_ON_CHIP (5 << DAC_MCR_MODE1_SHIFT) /* ch1 to on chip and external */ +#define DAC_MCR_MODE2_SHIFT (16) /* Bits 18-16: ch2 mode */ #define DAC_MCR_MODE2_MASK (7 << DAC_MCR_MODE2_SHIFT) #define DAC_MCR_MODE2_NORM (4 << DAC_MCR_MODE2_SHIFT) /* ch2 normal mode */ #define DAC_MCR_MODE2_SAMPLE_HOLD (0 << DAC_MCR_MODE2_SHIFT) /* ch2 sample and hold mode */ #define DAC_MCR_MODE2_DIS_BUFFER (2 << DAC_MCR_MODE2_SHIFT) /* ch2 buffer disabled */ -#define DAC_MCR_MODE2_EXT (0 << DAC_MCR_MODE2_SHIFT) /* ch2 to external pin */ -#define DAC_MCR_MODE2_ON_CHIP (1 << DAC_MCR_MODE2_SHIFT) /* ch2 to on chip and external */ +#define DAC_MCR_MODE2_EXT (1 << DAC_MCR_MODE2_SHIFT) /* ch2 to external pin */ +#define DAC_MCR_MODE2_ON_CHIP (5 << DAC_MCR_MODE2_SHIFT) /* ch2 to on chip and external */ #endif /* __ARCH_ARM_SRC_STM32H7_HARDWARE_STM32_DAC_H */ diff --git a/arch/arm/src/stm32h7/stm32_dac.c b/arch/arm/src/stm32h7/stm32_dac.c new file mode 100644 index 00000000000..4b4805cc1e7 --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_dac.c @@ -0,0 +1,1132 @@ +/**************************************************************************** + * arch/arm/src/stm32h7/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 + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "arm_internal.h" +#include "chip.h" +#include "stm32.h" +#include "stm32_dac.h" +#include "stm32_rcc.h" +#include "stm32_dma.h" +#include "stm32_tim.h" + +#ifdef CONFIG_DAC + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* 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 + +/* DMA configuration per channel */ + +#ifdef CONFIG_STM32_DAC1CH1_DMA +# if !defined(CONFIG_STM32_DAC1CH1_DMA_BUFFER_SIZE) || CONFIG_STM32_DAC1CH1_DMA_BUFFER_SIZE < 1 +# define CONFIG_STM32_DAC1CH1_DMA_BUFFER_SIZE 1 +# endif +# if !defined(CONFIG_STM32_DAC1CH1_TIMER) +# warning "A timer number must be specified in CONFIG_STM32_DAC1CH1_TIMER" +# undef CONFIG_STM32_DAC1CH1_DMA +# undef CONFIG_STM32_DAC1CH1_TIMER_FREQUENCY +# elif !defined(CONFIG_STM32_DAC1CH1_TIMER_FREQUENCY) || \ + (CONFIG_STM32_DAC1CH1_TIMER_FREQUENCY < 1) +# warning "A timer frequency (>0) must be specified in CONFIG_STM32_DAC1CH1_TIMER_FREQUENCY" +# undef CONFIG_STM32_DAC1CH1_DMA +# undef CONFIG_STM32_DAC1CH1_TIMER +# endif +#endif + +#ifdef CONFIG_STM32_DAC1CH2_DMA +# if !defined(CONFIG_STM32_DAC1CH2_DMA_BUFFER_SIZE) || CONFIG_STM32_DAC1CH2_DMA_BUFFER_SIZE < 1 +# define CONFIG_STM32_DAC1CH2_DMA_BUFFER_SIZE 1 +# endif +# if !defined(CONFIG_STM32_DAC1CH2_TIMER) +# warning "A timer number must be specified in CONFIG_STM32_DAC1CH2_TIMER" +# undef CONFIG_STM32_DAC1CH2_DMA +# undef CONFIG_STM32_DAC1CH2_TIMER_FREQUENCY +# elif !defined(CONFIG_STM32_DAC1CH2_TIMER_FREQUENCY) || \ + (CONFIG_STM32_DAC1CH2_TIMER_FREQUENCY < 1) +# warning "A timer frequency (>0) must be specified in CONFIG_STM32_DAC1CH2_TIMER_FREQUENCY" +# undef CONFIG_STM32_DAC1CH2_DMA +# undef CONFIG_STM32_DAC1CH2_TIMER +# endif +#endif + +/* Select DMA channels for each DAC channel */ + +#ifdef CONFIG_STM32_DAC1CH1_DMA +# if defined(CONFIG_STM32_DMA1) +# define DAC1_CH1_DMA_CHAN DMAMAP_DMA12_DAC1CH1_0 +# elif defined(CONFIG_STM32_DMA2) +# define DAC1_CH1_DMA_CHAN DMAMAP_DMA12_DAC1CH1_1 +# else +# error "No DMA channel for DAC1 CH1" +# endif +#endif + +#ifdef CONFIG_STM32_DAC1CH2_DMA +# if defined(CONFIG_STM32_DMA1) +# define DAC1_CH2_DMA_CHAN DMAMAP_DMA12_DAC1CH2_0 +# elif defined(CONFIG_STM32_DMA2) +# define DAC1_CH2_DMA_CHAN DMAMAP_DMA12_DAC1CH2_1 +# else +# error "No DMA channel for DAC1 CH2" +# endif +#endif + +/* Determine if any DMA is enabled (for common code) */ +#if defined(CONFIG_STM32_DAC1CH1_DMA) || defined(CONFIG_STM32_DAC1CH2_DMA) +# define HAVE_DMA 1 +#endif + +/* Timer trigger selection for DAC1 channels. + * On STM32H7, TSEL[3:0] encoding (from stm32h7xx_ll_dac.h): + * 0001: TIM1_TRGO (DAC_CR_TSEL1_0) + * 0010: TIM2_TRGO (DAC_CR_TSEL1_1) + * 0011: TIM4_TRGO (DAC_CR_TSEL1_1 | DAC_CR_TSEL1_0) + * 0100: TIM5_TRGO (DAC_CR_TSEL1_2) + * 0101: TIM6_TRGO (DAC_CR_TSEL1_2 | DAC_CR_TSEL1_0) + * 0110: TIM7_TRGO (DAC_CR_TSEL1_2 | DAC_CR_TSEL1_1) + * 0111: TIM8_TRGO (DAC_CR_TSEL1_2 | DAC_CR_TSEL1_1 | DAC_CR_TSEL1_0) + * 1000: TIM15_TRGO (DAC_CR_TSEL1_3) + * 1011: LPTIM1_OUT + * 1100: LPTIM2_OUT + * 1110: EXTI_LINE9 + * 1111: SWTRIG + * NOTE: TSEL=0000 is RESERVED on STM32H7 (unlike F4/F7 families). + * For channel 2, the same encoding is used but shifted by 16 bits. + */ + +#define DAC_TSEL_TIM1 (1 << DAC_CR_TSEL1_SHIFT) +#define DAC_TSEL_TIM2 (2 << DAC_CR_TSEL1_SHIFT) +#define DAC_TSEL_TIM4 (3 << DAC_CR_TSEL1_SHIFT) +#define DAC_TSEL_TIM5 (4 << DAC_CR_TSEL1_SHIFT) +#define DAC_TSEL_TIM6 (5 << DAC_CR_TSEL1_SHIFT) +#define DAC_TSEL_TIM7 (6 << DAC_CR_TSEL1_SHIFT) +#define DAC_TSEL_TIM8 (7 << DAC_CR_TSEL1_SHIFT) +#define DAC_TSEL_TIM15 (8 << DAC_CR_TSEL1_SHIFT) +#define DAC_TSEL_SW (15 << DAC_CR_TSEL1_SHIFT) + +#undef NEED_TIM1 +#undef NEED_TIM2 +#undef NEED_TIM3 +#undef NEED_TIM4 +#undef NEED_TIM5 +#undef NEED_TIM6 +#undef NEED_TIM7 +#undef NEED_TIM8 +#undef NEED_TIM15 + +/* Helper to define timer needs for a given channel */ +#define DAC_TIMER_NEED(ch) \ + defined(CONFIG_STM32_DAC1CH##ch##_DMA) && CONFIG_STM32_DAC1CH##ch##_TIMER + +#if DAC_TIMER_NEED(1) +# if CONFIG_STM32_DAC1CH1_TIMER == 1 +# ifndef CONFIG_STM32_TIM1 +# error "CONFIG_STM32_TIM1 required for DAC1 CH1" +# endif +# define NEED_TIM1 +# define DAC1_CH1_TSEL_VALUE DAC_TSEL_TIM1 +# define DAC1_CH1_TIMER_BASE STM32_TIM1_BASE +# elif CONFIG_STM32_DAC1CH1_TIMER == 2 +# ifndef CONFIG_STM32_TIM2 +# error "CONFIG_STM32_TIM2 required for DAC1 CH1" +# endif +# define NEED_TIM2 +# define DAC1_CH1_TSEL_VALUE DAC_TSEL_TIM2 +# define DAC1_CH1_TIMER_BASE STM32_TIM2_BASE +# elif CONFIG_STM32_DAC1CH1_TIMER == 3 +# error "TIM3_TRGO not available on STM32H7. Use TIM2/4/5/6/7/15." +# elif CONFIG_STM32_DAC1CH1_TIMER == 4 +# ifndef CONFIG_STM32_TIM4 +# error "CONFIG_STM32_TIM4 required for DAC1 CH1" +# endif +# define NEED_TIM4 +# define DAC1_CH1_TSEL_VALUE DAC_TSEL_TIM4 +# define DAC1_CH1_TIMER_BASE STM32_TIM4_BASE +# elif CONFIG_STM32_DAC1CH1_TIMER == 5 +# ifndef CONFIG_STM32_TIM5 +# error "CONFIG_STM32_TIM5 required for DAC1 CH1" +# endif +# define NEED_TIM5 +# define DAC1_CH1_TSEL_VALUE DAC_TSEL_TIM5 +# define DAC1_CH1_TIMER_BASE STM32_TIM5_BASE +# elif CONFIG_STM32_DAC1CH1_TIMER == 6 +# ifndef CONFIG_STM32_TIM6 +# error "CONFIG_STM32_TIM6 required for DAC1 CH1" +# endif +# define NEED_TIM6 +# define DAC1_CH1_TSEL_VALUE DAC_TSEL_TIM6 +# define DAC1_CH1_TIMER_BASE STM32_TIM6_BASE +# elif CONFIG_STM32_DAC1CH1_TIMER == 7 +# ifndef CONFIG_STM32_TIM7 +# error "CONFIG_STM32_TIM7 required for DAC1 CH1" +# endif +# define NEED_TIM7 +# define DAC1_CH1_TSEL_VALUE DAC_TSEL_TIM7 +# define DAC1_CH1_TIMER_BASE STM32_TIM7_BASE +# elif CONFIG_STM32_DAC1CH1_TIMER == 8 +# ifndef CONFIG_STM32_TIM8 +# error "CONFIG_STM32_TIM8 required for DAC1 CH1" +# endif +# define NEED_TIM8 +# define DAC1_CH1_TSEL_VALUE DAC_TSEL_TIM8 +# define DAC1_CH1_TIMER_BASE STM32_TIM8_BASE +# elif CONFIG_STM32_DAC1CH1_TIMER == 15 +# ifndef CONFIG_STM32_TIM15 +# error "CONFIG_STM32_TIM15 required for DAC1 CH1" +# endif +# define NEED_TIM15 +# define DAC1_CH1_TSEL_VALUE DAC_TSEL_TIM15 +# define DAC1_CH1_TIMER_BASE STM32_TIM15_BASE +# else +# error "Unsupported CONFIG_STM32_DAC1CH1_TIMER" +# endif +#else +# define DAC1_CH1_TSEL_VALUE DAC_TSEL_SW +#endif + +#if DAC_TIMER_NEED(2) +# if CONFIG_STM32_DAC1CH2_TIMER == 1 +# ifndef CONFIG_STM32_TIM1 +# error "CONFIG_STM32_TIM1 required for DAC1 CH2" +# endif +# define NEED_TIM1 +# define DAC1_CH2_TSEL_VALUE DAC_TSEL_TIM1 +# define DAC1_CH2_TIMER_BASE STM32_TIM1_BASE +# elif CONFIG_STM32_DAC1CH2_TIMER == 2 +# ifndef CONFIG_STM32_TIM2 +# error "CONFIG_STM32_TIM2 required for DAC1 CH2" +# endif +# define NEED_TIM2 +# define DAC1_CH2_TSEL_VALUE DAC_TSEL_TIM2 +# define DAC1_CH2_TIMER_BASE STM32_TIM2_BASE +# elif CONFIG_STM32_DAC1CH2_TIMER == 3 +# error "TIM3_TRGO not available on STM32H7. Use TIM2/4/5/6/7/15." +# elif CONFIG_STM32_DAC1CH2_TIMER == 4 +# ifndef CONFIG_STM32_TIM4 +# error "CONFIG_STM32_TIM4 required for DAC1 CH2" +# endif +# define NEED_TIM4 +# define DAC1_CH2_TSEL_VALUE DAC_TSEL_TIM4 +# define DAC1_CH2_TIMER_BASE STM32_TIM4_BASE +# elif CONFIG_STM32_DAC1CH2_TIMER == 5 +# ifndef CONFIG_STM32_TIM5 +# error "CONFIG_STM32_TIM5 required for DAC1 CH2" +# endif +# define NEED_TIM5 +# define DAC1_CH2_TSEL_VALUE DAC_TSEL_TIM5 +# define DAC1_CH2_TIMER_BASE STM32_TIM5_BASE +# elif CONFIG_STM32_DAC1CH2_TIMER == 6 +# ifndef CONFIG_STM32_TIM6 +# error "CONFIG_STM32_TIM6 required for DAC1 CH2" +# endif +# define NEED_TIM6 +# define DAC1_CH2_TSEL_VALUE DAC_TSEL_TIM6 +# define DAC1_CH2_TIMER_BASE STM32_TIM6_BASE +# elif CONFIG_STM32_DAC1CH2_TIMER == 7 +# ifndef CONFIG_STM32_TIM7 +# error "CONFIG_STM32_TIM7 required for DAC1 CH2" +# endif +# define NEED_TIM7 +# define DAC1_CH2_TSEL_VALUE DAC_TSEL_TIM7 +# define DAC1_CH2_TIMER_BASE STM32_TIM7_BASE +# elif CONFIG_STM32_DAC1CH2_TIMER == 8 +# ifndef CONFIG_STM32_TIM8 +# error "CONFIG_STM32_TIM8 required for DAC1 CH2" +# endif +# define NEED_TIM8 +# define DAC1_CH2_TSEL_VALUE DAC_TSEL_TIM8 +# define DAC1_CH2_TIMER_BASE STM32_TIM8_BASE +# elif CONFIG_STM32_DAC1CH2_TIMER == 15 +# ifndef CONFIG_STM32_TIM15 +# error "CONFIG_STM32_TIM15 required for DAC1 CH2" +# endif +# define NEED_TIM15 +# define DAC1_CH2_TSEL_VALUE DAC_TSEL_TIM15 +# define DAC1_CH2_TIMER_BASE STM32_TIM15_BASE +# else +# error "Unsupported CONFIG_STM32_DAC1CH2_TIMER" +# endif +#else +# define DAC1_CH2_TSEL_VALUE DAC_TSEL_SW +#endif + +/* DMA control word */ + +#define DAC_DMA_CONTROL_WORD (DMA_SCR_MSIZE_16BITS | \ + DMA_SCR_PSIZE_16BITS | \ + DMA_SCR_MINC | \ + DMA_SCR_CIRC | \ + DMA_SCR_DIR_M2P) + +/* Helper macros for register bits (already channel-specific) */ + +#define DAC_CR_EN(ch) ((ch) == 1 ? DAC_CR_EN1 : DAC_CR_EN2) +#define DAC_CR_TEN(ch) ((ch) == 1 ? DAC_CR_TEN1 : DAC_CR_TEN2) +#define DAC_CR_DMAEN(ch) ((ch) == 1 ? DAC_CR_DMAEN1 : DAC_CR_DMAEN2) +#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_EXT : DAC_MCR_MODE2_EXT) +#define DAC_MCR_MODE_ONCHIP(ch) ((ch) == 1 ? DAC_MCR_MODE1_ON_CHIP : DAC_MCR_MODE2_ON_CHIP) + +/* DRO register 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 (or 0xffffffff for internal) */ + uint32_t dro; /* Data output register address */ + uint32_t tsel; /* CR trigger select value */ +#ifdef HAVE_DMA + uint8_t hasdma : 1; /* True, this channel supports DMA */ + uint8_t dma_active : 1; /* True, DMA transfer is running */ + uint8_t timer; /* Timer number for DMA trigger */ + uint16_t dmachan; /* DMA channel */ + uint16_t buffer_len; /* DMA buffer length */ + DMA_HANDLE dma; /* Allocated DMA channel */ + uint32_t tbase; /* Timer base address */ + uint32_t tfrequency; /* Desired timer frequency */ + int result; /* DMA result */ + uint16_t buffer_pos; /* Position in dmabuffer */ + uint16_t *dmabuffer; /* DMA transfer buffer */ +#endif +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static uint32_t stm32_dac_getreg(struct stm32_chan_s *priv, int offset); +static void stm32_dac_dumpregs(struct stm32_chan_s *priv); + +#ifdef HAVE_DMA +static inline void stm32_tim_putreg(struct stm32_chan_s *chan, int offset, + uint32_t value); +static inline void stm32_tim_modifyreg(struct stm32_chan_s *chan, int offset, + uint32_t clearbits, uint32_t setbits); +#endif + +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); + +#ifdef HAVE_DMA +static int stm32_dac_timinit(struct stm32_chan_s *chan); +static void stm32_dac_timstart(struct stm32_chan_s *chan); +static void stm32_dac_dma_start(struct stm32_chan_s *chan); +static void stm32_dac_dma_stop(struct stm32_chan_s *chan); +static int stm32_dac_dmainit(struct stm32_chan_s *chan); +#endif +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); +#ifdef HAVE_DMA +static void stm32_dac_llops_startdma(struct stm32_dac_dev_s *dev); +static void stm32_dac_llops_stopdma(struct stm32_dac_dev_s *dev); +#endif +static void stm32_dac_llops_dumpregs(struct stm32_dac_dev_s *dev); +#endif /* CONFIG_STM32_DAC_LL_OPS */ + +/**************************************************************************** + * 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, +#ifdef HAVE_DMA + .start_dma = stm32_dac_llops_startdma, + .stop_dma = stm32_dac_llops_stopdma, +#endif + .dump_regs = stm32_dac_llops_dumpregs +}; +#endif /* CONFIG_STM32_DAC_LL_OPS */ + +#ifdef CONFIG_STM32_DAC1CH1 +/* DAC1 channel 1 */ + +#ifdef CONFIG_STM32_DAC1CH1_DMA +uint16_t stm32_dac1_ch1_dmabuffer[CONFIG_STM32_DAC1CH1_DMA_BUFFER_SIZE]; +#endif + +static struct stm32_chan_s g_dac1ch1priv = +{ +#ifdef CONFIG_STM32_DAC_LL_OPS + .llops = &g_dac_llops, +#endif + .ch = 1, +#ifdef CONFIG_STM32_DAC1CH1_OUTPUT_ADC + .pin = 0xffffffffu, +#else + .pin = GPIO_DAC1_OUT1_0, +#endif + .dro = DAC_DHR12R(1), + .tsel = DAC1_CH1_TSEL_VALUE, +#ifdef CONFIG_STM32_DAC1CH1_DMA + .hasdma = 1, + .dmachan = DAC1_CH1_DMA_CHAN, + .buffer_len = CONFIG_STM32_DAC1CH1_DMA_BUFFER_SIZE, + .dmabuffer = stm32_dac1_ch1_dmabuffer, + .timer = CONFIG_STM32_DAC1CH1_TIMER, + .tbase = DAC1_CH1_TIMER_BASE, + .tfrequency = CONFIG_STM32_DAC1CH1_TIMER_FREQUENCY, +#endif +}; + +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 */ + +#ifdef CONFIG_STM32_DAC1CH2_DMA +uint16_t stm32_dac1_ch2_dmabuffer[CONFIG_STM32_DAC1CH2_DMA_BUFFER_SIZE]; +#endif + +static struct stm32_chan_s g_dac1ch2priv = +{ +#ifdef CONFIG_STM32_DAC_LL_OPS + .llops = &g_dac_llops, +#endif + .ch = 2, +#ifdef CONFIG_STM32_DAC1CH2_OUTPUT_ADC + .pin = 0xffffffffu, +#else + .pin = GPIO_DAC1_OUT2_0, +#endif + .dro = DAC_DHR12R(2), + .tsel = DAC1_CH2_TSEL_VALUE, +#ifdef CONFIG_STM32_DAC1CH2_DMA + .hasdma = 1, + .dmachan = DAC1_CH2_DMA_CHAN, + .buffer_len = CONFIG_STM32_DAC1CH2_DMA_BUFFER_SIZE, + .dmabuffer = stm32_dac1_ch2_dmabuffer, + .timer = CONFIG_STM32_DAC1CH2_TIMER, + .tbase = DAC1_CH2_TIMER_BASE, + .tfrequency = CONFIG_STM32_DAC1CH2_TIMER_FREQUENCY, +#endif +}; + +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_getreg + ****************************************************************************/ + +static uint32_t stm32_dac_getreg(struct stm32_chan_s *priv, int offset) +{ + return getreg32(STM32_DAC1_BASE + offset); +} + +/**************************************************************************** + * Name: stm32_dac_dumpregs + ****************************************************************************/ + +static void stm32_dac_dumpregs(struct stm32_chan_s *priv) +{ + UNUSED(priv); + syslog(50, "CR: 0x%08" PRIx32 " SWTRGR: 0x%08" PRIx32 "\n" + "SR: 0x%08" PRIx32 " MCR: 0x%08" PRIx32 "\n", + stm32_dac_getreg(priv, STM32_DAC1_CR_OFFSET), + stm32_dac_getreg(priv, STM32_DAC1_SWTRGR_OFFSET), + stm32_dac_getreg(priv, STM32_DAC1_SR_OFFSET), + stm32_dac_getreg(priv, STM32_DAC1_MCR_OFFSET)); + syslog(50, "DHR12R1: 0x%08x\n", + (unsigned int)getreg32(STM32_DAC1_DHR12R1)); + syslog(50, "DHR12R2: 0x%08x\n", + (unsigned int)getreg32(STM32_DAC1_DHR12R2)); + syslog(50, "DOR1: 0x%08x\n", (unsigned int)getreg32(STM32_DAC1_DOR1)); + syslog(50, "DOR2: 0x%08x\n", (unsigned int)getreg32(STM32_DAC1_DOR2)); +} + +/**************************************************************************** + * Name: stm32_tim_putreg / stm32_tim_modifyreg + ****************************************************************************/ + +#ifdef HAVE_DMA +static inline void stm32_tim_putreg(struct stm32_chan_s *chan, int offset, + uint32_t value) +{ + putreg32(value, chan->tbase + offset); +} + +static inline void stm32_tim_modifyreg(struct stm32_chan_s *chan, int offset, + uint32_t clearbits, uint32_t setbits) +{ + modifyreg32(chan->tbase + offset, clearbits, setbits); +} +#endif + +/**************************************************************************** + * Name: stm32_dac_modify_cr + ****************************************************************************/ + +static inline void stm32_dac_modify_cr(struct stm32_chan_s *chan, + uint32_t clearbits, uint32_t setbits) +{ + /* Note: clearbits and setbits are already shifted for the specific + * channel. Do NOT add extra shift here. + */ + + modifyreg32(STM32_DAC1_CR, clearbits, setbits); +} + +/**************************************************************************** + * Name: stm32_dac_modify_mcr + ****************************************************************************/ + +static inline void stm32_dac_modify_mcr(struct stm32_chan_s *chan, + uint32_t clearbits, uint32_t setbits) +{ + /* Note: clearbits and setbits are already shifted for the specific + * channel. Do NOT add extra shift here. + */ + + modifyreg32(STM32_DAC1_MCR, clearbits, setbits); +} + +/**************************************************************************** + * Name: stm32_dac_reset + ****************************************************************************/ + +static void stm32_dac_reset(struct dac_dev_s *dev) +{ + struct stm32_chan_s *chan = dev->ad_priv; + + stm32_dac_modify_cr(chan, DAC_CR_EN(chan->ch), 0); + + uint32_t clearbits = DAC_CR_TSEL_MASK(chan->ch) | + DAC_CR_WAVE_MASK(chan->ch) | + DAC_CR_MAMP_MASK(chan->ch); + stm32_dac_modify_cr(chan, clearbits, 0); + + stm32_dac_modify_mcr(chan, 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; + + /* Configure the channel if not already configured */ + + if (!chan->inuse) + { + int ret = stm32_dac_chaninit(chan); + if (ret < 0) + { + aerr("ERROR: DAC channel init failed: %d\n", ret); + return ret; + } + } + +#ifdef HAVE_DMA + chan->buffer_pos = 0; +#endif + + return OK; +} + +/**************************************************************************** + * Name: stm32_dac_shutdown + ****************************************************************************/ + +static void stm32_dac_shutdown(struct dac_dev_s *dev) +{ + struct stm32_chan_s *chan = dev->ad_priv; + +#ifdef HAVE_DMA + stm32_dac_dma_stop(chan); +#endif + + stm32_dac_modify_cr(chan, DAC_CR_EN(chan->ch), 0); +} + +/**************************************************************************** + * Name: stm32_dac_txint + ****************************************************************************/ + +static void stm32_dac_txint(struct dac_dev_s *dev, bool enable) +{ + /* No interrupt support for DAC */ +} + +/**************************************************************************** + * Name: stm32_dac_dmatxcallback + ****************************************************************************/ + +#ifdef HAVE_DMA +static void stm32_dac_dmatxcallback(DMA_HANDLE handle, uint8_t isr, + void *arg) +{ + struct stm32_chan_s *chan = (struct stm32_chan_s *)arg; + struct dac_dev_s *dev; + + DEBUGASSERT(chan); + +#ifdef CONFIG_STM32_DAC1CH1 + if (chan->ch == 1) + dev = &g_dac1ch1dev; + else +#endif +#ifdef CONFIG_STM32_DAC1CH2 + if (chan->ch == 2) + dev = &g_dac1ch2dev; + else +#endif + DEBUGPANIC(); + + DEBUGASSERT(dev->ad_priv == chan); + + if (chan->result == -EBUSY) + { + chan->result = (isr & DMA_STREAM_TEIF_BIT) ? -EIO : OK; + dac_txdone(dev); + } +} +#endif + +/**************************************************************************** + * 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; + +#ifdef HAVE_DMA + if (chan->hasdma) + { + chan->dmabuffer[chan->buffer_pos++] = (uint16_t)msg->am_data; + dac_txdone(dev); + + if (chan->buffer_pos >= chan->buffer_len && !chan->dma_active) + { + stm32_dac_dma_start(chan); + } + } + else +#endif + { + stm32_dac_modify_cr(chan, 0, DAC_CR_EN(chan->ch)); + putreg16(msg->am_data, 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_timinit + ****************************************************************************/ + +#ifdef HAVE_DMA +static int stm32_dac_timinit(struct stm32_chan_s *chan) +{ + uint32_t pclk; + uint32_t prescaler; + uint32_t timclk; + uint32_t reload; + uint32_t regaddr; + uint32_t setbits; + + switch (chan->timer) + { +#ifdef NEED_TIM1 + case 1: + regaddr = STM32_RCC_APB2ENR; + setbits = RCC_APB2ENR_TIM1EN; + pclk = STM32_TIM1_CLKIN; + break; +#endif +#ifdef NEED_TIM2 + case 2: + regaddr = STM32_RCC_APB1LENR; + setbits = RCC_APB1LENR_TIM2EN; + pclk = STM32_TIM2_CLKIN; + break; +#endif +#ifdef NEED_TIM4 + case 4: + regaddr = STM32_RCC_APB1LENR; + setbits = RCC_APB1LENR_TIM4EN; + pclk = STM32_TIM4_CLKIN; + break; +#endif +#ifdef NEED_TIM5 + case 5: + regaddr = STM32_RCC_APB1LENR; + setbits = RCC_APB1LENR_TIM5EN; + pclk = STM32_TIM5_CLKIN; + break; +#endif +#ifdef NEED_TIM6 + case 6: + regaddr = STM32_RCC_APB1LENR; + setbits = RCC_APB1LENR_TIM6EN; + pclk = STM32_TIM6_CLKIN; + break; +#endif +#ifdef NEED_TIM7 + case 7: + regaddr = STM32_RCC_APB1LENR; + setbits = RCC_APB1LENR_TIM7EN; + pclk = STM32_TIM7_CLKIN; + break; +#endif +#ifdef NEED_TIM8 + case 8: + regaddr = STM32_RCC_APB2ENR; + setbits = RCC_APB2ENR_TIM8EN; + pclk = STM32_TIM8_CLKIN; + break; +#endif +#ifdef NEED_TIM15 + case 15: + regaddr = STM32_RCC_APB2ENR; + setbits = RCC_APB2ENR_TIM15EN; + pclk = STM32_TIM15_CLKIN; + break; +#endif + default: + aerr("ERROR: Unsupported timer %d\n", chan->timer); + return -EINVAL; + } + + modifyreg32(regaddr, 0, setbits); + + /* Calculate prescaler and reload */ + + prescaler = (pclk / chan->tfrequency + 65534) / 65535; + if (prescaler < 1) prescaler = 1; + if (prescaler > 65536) prescaler = 65536; + + timclk = pclk / prescaler; + reload = (timclk + chan->tfrequency / 2) / chan->tfrequency - 1; + if (reload > 65535) reload = 65535; + + stm32_tim_putreg(chan, STM32_GTIM_ARR_OFFSET, (uint16_t)reload); + stm32_tim_putreg(chan, STM32_GTIM_PSC_OFFSET, (uint16_t)(prescaler - 1)); + + stm32_tim_modifyreg(chan, STM32_GTIM_CR1_OFFSET, 0, GTIM_CR1_ARPE); + stm32_tim_modifyreg(chan, STM32_GTIM_CR2_OFFSET, GTIM_CR2_MMS_MASK, + GTIM_CR2_MMS_UPDATE); + + return OK; +} +#endif + +/**************************************************************************** + * Name: stm32_dac_timstart + ****************************************************************************/ + +#ifdef HAVE_DMA +static void stm32_dac_timstart(struct stm32_chan_s *chan) +{ + stm32_tim_modifyreg(chan, STM32_GTIM_CR1_OFFSET, 0, GTIM_CR1_CEN); + stm32_tim_modifyreg(chan, STM32_GTIM_EGR_OFFSET, 0, GTIM_EGR_UG); +} +#endif + +/**************************************************************************** + * Name: stm32_dac_dma_start + ****************************************************************************/ + +#ifdef HAVE_DMA +static void stm32_dac_dma_start(struct stm32_chan_s *chan) +{ + if (chan->dma_active) + return; + + chan->result = -EBUSY; + +#ifdef CONFIG_ARMV7M_DCACHE + up_clean_dcache((uintptr_t)chan->dmabuffer, + (uintptr_t)chan->dmabuffer + + chan->buffer_len * sizeof(uint16_t)); +#endif + + stm32_dmastart(chan->dma, stm32_dac_dmatxcallback, chan, false); + stm32_dac_modify_cr(chan, 0, DAC_CR_EN(chan->ch) | + DAC_CR_TEN(chan->ch) | + DAC_CR_DMAEN(chan->ch)); + stm32_dac_timstart(chan); + chan->dma_active = 1; + chan->buffer_pos = 0; +} +#endif + +/**************************************************************************** + * Name: stm32_dac_dma_stop + ****************************************************************************/ + +#ifdef HAVE_DMA +static void stm32_dac_dma_stop(struct stm32_chan_s *chan) +{ + if (!chan->dma_active) + return; + + stm32_tim_modifyreg(chan, STM32_GTIM_CR1_OFFSET, GTIM_CR1_CEN, 0); + stm32_dac_modify_cr(chan, DAC_CR_TEN(chan->ch) | + DAC_CR_DMAEN(chan->ch), 0); + stm32_dmastop(chan->dma); + chan->dma_active = 0; +} +#endif + +#ifdef HAVE_DMA +static int stm32_dac_dmainit(struct stm32_chan_s *chan) +{ + if (chan->hasdma) + { + chan->dma = stm32_dmachannel(chan->dmachan); + if (!chan->dma) + { + return -EINVAL; + } + + /* Configure for circular DMA */ + + struct stm32_dma_config_s dmacfg; + + dmacfg.paddr = chan->dro; + dmacfg.maddr = (uint32_t)chan->dmabuffer; + dmacfg.ndata = chan->buffer_len; + dmacfg.cfg1 = DAC_DMA_CONTROL_WORD; + dmacfg.cfg2 = 0; + stm32_dmasetup(chan->dma, &dmacfg); + + chan->result = -EBUSY; + chan->dma_active = 0; + return OK; + } + + return -EINVAL; +} +#endif + +/**************************************************************************** + * Name: stm32_dac_chaninit + ****************************************************************************/ + +static int stm32_dac_chaninit(struct stm32_chan_s *chan) +{ + uint32_t clearbits; + uint32_t setbits; + + if (chan->inuse) + { + return -EBUSY; + } + + /* Configure GPIO pin if needed */ + + if (chan->pin != 0xffffffffu) + stm32_configgpio(chan->pin); + + /* Disable channel before configuration */ + + stm32_dac_modify_cr(chan, DAC_CR_EN(chan->ch), 0); + + /* Configure trigger, waveform (none), amplitude (0) */ + + clearbits = DAC_CR_TSEL_MASK(chan->ch) | DAC_CR_WAVE_MASK(chan->ch) | + DAC_CR_MAMP_MASK(chan->ch); + setbits = chan->tsel; /* WAVE=0, MAMP=0 (no noise/triangle) */ + stm32_dac_modify_cr(chan, clearbits, setbits); + + /* Configure MCR (mode). + * MODE=000: external pin with buffer, supports DMA requests. + * MODE=001 (MODE1_EXT): external pin with buffer, NO DMA requests. + * MODE=101 (MODE1_ON_CHIP): internal (to ADC), supports DMA requests. + */ + + clearbits = DAC_MCR_MODE_MASK(chan->ch); + if (chan->pin != 0xffffffffu) + { +#ifdef HAVE_DMA + if (chan->hasdma) + setbits = 0; /* MODE=000: DMA capable */ + else +#endif + setbits = DAC_MCR_MODE_EXT(chan->ch); /* MODE=001 */ + } + else + { + setbits = DAC_MCR_MODE_ONCHIP(chan->ch); /* MODE=101 */ + } + + stm32_dac_modify_mcr(chan, clearbits, setbits); + +#ifdef HAVE_DMA + if (chan->hasdma) + { + /* MODE=000 is also sample-and-hold if DAC_CCR CxMODE=1. + * Force CxMODE=0 for normal DMA mode. + * DAC_CCR bit 0 = C1MODE, bit 1 = C2MODE. + */ + + modifyreg32(STM32_DAC1_CCR, (1 << (chan->ch - 1)), 0); + + /* Enable trigger */ + + stm32_dac_modify_cr(chan, 0, DAC_CR_TEN(chan->ch)); + + int ret = stm32_dac_dmainit(chan); + if (ret < 0) + { + aerr("ERROR: Failed to allocate DMA channel\n"); + return ret; + } + + ret = stm32_dac_timinit(chan); + if (ret < 0) + { + aerr("ERROR: Timer init failed: %d\n", ret); + stm32_dmafree(chan->dma); + return ret; + } + } +#endif + + chan->inuse = 1; + return OK; +} + +/**************************************************************************** + * Name: stm32_dac_blockinit + ****************************************************************************/ + +static void stm32_dac_blockinit(void) +{ + if (g_dacblock.init) + return; + + irqstate_t flags = enter_critical_section(); + + /* Enable clock for DAC1 (APB1L) and de-assert reset */ + + modifyreg32(STM32_RCC_APB1LENR, 0, RCC_APB1LENR_DAC1EN); + modifyreg32(STM32_RCC_APB1LRSTR, 0, RCC_APB1LRSTR_DAC1RST); + modifyreg32(STM32_RCC_APB1LRSTR, RCC_APB1LRSTR_DAC1RST, 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 *priv = (struct stm32_chan_s *)dev; + if (enabled) + stm32_dac_modify_cr(priv, 0, DAC_CR_EN(priv->ch)); + else + stm32_dac_modify_cr(priv, DAC_CR_EN(priv->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 *priv = (struct stm32_chan_s *)dev; + putreg16(data, priv->dro); +} + +#ifdef HAVE_DMA +/**************************************************************************** + * Name: stm32_dac_llops_startdma + ****************************************************************************/ + +static void stm32_dac_llops_startdma(struct stm32_dac_dev_s *dev) +{ + struct stm32_chan_s *priv = (struct stm32_chan_s *)dev; + stm32_dac_dma_start(priv); +} + +/**************************************************************************** + * Name: stm32_dac_llops_stopdma + ****************************************************************************/ + +static void stm32_dac_llops_stopdma(struct stm32_dac_dev_s *dev) +{ + struct stm32_chan_s *priv = (struct stm32_chan_s *)dev; + stm32_dac_dma_stop(priv); +} +#endif + +/**************************************************************************** + * Name: stm32_dac_llops_dumpregs + ****************************************************************************/ + +static void stm32_dac_llops_dumpregs(struct stm32_dac_dev_s *dev) +{ + struct stm32_chan_s *priv = (struct stm32_chan_s *)dev; + stm32_dac_dumpregs(priv); +} + +#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 */ \ No newline at end of file diff --git a/arch/arm/src/stm32h7/stm32_dac.h b/arch/arm/src/stm32h7/stm32_dac.h new file mode 100644 index 00000000000..46407608e54 --- /dev/null +++ b/arch/arm/src/stm32h7/stm32_dac.h @@ -0,0 +1,182 @@ +/**************************************************************************** + * arch/arm/src/stm32h7/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_STM32_STM32H7_DAC_H +#define __ARCH_ARM_SRC_STM32_STM32H7_DAC_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include "chip.h" +#include "hardware/stm32_dac.h" + +#include + +/**************************************************************************** + * Pre-processor definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* Timer devices may be used for different purposes. One special purpose is + * to control periodic DAC outputs. If CONFIG_STM32_TIMn is defined then + * CONFIG_STM32_TIMn_DAC must also be defined to indicate that timer "n" + * is intended to be used for that purpose. + */ + +#ifndef CONFIG_STM32_TIM1 +# undef CONFIG_STM32_TIM1_DAC +#endif +#ifndef CONFIG_STM32_TIM2 +# undef CONFIG_STM32_TIM2_DAC +#endif +#ifndef CONFIG_STM32_TIM3 +# undef CONFIG_STM32_TIM3_DAC +#endif +#ifndef CONFIG_STM32_TIM4 +# undef CONFIG_STM32_TIM4_DAC +#endif +#ifndef CONFIG_STM32_TIM5 +# undef CONFIG_STM32_TIM5_DAC +#endif +#ifndef CONFIG_STM32_TIM6 +# undef CONFIG_STM32_TIM6_DAC +#endif +#ifndef CONFIG_STM32_TIM7 +# undef CONFIG_STM32_TIM7_DAC +#endif +#ifndef CONFIG_STM32_TIM8 +# undef CONFIG_STM32_TIM8_DAC +#endif +#ifndef CONFIG_STM32_TIM15 +# undef CONFIG_STM32_TIM15_DAC +#endif +#ifndef CONFIG_STM32_TIM16 +# undef CONFIG_STM32_TIM16_DAC +#endif +#ifndef CONFIG_STM32_TIM17 +# undef CONFIG_STM32_TIM17_DAC +#endif + +/* 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_START_DMA(dac) \ + (dac)->llops->start_dma(dac) +#define DAC_STOP_DMA(dac) \ + (dac)->llops->stop_dma(dac) +#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" ADC driver structure */ + + const struct stm32_dac_ops_s *llops; + + /* Require cast-compatibility with private "lower-half" ADC 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); + + /* Start DMA */ + + void (*start_dma)(struct stm32_dac_dev_s *dev); + + /* Stop DMA */ + + void (*stop_dma)(struct stm32_dac_dev_s *dev); + + /* Dump DAC regs */ + + void (*dump_regs)(struct stm32_dac_dev_s *dev); +}; + +#endif /* CONFIG_STM32_DAC_LL_OPS */ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * 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 + * + * 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_STM32_STM32H7_DAC_H */ \ No newline at end of file