diff --git a/boards/mulle/Makefile.dep b/boards/mulle/Makefile.dep index f6ca7bf86ec4..0c5290f5b0f3 100644 --- a/boards/mulle/Makefile.dep +++ b/boards/mulle/Makefile.dep @@ -18,6 +18,7 @@ USEMODULE += periph_rtt ifneq (,$(filter saul_default,$(USEMODULE))) USEMODULE += saul_gpio USEMODULE += saul_adc + USEMODULE += saul_adc_conversion endif include $(RIOTCPU)/kinetis/Makefile.dep diff --git a/boards/mulle/include/adc_params.h b/boards/mulle/include/adc_params.h index 36f6842cca1a..9817d3e97562 100644 --- a/boards/mulle/include/adc_params.h +++ b/boards/mulle/include/adc_params.h @@ -21,11 +21,41 @@ #include "board.h" #include "saul/periph.h" +#if MODULE_SAUL_ADC_CONVERSION +#include "periph/adc_conv.h" +#endif #ifdef __cplusplus extern "C" { #endif +#if MODULE_SAUL_ADC_CONVERSION +#define ADC_CONV(func) .conv = (func) +#else +#define ADC_CONV(ignore) +#endif + +#if MODULE_SAUL_ADC_CONVERSION +/** + * @brief Convert Mulle Vbat, Vchr samples into real voltage + * + * @param[in] params SAUL ADC params pointer + * @param[in] res ADC sample + * + * @return 1 + */ +static int mulle_vbat_volts(const saul_adc_params_t *params, phydat_t *res) +{ + adc_conv_saul_adc_volts(params, res); + /* The Vbat, Vchr measurement point is located on a voltage divider which + * shows half of the full voltage, to be able to measure voltages greater + * than ADC Vref. */ + phydat_fit(res, res->val[0] * 2, 0, 0); + + return 1; +} +#endif + /** * @brief ADC configuration */ @@ -40,36 +70,43 @@ static const saul_adc_params_t saul_adc_params[] = .name = "k60vrefsh", .line = ADC_LINE(2), .res = ADC_RES_16BIT, + ADC_CONV(adc_conv_saul_adc_volts), }, { .name = "k60vrefsl", .line = ADC_LINE(3), .res = ADC_RES_16BIT, + ADC_CONV(adc_conv_saul_adc_volts), }, { .name = "k60bandgap", .line = ADC_LINE(1), .res = ADC_RES_16BIT, + ADC_CONV(adc_conv_saul_adc_volts), }, { .name = "DAC0feedback", .line = ADC_LINE(4), .res = ADC_RES_16BIT, + ADC_CONV(adc_conv_saul_adc_volts), }, { .name = "VREFfeedback", .line = ADC_LINE(5), .res = ADC_RES_16BIT, + ADC_CONV(adc_conv_saul_adc_volts), }, { .name = "Vbat", .line = MULLE_VBAT_ADC_LINE, .res = ADC_RES_16BIT, + ADC_CONV(mulle_vbat_volts), }, { .name = "Vchr", .line = MULLE_VCHR_ADC_LINE, .res = ADC_RES_16BIT, + ADC_CONV(mulle_vbat_volts), }, { .name = "PGA0_DP", diff --git a/boards/mulle/include/board.h b/boards/mulle/include/board.h index b5044c9e2237..6cd643c07e15 100644 --- a/boards/mulle/include/board.h +++ b/boards/mulle/include/board.h @@ -24,6 +24,15 @@ #include "mulle-nvram.h" #include "mtd.h" +/** + * @name ADC sample conversion configuration + * @{ + */ +#define BOARD_ADC_VREFH_DEFAULT (33000) +#define BOARD_ADC_VREFL_DEFAULT ( 0) +#define BOARD_ADC_VREF_SCALE_DEFAULT (-4) +/** @} */ + /* Use the on board RTC 32kHz clock for LPTMR clocking. */ #undef LPTIMER_CLKSRC /** @brief Clock source for the LPTMR module */ diff --git a/drivers/Makefile.dep b/drivers/Makefile.dep index a59395c06b60..e8e50b5c3188 100644 --- a/drivers/Makefile.dep +++ b/drivers/Makefile.dep @@ -283,6 +283,10 @@ ifneq (,$(filter pcd8544,$(USEMODULE))) USEMODULE += xtimer endif +ifneq (,$(filter periph_common,$(USEMODULE))) + USEMODULE += analog_util +endif + ifneq (,$(filter pir,$(USEMODULE))) FEATURES_REQUIRED += periph_gpio USEMODULE += xtimer diff --git a/drivers/include/periph/adc_conv.h b/drivers/include/periph/adc_conv.h new file mode 100644 index 000000000000..ad02b1a2202b --- /dev/null +++ b/drivers/include/periph/adc_conv.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2018 Eistec AB + * + * 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 drivers_periph_adc + * @{ + * + * @file + * @brief Conversion routines for CPU ADC samples + * + * @author Joakim Nohlgård + * + * @} + */ + +#include "periph/adc.h" +#if MODULE_SAUL_ADC +#include "saul/periph.h" +#endif + +#ifndef BOARD_ADC_VREFH_DEFAULT +/** + * @brief Boot default for vrefh + */ +/* 3300 mV is as good as any other guess */ +#define BOARD_ADC_VREFH_DEFAULT (3300) +#endif + +#ifndef BOARD_ADC_VREFL_DEFAULT +/** + * @brief Boot default for vrefl + */ +/* 0 mV is as good as any other guess */ +#define BOARD_ADC_VREFL_DEFAULT (0) +#endif + +#ifndef BOARD_ADC_VREF_SCALE_DEFAULT +/** + * @brief Boot default for vref scale + */ +/* millivolts by default */ +#define BOARD_ADC_VREF_SCALE_DEFAULT (-3) +#endif + +/** + * @brief Convert ADC sample to actual voltage + * + * This function uses the stored ADC reference voltage for the conversion + * + * @param[in] sample an ADC reading + * @param[in] res ADC resolution setting + */ +int adc_conv_volts(int sample, adc_res_t res); + +/** + * @brief Update the internal upper voltage reference value + * + * This should be called if the software knows that the reference voltage for + * the ADC has changed since last update. + * + * @param[in] new_vrefh new reference voltage + */ +void adc_conv_set_vrefh(int new_vrefh); + +/** + * @brief Update the internal lower voltage reference value + * + * This should be called if the software knows that the reference voltage for + * the ADC has changed since last update. + * + * @param[in] new_vrefl new reference voltage + */ +void adc_conv_set_vrefl(int new_vrefl); + +/** + * @brief Set the ADC VREF unit scale exponent + * + * This is the 10 exponent for the value in vrefh/vrefl, i.e. + * Real Vrefh = vrefh * 10^(scale) + * For example, scale = -3 means that vrefh, vrefl are given in millivolts. + * + * @param[in] new_scale Vref unit scale exponent + */ +void adc_conv_set_scale(int new_scale); + +#if MODULE_SAUL_ADC || DOXYGEN +/** + * @brief SAUL ADC compatible conversion wrapper function + * + * @param[in] params SAUL ADC params pointer + * @param[in] res ADC sample + * + * @return 1 + */ +int adc_conv_saul_adc_volts(const saul_adc_params_t *params, phydat_t *res); +#endif diff --git a/drivers/include/saul/periph.h b/drivers/include/saul/periph.h index 74f24c68f1d4..11e4063b8a58 100644 --- a/drivers/include/saul/periph.h +++ b/drivers/include/saul/periph.h @@ -53,13 +53,28 @@ typedef struct { #endif /* MODULE_SAUL_GPIO */ #if MODULE_SAUL_ADC || DOXYGEN +#if MODULE_SAUL_ADC_CONVERSION || DOXYGEN +/* Forward declaration */ +struct saul_adc_params; + +/** + * @brief ADC result conversion function pointer type + * + * @param[in] params pointer to ADC params + * @param[in] res an ADC sample + */ +typedef int (*adc_conv_t)(const struct saul_adc_params *params, phydat_t *res); +#endif /** * @brief Direct mapped ADC configuration values */ -typedef struct { +typedef struct saul_adc_params { const char *name; /**< name of the device connected to this pin */ adc_t line; /**< ADC line to initialize and expose */ adc_res_t res; /**< ADC resolution */ +#if MODULE_SAUL_ADC_CONVERSION || DOXYGEN + adc_conv_t conv; /**< ADC result conversion function, NULL for no-op */ +#endif } saul_adc_params_t; #endif /* MODULE_SAUL_ADC */ diff --git a/drivers/periph_common/adc_conv.c b/drivers/periph_common/adc_conv.c new file mode 100644 index 000000000000..73313bde7975 --- /dev/null +++ b/drivers/periph_common/adc_conv.c @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2018 Eistec AB + * + * 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 drivers_periph_adc + * @{ + * + * @file + * @brief Implementations of conversion routines for CPU internal analog signals + * + * @author Joakim Nohlgård + * + * @} + */ + +#include + +#include "board.h" +#include "phydat.h" +#include "periph/adc.h" +#include "periph/adc_conv.h" +#include "analog_util.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +/** + * @brief ADC low side potential + * + * An ADC reading of zero means that the sampled voltage is close to or less than this value. + */ +static int vrefl = BOARD_ADC_VREFL_DEFAULT; + +/** + * @brief ADC reference voltage high potential + * + * An ADC reading of max means that the sampled voltage is close to or greater than this value + */ +static int vrefh = BOARD_ADC_VREFH_DEFAULT; + +/** + * @brief ADC unit scale exponent + * + * e.g. -3 for millivolts + */ +static int vref_scale = BOARD_ADC_VREF_SCALE_DEFAULT; + +int adc_conv_volts(int sample, adc_res_t res) +{ + /* Convert to voltage */ + /* sample is in the range 0..max, where 0 means the sampled voltage was in + * the range [vrefl, vrefl + LSB), and max means the sampled voltage was in + * the range [vrefh - LSB, vrefh) */ + return adc_util_map(sample, res, vrefl, vrefh); +} + +void adc_conv_set_vrefh(int new_vrefh) +{ + vrefh = new_vrefh; +} + +void adc_conv_set_vrefl(int new_vrefl) +{ + vrefl = new_vrefl; +} + +void adc_conv_set_scale(int new_scale) +{ + vref_scale = new_scale; +} + +#if MODULE_SAUL_ADC +int adc_conv_saul_adc_volts(const saul_adc_params_t *params, phydat_t *res) +{ + int sample = res->val[0]; + if (params->res == ADC_RES_16BIT) { + /* Assume single-ended reading for now, clear sign bits */ + sample &= 0xffff; + } + int converted = adc_conv_volts(sample, params->res); + DEBUG("vref: %de%d\n", vrefh, vref_scale); + DEBUG("conv: %d\n", converted); + res->unit = UNIT_V; + res->scale = vref_scale; + phydat_fit(res, converted, 0, 0); + DEBUG("res: %d\n", res->val[0]); + return 1; +} +#endif diff --git a/drivers/saul/adc_saul.c b/drivers/saul/adc_saul.c index c732b1a852d5..a8637d6ec0f8 100644 --- a/drivers/saul/adc_saul.c +++ b/drivers/saul/adc_saul.c @@ -34,6 +34,11 @@ static int read_adc(const void *dev, phydat_t *res) /* Raw ADC reading has no unit */ res->unit = UNIT_NONE; res->scale = 0; +#if MODULE_SAUL_ADC_CONVERSION + if (params->conv) { + return params->conv(params, res); + } +#endif return 1; } diff --git a/makefiles/pseudomodules.inc.mk b/makefiles/pseudomodules.inc.mk index 85d5259c502f..75e001cb3a4a 100644 --- a/makefiles/pseudomodules.inc.mk +++ b/makefiles/pseudomodules.inc.mk @@ -69,6 +69,7 @@ PSEUDOMODULES += prng PSEUDOMODULES += prng_% PSEUDOMODULES += rdcli_simple_standalone PSEUDOMODULES += saul_adc +PSEUDOMODULES += saul_adc_conversion PSEUDOMODULES += saul_default PSEUDOMODULES += saul_gpio PSEUDOMODULES += schedstatistics