|
13 | 13 | #error "No suitable devicetree overlay specified" |
14 | 14 | #endif |
15 | 15 |
|
16 | | -#define ADC_NUM_CHANNELS DT_PROP_LEN(DT_PATH(zephyr_user), io_channels) |
| 16 | +#define DT_SPEC_AND_COMMA(node_id, prop, idx) \ |
| 17 | + ADC_DT_SPEC_GET_BY_IDX(node_id, idx), |
17 | 18 |
|
18 | | -#if ADC_NUM_CHANNELS > 2 |
19 | | -#error "Currently only 1 or 2 channels supported in this sample" |
20 | | -#endif |
| 19 | +/* Data of ADC io-channels specified in devicetree. */ |
| 20 | +static const struct adc_dt_spec adc_channels[] = { |
| 21 | + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, |
| 22 | + DT_SPEC_AND_COMMA) |
| 23 | +}; |
21 | 24 |
|
22 | | -#if ADC_NUM_CHANNELS == 2 && !DT_SAME_NODE( \ |
23 | | - DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), io_channels, 0), \ |
24 | | - DT_PHANDLE_BY_IDX(DT_PATH(zephyr_user), io_channels, 1)) |
25 | | -#error "Channels have to use the same ADC." |
26 | | -#endif |
| 25 | +#define LABEL_AND_COMMA(node_id, prop, idx) \ |
| 26 | + DT_LABEL(DT_IO_CHANNELS_CTLR_BY_IDX(node_id, idx)), |
27 | 27 |
|
28 | | -#define ADC_NODE DT_PHANDLE(DT_PATH(zephyr_user), io_channels) |
| 28 | +/* Labels of ADC controllers referenced by the above io-channels. */ |
| 29 | +static const char *const adc_labels[] = { |
| 30 | + DT_FOREACH_PROP_ELEM(DT_PATH(zephyr_user), io_channels, |
| 31 | + LABEL_AND_COMMA) |
| 32 | +}; |
29 | 33 |
|
30 | | -/* Common settings supported by most ADCs */ |
| 34 | +/* |
| 35 | + * Common settings supported by most ADCs. |
| 36 | + * If for a given channel a configuration is available in devicetree, values |
| 37 | + * for gain, reference, and acquisition time are taken from there instead of |
| 38 | + * those below. Also resolution can be optionally specified together with |
| 39 | + * the channel configuration in devicetree. If it is, that value is used |
| 40 | + * instead of the default one below. |
| 41 | + */ |
31 | 42 | #define ADC_RESOLUTION 12 |
32 | 43 | #define ADC_GAIN ADC_GAIN_1 |
33 | 44 | #define ADC_REFERENCE ADC_REF_INTERNAL |
34 | 45 | #define ADC_ACQUISITION_TIME ADC_ACQ_TIME_DEFAULT |
35 | 46 |
|
36 | | -#ifdef CONFIG_ADC_NRFX_SAADC |
37 | | -#define ADC_INPUT_POS_OFFSET SAADC_CH_PSELP_PSELP_AnalogInput0 |
38 | | -#else |
39 | | -#define ADC_INPUT_POS_OFFSET 0 |
40 | | -#endif |
41 | | - |
42 | | -/* Get the numbers of up to two channels */ |
43 | | -static uint8_t channel_ids[ADC_NUM_CHANNELS] = { |
44 | | - DT_IO_CHANNELS_INPUT_BY_IDX(DT_PATH(zephyr_user), 0), |
45 | | -#if ADC_NUM_CHANNELS == 2 |
46 | | - DT_IO_CHANNELS_INPUT_BY_IDX(DT_PATH(zephyr_user), 1) |
47 | | -#endif |
48 | | -}; |
| 47 | +static void configure_channel(const struct adc_dt_spec *dt_spec, |
| 48 | + uint16_t *vref_mv) |
| 49 | +{ |
| 50 | + /* If a configuration for the channel is specified in devicetree, |
| 51 | + * use it, otherwise use default settings that should be suitable |
| 52 | + * for most ADCs. |
| 53 | + */ |
| 54 | + if (dt_spec->channel_cfg_dt_node_exists) { |
| 55 | + adc_channel_setup(dt_spec->dev, &dt_spec->channel_cfg); |
49 | 56 |
|
50 | | -static int16_t sample_buffer[ADC_NUM_CHANNELS]; |
| 57 | + /* For the internal reference, use the voltage value returned |
| 58 | + * by the dedicated API function. For others, use the value |
| 59 | + * from devicetree if available. |
| 60 | + */ |
| 61 | + if (dt_spec->channel_cfg.reference == ADC_REF_INTERNAL) { |
| 62 | + *vref_mv = adc_ref_internal(dt_spec->dev); |
| 63 | + } else if (dt_spec->vref_mv > 0) { |
| 64 | + *vref_mv = dt_spec->vref_mv; |
| 65 | + } |
| 66 | + } else { |
| 67 | + struct adc_channel_cfg channel_cfg = { |
| 68 | + .channel_id = dt_spec->channel_id, |
| 69 | + .gain = ADC_GAIN, |
| 70 | + .reference = ADC_REFERENCE, |
| 71 | + .acquisition_time = ADC_ACQUISITION_TIME, |
| 72 | + }; |
51 | 73 |
|
52 | | -struct adc_channel_cfg channel_cfg = { |
53 | | - .gain = ADC_GAIN, |
54 | | - .reference = ADC_REFERENCE, |
55 | | - .acquisition_time = ADC_ACQUISITION_TIME, |
56 | | - /* channel ID will be overwritten below */ |
57 | | - .channel_id = 0, |
58 | | - .differential = 0 |
59 | | -}; |
| 74 | + adc_channel_setup(dt_spec->dev, &channel_cfg); |
60 | 75 |
|
61 | | -struct adc_sequence sequence = { |
62 | | - /* individual channels will be added below */ |
63 | | - .channels = 0, |
64 | | - .buffer = sample_buffer, |
65 | | - /* buffer size in bytes, not number of samples */ |
66 | | - .buffer_size = sizeof(sample_buffer), |
67 | | - .resolution = ADC_RESOLUTION, |
68 | | -}; |
| 76 | + *vref_mv = adc_ref_internal(dt_spec->dev); |
| 77 | + } |
| 78 | +} |
69 | 79 |
|
70 | | -void main(void) |
| 80 | +static void prepare_sequence(struct adc_sequence *sequence, |
| 81 | + const struct adc_dt_spec *dt_spec) |
71 | 82 | { |
72 | | - int err; |
73 | | - const struct device *dev_adc = DEVICE_DT_GET(ADC_NODE); |
| 83 | + sequence->channels = BIT(dt_spec->channel_id); |
| 84 | + sequence->resolution = ADC_RESOLUTION; |
| 85 | + sequence->oversampling = 0; |
74 | 86 |
|
75 | | - if (!device_is_ready(dev_adc)) { |
76 | | - printk("ADC device not found\n"); |
77 | | - return; |
| 87 | + if (dt_spec->channel_cfg_dt_node_exists) { |
| 88 | + if (dt_spec->resolution) { |
| 89 | + sequence->resolution = dt_spec->resolution; |
| 90 | + } |
| 91 | + if (dt_spec->oversampling) { |
| 92 | + sequence->oversampling = dt_spec->oversampling; |
| 93 | + } |
78 | 94 | } |
| 95 | +} |
79 | 96 |
|
80 | | - /* |
81 | | - * Configure channels individually prior to sampling |
82 | | - */ |
83 | | - for (uint8_t i = 0; i < ADC_NUM_CHANNELS; i++) { |
84 | | - channel_cfg.channel_id = channel_ids[i]; |
85 | | -#ifdef CONFIG_ADC_CONFIGURABLE_INPUTS |
86 | | - channel_cfg.input_positive = ADC_INPUT_POS_OFFSET + channel_ids[i]; |
87 | | -#endif |
| 97 | +static void print_millivolts(int32_t value, |
| 98 | + uint16_t vref_mv, |
| 99 | + uint8_t resolution, |
| 100 | + const struct adc_dt_spec *dt_spec) |
| 101 | +{ |
| 102 | + enum adc_gain gain = ADC_GAIN; |
88 | 103 |
|
89 | | - adc_channel_setup(dev_adc, &channel_cfg); |
| 104 | + if (dt_spec->channel_cfg_dt_node_exists) { |
| 105 | + gain = dt_spec->channel_cfg.gain; |
90 | 106 |
|
91 | | - sequence.channels |= BIT(channel_ids[i]); |
| 107 | + /* |
| 108 | + * For differential channels, one bit less needs to be specified |
| 109 | + * for resolution to achieve correct conversion. |
| 110 | + */ |
| 111 | + if (dt_spec->channel_cfg.differential) { |
| 112 | + resolution -= 1; |
| 113 | + } |
92 | 114 | } |
93 | 115 |
|
94 | | - int32_t adc_vref = adc_ref_internal(dev_adc); |
| 116 | + adc_raw_to_millivolts(vref_mv, gain, resolution, &value); |
| 117 | + printk(" = %d mV", value); |
| 118 | +} |
95 | 119 |
|
96 | | - while (1) { |
97 | | - /* |
98 | | - * Read sequence of channels (fails if not supported by MCU) |
99 | | - */ |
100 | | - err = adc_read(dev_adc, &sequence); |
101 | | - if (err != 0) { |
102 | | - printk("ADC reading failed with error %d.\n", err); |
| 120 | +void main(void) |
| 121 | +{ |
| 122 | + int err; |
| 123 | + int16_t sample_buffer[1]; |
| 124 | + struct adc_sequence sequence = { |
| 125 | + .buffer = sample_buffer, |
| 126 | + /* buffer size in bytes, not number of samples */ |
| 127 | + .buffer_size = sizeof(sample_buffer), |
| 128 | + }; |
| 129 | + uint16_t vref_mv[ARRAY_SIZE(adc_channels)] = { 0 }; |
| 130 | + |
| 131 | + /* Configure channels individually prior to sampling. */ |
| 132 | + for (uint8_t i = 0; i < ARRAY_SIZE(adc_channels); i++) { |
| 133 | + if (!device_is_ready(adc_channels[i].dev)) { |
| 134 | + printk("ADC device not found\n"); |
103 | 135 | return; |
104 | 136 | } |
105 | 137 |
|
106 | | - printk("ADC reading:"); |
107 | | - for (uint8_t i = 0; i < ADC_NUM_CHANNELS; i++) { |
108 | | - int32_t raw_value = sample_buffer[i]; |
109 | | - |
110 | | - printk(" %d", raw_value); |
111 | | - if (adc_vref > 0) { |
112 | | - /* |
113 | | - * Convert raw reading to millivolts if driver |
114 | | - * supports reading of ADC reference voltage |
115 | | - */ |
116 | | - int32_t mv_value = raw_value; |
117 | | - |
118 | | - adc_raw_to_millivolts(adc_vref, ADC_GAIN, |
119 | | - ADC_RESOLUTION, &mv_value); |
120 | | - printk(" = %d mV ", mv_value); |
| 138 | + configure_channel(&adc_channels[i], &vref_mv[i]); |
| 139 | + } |
| 140 | + |
| 141 | + while (1) { |
| 142 | + printk("ADC reading:\n"); |
| 143 | + for (uint8_t i = 0; i < ARRAY_SIZE(adc_channels); i++) { |
| 144 | + printk(" - %s, channel %d: ", |
| 145 | + adc_labels[i], adc_channels[i].channel_id); |
| 146 | + |
| 147 | + prepare_sequence(&sequence, &adc_channels[i]); |
| 148 | + |
| 149 | + err = adc_read(adc_channels[i].dev, &sequence); |
| 150 | + if (err < 0) { |
| 151 | + printk("error %d\n", err); |
| 152 | + continue; |
| 153 | + } else { |
| 154 | + printk("%d", sample_buffer[0]); |
| 155 | + } |
| 156 | + |
| 157 | + /* |
| 158 | + * Convert raw reading to millivolts if the reference |
| 159 | + * voltage is known. |
| 160 | + */ |
| 161 | + if (vref_mv[i] > 0) { |
| 162 | + print_millivolts(sample_buffer[0], |
| 163 | + vref_mv[i], |
| 164 | + sequence.resolution, |
| 165 | + &adc_channels[i]); |
121 | 166 | } |
| 167 | + printk("\n"); |
122 | 168 | } |
123 | | - printk("\n"); |
124 | 169 |
|
125 | 170 | k_sleep(K_MSEC(1000)); |
126 | 171 | } |
|
0 commit comments