adc: add microvolt conversion functions
Add a family of functions that convert to microvolts instead of millivolts. The resolution of an ADC with a 600 mV reference and a 12 bit output (nRF SAADC for example) is an order of magnitude better than millivolts (0.146 mV), even before considering non-unity gains. Signed-off-by: Jordan Yates <jordan@embeint.com>
This commit is contained in:
parent
8ab300d63e
commit
c0ca636692
1 changed files with 97 additions and 32 deletions
|
@ -859,10 +859,10 @@ static inline uint16_t adc_ref_internal(const struct device *dev)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert a raw ADC value to millivolts.
|
* @brief Conversion from raw ADC units to a specific output unit
|
||||||
*
|
*
|
||||||
* This function performs the necessary conversion to transform a raw
|
* This function performs the necessary conversion to transform a raw
|
||||||
* ADC measurement to a voltage in millivolts.
|
* ADC measurement to a physical voltage.
|
||||||
*
|
*
|
||||||
* @param ref_mv the reference voltage used for the measurement, in
|
* @param ref_mv the reference voltage used for the measurement, in
|
||||||
* millivolts. This may be from adc_ref_internal() or a known
|
* millivolts. This may be from adc_ref_internal() or a known
|
||||||
|
@ -875,15 +875,21 @@ static inline uint16_t adc_ref_internal(const struct device *dev)
|
||||||
* resolution in struct adc_sequence.
|
* resolution in struct adc_sequence.
|
||||||
*
|
*
|
||||||
* @param valp pointer to the raw measurement value on input, and the
|
* @param valp pointer to the raw measurement value on input, and the
|
||||||
* corresponding millivolt value on successful conversion. If
|
* corresponding output value on successful conversion. If
|
||||||
* conversion fails the stored value is left unchanged.
|
* conversion fails the stored value is left unchanged.
|
||||||
*
|
*
|
||||||
* @retval 0 on successful conversion
|
* @retval 0 on successful conversion
|
||||||
* @retval -EINVAL if the gain is not reversible
|
* @retval -EINVAL if the gain is not reversible
|
||||||
*/
|
*/
|
||||||
static inline int adc_raw_to_millivolts(int32_t ref_mv,
|
typedef int (*adc_raw_to_x_fn)(int32_t ref_mv, enum adc_gain gain, uint8_t resolution,
|
||||||
enum adc_gain gain,
|
int32_t *valp);
|
||||||
uint8_t resolution,
|
|
||||||
|
/**
|
||||||
|
* @brief Convert a raw ADC value to millivolts.
|
||||||
|
*
|
||||||
|
* @see adc_raw_to_x_fn
|
||||||
|
*/
|
||||||
|
static inline int adc_raw_to_millivolts(int32_t ref_mv, enum adc_gain gain, uint8_t resolution,
|
||||||
int32_t *valp)
|
int32_t *valp)
|
||||||
{
|
{
|
||||||
int32_t adc_mv = *valp * ref_mv;
|
int32_t adc_mv = *valp * ref_mv;
|
||||||
|
@ -896,6 +902,72 @@ static inline int adc_raw_to_millivolts(int32_t ref_mv,
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert a raw ADC value to microvolts.
|
||||||
|
*
|
||||||
|
* @see adc_raw_to_x_fn
|
||||||
|
*/
|
||||||
|
static inline int adc_raw_to_microvolts(int32_t ref_mv, enum adc_gain gain, uint8_t resolution,
|
||||||
|
int32_t *valp)
|
||||||
|
{
|
||||||
|
int64_t adc_uv = (int64_t)*valp * ref_mv * 1000;
|
||||||
|
int ret = adc_gain_invert_64(gain, &adc_uv);
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
*valp = (int32_t)(adc_uv >> resolution);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Convert a raw ADC value to an arbitrary output unit
|
||||||
|
*
|
||||||
|
* @param[in] conv_func Function that converts to the final output unit.
|
||||||
|
* @param[in] spec ADC specification from Devicetree.
|
||||||
|
* @param[in] channel_cfg Channel configuration used for sampling. This can be
|
||||||
|
* either the configuration from @a spec, or an alternate sampling configuration
|
||||||
|
* based on @a spec, for example a different gain value.
|
||||||
|
* @param[in,out] valp Pointer to the raw measurement value on input, and the
|
||||||
|
* corresponding output value on successful conversion. If conversion fails
|
||||||
|
* the stored value is left unchanged.
|
||||||
|
*
|
||||||
|
* @return A value from adc_raw_to_x_fn or -ENOTSUP if information from
|
||||||
|
* Devicetree is not valid.
|
||||||
|
* @see adc_raw_to_x_fn
|
||||||
|
*/
|
||||||
|
static inline int adc_raw_to_x_dt_chan(adc_raw_to_x_fn conv_func,
|
||||||
|
const struct adc_dt_spec *spec,
|
||||||
|
const struct adc_channel_cfg *channel_cfg,
|
||||||
|
int32_t *valp)
|
||||||
|
{
|
||||||
|
int32_t vref_mv;
|
||||||
|
uint8_t resolution;
|
||||||
|
|
||||||
|
if (!spec->channel_cfg_dt_node_exists) {
|
||||||
|
return -ENOTSUP;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel_cfg->reference == ADC_REF_INTERNAL) {
|
||||||
|
vref_mv = (int32_t)adc_ref_internal(spec->dev);
|
||||||
|
} else {
|
||||||
|
vref_mv = spec->vref_mv;
|
||||||
|
}
|
||||||
|
|
||||||
|
resolution = spec->resolution;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For differential channels, one bit less needs to be specified
|
||||||
|
* for resolution to achieve correct conversion.
|
||||||
|
*/
|
||||||
|
if (channel_cfg->differential) {
|
||||||
|
resolution -= 1U;
|
||||||
|
}
|
||||||
|
|
||||||
|
return conv_func(vref_mv, channel_cfg->gain, resolution, valp);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Convert a raw ADC value to millivolts using information stored
|
* @brief Convert a raw ADC value to millivolts using information stored
|
||||||
* in a struct adc_dt_spec.
|
* in a struct adc_dt_spec.
|
||||||
|
@ -909,34 +981,27 @@ static inline int adc_raw_to_millivolts(int32_t ref_mv,
|
||||||
* Devicetree is not valid.
|
* Devicetree is not valid.
|
||||||
* @see adc_raw_to_millivolts()
|
* @see adc_raw_to_millivolts()
|
||||||
*/
|
*/
|
||||||
static inline int adc_raw_to_millivolts_dt(const struct adc_dt_spec *spec,
|
static inline int adc_raw_to_millivolts_dt(const struct adc_dt_spec *spec, int32_t *valp)
|
||||||
int32_t *valp)
|
|
||||||
{
|
{
|
||||||
int32_t vref_mv;
|
return adc_raw_to_x_dt_chan(adc_raw_to_millivolts, spec, &spec->channel_cfg, valp);
|
||||||
uint8_t resolution;
|
}
|
||||||
|
|
||||||
if (!spec->channel_cfg_dt_node_exists) {
|
/**
|
||||||
return -ENOTSUP;
|
* @brief Convert a raw ADC value to microvolts using information stored
|
||||||
}
|
* in a struct adc_dt_spec.
|
||||||
|
*
|
||||||
if (spec->channel_cfg.reference == ADC_REF_INTERNAL) {
|
* @param[in] spec ADC specification from Devicetree.
|
||||||
vref_mv = (int32_t)adc_ref_internal(spec->dev);
|
* @param[in,out] valp Pointer to the raw measurement value on input, and the
|
||||||
} else {
|
* corresponding microvolt value on successful conversion. If conversion fails
|
||||||
vref_mv = spec->vref_mv;
|
* the stored value is left unchanged.
|
||||||
}
|
*
|
||||||
|
* @return A value from adc_raw_to_microvolts() or -ENOTSUP if information from
|
||||||
resolution = spec->resolution;
|
* Devicetree is not valid.
|
||||||
|
* @see adc_raw_to_microvolts()
|
||||||
/*
|
|
||||||
* For differential channels, one bit less needs to be specified
|
|
||||||
* for resolution to achieve correct conversion.
|
|
||||||
*/
|
*/
|
||||||
if (spec->channel_cfg.differential) {
|
static inline int adc_raw_to_microvolts_dt(const struct adc_dt_spec *spec, int32_t *valp)
|
||||||
resolution -= 1U;
|
{
|
||||||
}
|
return adc_raw_to_x_dt_chan(adc_raw_to_microvolts, spec, &spec->channel_cfg, valp);
|
||||||
|
|
||||||
return adc_raw_to_millivolts(vref_mv, spec->channel_cfg.gain,
|
|
||||||
resolution, valp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue