diff --git a/drivers/adc/adc_emul.c b/drivers/adc/adc_emul.c index 20b9764fd5fbb..88dcccbdfeb91 100644 --- a/drivers/adc/adc_emul.c +++ b/drivers/adc/adc_emul.c @@ -31,6 +31,8 @@ typedef uint16_t adc_emul_res_t; enum adc_emul_input_source { ADC_EMUL_CONST_VALUE, ADC_EMUL_CUSTOM_FUNC, + ADC_EMUL_CONST_RAW_VALUE, + ADC_EMUL_CUSTOM_FUNC_RAW_VALUE, }; /** @@ -39,11 +41,11 @@ enum adc_emul_input_source { * This structure contains configuration of one channel of emulated ADC. */ struct adc_emul_chan_cfg { - /** Pointer to function used to obtain input mV */ + /** Pointer to function used to obtain input mV or raw input value */ adc_emul_value_func func; /** Pointer to data that are passed to @a func on call */ void *func_data; - /** Constant mV input value */ + /** Constant mV input value or raw input value */ uint32_t const_value; /** Gain used on output value */ enum adc_gain gain; @@ -127,8 +129,31 @@ int adc_emul_const_value_set(const struct device *dev, unsigned int chan, return 0; } -int adc_emul_value_func_set(const struct device *dev, unsigned int chan, - adc_emul_value_func func, void *func_data) +int adc_emul_const_raw_value_set(const struct device *dev, unsigned int chan, uint32_t raw_value) +{ + const struct adc_emul_config *config = dev->config; + struct adc_emul_data *data = dev->data; + struct adc_emul_chan_cfg *chan_cfg; + + if (chan >= config->num_channels) { + LOG_ERR("unsupported channel %d", chan); + return -EINVAL; + } + + chan_cfg = &data->chan_cfg[chan]; + + k_mutex_lock(&data->cfg_mtx, K_FOREVER); + + chan_cfg->input = ADC_EMUL_CONST_RAW_VALUE; + chan_cfg->const_value = raw_value; + + k_mutex_unlock(&data->cfg_mtx); + + return 0; +} + +int adc_emul_value_func_set(const struct device *dev, unsigned int chan, adc_emul_value_func func, + void *func_data) { const struct adc_emul_config *config = dev->config; struct adc_emul_data *data = dev->data; @@ -152,6 +177,31 @@ int adc_emul_value_func_set(const struct device *dev, unsigned int chan, return 0; } +int adc_emul_raw_value_func_set(const struct device *dev, unsigned int chan, + adc_emul_value_func func, void *func_data) +{ + const struct adc_emul_config *config = dev->config; + struct adc_emul_data *data = dev->data; + struct adc_emul_chan_cfg *chan_cfg; + + if (chan >= config->num_channels) { + LOG_ERR("unsupported channel %d", chan); + return -EINVAL; + } + + chan_cfg = &data->chan_cfg[chan]; + + k_mutex_lock(&data->cfg_mtx, K_FOREVER); + + chan_cfg->func = func; + chan_cfg->func_data = func_data; + chan_cfg->input = ADC_EMUL_CUSTOM_FUNC_RAW_VALUE; + + k_mutex_unlock(&data->cfg_mtx); + + return 0; +} + int adc_emul_ref_voltage_set(const struct device *dev, enum adc_reference ref, uint16_t value) { @@ -428,6 +478,19 @@ static int adc_emul_get_chan_value(struct adc_emul_data *data, } break; + case ADC_EMUL_CONST_RAW_VALUE: + temp = chan_cfg->const_value; + goto check_bound_and_out; + + case ADC_EMUL_CUSTOM_FUNC_RAW_VALUE: + err = chan_cfg->func(data->dev, chan, chan_cfg->func_data, &input_mV); + if (err) { + LOG_ERR("failed to read channel %d (err %d)", chan, err); + goto out; + } + temp = input_mV; + goto check_bound_and_out; + default: LOG_ERR("unknown input source %d", chan_cfg->input); err = -EINVAL; @@ -446,6 +509,7 @@ static int adc_emul_get_chan_value(struct adc_emul_data *data, /* Calculate output value */ temp = (uint64_t)input_mV * data->res_mask / ref_v; +check_bound_and_out: /* If output value is greater than resolution, it has to be trimmed */ if (temp > data->res_mask) { temp = data->res_mask; diff --git a/include/zephyr/drivers/adc/adc_emul.h b/include/zephyr/drivers/adc/adc_emul.h index 03c2fbbb3fd0d..9c95dd8ab4dd9 100644 --- a/include/zephyr/drivers/adc/adc_emul.h +++ b/include/zephyr/drivers/adc/adc_emul.h @@ -72,6 +72,18 @@ typedef int (*adc_emul_value_func)(const struct device *dev, unsigned int chan, int adc_emul_const_value_set(const struct device *dev, unsigned int chan, uint32_t value); +/** + * @brief Set constant raw value input for emulated ADC @p chan + * + * @param dev The emulated ADC device + * @param chan The channel of ADC which input is assigned + * @param raw_value New raw value to assign to @p chan input + * + * @return 0 on success + * @return -EINVAL if an invalid argument is provided + */ +int adc_emul_const_raw_value_set(const struct device *dev, unsigned int chan, uint32_t raw_value); + /** * @brief Set function used to obtain voltage for input of emulated * ADC @p chan @@ -87,6 +99,21 @@ int adc_emul_const_value_set(const struct device *dev, unsigned int chan, int adc_emul_value_func_set(const struct device *dev, unsigned int chan, adc_emul_value_func func, void *data); +/** + * @brief Set function used to obtain voltage for raw input value of emulated + * ADC @p chan + * + * @param dev The emulated ADC device + * @param chan The channel of ADC to which @p func is assigned + * @param func New function to assign to @p chan + * @param data Pointer to data passed to @p func on call + * + * @return 0 on success + * @return -EINVAL if an invalid argument is provided + */ +int adc_emul_raw_value_func_set(const struct device *dev, unsigned int chan, + adc_emul_value_func func, void *data); + /** * @brief Set reference voltage * diff --git a/tests/drivers/adc/adc_emul/src/main.c b/tests/drivers/adc/adc_emul/src/main.c index 1cd6d746bcd57..7052f60c34b58 100644 --- a/tests/drivers/adc/adc_emul/src/main.c +++ b/tests/drivers/adc/adc_emul/src/main.c @@ -216,6 +216,68 @@ ZTEST_USER(adc_emul, test_adc_emul_single_value) /* Test sampling */ start_adc_read(adc_dev, BIT(ADC_1ST_CHANNEL_ID), samples); + /* Check samples */ + check_samples(samples, input_mv, 0 /* step */, 1 /* channels */, 0 /* first channel data */, + ADC_REF_INTERNAL_MV, ADC_GAIN_1); + + check_empty_samples(samples); +} + +/** @brief Test setting one channel with constant raw output. */ +ZTEST_USER(adc_emul, test_adc_emul_single_raw_value_half_reference) +{ + const uint16_t input_raw_value = (1 << ADC_RESOLUTION) / 2; + const uint16_t input_mv = ADC_REF_INTERNAL_MV / 2; + const int samples = 4; + int ret, i; + + for (i = 0; i < BUFFER_SIZE; ++i) { + m_sample_buffer[i] = INVALID_ADC_VALUE; + } + + /* Generic ADC setup */ + const struct device *adc_dev = get_adc_device(); + + channel_setup(adc_dev, ADC_REF_INTERNAL, ADC_GAIN_1, ADC_1ST_CHANNEL_ID); + + /* ADC emulator-specific setup */ + ret = adc_emul_const_raw_value_set(adc_dev, ADC_1ST_CHANNEL_ID, input_raw_value); + zassert_ok(ret, "adc_emul_const_value_set() failed with code %d", ret); + + /* Test sampling */ + start_adc_read(adc_dev, BIT(ADC_1ST_CHANNEL_ID), samples); + + /* Check samples */ + check_samples(samples, input_mv, 0 /* step */, 1 /* channels */, 0 /* first channel data */, + ADC_REF_INTERNAL_MV, ADC_GAIN_1); + + check_empty_samples(samples); +} + +/** @brief Test setting one channel with constant raw output. */ +ZTEST_USER(adc_emul, test_adc_emul_single_raw_value_quarter_reference) +{ + const uint16_t input_raw_value = (1 << ADC_RESOLUTION) / 4; + const uint16_t input_mv = ADC_REF_INTERNAL_MV / 4; + const int samples = 4; + int ret, i; + + for (i = 0; i < BUFFER_SIZE; ++i) { + m_sample_buffer[i] = INVALID_ADC_VALUE; + } + + /* Generic ADC setup */ + const struct device *adc_dev = get_adc_device(); + + channel_setup(adc_dev, ADC_REF_INTERNAL, ADC_GAIN_1, ADC_1ST_CHANNEL_ID); + + /* ADC emulator-specific setup */ + ret = adc_emul_const_raw_value_set(adc_dev, ADC_1ST_CHANNEL_ID, input_raw_value); + zassert_ok(ret, "adc_emul_const_value_set() failed with code %d", ret); + + /* Test sampling */ + start_adc_read(adc_dev, BIT(ADC_1ST_CHANNEL_ID), samples); + /* Check samples */ check_samples(samples, input_mv, 0 /* step */, 1 /* channels */, 0 /* first channel data */, ADC_REF_INTERNAL_MV, @@ -302,6 +364,41 @@ ZTEST_USER(adc_emul, test_adc_emul_custom_function) check_empty_samples(samples); } +/** @brief Test setting one channel with custom function raw value. */ +ZTEST_USER(adc_emul, test_adc_emul_custom_function_raw_value) +{ + struct handle_seq_params channel1_param; + const uint16_t raw_value = 1000; + const uint16_t input_mV = (raw_value * ADC_REF_INTERNAL_MV / BIT(ADC_RESOLUTION)); + const int samples = 4; + int ret, i; + + for (i = 0; i < BUFFER_SIZE; ++i) { + m_sample_buffer[i] = INVALID_ADC_VALUE; + } + + /* Generic ADC setup */ + const struct device *adc_dev = get_adc_device(); + + channel_setup(adc_dev, ADC_REF_INTERNAL, ADC_GAIN_1, ADC_1ST_CHANNEL_ID); + + /* ADC emulator-specific setup */ + channel1_param.value = raw_value; + + ret = adc_emul_raw_value_func_set(adc_dev, ADC_1ST_CHANNEL_ID, handle_seq, &channel1_param); + zassert_ok(ret, "adc_emul_value_func_set() failed with code %d", ret); + + /* Test sampling */ + start_adc_read(adc_dev, BIT(ADC_1ST_CHANNEL_ID), samples); + + /* Check samples */ + check_samples(samples, input_mV, + (SEQUENCE_STEP * ADC_REF_INTERNAL_MV / BIT(ADC_RESOLUTION)), 1 /* channels */, + 0 /* first channel data */, ADC_REF_INTERNAL_MV, ADC_GAIN_1); + + check_empty_samples(samples); +} + /** * @brief Test setting two channels with custom function and different * params.