From 30ef5d0a30acf408f9f7e202dd4c0aaaadb41dd8 Mon Sep 17 00:00:00 2001 From: Hauke Petersen Date: Mon, 26 Jun 2017 22:12:14 +0200 Subject: [PATCH] cpu/sam3: added ADC implementation --- boards/arduino-due/Makefile.features | 1 + cpu/sam3/include/periph_cpu.h | 24 +++++++ cpu/sam3/periph/adc.c | 99 ++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+) create mode 100644 cpu/sam3/periph/adc.c diff --git a/boards/arduino-due/Makefile.features b/boards/arduino-due/Makefile.features index 1cfb851304..6e911845de 100644 --- a/boards/arduino-due/Makefile.features +++ b/boards/arduino-due/Makefile.features @@ -1,4 +1,5 @@ # Put defined MCU peripherals here (in alphabetical order) +FEATURES_PROVIDED += periph_adc FEATURES_PROVIDED += periph_cpuid FEATURES_PROVIDED += periph_gpio FEATURES_PROVIDED += periph_hwrng diff --git a/cpu/sam3/include/periph_cpu.h b/cpu/sam3/include/periph_cpu.h index 89b44b92c9..117e772d38 100644 --- a/cpu/sam3/include/periph_cpu.h +++ b/cpu/sam3/include/periph_cpu.h @@ -80,6 +80,15 @@ typedef uint32_t gpio_t; */ #define GPIO_MODE(io, pu, od) (io | (pu << 1) | (od << 2)) +/** + * @name ADC configuration, valid for all boards using this CPU + * + * The sam3 has a fixed mapping of ADC pins and a fixed number of ADC channels, + * so this ADC configuration is valid for all boards using this CPU. No need for + * any board specific configuration. + */ +#define ADC_NUMOF (16U) + #ifndef DOXYGEN /** * @brief Override GPIO modes @@ -154,6 +163,21 @@ typedef enum { } spi_clk_t; /** @} */ +/** + * @brief Override ADC resolution values + * @{ + */ +#define HAVE_ADC_RES_T +typedef enum { + ADC_RES_6BIT = 0x1, /**< not applicable */ + ADC_RES_8BIT = 0x2, /**< not applicable */ + ADC_RES_10BIT = ADC_MR_LOWRES_BITS_10, /**< ADC resolution: 10 bit */ + ADC_RES_12BIT = ADC_MR_LOWRES_BITS_12, /**< ADC resolution: 12 bit */ + ADC_RES_14BIT = 0x4, /**< not applicable */ + ADC_RES_16BIT = 0x8 /**< not applicable */ +} adc_res_t; +/** @} */ + /** * @brief Timer configuration data */ diff --git a/cpu/sam3/periph/adc.c b/cpu/sam3/periph/adc.c new file mode 100644 index 0000000000..1c2651e670 --- /dev/null +++ b/cpu/sam3/periph/adc.c @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2017 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 details. + */ + +/** + * @ingroup cpu_sam3 + * @{ + * + * @file + * @brief Low-level ADC driver implementation + * + * @author Hauke Petersen + * + * @} + */ + +#include + + +#include "mutex.h" +#include "periph/adc.h" + +#define RES_INVALID (0x0f) +#define WP_KEY (0x414443) +#define PMC_BIT (1 << (ID_ADC - 32)) + +/* max ADC clock is 6.4MHz, we aim for 5MHz per default */ +#ifndef ADC_CLOCK_TARGET +#define ADC_CLOCK_TARGET (5000000) +#endif +#define PRESCALER ((CLOCK_CORECLOCK / (2 * ADC_CLOCK_TARGET)) - 1) + + +static mutex_t lock = MUTEX_INIT; + +static inline void use(void) +{ + mutex_lock(&lock); + PMC->PMC_PCER1 = PMC_BIT; +} + +static inline void done(void) +{ + PMC->PMC_PCDR1 = PMC_BIT; + mutex_unlock(&lock); +} + +int adc_init(adc_t line) +{ + assert(line < ADC_NUMOF); + + use(); + + /* disable ADC write protection */ + ADC->ADC_WPMR = ADC_WPMR_WPKEY(WP_KEY); + ADC->ADC_CHDR = 0x0000ffff; + + /* if line 15 is used, enable the temperature sensor */ + if (line == 15) { + ADC->ADC_ACR |= ADC_ACR_TSON; + } + + done(); + return 0; +} + +int adc_sample(adc_t line, adc_res_t res) +{ + assert(line < ADC_NUMOF); + + /* check if resolution is applicable */ + if (res & RES_INVALID) { + return -1; + } + + use(); + + /* set resolution */ + ADC->ADC_MR = (res | ADC_MR_PRESCAL(PRESCALER)); + /* enable channel */ + ADC->ADC_CHER = (1 << line); + + /* start conversion */ + ADC->ADC_CR = ADC_CR_START; + /* wait for result */ + while (!(ADC->ADC_ISR & ADC_ISR_DRDY)) {} + /* read result */ + int sample = (int)(ADC->ADC_LCDR & ADC_LCDR_LDATA_Msk); + + /* disable channel */ + ADC->ADC_CHDR = (1 << line); + + done(); + return sample; +}