123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353 |
- /*
- * DHT11/22 driver
- *
- * Copyright (c) 2018 Michael Buesch <m@bues.ch>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #include "dht.h"
- #include "util.h"
- #include <string.h>
- #include <avr/interrupt.h>
- #define DHT_PORT PORTB
- #define DHT_PIN PINB
- #define DHT_DDR DDRB
- #define DHT_BIT 4u
- enum dht_state {
- DHT_WAIT, /* Wait for the next data transfer */
- DHT_START_OUT_LOW, /* Start: Output low */
- DHT_START_OUT_HIGH, /* Start: Output high */
- DHT_START_IN_LOW, /* Start: Input low */
- DHT_START_IN_HIGH, /* Start: Input high */
- DHT_DATA_IN_LOW, /* Data bit input: Low level */
- DHT_DATA_IN_HIGH, /* Data bit input: High level */
- };
- struct dht {
- enum dht_state state;
- dht_callback_t callback;
- uint8_t timer_count;
- uint8_t byte_count;
- uint8_t bit_mask;
- uint8_t data[DHT_DATA_LEN];
- bool finished;
- };
- static struct dht dht;
- static uint8_t dht_checksum(const uint8_t *data)
- {
- uint8_t i, checksum = 0u;
- for (i = 0u; i < DHT_DATA_LEN - 1u; i++)
- checksum = ((uint8_t)(checksum + data[i]) & 0xFFu);
- return checksum;
- }
- static void dht_pin_change_irq_en(bool enable)
- {
- if (enable) {
- GIFR = 1u << PCIF;
- GIMSK |= 1u << PCIE;
- } else {
- GIMSK &= (uint8_t)~(1u << PCIE);
- }
- }
- static void dht_pin_output(bool output, bool state)
- {
- if (output) {
- DHT_DDR |= 1u << DHT_BIT;
- if (state)
- DHT_PORT |= 1u << DHT_BIT;
- else
- DHT_PORT &= (uint8_t)~(1u << DHT_BIT);
- } else {
- DHT_DDR &= (uint8_t)~(1u << DHT_BIT);
- DHT_PORT |= 1u << DHT_BIT;
- }
- }
- static void dht_pin_init(void)
- {
- dht_pin_change_irq_en(false);
- PCMSK |= 1u << PCINT4;
- dht_pin_output(false, false);
- }
- static void dht_timer_init(void)
- {
- TIMSK &= (uint8_t)~((1u << OCIE1A) | (1u << OCIE1B) | (1u << TOIE1));
- TIFR = (1u << OCF1A) | (1u << OCF1B) | (1u << TOV1);
- TCCR1 = 0u;
- TCNT1 = 0u;
- OCR1A = 0u;
- OCR1B = 0u;
- OCR1C = 0u;
- GTCCR &= (uint8_t)~((1u << PWM1B) |
- (1u << COM1B1) | (1u << COM1B0) |
- (1u << FOC1B) | (1u << FOC1A) |
- (1u << PSR1));
- TCCR1 = (1u << CTC1) | (0u << PWM1A) |
- (0u << COM1A1) | (0u << COM1A0) |
- (0u << CS13) | (0u << CS12) | (0u << CS11) | (0u << CS10);
- }
- static void dht_timer_arm(void)
- {
- uint8_t cs = 0u;
- uint8_t ocr = 0u;
- switch (dht.state) {
- case DHT_WAIT:
- /* Idle wait time. */
- /* Prescaler = 16384 */
- cs = (1u << CS13) | (1u << CS12) | (1u << CS11) | (1u << CS10);
- ocr = 98u;
- /* Wait duration = 100 ms * 13 = 1.3 s*/
- dht.timer_count = 13u;
- dht_pin_change_irq_en(false);
- break;
- case DHT_START_OUT_LOW:
- /* The start-out low period is about to start. */
- /* Prescaler = 16 */
- cs = (1u << CS13) | (1u << CS12) | (1u << CS11) | (1u << CS10);
- /* Output low duration = 20 ms */
- ocr = 20u;
- break;
- case DHT_START_OUT_HIGH:
- case DHT_START_IN_LOW:
- case DHT_START_IN_HIGH:
- case DHT_DATA_IN_LOW:
- case DHT_DATA_IN_HIGH:
- /* One of the periods is about to start. */
- /* Prescaler = 16 */
- cs = (0u << CS13) | (1u << CS12) | (0u << CS11) | (1u << CS10);
- /* Timeout = 150 us */
- ocr = 150u;
- if (dht.state == DHT_START_OUT_HIGH) {
- /* Enable pin change interrupt */
- dht_pin_change_irq_en(true);
- }
- break;
- }
- /* Re-program the timer. */
- TCCR1 &= (uint8_t)~((1u << CS13) | (1u << CS12) | (1u << CS11) | (1u << CS10));
- OCR1A = ocr;
- OCR1C = ocr;
- TCNT1 = 0u;
- TIFR = 1u << OCF1A;
- TIMSK |= 1u << OCIE1A;
- TCCR1 |= cs;
- }
- static void dht_timeout(void)
- {
- /* Configure signal pin as input with pullup level = high */
- dht_pin_output(false, false);
- /* Go to initial state */
- dht.state = DHT_WAIT;
- dht_timer_arm();
- }
- /* Timer interrupt.
- * This interrupt is triggered on timing events or timeouts.
- */
- ISR(TIMER1_COMPA_vect)
- {
- memory_barrier();
- switch (dht.state) {
- case DHT_WAIT:
- /* Idle wait time. */
- if (--dht.timer_count == 0u) {
- /* End of wait time. */
- /* Configure signal pin as output with level = low */
- dht_pin_output(true, false);
- dht.state = DHT_START_OUT_LOW;
- dht_timer_arm();
- }
- break;
- case DHT_START_OUT_LOW:
- /* The start-out low period just ended. */
- /* Configure signal pin as input with pullup level = high */
- dht_pin_output(false, false);
- dht.state = DHT_START_OUT_HIGH;
- dht_timer_arm();
- break;
- case DHT_START_OUT_HIGH:
- case DHT_START_IN_LOW:
- case DHT_START_IN_HIGH:
- case DHT_DATA_IN_LOW:
- case DHT_DATA_IN_HIGH:
- default:
- /* Timeout waiting for input signal.
- * The pin change interrupt did not trigger. */
- dht_timeout();
- break;
- }
- memory_barrier();
- }
- /* Pin change interrupt.
- * This interrupt is triggered on signal pin changes.
- */
- ISR(PCINT0_vect)
- {
- uint8_t timer, calc_checksum, data_checksum;
- bool pin_state;
- memory_barrier();
- /* First get the signal timer and signal pin states. */
- timer = TCNT1;
- pin_state = !!(DHT_PIN & (1u << DHT_BIT));
- switch (dht.state) {
- default:
- case DHT_WAIT:
- case DHT_START_OUT_LOW:
- /* Error: Invalid state machine state. */
- dht_timeout();
- break;
- case DHT_START_OUT_HIGH:
- /* The start-out high period just ended. */
- if (pin_state) {
- /* Error: Invalid pin state. */
- dht_timeout();
- } else {
- dht.state = DHT_START_IN_LOW;
- dht_timer_arm();
- }
- break;
- case DHT_START_IN_LOW:
- /* The start-in low period just ended. */
- if (pin_state) {
- dht.state = DHT_START_IN_HIGH;
- dht_timer_arm();
- } else {
- /* Error: Invalid pin state. */
- dht_timeout();
- }
- break;
- case DHT_START_IN_HIGH:
- /* The start-in high period just ended. */
- if (pin_state) {
- /* Error: Invalid pin state. */
- dht_timeout();
- } else {
- /* Prepare state for the first bit. */
- dht.byte_count = 0u;
- dht.bit_mask = 0x80u;
- dht.finished = false;
- dht.state = DHT_DATA_IN_LOW;
- dht_timer_arm();
- }
- break;
- case DHT_DATA_IN_LOW:
- /* The data-in low period just ended. */
- if (pin_state) {
- if (dht.finished) {
- dht.state = DHT_WAIT;
- /* We are done. Calculate and compare the checksum.
- * If it matches, call the user callback. */
- calc_checksum = dht_checksum(dht.data);
- data_checksum = dht.data[DHT_DATA_LEN - 1u];
- if (calc_checksum == data_checksum)
- dht.callback(dht.data);
- } else
- dht.state = DHT_DATA_IN_HIGH;
- dht_timer_arm();
- } else {
- /* Error: Invalid pin state. */
- dht_timeout();
- }
- break;
- case DHT_DATA_IN_HIGH:
- /* The data-in high period just ended. */
- if (pin_state) {
- /* Error: Invalid pin state. */
- dht_timeout();
- } else {
- /* If the period was a long one, we have a 1 bit */
- if (timer >= 48u) {
- dht.data[dht.byte_count] |= dht.bit_mask;
- } else {
- dht.data[dht.byte_count] &= (uint8_t)~dht.bit_mask;
- }
- /* Shift mask to next bit. */
- dht.bit_mask >>= 1;
- if (dht.bit_mask == 0u) {
- dht.bit_mask = 0x80u;
- if (++dht.byte_count >= sizeof(dht.data)) {
- dht.byte_count = 0u;
- dht.finished = true;
- }
- }
- /* State = next bit */
- dht.state = DHT_DATA_IN_LOW;
- dht_timer_arm();
- }
- break;
- }
- memory_barrier();
- }
- void dht_start(dht_callback_t callback)
- {
- if (dht.state == DHT_WAIT) {
- dht.callback = callback;
- memory_barrier();
- dht_timer_arm();
- }
- }
- void dht_init(void)
- {
- memset(&dht, 0, sizeof(dht));
- dht.state = DHT_WAIT;
- dht_pin_init();
- dht_timer_init();
- }
|