From fc7c25c95ef9913d7b915ba6c8528dff86d93987 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Mon, 7 Dec 2015 21:32:01 +0100 Subject: [PATCH] cpu/stm32f4: reworked the ADC driver --- cpu/stm32f4/include/periph_cpu.h | 50 +++++++ cpu/stm32f4/periph/adc.c | 222 ++++++++++++------------------- cpu/stm32f4/periph/gpio.c | 8 ++ 3 files changed, 141 insertions(+), 139 deletions(-) diff --git a/cpu/stm32f4/include/periph_cpu.h b/cpu/stm32f4/include/periph_cpu.h index 70f404cec4..120ac79eca 100644 --- a/cpu/stm32f4/include/periph_cpu.h +++ b/cpu/stm32f4/include/periph_cpu.h @@ -25,6 +25,15 @@ extern "C" { #endif +/** + * @brief Available number of ADC devices + */ +#if defined(CPU_MODEL_STM32F401RE) +#define ADC_DEVS (1U) +#elif defined(CPU_MODEL_STM32F407VG) || defined(CPU_MODEL_STM32F415RG) +#define ADC_DEVS (3U) +#endif + /** * @brief Overwrite the default gpio_t type definition * @{ @@ -43,6 +52,30 @@ typedef uint32_t gpio_t; */ #define GPIO_PIN(x, y) ((GPIOA_BASE + (x << 10)) | y) +/** + * @brief declare needed generic SPI functions + * @{ + */ +#define PERIPH_SPI_NEEDS_TRANSFER_BYTES +#define PERIPH_SPI_NEEDS_TRANSFER_REG +#define PERIPH_SPI_NEEDS_TRANSFER_REGS +/** @} */ + +/** + * @brief Override the ADC resolution configuration + * @{ + */ +#define HAVE_ADC_RES_T +typedef enum { + ADC_RES_6BIT = 0x03000000, /**< ADC resolution: 6 bit */ + ADC_RES_8BIT = 0x02000000, /**< ADC resolution: 8 bit */ + ADC_RES_10BIT = 0x01000000, /**< ADC resolution: 10 bit */ + ADC_RES_12BIT = 0x00000000, /**< ADC resolution: 12 bit */ + ADC_RES_14BIT = 1, /**< ADC resolution: 14 bit (not supported) */ + ADC_RES_16BIT = 2 /**< ADC resolution: 16 bit (not supported)*/ +} adc_res_t; +/** @} */ + /** * @brief Available ports on the STM32F4 family */ @@ -95,6 +128,16 @@ typedef struct { } uart_conf_t; /** @} */ +/** + * @brief ADC channel configuration data + */ +typedef struct { + gpio_t pin; /**< pin connected to the channel */ + uint8_t dev; /**< ADCx - 1 device used for the channel */ + uint8_t chan; /**< CPU ADC channel connected to the pin */ + uint8_t rcc; /**< bit in the RCC APB2 enable register */ +} adc_conf_t; + /** * @brief Configure the alternate function for the given pin * @@ -105,6 +148,13 @@ typedef struct { */ void gpio_init_af(gpio_t pin, gpio_af_t af); +/** + * @brief Configure the given pin to be used as ADC input + * + * @param[in] pin pin to configure + */ +void gpio_init_analog(gpio_t pin); + /** * @brief Power on the DMA device the given stream belongs to * diff --git a/cpu/stm32f4/periph/adc.c b/cpu/stm32f4/periph/adc.c index 12c1f47ceb..1ee863bc95 100644 --- a/cpu/stm32f4/periph/adc.c +++ b/cpu/stm32f4/periph/adc.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Freie Universität Berlin + * Copyright (C) 2014-2016 Freie Universität Berlin * * This file is subject to the terms and conditions of the GNU Lesser General * Public License v2.1. See the file LICENSE in the top level directory for more @@ -19,167 +19,111 @@ */ #include "cpu.h" +#include "mutex.h" #include "periph/adc.h" #include "periph_conf.h" -/* guard in case that no ADC device is defined */ -#if ADC_NUMOF +/** + * @brief Maximum allowed ADC clock speed + */ +#define MAX_ADC_SPEED (12000000U) -typedef struct { - int max_value; -} adc_config_t; +/** + * @brief Load the ADC configuration + * @{ + */ +#ifdef ADC_CONFIG +static const adc_conf_t adc_config[] = ADC_CONFIG; +#else +static const adc_conf_t adc_config[] = {}; +#endif -adc_config_t adc_config[ADC_NUMOF]; +/** + * @brief Allocate locks for all three available ADC devices + */ +static mutex_t locks[] = { +#if ADC_DEVS > 1 + MUTEX_INIT, +#endif +#if ADC_DEVS > 2 + MUTEX_INIT, +#endif + MUTEX_INIT +}; -int adc_init(adc_t dev, adc_precision_t precision) +static inline ADC_TypeDef *dev(adc_t line) { - ADC_TypeDef *adc = 0; + return (ADC_TypeDef *)(ADC1_BASE + (adc_config[line].dev << 8)); +} - adc_poweron(dev); +static inline void prep(adc_t line) +{ + mutex_lock(&locks[adc_config[line].dev]); + RCC->APB2ENR |= (RCC_APB2ENR_ADC1EN << adc_config[line].dev); +} - switch (dev) { -#if ADC_0_EN - case ADC_0: - adc = ADC_0_DEV; - ADC_0_PORT_CLKEN(); - ADC_0_PORT->MODER |= (3 << (ADC_0_CH0_PIN * 2) | 3 << (ADC_0_CH1_PIN * 2)); - break; -#endif -#if ADC_1_EN - case ADC_1: - adc = ADC_1_DEV; - ADC_1_PORT_CLKEN(); - ADC_1_PORT->MODER |= (3 << (ADC_1_CH0_PIN * 2) | 3 << (ADC_1_CH1_PIN * 2)); - break; -#endif - default: - return -1; +static inline void done(adc_t line) +{ + RCC->APB2ENR &= ~(RCC_APB2ENR_ADC1EN << adc_config[line].dev); + mutex_unlock(&locks[adc_config[line].dev]); +} + +int adc_init(adc_t line) +{ + uint32_t clk_div = 2; + + /* check if the line is valid */ + if (line >= ADC_NUMOF) { + return -1; } - /* reset control registers */ - adc->CR1 = 0; - adc->CR2 = 0; - adc->SQR1 = 0; + /* lock and power-on the device */ + prep(line); - /* set precision */ - - switch (precision) { - case ADC_RES_6BIT: - adc->CR1 |= ADC_CR1_RES_0 | ADC_CR1_RES_1; - adc_config[dev].max_value = 0x3f; - break; - case ADC_RES_8BIT: - adc->CR1 |= ADC_CR1_RES_1; - adc_config[dev].max_value = 0xff; - break; - case ADC_RES_10BIT: - adc->CR1 |= ADC_CR1_RES_0; - adc_config[dev].max_value = 0x3ff; - break; - case ADC_RES_12BIT: - adc_config[dev].max_value = 0xfff; - break; - case ADC_RES_14BIT: - case ADC_RES_16BIT: - adc_poweroff(dev); - return -1; + /* configure the pin */ + gpio_init_analog(adc_config[line].pin); + /* set sequence length to 1 conversion and enable the ADC device */ + dev(line)->SQR1 = 0; + dev(line)->CR2 = ADC_CR2_ADON; + /* set clock prescaler to get the maximal possible ADC clock value */ + for (clk_div = 2; clk_div < 8; clk_div += 2) { + if ((CLOCK_CORECLOCK / clk_div) <= MAX_ADC_SPEED) { break; + } } + ADC->CCR = ((clk_div / 2) - 1) << 16; - /* set clock prescaler */ - ADC->CCR = (3 << 16); /* ADC clock = 10,5MHz */ - - /* enable the ADC module */ - adc->CR2 |= ADC_CR2_ADON; - + /* free the device again */ + done(line); return 0; } -int adc_sample(adc_t dev, int channel) +int adc_sample(adc_t line, adc_res_t res) { - ADC_TypeDef *adc = 0; + int sample; - switch (dev) { -#if ADC_0_EN - case ADC_0: - adc = ADC_0_DEV; - switch (channel) { - case 0: - adc->SQR3 = ADC_0_CH0 & 0x1f; - break; - case 1: - adc->SQR3 = ADC_0_CH1 & 0x1f; - break; - default: - return -1; - } - break; -#endif -#if ADC_1_EN - case ADC_1: - adc = ADC_1_DEV; - switch (channel) { - case 0: - adc->SQR3 = ADC_1_CH0 & 0x1f; - break; - case 1: - adc->SQR3 = ADC_1_CH1 & 0x1f; - break; - default: - return -1; - } - break; -#endif + /* check if resolution is applicable */ + if (res < 0xff) { + return -1; } - /* start single conversion */ - adc->CR2 |= ADC_CR2_SWSTART; - /* wait until conversion is complete */ - while (!(adc->SR & ADC_SR_EOC)) {} - /* read and return result */ - return (int)adc->DR; -} + /* lock and power on the ADC device */ + prep(line); -void adc_poweron(adc_t dev) -{ - switch (dev) { -#if ADC_0_EN - case ADC_0: - ADC_0_CLKEN(); - break; -#endif -#if ADC_1_EN - case ADC_1: - ADC_1_CLKEN(); - break; -#endif - } -} + /* wait for any ongoing conversions to finish */ + while (dev(line)->SR & ADC_SR_STRT) {} + /* set resolution and conversion channel */ + dev(line)->CR1 = res; + dev(line)->SQR3 = adc_config[line].chan; + /* start conversion and wait for results */ + dev(line)->CR2 |= ADC_CR2_SWSTART; + while (!(dev(line)->SR & ADC_SR_EOC)) {} + /* finally read sample and reset the STRT bit in the status register */ + sample = (int)dev(line)->DR; + dev(line)->SR &= ~ADC_SR_STRT; -void adc_poweroff(adc_t dev) -{ - switch (dev) { -#if ADC_0_EN - case ADC_0: - ADC_0_CLKDIS(); - break; -#endif -#if ADC_1_EN - case ADC_1: - ADC_1_CLKDIS(); - break; -#endif - } -} + /* power off and unlock device again */ + done(line); -int adc_map(adc_t dev, int value, int min, int max) -{ - return (int)adc_mapf(dev, value, (float)min, (float)max); + return sample; } - -float adc_mapf(adc_t dev, int value, float min, float max) -{ - return ((max - min) / ((float)adc_config[dev].max_value)) * value; -} - -#endif /* ADC_NUMOF */ diff --git a/cpu/stm32f4/periph/gpio.c b/cpu/stm32f4/periph/gpio.c index 7b49f3fc35..774a31acf8 100644 --- a/cpu/stm32f4/periph/gpio.c +++ b/cpu/stm32f4/periph/gpio.c @@ -148,6 +148,14 @@ void gpio_init_af(gpio_t pin, gpio_af_t af) port->AFR[(pin_num > 7) ? 1 : 0] |= (af << ((pin_num & 0x07) * 4)); } +void gpio_init_analog(gpio_t pin) +{ + /* enable clock */ + RCC->AHB1ENR |= (RCC_AHB1ENR_GPIOAEN << _port_num(pin)); + /* set to analog mode */ + _port(pin)->MODER |= (0x3 << (2 * _pin_num(pin))); +} + void gpio_irq_enable(gpio_t pin) { EXTI->IMR |= (1 << _pin_num(pin));