diff --git a/cpu/nrf51/include/periph_cpu.h b/cpu/nrf51/include/periph_cpu.h index 251bd319c7166af7f5b2e14b8fc4c46791740e84..e4eac37287f73bdedd3eb4681e32a8e0d7dd014a 100644 --- a/cpu/nrf51/include/periph_cpu.h +++ b/cpu/nrf51/include/periph_cpu.h @@ -54,6 +54,21 @@ typedef enum { } i2c_speed_t; /** @} */ +/** + * @brief Override ADC resolution values + * @{ + */ +#define HAVE_ADC_RES_T +typedef enum { + ADC_RES_6BIT = 0xf0, /**< ADC resolution: 6 bit */ + ADC_RES_8BIT = 0x00, /**< ADC resolution: 8 bit */ + ADC_RES_10BIT = 0x02, /**< ADC resolution: 10 bit */ + ADC_RES_12BIT = 0xf1, /**< ADC resolution: 12 bit */ + ADC_RES_14BIT = 0xf2, /**< ADC resolution: 14 bit */ + ADC_RES_16BIT = 0xf3 /**< ADC resolution: 16 bit */ +} adc_res_t; +/** @} */ + /** * @brief I2C (TWI) configuration options */ diff --git a/cpu/nrf52/include/periph_cpu.h b/cpu/nrf52/include/periph_cpu.h index 38c7726fc95c6637cac5abcf571664fafd3d83d5..9b8adc33abcb4f227c4e6435c3704a539f393853 100644 --- a/cpu/nrf52/include/periph_cpu.h +++ b/cpu/nrf52/include/periph_cpu.h @@ -40,6 +40,30 @@ extern "C" { #define SPI_MISOSEL (dev(bus)->PSEL.MISO) /** @} */ +/** + * @brief Override ADC resolution values + * @{ + */ +#define HAVE_ADC_RES_T +typedef enum { + ADC_RES_6BIT = 0xf0, /**< not supported by hardware */ + ADC_RES_8BIT = 0x00, /**< ADC resolution: 8 bit */ + ADC_RES_10BIT = 0x01, /**< ADC resolution: 10 bit */ + ADC_RES_12BIT = 0x02, /**< ADC resolution: 12 bit */ + ADC_RES_14BIT = 0xf1, /**< supported with oversampling */ + ADC_RES_16BIT = 0xf2 /**< not supported by hardware */ +} adc_res_t; +/** @} */ + +/** + * @name ADC configuration, valid for all boards using this CPU + * + * The NRF52832 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 (8U) + #ifdef __cplusplus } #endif diff --git a/cpu/nrf52/periph/adc.c b/cpu/nrf52/periph/adc.c new file mode 100644 index 0000000000000000000000000000000000000000..42b3043b7fefb8e1c9dbb3e5ca921c329ffc689a --- /dev/null +++ b/cpu/nrf52/periph/adc.c @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2017 HAW Hamburg + * + * 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_nrf52 + * @{ + * + * Any one of the available channels can be enabled for the ADC to operate in + * one-shot mode. If more than one CH[n] is configured, the ADC enters scan mode. + * + * Example of RAM placement (RESULT.MAXCNT = 8), all channels enabled: + * + * 31 16 15 0 + * RESULT.PTR = adc_val[0] | CH[1] | CH[0] | + * RESULT.PTR + 4 | CH[3] | CH[2] | + * RESULT.PTR + 8 | CH[5] | CH[4] | + * RESULT.PTR + 12 | CH[7] | CH[6] | + * + * Example of RAM placement (RESULT.MAXCNT = 4), channels 0, 3, 4 and 7 enabled: + * + * 31 16 15 0 + * RESULT.PTR = adc_val[0] | CH[3] | CH[0] | + * RESULT.PTR + 4 | CH[7] | CH[4] | + * RESULT.PTR + 8 | | | + * RESULT.PTR + 12 | | | + * + * Scan mode and oversampling cannot be combined. + * -> 8/10/12-bit resolution, 14-bit resolution only with oversampling + * + * @file + * @brief Low-level ADC driver implementation + * + * @author Dimitri Nahm <dimitri.nahm@haw-hamburg.de> + * + * @} + */ + +#include "cpu.h" +#include "mutex.h" +#include "periph/adc.h" +#include "periph_conf.h" + +#ifdef ADC_NUMOF + +static int16_t adc_val[ADC_NUMOF]; +static uint8_t adc_ch_enabled = 0; +static uint8_t adc_return_idx; + +/** + * @brief Lock to prevent concurrency issues when used from different threads + */ +static mutex_t lock; + +static inline void prep(void) +{ + mutex_lock(&lock); + NRF_SAADC->ENABLE = 1; +} + +static inline void done(void) +{ + NRF_SAADC->ENABLE = 0; + mutex_unlock(&lock); +} + +int adc_init(adc_t line) +{ + if (line >= ADC_NUMOF) { + return -1; + } + + if ((adc_ch_enabled & (1 << line)) == 0) { + /* prepare device */ + prep(); + + /* set the number of enabled channels and the data pointer */ + NRF_SAADC->RESULT.MAXCNT += 1; + NRF_SAADC->RESULT.PTR = (uint32_t)&adc_val; + + /* set ADC channel and use VDD (+5V) as reference */ + NRF_SAADC->CH[line].PSELP = line + 1; + NRF_SAADC->CH[line].CONFIG = (SAADC_CH_CONFIG_GAIN_Gain1_4 << SAADC_CH_CONFIG_RESN_Pos) | + (SAADC_CH_CONFIG_REFSEL_VDD1_4 << SAADC_CH_CONFIG_REFSEL_Pos); + + /* calibrate the SAADC */ + NRF_SAADC->EVENTS_CALIBRATEDONE = 0; + NRF_SAADC->TASKS_CALIBRATEOFFSET = 1; + while (NRF_SAADC->EVENTS_CALIBRATEDONE == 0) {} + + /* remember which ADC channel is enabled */ + adc_ch_enabled |= (1 << line); + + /* free device */ + done(); + } + + return 0; +} + +int adc_sample(adc_t line, adc_res_t res) +{ + assert(line < ADC_NUMOF); + + /* check if resolution is valid */ + if (res > 2) { + return -1; + } + + /* prepare device */ + prep(); + + /* set resolution */ + NRF_SAADC->RESOLUTION = res; + + /* start the SAADC and wait for the started event */ + NRF_SAADC->EVENTS_STARTED = 0; + NRF_SAADC->TASKS_START = 1; + while (NRF_SAADC->EVENTS_STARTED == 0) {} + + /* start conversions and wait for conversions to be complete */ + for (uint8_t i = 0; i < NRF_SAADC->RESULT.MAXCNT; i++) { + while (NRF_SAADC->STATUS == (SAADC_STATUS_STATUS_Busy << SAADC_STATUS_STATUS_Pos)) {} + NRF_SAADC->EVENTS_DONE = 0; + NRF_SAADC->TASKS_SAMPLE = 1; + while (NRF_SAADC->EVENTS_DONE == 0) {} + } + + /* stop the SAADC */ + NRF_SAADC->EVENTS_STOPPED = 0; + NRF_SAADC->TASKS_STOP = 1; + while (NRF_SAADC->EVENTS_STOPPED == 0) {} + + /* free device */ + done(); + + /* return the ADC value for the given line */ + adc_return_idx = 0; + for (uint8_t i = 0; i < line; i++) { + if (adc_ch_enabled & (1 << i)) { + adc_return_idx++; + } + } + if (adc_val[adc_return_idx] < 0) { + return 0; + } + return adc_val[adc_return_idx]; +} + +#else +typedef int dont_be_pedantic; +#endif /* ADC_CONFIG */ diff --git a/cpu/nrf5x_common/include/periph_cpu_common.h b/cpu/nrf5x_common/include/periph_cpu_common.h index 78d1ab0491bc050bc2cc63fe8142d0f12edecafd..608883c8e6a8f53379826f81b44b84b17edafa08 100644 --- a/cpu/nrf5x_common/include/periph_cpu_common.h +++ b/cpu/nrf5x_common/include/periph_cpu_common.h @@ -109,21 +109,6 @@ typedef enum { GPIO_BOTH = 3 /**< emit interrupt on both flanks */ } gpio_flank_t; /** @} */ - -/** - * @brief Override ADC resolution values - * @{ - */ -#define HAVE_ADC_RES_T -typedef enum { - ADC_RES_6BIT = 0xf0, /**< ADC resolution: 6 bit */ - ADC_RES_8BIT = 0x00, /**< ADC resolution: 8 bit */ - ADC_RES_10BIT = 0x02, /**< ADC resolution: 10 bit */ - ADC_RES_12BIT = 0xf1, /**< ADC resolution: 12 bit */ - ADC_RES_14BIT = 0xf2, /**< ADC resolution: 14 bit */ - ADC_RES_16BIT = 0xf3 /**< ADC resolution: 16 bit */ -} adc_res_t; -/** @} */ #endif /* ndef DOXYGEN */ /**