diff --git a/drivers/adc/CMakeLists.txt b/drivers/adc/CMakeLists.txt index d07be2782a7..d43ad7d5bb7 100644 --- a/drivers/adc/CMakeLists.txt +++ b/drivers/adc/CMakeLists.txt @@ -2,6 +2,7 @@ zephyr_library() +zephyr_library_sources_ifdef(CONFIG_ADC adc_common.c) zephyr_library_sources_ifdef(CONFIG_ADC_SHELL adc_shell.c) zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_ADC12 adc_mcux_adc12.c) zephyr_library_sources_ifdef(CONFIG_ADC_MCUX_ADC16 adc_mcux_adc16.c) diff --git a/drivers/adc/adc_common.c b/drivers/adc/adc_common.c new file mode 100644 index 00000000000..9be30d4fb95 --- /dev/null +++ b/drivers/adc/adc_common.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2019 Peter Bigot Consulting, LLC + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +int adc_gain_invert(enum adc_gain gain, + s32_t *value) +{ + struct gain_desc { + u8_t mul; + u8_t div; + }; + static const struct gain_desc gains[] = { + [ADC_GAIN_1_6] = {.mul = 6, .div = 1}, + [ADC_GAIN_1_5] = {.mul = 5, .div = 1}, + [ADC_GAIN_1_4] = {.mul = 4, .div = 1}, + [ADC_GAIN_1_3] = {.mul = 3, .div = 1}, + [ADC_GAIN_1_2] = {.mul = 2, .div = 1}, + [ADC_GAIN_2_3] = {.mul = 3, .div = 2}, + [ADC_GAIN_1] = {.mul = 1, .div = 1}, + [ADC_GAIN_2] = {.mul = 1, .div = 2}, + [ADC_GAIN_3] = {.mul = 1, .div = 3}, + [ADC_GAIN_4] = {.mul = 1, .div = 4}, + [ADC_GAIN_8] = {.mul = 1, .div = 8}, + [ADC_GAIN_16] = {.mul = 1, .div = 16}, + [ADC_GAIN_32] = {.mul = 1, .div = 32}, + [ADC_GAIN_64] = {.mul = 1, .div = 64}, + }; + int rv = -EINVAL; + + if ((u8_t)gain < ARRAY_SIZE(gains)) { + const struct gain_desc *gdp = &gains[gain]; + + if ((gdp->mul != 0) && (gdp->div != 0)) { + *value = (gdp->mul * *value) / gdp->div; + rv = 0; + } + } + + return rv; +} diff --git a/include/drivers/adc.h b/include/drivers/adc.h index 7120ed70dd3..ab99917cdf0 100644 --- a/include/drivers/adc.h +++ b/include/drivers/adc.h @@ -45,6 +45,25 @@ enum adc_gain { ADC_GAIN_128, /**< x 128. */ }; +/** + * @brief Invert the application of gain to a measurement value. + * + * For example, if the gain passed in is ADC_GAIN_1_6 and the + * referenced value is 10, the value after the function returns is 60. + * + * @param gain the gain used to amplify the input signal. + * + * @param value a pointer to a value that initially has the effect of + * the applied gain but has that effect removed when this function + * successfully returns. If the gain cannot be reversed the value + * remains unchanged. + * + * @retval 0 if the gain was successfully reversed + * @retval -EINVAL if the gain could not be interpreted + */ +int adc_gain_invert(enum adc_gain gain, + s32_t *value); + /** @brief ADC references. */ enum adc_reference { ADC_REF_VDD_1, /**< VDD. */ @@ -135,6 +154,43 @@ struct adc_channel_cfg { #endif /* CONFIG_ADC_CONFIGURABLE_INPUTS */ }; +/** + * @brief Convert a raw ADC value to millivolts. + * + * This function performs the necessary conversion to transform a raw + * ADC measurement to a voltage in millivolts. + * + * @param ref_mv the reference voltage used for the measurement, in + * millivolts. This may be from adc_ref_internal() or a known + * external reference. + * + * @param gain the ADC gain configuration used to sample the input + * + * @param resolution the number of bits in the absolute value of the + * sample. For differential sampling this may be one less than the + * resolution in struct adc_sequence. + * + * @param valp pointer to the raw measurement value on input, and the + * corresponding millivolt value on successful conversion. If + * conversion fails the stored value is left unchanged. + * + * @retval 0 on successful conversion + * @retval -EINVAL if the gain is not reversible + */ +static inline int adc_raw_to_millivolts(s32_t ref_mv, + enum adc_gain gain, + u8_t resolution, + s32_t *valp) +{ + s32_t adc_mv = *valp * ref_mv; + int ret = adc_gain_invert(gain, &adc_mv); + + if (ret == 0) { + *valp = (adc_mv >> resolution); + } + + return ret; +} /* Forward declaration of the adc_sequence structure. */ struct adc_sequence;