1 /*
2  * Copyright (c) 2023 The ChromiumOS Authors
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/adc.h>
8 #include <zephyr/drivers/adc/adc_emul.h>
9 #include <zephyr/drivers/adc/voltage_divider.h>
10 #include <zephyr/drivers/adc/current_sense_shunt.h>
11 #include <zephyr/drivers/adc/current_sense_amplifier.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/ztest.h>
14 
15 /* Raw to millivolt conversion doesn't handle rounding */
16 #define MV_OUTPUT_EPS 10
17 
18 #define ADC_TEST_NODE_0 DT_NODELABEL(sensor0)
19 #define ADC_TEST_NODE_1 DT_NODELABEL(sensor1)
20 #define ADC_TEST_NODE_2 DT_NODELABEL(sensor2)
21 #define ADC_TEST_NODE_3 DT_NODELABEL(sensor3)
22 
23 /**
24  * @brief Get ADC emulated device
25  *
26  * @return pointer to ADC device
27  */
get_adc_device(void)28 const struct device *get_adc_device(void)
29 {
30 	const struct device *const adc_dev = DEVICE_DT_GET(DT_NODELABEL(adc0));
31 
32 	zassert_true(device_is_ready(adc_dev), "ADC device is not ready");
33 
34 	return adc_dev;
35 }
36 
init_adc(const struct adc_dt_spec * spec,int input_mv)37 static int init_adc(const struct adc_dt_spec *spec, int input_mv)
38 {
39 	int ret;
40 
41 	zassert_true(adc_is_ready_dt(spec), "ADC device is not ready");
42 
43 	ret = adc_channel_setup_dt(spec);
44 	zassert_equal(ret, 0, "Setting up of the first channel failed with code %d", ret);
45 
46 	/* ADC emulator-specific setup */
47 	ret = adc_emul_const_value_set(spec->dev, spec->channel_id, input_mv);
48 	zassert_ok(ret, "adc_emul_const_value_set() failed with code %d", ret);
49 
50 	return ret;
51 }
52 
53 /*
54  * test_adc_voltage_divider
55  */
test_task_voltage_divider(void)56 static int test_task_voltage_divider(void)
57 {
58 	int ret;
59 	int32_t calculated_voltage = 0;
60 	int32_t calculated_microvolts;
61 	int32_t input_mv = 1000;
62 	const struct voltage_divider_dt_spec adc_node_0 =
63 		VOLTAGE_DIVIDER_DT_SPEC_GET(ADC_TEST_NODE_0);
64 
65 	ret = init_adc(&adc_node_0.port, input_mv);
66 	zassert_equal(ret, 0, "Setting up of the first channel failed with code %d", ret);
67 
68 	struct adc_sequence sequence = {
69 		.buffer = &calculated_voltage,
70 		.buffer_size = sizeof(calculated_voltage),
71 	};
72 	adc_sequence_init_dt(&adc_node_0.port, &sequence);
73 
74 	ret = adc_read_dt(&adc_node_0.port, &sequence);
75 	zassert_equal(ret, 0, "adc_read() failed with code %d", ret);
76 	calculated_microvolts = calculated_voltage;
77 
78 	ret = adc_raw_to_millivolts_dt(&adc_node_0.port, &calculated_voltage);
79 	zassert_equal(ret, 0, "adc_raw_to_millivolts_dt() failed with code %d", ret);
80 
81 	ret = adc_raw_to_microvolts_dt(&adc_node_0.port, &calculated_microvolts);
82 	zassert_equal(ret, 0, "adc_raw_to_microvolts_dt() failed with code %d", ret);
83 	zassert_equal(calculated_microvolts / 1000, calculated_voltage);
84 
85 	ret = voltage_divider_scale_dt(&adc_node_0, &calculated_voltage);
86 	zassert_equal(ret, 0, "divider_scale_voltage_dt() failed with code %d", ret);
87 
88 	zassert_within(calculated_voltage, input_mv * 2, MV_OUTPUT_EPS,
89 		       "%u != %u should have set value", calculated_voltage, input_mv * 2);
90 
91 	return TC_PASS;
92 }
93 
ZTEST_USER(adc_rescale,test_adc_voltage_divider)94 ZTEST_USER(adc_rescale, test_adc_voltage_divider)
95 {
96 	zassert_true(test_task_voltage_divider() == TC_PASS);
97 }
98 
99 /*
100  * test_adc_current_sense_shunt
101  */
test_task_current_sense_shunt(void)102 static int test_task_current_sense_shunt(void)
103 {
104 	int ret;
105 	int32_t calculated_current = 0;
106 	int32_t calculated_microvolts;
107 	int32_t input_mv = 3000;
108 	const struct current_sense_shunt_dt_spec adc_node_1 =
109 		CURRENT_SENSE_SHUNT_DT_SPEC_GET(ADC_TEST_NODE_1);
110 
111 	ret = init_adc(&adc_node_1.port, input_mv);
112 	zassert_equal(ret, 0, "Setting up of the second channel failed with code %d", ret);
113 
114 	struct adc_sequence sequence = {
115 		.buffer = &calculated_current,
116 		.buffer_size = sizeof(calculated_current),
117 	};
118 	adc_sequence_init_dt(&adc_node_1.port, &sequence);
119 
120 	ret = adc_read_dt(&adc_node_1.port, &sequence);
121 	zassert_equal(ret, 0, "adc_read() failed with code %d", ret);
122 	calculated_microvolts = calculated_current;
123 
124 	ret = adc_raw_to_millivolts_dt(&adc_node_1.port, &calculated_current);
125 	zassert_equal(ret, 0, "adc_raw_to_millivolts_dt() failed with code %d", ret);
126 
127 	ret = adc_raw_to_microvolts_dt(&adc_node_1.port, &calculated_microvolts);
128 	zassert_equal(ret, 0, "adc_raw_to_microvolts_dt() failed with code %d", ret);
129 	zassert_equal(calculated_microvolts / 1000, calculated_current);
130 
131 	current_sense_shunt_scale_dt(&adc_node_1, &calculated_current);
132 
133 	zassert_within(calculated_current, input_mv * 2, MV_OUTPUT_EPS,
134 		       "%u != %u should have set value", calculated_current,
135 		       input_mv * 2);
136 
137 	return TC_PASS;
138 }
139 
ZTEST_USER(adc_rescale,test_adc_current_sense_shunt)140 ZTEST_USER(adc_rescale, test_adc_current_sense_shunt)
141 {
142 	zassert_true(test_task_current_sense_shunt() == TC_PASS);
143 }
144 
145 /*
146  * test_adc_current_sense_amplifier
147  */
test_task_current_sense_amplifier(void)148 static int test_task_current_sense_amplifier(void)
149 {
150 	int ret;
151 	int32_t calculated_current = 0;
152 	int32_t calculated_microvolts;
153 	int32_t input_mv = 3000;
154 	const struct current_sense_amplifier_dt_spec adc_node_2 =
155 		CURRENT_SENSE_AMPLIFIER_DT_SPEC_GET(ADC_TEST_NODE_2);
156 
157 	ret = init_adc(&adc_node_2.port, input_mv);
158 	zassert_equal(ret, 0, "Setting up of the third channel failed with code %d", ret);
159 
160 	struct adc_sequence sequence = {
161 		.buffer = &calculated_current,
162 		.buffer_size = sizeof(calculated_current),
163 	};
164 	adc_sequence_init_dt(&adc_node_2.port, &sequence);
165 
166 	ret = adc_read_dt(&adc_node_2.port, &sequence);
167 	zassert_equal(ret, 0, "adc_read() failed with code %d", ret);
168 	calculated_microvolts = calculated_current;
169 
170 	ret = adc_raw_to_millivolts_dt(&adc_node_2.port, &calculated_current);
171 	zassert_equal(ret, 0, "adc_raw_to_millivolts_dt() failed with code %d", ret);
172 
173 	ret = adc_raw_to_microvolts_dt(&adc_node_2.port, &calculated_microvolts);
174 	zassert_equal(ret, 0, "adc_raw_to_microvolts_dt() failed with code %d", ret);
175 	zassert_equal(calculated_microvolts / 1000, calculated_current);
176 
177 	current_sense_amplifier_scale_dt(&adc_node_2, &calculated_current);
178 
179 	zassert_within(calculated_current, input_mv * 2, MV_OUTPUT_EPS,
180 		       "%u != %u should have set value", calculated_current,
181 		       input_mv * 2);
182 
183 	return TC_PASS;
184 }
185 
ZTEST_USER(adc_rescale,test_adc_current_sense_amplifier)186 ZTEST_USER(adc_rescale, test_adc_current_sense_amplifier)
187 {
188 	zassert_true(test_task_current_sense_amplifier() == TC_PASS);
189 }
190 
ZTEST(adc_rescale,test_adc_current_sense_amplifier_with_offset)191 ZTEST(adc_rescale, test_adc_current_sense_amplifier_with_offset)
192 {
193 	int32_t v_to_i;
194 	const struct current_sense_amplifier_dt_spec amplifier_spec =
195 		CURRENT_SENSE_AMPLIFIER_DT_SPEC_GET(ADC_TEST_NODE_3);
196 
197 	/**
198 	 * test a voltage that corresponds to 0 mA
199 	 */
200 	v_to_i = amplifier_spec.zero_current_voltage_mv;
201 	current_sense_amplifier_scale_dt(&amplifier_spec, &v_to_i);
202 	zassert_equal(v_to_i, 0);
203 
204 	/**
205 	 * test a voltage that corresponds to 200 mA
206 	 */
207 	v_to_i = (200 * amplifier_spec.sense_gain_mult / amplifier_spec.sense_gain_div) / 1000;
208 	v_to_i = v_to_i + amplifier_spec.zero_current_voltage_mv;
209 	current_sense_amplifier_scale_dt(&amplifier_spec, &v_to_i);
210 	zassert_equal(v_to_i, 200);
211 
212 	/**
213 	 * test a voltage that corresponds to -1100 mA
214 	 */
215 	v_to_i = (-1100 * amplifier_spec.sense_gain_mult / amplifier_spec.sense_gain_div) / 1000;
216 	v_to_i = v_to_i + amplifier_spec.zero_current_voltage_mv;
217 	current_sense_amplifier_scale_dt(&amplifier_spec, &v_to_i);
218 	zassert_equal(v_to_i, -1100);
219 }
220 
adc_rescale_setup(void)221 void *adc_rescale_setup(void)
222 {
223 	k_object_access_grant(get_adc_device(), k_current_get());
224 
225 	return NULL;
226 }
227 
228 ZTEST_SUITE(adc_rescale, NULL, adc_rescale_setup, NULL, NULL, NULL);
229