1 /*
2 * This file is part of the MicroPython project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2017-2018 Glenn Ruben Bakke
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24 * THE SOFTWARE.
25 */
26
27 #include <stdio.h>
28 #include <string.h>
29
30 #include "py/nlr.h"
31 #include "py/runtime.h"
32 #include "py/mphal.h"
33
34 #if MICROPY_PY_MACHINE_ADC
35
36 #include "adc.h"
37
38 #if NRF51
39 #include "nrfx_adc.h"
40 #else
41 #include "nrfx_saadc.h"
42 #endif
43
44 typedef struct _machine_adc_obj_t {
45 mp_obj_base_t base;
46 uint8_t id;
47 #if NRF51
48 uint8_t ain;
49 #endif
50 } machine_adc_obj_t;
51
52 STATIC const machine_adc_obj_t machine_adc_obj[] = {
53 #if NRF51
54 {{&machine_adc_type}, .id = 0, .ain = NRF_ADC_CONFIG_INPUT_0},
55 {{&machine_adc_type}, .id = 1, .ain = NRF_ADC_CONFIG_INPUT_1},
56 {{&machine_adc_type}, .id = 2, .ain = NRF_ADC_CONFIG_INPUT_2},
57 {{&machine_adc_type}, .id = 3, .ain = NRF_ADC_CONFIG_INPUT_3},
58 {{&machine_adc_type}, .id = 4, .ain = NRF_ADC_CONFIG_INPUT_4},
59 {{&machine_adc_type}, .id = 5, .ain = NRF_ADC_CONFIG_INPUT_5},
60 {{&machine_adc_type}, .id = 6, .ain = NRF_ADC_CONFIG_INPUT_6},
61 {{&machine_adc_type}, .id = 7, .ain = NRF_ADC_CONFIG_INPUT_7},
62 #else
63 {{&machine_adc_type}, .id = 0},
64 {{&machine_adc_type}, .id = 1},
65 {{&machine_adc_type}, .id = 2},
66 {{&machine_adc_type}, .id = 3},
67 {{&machine_adc_type}, .id = 4},
68 {{&machine_adc_type}, .id = 5},
69 {{&machine_adc_type}, .id = 6},
70 {{&machine_adc_type}, .id = 7},
71 #endif
72 };
73
adc_init0(void)74 void adc_init0(void) {
75 #if defined(NRF52_SERIES)
76 const uint8_t interrupt_priority = 6;
77 nrfx_saadc_init(interrupt_priority);
78 #endif
79 }
80
adc_find(mp_obj_t id)81 STATIC int adc_find(mp_obj_t id) {
82 int adc_idx;
83 if (mp_obj_is_int(id)) {
84 // Given an integer id
85 adc_idx = mp_obj_get_int(id);
86 } else {
87 // Assume it's a pin-compatible object and convert it to an ADC channel number
88 mp_hal_pin_obj_t pin = mp_hal_get_pin_obj(id);
89 if (pin->adc_num & PIN_ADC1) {
90 adc_idx = pin->adc_channel;
91 } else {
92 mp_raise_ValueError(MP_ERROR_TEXT("invalid Pin for ADC"));
93 }
94 }
95
96 if (adc_idx >= 0 && adc_idx < MP_ARRAY_SIZE(machine_adc_obj)
97 && machine_adc_obj[adc_idx].id != (uint8_t)-1) {
98 return adc_idx;
99 }
100 mp_raise_ValueError(MP_ERROR_TEXT("ADC doesn't exist"));
101 }
102
103 /// \method __str__()
104 /// Return a string describing the ADC object.
machine_adc_print(const mp_print_t * print,mp_obj_t o,mp_print_kind_t kind)105 STATIC void machine_adc_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind) {
106 machine_adc_obj_t *self = o;
107
108 mp_printf(print, "ADC(%u)", self->id);
109 }
110
111 /******************************************************************************/
112 /* MicroPython bindings for machine API */
113
114 // for make_new
machine_adc_make_new(const mp_obj_type_t * type,size_t n_args,size_t n_kw,const mp_obj_t * all_args)115 STATIC mp_obj_t machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
116 enum { ARG_id };
117 static const mp_arg_t allowed_args[] = {
118 { MP_QSTR_id, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(-1) } },
119 };
120
121 // parse args
122 mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
123 mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
124
125 int adc_id = adc_find(args[ARG_id].u_obj);
126 const machine_adc_obj_t *self = &machine_adc_obj[adc_id];
127
128 #if defined(NRF52_SERIES)
129 const nrfx_saadc_channel_t config = { \
130 .channel_config =
131 {
132 .resistor_p = NRF_SAADC_RESISTOR_DISABLED,
133 .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
134 .gain = NRF_SAADC_GAIN1_4,
135 .reference = NRF_SAADC_REFERENCE_VDD4,
136 .acq_time = NRF_SAADC_ACQTIME_3US,
137 .mode = NRF_SAADC_MODE_SINGLE_ENDED,
138 .burst = NRF_SAADC_BURST_DISABLED,
139 },
140 .pin_p = (nrf_saadc_input_t)(1 + self->id), // pin_p=0 is AIN0, pin_p=8 is AIN7
141 .pin_n = NRF_SAADC_INPUT_DISABLED,
142 .channel_index = self->id,
143 };
144 nrfx_saadc_channels_config(&config, 1);
145 #endif
146
147 return MP_OBJ_FROM_PTR(self);
148 }
149
machine_adc_value_read(machine_adc_obj_t * adc_obj)150 int16_t machine_adc_value_read(machine_adc_obj_t * adc_obj) {
151
152 #if NRF51
153 nrf_adc_value_t value = 0;
154
155 nrfx_adc_channel_t channel_config = {
156 .config.resolution = NRF_ADC_CONFIG_RES_8BIT,
157 .config.input = NRF_ADC_CONFIG_SCALING_INPUT_TWO_THIRDS,
158 .config.reference = NRF_ADC_CONFIG_REF_VBG,
159 .config.input = adc_obj->ain,
160 .config.extref = ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos // Currently not defined in nrfx/hal.
161 };
162
163 nrfx_adc_sample_convert(&channel_config, &value);
164 #else // NRF52
165 nrf_saadc_value_t value = 0;
166
167 nrfx_saadc_simple_mode_set((1 << adc_obj->id), NRF_SAADC_RESOLUTION_8BIT, NRF_SAADC_INPUT_DISABLED, NULL);
168 nrfx_saadc_buffer_set(&value, 1);
169 nrfx_saadc_mode_trigger();
170 #endif
171 return value;
172 }
173
174 // read_u16()
machine_adc_read_u16(mp_obj_t self_in)175 STATIC mp_obj_t machine_adc_read_u16(mp_obj_t self_in) {
176 machine_adc_obj_t *self = self_in;
177 int16_t raw = machine_adc_value_read(self);
178 #if defined(NRF52_SERIES)
179 // raw is signed but the channel is in single-ended mode and this method cannot return negative values
180 if (raw < 0) {
181 raw = 0;
182 }
183 #endif
184 // raw is an 8-bit value
185 return MP_OBJ_NEW_SMALL_INT(raw << 8 | raw);
186 }
187 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_machine_adc_read_u16_obj, machine_adc_read_u16);
188
189 /// \method value()
190 /// Read adc level.
machine_adc_value(mp_obj_t self_in)191 mp_obj_t machine_adc_value(mp_obj_t self_in) {
192 machine_adc_obj_t *self = self_in;
193
194 int16_t value = machine_adc_value_read(self);
195
196 return MP_OBJ_NEW_SMALL_INT(value);
197 }
198 STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_machine_adc_value_obj, machine_adc_value);
199
200 #if NRF51
201
202 #define ADC_REF_VOLTAGE_IN_MILLIVOLT (1200) // Reference voltage in mV (1.2V).
203 #define ADC_PRE_SCALING_MULTIPLIER (3) // VDD 1/3 prescaling as input. Hence, multiplied by 3 to get the value of the battery voltage.
204
205 #else // NRF52
206
207 #define ADC_REF_VOLTAGE_IN_MILLIVOLT (600) // Reference voltage in mV (0.6V).
208 #define ADC_PRE_SCALING_MULTIPLIER (6) // VDD 1/6 prescaling as input. Hence, multiplied by 6 to get the value of the battery voltage.
209
210 #endif
211
212 #define DIODE_VOLT_DROP_MILLIVOLT (270) // Voltage drop over diode.
213
214 #define BATTERY_MILLIVOLT(VALUE) \
215 ((((VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLT) / 255) * ADC_PRE_SCALING_MULTIPLIER)
216
battery_level_in_percent(const uint16_t mvolts)217 static uint8_t battery_level_in_percent(const uint16_t mvolts)
218 {
219 uint8_t battery_level;
220
221 if (mvolts >= 3000) {
222 battery_level = 100;
223 } else if (mvolts > 2900) {
224 battery_level = 100 - ((3000 - mvolts) * 58) / 100;
225 } else if (mvolts > 2740) {
226 battery_level = 42 - ((2900 - mvolts) * 24) / 160;
227 } else if (mvolts > 2440) {
228 battery_level = 18 - ((2740 - mvolts) * 12) / 300;
229 } else if (mvolts > 2100) {
230 battery_level = 6 - ((2440 - mvolts) * 6) / 340;
231 } else {
232 battery_level = 0;
233 }
234
235 return battery_level;
236 }
237
238 /// \method battery_level()
239 /// Get battery level in percentage.
machine_adc_battery_level(void)240 mp_obj_t machine_adc_battery_level(void) {
241
242 #if NRF51
243 nrf_adc_value_t value = 0;
244
245 nrfx_adc_channel_t channel_config = {
246 .config.resolution = NRF_ADC_CONFIG_RES_8BIT,
247 .config.input = NRF_ADC_CONFIG_SCALING_SUPPLY_ONE_THIRD,
248 .config.reference = NRF_ADC_CONFIG_REF_VBG,
249 .config.input = NRF_ADC_CONFIG_INPUT_DISABLED,
250 .config.extref = ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos // Currently not defined in nrfx/hal.
251 };
252
253 nrfx_adc_sample_convert(&channel_config, &value);
254 #else // NRF52
255 nrf_saadc_value_t value = 0;
256
257 const nrfx_saadc_channel_t config = { \
258 .channel_config =
259 {
260 .resistor_p = NRF_SAADC_RESISTOR_DISABLED,
261 .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
262 .gain = NRF_SAADC_GAIN1_6,
263 .reference = NRF_SAADC_REFERENCE_INTERNAL,
264 .acq_time = NRF_SAADC_ACQTIME_3US,
265 .mode = NRF_SAADC_MODE_SINGLE_ENDED,
266 .burst = NRF_SAADC_BURST_DISABLED,
267 },
268 .pin_p = NRF_SAADC_INPUT_VDD,
269 .pin_n = NRF_SAADC_INPUT_DISABLED,
270 .channel_index = 0,
271 };
272 nrfx_saadc_channels_config(&config, 1);
273
274 nrfx_saadc_simple_mode_set((1 << 0), NRF_SAADC_RESOLUTION_8BIT, NRF_SAADC_INPUT_DISABLED, NULL);
275 nrfx_saadc_buffer_set(&value, 1);
276 nrfx_saadc_mode_trigger();
277 #endif
278
279 uint16_t batt_lvl_in_milli_volts = BATTERY_MILLIVOLT(value) + DIODE_VOLT_DROP_MILLIVOLT;
280 uint16_t batt_in_percent = battery_level_in_percent(batt_lvl_in_milli_volts);
281
282 return MP_OBJ_NEW_SMALL_INT(batt_in_percent);
283 }
284 STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_machine_adc_battery_level_obj, machine_adc_battery_level);
285
286 STATIC const mp_rom_map_elem_t machine_adc_locals_dict_table[] = {
287 // instance methods
288 { MP_ROM_QSTR(MP_QSTR_read_u16), MP_ROM_PTR(&mp_machine_adc_read_u16_obj) },
289 { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&mp_machine_adc_value_obj) },
290
291 // class methods
292 { MP_ROM_QSTR(MP_QSTR_battery_level), MP_ROM_PTR(&mp_machine_adc_battery_level_obj) },
293 };
294
295 STATIC MP_DEFINE_CONST_DICT(machine_adc_locals_dict, machine_adc_locals_dict_table);
296
297 const mp_obj_type_t machine_adc_type = {
298 { &mp_type_type },
299 .name = MP_QSTR_ADC,
300 .make_new = machine_adc_make_new,
301 .locals_dict = (mp_obj_dict_t*)&machine_adc_locals_dict,
302 .print = machine_adc_print,
303 };
304
305 #endif // MICROPY_PY_MACHINE_ADC
306