Skip to content

Commit 3e0679f

Browse files
ananglmbolivar-nordic
authored andcommitted
include: drivers: adc: Add macros for reading channel configs from DT
Add macros that allow reading configuration for ADC channels from child nodes of ADC controllers in devicetree. Signed-off-by: Andrzej Głąbek <[email protected]>
1 parent 77edc2c commit 3e0679f

File tree

1 file changed

+233
-0
lines changed
  • include/zephyr/drivers

1 file changed

+233
-0
lines changed

include/zephyr/drivers/adc.h

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,239 @@ struct adc_channel_cfg {
146146
#endif /* CONFIG_ADC_CONFIGURABLE_INPUTS */
147147
};
148148

149+
/**
150+
* @brief Get ADC channel configuration from a given devicetree node.
151+
*
152+
* This returns a static initializer for a <tt>struct adc_channel_cfg</tt>
153+
* filled with data from a given devicetree node.
154+
*
155+
* Example devicetree fragment:
156+
*
157+
* @code{.dts}
158+
* &adc {
159+
* #address-cells = <1>;
160+
* #size-cells = <0>;
161+
*
162+
* channel@0 {
163+
* reg = <0>;
164+
* zephyr,gain = "ADC_GAIN_1_6";
165+
* zephyr,reference = "ADC_REF_INTERNAL";
166+
* zephyr,acquisition-time = <ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20)>;
167+
* zephyr,input-positive = <NRF_SAADC_AIN6>;
168+
* zephyr,input-negative = <NRF_SAADC_AIN7>;
169+
* };
170+
*
171+
* channel@1 {
172+
* reg = <1>;
173+
* zephyr,gain = "ADC_GAIN_1_6";
174+
* zephyr,reference = "ADC_REF_INTERNAL";
175+
* zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
176+
* zephyr,input-positive = <NRF_SAADC_AIN0>;
177+
* };
178+
* };
179+
* @endcode
180+
*
181+
* Example usage:
182+
*
183+
* @code{.c}
184+
* static const struct adc_channel_cfg ch0_cfg_dt =
185+
* ADC_CHANNEL_CFG_DT(DT_CHILD(DT_NODELABEL(adc), channel_0));
186+
* static const struct adc_channel_cfg ch1_cfg_dt =
187+
* ADC_CHANNEL_CFG_DT(DT_CHILD(DT_NODELABEL(adc), channel_1));
188+
*
189+
* // Initializes 'ch0_cfg_dt' to:
190+
* // {
191+
* // .channel_id = 0,
192+
* // .gain = ADC_GAIN_1_6,
193+
* // .reference = ADC_REF_INTERNAL,
194+
* // .acquisition_time = ADC_ACQ_TIME(ADC_ACQ_TIME_MICROSECONDS, 20),
195+
* // .differential = true,
196+
* // .input_positive = NRF_SAADC_AIN6,
197+
* // .input-negative = NRF_SAADC_AIN7,
198+
* // }
199+
* // and 'ch1_cfg_dt' to:
200+
* // {
201+
* // .channel_id = 1,
202+
* // .gain = ADC_GAIN_1_6,
203+
* // .reference = ADC_REF_INTERNAL,
204+
* // .acquisition_time = ADC_ACQ_TIME_DEFAULT,
205+
* // .input_positive = NRF_SAADC_AIN0,
206+
* // }
207+
* @endcode
208+
*
209+
* @param node_id Devicetree node identifier.
210+
*
211+
* @return Static initializer for an adc_channel_cfg structure.
212+
*/
213+
#define ADC_CHANNEL_CFG_DT(node_id) { \
214+
.channel_id = DT_REG_ADDR(node_id), \
215+
.gain = DT_STRING_TOKEN(node_id, zephyr_gain), \
216+
.reference = DT_STRING_TOKEN(node_id, zephyr_reference), \
217+
.acquisition_time = DT_PROP(node_id, zephyr_acquisition_time), \
218+
IF_ENABLED(CONFIG_ADC_CONFIGURABLE_INPUTS, \
219+
(COND_CODE_1(DT_NODE_HAS_PROP(node_id, zephyr_input_negative), \
220+
(.differential = true, \
221+
.input_positive = DT_PROP(node_id, zephyr_input_positive), \
222+
.input_negative = DT_PROP(node_id, zephyr_input_negative),), \
223+
(.input_positive = DT_PROP(node_id, zephyr_input_positive),)))) \
224+
}
225+
226+
/**
227+
* @brief Container for ADC channel information specified in devicetree.
228+
*
229+
* @see ADC_DT_SPEC_GET_BY_IDX
230+
* @see ADC_DT_SPEC_GET
231+
*/
232+
struct adc_dt_spec {
233+
/**
234+
* Pointer to the device structure for the ADC driver instance
235+
* used by this io-channel.
236+
*/
237+
const struct device *dev;
238+
239+
/** ADC channel identifier used by this io-channel. */
240+
uint8_t channel_id;
241+
242+
/**
243+
* Flag indicating whether configuration of the associated ADC channel
244+
* is provided as a child node of the corresponding ADC controller in
245+
* devicetree.
246+
*/
247+
bool channel_cfg_dt_node_exists;
248+
249+
/**
250+
* Configuration of the associated ADC channel specified in devicetree.
251+
* This field is valid only when @a channel_cfg_dt_node_exists is set
252+
* to @a true.
253+
*/
254+
struct adc_channel_cfg channel_cfg;
255+
256+
/**
257+
* Voltage of the reference selected for the channel or 0 if this
258+
* value is not provided in devicetree.
259+
* This field is valid only when @a channel_cfg_dt_node_exists is set
260+
* to @a true.
261+
*/
262+
uint16_t vref_mv;
263+
264+
/**
265+
* ADC resolution to be used for that channel.
266+
* This field is valid only when @a channel_cfg_dt_node_exists is set
267+
* to @a true.
268+
*/
269+
uint8_t resolution;
270+
271+
/**
272+
* Oversampling setting to be used for that channel.
273+
* This field is valid only when @a channel_cfg_dt_node_exists is set
274+
* to @a true.
275+
*/
276+
uint8_t oversampling;
277+
};
278+
279+
/** @cond INTERNAL_HIDDEN */
280+
281+
#define ADC_DT_SPEC_STRUCT(ctlr, input) { \
282+
.dev = DEVICE_DT_GET(ctlr), \
283+
.channel_id = input, \
284+
ADC_CHANNEL_CFG_FROM_DT_NODE( \
285+
DT_CHILD(ctlr, UTIL_CAT(channel_, input))) \
286+
}
287+
288+
#define ADC_CHANNEL_CFG_FROM_DT_NODE(node_id) \
289+
IF_ENABLED(DT_NODE_EXISTS(node_id), \
290+
(.channel_cfg_dt_node_exists = true, \
291+
.channel_cfg = ADC_CHANNEL_CFG_DT(node_id), \
292+
.vref_mv = DT_PROP_OR(node_id, zephyr_vref_mv, 0), \
293+
.resolution = DT_PROP_OR(node_id, zephyr_resolution, 0), \
294+
.oversampling = DT_PROP_OR(node_id, zephyr_oversampling, 0),))
295+
296+
/** @endcond */
297+
298+
/**
299+
* @brief Get ADC io-channel information from devicetree.
300+
*
301+
* This returns a static initializer for an @p adc_dt_spec structure
302+
* given a devicetree node and a channel index. The node must have
303+
* the "io-channels" property defined.
304+
*
305+
* Example devicetree fragment:
306+
*
307+
* @code{.dts}
308+
* / {
309+
* zephyr,user {
310+
* io-channels = <&adc0 1>, <&adc0 3>;
311+
* };
312+
* };
313+
*
314+
* &adc0 {
315+
* #address-cells = <1>;
316+
* #size-cells = <0>;
317+
*
318+
* channel@3 {
319+
* reg = <3>;
320+
* zephyr,gain = "ADC_GAIN_1_5";
321+
* zephyr,reference = "ADC_REF_VDD_1_4";
322+
* zephyr,vref-mv = <750>;
323+
* zephyr,acquisition-time = <ADC_ACQ_TIME_DEFAULT>;
324+
* zephyr,resolution = <12>;
325+
* zephyr,oversampling = <4>;
326+
* };
327+
* };
328+
* @endcode
329+
*
330+
* Example usage:
331+
*
332+
* @code{.c}
333+
* static const struct adc_dt_spec adc_chan0 =
334+
* ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 0);
335+
* static const struct adc_dt_spec adc_chan1 =
336+
* ADC_DT_SPEC_GET_BY_IDX(DT_PATH(zephyr_user), 1);
337+
*
338+
* // Initializes 'adc_chan0' to:
339+
* // {
340+
* // .dev = DEVICE_DT_GET(DT_NODELABEL(adc0)),
341+
* // .channel_id = 1,
342+
* // }
343+
* // and 'adc_chan1' to:
344+
* // {
345+
* // .dev = DEVICE_DT_GET(DT_NODELABEL(adc0)),
346+
* // .channel_id = 3,
347+
* // .channel_cfg_dt_node_exists = true,
348+
* // .channel_cfg = {
349+
* // .channel_id = 3,
350+
* // .gain = ADC_GAIN_1_5,
351+
* // .reference = ADC_REF_VDD_1_4,
352+
* // .acquisition_time = ADC_ACQ_TIME_DEFAULT,
353+
* // },
354+
* // .vref_mv = 750,
355+
* // .resolution = 12,
356+
* // .oversampling = 4,
357+
* // }
358+
* @endcode
359+
*
360+
* @see ADC_DT_SPEC_GET()
361+
*
362+
* @param node_id Devicetree node identifier.
363+
* @param idx Channel index.
364+
*
365+
* @return Static initializer for an adc_dt_spec structure.
366+
*/
367+
#define ADC_DT_SPEC_GET_BY_IDX(node_id, idx) \
368+
ADC_DT_SPEC_STRUCT(DT_IO_CHANNELS_CTLR_BY_IDX(node_id, idx), \
369+
DT_IO_CHANNELS_INPUT_BY_IDX(node_id, idx))
370+
371+
/**
372+
* @brief Equivalent to ADC_DT_SPEC_GET_BY_IDX(node_id, 0).
373+
*
374+
* @see ADC_DT_SPEC_GET_BY_IDX()
375+
*
376+
* @param node_id Devicetree node identifier.
377+
*
378+
* @return Static initializer for an adc_dt_spec structure.
379+
*/
380+
#define ADC_DT_SPEC_GET(node_id) ADC_DT_SPEC_GET_BY_IDX(node_id, 0)
381+
149382
/**
150383
* @brief Convert a raw ADC value to millivolts.
151384
*

0 commit comments

Comments
 (0)