dht.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. /*
  2. * DHT11/22 driver
  3. *
  4. * Copyright (c) 2018 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 "dht.h"
  21. #include "util.h"
  22. #include <string.h>
  23. #include <avr/interrupt.h>
  24. #define DHT_PORT PORTB
  25. #define DHT_PIN PINB
  26. #define DHT_DDR DDRB
  27. #define DHT_BIT 4u
  28. enum dht_state {
  29. DHT_WAIT, /* Wait for the next data transfer */
  30. DHT_START_OUT_LOW, /* Start: Output low */
  31. DHT_START_OUT_HIGH, /* Start: Output high */
  32. DHT_START_IN_LOW, /* Start: Input low */
  33. DHT_START_IN_HIGH, /* Start: Input high */
  34. DHT_DATA_IN_LOW, /* Data bit input: Low level */
  35. DHT_DATA_IN_HIGH, /* Data bit input: High level */
  36. };
  37. struct dht {
  38. enum dht_state state;
  39. dht_callback_t callback;
  40. uint8_t timer_count;
  41. uint8_t byte_count;
  42. uint8_t bit_mask;
  43. uint8_t data[DHT_DATA_LEN];
  44. bool finished;
  45. };
  46. static struct dht dht;
  47. static uint8_t dht_checksum(const uint8_t *data)
  48. {
  49. uint8_t i, checksum = 0u;
  50. for (i = 0u; i < DHT_DATA_LEN - 1u; i++)
  51. checksum = ((uint8_t)(checksum + data[i]) & 0xFFu);
  52. return checksum;
  53. }
  54. static void dht_pin_change_irq_en(bool enable)
  55. {
  56. if (enable) {
  57. GIFR = 1u << PCIF;
  58. GIMSK |= 1u << PCIE;
  59. } else {
  60. GIMSK &= (uint8_t)~(1u << PCIE);
  61. }
  62. }
  63. static void dht_pin_output(bool output, bool state)
  64. {
  65. if (output) {
  66. DHT_DDR |= 1u << DHT_BIT;
  67. if (state)
  68. DHT_PORT |= 1u << DHT_BIT;
  69. else
  70. DHT_PORT &= (uint8_t)~(1u << DHT_BIT);
  71. } else {
  72. DHT_DDR &= (uint8_t)~(1u << DHT_BIT);
  73. DHT_PORT |= 1u << DHT_BIT;
  74. }
  75. }
  76. static void dht_pin_init(void)
  77. {
  78. dht_pin_change_irq_en(false);
  79. PCMSK |= 1u << PCINT4;
  80. dht_pin_output(false, false);
  81. }
  82. static void dht_timer_init(void)
  83. {
  84. TIMSK &= (uint8_t)~((1u << OCIE1A) | (1u << OCIE1B) | (1u << TOIE1));
  85. TIFR = (1u << OCF1A) | (1u << OCF1B) | (1u << TOV1);
  86. TCCR1 = 0u;
  87. TCNT1 = 0u;
  88. OCR1A = 0u;
  89. OCR1B = 0u;
  90. OCR1C = 0u;
  91. GTCCR &= (uint8_t)~((1u << PWM1B) |
  92. (1u << COM1B1) | (1u << COM1B0) |
  93. (1u << FOC1B) | (1u << FOC1A) |
  94. (1u << PSR1));
  95. TCCR1 = (1u << CTC1) | (0u << PWM1A) |
  96. (0u << COM1A1) | (0u << COM1A0) |
  97. (0u << CS13) | (0u << CS12) | (0u << CS11) | (0u << CS10);
  98. }
  99. static void dht_timer_arm(void)
  100. {
  101. uint8_t cs = 0u;
  102. uint8_t ocr = 0u;
  103. switch (dht.state) {
  104. case DHT_WAIT:
  105. /* Idle wait time. */
  106. /* Prescaler = 16384 */
  107. cs = (1u << CS13) | (1u << CS12) | (1u << CS11) | (1u << CS10);
  108. ocr = 98u;
  109. /* Wait duration = 100 ms * 13 = 1.3 s*/
  110. dht.timer_count = 13u;
  111. dht_pin_change_irq_en(false);
  112. break;
  113. case DHT_START_OUT_LOW:
  114. /* The start-out low period is about to start. */
  115. /* Prescaler = 16 */
  116. cs = (1u << CS13) | (1u << CS12) | (1u << CS11) | (1u << CS10);
  117. /* Output low duration = 20 ms */
  118. ocr = 20u;
  119. break;
  120. case DHT_START_OUT_HIGH:
  121. case DHT_START_IN_LOW:
  122. case DHT_START_IN_HIGH:
  123. case DHT_DATA_IN_LOW:
  124. case DHT_DATA_IN_HIGH:
  125. /* One of the periods is about to start. */
  126. /* Prescaler = 16 */
  127. cs = (0u << CS13) | (1u << CS12) | (0u << CS11) | (1u << CS10);
  128. /* Timeout = 150 us */
  129. ocr = 150u;
  130. if (dht.state == DHT_START_OUT_HIGH) {
  131. /* Enable pin change interrupt */
  132. dht_pin_change_irq_en(true);
  133. }
  134. break;
  135. }
  136. /* Re-program the timer. */
  137. TCCR1 &= (uint8_t)~((1u << CS13) | (1u << CS12) | (1u << CS11) | (1u << CS10));
  138. OCR1A = ocr;
  139. OCR1C = ocr;
  140. TCNT1 = 0u;
  141. TIFR = 1u << OCF1A;
  142. TIMSK |= 1u << OCIE1A;
  143. TCCR1 |= cs;
  144. }
  145. static void dht_timeout(void)
  146. {
  147. /* Configure signal pin as input with pullup level = high */
  148. dht_pin_output(false, false);
  149. /* Go to initial state */
  150. dht.state = DHT_WAIT;
  151. dht_timer_arm();
  152. }
  153. /* Timer interrupt.
  154. * This interrupt is triggered on timing events or timeouts.
  155. */
  156. ISR(TIMER1_COMPA_vect)
  157. {
  158. memory_barrier();
  159. switch (dht.state) {
  160. case DHT_WAIT:
  161. /* Idle wait time. */
  162. if (--dht.timer_count == 0u) {
  163. /* End of wait time. */
  164. /* Configure signal pin as output with level = low */
  165. dht_pin_output(true, false);
  166. dht.state = DHT_START_OUT_LOW;
  167. dht_timer_arm();
  168. }
  169. break;
  170. case DHT_START_OUT_LOW:
  171. /* The start-out low period just ended. */
  172. /* Configure signal pin as input with pullup level = high */
  173. dht_pin_output(false, false);
  174. dht.state = DHT_START_OUT_HIGH;
  175. dht_timer_arm();
  176. break;
  177. case DHT_START_OUT_HIGH:
  178. case DHT_START_IN_LOW:
  179. case DHT_START_IN_HIGH:
  180. case DHT_DATA_IN_LOW:
  181. case DHT_DATA_IN_HIGH:
  182. default:
  183. /* Timeout waiting for input signal.
  184. * The pin change interrupt did not trigger. */
  185. dht_timeout();
  186. break;
  187. }
  188. memory_barrier();
  189. }
  190. /* Pin change interrupt.
  191. * This interrupt is triggered on signal pin changes.
  192. */
  193. ISR(PCINT0_vect)
  194. {
  195. uint8_t timer, calc_checksum, data_checksum;
  196. bool pin_state;
  197. memory_barrier();
  198. /* First get the signal timer and signal pin states. */
  199. timer = TCNT1;
  200. pin_state = !!(DHT_PIN & (1u << DHT_BIT));
  201. switch (dht.state) {
  202. default:
  203. case DHT_WAIT:
  204. case DHT_START_OUT_LOW:
  205. /* Error: Invalid state machine state. */
  206. dht_timeout();
  207. break;
  208. case DHT_START_OUT_HIGH:
  209. /* The start-out high period just ended. */
  210. if (pin_state) {
  211. /* Error: Invalid pin state. */
  212. dht_timeout();
  213. } else {
  214. dht.state = DHT_START_IN_LOW;
  215. dht_timer_arm();
  216. }
  217. break;
  218. case DHT_START_IN_LOW:
  219. /* The start-in low period just ended. */
  220. if (pin_state) {
  221. dht.state = DHT_START_IN_HIGH;
  222. dht_timer_arm();
  223. } else {
  224. /* Error: Invalid pin state. */
  225. dht_timeout();
  226. }
  227. break;
  228. case DHT_START_IN_HIGH:
  229. /* The start-in high period just ended. */
  230. if (pin_state) {
  231. /* Error: Invalid pin state. */
  232. dht_timeout();
  233. } else {
  234. /* Prepare state for the first bit. */
  235. dht.byte_count = 0u;
  236. dht.bit_mask = 0x80u;
  237. dht.finished = false;
  238. dht.state = DHT_DATA_IN_LOW;
  239. dht_timer_arm();
  240. }
  241. break;
  242. case DHT_DATA_IN_LOW:
  243. /* The data-in low period just ended. */
  244. if (pin_state) {
  245. if (dht.finished) {
  246. dht.state = DHT_WAIT;
  247. /* We are done. Calculate and compare the checksum.
  248. * If it matches, call the user callback. */
  249. calc_checksum = dht_checksum(dht.data);
  250. data_checksum = dht.data[DHT_DATA_LEN - 1u];
  251. if (calc_checksum == data_checksum)
  252. dht.callback(dht.data);
  253. } else
  254. dht.state = DHT_DATA_IN_HIGH;
  255. dht_timer_arm();
  256. } else {
  257. /* Error: Invalid pin state. */
  258. dht_timeout();
  259. }
  260. break;
  261. case DHT_DATA_IN_HIGH:
  262. /* The data-in high period just ended. */
  263. if (pin_state) {
  264. /* Error: Invalid pin state. */
  265. dht_timeout();
  266. } else {
  267. /* If the period was a long one, we have a 1 bit */
  268. if (timer >= 48u) {
  269. dht.data[dht.byte_count] |= dht.bit_mask;
  270. } else {
  271. dht.data[dht.byte_count] &= (uint8_t)~dht.bit_mask;
  272. }
  273. /* Shift mask to next bit. */
  274. dht.bit_mask >>= 1;
  275. if (dht.bit_mask == 0u) {
  276. dht.bit_mask = 0x80u;
  277. if (++dht.byte_count >= sizeof(dht.data)) {
  278. dht.byte_count = 0u;
  279. dht.finished = true;
  280. }
  281. }
  282. /* State = next bit */
  283. dht.state = DHT_DATA_IN_LOW;
  284. dht_timer_arm();
  285. }
  286. break;
  287. }
  288. memory_barrier();
  289. }
  290. void dht_start(dht_callback_t callback)
  291. {
  292. if (dht.state == DHT_WAIT) {
  293. dht.callback = callback;
  294. memory_barrier();
  295. dht_timer_arm();
  296. }
  297. }
  298. void dht_init(void)
  299. {
  300. memset(&dht, 0, sizeof(dht));
  301. dht.state = DHT_WAIT;
  302. dht_pin_init();
  303. dht_timer_init();
  304. }