adc.c 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  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. #include <stdio.h>
  27. #include <string.h>
  28. #include "py/nlr.h"
  29. #include "py/runtime.h"
  30. #include "py/mphal.h"
  31. #if MICROPY_PY_MACHINE_ADC
  32. #include "adc.h"
  33. #if NRF51
  34. #include "nrfx_adc.h"
  35. #else
  36. #include "nrfx_saadc.h"
  37. #endif
  38. typedef struct _machine_adc_obj_t {
  39. mp_obj_base_t base;
  40. uint8_t id;
  41. #if NRF51
  42. uint8_t ain;
  43. #endif
  44. } machine_adc_obj_t;
  45. STATIC const machine_adc_obj_t machine_adc_obj[] = {
  46. #if NRF51
  47. {{&machine_adc_type}, .id = 0, .ain = NRF_ADC_CONFIG_INPUT_0},
  48. {{&machine_adc_type}, .id = 1, .ain = NRF_ADC_CONFIG_INPUT_1},
  49. {{&machine_adc_type}, .id = 2, .ain = NRF_ADC_CONFIG_INPUT_2},
  50. {{&machine_adc_type}, .id = 3, .ain = NRF_ADC_CONFIG_INPUT_3},
  51. {{&machine_adc_type}, .id = 4, .ain = NRF_ADC_CONFIG_INPUT_4},
  52. {{&machine_adc_type}, .id = 5, .ain = NRF_ADC_CONFIG_INPUT_5},
  53. {{&machine_adc_type}, .id = 6, .ain = NRF_ADC_CONFIG_INPUT_6},
  54. {{&machine_adc_type}, .id = 7, .ain = NRF_ADC_CONFIG_INPUT_7},
  55. #else
  56. {{&machine_adc_type}, .id = 0},
  57. {{&machine_adc_type}, .id = 1},
  58. {{&machine_adc_type}, .id = 2},
  59. {{&machine_adc_type}, .id = 3},
  60. {{&machine_adc_type}, .id = 4},
  61. {{&machine_adc_type}, .id = 5},
  62. {{&machine_adc_type}, .id = 6},
  63. {{&machine_adc_type}, .id = 7},
  64. #endif
  65. };
  66. #if defined(NRF52_SERIES)
  67. STATIC void saadc_event_handler(nrfx_saadc_evt_t const * p_event) {
  68. (void)p_event;
  69. }
  70. #endif
  71. void adc_init0(void) {
  72. #if defined(NRF52_SERIES)
  73. const nrfx_saadc_config_t config = {
  74. .resolution = NRF_SAADC_RESOLUTION_8BIT,
  75. .oversample = NRF_SAADC_OVERSAMPLE_DISABLED,
  76. .interrupt_priority = 6,
  77. .low_power_mode = false
  78. };
  79. nrfx_saadc_init(&config, saadc_event_handler);
  80. #endif
  81. }
  82. STATIC int adc_find(mp_obj_t id) {
  83. // given an integer id
  84. int adc_id = mp_obj_get_int(id);
  85. int adc_idx = adc_id;
  86. if (adc_idx >= 0 && adc_idx <= MP_ARRAY_SIZE(machine_adc_obj)
  87. && machine_adc_obj[adc_idx].id != (uint8_t)-1) {
  88. return adc_idx;
  89. }
  90. nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_ValueError,
  91. "ADC(%d) does not exist", adc_id));
  92. }
  93. /// \method __str__()
  94. /// Return a string describing the ADC object.
  95. STATIC void machine_adc_print(const mp_print_t *print, mp_obj_t o, mp_print_kind_t kind) {
  96. machine_adc_obj_t *self = o;
  97. mp_printf(print, "ADC(%u)", self->id);
  98. }
  99. /******************************************************************************/
  100. /* MicroPython bindings for machine API */
  101. // for make_new
  102. 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) {
  103. enum { ARG_id };
  104. static const mp_arg_t allowed_args[] = {
  105. { MP_QSTR_id, MP_ARG_OBJ, {.u_obj = MP_OBJ_NEW_SMALL_INT(-1) } },
  106. };
  107. // parse args
  108. mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
  109. mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args);
  110. int adc_id = adc_find(args[ARG_id].u_obj);
  111. const machine_adc_obj_t *self = &machine_adc_obj[adc_id];
  112. #if defined(NRF52_SERIES)
  113. const nrf_saadc_channel_config_t config = {
  114. .resistor_p = NRF_SAADC_RESISTOR_DISABLED,
  115. .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
  116. .gain = NRF_SAADC_GAIN1_4,
  117. .reference = NRF_SAADC_REFERENCE_VDD4,
  118. .acq_time = NRF_SAADC_ACQTIME_3US,
  119. .mode = NRF_SAADC_MODE_SINGLE_ENDED,
  120. .burst = NRF_SAADC_BURST_DISABLED,
  121. .pin_p = self->id, // 0 - 7
  122. .pin_n = NRF_SAADC_INPUT_DISABLED
  123. };
  124. nrfx_saadc_channel_init(self->id, &config);
  125. #endif
  126. return MP_OBJ_FROM_PTR(self);
  127. }
  128. int16_t machine_adc_value_read(machine_adc_obj_t * adc_obj) {
  129. #if NRF51
  130. nrf_adc_value_t value = 0;
  131. nrfx_adc_channel_t channel_config = {
  132. .config.resolution = NRF_ADC_CONFIG_RES_8BIT,
  133. .config.input = NRF_ADC_CONFIG_SCALING_INPUT_TWO_THIRDS,
  134. .config.reference = NRF_ADC_CONFIG_REF_VBG,
  135. .config.input = adc_obj->ain,
  136. .config.extref = ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos // Currently not defined in nrfx/hal.
  137. };
  138. nrfx_adc_sample_convert(&channel_config, &value);
  139. #else // NRF52
  140. nrf_saadc_value_t value = 0;
  141. nrfx_saadc_sample_convert(adc_obj->id, &value);
  142. #endif
  143. return value;
  144. }
  145. /// \method value()
  146. /// Read adc level.
  147. mp_obj_t machine_adc_value(mp_obj_t self_in) {
  148. machine_adc_obj_t *self = self_in;
  149. int16_t value = machine_adc_value_read(self);
  150. return MP_OBJ_NEW_SMALL_INT(value);
  151. }
  152. STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_machine_adc_value_obj, machine_adc_value);
  153. #if NRF51
  154. #define ADC_REF_VOLTAGE_IN_MILLIVOLT (1200) // Reference voltage in mV (1.2V).
  155. #define ADC_PRE_SCALING_MULTIPLIER (3) // VDD 1/3 prescaling as input. Hence, multiplied by 3 to get the value of the battery voltage.
  156. #else // NRF52
  157. #define ADC_REF_VOLTAGE_IN_MILLIVOLT (600) // Reference voltage in mV (0.6V).
  158. #define ADC_PRE_SCALING_MULTIPLIER (6) // VDD 1/6 prescaling as input. Hence, multiplied by 6 to get the value of the battery voltage.
  159. #endif
  160. #define DIODE_VOLT_DROP_MILLIVOLT (270) // Voltage drop over diode.
  161. #define BATTERY_MILLIVOLT(VALUE) \
  162. ((((VALUE) * ADC_REF_VOLTAGE_IN_MILLIVOLT) / 255) * ADC_PRE_SCALING_MULTIPLIER)
  163. static uint8_t battery_level_in_percent(const uint16_t mvolts)
  164. {
  165. uint8_t battery_level;
  166. if (mvolts >= 3000) {
  167. battery_level = 100;
  168. } else if (mvolts > 2900) {
  169. battery_level = 100 - ((3000 - mvolts) * 58) / 100;
  170. } else if (mvolts > 2740) {
  171. battery_level = 42 - ((2900 - mvolts) * 24) / 160;
  172. } else if (mvolts > 2440) {
  173. battery_level = 18 - ((2740 - mvolts) * 12) / 300;
  174. } else if (mvolts > 2100) {
  175. battery_level = 6 - ((2440 - mvolts) * 6) / 340;
  176. } else {
  177. battery_level = 0;
  178. }
  179. return battery_level;
  180. }
  181. /// \method battery_level()
  182. /// Get battery level in percentage.
  183. mp_obj_t machine_adc_battery_level(void) {
  184. #if NRF51
  185. nrf_adc_value_t value = 0;
  186. nrfx_adc_channel_t channel_config = {
  187. .config.resolution = NRF_ADC_CONFIG_RES_8BIT,
  188. .config.input = NRF_ADC_CONFIG_SCALING_SUPPLY_ONE_THIRD,
  189. .config.reference = NRF_ADC_CONFIG_REF_VBG,
  190. .config.input = NRF_ADC_CONFIG_INPUT_DISABLED,
  191. .config.extref = ADC_CONFIG_EXTREFSEL_None << ADC_CONFIG_EXTREFSEL_Pos // Currently not defined in nrfx/hal.
  192. };
  193. nrfx_adc_sample_convert(&channel_config, &value);
  194. #else // NRF52
  195. nrf_saadc_value_t value = 0;
  196. const nrf_saadc_channel_config_t config = {
  197. .resistor_p = NRF_SAADC_RESISTOR_DISABLED,
  198. .resistor_n = NRF_SAADC_RESISTOR_DISABLED,
  199. .gain = NRF_SAADC_GAIN1_6,
  200. .reference = NRF_SAADC_REFERENCE_INTERNAL,
  201. .acq_time = NRF_SAADC_ACQTIME_3US,
  202. .mode = NRF_SAADC_MODE_SINGLE_ENDED,
  203. .burst = NRF_SAADC_BURST_DISABLED,
  204. .pin_p = NRF_SAADC_INPUT_VDD,
  205. .pin_n = NRF_SAADC_INPUT_DISABLED
  206. };
  207. nrfx_saadc_channel_init(0, &config);
  208. nrfx_saadc_sample_convert(0, &value);
  209. #endif
  210. uint16_t batt_lvl_in_milli_volts = BATTERY_MILLIVOLT(value) + DIODE_VOLT_DROP_MILLIVOLT;
  211. uint16_t batt_in_percent = battery_level_in_percent(batt_lvl_in_milli_volts);
  212. return MP_OBJ_NEW_SMALL_INT(batt_in_percent);
  213. }
  214. STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_machine_adc_battery_level_obj, machine_adc_battery_level);
  215. STATIC const mp_rom_map_elem_t machine_adc_locals_dict_table[] = {
  216. // instance methods
  217. { MP_ROM_QSTR(MP_QSTR_value), MP_ROM_PTR(&mp_machine_adc_value_obj) },
  218. // class methods
  219. { MP_ROM_QSTR(MP_QSTR_battery_level), MP_ROM_PTR(&mp_machine_adc_battery_level_obj) },
  220. };
  221. STATIC MP_DEFINE_CONST_DICT(machine_adc_locals_dict, machine_adc_locals_dict_table);
  222. const mp_obj_type_t machine_adc_type = {
  223. { &mp_type_type },
  224. .name = MP_QSTR_ADC,
  225. .make_new = machine_adc_make_new,
  226. .locals_dict = (mp_obj_dict_t*)&machine_adc_locals_dict,
  227. .print = machine_adc_print,
  228. };
  229. #endif // MICROPY_PY_MACHINE_ADC