Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 68 additions & 4 deletions drivers/adc/adc_emul.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
};

/**
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand All @@ -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)
{
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down
27 changes: 27 additions & 0 deletions include/zephyr/drivers/adc/adc_emul.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
*
Expand Down
97 changes: 97 additions & 0 deletions tests/drivers/adc/adc_emul/src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down
Loading