sensor.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /*
  2. * Moistcontrol - sensor
  3. *
  4. * Copyright (c) 2013 Michael Buesch <m@bues.ch>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License along
  17. * with this program; if not, write to the Free Software Foundation, Inc.,
  18. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. */
  20. #include "sensor.h"
  21. #include "util.h"
  22. #include "main.h"
  23. #include <string.h>
  24. /* Warmup time: The time to wait with sensor _enabled_
  25. * before performing one measurement. */
  26. #define WARMUP_TIME msec_to_jiffies(50)
  27. /* Wait time: The time to wait with sensor _disabled_
  28. * between measurements. */
  29. #define WAIT_TIME msec_to_jiffies(500)
  30. /* Sensor state-machine values. */
  31. enum sensor_status {
  32. STAT_IDLE, /* Idle: No measurement requested. */
  33. STAT_WAIT, /* Wait: Wait time between measurements. */
  34. STAT_WARMUP_P0, /* Warmup: First warmup time before measurement. */
  35. STAT_WARMUP_P1, /* Warmup: Second warmup time before measurement. */
  36. STAT_ADC_CONV, /* ADC-conv: ADC-conversion is in progress. */
  37. };
  38. /* Context of a measurement. */
  39. struct sensor_context {
  40. /* Current state-machine status. */
  41. enum sensor_status stat;
  42. /* Generic timer used for wait and warmup. */
  43. jiffies_t timer;
  44. /* The sensor number of the currently active
  45. * measurement, if any. */
  46. uint8_t nr;
  47. /* Temporary buffer for the measured values. */
  48. uint16_t values[3];
  49. uint8_t value_count;
  50. };
  51. /* Instance of the measurement context. */
  52. static struct sensor_context sensor;
  53. /* Port mappings for the supply-A lines of the sensors.
  54. * The array indices are the sensor number.
  55. * The array values are the port/ddr bit number, DDR register
  56. * and PORT register, respectively.
  57. */
  58. static const uint8_t PROGMEM sensor_a_bit[] = {
  59. [0] = 5,
  60. [1] = 6,
  61. [2] = 7,
  62. [3] = 2,
  63. [4] = 1,
  64. [5] = 0,
  65. };
  66. static volatile uint8_t * const PROGMEM sensor_a_ddr[] = {
  67. [0] = &DDRD,
  68. [1] = &DDRD,
  69. [2] = &DDRD,
  70. [3] = &DDRB,
  71. [4] = &DDRB,
  72. [5] = &DDRB,
  73. };
  74. static volatile uint8_t * const PROGMEM sensor_a_port[] = {
  75. [0] = &PORTD,
  76. [1] = &PORTD,
  77. [2] = &PORTD,
  78. [3] = &PORTB,
  79. [4] = &PORTB,
  80. [5] = &PORTB,
  81. };
  82. /* Sensor supply terminal "B" definitions */
  83. #define SENSOR_SUPPLY_B_PORT PORTC
  84. #define SENSOR_SUPPLY_B_DDR DDRC
  85. #define SENSOR_SUPPLY_B_BIT 3
  86. /* The number of available sensors. */
  87. #define SENSOR_COUNT ARRAY_SIZE(sensor_a_bit)
  88. /* Read the current ADC value. */
  89. static uint16_t sensor_adc_read_value(void)
  90. {
  91. return ADCW;
  92. }
  93. /* Start an ADC conversion. */
  94. static void sensor_adc_start(void)
  95. {
  96. /* Initialize ADC to 125 kHz (on 16 MHz CPU).
  97. * Select AVcc reference.
  98. * Set multiplexer to ADC0.
  99. */
  100. build_assert(F_CPU == 16000000ul);
  101. ADMUX = (1 << REFS0);
  102. ADCSRA = (1 << ADEN) | (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2);
  103. ADCSRA |= (1 << ADSC);
  104. }
  105. /* Returns the boolean state of the ADC unit.
  106. * 0: conversion active. 1: conversion done.
  107. */
  108. static bool sensor_adc_done(void)
  109. {
  110. return !(ADCSRA & (1 << ADSC));
  111. }
  112. /* Get the port access values for a sensor "A" supply.
  113. * sensor_nr: The sensor number to get the values for.
  114. * bitmask: Pointer to the returned bitmask.
  115. * ddr: Pointer to the returned DDR register.
  116. * port: Pointer to the returned PORT register.
  117. */
  118. static inline void get_sensor_a_supply(uint8_t sensor_nr,
  119. uint8_t *bitmask,
  120. volatile uint8_t **ddr,
  121. volatile uint8_t **port)
  122. {
  123. uint8_t bitnr;
  124. PANIC_ON(sensor_nr >= SENSOR_COUNT);
  125. /* Read the bit number, DDR and PORT pointers from the tables. */
  126. bitnr = pgm_read_byte(&sensor_a_bit[sensor_nr]);
  127. *bitmask = BITMASK8(bitnr);
  128. *ddr = (volatile uint8_t *)pgm_read_word(&sensor_a_ddr[sensor_nr]);
  129. *port = (volatile uint8_t *)pgm_read_word(&sensor_a_port[sensor_nr]);
  130. }
  131. /* Enable the power supply of a sensor.
  132. * nr: The sensor number.
  133. * polarity: The supply polarity.
  134. * Polarity 0 means: Vcc on supply "B" and
  135. * GND on supply "A".
  136. * Polarity 1 means: Vcc on supply "A" and
  137. * GND on supply "B".
  138. */
  139. static void sensor_enable(uint8_t nr, bool polarity)
  140. {
  141. uint8_t sreg, a_mask;
  142. volatile uint8_t *a_ddr, *a_port;
  143. /* Get the supply-A credentials. */
  144. get_sensor_a_supply(nr, &a_mask, &a_ddr, &a_port);
  145. /* Enable A- and B-supply */
  146. sreg = irq_disable_save();
  147. if (polarity) {
  148. _MMIO_BYTE(a_ddr) |= a_mask;
  149. _MMIO_BYTE(a_port) |= a_mask;
  150. SENSOR_SUPPLY_B_DDR |= (1 << SENSOR_SUPPLY_B_BIT);
  151. SENSOR_SUPPLY_B_PORT &= ~(1 << SENSOR_SUPPLY_B_BIT);
  152. } else {
  153. _MMIO_BYTE(a_ddr) |= a_mask;
  154. _MMIO_BYTE(a_port) &= ~a_mask;
  155. SENSOR_SUPPLY_B_DDR |= (1 << SENSOR_SUPPLY_B_BIT);
  156. SENSOR_SUPPLY_B_PORT |= (1 << SENSOR_SUPPLY_B_BIT);
  157. }
  158. irq_restore(sreg);
  159. }
  160. /* Disable the power supply of a sensor.
  161. * nr: The sensor number.
  162. */
  163. static void sensor_disable(uint8_t nr)
  164. {
  165. uint8_t sreg, a_mask;
  166. volatile uint8_t *a_ddr, *a_port;
  167. /* Get the supply-A credentials. */
  168. get_sensor_a_supply(nr, &a_mask, &a_ddr, &a_port);
  169. /* Disable A- and B-supply. */
  170. sreg = irq_disable_save();
  171. _MMIO_BYTE(a_ddr) &= ~a_mask;
  172. _MMIO_BYTE(a_port) &= ~a_mask;
  173. SENSOR_SUPPLY_B_DDR &= ~(1 << SENSOR_SUPPLY_B_BIT);
  174. SENSOR_SUPPLY_B_PORT &= ~(1 << SENSOR_SUPPLY_B_BIT);
  175. irq_restore(sreg);
  176. }
  177. /* Start the warmup-cycle of the current sensor. */
  178. static void sensor_warmup_begin(void)
  179. {
  180. /* Set the warmup-end time and set
  181. * warmup-polarity-0 state. */
  182. sensor.timer = jiffies_get() + WARMUP_TIME;
  183. sensor.stat = STAT_WARMUP_P0;
  184. /* Enable the sensor with 0-polarity. */
  185. sensor_enable(sensor.nr, 0);
  186. }
  187. /* Start a measurement on a sensor.
  188. * nr: The sensor number.
  189. */
  190. void sensor_start(uint8_t nr)
  191. {
  192. if (sensor.stat != STAT_IDLE) {
  193. /* Current status is not idle, cannot start. */
  194. return;
  195. }
  196. if (nr >= SENSOR_COUNT) {
  197. /* Invalid sensor number. */
  198. return;
  199. }
  200. /* Reset the stored values to zero. */
  201. memset(sensor.values, 0, sizeof(sensor.values));
  202. sensor.value_count = 0;
  203. /* Store the sensor number. */
  204. sensor.nr = nr;
  205. /* Start the warmup sequence. */
  206. sensor_warmup_begin();
  207. }
  208. /* Cancel the currently running measurement. */
  209. void sensor_cancel(void)
  210. {
  211. if (sensor.stat == STAT_IDLE)
  212. return;
  213. /* Wait for possibly running ADC to finish. */
  214. while (!sensor_adc_done());
  215. /* Disable the supplies and reset the state machine. */
  216. sensor_disable(sensor.nr);
  217. sensor.stat = STAT_IDLE;
  218. }
  219. /* Returns 1, if no measurement is running.
  220. * Returns 0 otherwise.
  221. */
  222. bool sensors_idle(void)
  223. {
  224. return sensor.stat == STAT_IDLE;
  225. }
  226. /* Poll the current measurement state.
  227. * Returns 1, if the measurement is finished.
  228. * Returns 0, if the measurement is still in progress.
  229. * If the measurement is finished (1), it puts the
  230. * measurement result into "res".
  231. */
  232. bool sensor_poll(struct sensor_result *res)
  233. {
  234. jiffies_t now;
  235. uint16_t a, b, c, median;
  236. if (sensor.stat == STAT_IDLE) {
  237. /* No measurement running. Return early. */
  238. return 0;
  239. }
  240. /* Get the current time. */
  241. now = jiffies_get();
  242. /* Run the sensor statemachine. */
  243. switch (sensor.stat) {
  244. case STAT_IDLE:
  245. break;
  246. case STAT_WAIT:
  247. if (time_before(now, sensor.timer)) {
  248. /* Wait time not finished, yet. */
  249. break;
  250. }
  251. /* Wait finished. Switch to warmup. */
  252. sensor_warmup_begin();
  253. break;
  254. case STAT_WARMUP_P0:
  255. if (time_before(now, sensor.timer)) {
  256. /* Warmup with polarity 0 not finished, yet. */
  257. break;
  258. }
  259. /* Warmup with polarity 0 done.
  260. * Start warmup phase with polarity 1. */
  261. sensor_enable(sensor.nr, 1);
  262. sensor.timer = now + WARMUP_TIME;
  263. sensor.stat = STAT_WARMUP_P1;
  264. break;
  265. case STAT_WARMUP_P1:
  266. if (time_before(now, sensor.timer)) {
  267. /* Warmup with polarity 1 not finished, yet. */
  268. break;
  269. }
  270. /* Warmup with polarity 1 done.
  271. * Start ADC conversion. */
  272. sensor_adc_start();
  273. sensor.stat = STAT_ADC_CONV;
  274. break;
  275. case STAT_ADC_CONV:
  276. if (!sensor_adc_done()) {
  277. /* ADC conversion not finised, yet. */
  278. break;
  279. }
  280. /* ADC conversion done. Disable sensor. */
  281. sensor_disable(sensor.nr);
  282. /* Store the measured value. */
  283. sensor.values[sensor.value_count] = sensor_adc_read_value();
  284. sensor.value_count++;
  285. if (sensor.value_count >= 3) {
  286. /* All measurements done. */
  287. a = sensor.values[0];
  288. b = sensor.values[1];
  289. c = sensor.values[2];
  290. /* Get the median of all measurements.
  291. * 'b' will be the result. */
  292. if (a > b)
  293. swap_values(a, b);
  294. if (b > c)
  295. swap_values(b, c);
  296. median = b;
  297. /* Store the result. */
  298. res->nr = sensor.nr;
  299. res->value = median;
  300. sensor.stat = STAT_IDLE;
  301. /* Whole measurement done. */
  302. return 1;
  303. } else {
  304. /* Schedule the next measurement. */
  305. sensor.timer = now + WAIT_TIME;
  306. sensor.stat = STAT_WAIT;
  307. }
  308. break;
  309. }
  310. /* Measurement still in progress. */
  311. return 0;
  312. }
  313. /* Initialize the sensor unit. */
  314. void sensor_init(void)
  315. {
  316. uint8_t nr;
  317. build_assert(ARRAY_SIZE(sensor_a_bit) == ARRAY_SIZE(sensor_a_ddr));
  318. build_assert(ARRAY_SIZE(sensor_a_bit) == ARRAY_SIZE(sensor_a_port));
  319. /* Reset the sensor context. */
  320. memset(&sensor, 0, sizeof(sensor));
  321. /* Disable all sensors. */
  322. for (nr = 0; nr < SENSOR_COUNT; nr++)
  323. sensor_disable(nr);
  324. /* Perform one ADC conversion, as per AtMega datasheet the
  325. * very first conversion has less precision.
  326. * Discard the result.
  327. */
  328. sensor_adc_start();
  329. while (!sensor_adc_done());
  330. }