measure.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. /*
  2. * Xytronic LF-1600
  3. * Measurement routines
  4. *
  5. * Copyright (c) 2015-2017 Michael Buesch <m@bues.ch>
  6. *
  7. * This program is free software; you can redistribute it and/or modify
  8. * it under the terms of the GNU General Public License as published by
  9. * the Free Software Foundation; either version 2 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along
  18. * with this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  20. */
  21. #include "measure.h"
  22. #include "scale.h"
  23. #include "timer.h"
  24. #include "debug_uart.h"
  25. #include "ring.h"
  26. #include <string.h>
  27. #include <avr/io.h>
  28. #include <avr/interrupt.h>
  29. /* Trigger source selection */
  30. #define TS_FREERUN ((0 << ADTS2) | (0 << ADTS1) | (0 << ADTS0))
  31. #define TS_ACOMP ((0 << ADTS2) | (0 << ADTS1) | (1 << ADTS0))
  32. #define TS_INT0 ((0 << ADTS2) | (1 << ADTS1) | (0 << ADTS0))
  33. #define TS_T0CMA ((0 << ADTS2) | (1 << ADTS1) | (1 << ADTS0))
  34. #define TS_T0OV ((1 << ADTS2) | (0 << ADTS1) | (0 << ADTS0))
  35. #define TS_T1CMB ((1 << ADTS2) | (0 << ADTS1) | (1 << ADTS0))
  36. #define TS_T1OV ((1 << ADTS2) | (1 << ADTS1) | (0 << ADTS0))
  37. #define TS_T1CAP ((1 << ADTS2) | (1 << ADTS1) | (1 << ADTS0))
  38. extern const struct measure_config __flash meastemp_config;
  39. extern const struct measure_config __flash meascurr_config;
  40. struct meas_chan_context {
  41. const struct measure_config __flash *config;
  42. int16_t old_report_value;
  43. #if CONF_ADJ
  44. fixpt_t adjustment;
  45. #endif
  46. bool plaus_timeout;
  47. bool plaus_timeout_timer_running;
  48. struct timer plaus_timeout_timer;
  49. };
  50. struct meas_context {
  51. struct meas_chan_context channels[NR_MEAS_CHANS];
  52. uint8_t active_chan;
  53. uint16_t avg_count;
  54. uint32_t avg_sum;
  55. struct timer avg_timer;
  56. bool result_available;
  57. };
  58. static struct meas_context meas = {
  59. .channels = {
  60. /* MEAS_CHAN_CURRENT */
  61. {
  62. .config = &meascurr_config,
  63. .old_report_value = 0,
  64. #if CONF_ADJ
  65. .adjustment = FLOAT_TO_FIXPT(0),
  66. #endif
  67. .plaus_timeout = false,
  68. .plaus_timeout_timer_running = false,
  69. .plaus_timeout_timer = { },
  70. },
  71. /* MEAS_CHAN_TEMP */
  72. {
  73. .config = &meastemp_config,
  74. .old_report_value = 0,
  75. #if CONF_ADJ
  76. .adjustment = FLOAT_TO_FIXPT(0),
  77. #endif
  78. .plaus_timeout = false,
  79. .plaus_timeout_timer_running = false,
  80. .plaus_timeout_timer = { },
  81. },
  82. },
  83. .active_chan = 0,
  84. .avg_count = 0,
  85. .avg_sum = 0,
  86. .avg_timer = { },
  87. .result_available = false,
  88. };
  89. uint16_t meascurr_filter_handler(uint16_t raw_adc);
  90. static uint16_t measure_user_filter_handler(enum measure_chan chan_id,
  91. uint16_t raw_adc)
  92. {
  93. switch (chan_id) {
  94. case MEAS_CHAN_TEMP:
  95. default:
  96. return raw_adc;
  97. case MEAS_CHAN_CURRENT:
  98. return meascurr_filter_handler(raw_adc);
  99. }
  100. }
  101. void meastemp_result_handler(fixpt_t measured_phys_value,
  102. enum measure_plausibility plaus);
  103. void meascurr_result_handler(fixpt_t measured_phys_value,
  104. enum measure_plausibility plaus);
  105. static void measure_user_result_handler(enum measure_chan chan_id,
  106. fixpt_t phys,
  107. enum measure_plausibility plaus)
  108. {
  109. switch (chan_id) {
  110. case MEAS_CHAN_TEMP:
  111. meastemp_result_handler(phys, plaus);
  112. break;
  113. case MEAS_CHAN_CURRENT:
  114. default:
  115. meascurr_result_handler(phys, plaus);
  116. break;
  117. }
  118. }
  119. static void adc_disable(void)
  120. {
  121. /* Disable the ADC. */
  122. ADCSRA = 0;
  123. /* Clear the interrupt flag. */
  124. ADCSRA |= 1 << ADIF;
  125. }
  126. static void adc_busywait(void)
  127. {
  128. do { } while (ADCSRA & (1 << ADSC));
  129. }
  130. static void adc_trigger(uint8_t mux, uint8_t ps, uint8_t ref, uint8_t did,
  131. bool irq, bool freerunning)
  132. {
  133. uint8_t trig, ie;
  134. /* Free-running mode selection. */
  135. if (freerunning)
  136. trig = 1 << ADATE;
  137. else
  138. trig = 0 << ADATE;
  139. /* Interrupt-enable selection */
  140. if (irq)
  141. ie = 1 << ADIE;
  142. else
  143. ie = 0 << ADIE;
  144. mb();
  145. /* Disable the digital input */
  146. DIDR0 |= did;
  147. /* Set multiplexer and start conversion. */
  148. ADMUX = ref | (0 << ADLAR) | mux;
  149. ADCSRB = (0 << ACME) | TS_FREERUN;
  150. ADCSRA = (1 << ADEN) | (1 << ADSC) | ps | ie | trig;
  151. }
  152. static void adc_trigger_next_chan(void)
  153. {
  154. const struct measure_config __flash *config;
  155. uint8_t active_chan;
  156. /* Switch to the next channel. */
  157. active_chan = ring_next(meas.active_chan, ARRAY_SIZE(meas.channels) - 1u);
  158. meas.active_chan = active_chan;
  159. config = meas.channels[active_chan].config;
  160. /* Reset the averaging. */
  161. meas.avg_sum = 0;
  162. meas.avg_count = 0;
  163. timer_arm(&meas.avg_timer,
  164. config->averaging_timeout_ms);
  165. /* Start the ADC in freerunning mode. */
  166. adc_trigger(config->mux, config->ps, config->ref, config->did,
  167. true, true);
  168. }
  169. static void measure_handle_result(void)
  170. {
  171. struct meas_chan_context *active_chan;
  172. enum measure_chan active_chan_id;
  173. const struct measure_config __flash *config;
  174. uint16_t raw_adc;
  175. fixpt_t phys;
  176. enum measure_plausibility plaus;
  177. active_chan_id = (enum measure_chan)meas.active_chan;
  178. active_chan = &meas.channels[active_chan_id];
  179. config = active_chan->config;
  180. /* Calculate the result of the averaging. */
  181. raw_adc = (uint16_t)(meas.avg_sum / meas.avg_count);
  182. debug_report_int16(DEBUG_PREFIX1(config->name),
  183. &active_chan->old_report_value,
  184. (int16_t)raw_adc);
  185. /* Filter the raw adc value, if we have a filter. */
  186. raw_adc = measure_user_filter_handler(active_chan_id, raw_adc);
  187. /* Scale raw to phys. */
  188. phys = scale((int16_t)raw_adc,
  189. (int16_t)config->scale_raw_lo,
  190. (int16_t)config->scale_raw_hi,
  191. config->scale_phys_lo,
  192. config->scale_phys_hi);
  193. /* Apply the physical value adjustment. */
  194. #if CONF_ADJ
  195. phys = fixpt_sub(phys, active_chan->adjustment);
  196. #endif
  197. /* Plausibility check. */
  198. if (phys < config->plaus_neglim) {
  199. phys = config->plaus_neglim;
  200. plaus = MEAS_NOT_PLAUSIBLE;
  201. } else if (phys > config->plaus_poslim) {
  202. phys = config->plaus_poslim;
  203. plaus = MEAS_NOT_PLAUSIBLE;
  204. } else {
  205. plaus = MEAS_PLAUSIBLE;
  206. active_chan->plaus_timeout_timer_running = false;
  207. active_chan->plaus_timeout = false;
  208. }
  209. if (plaus != MEAS_PLAUSIBLE &&
  210. !active_chan->plaus_timeout_timer_running) {
  211. timer_arm(&active_chan->plaus_timeout_timer,
  212. config->plaus_timeout_ms);
  213. active_chan->plaus_timeout_timer_running = true;
  214. }
  215. if (active_chan->plaus_timeout_timer_running &&
  216. timer_expired(&active_chan->plaus_timeout_timer))
  217. active_chan->plaus_timeout = true;
  218. if (active_chan->plaus_timeout)
  219. plaus = MEAS_PLAUS_TIMEOUT;
  220. measure_user_result_handler(active_chan_id, phys, plaus);
  221. }
  222. ISR(ADC_vect)
  223. {
  224. uint16_t raw_adc;
  225. mb();
  226. /* Read the raw ADC value. */
  227. raw_adc = ADC;
  228. /* Add it to the averaging sum and check if we are done. */
  229. meas.avg_sum += raw_adc;
  230. meas.avg_count += 1;
  231. if (timer_expired(&meas.avg_timer)) {
  232. meas.result_available = true;
  233. adc_disable();
  234. }
  235. mb();
  236. }
  237. void measure_start(void)
  238. {
  239. adc_trigger_next_chan();
  240. }
  241. void measure_work(void)
  242. {
  243. bool result_available;
  244. irq_disable();
  245. result_available = meas.result_available;
  246. meas.result_available = false;
  247. irq_enable();
  248. if (result_available) {
  249. measure_handle_result();
  250. adc_trigger_next_chan();
  251. }
  252. }
  253. void measure_adjust_set(enum measure_chan chan,
  254. fixpt_t adjustment)
  255. {
  256. #if CONF_ADJ
  257. meas.channels[chan].adjustment = adjustment;
  258. #endif
  259. }
  260. fixpt_t measure_adjust_get(enum measure_chan chan)
  261. {
  262. #if CONF_ADJ
  263. return meas.channels[chan].adjustment;
  264. #else
  265. return int_to_fixpt(0);
  266. #endif
  267. }
  268. void measure_init(void)
  269. {
  270. /* Discard the first measurement. */
  271. adc_trigger(MEAS_MUX_GND, MEAS_PS_64, MEAS_REF_AREF, MEAS_DID_NONE,
  272. true, false);
  273. adc_busywait();
  274. adc_disable();
  275. }