diff --git a/boards/saml21-xpro/include/periph_conf.h b/boards/saml21-xpro/include/periph_conf.h index 03e9d383af..33f41658d9 100644 --- a/boards/saml21-xpro/include/periph_conf.h +++ b/boards/saml21-xpro/include/periph_conf.h @@ -122,18 +122,13 @@ static const spi_conf_t spi_config[] = { * @name ADC Configuration * @{ */ -#define ADC_NUMOF ADC_0_CHANNELS -#define ADC_0_EN 1 -#define ADC_MAX_CHANNELS 14 -/* ADC 0 device configuration */ -#define ADC_0_DEV ADC -#define ADC_0_IRQ ADC_IRQn -#define ADC_0_CHANNELS (3U) +#define ADC_NUMOF (3U) /* ADC 0 Default values */ #define ADC_0_CLK_SOURCE 0 /* GCLK_GENERATOR_0 */ #define ADC_0_PRESCALER ADC_CTRLB_PRESCALER_DIV256 -static const adc_channel_t adc_channels[] = { + +static const adc_conf_chan_t adc_channels[] = { /* port, pin, muxpos */ {GPIO_PIN(PA, 10), ADC_INPUTCTRL_MUXPOS(ADC_INPUTCTRL_MUXPOS_AIN18)}, {GPIO_PIN(PA, 11), ADC_INPUTCTRL_MUXPOS(ADC_INPUTCTRL_MUXPOS_AIN19)}, diff --git a/cpu/sam0_common/include/periph_cpu_common.h b/cpu/sam0_common/include/periph_cpu_common.h index 15d924b06e..65bb36a5e7 100644 --- a/cpu/sam0_common/include/periph_cpu_common.h +++ b/cpu/sam0_common/include/periph_cpu_common.h @@ -202,6 +202,15 @@ static inline int sercom_id(void *sercom) #endif } +/** + * @brief ADC Channel Configuration + */ +typedef struct { + gpio_t pin; /**< ADC channel pin */ + uint32_t muxpos; /**< ADC channel pin multiplexer value */ +} adc_conf_chan_t; + + #ifdef __cplusplus } #endif diff --git a/cpu/sam0_common/periph/adc.c b/cpu/sam0_common/periph/adc.c new file mode 100644 index 0000000000..e1f7bdc549 --- /dev/null +++ b/cpu/sam0_common/periph/adc.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2017 Dan Evans + * Copyright (C) 2017 Travis Griggs + * Copyright (C) 2017 Dylan Laduranty + * + * 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. + */ + +#include +#include "cpu.h" +#include "periph/gpio.h" +#include "periph/adc.h" +#include "periph_conf.h" +#include "mutex.h" + +#define ENABLE_DEBUG (1) +#include "debug.h" + +/* Only if we actually have any ADCs */ +#if ADC_NUMOF + +/* ADC 0 device configuration */ +#define ADC_0_DEV ADC +#define ADC_0_IRQ ADC_IRQn + +/* Prototypes */ +static bool _adc_syncing(void); +static void _adc_poweroff(void); +static int _adc_configure(adc_res_t res); + +static mutex_t _lock = MUTEX_INIT; + +static inline void _prep(void) +{ + mutex_lock(&_lock); +} + +static inline void _done(void) +{ + mutex_unlock(&_lock); +} + +static bool _adc_syncing(void) +{ +#ifdef CPU_SAMD21 + if (ADC_0_DEV->STATUS.reg & ADC_STATUS_SYNCBUSY) { + return true; + } +#else /* CPU_SAML21 */ + if (ADC_0_DEV->SYNCBUSY.reg) { + return true; + } +#endif + return false; +} + +static void _adc_poweroff(void) +{ + while (_adc_syncing()) {} + /* Disable */ + ADC_0_DEV->CTRLA.reg &= ~ADC_CTRLA_ENABLE; + while (_adc_syncing()) {} + /* Disable bandgap */ +#ifdef CPU_SAMD21 + if (ADC_0_REF_DEFAULT == ADC_REFCTRL_REFSEL_INT1V) { + SYSCTRL->VREF.reg &= ~SYSCTRL_VREF_BGOUTEN; + } +#else /* CPU_SAML21 */ + if (ADC_0_REF_DEFAULT == ADC_REFCTRL_REFSEL_INTREF) { + SUPC->VREF.reg &= ~SUPC_VREF_VREFOE; + } +#endif +} + +static int _adc_configure(adc_res_t res) +{ + assert(res >= ADC_RES_8BIT && res <= ADC_RES_12BIT); + _adc_poweroff(); + if (ADC_0_DEV->CTRLA.reg & ADC_CTRLA_SWRST || + ADC_0_DEV->CTRLA.reg & ADC_CTRLA_ENABLE ) { + _done(); + DEBUG("adc: not ready\n"); + return -1; + } +#ifdef CPU_SAMD21 + /* Power On */ + PM->APBCMASK.reg |= PM_APBCMASK_ADC; + /* GCLK Setup */ + GCLK->CLKCTRL.reg = (uint32_t)(GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | + (GCLK_CLKCTRL_ID(ADC_GCLK_ID))); + /* Configure CTRLB Register HERE IS THE RESOLUTION SET! */ + ADC_0_DEV->CTRLB.reg = ADC_0_PRESCALER | res; + /* Load the fixed device calibration constants */ + ADC_0_DEV->CALIB.reg = + ADC_CALIB_BIAS_CAL((*(uint32_t*)ADC_FUSES_BIASCAL_ADDR >> + ADC_FUSES_BIASCAL_Pos)) | + ADC_CALIB_LINEARITY_CAL((*(uint64_t*)ADC_FUSES_LINEARITY_0_ADDR >> + ADC_FUSES_LINEARITY_0_Pos)); + /* Set Voltage Reference */ + ADC_0_DEV->REFCTRL.reg = ADC_0_REF_DEFAULT; + /* Disable all interrupts */ + ADC_0_DEV->INTENCLR.reg = (ADC_INTENCLR_SYNCRDY) | (ADC_INTENCLR_WINMON) | + (ADC_INTENCLR_OVERRUN) | (ADC_INTENCLR_RESRDY); + while (_adc_syncing()) {} + /* Enable bandgap if VREF is internal 1V */ + if (ADC_0_REF_DEFAULT == ADC_REFCTRL_REFSEL_INT1V) { + SYSCTRL->VREF.reg |= SYSCTRL_VREF_BGOUTEN; + } +#else /* CPU_SAML21 */ + /* Power on */ + MCLK->APBDMASK.reg |= MCLK_APBDMASK_ADC; + /* GCLK Setup */ + GCLK->PCHCTRL[ADC_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0; + /* Set Voltage Reference */ + ADC_0_DEV->REFCTRL.reg = ADC_0_REF_DEFAULT; + /* Configure CTRLB & CTRLC Register */ + ADC_0_DEV->CTRLB.reg = ADC_0_PRESCALER; + ADC_0_DEV->CTRLC.reg |= res; + /* Disable all interrupts */ + ADC_0_DEV->INTENCLR.reg = ADC_INTENCLR_WINMON | ADC_INTENCLR_OVERRUN | + ADC_INTENCLR_RESRDY; + /* Set default calibration from NVM */ + ADC_0_DEV->CALIB.reg = + ADC_FUSES_BIASCOMP((*(uint32_t*)ADC_FUSES_BIASCOMP_ADDR)) >> + ADC_CALIB_BIASCOMP_Pos | + ADC_FUSES_BIASREFBUF((*(uint32_t*)ADC_FUSES_BIASREFBUF_ADDR) >> + ADC_FUSES_BIASREFBUF_Pos); + while (_adc_syncing()) {} + /* Enable bandgap if necessary */ + if (ADC_0_REF_DEFAULT == ADC_REFCTRL_REFSEL_INTREF) { + SUPC->VREF.reg |= SUPC_VREF_VREFOE; + } +#endif + /* Enable ADC Module */ + ADC_0_DEV->CTRLA.reg |= ADC_CTRLA_ENABLE; + while (_adc_syncing()) {} + return 0; +} + +int adc_init(adc_t line) +{ + _prep(); + gpio_init(adc_channels[line].pin, GPIO_IN); + gpio_init_mux(adc_channels[line].pin, GPIO_MUX_B); + _done(); + return 0; +} + +int adc_sample(adc_t line, adc_res_t res) +{ + if (line >= ADC_NUMOF) { + DEBUG("adc: line arg not applicable\n"); + return -1; + } + _prep(); + if (_adc_configure(res) != 0) { + _done(); + DEBUG("adc: configuration failed\n"); + return -1; + } +#ifdef CPU_SAMD21 + ADC_0_DEV->INPUTCTRL.reg = ADC_0_GAIN_FACTOR_DEFAULT | + adc_channels[line].muxpos | ADC_0_NEG_INPUT; +#else /* CPU_SAML21 */ + ADC_0_DEV->INPUTCTRL.reg = adc_channels[line].muxpos | ADC_0_NEG_INPUT; +#endif + while (_adc_syncing()) {} + /* Start the conversion */ + ADC_0_DEV->SWTRIG.reg = ADC_SWTRIG_START; + /* Wait for the result */ + while (!(ADC_0_DEV->INTFLAG.reg & ADC_INTFLAG_RESRDY)) {} + int result = ADC_0_DEV->RESULT.reg; + _adc_poweroff(); + _done(); + return result; +} + +#endif /* #if ADC_NUMOF */ diff --git a/cpu/samd21/include/periph_cpu.h b/cpu/samd21/include/periph_cpu.h index c2ec27b986..81cdcdcbff 100644 --- a/cpu/samd21/include/periph_cpu.h +++ b/cpu/samd21/include/periph_cpu.h @@ -99,14 +99,6 @@ typedef struct { uart_txpad_t tx_pad; /**< pad selection for TX line */ } uart_conf_t; -/** - * @brief ADC Channel Configuration - */ -typedef struct { - gpio_t pin; /**< ADC channel pin */ - uint32_t muxpos; /**< ADC channel pin multiplexer value */ -} adc_conf_chan_t; - /** * @brief Return the numeric id of a SERCOM device derived from its address * @@ -119,6 +111,20 @@ static inline int _sercom_id(SercomUsart *sercom) return ((((uint32_t)sercom) >> 10) & 0x7) - 2; } +/** + * @brief Override the ADC resolution configuration + * @{ + */ +#define HAVE_ADC_RES_T +typedef enum { + ADC_RES_6BIT = 0xff, /**< not supported */ + ADC_RES_8BIT = ADC_CTRLB_RESSEL_8BIT, /**< ADC resolution: 8 bit */ + ADC_RES_10BIT = ADC_CTRLB_RESSEL_10BIT, /**< ADC resolution: 10 bit */ + ADC_RES_12BIT = ADC_CTRLB_RESSEL_12BIT, /**< ADC resolution: 12 bit */ + ADC_RES_14BIT = 0xfe, /**< not supported */ + ADC_RES_16BIT = 0xfd /**< not supported */ +} adc_res_t; +/** @} */ #ifdef __cplusplus } #endif diff --git a/cpu/samd21/periph/adc.c b/cpu/samd21/periph/adc.c deleted file mode 100644 index c1770fc819..0000000000 --- a/cpu/samd21/periph/adc.c +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2017 Dan Evans - * Copyright (C) 2017 Travis Griggs - * - * 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. - */ - -#include -#include "cpu.h" -#include "periph/gpio.h" -#include "periph/adc.h" -#include "periph_conf.h" -#include "mutex.h" - -/* Only if we actually have any ADCs */ -#if ADC_NUMOF - -/* Prototypes */ -static bool _adc_syncing(void); -static void _adc_powerOff(void); -static int _adc_configure(uint32_t resolution); - -static mutex_t _lock = MUTEX_INIT; - -static inline void _prep(void) -{ - mutex_lock(&_lock); -} - -static inline void _done(void) -{ - mutex_unlock(&_lock); -} - -static bool _adc_syncing(void) -{ - if (ADC_0_DEV->STATUS.reg & ADC_STATUS_SYNCBUSY) { - return true; - } - return false; -} - -static void _adc_powerOff(void) -{ - while (_adc_syncing()) {} - /* Disable */ - ADC_0_DEV->CTRLA.reg &= ~ADC_CTRLA_ENABLE; - /* Reset */ - ADC_0_DEV->CTRLA.reg |= ADC_CTRLA_SWRST; - /* Disable bandgap */ - if (ADC_0_REF_DEFAULT == ADC_REFCTRL_REFSEL_INT1V) { - SYSCTRL->VREF.reg &= ~SYSCTRL_VREF_BGOUTEN; - } -} - -static int _adc_configure(uint32_t resolution) -{ - while (_adc_syncing()) {} - /* Disable ADC Module before init */ - ADC_0_DEV->CTRLA.reg &= ~ADC_CTRLA_ENABLE; - /* Power On */ - PM->APBCMASK.reg |= PM_APBCMASK_ADC; - if (ADC_0_DEV->CTRLA.reg & ADC_CTRLA_SWRST) { - _done(); - return -1; - } - if (ADC_0_DEV->CTRLA.reg & ADC_CTRLA_ENABLE) { - _done(); - return -1; - } - /* GCLK Setup */ - GCLK->CLKCTRL.reg = (uint32_t)((GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN_GCLK0 | (ADC_GCLK_ID << GCLK_CLKCTRL_ID_Pos))); - /* Set Voltage Reference */ - ADC_0_DEV->REFCTRL.reg = ADC_0_REF_DEFAULT; - /* Configure CTRLB Register HERE IS THE RESOLUTION SET! */ - ADC_0_DEV->CTRLB.reg = ADC_0_PRESCALER | resolution; - /* Disable all interrupts */ - ADC_0_DEV->INTENCLR.reg = - (1 << ADC_INTENCLR_SYNCRDY_Pos) | - (1 << ADC_INTENCLR_WINMON_Pos) | - (1 << ADC_INTENCLR_OVERRUN_Pos) | - (1 << ADC_INTENCLR_RESRDY_Pos); - /* Load the fixed device calibration constants */ - ADC_0_DEV->CALIB.reg = - ADC_CALIB_BIAS_CAL((*(uint32_t*)ADC_FUSES_BIASCAL_ADDR >> ADC_FUSES_BIASCAL_Pos)) | - ADC_CALIB_LINEARITY_CAL((*(uint64_t*)ADC_FUSES_LINEARITY_0_ADDR >> ADC_FUSES_LINEARITY_0_Pos)); - while (_adc_syncing()) {} - /* Enable bandgap if VREF is internal 1V */ - if (ADC_0_REF_DEFAULT == ADC_REFCTRL_REFSEL_INT1V) { - SYSCTRL->VREF.reg |= SYSCTRL_VREF_BGOUTEN; - } - /* Enable ADC Module */ - ADC_0_DEV->CTRLA.reg |= ADC_CTRLA_ENABLE; - return 0; -} - -int adc_init(adc_t line) -{ - _prep(); - gpio_init(adc_channels[line].pin, GPIO_IN); - gpio_init_mux(adc_channels[line].pin, GPIO_MUX_B); - _done(); - return 0; -} - -int adc_sample(adc_t line, adc_res_t res) -{ - if (line >= ADC_0_CHANNELS) { - return -1; - } - uint32_t resolution; - switch (res) { - case ADC_RES_8BIT: - resolution = ADC_CTRLB_RESSEL_8BIT; - break; - case ADC_RES_10BIT: - resolution = ADC_CTRLB_RESSEL_10BIT; - break; - case ADC_RES_12BIT: - resolution = ADC_CTRLB_RESSEL_12BIT; - break; - default: - resolution = 0xFFFFFFFF; - return -1; - } - _prep(); - if (_adc_configure(resolution) != 0) { - _done(); - return -1; - } - ADC_0_DEV->INPUTCTRL.reg = ADC_0_GAIN_FACTOR_DEFAULT | adc_channels[line].muxpos | ADC_0_NEG_INPUT; - while (_adc_syncing()) {} - /* Start the conversion */ - ADC_0_DEV->SWTRIG.reg = ADC_SWTRIG_START; - /* Wait for the result */ - while (!(ADC_0_DEV->INTFLAG.reg & ADC_INTFLAG_RESRDY)) {} - int result = ADC_0_DEV->RESULT.reg; - _adc_powerOff(); - _done(); - return result; -} - -#endif /* #if ADC_NUMOF */ diff --git a/cpu/saml21/include/periph_cpu.h b/cpu/saml21/include/periph_cpu.h index 3bdbeaf734..76bee3eb7e 100644 --- a/cpu/saml21/include/periph_cpu.h +++ b/cpu/saml21/include/periph_cpu.h @@ -43,14 +43,6 @@ enum { */ #define GPIO_MODE(pr, ie, pe) (pr | (ie << 1) | (pe << 2)) -/** - * @brief ADC Channel Configuration - */ -typedef struct { - gpio_t pin; /**< ADC channel pin */ - uint32_t muxpos; /**< ADC channel pin multiplexer value */ -} adc_channel_t; - #ifndef DOXYGEN /** * @brief Override GPIO modes @@ -68,6 +60,16 @@ typedef enum { /** @} */ #endif /* ndef DOXYGEN */ +#define HAVE_ADC_RES_T +typedef enum { + ADC_RES_6BIT = 0xff, /**< not supported */ + ADC_RES_8BIT = ADC_CTRLC_RESSEL_8BIT, /**< ADC resolution: 8 bit */ + ADC_RES_10BIT = ADC_CTRLC_RESSEL_10BIT, /**< ADC resolution: 10 bit */ + ADC_RES_12BIT = ADC_CTRLC_RESSEL_12BIT, /**< ADC resolution: 12 bit */ + ADC_RES_14BIT = 0xfe, /**< not supported */ + ADC_RES_16BIT = 0xfd /**< not supported */ +} adc_res_t; +/** @} */ #ifdef __cplusplus } #endif diff --git a/cpu/saml21/periph/adc.c b/cpu/saml21/periph/adc.c deleted file mode 100644 index 0916f7e8eb..0000000000 --- a/cpu/saml21/periph/adc.c +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (C) 2017 Dylan Laduranty - * - * 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. - */ - -#include -#include "cpu.h" -#include "periph/gpio.h" -#include "periph/adc.h" -#include "periph_conf.h" -#include "mutex.h" - -#define ENABLE_DEBUG (0) -#include "debug.h" - -/* Only if we actually have any ADCs */ -#if ADC_NUMOF - -/* Prototypes */ -static void _adc_poweroff(void); -static int _adc_configure(uint32_t resolution); - -static mutex_t _lock = MUTEX_INIT; - -static inline void _prep(void) -{ - mutex_lock(&_lock); -} - -static inline void _done(void) -{ - mutex_unlock(&_lock); -} - -static void _adc_poweroff(void) -{ - /* Disable */ - ADC_0_DEV->CTRLA.reg &= ~ADC_CTRLA_ENABLE; - while (ADC_0_DEV->SYNCBUSY.reg); - /* Disable bandgap */ - if (ADC_0_REF_DEFAULT == ADC_REFCTRL_REFSEL_INTREF) { - SUPC->VREF.reg &= ~SUPC_VREF_VREFOE; - } -} - -static int _adc_configure(uint32_t resolution) -{ - _adc_poweroff(); - if (ADC_0_DEV->CTRLA.reg & ADC_CTRLA_SWRST || - ADC_0_DEV->CTRLA.reg & ADC_CTRLA_ENABLE ) { - _done(); - DEBUG("adc: not ready\n"); - return -1; - } - /* GCLK Setup */ - GCLK->PCHCTRL[ADC_GCLK_ID].reg = GCLK_PCHCTRL_CHEN | GCLK_PCHCTRL_GEN_GCLK0; - /* Set Voltage Reference */ - ADC_0_DEV->REFCTRL.reg = ADC_0_REF_DEFAULT; - /* Configure CTRLB & CTRLC Register */ - ADC_0_DEV->CTRLB.reg = ADC_0_PRESCALER; - ADC_0_DEV->CTRLC.reg |= resolution; - /* Disable all interrupts */ - ADC_0_DEV->INTENCLR.reg = ADC_INTENCLR_WINMON |ADC_INTENCLR_OVERRUN | - ADC_INTENCLR_RESRDY; - /* Set default calibration from NVM */ - ADC_0_DEV->CALIB.reg = - ADC_FUSES_BIASCOMP(*(uint32_t*)ADC_FUSES_BIASCOMP_ADDR) | - ADC_FUSES_BIASREFBUF(*(uint32_t*)ADC_FUSES_BIASREFBUF_ADDR); - - while (ADC_0_DEV->SYNCBUSY.reg); - /* Enable bandgap if necessary */ - if (ADC_0_REF_DEFAULT == ADC_REFCTRL_REFSEL_INTREF) { - SUPC->VREF.reg |= SUPC_VREF_VREFOE; - } - /* Enable ADC Module */ - ADC_0_DEV->CTRLA.reg |= ADC_CTRLA_ENABLE; - /* Wait for sync */ - while (ADC_0_DEV->SYNCBUSY.reg); - return 0; -} - -int adc_init(adc_t line) -{ - _prep(); - gpio_init(adc_channels[line].pin, GPIO_IN); - gpio_init_mux(adc_channels[line].pin, GPIO_MUX_B); - MCLK->APBDMASK.reg |= MCLK_APBDMASK_ADC; - _done(); - return 0; -} - -int adc_sample(adc_t line, adc_res_t res) -{ - if (line >= ADC_0_CHANNELS) { - DEBUG("adc: line arg not applicable\n"); - return -1; - } - uint32_t resolution; - switch (res) { - case ADC_RES_8BIT: - resolution = ADC_CTRLC_RESSEL_8BIT; - break; - case ADC_RES_10BIT: - resolution = ADC_CTRLC_RESSEL_10BIT; - break; - case ADC_RES_12BIT: - resolution = ADC_CTRLC_RESSEL_12BIT; - break; - default: - resolution = 0xFFFFFFFF; - DEBUG("adc: Resolution arg not applicable\n"); - return -1; - } - _prep(); - if (_adc_configure(resolution) != 0) { - _done(); - DEBUG("adc: configuration failed\n"); - return -1; - } - ADC_0_DEV->INPUTCTRL.reg = adc_channels[line].muxpos | ADC_0_NEG_INPUT; - /* Wait for sync */ - while (ADC_0_DEV->SYNCBUSY.reg); - /* Start the conversion */ - ADC_0_DEV->SWTRIG.reg = ADC_SWTRIG_START; - /* Wait for the result */ - while (!(ADC_0_DEV->INTFLAG.reg & ADC_INTFLAG_RESRDY)); - int result = ADC_0_DEV->RESULT.reg; - _adc_poweroff(); - _done(); - return result; -} - -#endif /* #if ADC_NUMOF */