diff --git a/arch/arm/src/stm32f0l0/Kconfig b/arch/arm/src/stm32f0l0/Kconfig index 84574e753d..5c8895276c 100644 --- a/arch/arm/src/stm32f0l0/Kconfig +++ b/arch/arm/src/stm32f0l0/Kconfig @@ -697,11 +697,24 @@ endchoice config STM32F0L0_STM32F0 bool default n + select STM32F0L0_HAVE_USART3 + select STM32F0L0_HAVE_USART4 + select STM32F0L0_HAVE_TIM1 + select STM32F0L0_HAVE_TIM2 + select STM32F0L0_HAVE_TIM3 + select STM32F0L0_HAVE_TIM6 + select STM32F0L0_HAVE_TIM7 + select STM32F0L0_HAVE_TIM14 + select STM32F0L0_HAVE_TIM15 + select STM32F0L0_HAVE_TIM16 + select STM32F0L0_HAVE_TIM17 + select STM32F0L0_HAVE_ADC1_DMA config STM32F0L0_STM32L0 bool - select STM32F0L0_ENERGYLITE default n + select STM32F0L0_ENERGYLITE + select STM32F0L0_HAVE_ADC1_DMA config STM32F0L0_STM32F03X bool @@ -728,41 +741,19 @@ config STM32F0L0_STM32F09X default n select STM32F0L0_STM32F0 select STM32F0L0_HAVE_HSI48 + select STM32F0L0_HAVE_DMA2 config STM32F0L0_VALUELINE bool default n - select STM32F0L0_HAVE_USART3 - select STM32F0L0_HAVE_USART4 select STM32F0L0_HAVE_USART5 - select STM32F0L0_HAVE_TIM1 - select STM32F0L0_HAVE_TIM2 - select STM32F0L0_HAVE_TIM3 - select STM32F0L0_HAVE_TIM6 - select STM32F0L0_HAVE_TIM7 - select STM32F0L0_HAVE_TIM14 - select STM32F0L0_HAVE_TIM15 - select STM32F0L0_HAVE_TIM16 - select STM32F0L0_HAVE_TIM17 select STM32F0L0_HAVE_SPI2 if STM32F0L0_HIGHDENSITY select STM32F0L0_HAVE_SPI3 if STM32F0L0_HIGHDENSITY config STM32F0L0_ACCESSLINE bool default n - select STM32F0L0_HAVE_USART3 - select STM32F0L0_HAVE_USART4 select STM32F0L0_HAVE_USART5 - select STM32F0L0_HAVE_TIM1 - select STM32F0L0_HAVE_TIM2 - select STM32F0L0_HAVE_TIM3 - select STM32F0L0_HAVE_TIM6 - select STM32F0L0_HAVE_TIM7 - select STM32F0L0_HAVE_TIM14 - select STM32F0L0_HAVE_TIM15 - select STM32F0L0_HAVE_TIM16 - select STM32F0L0_HAVE_TIM17 - select STM32F0L0_HAVE_ADC2 select STM32F0L0_HAVE_CAN1 select STM32F0L0_HAVE_SPI2 select STM32F0L0_HAVE_SPI3 @@ -770,19 +761,7 @@ config STM32F0L0_ACCESSLINE config STM32F0L0_LOWVOLTLINE bool default n - select STM32F0L0_HAVE_USART3 - select STM32F0L0_HAVE_USART4 select STM32F0L0_HAVE_USART5 - select STM32F0L0_HAVE_TIM1 - select STM32F0L0_HAVE_TIM2 - select STM32F0L0_HAVE_TIM3 - select STM32F0L0_HAVE_TIM6 - select STM32F0L0_HAVE_TIM7 - select STM32F0L0_HAVE_TIM14 - select STM32F0L0_HAVE_TIM15 - select STM32F0L0_HAVE_TIM16 - select STM32F0L0_HAVE_TIM17 - select STM32F0L0_HAVE_ADC2 select STM32F0L0_HAVE_CAN1 select STM32F0L0_HAVE_SPI2 select STM32F0L0_HAVE_SPI3 @@ -791,18 +770,6 @@ config STM32F0L0_USBLINE bool default n select STM32F0L0_HAVE_HSI48 - select STM32F0L0_HAVE_USART3 - select STM32F0L0_HAVE_USART4 - select STM32F0L0_HAVE_TIM1 - select STM32F0L0_HAVE_TIM2 - select STM32F0L0_HAVE_TIM3 - select STM32F0L0_HAVE_TIM6 - select STM32F0L0_HAVE_TIM7 - select STM32F0L0_HAVE_TIM14 - select STM32F0L0_HAVE_TIM15 - select STM32F0L0_HAVE_TIM16 - select STM32F0L0_HAVE_TIM17 - select STM32F0L0_HAVE_ADC2 select STM32F0L0_HAVE_CAN1 select STM32F0L0_HAVE_SPI2 select STM32F0L0_HAVE_SPI3 @@ -855,7 +822,6 @@ config STM32F0L0_SYSTICK_CORECLK_DIV16 endchoice - menu "STM32 Peripheral Support" # These "hidden" settings determine is a peripheral option is available for the @@ -869,6 +835,10 @@ config STM32F0L0_HAVE_HSI48 bool default n +config STM32F0L0_HAVE_LCD + bool + default n + config STM32F0L0_HAVE_USBDEV bool default n @@ -941,55 +911,11 @@ config STM32F0L0_HAVE_TSC bool default n -config STM32F0L0_HAVE_ADC2 - bool - default n - -config STM32F0L0_HAVE_ADC3 - bool - default n - -config STM32F0L0_HAVE_ADC4 - bool - default n - config STM32F0L0_HAVE_ADC1_DMA bool default n -config STM32F0L0_HAVE_ADC2_DMA - bool - default n - -config STM32F0L0_HAVE_ADC3_DMA - bool - default n - -config STM32F0L0_HAVE_ADC4_DMA - bool - default n - -config STM32F0L0_HAVE_SDADC1 - bool - default n - -config STM32F0L0_HAVE_SDADC2 - bool - default n - -config STM32F0L0_HAVE_SDADC3 - bool - default n - -config STM32F0L0_HAVE_SDADC1_DMA - bool - default n - -config STM32F0L0_HAVE_SDADC2_DMA - bool - default n - -config STM32F0L0_HAVE_SDADC3_DMA +config STM32F0L0_HAVE_CEC bool default n @@ -1005,31 +931,11 @@ config STM32F0L0_HAVE_COMP2 bool default n -config STM32F0L0_HAVE_COMP3 - bool - default n - -config STM32F0L0_HAVE_COMP4 - bool - default n - -config STM32F0L0_HAVE_COMP5 - bool - default n - -config STM32F0L0_HAVE_COMP6 - bool - default n - -config STM32F0L0_HAVE_COMP7 - bool - default n - config STM32F0L0_HAVE_DAC1 bool default n -config STM32F0L0_HAVE_DAC2 +config STM32F0L0_HAVE_DMA2 bool default n @@ -1069,6 +975,10 @@ config STM32F0L0_HAVE_SAIPLL bool default n +config STM32F0L0_HAVE_SDIO + bool + default n + config STM32F0L0_HAVE_I2SPLL bool default n @@ -1094,48 +1004,9 @@ config STM32F0L0_HAVE_OPAMP4 config STM32F0L0_ADC1 bool "ADC1" default n + depends on EXPERIMENTAL select STM32F0L0_ADC -config STM32F0L0_ADC2 - bool "ADC2" - default n - select STM32F0L0_ADC - depends on STM32F0L0_HAVE_ADC2 - -config STM32F0L0_ADC3 - bool "ADC3" - default n - select STM32F0L0_ADC - depends on STM32F0L0_HAVE_ADC3 - -config STM32F0L0_ADC4 - bool "ADC4" - default n - select STM32F0L0_ADC - depends on STM32F0L0_HAVE_ADC4 - -config STM32F0L0_SDADC1 - bool "SDADC1" - default n - select STM32F0L0_SDADC - depends on STM32F0L0_HAVE_SDADC1 - -config STM32F0L0_SDADC2 - bool "SDADC2" - default n - select STM32F0L0_SDADC - depends on STM32F0L0_HAVE_SDADC2 - -config STM32F0L0_SDADC3 - bool "SDADC3" - default n - select STM32F0L0_SDADC - depends on STM32F0L0_HAVE_SDADC3 - -config STM32F0L0_COMP - bool "COMP" - default n - config STM32F0L0_COMP1 bool "COMP1" default n @@ -1146,31 +1017,6 @@ config STM32F0L0_COMP2 default n depends on STM32F0L0_HAVE_COMP2 -config STM32F0L0_COMP3 - bool "COMP3" - default n - depends on STM32F0L0_HAVE_COMP3 - -config STM32F0L0_COMP4 - bool "COMP4" - default n - depends on STM32F0L0_HAVE_COMP4 - -config STM32F0L0_COMP5 - bool "COMP5" - default n - depends on STM32F0L0_HAVE_COMP5 - -config STM32F0L0_COMP6 - bool "COMP6" - default n - depends on STM32F0L0_HAVE_COMP6 - -config STM32F0L0_COMP7 - bool "COMP7" - default n - depends on STM32F0L0_HAVE_COMP6 - config STM32F0L0_BKP bool "BKP" default n @@ -1189,7 +1035,7 @@ config STM32F0L0_CAN1 config STM32F0L0_CEC bool "CEC" default n - depends on STM32F0L0_VALUELINE + depends on STM32F0L0_HAVE_CEC config STM32F0L0_CRC bool "CRC" @@ -1198,7 +1044,7 @@ config STM32F0L0_CRC config STM32F0L0_CRYP bool "CRYP" default n - depends on STM32F0L0_STM32F207 || STM32F0L0_STM32F40XX + depends on STM32F0L0_HAVE_HASH config STM32F0L0_DMA1 bool "DMA1" @@ -1208,8 +1054,8 @@ config STM32F0L0_DMA1 config STM32F0L0_DMA2 bool "DMA2" default n + depends on STM32F0L0_HAVE_DMA2 select ARCH_DMA - depends on !STM32F0L0_VALUELINE || (STM32F0L0_VALUELINE && STM32F0L0_HIGHDENSITY) config STM32F0L0_DAC1 bool "DAC1" @@ -1217,12 +1063,6 @@ config STM32F0L0_DAC1 depends on STM32F0L0_HAVE_DAC1 select STM32F0L0_DAC -config STM32F0L0_DAC2 - bool "DAC2" - default n - depends on STM32F0L0_HAVE_DAC2 - select STM32F0L0_DAC - config STM32F0L0_FSMC bool "FSMC" default n @@ -1231,7 +1071,7 @@ config STM32F0L0_FSMC config STM32F0L0_HASH bool "HASH" default n - depends on STM32F0L0_STM32F207 || STM32F0L0_STM32F40XX + depends on STM32F0L0_HAVE_HASH config STM32F0L0_I2C1 bool "I2C1" @@ -1263,7 +1103,7 @@ config STM32F0L0_RNG config STM32F0L0_SDIO bool "SDIO" default n - depends on !STM32F0L0_CONNECTIVITYLINE && !STM32F0L0_VALUELINE + depends on STM32F0L0_HAVE_SDIO select ARCH_HAVE_SDIO select ARCH_HAVE_SDIOWAIT_WRCOMPLETE select ARCH_HAVE_SDIO_PREFLIGHT @@ -1414,6 +1254,12 @@ config STM32F0L0_USB depends on STM32F0L0_HAVE_USBDEV select USBDEV +config STM32F0L0_LCD + bool "Segment LCD" + default n + depends on STM32F0L0_HAVE_LCD + select USBDEV + config STM32F0L0_IWDG bool "IWDG" default n @@ -1426,10 +1272,10 @@ config STM32F0L0_WWDG endmenu -config STM32F0L0_ADC +config STM32F0L0_COMP bool -config STM32F0L0_SDADC +config STM32F0L0_ADC bool config STM32F0L0_DAC @@ -1569,7 +1415,6 @@ config USART3_RS485_DIR_POLARITY endif # STM32F0L0_USART3_SERIALDRIVER - choice prompt "USART4 Driver Configuration" default STM32F0L0_USART4_SERIALDRIVER @@ -1794,3 +1639,126 @@ config STM32F0L0_PM_SERIAL_ACTIVITY endif endmenu + +menu "ADC Configuration" + depends on STM32F0L0_ADC + +config STM32F0L0_ADC1_RESOLUTION + int "ADC1 resolution" + depends on STM32F0L0_ADC1 + default 0 + range 0 3 + ---help--- + ADC1 resolution. 0 - 12 bit, 1 - 10 bit, 2 - 8 bit, 3 - 6 bit + +config STM32F0L0_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 STM32F0L0_ADC_NOIRQ + bool "Do not use default ADC interrupts" + default n + ---help--- + Do not use default ADC interrupts handlers. + +config STM32F0L0_ADC_LL_OPS + bool "ADC low-level operations" + default n + ---help--- + Enable low-level ADC ops. + +config STM32F0L0_ADC_CHANGE_SAMPLETIME + bool "ADC sample time configuration" + default n + depends on STM32F0L0_ADC_LL_OPS + ---help--- + Enable ADC sample time configuration (SMPRx registers). + +config STM32F0L0_ADC1_DMA + bool "ADC1 DMA" + depends on STM32F0L0_ADC1 && STM32F0L0_HAVE_ADC1_DMA + default n + ---help--- + If DMA is selected, then the ADC may be configured to support + DMA transfer, which is necessary if multiple channels are read + or if very high trigger frequencies are used. + +config STM32F0L0_ADC1_DMA_CFG + int "ADC1 DMA configuration" + depends on STM32F0L0_ADC1_DMA && !STM32F0L0_HAVE_IP_ADC_V1_BASIC + range 0 1 + default 0 + ---help--- + 0 - ADC1 DMA in One Shot Mode, 1 - ADC1 DMA in Circular Mode + +endmenu + +menu "SPI Configuration" + depends on STM32F0L0_SPI + +config STM32F0L0_SPI_INTERRUPTS + bool "Interrupt driver SPI" + default n + ---help--- + Select to enable interrupt driven SPI support. Non-interrupt-driven, + poll-waiting is recommended if the interrupt rate would be to high in + the interrupt driven case. + +config STM32F0L0_SPI_DMA + bool "SPI DMA" + default n + ---help--- + Use DMA to improve SPI transfer performance. Cannot be used with STM32F0L0_SPI_INTERRUPT. + +config STM32F0L0_SPI1_DMA + bool "SPI1 DMA" + default n + depends on STM32F0L0_SPI1 && STM32F0L0_SPI_DMA + ---help--- + Use DMA to improve SPI1 transfer performance. + +config STM32F0L0_SPI2_DMA + bool "SPI2 DMA" + default n + depends on STM32F0L0_SPI2 && STM32F0L0_SPI_DMA + ---help--- + Use DMA to improve SPI2 transfer performance. + +endmenu # SPI Configuration + +menu "I2C Configuration" + depends on STM32F0L0_I2C + +config STM32F0L0_I2C_DYNTIMEO + bool "Use dynamic timeouts" + default n + depends on STM32F0L0_I2C + +config STM32F0L0_I2C_DYNTIMEO_USECPERBYTE + int "Timeout Microseconds per Byte" + default 500 + depends on STM32F0L0_I2C_DYNTIMEO + +config STM32F0L0_I2C_DYNTIMEO_STARTSTOP + int "Timeout for Start/Stop (Milliseconds)" + default 1000 + depends on STM32F0L0_I2C_DYNTIMEO + +config STM32F0L0_I2CTIMEOSEC + int "Timeout seconds" + default 0 + depends on STM32F0L0_I2C + +config STM32F0L0_I2CTIMEOMS + int "Timeout Milliseconds" + default 500 + depends on STM32F0L0_I2C && !STM32F0L0_I2C_DYNTIMEO + +config STM32F0L0_I2CTIMEOTICKS + int "Timeout for Done and Stop (ticks)" + default 500 + depends on STM32F0L0_I2C && !STM32F0L0_I2C_DYNTIMEO + +endmenu #I2C Configuration diff --git a/arch/arm/src/stm32f0l0/Make.defs b/arch/arm/src/stm32f0l0/Make.defs index 4a3a9aebb6..39b964e4f3 100644 --- a/arch/arm/src/stm32f0l0/Make.defs +++ b/arch/arm/src/stm32f0l0/Make.defs @@ -63,7 +63,7 @@ CMN_CSRCS += up_dumpnvic.c endif CHIP_ASRCS = -CHIP_CSRCS = stm32_start.c stm32_gpio.c stm32_exti_gpio.c stm32_irq.c # stm32_dma_v1.c +CHIP_CSRCS = stm32_start.c stm32_gpio.c stm32_exti_gpio.c stm32_irq.c stm32_dma_v1.c CHIP_CSRCS += stm32_lse.c stm32_lowputc.c stm32_serial.c stm32_rcc.c ifeq ($(CONFIG_STM32F0L0_PWR),y) @@ -106,6 +106,10 @@ ifeq ($(CONFIG_STM32F0L0_SPI),y) CHIP_CSRCS += stm32_spi.c endif -ifeq ($(CONFIG_PWM),y) +ifeq ($(CONFIG_STM32F0L0_PWM),y) CHIP_CSRCS += stm32_pwm.c endif + +ifeq ($(CONFIG_STM32F0L0_ADC),y) +CHIP_CSRCS += stm32_adc.c +endif diff --git a/arch/arm/src/stm32f0l0/hardware/stm32_adc.h b/arch/arm/src/stm32f0l0/hardware/stm32_adc.h index 75dd3ddcd4..eeaa3234e3 100644 --- a/arch/arm/src/stm32f0l0/hardware/stm32_adc.h +++ b/arch/arm/src/stm32f0l0/hardware/stm32_adc.h @@ -45,10 +45,64 @@ #include "chip.h" +/* STM32 M0 ADC driver: + * - no injected channels + * - no offset registers + * - the F0/L0 family support one sampling time configuration for all channels + * - the G0 family support two sampling time configurations + */ + +/* Support for battery voltage */ + +#if 0 +# define HAVE_ADC_VBAT +#else +# undef HAVE_ADC_VBAT +#endif + +/* Support for ADC clock prescaler */ + +#if defined(CONFIG_STM32F0L0_STM32L0) || defined(CONFIG_STM32F0L0_STM32G0) +# define HAVE_ADC_PRE +#else +# undef HAVE_ADC_PRE +#endif + +/* Support for LCD voltage */ + +#ifdef CONFIG_STM32F0L0_HAVE_LCD +# define HAVE_ADC_VLCD +#else +# undef HAVE_ADC_VLCD +#endif + +/* Supprot for Low frequency mode */ + +#ifdef CONFIG_STM32F0L0_ENERGYLITE +# define HAVE_ADC_LFM +#else +# undef HAVE_ADC_LFM +#endif + +#undef ADC_HAVE_INJECTED + /******************************************************************************** * Pre-processor Definitions ********************************************************************************/ +#define STM32_ADC1_OFFSET 0x0000 +#define STM32_ADC2_OFFSET 0x0100 +#define STM32_ADC3_OFFSET 0x0000 +#define STM32_ADC4_OFFSET 0x0100 +#define STM32_ADCCMN_OFFSET 0x0300 + +#define STM32_ADC1_BASE (STM32_ADC1_OFFSET+STM32_ADC12_BASE) /* ADC1 Master ADC */ +#define STM32_ADC2_BASE (STM32_ADC2_OFFSET+STM32_ADC12_BASE) /* ADC2 Slave ADC */ +#define STM32_ADC3_BASE (STM32_ADC3_OFFSET+STM32_ADC34_BASE) /* ADC3 Master ADC */ +#define STM32_ADC4_BASE (STM32_ADC4_OFFSET+STM32_ADC34_BASE) /* ADC4 Slave ADC */ +#define STM32_ADC12CMN_BASE (STM32_ADCCMN_OFFSET+STM32_ADC12_BASE) /* ADC1, ADC2 common */ +#define STM32_ADC34CMN_BASE (STM32_ADCCMN_OFFSET+STM32_ADC34_BASE) /* ADC3, ADC4 common */ + /* Register Offsets *********************************************************************************/ #define STM32_ADC_ISR_OFFSET 0x0000 /* ADC interrupt and status register */ @@ -60,20 +114,23 @@ #define STM32_ADC_TR_OFFSET 0x0020 /* ADC watchdog threshold register */ #define STM32_ADC_CHSELR_OFFSET 0x0028 /* ADC channel selection register */ #define STM32_ADC_DR_OFFSET 0x0040 /* ADC regular data register */ -#define STM32_ADC_CCR_OFFSET 0x0308 /* ADC common configuration register */ + +/* Master and Slave ADC Common Registers */ + +#define STM32_ADC_CCR_OFFSET 0x0008 /* Common control register */ /* Register Addresses *******************************************************************************/ -#define STM32_ADC_ISR (STM32_ADC_BASE + STM32_ADC_ISR_OFFSET) -#define STM32_ADC_IER (STM32_ADC_BASE + STM32_ADC_IER_OFFSET) -#define STM32_ADC_CR (STM32_ADC_BASE + STM32_ADC_CR_OFFSET) -#define STM32_ADC_CFGR1 (STM32_ADC_BASE + STM32_ADC_CFGR1_OFFSET) -#define STM32_ADC_CFGR2 (STM32_ADC_BASE + STM32_ADC_CFGR2_OFFSET) -#define STM32_ADC_SMPR (STM32_ADC_BASE + STM32_ADC_SMPR_OFFSET) -#define STM32_ADC_TR (STM32_ADC_BASE + STM32_ADC_TR_OFFSET) -#define STM32_ADC_CHSELR (STM32_ADC_BASE + STM32_ADC_CHSELR_OFFSET) -#define STM32_ADC_DR (STM32_ADC_BASE + STM32_ADC_DR_OFFSET) -#define STM32_ADC_CCR (STM32_ADC_BASE + STM32_ADC_CCR_OFFSET) +#define STM32_ADC1_ISR (STM32_ADC1_BASE + STM32_ADC_ISR_OFFSET) +#define STM32_ADC1_IER (STM32_ADC1_BASE + STM32_ADC_IER_OFFSET) +#define STM32_ADC1_CR (STM32_ADC1_BASE + STM32_ADC_CR_OFFSET) +#define STM32_ADC1_CFGR1 (STM32_ADC1_BASE + STM32_ADC_CFGR1_OFFSET) +#define STM32_ADC1_CFGR2 (STM32_ADC1_BASE + STM32_ADC_CFGR2_OFFSET) +#define STM32_ADC1_SMPR (STM32_ADC1_BASE + STM32_ADC_SMPR_OFFSET) +#define STM32_ADC1_TR (STM32_ADC1_BASE + STM32_ADC_TR_OFFSET) +#define STM32_ADC1_CHSELR (STM32_ADC1_BASE + STM32_ADC_CHSELR_OFFSET) +#define STM32_ADC1_DR (STM32_ADC1_BASE + STM32_ADC_DR_OFFSET) +#define STM32_ADC1_CCR (STM32_ADC1_BASE + STM32_ADC_CCR_OFFSET) /* Register Bitfield Definitions ************************************************/ @@ -92,6 +149,7 @@ #define ADC_CR_ADDIS (1 << 1) /* Bit 1: ADC disable command */ #define ADC_CR_ADSTART (1 << 2) /* Bit 2: ADC start of regular conversion */ #define ADC_CR_ADSTP (1 << 4) /* Bit 4: ADC stop of regular conversion command */ +#define ADC_CR_ADVREGEN (1 << 28) /* Bit 28: ADC Voltage Regulator Enable */ #define ADC_CR_ADCAL (1 << 31) /* Bit 31: ADC calibration */ /* ADC configuration register 1 */ @@ -143,16 +201,21 @@ /* ADC sample time register */ -#define ADC_SMPR_SMP_SHIFT (0) /* Bits 0-2: Sampling time selection */ -#define ADC_SMPR_SMP_MASK (7 << ADC_SMPR_SMP_SHIFT) -#define ADC_SMPR_SMP_1p5 (0 << ADC_SMPR_SMP_SHIFT) /* 000: 1.5 cycles */ -#define ADC_SMPR_SMP_7p5 (1 << ADC_SMPR_SMP_SHIFT) /* 001: 7.5 cycles */ -#define ADC_SMPR_SMP_13p5 (2 << ADC_SMPR_SMP_SHIFT) /* 010: 13.5 cycles */ -#define ADC_SMPR_SMP_28p5 (3 << ADC_SMPR_SMP_SHIFT) /* 011: 28.5 cycles */ -#define ADC_SMPR_SMP_41p5 (4 << ADC_SMPR_SMP_SHIFT) /* 100: 41.5 cycles */ -#define ADC_SMPR_SMP_55p5 (5 << ADC_SMPR_SMP_SHIFT) /* 101: 55.5 cycles */ -#define ADC_SMPR_SMP_71p5 (6 << ADC_SMPR_SMP_SHIFT) /* 110: 71.5 cycles */ -#define ADC_SMPR_SMP_239p5 (7 << ADC_SMPR_SMP_SHIFT) /* 111: 239.5 cycles */ +#define ADC_SMPR_1p5 (0) /* 000: 1.5 cycles */ +#define ADC_SMPR_7p5 (1) /* 001: 7.5 cycles */ +#define ADC_SMPR_13p5 (2) /* 010: 13.5 cycles */ +#define ADC_SMPR_28p5 (3) /* 011: 28.5 cycles */ +#define ADC_SMPR_41p5 (4) /* 100: 41.5 cycles */ +#define ADC_SMPR_55p5 (5) /* 101: 55.5 cycles */ +#define ADC_SMPR_71p5 (6) /* 110: 71.5 cycles */ +#define ADC_SMPR_239p5 (7) /* 111: 239.5 cycles */ + +#define ADC_SMPR_SMP1_SHIFT (0) /* Bits 0-2: Sampling time selection 1 */ +#define ADC_SMPR_SMP1_MASK (7 << ADC_SMPR_SMP_SHIFT) +#define ADC_SMPR_SMP2_SHIFT (4) /* Bits 4-6: Sampling time selection 2 */ +#define ADC_SMPR_SMP2_MASK (7 << ADC_SMPR_SMP_SHIFT) +#define ADC_SMPR_SMPSEL_SHIFT (8) /* Bits 8-26: channel-x sampling time selection */ +#define ADC_SMPR_SMPSEL(ch, smp) (smp << ADC_SMPR_SMPSEL_SHIFT) /* ADC watchdog threshold register */ @@ -173,23 +236,36 @@ #define ADC_CHSELR_CHSEL7 (1 << 7) /* Select channel 7 */ #define ADC_CHSELR_CHSEL8 (1 << 8) /* Select channel 8 */ #define ADC_CHSELR_CHSEL9 (1 << 9) /* Select channel 9 */ -#define ADC_CHSELR_CHSEL10 (1 << 10) /* Select channel 10 */ -#define ADC_CHSELR_CHSEL11 (1 << 11) /* Select channel 11 */ -#define ADC_CHSELR_CHSEL12 (1 << 12) /* Select channel 12 */ -#define ADC_CHSELR_CHSEL13 (1 << 13) /* Select channel 13 */ -#define ADC_CHSELR_CHSEL14 (1 << 14) /* Select channel 14 */ -#define ADC_CHSELR_CHSEL15 (1 << 15) /* Select channel 15 */ -#define ADC_CHSELR_CHSEL16 (1 << 16) /* Select channel 16 */ -#define ADC_CHSELR_CHSEL17 (1 << 17) /* Select channel 17 */ -#define ADC_CHSELR_CHSEL18 (1 << 18) /* Select channel 18 */ +#define ADC_CHSELR_CHSEL10 (1 << 10) /* Select channel 10 */ +#define ADC_CHSELR_CHSEL11 (1 << 11) /* Select channel 11 */ +#define ADC_CHSELR_CHSEL12 (1 << 12) /* Select channel 12 */ +#define ADC_CHSELR_CHSEL13 (1 << 13) /* Select channel 13 */ +#define ADC_CHSELR_CHSEL14 (1 << 14) /* Select channel 14 */ +#define ADC_CHSELR_CHSEL15 (1 << 15) /* Select channel 15 */ +#define ADC_CHSELR_CHSEL16 (1 << 16) /* Select channel 16 */ +#define ADC_CHSELR_CHSEL17 (1 << 17) /* Select channel 17 */ +#define ADC_CHSELR_CHSEL18 (1 << 18) /* Select channel 18 */ +#define ADC_CHSELR_CHSEL(ch) (1 << ch) #define ADC_DR_RDATA_SHIFT (0) #define ADC_DR_RDATA_MASK (0xffff << ADC_DR_RDATA_SHIFT) /* Common configuration register */ +#ifdef HAVE_ADC_VLCD +# define ADC_CCR_PRESC_SHIFT (18) /* ADC Prescaler */ +# define ADC_CCR_PRESC_MASK (0xf << ADC_CCR_PRESC_SHIFT) +#endif #define ADC_CCR_VREFEN (1 << 22) /* Bit 22: VREFINT enable */ #define ADC_CCR_TSEN (1 << 23) /* Bit 23: Temperature sensor enable */ -#define ADC_CCR_VBATEN (1 << 24) /* Bit 22: VBAT enable */ +#ifdef HAVE_ADC_VBAT +# define ADC_CCR_VBATEN (1 << 24) /* Bit 24: VBAT enable */ +#endif +#ifdef HAVE_ADC_VLCD +# define ADC_CCR_VLCDEN (1 << 24) /* Bit 24: VLCD enable */ +#endif +#ifdef HAVE_ADC_LFM +# define ADC_CCR_LFMEN (1 << 25) /* Bit 25: Low Frequency Mode enable */ +#endif #endif /* __ARCH_ARM_SRC_STM32F0L0_CHIP_STM32_ADC_H */ diff --git a/arch/arm/src/stm32f0l0/hardware/stm32f05xf07xf09x_memorymap.h b/arch/arm/src/stm32f0l0/hardware/stm32f05xf07xf09x_memorymap.h index 7aed68d2dd..110db4b0e7 100644 --- a/arch/arm/src/stm32f0l0/hardware/stm32f05xf07xf09x_memorymap.h +++ b/arch/arm/src/stm32f0l0/hardware/stm32f05xf07xf09x_memorymap.h @@ -119,7 +119,7 @@ #define STM32_USART6_BASE 0x40011400 /* 0x40011400-0x400117ff USART6 */ #define STM32_USART7_BASE 0x40011800 /* 0x40011800-0x40011bff USART7 */ #define STM32_USART8_BASE 0x40011c00 /* 0x40011c00-0x40011fff USART8 */ -#define STM32_ADC1_BASE 0x40012400 /* 0x40012400-0x400127ff ADC 1 */ +#define STM32_ADC12_BASE 0x40012400 /* 0x40012400-0x400127ff ADC 12 */ #define STM32_TIM1_BASE 0x40012c00 /* 0x40012c00-0x40012fff TIM1 */ #define STM32_SPI1_BASE 0x40013000 /* 0x40013000-0x400133ff SPI1 */ #define STM32_USART1_BASE 0x40013800 /* 0x40013800-0x40013bff USART1 */ diff --git a/arch/arm/src/stm32f0l0/hardware/stm32l0_memorymap.h b/arch/arm/src/stm32f0l0/hardware/stm32l0_memorymap.h index 16ec89f634..b401c40c9f 100644 --- a/arch/arm/src/stm32f0l0/hardware/stm32l0_memorymap.h +++ b/arch/arm/src/stm32f0l0/hardware/stm32l0_memorymap.h @@ -103,7 +103,7 @@ #define STM32_TIM21_BASE 0x40010800 /* 0x40010800-0x40010bff TIM21 */ #define STM32_TIM22_BASE 0x40014000 /* 0x40014000-0x400117ff TIM22 */ #define STM32_FIREWALL_BASE 0x4001c000 /* 0x4001c000-0x400113ff Firewall */ -#define STM32_ADC1_BASE 0x40012400 /* 0x40012400-0x400127ff ADC1 */ +#define STM32_ADC12_BASE 0x40012400 /* 0x40012400-0x400127ff ADC12 */ #define STM32_SPI1_BASE 0x40013000 /* 0x40013000-0x400133ff SPI1 */ #define STM32_USART1_BASE 0x40013800 /* 0x40013800-0x40013bff USART1 */ #define STM32_DBGMCU_BASE 0x40015800 /* 0x40015800-0x40015bff DBGMCU */ diff --git a/arch/arm/src/stm32f0l0/stm32.h b/arch/arm/src/stm32f0l0/stm32.h index 20bac988fd..f222df2555 100644 --- a/arch/arm/src/stm32f0l0/stm32.h +++ b/arch/arm/src/stm32f0l0/stm32.h @@ -62,5 +62,6 @@ #include "stm32_spi.h" #include "stm32_uart.h" #include "stm32_lowputc.h" +#include "stm32_adc.h" #endif /* __ARCH_ARM_SRC_STM32F0L0_STM32_H */ diff --git a/arch/arm/src/stm32f0l0/stm32_adc.c b/arch/arm/src/stm32f0l0/stm32_adc.c new file mode 100644 index 0000000000..c88237fbc5 --- /dev/null +++ b/arch/arm/src/stm32f0l0/stm32_adc.c @@ -0,0 +1,2400 @@ +/**************************************************************************** + * arch/arm/src/stm32/stm32_adc.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Mateusz Szafoni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "up_internal.h" +#include "up_arch.h" + +#include "chip.h" +#include "stm32.h" +#include "stm32_dma.h" +#include "stm32_adc.h" + +/* STM32 ADC "lower-half" support must be enabled */ + +#ifdef CONFIG_STM32F0L0_ADC + +/* Some ADC peripheral must be enabled */ + +#if defined(CONFIG_STM32F0L0_ADC1) + +#if !defined(CONFIG_STM32F0L0_STM32L0) +# error Only L0 supported for now +#endif + +/* At the moment there is no proper implementation for timers external + * trigger. + */ + +#if defined(ADC_HAVE_TIMER) +# error not supported yet +#endif + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ +/* RCC reset ****************************************************************/ + +#define STM32_RCC_RSTR STM32_RCC_APB2RSTR +#define RCC_RSTR_ADC1RST RCC_APB2RSTR_ADC1RST + +/* 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 +# define ADC_MAX_SAMPLES ADC_MAX_CHANNELS_DMA +#else +# define ADC_MAX_SAMPLES ADC_MAX_CHANNELS_NODMA +#endif + +/* DMA channels and interface values differs according to STM32 DMA IP core version */ + +#if defined(HAVE_IP_DMA_V1) +# define ADC_DMA_CONTROL_WORD (DMA_CCR_MSIZE_16BITS | \ + DMA_CCR_PSIZE_16BITS | \ + DMA_CCR_MINC | \ + DMA_CCR_CIRC) +#else +# error Not supported +#endif + +/* Sample time default configuration */ + +/* G0 support additional sample time selection 2 */ + +#if defined(CONFIG_STM32F0L0_STM32G0) +# define ADC_HAVE_SMPR_SMP2 +#endif + +#if defined(ADC_HAVE_DMA) || (ADC_MAX_SAMPLES == 1) +# define ADC_SMP1_DEFAULT ADC_SMPR_13p5 +# define ADC_SMP2_DEFAULT ADC_SMPR_13p5 +#else /* Slow down sampling frequency */ +# define ADC_SMP1_DEFAULT ADC_SMPR_239p5 +# define ADC_SMP2_DEFAULT ADC_SMPR_239p5 +#endif + +#ifdef ADC_HAVE_SMPR_SMP2 +# define ADC_SMPSEL_DEFAULT 0 /* For now we use only SMP1 */ +#endif + +/* Number of channels per ADC: + * - F0, L0, G0 - 19, but singe SMP for all channels + * + * NOTE: this value can be obtained from SMPRx register description (ST manual) + */ + +#if defined(CONFIG_STM32F0L0_STM32F0) || defined(CONFIG_STM32F0L0_STM32L0) +# define ADC_CHANNELS_NUMBER 19 +#else +# error "Not supported" +#endif + +/* ADC resolution */ + +#define HAVE_ADC_RESOLUTION + +/* ADC have common registers but only single ADC */ + +#define HAVE_ADC_CMN_REGS 1 + +/* ADCx_EXTSEL_VALUE */ + +#ifdef ADC1_EXTSEL_VALUE +# define ADC1_HAVE_EXTCFG 1 +# define ADC1_EXTCFG_VALUE (ADC1_EXTSEL_VALUE | ADC_EXTREG_EXTEN_DEFAULT) +#else +# undef ADC1_HAVE_EXTCFG +#endif + +#if defined(ADC1_HAVE_EXTCFG) +# define ADC_HAVE_EXTCFG +#endif + +/* ADC DMA configuration bit support */ + +#define ADC_HAVE_DMACFG 1 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* Data common to all ADC instances */ + +#ifdef HAVE_ADC_CMN_DATA +struct adccmn_data_s +{ + uint8_t initialized; /* How many ADC instances are currently in use */ + sem_t lock; /* Exclusive access to common ADC data */ +}; +#endif + +/* This structure describes the state of one ADC block + * REVISIT: save some space with bit fields. + */ + +struct stm32_dev_s +{ +#ifdef CONFIG_STM32F0L0_ADC_LL_OPS + FAR const struct stm32_adc_ops_s *llops; /* Low-level ADC ops */ +#endif +#if !defined(CONFIG_STM32F0L0_ADC_NOIRQ) | defined(ADC_HAVE_DMA) + FAR const struct adc_callback_s *cb; + uint8_t irq; /* Interrupt generated by this ADC block */ +#endif +#ifdef HAVE_ADC_CMN_DATA + struct adccmn_data_s *cmn; /* Common ADC data */ +#endif + uint8_t rnchannels; /* Number of regular channels */ + uint8_t cr_channels; /* Number of configured regular channels */ + uint8_t intf; /* ADC interface number */ + uint8_t current; /* Current ADC channel being converted */ +#ifdef HAVE_ADC_RESOLUTION + uint8_t resolution; /* ADC resolution (0-3) */ +#endif +#ifdef ADC_HAVE_DMA + uint8_t dmachan; /* DMA channel needed by this ADC */ +# ifdef ADC_HAVE_DMACFG + uint8_t dmacfg; /* DMA channel configuration, only for ADC IPv2 */ +# endif + bool hasdma; /* True: This channel supports DMA */ +#endif +#ifdef CONFIG_STM32F0L0_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 */ +#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_TIMER + 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 ADC_HAVE_DMA + DMA_HANDLE dma; /* Allocated DMA channel */ + + /* DMA transfer buffer */ + + uint16_t r_dmabuffer[ADC_MAX_SAMPLES]; +#endif + + /* List of selected ADC channels to sample */ + + uint8_t r_chanlist[ADC_MAX_SAMPLES]; +}; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* ADC Register access */ + +static void stm32_modifyreg32(unsigned int addr, uint32_t clrbits, + uint32_t setbits); +static uint32_t adc_getreg(FAR struct stm32_dev_s *priv, int offset); +static void adc_putreg(FAR struct stm32_dev_s *priv, int offset, + uint32_t value); +static void adc_modifyreg(FAR struct stm32_dev_s *priv, int offset, + uint32_t clrbits, uint32_t setbits); +#ifdef HAVE_ADC_CMN_REGS +static uint32_t adccmn_base_get(FAR struct stm32_dev_s *priv); +static void adccmn_modifyreg(FAR struct stm32_dev_s *priv, uint32_t offset, + uint32_t clrbits, uint32_t setbits); +# ifdef CONFIG_DEBUG_ANALOG_INFO +static uint32_t adccmn_getreg(FAR struct stm32_dev_s *priv, uint32_t offset); +# endif +#endif +#ifdef ADC_HAVE_TIMER +static uint16_t tim_getreg(FAR struct stm32_dev_s *priv, int offset); +static void tim_putreg(FAR struct stm32_dev_s *priv, int offset, + uint16_t value); +static void tim_modifyreg(FAR struct stm32_dev_s *priv, int offset, + uint16_t clrbits, uint16_t setbits); +static void tim_dumpregs(FAR struct stm32_dev_s *priv, + FAR const char *msg); +#endif + +#ifdef HAVE_ADC_CMN_DATA +static int adccmn_lock(FAR struct stm32_dev_s *priv, bool lock); +#endif + +static void adc_rccreset(FAR struct stm32_dev_s *priv, bool reset); + +/* ADC Interrupt Handler */ + +#ifndef CONFIG_STM32F0L0_ADC_NOIRQ +static int adc_interrupt(FAR struct adc_dev_s *dev); +static int adc1_interrupt(int irq, FAR void *context, FAR void *arg); +#endif /* CONFIG_STM32F0L0_ADC_NOIRQ */ + +/* ADC Driver Methods */ + +static int adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback); +static void adc_reset(FAR struct adc_dev_s *dev); +static int adc_setup(FAR struct adc_dev_s *dev); +static void adc_shutdown(FAR struct adc_dev_s *dev); +static void adc_rxint(FAR struct adc_dev_s *dev, bool enable); +static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg); +static void adc_enable(FAR struct stm32_dev_s *priv, bool enable); + +static int adc_set_ch(FAR struct adc_dev_s *dev, uint8_t ch); + +static int adc_ioc_change_ints(FAR struct adc_dev_s *dev, int cmd, + bool arg); + +#ifdef HAVE_ADC_RESOLUTION +static int adc_resolution_set(FAR struct adc_dev_s *dev, uint8_t res); +#endif +#ifdef HAVE_ADC_VBAT +static void adc_enable_vbat_channel(FAR struct adc_dev_s *dev, bool enable); +#endif +#ifdef HAVE_ADC_POWERDOWN +static int adc_ioc_change_sleep_between_opers(FAR struct adc_dev_s *dev, + int cmd, bool arg); +static void adc_power_down_idle(FAR struct stm32_dev_s *priv, + bool pdi_high); +static void adc_power_down_delay(FAR struct stm32_dev_s *priv, + bool pdd_high); +#endif + +#ifdef ADC_HAVE_TIMER +static void adc_timstart(FAR struct stm32_dev_s *priv, bool enable); +static int adc_timinit(FAR struct stm32_dev_s *priv); +#endif + +#if defined(ADC_HAVE_DMA) +static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t isr, + FAR void *arg); +#endif + +static void adc_reg_startconv(FAR struct stm32_dev_s *priv, bool enable); + +#ifdef ADC_HAVE_EXTCFG +static int adc_extcfg_set(FAR struct adc_dev_s *dev, uint32_t extcfg); +#endif + +static void adc_dumpregs(FAR struct stm32_dev_s *priv); + +#ifdef CONFIG_STM32F0L0_ADC_LL_OPS +static void adc_llops_intack(FAR struct stm32_adc_dev_s *dev, uint32_t source); +static void adc_llops_inten(FAR struct stm32_adc_dev_s *dev, uint32_t source); +static void adc_llops_intdis(FAR struct stm32_adc_dev_s *dev, uint32_t source); +static uint32_t adc_llops_intget(FAR struct stm32_adc_dev_s *dev); +static uint32_t adc_llops_regget(FAR struct stm32_adc_dev_s *dev); +static void adc_llops_reg_startconv(FAR struct stm32_adc_dev_s *dev, bool enable); +# ifdef ADC_HAVE_DMA +static int adc_llops_regbufregister(FAR struct stm32_adc_dev_s *dev, + uint16_t *buffer, uint8_t len); +# endif +# ifdef CONFIG_STM32F0L0_ADC_CHANGE_SAMPLETIME +static void adc_sampletime_set(FAR struct stm32_adc_dev_s *dev, + FAR struct adc_sample_time_s *time_samples); +static void adc_sampletime_write(FAR struct stm32_adc_dev_s *dev); +# endif +static void adc_llops_dumpregs(FAR struct stm32_adc_dev_s *dev); +#endif + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* ADC interface operations */ + +static const struct adc_ops_s g_adcops = +{ + .ao_bind = adc_bind, + .ao_reset = adc_reset, + .ao_setup = adc_setup, + .ao_shutdown = adc_shutdown, + .ao_rxint = adc_rxint, + .ao_ioctl = adc_ioctl, +}; + +/* Publicly visible ADC lower-half operations */ + +#ifdef CONFIG_STM32F0L0_ADC_LL_OPS +static const struct stm32_adc_ops_s g_adc_llops = +{ + .int_ack = adc_llops_intack, + .int_get = adc_llops_intget, + .int_en = adc_llops_inten, + .int_dis = adc_llops_intdis, + .val_get = adc_llops_regget, + .reg_startconv = adc_llops_reg_startconv, +# ifdef ADC_HAVE_DMA + .regbuf_reg = adc_llops_regbufregister, +# endif +# ifdef CONFIG_STM32F0L0_ADC_CHANGE_SAMPLETIME + .stime_set = adc_sampletime_set, + .stime_write = adc_sampletime_write, +# endif + .dump_regs = adc_llops_dumpregs +}; +#endif + +/* ADC1 state */ + +#ifdef CONFIG_STM32F0L0_ADC1 +static struct stm32_dev_s g_adcpriv1 = +{ +#ifdef CONFIG_STM32F0L0_ADC_LL_OPS + .llops = &g_adc_llops, +#endif +#ifndef CONFIG_STM32F0L0_ADC_NOIRQ + .irq = STM32_IRQ_ADC, + .isr = adc1_interrupt, +#endif /* CONFIG_STM32F0L0_ADC_NOIRQ */ +#ifdef HAVE_ADC_CMN_DATA + .cmn = &ADC1CMN_DATA, +#endif + .intf = 1, +#ifdef HAVE_ADC_RESOLUTION + .resolution = CONFIG_STM32F0L0_ADC1_RESOLUTION, +#endif + .base = STM32_ADC1_BASE, +#ifdef ADC1_HAVE_EXTCFG + .extcfg = ADC1_EXTCFG_VALUE, +#endif +#ifdef ADC1_HAVE_TIMER + .trigger = CONFIG_STM32F0L0_ADC1_TIMTRIG, + .tbase = ADC1_TIMER_BASE, + .pclck = ADC1_TIMER_PCLK_FREQUENCY, + .freq = CONFIG_STM32F0L0_ADC1_SAMPLE_FREQUENCY, +#endif +#ifdef ADC1_HAVE_DMA + .dmachan = ADC1_DMA_CHAN, +# ifdef ADC_HAVE_DMACFG + .dmacfg = CONFIG_STM32F0L0_ADC1_DMA_CFG, +# endif + .hasdma = true, +#endif +}; + +static struct adc_dev_s g_adcdev1 = +{ + .ad_ops = &g_adcops, + .ad_priv = &g_adcpriv1, +}; +#endif + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_modifyreg32 + * + * Description: + * Modify the value of a 32-bit register (not atomic). + * + * Input Parameters: + * addr - The address of the register + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void stm32_modifyreg32(unsigned int addr, uint32_t clrbits, + uint32_t setbits) +{ + putreg32((getreg32(addr) & ~clrbits) | setbits, addr); +} + +/**************************************************************************** + * Name: adc_getreg + * + * Description: + * Read the value of an ADC register. + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to read + * + * Returned Value: + * The current contents of the specified register + * + ****************************************************************************/ + +static uint32_t adc_getreg(FAR struct stm32_dev_s *priv, int offset) +{ + return getreg32(priv->base + offset); +} + +/**************************************************************************** + * Name: adc_putreg + * + * Description: + * Write a value to an ADC register. + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to write to + * value - The value to write to the register + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void adc_putreg(FAR struct stm32_dev_s *priv, int offset, + uint32_t value) +{ + putreg32(value, priv->base + offset); +} + +/**************************************************************************** + * Name: adc_modifyreg + * + * Description: + * Modify the value of an ADC register (not atomic). + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to modify + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +static void adc_modifyreg(FAR struct stm32_dev_s *priv, int offset, + uint32_t clrbits, uint32_t setbits) +{ + adc_putreg(priv, offset, (adc_getreg(priv, offset) & ~clrbits) | setbits); +} + +#ifdef HAVE_ADC_CMN_REGS + +/**************************************************************************** + * Name: adccmn_base_get + ****************************************************************************/ + +static uint32_t adccmn_base_get(FAR struct stm32_dev_s *priv) +{ + uint32_t base = 0; + + if (priv->base == STM32_ADC1_BASE) + { + base = STM32_ADC12CMN_BASE; + } + + return base; +} + +/**************************************************************************** + * Name: adccmn_modifyreg + ****************************************************************************/ + +static void adccmn_modifyreg(FAR struct stm32_dev_s *priv, uint32_t offset, + uint32_t clrbits, uint32_t setbits) +{ + uint32_t base = 0; + + /* Get base address for ADC common register */ + + base = adccmn_base_get(priv); + + /* Modify register */ + + stm32_modifyreg32(offset + base, clrbits, setbits); +} + +/**************************************************************************** + * Name: adccmn_getreg + ****************************************************************************/ + +# ifdef CONFIG_DEBUG_ANALOG_INFO +static uint32_t adccmn_getreg(FAR struct stm32_dev_s *priv, uint32_t offset) +{ + uint32_t base = 0; + + /* Get base address for ADC common register */ + + base = adccmn_base_get(priv); + + /* Return register value */ + + return getreg32(base+offset); +} +# endif +#endif /* HAVE_ADC_CMN_REGS */ + +/**************************************************************************** + * Name: tim_getreg + * + * Description: + * Read the value of an ADC timer register. + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to read + * + * Returned Value: + * The current contents of the specified register + * + ****************************************************************************/ + +#ifdef ADC_HAVE_TIMER +static uint16_t tim_getreg(FAR struct stm32_dev_s *priv, int offset) +{ + return getreg16(priv->tbase + offset); +} +#endif + +/**************************************************************************** + * Name: tim_putreg + * + * Description: + * Write a value to an ADC timer register. + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to write to + * value - The value to write to the register + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef ADC_HAVE_TIMER +static void tim_putreg(FAR struct stm32_dev_s *priv, int offset, + uint16_t value) +{ + putreg16(value, priv->tbase + offset); +} +#endif + +/**************************************************************************** + * Name: tim_modifyreg + * + * Description: + * Modify the value of an ADC timer register (not atomic). + * + * Input Parameters: + * priv - A reference to the ADC block status + * offset - The offset to the register to modify + * clrbits - The bits to clear + * setbits - The bits to set + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef ADC_HAVE_TIMER +static void tim_modifyreg(FAR struct stm32_dev_s *priv, int offset, + uint16_t clrbits, uint16_t setbits) +{ + tim_putreg(priv, offset, (tim_getreg(priv, offset) & ~clrbits) | setbits); +} +#endif + +/**************************************************************************** + * Name: tim_dumpregs + * + * Description: + * Dump all timer registers. + * + * Input Parameters: + * priv - A reference to the ADC block status + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef ADC_HAVE_TIMER +static void tim_dumpregs(FAR struct stm32_dev_s *priv, FAR const char *msg) +{ + ainfo("%s:\n", msg); + ainfo(" CR1: %04x CR2: %04x SMCR: %04x DIER: %04x\n", + tim_getreg(priv, STM32_GTIM_CR1_OFFSET), + tim_getreg(priv, STM32_GTIM_CR2_OFFSET), + tim_getreg(priv, STM32_GTIM_SMCR_OFFSET), + tim_getreg(priv, STM32_GTIM_DIER_OFFSET)); + ainfo(" SR: %04x EGR: 0000 CCMR1: %04x CCMR2: %04x\n", + tim_getreg(priv, STM32_GTIM_SR_OFFSET), + tim_getreg(priv, STM32_GTIM_CCMR1_OFFSET), + tim_getreg(priv, STM32_GTIM_CCMR2_OFFSET)); + ainfo(" CCER: %04x CNT: %04x PSC: %04x ARR: %04x\n", + tim_getreg(priv, STM32_GTIM_CCER_OFFSET), + tim_getreg(priv, STM32_GTIM_CNT_OFFSET), + tim_getreg(priv, STM32_GTIM_PSC_OFFSET), + tim_getreg(priv, STM32_GTIM_ARR_OFFSET)); + ainfo(" CCR1: %04x CCR2: %04x CCR3: %04x CCR4: %04x\n", + tim_getreg(priv, STM32_GTIM_CCR1_OFFSET), + tim_getreg(priv, STM32_GTIM_CCR2_OFFSET), + tim_getreg(priv, STM32_GTIM_CCR3_OFFSET), + tim_getreg(priv, STM32_GTIM_CCR4_OFFSET)); +#if STM32_NATIM > 0 + if (priv->tbase == STM32_TIM1_BASE) + { + ainfo(" RCR: %04x BDTR: %04x DCR: %04x DMAR: %04x\n", + tim_getreg(priv, STM32_ATIM_RCR_OFFSET), + tim_getreg(priv, STM32_ATIM_BDTR_OFFSET), + tim_getreg(priv, STM32_ATIM_DCR_OFFSET), + tim_getreg(priv, STM32_ATIM_DMAR_OFFSET)); + } + else +#endif + { + ainfo(" DCR: %04x DMAR: %04x\n", + tim_getreg(priv, STM32_GTIM_DCR_OFFSET), + tim_getreg(priv, STM32_GTIM_DMAR_OFFSET)); + } +} +#endif + +/**************************************************************************** + * Name: adc_timstart + * + * Description: + * Start (or stop) the timer counter + * + * Input Parameters: + * priv - A reference to the ADC block status + * enable - True: Start conversion + * + * Returned Value: + * + ****************************************************************************/ + +#ifdef ADC_HAVE_TIMER +static void adc_timstart(FAR struct stm32_dev_s *priv, bool enable) +{ + ainfo("enable: %d\n", enable ? 1 : 0); + + if (enable) + { + /* Start the counter */ + + tim_modifyreg(priv, STM32_GTIM_CR1_OFFSET, 0, GTIM_CR1_CEN); + } + else + { + /* Disable the counter */ + + tim_modifyreg(priv, STM32_GTIM_CR1_OFFSET, GTIM_CR1_CEN, 0); + } +} +#endif + +/**************************************************************************** + * Name: adc_timinit + * + * Description: + * Initialize the timer that drivers the ADC sampling for this channel + * using the pre-calculated timer divider definitions. + * + * Input Parameters: + * priv - A reference to the ADC block status + * + * Returned Value: + * Zero on success; a negated errno value on failure. + * + ****************************************************************************/ + +#ifdef ADC_HAVE_TIMER +static int adc_timinit(FAR struct stm32_dev_s *priv) +{ +#warning TODO: adc_timinit +} +#endif + +/**************************************************************************** + * Name: adc_reg_startconv + * + * Description: + * Start (or stop) the ADC regular conversion process + * + * Input Parameters: + * priv - A reference to the ADC block status + * enable - True: Start conversion + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_reg_startconv(FAR struct stm32_dev_s *priv, bool enable) +{ + uint32_t regval; + + ainfo("reg enable: %d\n", enable ? 1 : 0); + + if (enable) + { + /* Start the conversion of regular channels */ + + adc_modifyreg(priv, STM32_ADC_CR_OFFSET, 0, ADC_CR_ADSTART); + } + else + { + regval = adc_getreg(priv, STM32_ADC_CR_OFFSET); + + /* Is a conversion ongoing? */ + + if ((regval & ADC_CR_ADSTART) != 0) + { + /* Stop the conversion */ + + adc_putreg(priv, STM32_ADC_CR_OFFSET, regval | ADC_CR_ADSTP); + + /* Wait for the conversion to stop */ + + while ((adc_getreg(priv, STM32_ADC_CR_OFFSET) & ADC_CR_ADSTP) != 0); + } + } +} + +/**************************************************************************** + * Name: adccmn_lock + ****************************************************************************/ + +#ifdef HAVE_ADC_CMN_DATA +static int adccmn_lock(FAR struct stm32_dev_s *priv, bool lock) +{ + int ret; + + if (lock) + { + /* Take the semaphore (perhaps waiting) */ + + do + { + ret = nxsem_wait(&priv->cmn->lock); + + /* The only case that an error should occur here is if the wait + * was awakened by a signal. + */ + + DEBUGASSERT(ret == OK || ret == -EINTR); + } + while (ret == -EINTR); + } + else + { + (void)nxsem_post(&priv->cmn->lock); + ret = OK; + } + + return ret; +} +#endif + +/**************************************************************************** + * Name: adc_rccreset + * + * Description: + * Deinitializes the ADCx peripheral registers to their default + * reset values. It could set all the ADCs configured. + * + * Input Parameters: + * regaddr - The register to read + * reset - Condition, set or reset + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_rccreset(FAR struct stm32_dev_s *priv, bool reset) +{ + uint32_t adcbit; + + /* Pick the appropriate bit in the RCC reset register. + * For the STM32 ADC IPv2, there is an individual bit to reset each ADC block. + */ + + switch (priv->intf) + { +#if defined(CONFIG_STM32F0L0_ADC1) + case 1: + { + adcbit = RCC_RSTR_ADC1RST; + break; + } +#endif + default: + { + return; + } + } + + /* Set or clear the selected bit in the RCC reset register */ + + if (reset) + { + /* Enable ADC reset state */ + + modifyreg32(STM32_RCC_RSTR, 0, adcbit); + } + else + { + /* Release ADC from reset state */ + + modifyreg32(STM32_RCC_RSTR, adcbit, 0); + } +} + +/**************************************************************************** + * Name: adc_enable + * + * Description: + * Enables or disables the specified ADC peripheral. Also, starts a + * conversion when the ADC is not triggered by timers + * + * Input Parameters: + * + * enable - true: enable ADC conversion + * false: disable ADC conversion + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_enable(FAR struct stm32_dev_s *priv, bool enable) +{ + uint32_t regval; + + ainfo("enable: %d\n", enable ? 1 : 0); + + regval = adc_getreg(priv, STM32_ADC_CR_OFFSET); + + if (enable) + { + /* Enable the ADC */ + + adc_putreg(priv, STM32_ADC_CR_OFFSET, regval | ADC_CR_ADEN); + + /* Wait for the ADC to be ready */ + + while ((adc_getreg(priv, STM32_ADC_ISR_OFFSET) & ADC_INT_ARDY) == 0); + } + else if ((regval & ADC_CR_ADEN) != 0 && (regval & ADC_CR_ADDIS) == 0) + { + /* Stop ongoing regular conversions */ + + adc_reg_startconv(priv, false); + + /* Disable the ADC */ + + adc_putreg(priv, STM32_ADC_CR_OFFSET, regval | ADC_CR_ADDIS); + + /* Wait for the ADC to be disabled */ + + while ((adc_getreg(priv, STM32_ADC_CR_OFFSET) & ADC_CR_ADEN) != 0); + } +} + +/**************************************************************************** + * Name: adc_dmaconvcallback + * + * Description: + * Callback for DMA. Called from the DMA transfer complete interrupt after + * all channels have been converted and transferred with DMA. + * + * Input Parameters: + * + * handle - handle to DMA + * isr - + * arg - adc device + * + * Returned Value: + * + ****************************************************************************/ + +#if defined(ADC_HAVE_DMA) +static void adc_dmaconvcallback(DMA_HANDLE handle, uint8_t isr, FAR void *arg) +{ + FAR struct adc_dev_s *dev = (FAR struct adc_dev_s *)arg; + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + int i; + + /* Verify that the upper-half driver has bound its callback functions */ + + if (priv->cb != NULL) + { + DEBUGASSERT(priv->cb->au_receive != NULL); + + for (i = 0; i < priv->rnchannels; i++) + { + priv->cb->au_receive(dev, priv->r_chanlist[priv->current], + priv->r_dmabuffer[priv->current]); + priv->current++; + if (priv->current >= priv->rnchannels) + { + /* Restart the conversion sequence from the beginning */ + + priv->current = 0; + } + } + } + + /* Restart DMA for the next conversion series */ + + adc_modifyreg(priv, STM32_ADC_DMAREG_OFFSET, ADC_DMAREG_DMA, 0); + adc_modifyreg(priv, STM32_ADC_DMAREG_OFFSET, 0, ADC_DMAREG_DMA); +} +#endif + +/**************************************************************************** + * Name: adc_bind + * + * Description: + * Bind the upper-half driver callbacks to the lower-half implementation. + * This must be called early in order to receive ADC event notifications. + * + ****************************************************************************/ + +static int adc_bind(FAR struct adc_dev_s *dev, + FAR const struct adc_callback_s *callback) +{ +#ifndef CONFIG_STM32F0L0_ADC_NOIRQ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + + DEBUGASSERT(priv != NULL); + priv->cb = callback; +#endif + + return OK; +} + +/**************************************************************************** + * Name: adc_watchdog_cfg + ****************************************************************************/ + +static void adc_watchdog_cfg(FAR struct stm32_dev_s *priv) +{ + uint32_t clrbits = 0; + uint32_t setbits = 0; + + uint32_t th = 0; + + /* Initialize the watchdog high threshold register */ + + th |= 0x0fff << ADC_TR_HT_SHIFT; + + /* Initialize the watchdog low threshold register */ + + th |= 0x0000 << ADC_TR_LT_SHIFT; + + /* Write threshold register */ + + adc_putreg(priv, STM32_ADC_TR_OFFSET, th); + + clrbits = ADC_CFGR1_AWDCH_MASK; + setbits = ADC_CFGR1_AWDEN | (priv->r_chanlist[0] << ADC_CFGR1_AWDCH_SHIFT); + + /* Modify CFGR1 configuration */ + + adc_modifyreg(priv, STM32_ADC_CFGR1_OFFSET, clrbits, setbits); +} + +/**************************************************************************** + * Name: adc_calibrate + ****************************************************************************/ + +static void adc_calibrate(FAR struct stm32_dev_s *priv) +{ +#if 0 /* Doesn't work */ + /* Calibrate the ADC */ + + adc_modifyreg(priv, STM32_ADC_CR_OFFSET, ADC_CR_ADCALDIF, AD_CR_ADCAL); + + /* Wait for the calibration to complete */ + + while ((adc_getreg(priv, STM32_ADC_CR_OFFSET) & ADC_CR_ADCAL) != 0); + +#else + UNUSED(priv); +#endif +} + +/**************************************************************************** + * Name: adc_mode_cfg + ****************************************************************************/ + +static void adc_mode_cfg(FAR struct stm32_dev_s *priv) +{ + uint32_t clrbits = 0; + uint32_t setbits = 0; + + /* Disable continuous mode and set align to right */ + + clrbits = ADC_CFGR1_CONT | ADC_CFGR1_ALIGN; + + /* Disable external trigger for regular channels */ + + clrbits |= ADC_CFGR1_EXTEN_MASK; + setbits |= ADC_CFGR1_EXTEN_NONE; + + /* Set CFGR configuration */ + + adc_modifyreg(priv, STM32_ADC_CFGR1_OFFSET, clrbits, setbits); +} + +/**************************************************************************** + * Name: adc_voltreg_cfg + ****************************************************************************/ + +static void adc_voltreg_cfg(FAR struct stm32_dev_s *priv) +{ + UNUSED(priv); +} + +/**************************************************************************** + * Name: adc_sampletime_cfg + ****************************************************************************/ + +static void adc_sampletime_cfg(FAR struct adc_dev_s *dev) +{ + /* Initialize the same sample time for each ADC. + * During sample cycles channel selection bits must remain unchanged. + */ + +#ifdef CONFIG_STM32F0L0_ADC_CHANGE_SAMPLETIME + adc_sampletime_write((FAR struct stm32_adc_dev_s *)dev); +#else + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + uint32_t setbits = 0; + + /* Configure sample time 1 */ + + setbits |= ADC_SMP1_DEFAULT << ADC_SMPR_SMP1_SHIFT; + +# ifdef ADC_HAVE_SMPR_SMP2 + /* Configure sample time 2 */ + + setbits |= ADC_SMP2_DEFAULT << ADC_SMPR_SMP2_SHIFT; + + /* Configure sample time selection */ + + setbits |= ADC_SMPSEL_DEFAULT << ADC_SMPR_SMPSEL_SHIFT; +# endif + + /* Write SMPR register */ + + adc_putreg(priv, STM32_ADC_SMPR_OFFSET, setbits); + +#endif +} + +/**************************************************************************** + * Name: adc_common_cfg + ****************************************************************************/ + +static void adc_common_cfg(FAR struct stm32_dev_s *priv) +{ + uint32_t clrbits = 0; + uint32_t setbits = 0; + + /* REVISIT: for now we reset all CCR bits */ + + clrbits |= ADC_CCR_VREFEN; + clrbits |= ADC_CCR_TSEN; + +#ifdef HAVE_ADC_VLCD + clrbits |= ADC_CCR_PRESC_MASK; +#endif + +#ifdef HAVE_ADC_VLCD + clrbits |= ADC_CCR_VLCDEN; +#endif + +#ifdef HAVE_ADC_LFM + clrbits |= ADC_CCR_LFMEN; +#endif + + setbits = 0; + + adccmn_modifyreg(priv, STM32_ADC_CCR_OFFSET, clrbits, setbits); +} + +#ifdef ADC_HAVE_DMA +/**************************************************************************** + * Name: adc_dma_cfg + ****************************************************************************/ + +static void adc_dma_cfg(FAR struct stm32_dev_s *priv) +{ + uint32_t clrbits = 0; + uint32_t setbits = 0; + + /* Set DMA mode */ + + if (priv->dmacfg == 0) + { + /* One Shot Mode */ + + clrbits |= ADC_CFGR1_DMACFG; + } + else + { + /* Circular Mode */ + + setbits |= ADC_CFGR1_DMACFG; + } + + /* Enable DMA */ + + setbits |= ADC_CFGR1_DMAEN; + + /* Modify CFGR configuration */ + + adc_modifyreg(priv, STM32_ADC_CFGR1_OFFSET, clrbits, setbits); +} + +/**************************************************************************** + * Name: adc_dma_start + ****************************************************************************/ + +static void adc_dma_start(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR 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); + + 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 /* ADC_HAVE_DMA */ + +/**************************************************************************** + * Name: adc_configure + ****************************************************************************/ + +static void adc_configure(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + + /* Turn off the ADC before configuration */ + + adc_enable(priv, false); + + /* Configure voltage regulator if present */ + + adc_voltreg_cfg(priv); + + /* Calibrate ADC - doesnt work for now */ + + adc_calibrate(priv); + + /* 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) + { + adc_set_ch(dev, 0); + } + + /* ADC common register configuration */ + + adc_common_cfg(priv); + +#ifdef ADC_HAVE_DMA + /* Configure ADC DMA if enabled */ + + if (priv->hasdma) + { + /* Configure ADC DMA */ + + adc_dma_cfg(priv); + + /* Start ADC DMA */ + + adc_dma_start(dev); + } +#endif + +#ifdef HAVE_ADC_RESOLUTION + /* Configure ADC resolution */ + + (void)adc_resolution_set(dev, priv->resolution); +#endif + +#ifdef ADC_HAVE_EXTCFG + /* Configure external event for regular group */ + + adc_extcfg_set(dev, priv->extcfg); +#endif + + /* Enable ADC */ + + adc_enable(priv, true); + + /* 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(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + irqstate_t flags; + + ainfo("intf: %d\n", priv->intf); + flags = enter_critical_section(); + +#if defined(HAVE_IP_ADC_V2) + /* Turn off the ADC so we can write the RCC bits */ + + adc_enable(priv, false); +#endif + + /* Only if this is the first initialzied ADC instance in the ADC block */ + +#ifdef HAVE_ADC_CMN_DATA + adccmn_lock(priv, true); + + if (priv->cmn->initialized == 0) +#endif + { + /* Enable ADC reset state */ + + adc_rccreset(priv, true); + + /* Release ADC from reset state */ + + adc_rccreset(priv, false); + } + +#ifdef HAVE_ADC_CMN_DATA + adccmn_lock(priv, false); +#endif + + leave_critical_section(flags); +} + +/**************************************************************************** + * Name: adc_setup + * + * Description: + * Configure the ADC. This method is called the first time that the ADC + * device is opened. This will occur when the port is first opened. + * This setup includes configuring and attaching ADC interrupts. + * Interrupts are all disabled upon return. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int adc_setup(FAR struct adc_dev_s *dev) +{ +#if !defined(CONFIG_STM32F0L0_ADC_NOIRQ) || defined(HAVE_ADC_CMN_DATA) || \ + defined(ADC_HAVE_TIMER) || !defined(CONFIG_STM32F0L0_ADC_NO_STARTUP_CONV) + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; +#endif + int ret = OK; + + /* Attach the ADC interrupt */ + +#ifndef CONFIG_STM32F0L0_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); + + /* Configure ADC device */ + + 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. + * + * NOTE: for ADC IPv2 (J)ADSTART bit must be set to start ADC conversion + * even if hardware trigger is selected. + * This can be done here during the opening of the ADC device + * or later with ANIOC_TRIGGER ioctl call. + */ + +#ifndef CONFIG_STM32F0L0_ADC_NO_STARTUP_CONV + /* Start regular conversion */ + + adc_reg_startconv(priv, true); + +#endif + + /* Enable the ADC interrupt */ + +#ifndef CONFIG_STM32F0L0_ADC_NOIRQ + ainfo("Enable the ADC interrupt: irq=%d\n", priv->irq); + up_enable_irq(priv->irq); +#endif + +#ifdef HAVE_ADC_CMN_DATA + /* Increase instances counter */ + + adccmn_lock(priv, true); + priv->cmn->initialized += 1; + adccmn_lock(priv, false); +#endif + + return ret; +} + +/**************************************************************************** + * Name: adc_shutdown + * + * Description: + * Disable the ADC. This method is called when the ADC device is closed. + * This method reverses the operation the setup method. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_shutdown(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + + /* Disable ADC */ + + adc_enable(priv, false); + +#ifndef CONFIG_STM32F0L0_ADC_NOIRQ + /* Disable ADC interrupts and detach the ADC interrupt handler */ + + up_disable_irq(priv->irq); + irq_detach(priv->irq); +#endif + +#ifdef HAVE_ADC_CMN_DATA + adccmn_lock(priv, true); + + if (priv->cmn->initialized <= 1) +#endif + { + /* 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 + +#ifdef HAVE_ADC_CMN_DATA + /* Decrease instances counter */ + + priv->cmn->initialized -= 1; + + adccmn_lock(priv, false); +#endif +} + +/**************************************************************************** + * Name: adc_rxint + * + * Description: + * Call to enable or disable RX interrupts. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_rxint(FAR struct adc_dev_s *dev, bool enable) +{ + FAR struct stm32_dev_s *priv = (FAR 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 analog watchdog / overrun interrupts, and if no DMA, + * end-of-conversion ADC. + */ + + regval = ADC_IER_ALLINTS; +#ifdef ADC_HAVE_DMA + if (priv->hasdma) + { + regval &= ~(ADC_IER_EOC); + } +#endif + + adc_modifyreg(priv, STM32_ADC_IER_OFFSET, 0, regval); + } + else + { + /* Disable all ADC interrupts */ + + adc_modifyreg(priv, STM32_ADC_IER_OFFSET, ADC_IER_ALLINTS, 0); + } +} + +/**************************************************************************** + * Name: adc_resolution_set + ****************************************************************************/ + +#ifdef HAVE_ADC_RESOLUTION +static int adc_resolution_set(FAR struct adc_dev_s *dev, uint8_t res) +{ + FAR struct stm32_dev_s *priv = (FAR 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_CFGR1_OFFSET, ADC_CFGR1_RES_MASK, + res << ADC_CFGR1_RES_SHIFT); + +errout: + return ret; +} +#endif + +/**************************************************************************** + * Name: adc_extsel_set + ****************************************************************************/ + +#ifdef ADC_HAVE_EXTCFG +static int adc_extcfg_set(FAR struct adc_dev_s *dev, uint32_t extcfg) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + 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%08x\n", extsel); + + /* Write register */ + + adc_modifyreg(priv, STM32_ADC_EXTREG_OFFSET, clrbits, setbits); + } + + return OK; +} +#endif + +/**************************************************************************** + * Name: adc_dumpregs + ****************************************************************************/ + +static void adc_dumpregs(FAR struct stm32_dev_s *priv) +{ + UNUSED(priv); + + ainfo("ISR: 0x%08x IER: 0x%08x CR: 0x%08x CFGR1: 0x%08x\n", + adc_getreg(priv, STM32_ADC_ISR_OFFSET), + adc_getreg(priv, STM32_ADC_IER_OFFSET), + adc_getreg(priv, STM32_ADC_CR_OFFSET), + adc_getreg(priv, STM32_ADC_CFGR1_OFFSET)); + + ainfo("SMPR: 0x%08x CHSELR: 0x%08x\n", + adc_getreg(priv, STM32_ADC_SMPR_OFFSET), + adc_getreg(priv, STM32_ADC_CHSELR_OFFSET)); + + ainfo("CCR: 0x%08x\n", adccmn_getreg(priv, STM32_ADC_CCR_OFFSET)); +} + +/**************************************************************************** + * Name: adc_enable_vbat_channel + * + * Description: + * Enable/disable the Vbat voltage measurement channel. + * + * Input Parameters: + * dev - pointer to device structure used by the driver + * enable - true: Vbat input channel enabled (ch 18) + * false: Vbat input channel disabled (ch 18) + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef HAVE_ADC_VBAT +static void adc_enable_vbat_channel(FAR struct adc_dev_s *dev, bool enable) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + + if (enable) + { + adccmn_modifyreg(priv, STM32_ADC_CCR_OFFSET, 0, ADC_CCR_VBATEN); + } + else + { + adccmn_modifyreg(priv, STM32_ADC_CCR_OFFSET, ADC_CCR_VBATEN, 0); + } + + ainfo("STM32_ADC_CCR value: 0x%08x\n", adccmn_getreg(priv, STM32_ADC_CCR_OFFSET)); +} +#endif + +/**************************************************************************** + * Name: adc_ioc_change_sleep_between_opers + * + * Description: + * Changes PDI and PDD bits to save battery. + * + * Input Parameters: + * dev - pointer to device structure used by the driver + * cmd - command + * arg - arguments passed with command + * + * Returned Value: + * + ****************************************************************************/ + +#ifdef HAVE_ADC_POWERDOWN +static int adc_ioc_change_sleep_between_opers(FAR struct adc_dev_s *dev, + int cmd, bool arg) +{ + int ret = OK; + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + + adc_enable(priv, false); + + switch (cmd) + { + case IO_ENABLE_DISABLE_PDI: + adc_power_down_idle(priv, arg); + break; + + case IO_ENABLE_DISABLE_PDD: + adc_power_down_delay(priv, arg); + break; + + case IO_ENABLE_DISABLE_PDD_PDI: + adc_power_down_idle(priv, arg); + adc_power_down_delay(priv, arg); + break; + + default: + ainfo("unknown cmd: %d\n", cmd); + break; + } + + adc_enable(priv, true); + + return ret; +} +#endif + +/**************************************************************************** + * Name: adc_ioc_enable_awd_int + * + * Description: + * Turns ON/OFF ADC analog watchdog interrupt. + * + * Input Parameters: + * dev - pointer to device structure used by the driver + * arg - true: Turn ON interrupt + * false: Turn OFF interrupt + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_ioc_enable_awd_int(FAR struct stm32_dev_s *priv, bool enable) +{ + if (enable) + { + adc_modifyreg(priv, STM32_ADC_IER_OFFSET, 0, ADC_IER_AWD); + } + else + { + adc_modifyreg(priv, STM32_ADC_IER_OFFSET, ADC_IER_AWD, 0); + } +} + +/**************************************************************************** + * Name: adc_ioc_enable_eoc_int + * + * Description: + * Turns ON/OFF ADC EOC interrupt. + * + * Input Parameters: + * dev - pointer to device structure used by the driver + * arg - true: Turn ON interrupt + * false: Turn OFF interrupt + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_ioc_enable_eoc_int(FAR struct stm32_dev_s *priv, bool enable) +{ + if (enable) + { + adc_modifyreg(priv, STM32_ADC_IER_OFFSET, 0, ADC_IER_EOC); + } + else + { + adc_modifyreg(priv, STM32_ADC_IER_OFFSET, ADC_IER_EOC, 0); + } +} + +/**************************************************************************** + * Name: adc_ioc_enable_ovr_int + * + * Description: + * Turns ON/OFF ADC overrun interrupt. + * + * Input Parameters: + * dev - pointer to device structure used by the driver + * arg - true: Turn ON interrupt + * false: Turn OFF interrupt + * + * Returned Value: + * + ****************************************************************************/ + +static void adc_ioc_enable_ovr_int(FAR struct stm32_dev_s *priv, bool enable) +{ + if (enable) + { + adc_modifyreg(priv, STM32_ADC_IER_OFFSET, 0, ADC_IER_OVR); + } + else + { + adc_modifyreg(priv, STM32_ADC_IER_OFFSET, ADC_IER_OVR, 0); + } +} + +/**************************************************************************** + * Name: adc_ioc_change_ints + * + * Description: + * Turns ON/OFF ADC interrupts. + * + * Input Parameters: + * dev - pointer to device structure used by the driver + * cmd - command + * arg - arguments passed with command + * + * Returned Value: + * + ****************************************************************************/ + +static int adc_ioc_change_ints(FAR struct adc_dev_s *dev, int cmd, bool arg) +{ + int ret = OK; + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + + switch (cmd) + { + case IO_ENABLE_DISABLE_AWDIE: + adc_ioc_enable_awd_int(priv, arg); + break; + + case IO_ENABLE_DISABLE_EOCIE: + adc_ioc_enable_eoc_int(priv, arg); + break; + + case IO_ENABLE_DISABLE_OVRIE: + adc_ioc_enable_ovr_int(priv, arg); + break; + + case IO_ENABLE_DISABLE_ALL_INTS: + adc_ioc_enable_awd_int(priv, arg); + adc_ioc_enable_eoc_int(priv, arg); + adc_ioc_enable_ovr_int(priv, arg); + break; + + default: + ainfo("unknown cmd: %d\n", cmd); + break; + } + + return ret; +} + +/**************************************************************************** + * Name: adc_set_ch + * + * Description: + * Sets the ADC channel. + * + * Input Parameters: + * dev - pointer to device structure used by the driver + * ch - ADC channel number + 1. 0 reserved for all configured channels + * + * Returned Value: + * int - errno + * + ****************************************************************************/ + +static int adc_set_ch(FAR struct adc_dev_s *dev, uint8_t ch) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + uint32_t bits = 0; + int i = 0; + + if (ch == 0) + { + priv->current = 0; + priv->rnchannels = priv->cr_channels; + } + else + { + for (i = 0; i < priv->cr_channels && priv->r_chanlist[i] != ch - 1; i++); + + if (i >= priv->cr_channels) + { + return -ENODEV; + } + + priv->current = i; + priv->rnchannels = 1; + } + + + for (i = 0; i < priv->rnchannels; i+=1) + { + bits |= ADC_CHSELR_CHSEL(priv->r_chanlist[i]); + } + + /* Write register */ + + adc_modifyreg(priv, STM32_ADC_CHSELR_OFFSET, 0, bits); + + return OK; +} + +/**************************************************************************** + * Name: adc_ioctl + * + * Description: + * All ioctl calls will be routed through this method. + * + * Input Parameters: + * dev - pointer to device structure used by the driver + * cmd - command + * arg - arguments passed with command + * + * Returned Value: + * + ****************************************************************************/ + +static int adc_ioctl(FAR struct adc_dev_s *dev, int cmd, unsigned long arg) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + int ret = OK; + + switch (cmd) + { + case ANIOC_TRIGGER: + { + /* Start regular conversion if regular channels configured */ + + if (priv->cr_channels > 0) + { + adc_reg_startconv(priv, true); + } + + break; + } + + case IO_TRIGGER_REG: + { + /* Start regular conversion if regular channels configured */ + + if (priv->cr_channels > 0) + { + adc_reg_startconv(priv, true); + } + + break; + } + + case IO_ENABLE_DISABLE_AWDIE: + case IO_ENABLE_DISABLE_EOCIE: + case IO_ENABLE_DISABLE_OVRIE: + case IO_ENABLE_DISABLE_ALL_INTS: + { + adc_ioc_change_ints(dev, cmd, *(bool *)arg); + break; + } + +#ifdef HAVE_ADC_VBAT + case IO_ENABLE_DISABLE_VBAT_CH: + { + adc_enable_vbat_channel(dev, *(bool *)arg); + break; + } +#endif + +#ifdef HAVE_ADC_POWERDOWN + case IO_ENABLE_DISABLE_PDI: + case IO_ENABLE_DISABLE_PDD: + case IO_ENABLE_DISABLE_PDD_PDI: + { + adc_ioc_change_sleep_between_opers(dev, cmd, *(bool *)arg); + 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; + } + + default: + { + aerr("ERROR: Unknown cmd: %d\n", cmd); + ret = -ENOTTY; + break; + } + } + + return ret; +} + +#ifndef CONFIG_STM32F0L0_ADC_NOIRQ + +/**************************************************************************** + * Name: adc_interrupt + * + * Description: + * Common ADC interrupt handler. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +static int adc_interrupt(FAR struct adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev->ad_priv; + uint32_t regval; + uint32_t pending; + int32_t data; + + regval = adc_getreg(priv, STM32_ADC_ISR_OFFSET); + pending = regval & ADC_ISR_ALLINTS; + if (pending == 0) + { + return OK; + } + + /* Identifies the interruption AWD, OVR or EOC */ + + if ((regval & ADC_ISR_AWD) != 0) + { + awarn("WARNING: Analog Watchdog, Value converted out of range!\n"); + } + + if ((regval & ADC_ISR_OVR) != 0) + { + awarn("WARNING: Overrun has occurred!\n"); + } + + /* EOC: End of conversion */ + + if ((regval & ADC_ISR_EOC) != 0) + { + /* Read the converted value and clear EOC bit + * (It is cleared by reading the ADC_DR) + */ + + data = adc_getreg(priv, STM32_ADC_DR_OFFSET) & ADC_DR_RDATA_MASK; + + /* Verify that the upper-half driver has bound its callback functions */ + + if (priv->cb != NULL) + { + /* Give the ADC data to the ADC driver. The ADC receive() method + * accepts 3 parameters: + * + * 1) The first is the ADC device instance for this ADC block. + * 2) The second is the channel number for the data, and + * 3) The third is the converted data for the channel. + */ + + DEBUGASSERT(priv->cb->au_receive != NULL); + priv->cb->au_receive(dev, priv->r_chanlist[priv->current], data); + } + + /* Set the channel number of the next channel that will complete + * conversion. + */ + + priv->current++; + + if (priv->current >= priv->rnchannels) + { + /* Restart the conversion sequence from the beginning */ + + priv->current = 0; + } + } + + /* Clear pending interrupts */ + + adc_putreg(priv, STM32_ADC_ISR_OFFSET, pending); + + return OK; +} + +/**************************************************************************** + * Name: adc1_interrupt + * + * Description: + * ADC interrupt handler for the STM32 L15XX family. + * + * Input Parameters: + * irq - The IRQ number that generated the interrupt. + * context - Architecture specific register save information. + * + * Returned Value: + * + ****************************************************************************/ + +static int adc1_interrupt(int irq, FAR void *context, FAR void *arg) +{ + adc_interrupt(&g_adcdev1); + + return OK; +} +#endif /* CONFIG_STM32F0L0_ADC_NOIRQ */ + +#ifdef CONFIG_STM32F0L0_ADC_LL_OPS + +/**************************************************************************** + * Name: adc_llops_intack + ****************************************************************************/ + +static void adc_llops_intack(FAR struct stm32_adc_dev_s *dev, uint32_t source) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev; + + /* Clear pending interrupts */ + +#ifdef HAVE_IP_ADC_V2 + /* Cleared by writing 1 to it */ + + adc_putreg(priv, STM32_ADC_ISR_OFFSET, (source & ADC_ISR_ALLINTS)); +#else + /* Cleared by writing 0 to it */ + + adc_modifyreg(priv, STM32_ADC_ISR_OFFSET, (source & ADC_ISR_ALLINTS), 0); +#endif +} + +/**************************************************************************** + * Name: adc_llops_inten + ****************************************************************************/ + +static void adc_llops_inten(FAR struct stm32_adc_dev_s *dev, uint32_t source) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev; + + /* Enable interrupts */ + + adc_modifyreg(priv, STM32_ADC_IER_OFFSET, 0, (source & ADC_IER_ALLINTS)); +} + +/**************************************************************************** + * Name: adc_llops_intdis + ****************************************************************************/ + +static void adc_llops_intdis(FAR struct stm32_adc_dev_s *dev, uint32_t source) +{ + FAR struct stm32_dev_s *priv = (FAR 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_llops_intget(FAR struct stm32_adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR 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_llops_regget + ****************************************************************************/ + +static uint32_t adc_llops_regget(FAR struct stm32_adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR 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(FAR struct stm32_adc_dev_s *dev, bool enable) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev; + + adc_reg_startconv(priv, enable); +} + +/**************************************************************************** + * Name: adc_llops_regbufregister + ****************************************************************************/ + +#ifdef ADC_HAVE_DMA +static int adc_llops_regbufregister(FAR struct stm32_adc_dev_s *dev, + uint16_t *buffer, uint8_t len) +{ + FAR struct stm32_dev_s *priv = (FAR 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_sampletime_write + * + * Description: + * Writes previously defined values into ADC_SMPRx registers. + * + * Input Parameters: + * + * Returned Value: + * + ****************************************************************************/ + +#ifdef CONFIG_STM32F0L0_ADC_CHANGE_SAMPLETIME +static void adc_sampletime_write(FAR struct stm32_adc_dev_s *dev) +{ + #error TODO adc_sampletime_write +} + +/**************************************************************************** + * Name: adc_change_sample_time + * + * 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: + * priv - pointer to the adc device structure + * pdi_high - true: The ADC is powered down when waiting for a start event + * false: The ADC is powered up when waiting for a start event + * + * Returned Value: + * None + * + ****************************************************************************/ + +void adc_sampletime_set(FAR struct stm32_adc_dev_s *dev, + FAR struct adc_sample_time_s *time_samples) +{ + #error TODO adc_sampletime_write +} +#endif /* CONFIG_STM32F0L0_ADC_CHANGE_SAMPLETIME */ + +/**************************************************************************** + * Name: adc_llops_dumpregs + ****************************************************************************/ + +static void adc_llops_dumpregs(FAR struct stm32_adc_dev_s *dev) +{ + FAR struct stm32_dev_s *priv = (FAR struct stm32_dev_s *)dev; + + adc_dumpregs(priv); +} + +#endif /* CONFIG_STM32F0L0_ADC_LL_OPS */ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_adc_initialize + * + * Description: + * Initialize the ADC. + * + * Input Parameters: + * intf - Could be {1} for ADC1 + * chanlist - The list of channels + * channels - Number of channels + * + * Returned Value: + * Valid ADC device structure reference on success; a NULL on failure + * + ****************************************************************************/ + +struct adc_dev_s *stm32_adcinitialize(int intf, FAR const uint8_t *chanlist, + int channels) +{ + FAR struct adc_dev_s *dev; + FAR struct stm32_dev_s *priv; + + ainfo("intf: %d cchannels: %d\n", intf, channels); + + switch (intf) + { +#ifdef CONFIG_STM32F0L0_ADC1 + case 1: + { + ainfo("ADC1 selected\n"); + dev = &g_adcdev1; + break; + } +#endif + default: + { + aerr("ERROR: No ADC interface defined\n"); + return NULL; + } + } + + /* Configure the selected ADC */ + + priv = (FAR struct stm32_dev_s *)dev->ad_priv; + priv->cb = NULL; + + DEBUGASSERT(channels <= ADC_MAX_SAMPLES); + + priv->cr_channels = channels; + memcpy(priv->r_chanlist, chanlist, channels); + +#ifdef CONFIG_PM + if (pm_register(&priv->pm_callback) != OK) + { + aerr("Power management registration failed\n"); + return NULL; + } +#endif + + return dev; +} + +#endif /* CONFIG_STM32F0L0_ADC1 */ +#endif /* CONFIG_STM32F0L0_ADC */ diff --git a/arch/arm/src/stm32f0l0/stm32_adc.h b/arch/arm/src/stm32f0l0/stm32_adc.h new file mode 100644 index 0000000000..6d3568759c --- /dev/null +++ b/arch/arm/src/stm32f0l0/stm32_adc.h @@ -0,0 +1,343 @@ +/************************************************************************************ + * arch/arm/src/stm32/stm32_adc.h + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Mateusz Szafoni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ************************************************************************************/ + +#ifndef __ARCH_ARM_SRC_STM32F0L0_STM32_ADC_H +#define __ARCH_ARM_SRC_STM32F0L0_STM32_ADC_H + +/************************************************************************************ + * Included Files + ************************************************************************************/ + +#include + +#include "chip.h" + +#include "hardware/stm32_adc.h" + +#include + +/************************************************************************************ + * Pre-processor Definitions + ************************************************************************************/ +/* Configuration ********************************************************************/ + +/* Timer ADC trigger not supported yet */ + +#undef ADC1_HAVE_TIMER + +/* Up to 1 ADC interfaces are supported */ + +#if STM32_NADC < 1 +# undef CONFIG_STM32F0L0_ADC1 +#endif + +#if defined(CONFIG_STM32F0L0_ADC1) + +/* DMA support */ + +#undef ADC_HAVE_DMA +#if defined(CONFIG_STM32F0L0_ADC1_DMA) +# define ADC_HAVE_DMA 1 +#endif + +#ifdef CONFIG_STM32F0L0_ADC1_DMA +# define ADC1_HAVE_DMA 1 +#else +# undef ADC1_HAVE_DMA +#endif + +/* EXTSEL */ + +#if defined(CONFIG_STM32F0L0_STM32F0) +# define ADC1_EXTSEL_T1TRGO ADC12_CFGR1_EXTSEL_TRG0 +# define ADC1_EXTSEL_T1CC4 ADC12_CFGR1_EXTSEL_TRG1 +# define ADC1_EXTSEL_T2TRGO ADC12_CFGR1_EXTSEL_TRG2 +# define ADC1_EXTSEL_T3TRGO ADC12_CFGR1_EXTSEL_TRG3 +# define ADC1_EXTSEL_T15TRGO ADC12_CFGR1_EXTSEL_TRG4 + /* TRG5 reserved */ + /* TRG6 reserved */ + /* TRG7 reserved */ +#elif defined(CONFIG_STM32F0L0_STM32L0) + /* TRG0 reserved */ +# define ADC1_EXTSEL_T21CC2 ADC12_CFGR1_EXTSEL_TRG1 +# define ADC1_EXTSEL_T2TRGO ADC12_CFGR1_EXTSEL_TRG2 +# define ADC1_EXTSEL_T2CC4 ADC12_CFGR1_EXTSEL_TRG3 +# define ADC1_EXTSEL_T21TRGO ADC12_CFGR1_EXTSEL_TRG4 +# define ADC1_EXTSEL_T2CC3 ADC12_CFGR1_EXTSEL_TRG5 + /* TRG6 reserved */ +# define ADC1_EXTSEL_EXTI11 ADC12_CFGR1_EXTSEL_TRG7 +#elif defined(CONFIG_STM32F0L0_STM32G0) +# define ADC1_EXTSEL_T1TRGO2 ADC12_CFGR1_EXTSEL_TRG0 +# define ADC1_EXTSEL_T1CC4 ADC12_CFGR1_EXTSEL_TRG1 +# define ADC1_EXTSEL_T2TRGO ADC12_CFGR1_EXTSEL_TRG2 +# define ADC1_EXTSEL_T3TRGO ADC12_CFGR1_EXTSEL_TRG3 +# define ADC1_EXTSEL_T15TRGO ADC12_CFGR1_EXTSEL_TRG4 +# define ADC1_EXTSEL_T6TRGO ADC12_CFGR1_EXTSEL_TRG5 + /* TRG6 reserved */ +# define ADC1_EXTSEL_EXTI11 ADC12_CFGR1_EXTSEL_TRG7 +#else +# error +#endif + +/* EXTSEL configuration *****************************************************/ +/* TODO */ + +/* ADC interrupts ***********************************************************/ + +#define ADC_ISR_EOC ADC_INT_EOC +#define ADC_IER_EOC ADC_INT_EOC +#define ADC_ISR_AWD ADC_INT_AWD +#define ADC_IER_AWD ADC_INT_AWD +#define ADC_ISR_OVR ADC_INT_OVR +#define ADC_IER_OVR ADC_INT_OVR + +#define ADC_ISR_ALLINTS (ADC_ISR_EOC | ADC_ISR_AWD | ADC_ISR_OVR) +#define ADC_IER_ALLINTS (ADC_IER_EOC | ADC_IER_AWD | ADC_IER_OVR) + +/* ADC registers ***********************************************************/ + +#define STM32_ADC_DMAREG_OFFSET STM32_ADC_CFGR1_OFFSET +#define ADC_DMAREG_DMA ADC_CFGR1_DMAEN +#define STM32_ADC_EXTREG_OFFSET STM32_ADC_CFGR1_OFFSET +#define ADC_EXTREG_EXTSEL_MASK ADC_CFGR1_EXTSEL_MASK +#define ADC_EXTREG_EXTEN_MASK ADC_CFGR1_EXTEN_MASK +#define ADC_EXTREG_EXTEN_DEFAULT ADC_CFGR1_EXTEN_RISING + +/* Low-level ops helpers ****************************************************/ + +#define ADC_INT_ACK(adc, source) \ + (adc)->llops->int_ack(adc, source) +#define ADC_INT_GET(adc) \ + (adc)->llops->int_get(adc) +#define ADC_INT_ENABLE(adc, source) \ + (adc)->llops->int_en(adc, source) +#define ADC_INT_DISABLE(adc, source) \ + (adc)->llops->int_dis(adc, source) +#define ADC_REGDATA_GET(adc) \ + (adc)->llops->val_get(adc) +#define ADC_REGBUF_REGISTER(adc, buffer, len) \ + (adc)->llops->regbuf_reg(adc, buffer, len) +#define ADC_REG_STARTCONV(adc, state) \ + (adc)->llops->reg_startconv(adc, state) +#define ADC_SAMPLETIME_SET(adc, time_samples) \ + (adc)->llops->stime_set(adc, time_samples) +#define ADC_SAMPLETIME_WRITE(adc) \ + (adc)->llops->stime_write(adc) +#define ADC_DUMP_REGS(adc) \ + (adc)->llops->dump_regs(adc) + +/************************************************************************************ + * Public Types + ************************************************************************************/ + +/* On STM32F42xx and STM32F43xx devices,VBAT and temperature sensor are connected + * to the same ADC internal channel (ADC1_IN18). Only one conversion, either + * temperature sensor or VBAT, must be selected at a time. When both conversion are + * enabled simultaneously, only the VBAT conversion is performed. + */ + +enum adc_io_cmds_e +{ +#ifdef HAVE_ADC_VBAT + IO_ENABLE_DISABLE_VBAT_CH, +#endif + IO_ENABLE_DISABLE_AWDIE, + IO_ENABLE_DISABLE_EOCIE, + IO_ENABLE_DISABLE_JEOCIE, + IO_ENABLE_DISABLE_OVRIE, + IO_ENABLE_DISABLE_ALL_INTS, + IO_STOP_ADC, + IO_START_ADC, + IO_START_CONV, + IO_TRIGGER_REG, +#ifdef ADC_HAVE_INJECTED + IO_TRIGGER_INJ, +#endif +#ifdef HAVE_ADC_POWERDOWN + IO_ENABLE_DISABLE_PDI, + IO_ENABLE_DISABLE_PDD, + IO_ENABLE_DISABLE_PDD_PDI +#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_STM32F0L0_ADC_LL_OPS + +#ifdef CONFIG_STM32F0L0_ADC_CHANGE_SAMPLETIME + +/* Channel and sample time pair */ + +typedef struct adc_channel_s +{ + uint8_t channel:5; + + /* Sampling time individually for each channel. It differs between families */ + + 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_STM32F0L0_ADC_CHANGE_SAMPLETIME */ + +/* This structure provides the publicly visable representation of the + * "lower-half" ADC driver structure. + */ + +struct stm32_adc_dev_s +{ + /* Publicly visible portion of the "lower-half" ADC driver structure */ + + FAR const struct stm32_adc_ops_s *llops; + + /* Require cast-compatibility with private "lower-half" ADC strucutre */ +}; + +/* Low-level operations for ADC */ + +struct stm32_adc_ops_s +{ + /* Acknowledge interrupts */ + + void (*int_ack)(FAR struct stm32_adc_dev_s *dev, uint32_t source); + + /* Get pending interrupts */ + + uint32_t (*int_get)(FAR struct stm32_adc_dev_s *dev); + + /* Enable interrupts */ + + void (*int_en)(FAR struct stm32_adc_dev_s *dev, uint32_t source); + + /* Disable interrupts */ + + void (*int_dis)(FAR struct stm32_adc_dev_s *dev, uint32_t source); + + /* Get current ADC data register */ + + uint32_t (*val_get)(FAR struct stm32_adc_dev_s *dev); + + /* Register buffer for ADC DMA transfer */ + + int (*regbuf_reg)(FAR struct stm32_adc_dev_s *dev, uint16_t *buffer, uint8_t len); + + /* Start/stop regular conversion */ + + void (*reg_startconv)(FAR struct stm32_adc_dev_s *dev, bool state); + +#ifdef CONFIG_STM32F0L0_ADC_CHANGE_SAMPLETIME + /* Set ADC sample time */ + + void (*stime_set)(FAR struct stm32_adc_dev_s *dev, + FAR struct adc_sample_time_s *time_samples); + + /* Write ADC sample time */ + + void (*stime_write)(FAR struct stm32_adc_dev_s *dev); +#endif + + void (*dump_regs)(FAR struct stm32_adc_dev_s *dev); +}; + +#endif /* CONFIG_STM32F0L0_ADC_LL_OPS */ + +/************************************************************************************ + * Public Function Prototypes + ************************************************************************************/ + +#ifndef __ASSEMBLY__ +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +/**************************************************************************** + * Name: stm32_adcinitialize + * + * Description: + * Initialize the ADC. See stm32_adc.c for more details. + * + * Input Parameters: + * intf - Could be {1,2,3,4} for ADC1, ADC2, ADC3 or ADC4 + * chanlist - The list of channels (regular + injected) + * nchannels - Number of channels (regular + injected) + * + * Returned Value: + * Valid can device structure reference on succcess; a NULL on failure + * + ****************************************************************************/ + +struct adc_dev_s; +struct adc_dev_s *stm32_adcinitialize(int intf, FAR const uint8_t *chanlist, + int channels); + +/************************************************************************************ + * Name: stm32_adc_llops_get + ************************************************************************************/ + +#ifdef CONFIG_STM32F0L0_ADC_LL_OPS +FAR const struct stm32_adc_ops_s *stm32_adc_llops_get(FAR struct adc_dev_s *dev); +#endif + +#undef EXTERN +#ifdef __cplusplus +} +#endif +#endif /* __ASSEMBLY__ */ + +#endif /* CONFIG_STM32F0L0_ADC1 */ +#endif /* __ARCH_ARM_SRC_STM32F0L0_STM32_ADC_H */ diff --git a/configs/b-l072z-lrwan1/adc/defconfig b/configs/b-l072z-lrwan1/adc/defconfig new file mode 100644 index 0000000000..a194a28a6f --- /dev/null +++ b/configs/b-l072z-lrwan1/adc/defconfig @@ -0,0 +1,67 @@ +# +# This file is autogenerated: PLEASE DO NOT EDIT IT. +# +# You can use "make menuconfig" to make any modifications to the installed .config file. +# You can then do "make savedefconfig" to generate a new defconfig file that includes your +# modifications. +# +# CONFIG_LIBC_LONG_LONG is not set +# CONFIG_NSH_ARGCAT is not set +CONFIG_ADC=y +CONFIG_ANALOG=y +CONFIG_ARCH="arm" +CONFIG_ARCH_BOARD="b-l072z-lrwan1" +CONFIG_ARCH_BOARD_B_L072Z_LRWAN1=y +CONFIG_ARCH_CHIP_STM32L072CZ=y +CONFIG_ARCH_CHIP_STM32L072XX=y +CONFIG_ARCH_CHIP_STM32L0=y +CONFIG_ARCH_STACKDUMP=y +CONFIG_BOARD_LOOPSPERMSEC=2796 +CONFIG_BUILTIN=y +CONFIG_DISABLE_ENVIRON=y +CONFIG_DISABLE_MOUNTPOINT=y +CONFIG_DISABLE_MQUEUE=y +CONFIG_DISABLE_POLL=y +CONFIG_DISABLE_POSIX_TIMERS=y +CONFIG_DISABLE_PSEUDOFS_OPERATIONS=y +CONFIG_EXAMPLES_ADC=y +CONFIG_EXAMPLES_ADC_SWTRIG=y +CONFIG_EXPERIMENTAL=y +CONFIG_INTELHEX_BINARY=y +CONFIG_MAX_TASKS=8 +CONFIG_MAX_WDOGPARMS=2 +CONFIG_NFILE_DESCRIPTORS=6 +CONFIG_NFILE_STREAMS=6 +CONFIG_NPTHREAD_KEYS=0 +CONFIG_NSH_ARCHINIT=y +CONFIG_NSH_BUILTIN_APPS=y +CONFIG_NSH_FILEIOSIZE=64 +CONFIG_NSH_LINELEN=64 +CONFIG_NSH_READLINE=y +CONFIG_NUNGET_CHARS=0 +CONFIG_PREALLOC_TIMERS=0 +CONFIG_PREALLOC_WDOGS=4 +CONFIG_PTHREAD_MUTEX_UNSAFE=y +CONFIG_PTHREAD_STACK_DEFAULT=1536 +CONFIG_RAM_SIZE=20480 +CONFIG_RAM_START=0x20000000 +CONFIG_RAW_BINARY=y +CONFIG_RR_INTERVAL=200 +CONFIG_SCHED_WAITPID=y +CONFIG_SDCLONE_DISABLE=y +CONFIG_START_DAY=19 +CONFIG_START_MONTH=5 +CONFIG_START_YEAR=2013 +CONFIG_STDIO_DISABLE_BUFFERING=y +CONFIG_STM32F0L0_ADC1=y +CONFIG_STM32F0L0_ADC1_DMA=y +CONFIG_STM32F0L0_DMA1=y +CONFIG_STM32F0L0_PWR=y +CONFIG_STM32F0L0_USART2=y +CONFIG_SYSTEM_NSH=y +CONFIG_TASK_NAME_SIZE=0 +CONFIG_TASK_SPAWN_DEFAULT_STACKSIZE=1536 +CONFIG_USART2_SERIAL_CONSOLE=y +CONFIG_USERMAIN_STACKSIZE=1536 +CONFIG_USER_ENTRYPOINT="nsh_main" +CONFIG_WDOG_INTRESERVE=0 diff --git a/configs/b-l072z-lrwan1/include/board.h b/configs/b-l072z-lrwan1/include/board.h index d4a2dbafb4..262ea1de76 100644 --- a/configs/b-l072z-lrwan1/include/board.h +++ b/configs/b-l072z-lrwan1/include/board.h @@ -248,6 +248,6 @@ /* DMA channels *************************************************************/ /* ADC */ -#define ADC1_DMA_CHAN DMACHAN_ADC1 /* DMA1_CH1 */ +#define ADC1_DMA_CHAN DMACHAN_ADC1_1 /* DMA1_CH1 */ #endif /* __CONFIG_NUCLEO_LO73RZ_INCLUDE_BOARD_H */ diff --git a/configs/b-l072z-lrwan1/src/Makefile b/configs/b-l072z-lrwan1/src/Makefile index e862eb9238..c6fbe3a069 100644 --- a/configs/b-l072z-lrwan1/src/Makefile +++ b/configs/b-l072z-lrwan1/src/Makefile @@ -60,4 +60,8 @@ ifeq ($(CONFIG_LPWAN_SX127X),y) CSRCS += stm32_sx127x.c endif +ifeq ($(CONFIG_ADC),y) +CSRCS += stm32_adc.c +endif + include $(TOPDIR)/configs/Board.mk diff --git a/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h b/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h index 6c3e1bbb2d..a91df6e665 100644 --- a/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h +++ b/configs/b-l072z-lrwan1/src/b-l072z-lrwan1.h @@ -161,4 +161,16 @@ void stm32_spidev_initialize(void); int stm32_lpwaninitialize(void); #endif +/************************************************************************************ + * Name: stm32_adc_setup + * + * Description: + * Initialize ADC and register the ADC driver. + * + ************************************************************************************/ + +#ifdef CONFIG_ADC +int stm32_adc_setup(void); +#endif + #endif /* __CONFIGS_B_L072Z_LRWAN1_SRC_B_L072Z_LRWAN1_H */ diff --git a/configs/b-l072z-lrwan1/src/stm32_adc.c b/configs/b-l072z-lrwan1/src/stm32_adc.c new file mode 100644 index 0000000000..4fe1261066 --- /dev/null +++ b/configs/b-l072z-lrwan1/src/stm32_adc.c @@ -0,0 +1,145 @@ +/**************************************************************************** + * configs/b-l072z-lrwan1/src/stm32_adc.c + * + * Copyright (C) 2019 Gregory Nutt. All rights reserved. + * Author: Mateusz Szafoni + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include + +#include +#include + +#include "stm32.h" + +#if defined(CONFIG_ADC) && defined(CONFIG_STM32F0L0_ADC1) + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Configuration ************************************************************/ + +/* The number of ADC channels in the conversion list */ + +#define ADC1_NCHANNELS 2 + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* Identifying number of each ADC channel (even if NCHANNELS is less ) */ + +static const uint8_t g_chanlist1[2] = +{ + 0, + 4, +}; + +/* Configurations of pins used by each ADC channel */ + +static const uint32_t g_pinlist1[2] = +{ + GPIO_ADC1_IN0, /* PA0/A0 */ + GPIO_ADC1_IN4 /* PA4/A2 */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: stm32_adc_setup + * + * Description: + * Initialize ADC and register the ADC driver. + * + ****************************************************************************/ + +int stm32_adc_setup(void) +{ + static bool initialized = false; + FAR struct adc_dev_s *adc; + int ret; + int i; + + /* Check if we have already initialized */ + + if (!initialized) + { + /* Configure the pins as analog inputs for the selected channels */ + + for (i = 0; i < ADC1_NCHANNELS; i++) + { + stm32_configgpio(g_pinlist1[i]); + } + + /* Call stm32_adcinitialize() to get an instance of the ADC interface */ + + adc = stm32_adcinitialize(1, g_chanlist1, ADC1_NCHANNELS); + if (adc == NULL) + { + aerr("ERROR: Failed to get ADC interface 1\n"); + return -ENODEV; + } + + /* Register the ADC driver at "/dev/adc0" */ + + ret = adc_register("/dev/adc0", adc); + if (ret < 0) + { + aerr("ERROR: adc_register /dev/adc0 failed: %d\n", ret); + return ret; + } + + initialized = true; + } + + return OK; +} + +#endif /* CONFIG_ADC && CONFIG_STM32F0L0_ADC1 */