diff --git a/cpu/sam0_common/Kconfig b/cpu/sam0_common/Kconfig index 2f9b333e95..fe4c1afc42 100644 --- a/cpu/sam0_common/Kconfig +++ b/cpu/sam0_common/Kconfig @@ -8,6 +8,7 @@ config CPU_COMMON_SAM0 bool select HAS_PERIPH_CPUID + select HAS_PERIPH_ADC_CONTINUOUS select HAS_PERIPH_FLASHPAGE select HAS_PERIPH_FLASHPAGE_IN_ADDRESS_SPACE select HAS_PERIPH_FLASHPAGE_PAGEWISE diff --git a/cpu/sam0_common/Makefile.features b/cpu/sam0_common/Makefile.features index 2d2035c20d..2f01bb135e 100644 --- a/cpu/sam0_common/Makefile.features +++ b/cpu/sam0_common/Makefile.features @@ -7,6 +7,7 @@ ifeq (,$(filter $(CPU_MODELS_WITHOUT_DMA),$(CPU_MODEL))) FEATURES_PROVIDED += periph_dma endif +FEATURES_PROVIDED += periph_adc_continuous FEATURES_PROVIDED += periph_flashpage FEATURES_PROVIDED += periph_flashpage_in_address_space FEATURES_PROVIDED += periph_flashpage_pagewise diff --git a/cpu/sam0_common/periph/adc.c b/cpu/sam0_common/periph/adc.c index 59c236a7e8..cf65a41605 100644 --- a/cpu/sam0_common/periph/adc.c +++ b/cpu/sam0_common/periph/adc.c @@ -49,16 +49,6 @@ static int _adc_configure(Adc *dev, 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 inline void _wait_syncbusy(Adc *dev) { #ifdef ADC_STATUS_SYNCBUSY @@ -263,7 +253,7 @@ int adc_init(adc_t line) const uint8_t adc = 0; #endif - _prep(); + mutex_lock(&_lock); uint8_t muxpos = (adc_channels[line].inputctrl & ADC_INPUTCTRL_MUXPOS_Msk) >> ADC_INPUTCTRL_MUXPOS_Pos; @@ -284,34 +274,44 @@ int adc_init(adc_t line) gpio_init_mux(sam0_adc_pins[adc][muxneg], GPIO_MUX_B); } - _done(); + mutex_unlock(&_lock); return 0; } -int32_t adc_sample(adc_t line, adc_res_t res) +static Adc *_dev(adc_t line) { - if (line >= ADC_NUMOF) { - DEBUG("adc: line arg not applicable\n"); - return -1; - } - /* The SAMD5x/SAME5x family has two ADCs: ADC0 and ADC1. */ #ifdef ADC0 - Adc *dev = adc_channels[line].dev; + return adc_channels[line].dev; #else - Adc *dev = ADC; + (void)line; + return ADC; #endif +} - bool diffmode = adc_channels[line].inputctrl & ADC_INPUTCTRL_DIFFMODE; - - _prep(); - - if (_adc_configure(dev, res) != 0) { - _done(); - DEBUG("adc: configuration failed\n"); - return -1; +static Adc *_adc(uint8_t dev) +{ + /* The SAMD5x/SAME5x family has two ADCs: ADC0 and ADC1. */ +#ifdef ADC0 + switch (dev) { + case 0: + return ADC0; + case 1: + return ADC1; + default: + return NULL; } +#else + (void)dev; + return ADC; +#endif +} + +static int32_t _sample(adc_t line) +{ + Adc *dev = _dev(line); + bool diffmode = adc_channels[line].inputctrl & ADC_INPUTCTRL_DIFFMODE; dev->INPUTCTRL.reg = ADC_GAIN_FACTOR_DEFAULT | adc_channels[line].inputctrl @@ -319,7 +319,6 @@ int32_t adc_sample(adc_t line, adc_res_t res) #ifdef ADC_CTRLB_DIFFMODE dev->CTRLB.bit.DIFFMODE = diffmode; #endif - _wait_syncbusy(dev); /* Start the conversion */ @@ -331,9 +330,6 @@ int32_t adc_sample(adc_t line, adc_res_t res) uint16_t sample = dev->RESULT.reg; int result; - _adc_poweroff(dev); - _done(); - /* in differential mode we lose one bit for the sign */ if (diffmode) { result = 2 * (int16_t)sample; @@ -341,11 +337,102 @@ int32_t adc_sample(adc_t line, adc_res_t res) result = sample; } + return result; +} + +static uint8_t _shift_from_res(adc_res_t res) +{ /* 16 bit mode is implemented as oversampling */ if ((res & 0x3) == 1) { /* ADC does automatic right shifts beyond 16 samples */ - result <<= (4 - MIN(4, res >> 2)); + return 4 - MIN(4, res >> 2); + } + return 0; +} + +static void _get_adcs(bool *adc0, bool *adc1) +{ +#ifndef ADC1 + *adc0 = true; + *adc1 = false; + return; +#else + for (unsigned i = 0; i < ADC_NUMOF; ++i) { + if (adc_channels[i].dev == ADC0) { + *adc0 = true; + } else if (adc_channels[i].dev == ADC1) { + *adc1 = true; + } + } +#endif +} + +static uint8_t _shift; +void adc_continuous_begin(adc_res_t res) +{ + bool adc0, adc1; + _get_adcs(&adc0, &adc1); + + mutex_lock(&_lock); + + if (adc0) { + _adc_configure(_adc(0), res); + } + if (adc1) { + _adc_configure(_adc(1), res); } - return result; + _shift = _shift_from_res(res); +} + +int32_t adc_continuous_sample(adc_t line) +{ + int val; + assert(line < ADC_NUMOF); + + mutex_lock(&_lock); + val = _sample(line) << _shift; + mutex_unlock(&_lock); + + return val; +} + +void adc_continuous_stop(void) +{ + bool adc0, adc1; + _get_adcs(&adc0, &adc1); + + if (adc0) { + _adc_poweroff(_adc(0)); + } + if (adc1) { + _adc_poweroff(_adc(1)); + } + + mutex_unlock(&_lock); +} + +int32_t adc_sample(adc_t line, adc_res_t res) +{ + if (line >= ADC_NUMOF) { + DEBUG("adc: line arg not applicable\n"); + return -1; + } + + mutex_lock(&_lock); + + Adc *dev = _dev(line); + + if (_adc_configure(dev, res) != 0) { + DEBUG("adc: configuration failed\n"); + mutex_unlock(&_lock); + return -1; + } + + int val = _sample(line) << _shift_from_res(res); + + _adc_poweroff(dev); + mutex_unlock(&_lock); + + return val; }