This is an automated email from the ASF dual-hosted git repository.
xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git
The following commit(s) were added to refs/heads/master by this push:
new 08b8234e9e arch/arm/src/stm32f7: port ADC driver from arch/stm32
08b8234e9e is described below
commit 08b8234e9e90ee20913e203050a96b98ce2a55fc
Author: raiden00pl <[email protected]>
AuthorDate: Sat Jul 30 18:20:20 2022 +0200
arch/arm/src/stm32f7: port ADC driver from arch/stm32
This change adds support for the following features:
- injected channels (default: 0)
- ADC resolution (default: 12bit)
- ADC low-level operations
- ADC external triggers
- custom ADC interrupts
- ADC sample time configuration
- configurable ADC SCAN mode (default on if DMA)
- configurable ADC DMA mode (default: one shot mode)
- reset the ADC block only if all ADC instances are closed
---
arch/arm/src/stm32f7/Kconfig | 192 ++
arch/arm/src/stm32f7/hardware/stm32f72xx73xx_adc.h | 2 +
arch/arm/src/stm32f7/hardware/stm32f74xx77xx_adc.h | 2 +
arch/arm/src/stm32f7/stm32_adc.c | 1887 ++++++++++++++++----
arch/arm/src/stm32f7/stm32_adc.h | 424 ++++-
5 files changed, 2116 insertions(+), 391 deletions(-)
diff --git a/arch/arm/src/stm32f7/Kconfig b/arch/arm/src/stm32f7/Kconfig
index 388ef1d975..2dbf77eaad 100644
--- a/arch/arm/src/stm32f7/Kconfig
+++ b/arch/arm/src/stm32f7/Kconfig
@@ -6089,12 +6089,66 @@ endmenu
menu "ADC Configuration"
depends on STM32F7_ADC
+config STM32F7_ADC1_RESOLUTION
+ int "ADC1 resolution"
+ depends on STM32F7_ADC1
+ default 0
+ range 0 3
+ ---help---
+ ADC1 resolution. 0 - 12 bit, 1 - 10 bit, 2 - 8 bit, 3 - 6 bit
+
+config STM32F7_ADC2_RESOLUTION
+ int "ADC2 resolution"
+ depends on STM32F7_ADC2
+ default 0
+ range 0 3
+ ---help---
+ ADC2 resolution. 0 - 12 bit, 1 - 10 bit, 2 - 8 bit, 3 - 6 bit
+
+config STM32F7_ADC3_RESOLUTION
+ int "ADC3 resolution"
+ depends on STM32F7_ADC3
+ default 0
+ range 0 3
+ ---help---
+ ADC3 resolution. 0 - 12 bit, 1 - 10 bit, 2 - 8 bit, 3 - 6 bit
+
+config STM32F7_ADC_MAX_SAMPLES
+ int "The maximum number of channels that can be sampled"
+ default 16
+ ---help---
+ The maximum number of samples which can be handled without
+ overrun depends on various factors. This is the user's
+ responsibility to correctly select this value.
+ Since the interface to update the sampling time is available
+ for all supported devices, the user can change the default
+ values in the board initialization logic and avoid ADC overrun.
+
config STM32F7_ADC_NO_STARTUP_CONV
bool "Do not start conversion when opening ADC device"
default n
---help---
Do not start conversion when opening ADC device.
+config STM32F7_ADC_NOIRQ
+ bool "Do not use default ADC interrupts"
+ default n
+ ---help---
+ Do not use default ADC interrupts handlers.
+
+config STM32F7_ADC_LL_OPS
+ bool "ADC low-level operations"
+ default n
+ ---help---
+ Enable low-level ADC ops.
+
+config STM32F7_ADC_CHANGE_SAMPLETIME
+ bool "ADC sample time configuration"
+ default n
+ depends on STM32F7_ADC_LL_OPS
+ ---help---
+ Enable ADC sample time configuration (SMPRx registers).
+
config STM32F7_ADC1_DMA
bool "ADC1 DMA"
depends on STM32F7_ADC1 && STM32F7_HAVE_ADC1_DMA
@@ -6104,6 +6158,30 @@ config STM32F7_ADC1_DMA
DMA transfer, which is necessary if multiple channels are read
or if very high trigger frequencies are used.
+config STM32F7_ADC1_SCAN
+ bool "ADC1 scan mode"
+ depends on STM32F7_ADC1
+ default y if STM32F7_ADC1_DMA
+ default n
+
+config STM32F7_ADC1_DMA_CFG
+ int "ADC1 DMA configuration"
+ depends on STM32F7_ADC1_DMA
+ range 0 1
+ default 0
+ ---help---
+ 0 - ADC1 DMA in One Shot Mode, 1 - ADC1 DMA in Circular Mode
+
+config STM32F7_ADC1_ANIOC_TRIGGER
+ int "ADC1 software trigger (ANIOC_TRIGGER) configuration"
+ depends on STM32F7_ADC1
+ range 1 3
+ default 3
+ ---help---
+ 1 - ANIOC_TRIGGER only starts regular conversion
+ 2 - ANIOC_TRIGGER only starts injected conversion
+ 3 - ANIOC_TRIGGER starts both regular and injected conversions
+
config STM32F7_ADC2_DMA
bool "ADC2 DMA"
depends on STM32F7_ADC2 && STM32F7_HAVE_ADC2_DMA
@@ -6113,6 +6191,30 @@ config STM32F7_ADC2_DMA
DMA transfer, which is necessary if multiple channels are read
or if very high trigger frequencies are used.
+config STM32F7_ADC2_SCAN
+ bool "ADC2 scan mode"
+ depends on STM32F7_ADC2
+ default y if STM32F7_ADC2_DMA
+ default n
+
+config STM32F7_ADC2_DMA_CFG
+ int "ADC2 DMA configuration"
+ depends on STM32F7_ADC2_DMA
+ range 0 1
+ default 0
+ ---help---
+ 0 - ADC2 DMA in One Shot Mode, 1 - ADC2 DMA in Circular Mode
+
+config STM32F7_ADC2_ANIOC_TRIGGER
+ int "ADC2 software trigger (ANIOC_TRIGGER) configuration"
+ depends on STM32F7_ADC2
+ range 1 3
+ default 3
+ ---help---
+ 1 - ANIOC_TRIGGER only starts regular conversion
+ 2 - ANIOC_TRIGGER only starts injected conversion
+ 3 - ANIOC_TRIGGER starts both regular and injected conversions
+
config STM32F7_ADC3_DMA
bool "ADC3 DMA"
depends on STM32F7_ADC3 && STM32F7_HAVE_ADC3_DMA
@@ -6122,6 +6224,96 @@ config STM32F7_ADC3_DMA
DMA transfer, which is necessary if multiple channels are read
or if very high trigger frequencies are used.
+config STM32F7_ADC3_SCAN
+ bool "ADC3 scan mode"
+ depends on STM32F7_ADC3
+ default y if STM32F7_ADC3_DMA
+ default n
+
+config STM32F7_ADC3_DMA_CFG
+ int "ADC3 DMA configuration"
+ depends on STM32F7_ADC3_DMA
+ range 0 1
+ default 0
+ ---help---
+ 0 - ADC3 DMA in One Shot Mode, 1 - ADC3 DMA in Circular Mode
+
+config STM32F7_ADC3_ANIOC_TRIGGER
+ int "ADC3 software trigger (ANIOC_TRIGGER) configuration"
+ depends on STM32F7_ADC3
+ range 1 3
+ default 3
+ ---help---
+ 1 - ANIOC_TRIGGER only starts regular conversion
+ 2 - ANIOC_TRIGGER only starts injected conversion
+ 3 - ANIOC_TRIGGER starts both regular and injected conversions
+
+config STM32F7_ADC1_INJECTED_CHAN
+ int "ADC1 injected channels"
+ depends on STM32F7_ADC1
+ range 0 4
+ default 0
+ ---help---
+ Support for ADC1 injected channels.
+
+config STM32F7_ADC2_INJECTED_CHAN
+ int "ADC2 injected channels"
+ depends on STM32F7_ADC2
+ range 0 4
+ default 0
+ ---help---
+ Support for ADC2 injected channels.
+
+config STM32F7_ADC3_INJECTED_CHAN
+ int "ADC3 injected channels"
+ depends on STM32F7_ADC3
+ range 0 4
+ default 0
+ ---help---
+ Support for ADC3 injected channels.
+
+config STM32F7_ADC1_EXTSEL
+ bool "ADC1 external trigger for regular group"
+ depends on STM32F7_ADC1 && !STM32F7_HAVE_ADC1_TIMER
+ default n
+ ---help---
+ Enable EXTSEL for ADC1.
+
+config STM32F7_ADC2_EXTSEL
+ bool "ADC2 external trigger for regular group"
+ depends on STM32F7_ADC2 && !STM32F7_HAVE_ADC2_TIMER
+ default n
+ ---help---
+ Enable EXTSEL for ADC2.
+
+config STM32F7_ADC3_EXTSEL
+ bool "ADC3 external trigger for regular group"
+ depends on STM32F7_ADC3 && !STM32F7_HAVE_ADC3_TIMER
+ default n
+ ---help---
+ Enable EXTSEL for ADC3.
+
+config STM32F7_ADC1_JEXTSEL
+ bool "ADC1 external trigger for injected group"
+ depends on STM32F7_ADC1
+ default n
+ ---help---
+ Enable JEXTSEL for ADC1.
+
+config STM32F7_ADC2_JEXTSEL
+ bool "ADC2 external trigger for injected group"
+ depends on STM32F7_ADC2
+ default n
+ ---help---
+ Enable JEXTSEL for ADC2.
+
+config STM32F7_ADC3_JEXTSEL
+ bool "ADC3 external trigger for injected group"
+ depends on STM32F7_ADC3
+ default n
+ ---help---
+ Enable JEXTSEL for ADC3.
+
endmenu # "ADC Configuration"
menu "Ethernet MAC configuration"
diff --git a/arch/arm/src/stm32f7/hardware/stm32f72xx73xx_adc.h
b/arch/arm/src/stm32f7/hardware/stm32f72xx73xx_adc.h
index 9fa38fb02f..083f66074b 100644
--- a/arch/arm/src/stm32f7/hardware/stm32f72xx73xx_adc.h
+++ b/arch/arm/src/stm32f7/hardware/stm32f72xx73xx_adc.h
@@ -372,8 +372,10 @@
#define ADC_JSQR_JSQ3_MASK (0x1f << ADC_JSQR_JSQ3_SHIFT)
#define ADC_JSQR_JSQ4_SHIFT (15) /* Bits 19-15: 4th conversion
in injected sequence */
#define ADC_JSQR_JSQ4_MASK (0x1f << ADC_JSQR_JSQ4_SHIFT)
+#define ADC_JSQR_JSQ_SHIFT (5) /* Shift between JSQx bits */
#define ADC_JSQR_JL_SHIFT (20) /* Bits 21-20: Injected
Sequence length */
#define ADC_JSQR_JL_MASK (3 << ADC_JSQR_JL_SHIFT)
+# define ADC_JSQR_JL(n) (((n) - 1) << ADC_JSQR_JL_SHIFT) /*
n=1..4 */
/* ADC injected data register 1-4 */
diff --git a/arch/arm/src/stm32f7/hardware/stm32f74xx77xx_adc.h
b/arch/arm/src/stm32f7/hardware/stm32f74xx77xx_adc.h
index 2ec4d963b8..9b2a598833 100644
--- a/arch/arm/src/stm32f7/hardware/stm32f74xx77xx_adc.h
+++ b/arch/arm/src/stm32f7/hardware/stm32f74xx77xx_adc.h
@@ -372,8 +372,10 @@
#define ADC_JSQR_JSQ3_MASK (0x1f << ADC_JSQR_JSQ3_SHIFT)
#define ADC_JSQR_JSQ4_SHIFT (15) /* Bits 19-15: 4th conversion
in injected sequence */
#define ADC_JSQR_JSQ4_MASK (0x1f << ADC_JSQR_JSQ4_SHIFT)
+#define ADC_JSQR_JSQ_SHIFT (5) /* Shift between JSQx bits */
#define ADC_JSQR_JL_SHIFT (20) /* Bits 21-20: Injected
Sequence length */
#define ADC_JSQR_JL_MASK (3 << ADC_JSQR_JL_SHIFT)
+# define ADC_JSQR_JL(n) (((n) - 1) << ADC_JSQR_JL_SHIFT) /*
n=1..4 */
/* ADC injected data register 1-4 */
diff --git a/arch/arm/src/stm32f7/stm32_adc.c b/arch/arm/src/stm32f7/stm32_adc.c
index 5a23886e96..c452001a46 100644
--- a/arch/arm/src/stm32f7/stm32_adc.c
+++ b/arch/arm/src/stm32f7/stm32_adc.c
@@ -45,6 +45,7 @@
#include <nuttx/power/pm.h>
#include <nuttx/analog/adc.h>
#include <nuttx/analog/ioctl.h>
+#include <nuttx/semaphore.h>
#include "arm_internal.h"
#include "chip.h"
@@ -53,20 +54,21 @@
#include "stm32_dma.h"
#include "stm32_adc.h"
-/* ADC "upper half" support must be enabled */
+/* Based on arch/src/stm32/stm32_adc.c */
-#ifdef CONFIG_ADC
-
-/* Some ADC peripheral must be enabled */
+/* STM32 ADC "lower-half" support must be enabled */
-#if defined(CONFIG_STM32F7_ADC1) || defined(CONFIG_STM32F7_ADC2) || \
- defined(CONFIG_STM32F7_ADC3)
+#ifdef CONFIG_STM32F7_ADC
-/* This implementation is for the STM32 F7[4-7] only */
+/* This implementation is for the STM32 ADC IP version 1 */
-#if defined(CONFIG_STM32F7_STM32F72XX) || defined(CONFIG_STM32F7_STM32F73XX)
|| \
- defined(CONFIG_STM32F7_STM32F74XX) || defined(CONFIG_STM32F7_STM32F75XX)
|| \
- defined(CONFIG_STM32F7_STM32F76XX) || defined(CONFIG_STM32F7_STM32F77XX)
+/* Supported ADC modes:
+ * - SW triggering with/without DMA transfer
+ * - TIM triggering with/without DMA transfer
+ * - external triggering with/without DMA transfer
+ *
+ * (tested with ADC example app from NuttX apps repo).
+ */
/****************************************************************************
* Pre-processor Definitions
@@ -74,65 +76,18 @@
/* RCC reset ****************************************************************/
-#define STM32_RCC_RSTR STM32_RCC_APB2RSTR
-#define RCC_RSTR_ADC1RST RCC_APB2RSTR_ADCRST
-#define RCC_RSTR_ADC2RST RCC_APB2RSTR_ADCRST
-#define RCC_RSTR_ADC3RST RCC_APB2RSTR_ADCRST
-
-/* ADC interrupts ***********************************************************/
-
-#define STM32_ADC_DMAREG_OFFSET STM32_ADC_CR2_OFFSET
-#define ADC_DMAREG_DMA ADC_CR2_DMA
-#define STM32_ADC_EXTREG_OFFSET STM32_ADC_CR2_OFFSET
-#define ADC_EXTREG_EXTSEL_MASK ADC_CR2_EXTSEL_MASK
-#define STM32_ADC_ISR_OFFSET STM32_ADC_SR_OFFSET
-#define STM32_ADC_IER_OFFSET STM32_ADC_CR1_OFFSET
-#define ADC_ISR_EOC ADC_SR_EOC
-#define ADC_IER_EOC ADC_CR1_EOCIE
-#define ADC_ISR_AWD ADC_SR_AWD
-#define ADC_IER_AWD ADC_CR1_AWDIE
-#define ADC_ISR_JEOC ADC_SR_JEOC
-#define ADC_IER_JEOC ADC_CR1_JEOCIE
-#define ADC_EXTREG_EXTEN_MASK ADC_CR2_EXTEN_MASK
-#define ADC_EXTREG_EXTEN_NONE ADC_CR2_EXTEN_NONE
-#define ADC_EXTREG_EXTEN_DEFAULT ADC_CR2_EXTEN_RISING
-#define ADC_ISR_OVR ADC_SR_OVR
-#define ADC_IER_OVR ADC_CR1_OVRIE
-
-#define ADC_ISR_ALLINTS (ADC_ISR_EOC | ADC_ISR_AWD | ADC_ISR_JEOC | \
- ADC_ISR_OVR)
-#define ADC_IER_ALLINTS (ADC_IER_EOC | ADC_IER_AWD | ADC_IER_JEOC | \
- ADC_IER_OVR)
+#define STM32_RCC_RSTR STM32_RCC_APB2RSTR
+#define RCC_RSTR_ADC123RST RCC_APB2RSTR_ADCRST
/* ADC Channels/DMA *********************************************************/
-/* The maximum number of channels that can be sampled. If DMA support is
- * not enabled, then only a single channel can be sampled. Otherwise,
- * data overruns would occur.
- */
-
-#define ADC_MAX_CHANNELS_DMA 16
-#define ADC_MAX_CHANNELS_NODMA 1
-
-#ifdef ADC_HAVE_DMA
-# ifndef CONFIG_STM32F7_DMA2
-# error "STM32F7 ADC DMA support requires CONFIG_STM32F7_DMA2"
-# endif
-#endif
-
-#ifdef ADC_HAVE_DMA
-# define ADC_MAX_SAMPLES ADC_MAX_CHANNELS_DMA
-#else
-# define ADC_MAX_SAMPLES ADC_MAX_CHANNELS_NODMA
-#endif
-
-#define ADC_DMA_CONTROL_WORD (DMA_SCR_MSIZE_16BITS | \
- DMA_SCR_PSIZE_16BITS | \
- DMA_SCR_MINC | \
- DMA_SCR_CIRC | \
- DMA_SCR_DIR_P2M)
+#define ADC_DMA_CONTROL_WORD (DMA_SCR_MSIZE_16BITS | \
+ DMA_SCR_PSIZE_16BITS | \
+ DMA_SCR_MINC | \
+ DMA_SCR_CIRC | \
+ DMA_SCR_DIR_P2M)
-/* DMA channels and interface values */
+/* Sample time default configuration */
#define ADC_SMPR_DEFAULT ADC_SMPR_112
#define ADC_SMPR1_DEFAULT ((ADC_SMPR_DEFAULT << ADC_SMPR1_SMP10_SHIFT) | \
@@ -155,6 +110,41 @@
(ADC_SMPR_DEFAULT << ADC_SMPR2_SMP8_SHIFT) | \
(ADC_SMPR_DEFAULT << ADC_SMPR2_SMP9_SHIFT))
+/* Number of channels per ADC */
+
+#define ADC_CHANNELS_NUMBER 19
+
+/* Max 4 injected channels */
+
+#define ADC_INJ_MAX_SAMPLES 4
+
+/* ADC scan mode support */
+
+#ifndef CONFIG_STM32F7_ADC1_SCAN
+# define CONFIG_STM32F7_ADC1_SCAN 0
+#endif
+#ifndef CONFIG_STM32F7_ADC2_SCAN
+# define CONFIG_STM32F7_ADC2_SCAN 0
+#endif
+#ifndef CONFIG_STM32F7_ADC3_SCAN
+# define CONFIG_STM32F7_ADC3_SCAN 0
+#endif
+
+/* We have to support ADC callbacks if default ADC interrupts or
+ * DMA transfer are enabled
+ */
+
+#if !defined(CONFIG_STM32F7_ADC_NOIRQ) || defined(ADC_HAVE_DMA)
+# define ADC_HAVE_CB
+#else
+# undef ADC_HAVE_CB
+#endif
+
+/* ADC software trigger configuration */
+
+#define ANIOC_TRIGGER_REGULAR (1 << 0)
+#define ANIOC_TRIGGER_INJECTED (1 << 1)
+
/* The last external channel on ADC 1 to enable Reading Vref or
* Vbat / Vsence
*/
@@ -181,32 +171,68 @@
* Private Types
****************************************************************************/
+/* Data common to all ADC instances */
+
+struct adccmn_data_s
+{
+ uint8_t refcount; /* How many ADC instances are currently in use */
+ sem_t lock; /* Exclusive access to common ADC data */
+};
+
/* This structure describes the state of one ADC block */
struct stm32_dev_s
{
+#ifdef CONFIG_STM32F7_ADC_LL_OPS
+ const struct stm32_adc_ops_s *llops; /* Low-level ADC ops */
+ struct adc_dev_s *dev; /* Upper-half ADC reference */
+#endif
+#ifdef ADC_HAVE_CB
const struct adc_callback_s *cb;
- uint8_t irq; /* Interrupt generated by this ADC block */
- uint8_t nchannels; /* Number of channels */
- uint8_t cchannels; /* Number of configured channels */
- uint8_t intf; /* ADC interface number */
- uint8_t current; /* Current ADC channel being converted */
+ uint8_t irq; /* Interrupt generated by this ADC block */
+#endif
+ struct adccmn_data_s *cmn; /* Common ADC data */
+ uint8_t rnchannels; /* Number of regular channels */
+ uint8_t cr_channels; /* Number of configured regular channels */
+#ifdef ADC_HAVE_INJECTED
+ uint8_t cj_channels; /* Number of configured injected channels */
+#endif
+ uint8_t intf; /* ADC interface number */
+ uint8_t initialized; /* ADC interface initialization counter */
+ uint8_t current; /* Current ADC channel being converted */
+ uint8_t anioc_trg; /* ANIOC_TRIGGER configuration */
+ uint8_t resolution; /* ADC resolution (0-3) */
#ifdef ADC_HAVE_DMA
- uint8_t dmachan; /* DMA channel needed by this ADC */
- bool hasdma; /* True: This channel supports DMA */
+ uint8_t dmachan; /* DMA channel needed by this ADC */
+ uint8_t dmacfg; /* DMA channel configuration, only for ADC IPv2 */
+ bool hasdma; /* True: This channel supports DMA */
+#endif
+ bool scan; /* True: Scan mode */
+#ifdef CONFIG_STM32F7_ADC_CHANGE_SAMPLETIME
+ /* Sample time selection. These bits must be written only when ADON=0.
+ * REVISIT: this takes too much space. We need only 3 bits per channel.
+ */
+
+ uint8_t sample_rate[ADC_CHANNELS_NUMBER];
+ uint8_t adc_channels; /* ADC channels number */
#endif
#ifdef ADC_HAVE_TIMER
- uint8_t trigger; /* Timer trigger channel: 0=CC1, 1=CC2, 2=CC3,
- * 3=CC4, 4=TRGO */
+ uint8_t trigger; /* Timer trigger channel: 0=CC1, 1=CC2, 2=CC3,
+ * 3=CC4, 4=TRGO */
+#endif
+ xcpt_t isr; /* Interrupt handler for this ADC block */
+ uint32_t base; /* Base address of registers unique to this ADC
+ * block */
+#ifdef ADC_HAVE_EXTCFG
+ uint32_t extcfg; /* External event configuration for regular group
*/
+#endif
+#ifdef ADC_HAVE_JEXTCFG
+ uint32_t jextcfg; /* External event configuration for injected
group */
#endif
- xcpt_t isr; /* Interrupt handler for this ADC block */
- uint32_t base; /* Base address of registers unique to this ADC
- * block */
#ifdef ADC_HAVE_TIMER
- uint32_t tbase; /* Base address of timer used by this ADC block */
- uint32_t extsel; /* EXTSEL value used by this ADC block */
- uint32_t pclck; /* The PCLK frequency that drives this timer */
- uint32_t freq; /* The desired frequency of conversions */
+ uint32_t tbase; /* Base address of timer used by this ADC block */
+ uint32_t pclck; /* The PCLK frequency that drives this timer */
+ uint32_t freq; /* The desired frequency of conversions */
#endif
#ifdef CONFIG_PM
@@ -214,16 +240,22 @@ struct stm32_dev_s
#endif
#ifdef ADC_HAVE_DMA
- DMA_HANDLE dma; /* Allocated DMA channel */
+ DMA_HANDLE dma; /* Allocated DMA channel */
/* DMA transfer buffer */
- uint16_t dmabuffer[ADC_MAX_SAMPLES];
+ uint16_t r_dmabuffer[CONFIG_STM32F7_ADC_MAX_SAMPLES];
#endif
/* List of selected ADC channels to sample */
- uint8_t chanlist[ADC_MAX_SAMPLES];
+ uint8_t r_chanlist[CONFIG_STM32F7_ADC_MAX_SAMPLES];
+
+#ifdef ADC_HAVE_INJECTED
+ /* List of selected ADC injected channels to sample */
+
+ uint8_t j_chanlist[ADC_INJ_MAX_SAMPLES];
+#endif
};
/****************************************************************************
@@ -235,26 +267,33 @@ struct stm32_dev_s
static void stm32_modifyreg32(unsigned int addr, uint32_t clrbits,
uint32_t setbits);
static uint32_t adc_getreg(struct stm32_dev_s *priv, int offset);
-static void adc_putreg(struct stm32_dev_s *priv, int offset,
- uint32_t value);
-static void adc_modifyreg(struct stm32_dev_s *priv, int offset,
- uint32_t clrbits, uint32_t setbits);
+static void adc_putreg(struct stm32_dev_s *priv, int offset,
+ uint32_t value);
+static void adc_modifyreg(struct stm32_dev_s *priv, int offset,
+ uint32_t clrbits, uint32_t setbits);
+static void adccmn_modifyreg(struct stm32_dev_s *priv, uint32_t offset,
+ uint32_t clrbits, uint32_t setbits);
+static uint32_t adccmn_getreg(struct stm32_dev_s *priv, uint32_t offset);
+
#ifdef ADC_HAVE_TIMER
static uint16_t tim_getreg(struct stm32_dev_s *priv, int offset);
-static void tim_putreg(struct stm32_dev_s *priv, int offset,
- uint16_t value);
-static void tim_modifyreg(struct stm32_dev_s *priv, int offset,
- uint16_t clrbits, uint16_t setbits);
-static void tim_dumpregs(struct stm32_dev_s *priv,
- const char *msg);
+static void tim_putreg(struct stm32_dev_s *priv, int offset,
+ uint16_t value);
+static void tim_modifyreg(struct stm32_dev_s *priv, int offset,
+ uint16_t clrbits, uint16_t setbits);
+static void tim_dumpregs(struct stm32_dev_s *priv, const char *msg);
#endif
+static int adccmn_lock(struct stm32_dev_s *priv, bool lock);
+
static void adc_rccreset(struct stm32_dev_s *priv, bool reset);
/* ADC Interrupt Handler */
+#ifndef CONFIG_STM32F7_ADC_NOIRQ
static int adc_interrupt(struct adc_dev_s *dev);
static int adc123_interrupt(int irq, void *context, void *arg);
+#endif /* CONFIG_STM32F7_ADC_NOIRQ */
/* ADC Driver Methods */
@@ -268,28 +307,79 @@ static int adc_ioctl(struct adc_dev_s *dev, int cmd,
unsigned long arg);
static void adc_enable(struct stm32_dev_s *priv, bool enable);
static uint32_t adc_sqrbits(struct stm32_dev_s *priv, int first,
- int last,
- int offset);
-static int adc_set_ch(struct adc_dev_s *dev, uint8_t ch);
-static bool adc_internal(struct stm32_dev_s * priv);
+ int last, int offset);
+static int adc_set_ch(struct adc_dev_s *dev, uint8_t ch);
+static bool adc_internal(struct stm32_dev_s * priv);
+
+static int adc_resolution_set(struct adc_dev_s *dev, uint8_t res);
#ifdef ADC_HAVE_TIMER
static void adc_timstart(struct stm32_dev_s *priv, bool enable);
static int adc_timinit(struct stm32_dev_s *priv);
#endif
-#ifdef ADC_HAVE_DMA
+#if defined(ADC_HAVE_DMA) && !defined(CONFIG_STM32F7_ADC_NOIRQ)
static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t isr,
void *arg);
#endif
-static void adc_startconv(struct stm32_dev_s *priv, bool enable);
-
#ifdef CONFIG_PM
static int adc_pm_prepare(struct pm_callback_s *cb, int domain,
enum pm_state_e state);
#endif
+static void adc_reg_startconv(struct stm32_dev_s *priv, bool enable);
+#ifdef ADC_HAVE_INJECTED
+static void adc_inj_startconv(struct stm32_dev_s *priv, bool enable);
+static int adc_inj_set_ch(struct adc_dev_s *dev, uint8_t ch);
+#endif
+
+#ifdef ADC_HAVE_EXTCFG
+static int adc_extcfg_set(struct stm32_dev_s *priv, uint32_t extcfg);
+#endif
+#ifdef ADC_HAVE_JEXTCFG
+static int adc_jextcfg_set(struct stm32_dev_s *priv, uint32_t jextcfg);
+#endif
+
+static void adc_dumpregs(struct stm32_dev_s *priv);
+
+#ifdef CONFIG_STM32F7_ADC_LL_OPS
+static int adc_llops_setup(struct stm32_adc_dev_s *dev);
+static void adc_llops_shutdown(struct stm32_adc_dev_s *dev);
+static void adc_intack(struct stm32_adc_dev_s *dev, uint32_t source);
+static void adc_inten(struct stm32_adc_dev_s *dev, uint32_t source);
+static void adc_intdis(struct stm32_adc_dev_s *dev, uint32_t source);
+static uint32_t adc_intget(struct stm32_adc_dev_s *dev);
+static uint32_t adc_regget(struct stm32_adc_dev_s *dev);
+static void adc_llops_reg_startconv(struct stm32_adc_dev_s *dev,
+ bool enable);
+static int adc_offset_set(struct stm32_adc_dev_s *dev, uint8_t ch,
+ uint8_t i, uint16_t offset);
+# ifdef ADC_HAVE_EXTCFG
+static void adc_llops_extcfg_set(struct stm32_adc_dev_s *dev,
+ uint32_t extcfg);
+# endif
+# ifdef ADC_HAVE_JEXTCFG
+static void adc_llops_jextcfg_set(struct stm32_adc_dev_s *dev,
+ uint32_t jextcfg);
+# endif
+# ifdef ADC_HAVE_DMA
+static int adc_regbufregister(struct stm32_adc_dev_s *dev,
+ uint16_t *buffer, uint8_t len);
+# endif
+# ifdef ADC_HAVE_INJECTED
+static uint32_t adc_injget(struct stm32_adc_dev_s *dev, uint8_t chan);
+static void adc_llops_inj_startconv(struct stm32_adc_dev_s *dev,
+ bool enable);
+# endif
+# ifdef CONFIG_STM32F7_ADC_CHANGE_SAMPLETIME
+static void adc_sampletime_set(struct stm32_adc_dev_s *dev,
+ struct adc_sample_time_s *time_samples);
+static void adc_sampletime_write(struct stm32_adc_dev_s *dev);
+# endif
+static void adc_llops_dumpregs(struct stm32_adc_dev_s *dev);
+#endif
+
/****************************************************************************
* Private Data
****************************************************************************/
@@ -306,26 +396,90 @@ static const struct adc_ops_s g_adcops =
.ao_ioctl = adc_ioctl,
};
+/* Publicly visible ADC lower-half operations */
+
+#ifdef CONFIG_STM32F7_ADC_LL_OPS
+static const struct stm32_adc_ops_s g_adc_llops =
+{
+ .setup = adc_llops_setup,
+ .shutdown = adc_llops_shutdown,
+ .int_ack = adc_intack,
+ .int_get = adc_intget,
+ .int_en = adc_inten,
+ .int_dis = adc_intdis,
+ .val_get = adc_regget,
+ .reg_startconv = adc_llops_reg_startconv,
+ .offset_set = adc_offset_set,
+# ifdef ADC_HAVE_DMA
+ .regbuf_reg = adc_regbufregister,
+# endif
+# ifdef ADC_HAVE_EXTCFG
+ .extcfg_set = adc_llops_extcfg_set,
+# endif
+# ifdef ADC_HAVE_JEXTCFG
+ .jextcfg_set = adc_llops_jextcfg_set,
+# endif
+# ifdef ADC_HAVE_INJECTED
+ .inj_get = adc_injget,
+ .inj_startconv = adc_llops_inj_startconv,
+# endif
+# ifdef CONFIG_STM32F7_ADC_CHANGE_SAMPLETIME
+ .stime_set = adc_sampletime_set,
+ .stime_write = adc_sampletime_write,
+# endif
+ .dump_regs = adc_llops_dumpregs
+};
+#endif
+
+/* ADC instances are coupled in blocks */
+
+#define ADC1CMN_DATA g_adc123_cmn
+#define ADC2CMN_DATA g_adc123_cmn
+#define ADC3CMN_DATA g_adc123_cmn
+
+/* ADC123 common data */
+
+struct adccmn_data_s g_adc123_cmn =
+{
+ .refcount = 0
+};
+
/* ADC1 state */
#ifdef CONFIG_STM32F7_ADC1
static struct stm32_dev_s g_adcpriv1 =
{
+#ifdef CONFIG_STM32F7_ADC_LL_OPS
+ .llops = &g_adc_llops,
+#endif
+#ifndef CONFIG_STM32F7_ADC_NOIRQ
.irq = STM32_IRQ_ADC,
.isr = adc123_interrupt,
+#endif /* CONFIG_STM32F7_ADC_NOIRQ */
+ .cmn = &ADC1CMN_DATA,
.intf = 1,
+ .initialized = 0,
+ .anioc_trg = CONFIG_STM32F7_ADC1_ANIOC_TRIGGER,
+ .resolution = CONFIG_STM32F7_ADC1_RESOLUTION,
.base = STM32_ADC1_BASE,
+#ifdef ADC1_HAVE_EXTCFG
+ .extcfg = ADC1_EXTCFG_VALUE,
+#endif
+#ifdef ADC1_HAVE_JEXTCFG
+ .jextcfg = ADC1_JEXTCFG_VALUE,
+#endif
#ifdef ADC1_HAVE_TIMER
.trigger = CONFIG_STM32F7_ADC1_TIMTRIG,
.tbase = ADC1_TIMER_BASE,
- .extsel = ADC1_EXTSEL_VALUE,
.pclck = ADC1_TIMER_PCLK_FREQUENCY,
.freq = CONFIG_STM32F7_ADC1_SAMPLE_FREQUENCY,
#endif
#ifdef ADC1_HAVE_DMA
.dmachan = ADC1_DMA_CHAN,
+ .dmacfg = CONFIG_STM32F7_ADC1_DMA_CFG,
.hasdma = true,
#endif
+ .scan = CONFIG_STM32F7_ADC1_SCAN,
#ifdef CONFIG_PM
.pm_callback =
{
@@ -346,21 +500,37 @@ static struct adc_dev_s g_adcdev1 =
#ifdef CONFIG_STM32F7_ADC2
static struct stm32_dev_s g_adcpriv2 =
{
+#ifdef CONFIG_STM32F7_ADC_LL_OPS
+ .llops = &g_adc_llops,
+#endif
+#ifndef CONFIG_STM32F7_ADC_NOIRQ
.irq = STM32_IRQ_ADC,
.isr = adc123_interrupt,
+#endif /* CONFIG_STM32F7_ADC_NOIRQ */
+ .cmn = &ADC2CMN_DATA,
.intf = 2,
+ .initialized = 0,
+ .anioc_trg = CONFIG_STM32F7_ADC2_ANIOC_TRIGGER,
+ .resolution = CONFIG_STM32F7_ADC2_RESOLUTION,
.base = STM32_ADC2_BASE,
+#ifdef ADC2_HAVE_EXTCFG
+ .extcfg = ADC2_EXTCFG_VALUE,
+#endif
+#ifdef ADC2_HAVE_JEXTCFG
+ .jextcfg = ADC2_JEXTCFG_VALUE,
+#endif
#ifdef ADC2_HAVE_TIMER
.trigger = CONFIG_STM32F7_ADC2_TIMTRIG,
.tbase = ADC2_TIMER_BASE,
- .extsel = ADC2_EXTSEL_VALUE,
.pclck = ADC2_TIMER_PCLK_FREQUENCY,
.freq = CONFIG_STM32F7_ADC2_SAMPLE_FREQUENCY,
#endif
#ifdef ADC2_HAVE_DMA
.dmachan = ADC2_DMA_CHAN,
+ .dmacfg = CONFIG_STM32F7_ADC2_DMA_CFG,
.hasdma = true,
#endif
+ .scan = CONFIG_STM32F7_ADC2_SCAN,
#ifdef CONFIG_PM
.pm_callback =
{
@@ -381,21 +551,37 @@ static struct adc_dev_s g_adcdev2 =
#ifdef CONFIG_STM32F7_ADC3
static struct stm32_dev_s g_adcpriv3 =
{
+#ifdef CONFIG_STM32F7_ADC_LL_OPS
+ .llops = &g_adc_llops,
+#endif
+#ifndef CONFIG_STM32F7_ADC_NOIRQ
.irq = STM32_IRQ_ADC,
.isr = adc123_interrupt,
+#endif /* CONFIG_STM32F7_ADC_NOIRQ */
+ .cmn = &ADC3CMN_DATA,
.intf = 3,
+ .initialized = 0,
+ .anioc_trg = CONFIG_STM32F7_ADC3_ANIOC_TRIGGER,
+ .resolution = CONFIG_STM32F7_ADC3_RESOLUTION,
.base = STM32_ADC3_BASE,
+#ifdef ADC3_HAVE_EXTCFG
+ .extcfg = ADC3_EXTCFG_VALUE,
+#endif
+#ifdef ADC3_HAVE_JEXTCFG
+ .jextcfg = ADC3_JEXTCFG_VALUE,
+#endif
#ifdef ADC3_HAVE_TIMER
.trigger = CONFIG_STM32F7_ADC3_TIMTRIG,
.tbase = ADC3_TIMER_BASE,
- .extsel = ADC3_EXTSEL_VALUE,
.pclck = ADC3_TIMER_PCLK_FREQUENCY,
.freq = CONFIG_STM32F7_ADC3_SAMPLE_FREQUENCY,
#endif
#ifdef ADC3_HAVE_DMA
.dmachan = ADC3_DMA_CHAN,
+ .dmacfg = CONFIG_STM32F7_ADC3_DMA_CFG,
.hasdma = true,
#endif
+ .scan = CONFIG_STM32F7_ADC3_SCAN,
#ifdef CONFIG_PM
.pm_callback =
{
@@ -502,6 +688,29 @@ static void adc_modifyreg(struct stm32_dev_s *priv, int
offset,
adc_putreg(priv, offset, (adc_getreg(priv, offset) & ~clrbits) | setbits);
}
+/****************************************************************************
+ * Name: adccmn_modifyreg
+ ****************************************************************************/
+
+static void adccmn_modifyreg(struct stm32_dev_s *priv, uint32_t offset,
+ uint32_t clrbits, uint32_t setbits)
+{
+ /* Modify register */
+
+ stm32_modifyreg32(offset + STM32_ADCCMN_BASE, clrbits, setbits);
+}
+
+/****************************************************************************
+ * Name: adccmn_getreg
+ ****************************************************************************/
+
+static uint32_t adccmn_getreg(struct stm32_dev_s *priv, uint32_t offset)
+{
+ /* Return register value */
+
+ return getreg32(offset + STM32_ADCCMN_BASE);
+}
+
/****************************************************************************
* Name: tim_getreg
*
@@ -610,6 +819,7 @@ static void tim_dumpregs(struct stm32_dev_s *priv, const
char *msg)
tim_getreg(priv, STM32_GTIM_CCR2_OFFSET),
tim_getreg(priv, STM32_GTIM_CCR3_OFFSET),
tim_getreg(priv, STM32_GTIM_CCR4_OFFSET));
+
if (priv->tbase == STM32_TIM1_BASE || priv->tbase == STM32_TIM8_BASE)
{
ainfo(" RCR: %04x BDTR: %04x DCR: %04x DMAR: %04x\n",
@@ -703,20 +913,7 @@ static int adc_timinit(struct stm32_dev_s *priv)
return ERROR;
}
- /* EXTSEL selection: These bits select the external event used to trigger
- * the start of conversion of a regular group. NOTE:
- *
- * - The position with of the EXTSEL field varies from one STM32 MCU
- * to another.
- * - The width of the EXTSEL field varies from one STM32 MCU to another.
- * - The value in priv->extsel is already shifted into the correct bit
- * position.
- */
-
- ainfo("Initializing timers extsel = 0x%08" PRIx32 "\n", priv->extsel);
- adc_modifyreg(priv, STM32_ADC_EXTREG_OFFSET,
- ADC_EXTREG_EXTEN_MASK | ADC_EXTREG_EXTSEL_MASK,
- ADC_EXTREG_EXTEN_DEFAULT | priv->extsel);
+ /* NOTE: EXTSEL configuration is done in adc_reset function */
/* Configure the timer channel to drive the ADC */
@@ -971,6 +1168,20 @@ static int adc_timinit(struct stm32_dev_s *priv)
ccer &= ~(GTIM_CCER_CC1NP | GTIM_CCER_CC2NP | GTIM_CCER_CC3NP);
}
+ /* Reset the output compare and output compare N IDLE State */
+
+ if (priv->tbase >= STM32_TIM2_BASE && priv->tbase <= STM32_TIM4_BASE)
+ {
+ /* Reset output N polarity level, output N state, output compare state,
+ * output compare N idle state.
+ */
+
+ ccer &= ~(GTIM_CCER_CC1NE | GTIM_CCER_CC1NP |
+ GTIM_CCER_CC2NE | GTIM_CCER_CC2NP |
+ GTIM_CCER_CC3NE | GTIM_CCER_CC3NP |
+ GTIM_CCER_CC4NP);
+ }
+
/* Save the modified register values */
tim_putreg(priv, STM32_GTIM_CR2_OFFSET, cr2);
@@ -1023,10 +1234,10 @@ static int adc_pm_prepare(struct pm_callback_s *cb, int
domain,
#endif
/****************************************************************************
- * Name: adc_startconv
+ * Name: adc_reg_startconv
*
* Description:
- * Start (or stop) the ADC conversion process
+ * Start (or stop) the ADC regular conversion process
*
* Input Parameters:
* priv - A reference to the ADC block status
@@ -1036,9 +1247,9 @@ static int adc_pm_prepare(struct pm_callback_s *cb, int
domain,
*
****************************************************************************/
-static void adc_startconv(struct stm32_dev_s *priv, bool enable)
+static void adc_reg_startconv(struct stm32_dev_s *priv, bool enable)
{
- ainfo("enable: %d\n", enable ? 1 : 0);
+ ainfo("reg enable: %d\n", enable ? 1 : 0);
if (enable)
{
@@ -1054,6 +1265,62 @@ static void adc_startconv(struct stm32_dev_s *priv, bool
enable)
}
}
+#ifdef ADC_HAVE_INJECTED
+
+/****************************************************************************
+ * Name: adc_inj_startconv
+ *
+ * Description:
+ * Start (or stop) the ADC injected conversion process
+ *
+ * Input Parameters:
+ * priv - A reference to the ADC block status
+ * enable - True: Start conversion
+ *
+ * Returned Value:
+ *
+ ****************************************************************************/
+
+static void adc_inj_startconv(struct stm32_dev_s *priv, bool enable)
+{
+ ainfo("inj enable: %d\n", enable ? 1 : 0);
+
+ if (enable)
+ {
+ /* Start the conversion of injected channels */
+
+ adc_modifyreg(priv, STM32_ADC_CR2_OFFSET, 0, ADC_CR2_JSWSTART);
+ }
+ else
+ {
+ /* Stop the conversion */
+
+ adc_modifyreg(priv, STM32_ADC_CR2_OFFSET, ADC_CR2_JSWSTART, 0);
+ }
+}
+
+#endif /* ADC_HAVE_INJECTED */
+
+/****************************************************************************
+ * Name: adccmn_lock
+ ****************************************************************************/
+
+static int adccmn_lock(struct stm32_dev_s *priv, bool lock)
+{
+ int ret;
+
+ if (lock)
+ {
+ ret = nxsem_wait_uninterruptible(&priv->cmn->lock);
+ }
+ else
+ {
+ ret = nxsem_post(&priv->cmn->lock);
+ }
+
+ return ret;
+}
+
/****************************************************************************
* Name: adc_rccreset
*
@@ -1073,37 +1340,27 @@ static void adc_rccreset(struct stm32_dev_s *priv, bool
reset)
{
uint32_t adcbit;
- /* Pick the appropriate bit in the APB2 reset register.
- * For the STM32 F1, there is an individual bit to reset each ADC,
- * but for the STM32 F2/F4, there is one common reset for all ADCs.
- * THIS will probably cause some problems!
+ /* Pick the appropriate bit in the RCC reset register.
+ * For the STM32 ADC IPv1, there is one common reset for all ADCs.
*/
switch (priv->intf)
{
-#ifdef CONFIG_STM32F7_ADC1
case 1:
- adcbit = RCC_RSTR_ADC1RST;
- break;
-#endif
-#ifdef CONFIG_STM32F7_ADC2
case 2:
- adcbit = RCC_RSTR_ADC2RST;
- break;
-#endif
-#ifdef CONFIG_STM32F7_ADC3
case 3:
- adcbit = RCC_RSTR_ADC3RST;
- break;
-#endif
+ {
+ adcbit = RCC_RSTR_ADC123RST;
+ break;
+ }
+
default:
- return;
+ {
+ return;
+ }
}
- /* Set or clear the selected bit in the APB2 reset register.
- * modifyreg32() disables interrupts. Disabling interrupts is necessary
- * because the APB2RTSR register is used by several different drivers.
- */
+ /* Set or clear the selected bit in the RCC reset register */
if (reset)
{
@@ -1150,7 +1407,7 @@ static void adc_enable(struct stm32_dev_s *priv, bool
enable)
}
/****************************************************************************
- * Name: adc_dmacovcallback
+ * Name: adc_dmaconvcallback
*
* Description:
* Callback for DMA. Called from the DMA transfer complete interrupt after
@@ -1166,7 +1423,7 @@ static void adc_enable(struct stm32_dev_s *priv, bool
enable)
*
****************************************************************************/
-#ifdef ADC_HAVE_DMA
+#if defined(ADC_HAVE_DMA) && !defined(CONFIG_STM32F7_ADC_NOIRQ)
static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t isr,
void *arg)
{
@@ -1174,8 +1431,9 @@ static void adc_dmaconvcallback(DMA_HANDLE handle,
uint8_t isr,
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
int i;
- up_invalidate_dcache((uintptr_t)priv->dmabuffer,
- (uintptr_t)priv->dmabuffer + sizeof(priv->dmabuffer));
+ up_invalidate_dcache((uintptr_t)priv->r_dmabuffer,
+ (uintptr_t)priv->r_dmabuffer +
+ sizeof(priv->r_dmabuffer));
/* Verify that the upper-half driver has bound its callback functions */
@@ -1183,12 +1441,12 @@ static void adc_dmaconvcallback(DMA_HANDLE handle,
uint8_t isr,
{
DEBUGASSERT(priv->cb->au_receive != NULL);
- for (i = 0; i < priv->nchannels; i++)
+ for (i = 0; i < priv->rnchannels; i++)
{
- priv->cb->au_receive(dev, priv->chanlist[priv->current],
- priv->dmabuffer[priv->current]);
+ priv->cb->au_receive(dev, priv->r_chanlist[priv->current],
+ priv->r_dmabuffer[priv->current]);
priv->current++;
- if (priv->current >= priv->nchannels)
+ if (priv->current >= priv->rnchannels)
{
/* Restart the conversion sequence from the beginning */
@@ -1216,46 +1474,27 @@ static void adc_dmaconvcallback(DMA_HANDLE handle,
uint8_t isr,
static int adc_bind(struct adc_dev_s *dev,
const struct adc_callback_s *callback)
{
+#ifdef ADC_HAVE_CB
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
DEBUGASSERT(priv != NULL);
priv->cb = callback;
+#else
+ UNUSED(dev);
+ UNUSED(callback);
+#endif
+
return OK;
}
/****************************************************************************
- * Name: adc_reset
- *
- * Description:
- * Reset the ADC device. Called early to initialize the hardware. This
- * is called, before adc_setup() and on error conditions.
- *
- * Input Parameters:
- *
- * Returned Value:
- *
+ * Name: adc_watchdog_cfg
****************************************************************************/
-static void adc_reset(struct adc_dev_s *dev)
+static void adc_watchdog_cfg(struct stm32_dev_s *priv)
{
- struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
- irqstate_t flags;
- uint32_t clrbits;
- uint32_t setbits;
-#ifdef ADC_HAVE_TIMER
- int ret;
-#endif
-
- ainfo("intf: %d\n", priv->intf);
- flags = enter_critical_section();
-
- /* Enable ADC reset state */
-
- adc_rccreset(priv, true);
-
- /* Release ADC from reset state */
-
- adc_rccreset(priv, false);
+ uint32_t clrbits = 0;
+ uint32_t setbits = 0;
/* Initialize the watchdog high threshold register */
@@ -1265,34 +1504,27 @@ static void adc_reset(struct adc_dev_s *dev)
adc_putreg(priv, STM32_ADC_LTR_OFFSET, 0x00000000);
- /* Initialize the same sample time for each ADC.
- * During sample cycles channel selection bits must remain unchanged.
- */
-
- adc_putreg(priv, STM32_ADC_SMPR1_OFFSET, ADC_SMPR1_DEFAULT);
- adc_putreg(priv, STM32_ADC_SMPR2_OFFSET, ADC_SMPR2_DEFAULT);
+ clrbits = ADC_CR1_AWDCH_MASK;
+ setbits = ADC_CR1_AWDEN | (priv->r_chanlist[0] << ADC_CR1_AWDCH_SHIFT);
- /* Enable the analog watchdog */
+ /* Modify CR1 configuration */
- clrbits = ADC_CR1_AWDCH_MASK;
- setbits = ADC_CR1_AWDEN | (priv->chanlist[0] << ADC_CR1_AWDCH_SHIFT);
+ adc_modifyreg(priv, STM32_ADC_CR1_OFFSET, clrbits, setbits);
+}
- /* Set the resolution of the conversion */
+/****************************************************************************
+ * Name: adc_mode_cfg
+ ****************************************************************************/
- clrbits |= ADC_CR1_RES_MASK;
- setbits |= ADC_CR1_RES_12BIT;
+static void adc_mode_cfg(struct stm32_dev_s *priv)
+{
+ uint32_t clrbits = 0;
+ uint32_t setbits = 0;
-#ifdef ADC_HAVE_DMA
- if (priv->hasdma)
+ if (priv->scan)
{
setbits |= ADC_CR1_SCAN;
}
-#endif
-
- /* Enable interrupt flags, but disable overrun interrupt */
-
- clrbits |= ADC_IER_OVR;
- setbits |= ADC_IER_ALLINTS & ~ADC_IER_OVR;
/* Set CR1 configuration */
@@ -1308,25 +1540,42 @@ static void adc_reset(struct adc_dev_s *dev)
clrbits |= ADC_EXTREG_EXTEN_MASK;
setbits |= ADC_EXTREG_EXTEN_NONE;
-#ifdef ADC_HAVE_DMA
- if (priv->hasdma)
- {
- setbits |= ADC_CR2_DMA;
- }
-#endif
-
/* Set CR2 configuration */
adc_modifyreg(priv, STM32_ADC_CR2_OFFSET, clrbits, setbits);
+}
- /* Configuration of the channel conversions */
+/****************************************************************************
+ * Name: adc_sampletime_cfg
+ ****************************************************************************/
+
+static void adc_sampletime_cfg(struct adc_dev_s *dev)
+{
+ /* Initialize the same sample time for each ADC.
+ * During sample cycles channel selection bits must remain unchanged.
+ */
+
+#ifdef CONFIG_STM32F7_ADC_CHANGE_SAMPLETIME
+ adc_sampletime_write((struct stm32_adc_dev_s *)dev->ad_priv);
+#else
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
+
+ adc_putreg(priv, STM32_ADC_SMPR1_OFFSET, ADC_SMPR1_DEFAULT);
+ adc_putreg(priv, STM32_ADC_SMPR2_OFFSET, ADC_SMPR2_DEFAULT);
+#endif
+}
- adc_set_ch(dev, 0);
+/****************************************************************************
+ * Name: adc_common_cfg
+ ****************************************************************************/
- /* ADC CCR configuration */
+static void adc_common_cfg(struct stm32_dev_s *priv)
+{
+ uint32_t clrbits = 0;
+ uint32_t setbits = 0;
- clrbits = ADC_CCR_ADCPRE_MASK | ADC_CCR_TSVREFE;
- setbits = ADC_CCR_ADCPRE_DIV;
+ clrbits = ADC_CCR_ADCPRE_MASK;
+ setbits = ADC_CCR_ADCPRE_DIV;
if (adc_internal(priv))
{
@@ -1337,73 +1586,209 @@ static void adc_reset(struct adc_dev_s *dev)
ADC_CCR_DMA_MASK | ADC_CCR_VBATE;
setbits |= ADC_CCR_MULTI_NONE | ADC_CCR_DMA_DISABLED;
- stm32_modifyreg32(STM32_ADC_CCR, clrbits, setbits);
+ adccmn_modifyreg(priv, STM32_ADC_CCR_OFFSET, clrbits, setbits);
+}
#ifdef ADC_HAVE_DMA
+/****************************************************************************
+ * Name: adc_dma_cfg
+ ****************************************************************************/
- /* Enable DMA */
+static void adc_dma_cfg(struct stm32_dev_s *priv)
+{
+ uint32_t clrbits = 0;
+ uint32_t setbits = 0;
- if (priv->hasdma)
+ /* Set DMA mode */
+
+ if (priv->dmacfg == 0)
{
- /* Stop and free DMA if it was started before */
+ /* One Shot Mode */
- if (priv->dma != NULL)
- {
- stm32_dmastop(priv->dma);
- stm32_dmafree(priv->dma);
- }
+ clrbits |= ADC_CR2_DDS;
+ }
+ else
+ {
+ /* Circular Mode */
- priv->dma = stm32_dmachannel(priv->dmachan);
+ setbits |= ADC_CR2_DDS;
+ }
+
+ /* Enable DMA */
- stm32_dmasetup(priv->dma,
- priv->base + STM32_ADC_DR_OFFSET,
- (uint32_t)priv->dmabuffer,
- priv->nchannels,
- ADC_DMA_CONTROL_WORD);
+ setbits |= ADC_CR2_DMA;
- stm32_dmastart(priv->dma, adc_dmaconvcallback, dev, false);
+ /* Modify CR2 configuration */
+
+ adc_modifyreg(priv, STM32_ADC_CR2_OFFSET, clrbits, setbits);
+}
+
+/****************************************************************************
+ * Name: adc_dma_start
+ ****************************************************************************/
+
+static void adc_dma_start(struct adc_dev_s *dev)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
+
+ /* Stop and free DMA if it was started before */
+
+ if (priv->dma != NULL)
+ {
+ stm32_dmastop(priv->dma);
+ stm32_dmafree(priv->dma);
}
+ priv->dma = stm32_dmachannel(priv->dmachan);
+
+#ifndef CONFIG_STM32F7_ADC_NOIRQ
+ /* Start DMA only if standard ADC interrupts used */
+
+ stm32_dmasetup(priv->dma,
+ priv->base + STM32_ADC_DR_OFFSET,
+ (uint32_t)priv->r_dmabuffer,
+ priv->rnchannels,
+ ADC_DMA_CONTROL_WORD);
+
+ stm32_dmastart(priv->dma, adc_dmaconvcallback, dev, false);
#endif
+}
+#endif /* ADC_HAVE_DMA */
- /* Set ADON to wake up the ADC from the power down state */
+/****************************************************************************
+ * Name: adc_configure
+ ****************************************************************************/
- adc_enable(priv, true);
+static void adc_configure(struct adc_dev_s *dev)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
-#ifdef ADC_HAVE_TIMER
- if (priv->tbase != 0)
+ /* Turn off the ADC before configuration */
+
+ adc_enable(priv, false);
+
+ /* Initialize the ADC watchdog */
+
+ adc_watchdog_cfg(priv);
+
+ /* Initialize the ADC sample time */
+
+ adc_sampletime_cfg(dev);
+
+ /* Set ADC working mode */
+
+ adc_mode_cfg(priv);
+
+ /* Configuration of the channel conversions */
+
+ if (priv->cr_channels > 0)
{
- ret = adc_timinit(priv);
- if (ret < 0)
- {
- aerr("ERROR: adc_timinit failed: %d\n", ret);
- }
+ adc_set_ch(dev, 0);
+ }
+
+#ifdef ADC_HAVE_INJECTED
+ /* Configuration of the injected channel conversions after adc enabled */
+
+ if (priv->cj_channels > 0)
+ {
+ adc_inj_set_ch(dev, 0);
}
-#ifndef CONFIG_STM32F7_ADC_NO_STARTUP_CONV
- else
-#endif
#endif
-#ifndef CONFIG_STM32F7_ADC_NO_STARTUP_CONV
+
+ /* ADC common register configuration */
+
+ adc_common_cfg(priv);
+
+#ifdef ADC_HAVE_DMA
+ /* Configure ADC DMA if enabled */
+
+ if (priv->hasdma)
{
- adc_startconv(priv, true);
+ /* Configure ADC DMA */
+
+ adc_dma_cfg(priv);
+
+ /* Start ADC DMA */
+
+ adc_dma_start(dev);
}
#endif
- leave_critical_section(flags);
+ /* Configure ADC resolution */
- ainfo("SR: 0x%08" PRIx32 " CR1: 0x%08" PRIx32
- " CR2: 0x%08" PRIx32 "\n",
- adc_getreg(priv, STM32_ADC_SR_OFFSET),
- adc_getreg(priv, STM32_ADC_CR1_OFFSET),
- adc_getreg(priv, STM32_ADC_CR2_OFFSET));
+ adc_resolution_set(dev, priv->resolution);
- ainfo("SQR1: 0x%08" PRIx32 " SQR2: 0x%08" PRIx32
- " SQR3: 0x%08" PRIx32 "\n",
- adc_getreg(priv, STM32_ADC_SQR1_OFFSET),
- adc_getreg(priv, STM32_ADC_SQR2_OFFSET),
- adc_getreg(priv, STM32_ADC_SQR3_OFFSET));
+#ifdef ADC_HAVE_EXTCFG
+ /* Configure external event for regular group */
+
+ adc_extcfg_set(priv, priv->extcfg);
+#endif
+
+ /* Enable ADC */
- ainfo("CCR: 0x%08" PRIx32 "\n", getreg32(STM32_ADC_CCR));
+ adc_enable(priv, true);
+
+#ifdef ADC_HAVE_JEXTCFG
+ /* Configure external event for injected group when ADC enabled */
+
+ adc_jextcfg_set(priv, priv->jextcfg);
+#endif
+
+ /* Dump regs */
+
+ adc_dumpregs(priv);
+}
+
+/****************************************************************************
+ * Name: adc_reset
+ *
+ * Description:
+ * Reset the ADC device. Called early to initialize the hardware.
+ * This is called, before adc_setup() and on error conditions.
+ *
+ * Input Parameters:
+ *
+ * Returned Value:
+ *
+ ****************************************************************************/
+
+static void adc_reset(struct adc_dev_s *dev)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
+ irqstate_t flags;
+
+ ainfo("intf: %d\n", priv->intf);
+ flags = enter_critical_section();
+
+ /* Do nothing if ADC instance is currently in use */
+
+ if (priv->initialized > 0)
+ {
+ goto out;
+ }
+
+ /* Only if this is the first initialzied ADC instance in the ADC block */
+
+ if (adccmn_lock(priv, true) < 0)
+ {
+ goto out;
+ }
+
+ if (priv->cmn->refcount == 0)
+ {
+ /* Enable ADC reset state */
+
+ adc_rccreset(priv, true);
+
+ /* Release ADC from reset state */
+
+ adc_rccreset(priv, false);
+ }
+
+ adccmn_lock(priv, false);
+
+out:
+ leave_critical_section(flags);
}
/****************************************************************************
@@ -1424,25 +1809,85 @@ static void adc_reset(struct adc_dev_s *dev)
static int adc_setup(struct adc_dev_s *dev)
{
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
- int ret;
+ int ret = OK;
+
+ /* Do nothing when the ADC device is already set up */
+
+ if (priv->initialized > 0)
+ {
+ return OK;
+ }
/* Attach the ADC interrupt */
+#ifndef CONFIG_STM32F7_ADC_NOIRQ
ret = irq_attach(priv->irq, priv->isr, NULL);
if (ret < 0)
{
ainfo("irq_attach failed: %d\n", ret);
return ret;
}
+#endif
/* Make sure that the ADC device is in the powered up, reset state */
adc_reset(dev);
- /* Enable the ADC interrupt */
+ /* Configure ADC device */
- ainfo("Enable the ADC interrupt: irq=%d\n", priv->irq);
- up_enable_irq(priv->irq);
+ adc_configure(dev);
+
+#ifdef ADC_HAVE_TIMER
+ /* Configure timer */
+
+ if (priv->tbase != 0)
+ {
+ ret = adc_timinit(priv);
+ if (ret < 0)
+ {
+ aerr("ERROR: adc_timinit failed: %d\n", ret);
+ }
+ }
+#endif
+
+ /* As default conversion is started here */
+
+#ifndef CONFIG_STM32F7_ADC_NO_STARTUP_CONV
+ /* Start regular conversion */
+
+ adc_reg_startconv(priv, true);
+
+# ifdef ADC_HAVE_INJECTED
+ /* Start injected conversion */
+
+ adc_inj_startconv(priv, true);
+# endif
+#endif
+
+ /* Increase instances counter */
+
+ ret = adccmn_lock(priv, true);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (priv->cmn->refcount == 0)
+ {
+ /* Enable the ADC interrupt */
+
+#ifndef CONFIG_STM32F7_ADC_NOIRQ
+ ainfo("Enable the ADC interrupt: irq=%d\n", priv->irq);
+ up_enable_irq(priv->irq);
+#endif
+ }
+
+ priv->cmn->refcount += 1;
+ adccmn_lock(priv, false);
+
+ /* The ADC device is ready */
+
+ priv->initialized += 1;
return ret;
}
@@ -1464,16 +1909,67 @@ static void adc_shutdown(struct adc_dev_s *dev)
{
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
+ /* Decrement count only when ADC device is in use */
+
+ if (priv->initialized > 0)
+ {
+ priv->initialized -= 1;
+ }
+
+ /* Shutdown the ADC device only when not in use */
+
+ if (priv->initialized > 0)
+ {
+ return;
+ }
+
+ /* Disable ADC */
+
adc_enable(priv, false);
- /* Disable ADC interrupts and detach the ADC interrupt handler */
+ if (adccmn_lock(priv, true) < 0)
+ {
+ return;
+ }
- up_disable_irq(priv->irq);
- irq_detach(priv->irq);
+ if (priv->cmn->refcount <= 1)
+ {
+#ifndef CONFIG_STM32F7_ADC_NOIRQ
+ /* Disable ADC interrupts and detach the ADC interrupt handler */
- /* Disable and reset the ADC module */
+ up_disable_irq(priv->irq);
+ irq_detach(priv->irq);
+#endif
- adc_rccreset(priv, true);
+ /* Disable and reset the ADC module.
+ *
+ * NOTE: The ADC block will be reset to its reset state only if all
+ * ADC block instances are closed. This means that the closed
+ * ADC may not be reset which in turn may affect low-power
+ * applications. (But ADC is turned off here, is not that
+ * enough?)
+ */
+
+ adc_rccreset(priv, true);
+ }
+
+#ifdef ADC_HAVE_TIMER
+ /* Disable timer */
+
+ if (priv->tbase != 0)
+ {
+ adc_timstart(priv, false);
+ }
+#endif
+
+ /* Decrease instances counter */
+
+ if (priv->cmn->refcount > 0)
+ {
+ priv->cmn->refcount -= 1;
+ }
+
+ adccmn_lock(priv, false);
}
/****************************************************************************
@@ -1491,14 +1987,25 @@ static void adc_shutdown(struct adc_dev_s *dev)
static void adc_rxint(struct adc_dev_s *dev, bool enable)
{
struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
+ uint32_t regval;
ainfo("intf: %d enable: %d\n", priv->intf, enable ? 1 : 0);
if (enable)
{
- /* Enable the end-of-conversion ADC and analog watchdog interrupts */
+ /* Enable the analog watchdog / overrun interrupts, and if no DMA,
+ * end-of-conversion ADC.
+ */
- adc_modifyreg(priv, STM32_ADC_IER_OFFSET, 0, ADC_IER_ALLINTS);
+ regval = ADC_IER_ALLINTS;
+#ifdef ADC_HAVE_DMA
+ if (priv->hasdma)
+ {
+ regval &= ~(ADC_IER_EOC | ADC_IER_JEOC);
+ }
+#endif
+
+ adc_modifyreg(priv, STM32_ADC_IER_OFFSET, 0, regval);
}
else
{
@@ -1508,22 +2015,161 @@ static void adc_rxint(struct adc_dev_s *dev, bool
enable)
}
}
+/****************************************************************************
+ * Name: adc_resolution_set
+ ****************************************************************************/
+
+static int adc_resolution_set(struct adc_dev_s *dev, uint8_t res)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
+ int ret = OK;
+
+ /* Check input */
+
+ if (res > 3)
+ {
+ ret = -EINVAL;
+ goto errout;
+ }
+
+ /* Modify appropriate register */
+
+ adc_modifyreg(priv, STM32_ADC_CR1_OFFSET, ADC_CR1_RES_MASK,
+ res << ADC_CR1_RES_SHIFT);
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: adc_extcfg_set
+ ****************************************************************************/
+
+#ifdef ADC_HAVE_EXTCFG
+static int adc_extcfg_set(struct stm32_dev_s *priv, uint32_t extcfg)
+{
+ uint32_t exten = 0;
+ uint32_t extsel = 0;
+ uint32_t setbits = 0;
+ uint32_t clrbits = 0;
+
+ /* Get EXTEN and EXTSEL from input */
+
+ exten = extcfg & ADC_EXTREG_EXTEN_MASK;
+ extsel = extcfg & ADC_EXTREG_EXTSEL_MASK;
+
+ /* EXTSEL selection: These bits select the external event used
+ * to trigger the start of conversion of a regular group. NOTE:
+ *
+ * - The position with of the EXTSEL field varies from one STM32 MCU
+ * to another.
+ * - The width of the EXTSEL field varies from one STM32 MCU to another.
+ */
+
+ if (exten > 0)
+ {
+ setbits = extsel | exten;
+ clrbits = ADC_EXTREG_EXTEN_MASK | ADC_EXTREG_EXTSEL_MASK;
+
+ ainfo("Initializing extsel = 0x%08" PRIx32 "\n", extsel);
+
+ /* Write register */
+
+ adc_modifyreg(priv, STM32_ADC_EXTREG_OFFSET, clrbits, setbits);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: adc_jextcfg_set
+ ****************************************************************************/
+
+#ifdef ADC_HAVE_JEXTCFG
+static int adc_jextcfg_set(struct stm32_dev_s *priv, uint32_t jextcfg)
+{
+ uint32_t jexten = 0;
+ uint32_t jextsel = 0;
+ uint32_t setbits = 0;
+ uint32_t clrbits = 0;
+
+ /* Get JEXTEN and JEXTSEL from input */
+
+ jexten = jextcfg & ADC_JEXTREG_JEXTEN_MASK;
+ jextsel = jextcfg & ADC_JEXTREG_JEXTSEL_MASK;
+
+ /* JEXTSEL selection: These bits select the external event used
+ * to trigger the start of conversion of a injected group. NOTE:
+ *
+ * - The position with of the JEXTSEL field varies from one STM32 MCU
+ * to another.
+ * - The width of the JEXTSEL field varies from one STM32 MCU to another.
+ */
+
+ if (jexten > 0)
+ {
+ setbits = jexten | jextsel;
+ clrbits = ADC_JEXTREG_JEXTEN_MASK | ADC_JEXTREG_JEXTSEL_MASK;
+
+ ainfo("Initializing jextsel = 0x%08" PRIx32 "\n", jextsel);
+
+ /* Write register */
+
+ adc_modifyreg(priv, STM32_ADC_JEXTREG_OFFSET, clrbits, setbits);
+ }
+
+ return OK;
+}
+#endif
+
+/****************************************************************************
+ * Name: adc_dumpregs
+ ****************************************************************************/
+
+static void adc_dumpregs(struct stm32_dev_s *priv)
+{
+ UNUSED(priv);
+
+ ainfo("SR: 0x%08" PRIx32 " CR1: 0x%08" PRIx32
+ " CR2: 0x%08" PRIx32 "\n",
+ adc_getreg(priv, STM32_ADC_SR_OFFSET),
+ adc_getreg(priv, STM32_ADC_CR1_OFFSET),
+ adc_getreg(priv, STM32_ADC_CR2_OFFSET));
+
+ ainfo("SQR1: 0x%08" PRIx32 " SQR2: 0x%08" PRIx32
+ " SQR3: 0x%08" PRIx32 "\n",
+ adc_getreg(priv, STM32_ADC_SQR1_OFFSET),
+ adc_getreg(priv, STM32_ADC_SQR2_OFFSET),
+ adc_getreg(priv, STM32_ADC_SQR3_OFFSET));
+
+ ainfo("SMPR1: 0x%08" PRIx32 " SMPR2: 0x%08" PRIx32 "\n",
+ adc_getreg(priv, STM32_ADC_SMPR1_OFFSET),
+ adc_getreg(priv, STM32_ADC_SMPR2_OFFSET));
+
+#ifdef ADC_HAVE_INJECTED
+ ainfo("JSQR: 0x%08" PRIx32 "\n", adc_getreg(priv, STM32_ADC_JSQR_OFFSET));
+#endif
+
+ ainfo("CCR: 0x%08" PRIx32 "\n",
+ adccmn_getreg(priv, STM32_ADC_CCR_OFFSET));
+}
+
/****************************************************************************
* Name: adc_sqrbits
****************************************************************************/
static uint32_t adc_sqrbits(struct stm32_dev_s *priv, int first,
- int last,
- int offset)
+ int last, int offset)
{
uint32_t bits = 0;
int i;
for (i = first - 1;
- i < priv->nchannels && i < last;
+ i < priv->rnchannels && i < last;
i++, offset += ADC_SQ_OFFSET)
{
- bits |= (uint32_t)priv->chanlist[i] << offset;
+ bits |= (uint32_t)priv->r_chanlist[i] << offset;
}
return bits;
@@ -1539,13 +2185,23 @@ static bool adc_internal(struct stm32_dev_s * priv)
if (priv->intf == 1)
{
- for (i = 0; i < priv->nchannels; i++)
+ for (i = 0; i < priv->cr_channels; i++)
{
- if (priv->chanlist[i] > ADC_LAST_EXTERNAL_CHAN)
+ if (priv->r_chanlist[i] > ADC_LAST_EXTERNAL_CHAN)
{
return true;
}
}
+
+#ifdef ADC_HAVE_INJECTED
+ for (i = 0; i < priv->cj_channels; i++)
+ {
+ if (priv->j_chanlist[i] > ADC_LAST_EXTERNAL_CHAN)
+ {
+ return true;
+ }
+ }
+#endif
}
return false;
@@ -1575,19 +2231,20 @@ static int adc_set_ch(struct adc_dev_s *dev, uint8_t ch)
if (ch == 0)
{
priv->current = 0;
- priv->nchannels = priv->cchannels;
+ priv->rnchannels = priv->cr_channels;
}
else
{
- for (i = 0; i < priv->cchannels && priv->chanlist[i] != ch - 1; i++);
+ for (i = 0; i < priv->cr_channels && priv->r_chanlist[i] != ch - 1;
+ i++);
- if (i >= priv->cchannels)
+ if (i >= priv->cr_channels)
{
return -ENODEV;
}
- priv->current = i;
- priv->nchannels = 1;
+ priv->current = i;
+ priv->rnchannels = 1;
}
bits = adc_sqrbits(priv, ADC_SQR3_FIRST, ADC_SQR3_LAST,
@@ -1598,14 +2255,57 @@ static int adc_set_ch(struct adc_dev_s *dev, uint8_t ch)
ADC_SQR2_SQ_OFFSET);
adc_modifyreg(priv, STM32_ADC_SQR2_OFFSET, ~ADC_SQR2_RESERVED, bits);
- bits = ((uint32_t)priv->nchannels - 1) << ADC_SQR1_L_SHIFT |
- adc_sqrbits(priv, ADC_SQR1_FIRST, ADC_SQR1_LAST,
- ADC_SQR1_SQ_OFFSET);
+ bits = ((uint32_t)priv->rnchannels - 1) << ADC_SQR1_L_SHIFT;
+ bits |= adc_sqrbits(priv, ADC_SQR1_FIRST,
+ ADC_SQR1_LAST, ADC_SQR1_SQ_OFFSET);
adc_modifyreg(priv, STM32_ADC_SQR1_OFFSET, ~ADC_SQR1_RESERVED, bits);
return OK;
}
+#ifdef ADC_HAVE_INJECTED
+
+/****************************************************************************
+ * Name: adc_inj_set_ch
+ ****************************************************************************/
+
+static int adc_inj_set_ch(struct adc_dev_s *dev, uint8_t ch)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev->ad_priv;
+ uint32_t clrbits;
+ uint32_t setbits;
+ int i;
+
+ /* Configure injected sequence length */
+
+ setbits = ADC_JSQR_JL(priv->cj_channels);
+ clrbits = ADC_JEXTREG_JEXTSEL_MASK | ADC_JSQR_JL_MASK;
+
+ /* Configure injected channels */
+
+ for (i = 0 ; i < priv->cj_channels; i += 1)
+ {
+ /* Injected channels sequence for for ADC IPv1:
+ *
+ * 1 2 3 4
+ * IL=1: JSQR4,
+ * IL=2: JSQR3, JSQR4
+ * IL=3: JSQR2, JSQR3, JSQR4
+ * IL=4: JSQR1, JSQR2, JSQR3, JSQR4
+ */
+
+ setbits |= (priv->j_chanlist[priv->cj_channels - 1 - i] <<
+ (ADC_JSQR_JSQ4_SHIFT - ADC_JSQR_JSQ_SHIFT * i));
+ }
+
+ /* Write register */
+
+ adc_modifyreg(priv, STM32_ADC_JSQR_OFFSET, clrbits, setbits);
+
+ return OK;
+}
+#endif
+
/****************************************************************************
* Name: adc_ioctl
*
@@ -1630,29 +2330,114 @@ static int adc_ioctl(struct adc_dev_s *dev, int cmd,
unsigned long arg)
{
case ANIOC_TRIGGER:
{
- adc_startconv(priv, true);
+ /* Start regular conversion if regular channels configured */
+
+ if (priv->anioc_trg & ANIOC_TRIGGER_REGULAR)
+ {
+ if (priv->cr_channels > 0)
+ {
+ adc_reg_startconv(priv, true);
+ }
+ }
+
+#ifdef ADC_HAVE_INJECTED
+ /* Start injected conversion if injected channels configured */
+
+ if (priv->anioc_trg & ANIOC_TRIGGER_INJECTED)
+ {
+ if (priv->cj_channels > 0)
+ {
+ adc_inj_startconv(priv, true);
+ }
+ }
+#endif
+
+ break;
}
- break;
case ANIOC_GET_NCHANNELS:
{
/* Return the number of configured channels */
- ret = priv->cchannels;
+ ret = priv->rnchannels;
+ }
+ break;
+
+ case IO_TRIGGER_REG:
+ {
+ /* Start regular conversion if regular channels configured */
+
+ if (priv->cr_channels > 0)
+ {
+ adc_reg_startconv(priv, true);
+ }
+
+ break;
+ }
+
+#ifdef ADC_HAVE_INJECTED
+ case IO_TRIGGER_INJ:
+ {
+ /* Start injected conversion if injected channels configured */
+
+ if (priv->cj_channels > 0)
+ {
+ adc_inj_startconv(priv, true);
+ }
+
+ break;
+ }
+#endif
+
+ case IO_STOP_ADC:
+ {
+ adc_enable(priv, false);
+ break;
+ }
+
+ case IO_START_ADC:
+ {
+ adc_enable(priv, true);
+ break;
+ }
+
+ case IO_START_CONV:
+ {
+ uint8_t ch = ((uint8_t)arg);
+
+ ret = adc_set_ch(dev, ch);
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+#ifdef CONFIG_ADC
+ if (ch)
+ {
+ /* Clear fifo if upper-half driver enabled */
+
+ dev->ad_recv.af_head = 0;
+ dev->ad_recv.af_tail = 0;
+ }
+#endif
+
+ adc_reg_startconv(priv, true);
+ break;
}
- break;
default:
{
aerr("ERROR: Unknown cmd: %d\n", cmd);
ret = -ENOTTY;
+ break;
}
- break;
}
return ret;
}
+#ifndef CONFIG_STM32F7_ADC_NOIRQ
+
/****************************************************************************
* Name: adc_interrupt
*
@@ -1714,7 +2499,7 @@ static int adc_interrupt(struct adc_dev_s *dev)
*/
DEBUGASSERT(priv->cb->au_receive != NULL);
- priv->cb->au_receive(dev, priv->chanlist[priv->current], data);
+ priv->cb->au_receive(dev, priv->r_chanlist[priv->current], data);
}
/* Set the channel number of the next channel that will complete
@@ -1723,7 +2508,7 @@ static int adc_interrupt(struct adc_dev_s *dev)
priv->current++;
- if (priv->current >= priv->nchannels)
+ if (priv->current >= priv->rnchannels)
{
/* Restart the conversion sequence from the beginning */
@@ -1731,8 +2516,10 @@ static int adc_interrupt(struct adc_dev_s *dev)
}
}
- regval &= ~pending;
- adc_putreg(priv, STM32_ADC_ISR_OFFSET, regval);
+ /* Clear pending interrupts */
+
+ adc_putreg(priv, STM32_ADC_ISR_OFFSET, pending);
+
return OK;
}
@@ -1764,6 +2551,347 @@ static int adc123_interrupt(int irq, void *context,
void *arg)
return OK;
}
+#endif /* CONFIG_STM32F7_ADC_NOIRQ */
+
+#ifdef CONFIG_STM32F7_ADC_LL_OPS
+
+/****************************************************************************
+ * Name: adc_llops_setup
+ ****************************************************************************/
+
+static int adc_llops_setup(struct stm32_adc_dev_s *dev)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ return adc_setup(priv->dev);
+}
+
+/****************************************************************************
+ * Name: adc_llops_shutdown
+ ****************************************************************************/
+
+static void adc_llops_shutdown(struct stm32_adc_dev_s *dev)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ adc_shutdown(priv->dev);
+}
+
+/****************************************************************************
+ * Name: adc_intack
+ ****************************************************************************/
+
+static void adc_intack(struct stm32_adc_dev_s *dev, uint32_t source)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ /* Cleared by writing 0 to it */
+
+ adc_modifyreg(priv, STM32_ADC_ISR_OFFSET, (source & ADC_ISR_ALLINTS), 0);
+}
+
+/****************************************************************************
+ * Name: adc_inten
+ ****************************************************************************/
+
+static void adc_inten(struct stm32_adc_dev_s *dev, uint32_t source)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ /* Enable interrupts */
+
+ adc_modifyreg(priv, STM32_ADC_IER_OFFSET, 0, (source & ADC_IER_ALLINTS));
+}
+
+/****************************************************************************
+ * Name: adc_intdis
+ ****************************************************************************/
+
+static void adc_intdis(struct stm32_adc_dev_s *dev, uint32_t source)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ /* Disable interrupts */
+
+ adc_modifyreg(priv, STM32_ADC_IER_OFFSET, (source & ADC_IER_ALLINTS), 0);
+}
+
+/****************************************************************************
+ * Name: adc_ackget
+ ****************************************************************************/
+
+static uint32_t adc_intget(struct stm32_adc_dev_s *dev)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+ uint32_t regval;
+ uint32_t pending;
+
+ regval = adc_getreg(priv, STM32_ADC_ISR_OFFSET);
+ pending = regval & ADC_ISR_ALLINTS;
+
+ return pending;
+}
+
+/****************************************************************************
+ * Name: adc_regget
+ ****************************************************************************/
+
+static uint32_t adc_regget(struct stm32_adc_dev_s *dev)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ return adc_getreg(priv, STM32_ADC_DR_OFFSET) & ADC_DR_RDATA_MASK;
+}
+
+/****************************************************************************
+ * Name: adc_llops_reg_startconv
+ ****************************************************************************/
+
+static void adc_llops_reg_startconv(struct stm32_adc_dev_s *dev,
+ bool enable)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ adc_reg_startconv(priv, enable);
+}
+
+/****************************************************************************
+ * Name: adc_offset_set
+ ****************************************************************************/
+
+static int adc_offset_set(struct stm32_adc_dev_s *dev, uint8_t ch,
+ uint8_t i, uint16_t offset)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+ uint32_t reg = 0;
+ int ret = OK;
+
+ /* WARNING: Offset only for injected channels! */
+
+ UNUSED(ch);
+
+ if (i >= 4)
+ {
+ /* There are only four offset registers. */
+
+ ret = -E2BIG;
+ goto errout;
+ }
+
+ reg = STM32_ADC_JOFR1_OFFSET + i * 4;
+
+ adc_putreg(priv, reg, offset);
+
+errout:
+ return ret;
+}
+
+/****************************************************************************
+ * Name: adc_llops_extcfg_set
+ ****************************************************************************/
+
+#ifdef ADC_HAVE_EXTCFG
+static void adc_llops_extcfg_set(struct stm32_adc_dev_s *dev,
+ uint32_t extcfg)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ adc_extcfg_set(priv, extcfg);
+}
+#endif
+
+/****************************************************************************
+ * Name: adc_llops_jextcfg_set
+ ****************************************************************************/
+
+#ifdef ADC_HAVE_JEXTCFG
+static void adc_llops_jextcfg_set(struct stm32_adc_dev_s *dev,
+ uint32_t jextcfg)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ adc_jextcfg_set(priv, jextcfg);
+}
+#endif
+
+/****************************************************************************
+ * Name: adc_regbufregister
+ ****************************************************************************/
+
+#ifdef ADC_HAVE_DMA
+static int adc_regbufregister(struct stm32_adc_dev_s *dev,
+ uint16_t *buffer, uint8_t len)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ stm32_dmasetup(priv->dma,
+ priv->base + STM32_ADC_DR_OFFSET,
+ (uint32_t)buffer,
+ len,
+ ADC_DMA_CONTROL_WORD);
+
+ /* No DMA callback */
+
+ stm32_dmastart(priv->dma, NULL, dev, false);
+
+ return OK;
+}
+#endif /* ADC_HAVE_DMA */
+
+/****************************************************************************
+ * Name: adc_injget
+ ****************************************************************************/
+
+#ifdef ADC_HAVE_INJECTED
+static uint32_t adc_injget(struct stm32_adc_dev_s *dev, uint8_t chan)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+ uint32_t regval = 0;
+
+ if (chan > (priv->cj_channels - 1))
+ {
+ /* REVISIT: return valute with MSB set to indicate error ? */
+
+ goto errout;
+ }
+
+ regval = adc_getreg(priv, STM32_ADC_JDR1_OFFSET + 4 * (chan)) &
+ ADC_JDR_JDATA_MASK;
+
+errout:
+ return regval;
+}
+
+/****************************************************************************
+ * Name: adc_llops_inj_startconv
+ ****************************************************************************/
+
+static void adc_llops_inj_startconv(struct stm32_adc_dev_s *dev,
+ bool enable)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ adc_inj_startconv(priv, enable);
+}
+
+#endif /* ADC_HAVE_INJECTED */
+
+/****************************************************************************
+ * Name: adc_sampletime_write
+ *
+ * Description:
+ * Writes previously defined values into ADC_SMPRx registers.
+ *
+ * Input Parameters:
+ *
+ * Returned Value:
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_STM32F7_ADC_CHANGE_SAMPLETIME
+static void adc_sampletime_write(struct stm32_adc_dev_s *dev)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+ uint32_t value = 0;
+ uint8_t i;
+ uint8_t shift;
+
+ /* Sampling time individually for each channel */
+
+ for (i = 0, shift = 0; i < priv->adc_channels; i++)
+ {
+ value |= priv->sample_rate[i] << (shift * 3);
+ switch (i)
+ {
+ case (ADC_CHANNELS_NUMBER - 1):
+ {
+ adc_putreg(priv, STM32_ADC_SMPR2_OFFSET, value);
+ shift = 0;
+ value = 0;
+ break;
+ }
+
+ case 9:
+ {
+ adc_putreg(priv, STM32_ADC_SMPR1_OFFSET, value);
+ shift = 0;
+ value = 0;
+ break;
+ }
+
+ default:
+ {
+ shift++;
+ break;
+ }
+ }
+ }
+}
+
+/****************************************************************************
+ * Name: adc_sampletime_set
+ *
+ * Description:
+ * Changes sample times for specified channels. This method
+ * doesn't make any register writing. So, it's only stores the information.
+ * Values provided by user will be written in registers only on the next
+ * ADC peripheral start, as it was told to do in manual. However, before
+ * very first start, user can call this method and override default values
+ * either for every channels or for only some predefined by user channel(s)
+ *
+ * Input Parameters:
+ * dev - pointer to the adc device structure
+ * time_samples - pointe to the adc sample time configuration data
+ *
+ * Returned Value:
+ * None
+ *
+ ****************************************************************************/
+
+void adc_sampletime_set(struct stm32_adc_dev_s *dev,
+ struct adc_sample_time_s *time_samples)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+ uint8_t ch_index;
+ uint8_t i;
+
+ /* Check if user wants to assign the same value for all channels
+ * or just wants to change sample time values for certain channels
+ */
+
+ if (time_samples->all_same)
+ {
+ memset(priv->sample_rate, time_samples->all_ch_sample_time,
+ ADC_CHANNELS_NUMBER);
+ }
+ else
+ {
+ for (i = 0; i < time_samples->channels_nbr; i++)
+ {
+ ch_index = time_samples->channel[i].channel;
+ if (ch_index >= ADC_CHANNELS_NUMBER)
+ {
+ break;
+ }
+
+ priv->sample_rate[ch_index] = time_samples->channel[i].sample_time;
+ }
+ }
+}
+#endif /* CONFIG_STM32F7_ADC_CHANGE_SAMPLETIME */
+
+/****************************************************************************
+ * Name: adc_llops_dumpregs
+ ****************************************************************************/
+
+static void adc_llops_dumpregs(struct stm32_adc_dev_s *dev)
+{
+ struct stm32_dev_s *priv = (struct stm32_dev_s *)dev;
+
+ adc_dumpregs(priv);
+}
+
+#endif /* CONFIG_STM32F7_ADC_LL_OPS */
/****************************************************************************
* Public Functions
@@ -1775,79 +2903,182 @@ static int adc123_interrupt(int irq, void *context,
void *arg)
* Description:
* Initialize the ADC.
*
- * The logic is, save nchannels : # of channels (conversions) in ADC_SQR1_L
- * Then, take the chanlist array and store it in the SQR Regs,
- * chanlist[0] -> ADC_SQR3_SQ1
- * chanlist[1] -> ADC_SQR3_SQ2
+ * The logic allow initialize ADC regular and injected channels.
+ *
+ * The number of injected channels for given ADC is selected from Kconfig
+ * with CONFIG_STM32F7_ADCx_INJECTED_CHAN definitions
+ *
+ * The number of regular channels is obtained from the equation:
+ *
+ * cr_channels = channels - cj_channels
+ *
+ * where:
+ * cr_channels - regular channels
+ * cj_channels - injected channels
+ * channels - this function parameter
+ *
+ * The chanlist array store both regular channels and injected channels
+ * configuration so that regular channels are the first in order:
+ *
+ * # regular channels start from here
+ * chanlist[0] -> ADC_SQRx_SQ1
+ * chanlist[1] -> ADC_SQRx_SQ2
* ...
- * chanlist[15]-> ADC_SQR1_SQ16
+ * # injected channels start from here
+ * chanlist[channels - (y - 1)] -> ADC_JSQR_JSQ1
+ * ...
+ * chanlist[channels] -> ADC_JSQR_ISQy
+ *
+ * where:
+ * y = CONFIG_STM32F7_ADCx_INJECTED_CHAN, and y > 0
*
- * up to
- * chanlist[nchannels]
+ * If CONFIG_STM32F7_ADCx_INJECTED_CHAN = 0, then all channels from
+ * chanlist are regular channels.
*
* Input Parameters:
- * intf - Could be {1,2,3} for ADC1, ADC2, or ADC3
- * chanlist - The list of channels
- * cchannels - Number of channels
+ * intf - Could be {1,2,3,4} for ADC1, ADC2, ADC3
+ * chanlist - The list of channels (regular + injected)
+ * channels - Number of channels (regular + injected)
*
* Returned Value:
* Valid ADC device structure reference on success; a NULL on failure
*
****************************************************************************/
-struct adc_dev_s *stm32_adc_initialize(int intf, const uint8_t *chanlist,
- int cchannels)
+struct adc_dev_s *stm32_adc_initialize(int intf,
+ const uint8_t *chanlist,
+ int channels)
{
struct adc_dev_s *dev;
struct stm32_dev_s *priv;
-
- ainfo("intf: %d cchannels: %d\n", intf, cchannels);
+ uint8_t cr_channels = 0;
+ uint8_t cj_channels = 0;
+#ifdef ADC_HAVE_INJECTED
+ uint8_t *j_chanlist = NULL;
+#endif
switch (intf)
{
#ifdef CONFIG_STM32F7_ADC1
case 1:
- ainfo("ADC1 selected\n");
- dev = &g_adcdev1;
- break;
-#endif
+ {
+ ainfo("ADC1 selected\n");
+ dev = &g_adcdev1;
+ cj_channels = CONFIG_STM32F7_ADC1_INJECTED_CHAN;
+ cr_channels = channels - cj_channels;
+# ifdef ADC_HAVE_INJECTED
+ if (cj_channels > 0)
+ {
+ j_chanlist = (uint8_t *)chanlist + cr_channels;
+ }
+# endif
+ break;
+ }
+#endif /* CONFIG_STM32F7_ADC1 */
+
#ifdef CONFIG_STM32F7_ADC2
case 2:
- ainfo("ADC2 selected\n");
- dev = &g_adcdev2;
- break;
-#endif
+ {
+ ainfo("ADC2 selected\n");
+ dev = &g_adcdev2;
+ cj_channels = CONFIG_STM32F7_ADC2_INJECTED_CHAN;
+ cr_channels = channels - cj_channels;
+# ifdef ADC_HAVE_INJECTED
+ if (cj_channels > 0)
+ {
+ j_chanlist = (uint8_t *)chanlist + cr_channels;
+ }
+# endif
+ break;
+ }
+#endif /* CONFIG_STM32F7_ADC2 */
+
#ifdef CONFIG_STM32F7_ADC3
case 3:
- ainfo("ADC3 selected\n");
- dev = &g_adcdev3;
- break;
-#endif
+ {
+ ainfo("ADC3 selected\n");
+ dev = &g_adcdev3;
+ cj_channels = CONFIG_STM32F7_ADC3_INJECTED_CHAN;
+ cr_channels = channels - cj_channels;
+# ifdef ADC_HAVE_INJECTED
+ if (cj_channels > 0)
+ {
+ j_chanlist = (uint8_t *)chanlist + cr_channels;
+ }
+# endif
+ break;
+ }
+
+#endif /* CONFIG_STM32F7_ADC3 */
+
default:
- aerr("ERROR: No ADC interface defined\n");
- return NULL;
+ {
+ aerr("ERROR: No ADC interface defined\n");
+ return NULL;
+ }
}
/* Configure the selected ADC */
- priv = (struct stm32_dev_s *)dev->ad_priv;
- priv->cb = NULL;
+ priv = (struct stm32_dev_s *)dev->ad_priv;
+
+ /* Configure regular channels */
+
+ DEBUGASSERT(cr_channels <= CONFIG_STM32F7_ADC_MAX_SAMPLES);
+ if (cr_channels > CONFIG_STM32F7_ADC_MAX_SAMPLES)
+ {
+ cr_channels = CONFIG_STM32F7_ADC_MAX_SAMPLES;
+ }
+
+ priv->cr_channels = cr_channels;
+ memcpy(priv->r_chanlist, chanlist, cr_channels);
- DEBUGASSERT(cchannels <= ADC_MAX_SAMPLES);
- if (cchannels > ADC_MAX_SAMPLES)
+#ifdef ADC_HAVE_INJECTED
+ /* Configure injected channels */
+
+ DEBUGASSERT(cj_channels <= ADC_INJ_MAX_SAMPLES);
+ if (cj_channels > ADC_INJ_MAX_SAMPLES)
{
- cchannels = ADC_MAX_SAMPLES;
+ cj_channels = ADC_INJ_MAX_SAMPLES;
}
- priv->cchannels = cchannels;
+ priv->cj_channels = cj_channels;
+ memcpy(priv->j_chanlist, j_chanlist, cj_channels);
+#endif
+
+#ifdef CONFIG_STM32F7_ADC_CHANGE_SAMPLETIME
+ /* Assign default values for the sample time table */
+
+ memset(priv->sample_rate, ADC_SMPR_DEFAULT, ADC_CHANNELS_NUMBER);
+ priv->adc_channels = ADC_CHANNELS_NUMBER;
+#endif
+
+#ifdef ADC_HAVE_CB
+ priv->cb = NULL;
+#endif
+
+#ifdef CONFIG_STM32F7_ADC_LL_OPS
+ /* Store reference to the upper-half ADC device */
+
+ priv->dev = dev;
+#endif
+
+#ifdef ADC_HAVE_INJECTED
+ ainfo("intf: %d cr_channels: %d, cj_channels: %d\n",
+ intf, priv->cr_channels, priv->cj_channels);
+#else
+ ainfo("intf: %d cr_channels: %d\n", intf, priv->cr_channels);
+#endif
+
+ /* Initialize the ADC common data semaphore.
+ *
+ * REVISIT: This will be done several times for each initialzied ADC in
+ * the ADC block.
+ */
- memcpy(priv->chanlist, chanlist, cchannels);
+ nxsem_init(&priv->cmn->lock, 0, 1);
return dev;
}
-#endif /* CONFIG_STM32F7_STM32F74XX */
-#endif /* CONFIG_STM32F7_ADC1 || CONFIG_STM32F7_ADC2 ||
- * CONFIG_STM32F7_ADC3
- */
-#endif /* CONFIG_ADC */
+#endif /* CONFIG_STM32F7_ADC */
diff --git a/arch/arm/src/stm32f7/stm32_adc.h b/arch/arm/src/stm32f7/stm32_adc.h
index cdeb74511f..6ebde261ca 100644
--- a/arch/arm/src/stm32f7/stm32_adc.h
+++ b/arch/arm/src/stm32f7/stm32_adc.h
@@ -34,6 +34,25 @@
* Pre-processor Definitions
****************************************************************************/
+/* Generalized definitions for ADC *****************************************/
+
+#define STM32_ADC_DMAREG_OFFSET STM32_ADC_CR2_OFFSET
+#define ADC_DMAREG_DMA ADC_CR2_DMA
+#define STM32_ADC_EXTREG_OFFSET STM32_ADC_CR2_OFFSET
+#define ADC_EXTREG_EXTSEL_MASK ADC_CR2_EXTSEL_MASK
+#define ADC_EXTREG_EXTSEL_SHIFT ADC_CR2_EXTSEL_SHIFT
+#define STM32_ADC_JEXTREG_OFFSET STM32_ADC_CR2_OFFSET
+#define ADC_JEXTREG_JEXTSEL_MASK ADC_CR2_JEXTSEL_MASK
+#define ADC_EXTREG_JEXTSEL_SHIFT ADC_CR2_JEXTSEL_SHIFT
+#define STM32_ADC_ISR_OFFSET STM32_ADC_SR_OFFSET
+#define STM32_ADC_IER_OFFSET STM32_ADC_CR1_OFFSET
+#define ADC_EXTREG_EXTEN_MASK ADC_CR2_EXTEN_MASK
+#define ADC_EXTREG_EXTEN_NONE ADC_CR2_EXTEN_NONE
+#define ADC_EXTREG_EXTEN_DEFAULT ADC_CR2_EXTEN_RISING
+#define ADC_JEXTREG_JEXTEN_MASK ADC_CR2_JEXTEN_MASK
+#define ADC_JEXTREG_JEXTEN_NONE ADC_CR2_JEXTEN_NONE
+#define ADC_JEXTREG_JEXTEN_DEFAULT ADC_CR2_JEXTEN_RISING
+
/* Configuration ************************************************************/
/* Timer devices may be used for different purposes. One special purpose is
@@ -87,18 +106,6 @@
/* Up to 3 ADC interfaces are supported */
-#if STM32F7_NADC < 3
-# undef CONFIG_STM32F7_ADC3
-#endif
-
-#if STM32F7_NADC < 2
-# undef CONFIG_STM32F7_ADC2
-#endif
-
-#if STM32F7_NADC < 1
-# undef CONFIG_STM32F7_ADC1
-#endif
-
#if defined(CONFIG_STM32F7_ADC1) || defined(CONFIG_STM32F7_ADC2) || \
defined(CONFIG_STM32F7_ADC3)
@@ -128,6 +135,14 @@
# undef ADC3_HAVE_DMA
#endif
+/* Injected channels support */
+
+#if (defined(CONFIG_STM32F7_ADC1) && (CONFIG_STM32F7_ADC1_INJECTED_CHAN > 0))
|| \
+ (defined(CONFIG_STM32F7_ADC2) && (CONFIG_STM32F7_ADC2_INJECTED_CHAN > 0))
|| \
+ (defined(CONFIG_STM32F7_ADC3) && (CONFIG_STM32F7_ADC3_INJECTED_CHAN > 0))
+# define ADC_HAVE_INJECTED
+#endif
+
/* Timer configuration: If a timer trigger is specified, then get
* information about the timer.
*/
@@ -175,35 +190,35 @@
#endif
#if defined(CONFIG_STM32F7_TIM1_ADC2)
-# define ADC2_HAVE_TIMER 1
-# define ADC2_TIMER_BASE STM32_TIM1_BASE
-# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB2_TIM1_CLKIN
+# define ADC2_HAVE_TIMER 1
+# define ADC2_TIMER_BASE STM32_TIM1_BASE
+# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB2_TIM1_CLKIN
#elif defined(CONFIG_STM32F7_TIM2_ADC2)
-# define ADC2_HAVE_TIMER 1
-# define ADC2_TIMER_BASE STM32_TIM2_BASE
-# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM2_CLKIN
+# define ADC2_HAVE_TIMER 1
+# define ADC2_TIMER_BASE STM32_TIM2_BASE
+# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM2_CLKIN
#elif defined(CONFIG_STM32F7_TIM3_ADC2)
-# define ADC2_HAVE_TIMER 1
-# define ADC2_TIMER_BASE STM32_TIM3_BASE
-# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM3_CLKIN
+# define ADC2_HAVE_TIMER 1
+# define ADC2_TIMER_BASE STM32_TIM3_BASE
+# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM3_CLKIN
#elif defined(CONFIG_STM32F7_TIM4_ADC2)
-# define ADC2_HAVE_TIMER 1
-# define ADC2_TIMER_BASE STM32_TIM4_BASE
-# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM4_CLKIN
+# define ADC2_HAVE_TIMER 1
+# define ADC2_TIMER_BASE STM32_TIM4_BASE
+# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM4_CLKIN
#elif defined(CONFIG_STM32F7_TIM5_ADC2)
-# define ADC2_HAVE_TIMER 1
-# define ADC2_TIMER_BASE STM32_TIM5_BASE
-# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM5_CLKIN
+# define ADC2_HAVE_TIMER 1
+# define ADC2_TIMER_BASE STM32_TIM5_BASE
+# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM5_CLKIN
#elif defined(CONFIG_STM32F7_TIM6_ADC2)
-# define ADC2_HAVE_TIMER 1
-# define ADC2_TIMER_BASE STM32_TIM6_BASE
-# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM6_CLKIN
+# define ADC2_HAVE_TIMER 1
+# define ADC2_TIMER_BASE STM32_TIM6_BASE
+# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB1_TIM6_CLKIN
#elif defined(CONFIG_STM32F7_TIM8_ADC2)
-# define ADC2_HAVE_TIMER 1
-# define ADC2_TIMER_BASE STM32_TIM8_BASE
-# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB2_TIM8_CLKIN
+# define ADC2_HAVE_TIMER 1
+# define ADC2_TIMER_BASE STM32_TIM8_BASE
+# define ADC2_TIMER_PCLK_FREQUENCY STM32_APB2_TIM8_CLKIN
#else
-# undef ADC2_HAVE_TIMER
+# undef ADC2_HAVE_TIMER
#endif
#ifdef ADC2_HAVE_TIMER
@@ -217,35 +232,31 @@
#endif
#if defined(CONFIG_STM32F7_TIM1_ADC3)
-# define ADC3_HAVE_TIMER 1
-# define ADC3_TIMER_BASE STM32_TIM1_BASE
-# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB2_TIM1_CLKIN
+# define ADC3_HAVE_TIMER 1
+# define ADC3_TIMER_BASE STM32_TIM1_BASE
+# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB2_TIM1_CLKIN
#elif defined(CONFIG_STM32F7_TIM2_ADC3)
-# define ADC3_HAVE_TIMER 1
-# define ADC3_TIMER_BASE STM32_TIM2_BASE
-# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM2_CLKIN
+# define ADC3_HAVE_TIMER 1
+# define ADC3_TIMER_BASE STM32_TIM2_BASE
+# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM2_CLKIN
#elif defined(CONFIG_STM32F7_TIM3_ADC3)
-# define ADC3_HAVE_TIMER 1
-# define ADC3_TIMER_BASE STM32_TIM3_BASE
-# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM3_CLKIN
+# define ADC3_HAVE_TIMER 1
+# define ADC3_TIMER_BASE STM32_TIM3_BASE
+# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM3_CLKIN
#elif defined(CONFIG_STM32F7_TIM4_ADC3)
-# define ADC3_HAVE_TIMER 1
-# define ADC3_TIMER_BASE STM32_TIM4_BASE
-# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM4_CLKIN
+# define ADC3_HAVE_TIMER 1
+# define ADC3_TIMER_BASE STM32_TIM4_BASE
+# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM4_CLKIN
#elif defined(CONFIG_STM32F7_TIM5_ADC3)
-# define ADC3_HAVE_TIMER 1
-# define ADC3_TIMER_BASE STM32_TIM5_BASE
-# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM5_CLKIN
-#elif defined(CONFIG_STM32F7_TIM6_ADC3)
-# define ADC3_HAVE_TIMER 1
-# define ADC3_TIMER_BASE STM32_TIM6_BASE
-# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM6_CLKIN
+# define ADC3_HAVE_TIMER 1
+# define ADC3_TIMER_BASE STM32_TIM5_BASE
+# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB1_TIM5_CLKIN
#elif defined(CONFIG_STM32F7_TIM8_ADC3)
-# define ADC3_HAVE_TIMER 1
-# define ADC3_TIMER_BASE STM32_TIM8_BASE
-# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB2_TIM8_CLKIN
+# define ADC3_HAVE_TIMER 1
+# define ADC3_TIMER_BASE STM32_TIM8_BASE
+# define ADC3_TIMER_PCLK_FREQUENCY STM32_APB2_TIM8_CLKIN
#else
-# undef ADC3_HAVE_TIMER
+# undef ADC3_HAVE_TIMER
#endif
#ifdef ADC3_HAVE_TIMER
@@ -390,7 +401,15 @@
#define ADC3_EXTSEL_T8CC3 ADC_CR2_EXTSEL_T8CC3
#define ADC3_EXTSEL_T8CC4 ADC_CR2_EXTSEL_T8CC4
#define ADC3_EXTSEL_T8TRGO ADC_CR2_EXTSEL_T8TRGO
-#define ADC3_EXTSEL_T8TRGO2 ADC_CR2_EXTSEL_T8TRGO2
+#define ADC3_EXTSEL_T8TRGO2 ADC_CR2_EXTSEL_T8TRGO2
+
+/* EXTSEL configuration *****************************************************/
+
+/* NOTE:
+ * this configuration if used only if CONFIG_STM32F7_TIMx_ADCy is selected.
+ * You can still connect the ADC with a timer trigger using the
+ * CONFIG_STM32F7_ADCx_EXTSEL option.
+ */
#if defined(CONFIG_STM32F7_TIM1_ADC1)
# if CONFIG_STM32F7_ADC1_TIMTRIG == 0
@@ -704,10 +723,288 @@
# endif
#endif
+/* Regular channels external trigger support */
+
+#ifdef ADC1_EXTSEL_VALUE
+# define ADC1_HAVE_EXTCFG 1
+# define ADC1_EXTCFG_VALUE (ADC1_EXTSEL_VALUE | ADC_EXTREG_EXTEN_DEFAULT)
+#elif defined(CONFIG_STM32F7_ADC1_EXTSEL)
+# define ADC1_HAVE_EXTCFG 1
+# define ADC1_EXTCFG_VALUE 0
+#else
+# undef ADC1_HAVE_EXTCFG
+#endif
+#ifdef ADC2_EXTSEL_VALUE
+# define ADC2_HAVE_EXTCFG 1
+# define ADC2_EXTCFG_VALUE (ADC2_EXTSEL_VALUE | ADC_EXTREG_EXTEN_DEFAULT)
+#elif defined(CONFIG_STM32F7_ADC2_EXTSEL)
+# define ADC2_HAVE_EXTCFG 1
+# define ADC2_EXTCFG_VALUE 0
+#else
+# undef ADC2_HAVE_EXTCFG
+#endif
+#ifdef ADC3_EXTSEL_VALUE
+# define ADC3_HAVE_EXTCFG 1
+# define ADC3_EXTCFG_VALUE (ADC3_EXTSEL_VALUE | ADC_EXTREG_EXTEN_DEFAULT)
+#elif defined(CONFIG_STM32F7_ADC3_EXTSEL)
+# define ADC3_HAVE_EXTCFG 1
+# define ADC3_EXTCFG_VALUE 0
+#else
+# undef ADC3_HAVE_EXTCFG
+#endif
+
+#if defined(ADC1_HAVE_EXTCFG) || defined(ADC2_HAVE_EXTCFG) || \
+ defined(ADC3_HAVE_EXTCFG) || defined(ADC3_HAVE_EXTCFG)
+# define ADC_HAVE_EXTCFG
+#endif
+
+/* JEXTSEL configuration ****************************************************/
+
+/* There is no automatic timer tirgger configuration from Kconfig for
+ * injected channels conversion.
+ */
+
+/* Injected channels external trigger support */
+
+#ifdef ADC1_JEXTSEL_VALUE
+# define ADC1_HAVE_JEXTCFG 1
+# define ADC1_JEXTCFG_VALUE (ADC1_JEXTSEL_VALUE | ADC_JEXTREG_JEXTEN_DEFAULT)
+#elif defined(CONFIG_STM32F7_ADC1_JEXTSEL)
+# define ADC1_HAVE_JEXTCFG 1
+# define ADC1_JEXTCFG_VALUE 0
+#else
+# undef ADC1_HAVE_JEXTCFG
+#endif
+#ifdef ADC2_JEXTSEL_VALUE
+# define ADC2_HAVE_JEXTCFG 1
+# define ADC2_JEXTCFG_VALUE (ADC2_JEXTSEL_VALUE | ADC_JEXTREG_JEXTEN_DEFAULT)
+#elif defined(CONFIG_STM32F7_ADC2_JEXTSEL)
+# define ADC2_HAVE_JEXTCFG 1
+# define ADC2_JEXTCFG_VALUE 0
+#else
+# undef ADC2_HAVE_JEXTCFG
+#endif
+#ifdef ADC3_JEXTSEL_VALUE
+# define ADC3_HAVE_JEXTCFG 1
+# define ADC3_JEXTCFG_VALUE (ADC3_JEXTSEL_VALUE | ADC_JEXTREG_JEXTEN_DEFAULT)
+#elif defined(CONFIG_STM32F7_ADC3_JEXTSEL)
+# define ADC3_HAVE_JEXTCFG 1
+# define ADC3_JEXTCFG_VALUE 0
+#else
+# undef ADC3_HAVE_JEXTCFG
+#endif
+
+#if defined(ADC1_HAVE_JEXTCFG) || defined(ADC2_HAVE_JEXTCFG) || \
+ defined(ADC3_HAVE_JEXTCFG)
+# define ADC_HAVE_JEXTCFG
+#endif
+
+/* ADC interrupts ***********************************************************/
+
+#define ADC_ISR_EOC ADC_SR_EOC
+#define ADC_IER_EOC ADC_CR1_EOCIE
+#define ADC_ISR_AWD ADC_SR_AWD
+#define ADC_IER_AWD ADC_CR1_AWDIE
+#define ADC_ISR_JEOC ADC_SR_JEOC
+#define ADC_IER_JEOC ADC_CR1_JEOCIE
+#define ADC_ISR_JEOS 0 /* No JEOS */
+#define ADC_IER_JEOS 0 /* No JEOS */
+#define ADC_ISR_OVR ADC_SR_OVR
+#define ADC_IER_OVR ADC_CR1_OVRIE
+
+#define ADC_ISR_ALLINTS (ADC_ISR_EOC | ADC_ISR_AWD | ADC_ISR_JEOC | \
+ ADC_ISR_JEOS | ADC_ISR_OVR)
+#define ADC_IER_ALLINTS (ADC_IER_EOC | ADC_IER_AWD | ADC_IER_JEOC | \
+ ADC_IER_JEOS | ADC_IER_OVR)
+
+/* Low-level ops helpers ****************************************************/
+
+#define STM32_ADC_INT_ACK(adc, source) \
+ (adc)->llops->int_ack(adc, source)
+#define STM32_ADC_INT_GET(adc) \
+ (adc)->llops->int_get(adc)
+#define STM32_ADC_INT_ENABLE(adc, source) \
+ (adc)->llops->int_en(adc, source)
+#define STM32_ADC_INT_DISABLE(adc, source) \
+ (adc)->llops->int_dis(adc, source)
+#define STM32_ADC_REGDATA_GET(adc) \
+ (adc)->llops->val_get(adc)
+#define STM32_ADC_REGBUF_REGISTER(adc, buffer, len) \
+ (adc)->llops->regbuf_reg(adc, buffer, len)
+#define STM32_ADC_REG_STARTCONV(adc, state) \
+ (adc)->llops->reg_startconv(adc, state)
+#define STM32_ADC_OFFSET_SET(adc, ch, i, o) \
+ (adc)->llops->offset_set(adc, ch, i, o)
+#define STM32_ADC_EXTCFG_SET(adc, c) \
+ (adc)->llops->extcfg_set(adc, c)
+#define STM32_ADC_INJ_STARTCONV(adc, state) \
+ (adc)->llops->inj_startconv(adc, state)
+#define STM32_ADC_INJDATA_GET(adc, chan) \
+ (adc)->llops->inj_get(adc, chan)
+#define STM32_ADC_JEXTCFG_SET(adc, c) \
+ (adc)->llops->jextcfg_set(adc, c)
+#define STM32_ADC_SAMPLETIME_SET(adc, time_samples) \
+ (adc)->llops->stime_set(adc, time_samples)
+#define STM32_ADC_SAMPLETIME_WRITE(adc) \
+ (adc)->llops->stime_write(adc)
+#define STM32_ADC_DUMP_REGS(adc) \
+ (adc)->llops->dump_regs(adc)
+#define STM32_ADC_SETUP(adc) \
+ (adc)->llops->setup(adc)
+#define STM32_ADC_SHUTDOWN(adc) \
+ (adc)->llops->shutdown(adc)
+
/****************************************************************************
* Public Types
****************************************************************************/
+enum adc_io_cmds_e
+{
+ IO_STOP_ADC,
+ IO_START_ADC,
+ IO_START_CONV,
+ IO_TRIGGER_REG,
+#ifdef ADC_HAVE_INJECTED
+ IO_TRIGGER_INJ,
+#endif
+};
+
+/* ADC resolution can be reduced in order to perform faster conversion */
+
+enum stm32_adc_resoluton_e
+{
+ ADC_RESOLUTION_12BIT = 0, /* 12 bit */
+ ADC_RESOLUTION_10BIT = 1, /* 10 bit */
+ ADC_RESOLUTION_8BIT = 2, /* 8 bit */
+ ADC_RESOLUTION_6BIT = 3 /* 6 bit */
+};
+
+#ifdef CONFIG_STM32F7_ADC_LL_OPS
+
+#ifdef CONFIG_STM32F7_ADC_CHANGE_SAMPLETIME
+
+/* Channel and sample time pair */
+
+typedef struct adc_channel_s
+{
+ uint8_t channel:5;
+
+ /* Sampling time individually for each channel */
+
+ uint8_t sample_time:3;
+} adc_channel_t;
+
+/* This structure will be used while setting channels to specified by the
+ * "channel-sample time" pairs' values
+ */
+
+struct adc_sample_time_s
+{
+ adc_channel_t *channel; /* Array of channels */
+ uint8_t channels_nbr:5; /* Number of channels in array */
+ bool all_same:1; /* All channels will get the
+ * same value of the sample time */
+ uint8_t all_ch_sample_time:3; /* Sample time for all channels */
+};
+#endif /* CONFIG_STM32F7_ADC_CHANGE_SAMPLETIME */
+
+/* This structure provides the publicly visible representation of the
+ * "lower-half" ADC driver structure.
+ */
+
+struct stm32_adc_dev_s
+{
+ /* Publicly visible portion of the "lower-half" ADC driver structure */
+
+ const struct stm32_adc_ops_s *llops;
+
+ /* Require cast-compatibility with private "lower-half" ADC structure */
+};
+
+/* Low-level operations for ADC */
+
+struct stm32_adc_ops_s
+{
+ /* Low-level ADC setup */
+
+ int (*setup)(struct stm32_adc_dev_s *dev);
+
+ /* Low-level ADC shutdown */
+
+ void (*shutdown)(struct stm32_adc_dev_s *dev);
+
+ /* Acknowledge interrupts */
+
+ void (*int_ack)(struct stm32_adc_dev_s *dev, uint32_t source);
+
+ /* Get pending interrupts */
+
+ uint32_t (*int_get)(struct stm32_adc_dev_s *dev);
+
+ /* Enable interrupts */
+
+ void (*int_en)(struct stm32_adc_dev_s *dev, uint32_t source);
+
+ /* Disable interrupts */
+
+ void (*int_dis)(struct stm32_adc_dev_s *dev, uint32_t source);
+
+ /* Get current ADC data register */
+
+ uint32_t (*val_get)(struct stm32_adc_dev_s *dev);
+
+ /* Register buffer for ADC DMA transfer */
+
+ int (*regbuf_reg)(struct stm32_adc_dev_s *dev,
+ uint16_t *buffer, uint8_t len);
+
+ /* Start/stop regular conversion */
+
+ void (*reg_startconv)(struct stm32_adc_dev_s *dev, bool state);
+
+ /* Set offset for channel */
+
+ int (*offset_set)(struct stm32_adc_dev_s *dev, uint8_t ch, uint8_t i,
+ uint16_t offset);
+
+#ifdef ADC_HAVE_EXTCFG
+ /* Configure the ADC external trigger for regular conversion */
+
+ void (*extcfg_set)(struct stm32_adc_dev_s *dev, uint32_t extcfg);
+#endif
+
+#ifdef ADC_HAVE_JEXTCFG
+ /* Configure the ADC external trigger for injected conversion */
+
+ void (*jextcfg_set)(struct stm32_adc_dev_s *dev, uint32_t jextcfg);
+#endif
+
+#ifdef ADC_HAVE_INJECTED
+ /* Get current ADC injected data register */
+
+ uint32_t (*inj_get)(struct stm32_adc_dev_s *dev, uint8_t chan);
+
+ /* Start/stop injected conversion */
+
+ void (*inj_startconv)(struct stm32_adc_dev_s *dev, bool state);
+#endif
+
+#ifdef CONFIG_STM32F7_ADC_CHANGE_SAMPLETIME
+ /* Set ADC sample time */
+
+ void (*stime_set)(struct stm32_adc_dev_s *dev,
+ struct adc_sample_time_s *time_samples);
+
+ /* Write ADC sample time */
+
+ void (*stime_write)(struct stm32_adc_dev_s *dev);
+#endif
+
+ void (*dump_regs)(struct stm32_adc_dev_s *dev);
+};
+
+#endif /* CONFIG_STM32F7_ADC_LL_OPS */
+
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
@@ -722,15 +1019,15 @@ extern "C"
#endif
/****************************************************************************
- * Name: stm32_adc_initialiize
+ * Name: stm32_adc_initialize
*
* Description:
- * Initialize the ADC.
+ * Initialize the ADC. See stm32_adc.c for more details.
*
* Input Parameters:
- * intf - Could be {1,2,3} for ADC1, ADC2, or ADC3
- * chanlist - The list of channels
- * nchannels - Number of channels
+ * intf - Could be {1,2,3} for ADC1, ADC2, ADC3
+ * chanlist - The list of channels (regular + injected)
+ * nchannels - Number of channels (regular + injected)
*
* Returned Value:
* Valid ADC device structure reference on success; a NULL on failure
@@ -741,6 +1038,7 @@ struct adc_dev_s;
struct adc_dev_s *stm32_adc_initialize(int intf,
const uint8_t *chanlist,
int nchannels);
+
#undef EXTERN
#ifdef __cplusplus
}