1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-01-18 12:52:44 +01:00

cpu/nrf52: some optimizations for ADC driver

This commit is contained in:
Hauke Petersen 2017-10-09 15:30:50 +02:00
parent 6f9a5004c6
commit b3654c2ead
4 changed files with 85 additions and 81 deletions

View File

@ -60,12 +60,12 @@ typedef enum {
*/
#define HAVE_ADC_RES_T
typedef enum {
ADC_RES_6BIT = 0xf0, /**< ADC resolution: 6 bit */
ADC_RES_6BIT = 0xf0, /**< ADC resolution: 6 bit (not supported) */
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_12BIT = 0xf1, /**< ADC resolution: 12 bit (not supported) */
ADC_RES_14BIT = 0xf2, /**< ADC resolution: 14 bit (not supported) */
ADC_RES_16BIT = 0xf3 /**< ADC resolution: 16 bit (not supported) */
} adc_res_t;
/** @} */

View File

@ -1 +1,4 @@
# The ADC does not depend on any board configuration, so always available
FEATURES_PROVIDED += periph_adc
-include $(RIOTCPU)/nrf5x_common/Makefile.features

View File

@ -40,6 +40,26 @@ extern "C" {
#define SPI_MISOSEL (dev(bus)->PSEL.MISO)
/** @} */
/**
* @brief The nRF52 family of CPUs provides a fixed number of 9 ADC lines
*/
#define ADC_NUMOF (9U)
/**
* @brief nRF52 specific naming of ADC lines (for convenience)
*/
enum {
NRF52_AIN0 = 0, /**< Analog Input 0 */
NRF52_AIN1 = 1, /**< Analog Input 1 */
NRF52_AIN2 = 2, /**< Analog Input 2 */
NRF52_AIN3 = 3, /**< Analog Input 3 */
NRF52_AIN4 = 4, /**< Analog Input 4 */
NRF52_AIN5 = 5, /**< Analog Input 5 */
NRF52_AIN6 = 6, /**< Analog Input 6 */
NRF52_AIN7 = 7, /**< Analog Input 7 */
NRF52_VDD = 8, /**< VDD, not useful if VDD is reference... */
};
/**
* @brief Override ADC resolution values
* @{
@ -50,20 +70,11 @@ typedef enum {
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_14BIT = 0xf1, /**< supported with oversampling, not implemented */
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

View File

@ -1,5 +1,6 @@
/*
* Copyright (C) 2017 HAW Hamburg
* 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
@ -10,32 +11,11 @@
* @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>
* @author Hauke Petersen <hauke.petersen@fu-berlin.de>
*
* @}
*/
@ -45,16 +25,35 @@
#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;
/**
* @name Default ADC reference, gain configuration and acquisition time
*
* Can be overridden by the board configuration if needed. The default
* configuration uses the full VDD (typically 3V3) as reference and samples for
* 10us.
* @{
*/
#ifndef ADC_REF
#define ADC_REF SAADC_CH_CONFIG_REFSEL_VDD1_4
#endif
#ifndef ADC_GAIN
#define ADC_GAIN SAADC_CH_CONFIG_GAIN_Gain1_4
#endif
#ifndef ADC_TACQ
#define ADC_TACQ SAADC_CH_CONFIG_TACQ_10us
#endif
/** @} */
/**
* @brief Lock to prevent concurrency issues when used from different threads
*/
static mutex_t lock;
static mutex_t lock = MUTEX_INIT;
/**
* @brief We use a static result buffer so we do not have to reprogram the
* result pointer register
*/
static int16_t result;
static inline void prep(void)
{
@ -74,31 +73,33 @@ int adc_init(adc_t line)
return -1;
}
if ((adc_ch_enabled & (1 << line)) == 0) {
/* prepare device */
prep();
prep();
/* set the number of enabled channels and the data pointer */
NRF_SAADC->RESULT.MAXCNT += 1;
NRF_SAADC->RESULT.PTR = (uint32_t)&adc_val;
/* prevent multiple initialization by checking the result ptr register */
if (NRF_SAADC->RESULT.PTR != (uint32_t)&result) {
/* set data pointer and the single channel we want to convert */
NRF_SAADC->RESULT.MAXCNT = 1;
NRF_SAADC->RESULT.PTR = (uint32_t)&result;
/* 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);
/* configure the first channel (the only one we use):
* - bypass resistor ladder+
* - acquisition time as defined by board (or 10us as default)
* - reference and gain as defined by board (or VDD as default)
* - no oversampling */
NRF_SAADC->CH[0].CONFIG = ((ADC_GAIN << SAADC_CH_CONFIG_GAIN_Pos) |
(ADC_REF << SAADC_CH_CONFIG_REFSEL_Pos) |
(ADC_TACQ << SAADC_CH_CONFIG_TACQ_Pos));
NRF_SAADC->CH[0].PSELN = SAADC_CH_PSELN_PSELN_NC;
NRF_SAADC->OVERSAMPLE = SAADC_OVERSAMPLE_OVERSAMPLE_Bypass;
/* calibrate the SAADC */
/* calibrate 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();
}
done();
return 0;
}
@ -116,19 +117,18 @@ int adc_sample(adc_t line, adc_res_t res)
/* set resolution */
NRF_SAADC->RESOLUTION = res;
/* set line to sample */
NRF_SAADC->CH[0].PSELP = (line + 1);
/* 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) {}
}
/* trigger the actual conversion */
NRF_SAADC->EVENTS_END = 0;
NRF_SAADC->TASKS_SAMPLE = 1;
while (NRF_SAADC->EVENTS_END == 0) {}
/* stop the SAADC */
NRF_SAADC->EVENTS_STOPPED = 0;
@ -138,19 +138,9 @@ int adc_sample(adc_t line, adc_res_t res)
/* 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];
/* hack -> the result can be a small negative number when a AINx pin is
* connected via jumper wire a the board's GND pin. There seems to be a
* slight difference between the internal CPU GND and the board's GND
* voltage levels?! (observed on nrf52dk and nrf52840dk) */
return (result < 0) ? 0 : (int)result;
}
#else
typedef int dont_be_pedantic;
#endif /* ADC_CONFIG */