Tuesday, January 14, 2014

DHT22 Interrupt Driven library for AVR

This lib can read temperature and humidity of a DHT22 sensor without blocking the microcontroller with unnecessary delay functions. The lib is interrupt driven, all the timing measurements of sensor signal is done with a timer and a external interrupt using a state machine.

This way, you can use this lib with multiplexed displays without flicker the display during the measurement of the sensor data.
I hope this lib can be useful to someone.

Features


  1. Does not block the processor while reading the sensor.
  2. Does not uses floating point (code size much smaller).

Requirements


In order to use this library, you will need the following resources from your microcontroller:

  1. A pin with external interrupt (INT0, INT1 or other).
  2. A timer with Clear Timer on Compare Match mode (CTC). 
  3. Timer prescaler that gives a timer frequency of 1MHz. 

Pin change interrupt is not supported yet.

How it works


Check the comments in DHT22int.c file  to fully understand how it works. Basically:

  1. A timer in CTC is used to generate the host start condition. Pin is configured as output (this is done in the timer interrupt handler function). 
  2. Pin is switched to input with external interrupt. At each external interrupt the number of timer ticks (configured to occur at each microsecond) is counted. 
  3. The value of the counter (microseconds) is compared with a fixed value in a state machine, this way, the signal from DHT22 is interpreted. This is done at the External Interrupt Handler.

How to use


Include the lib:
#include "DHT22int.h"
Before entering the main loop of your program, declare some needed variables call the init function and enable interrupts.
DHT22_STATE_t state;
DHT22_DATA_t sensor_data;
DHT22_Init();
sei();
Periodically (or not, depending of your use), call the function to start reading the sensor:
state = DHT22_StartReading();
Check the state if you want to confirm that the state machine has started. In your main loop, check periodically when the data is available and in case of available, do something:
state = DHT22_CheckStatus(&sensor_data);

if (state == DHT_DATA_READY){
   // Do something with the data.
   // sensor_data.temperature_integral
   // sensor_data.temperature_decimal
   // sensor_data.humidity_integral
   // sensor_data.humidity_decimal
   }
else if (state == DHT_ERROR_CHECKSUM){
   // Do something if there is a Checksum error
   }
else if (state == DHT_ERROR_NOT_RESPOND){
   // Do something if the sensor did not respond
   }
To start a new measurement, you have to call DHT22_StartReading() again.

IMPORTANT: You need to modify the header (.h) file accordingly with your microcontroller, the external interrupt used (and the pin) and the timer.

Configure


The library is pre-configured for the following device and peripherals:

  • Microcontroller: ATmega328P
  • Timer: 8bit Timer 2
  • Sensor Pin: PD2 => INT0 pin
  • 8MHz clock from internal RC oscillator.
  • Divide by 8 fuse not programmed (clock is not divided by 8).

This config should also work with ATmega48A(PA), ATmega88A(PA), ATmega168A(PA) and ATmega328.

In order to change this configuration, please see the library header file (DHT22int.h).

The code was written in AtmelStudio 6. AVR-GCC compiler.

Please, let me know if this lib work with any other microcontroller. I can post here the configuration that you use (with credits, of course).

Notes


Note on 2014/09/20:
It seens that these DHT22 sensors sometimes show unpredicted behavior. I noticed that sometimes one of my DHT22 responds too fast. The datasheet says it shoul pull the line low for 80us in response to the initial pulse. This lib check if this time value is between 60us and 100us. With these default values, sometimes the DHT22_CheckStatus function returns DHT_ERROR_NOT_RESPOND. After changing the above lower limit to 40us it worked fine. So I conclude that this particular sensor is not according to the datasheet.

Download

C source file: link
C header file: link

18 comments:

  1. Thanks for this great library. I might still create my own, just for the fun of it, but this piece of well-documented code is the best learning material I could have wished for.

    The only thing that could be even better would be if the code was on Github ... :)

    ReplyDelete
    Replies
    1. Hello Zappes,

      thank you. I'm not very familiar with Github, but I will try to create a repository there.

      Best regards,

      Moreto

      Delete
  2. Hi, I think i understand how it works, but when I run your programm on my ATtiny2313 I always get 1 in variables witch contains values of measurement and check sum. I changed pin where is DHT22 and I changed timer to TIMER0. Can you help me with this problem?

    ReplyDelete
    Replies
    1. Hello Mr. Nice,

      sorry to take so much time to reply. I had to stop my projects for a while because I moved to other city and other university. Now I'm back again. Do you still have this problem? if so, can you show me the piece of code that you changed? Best regards,

      Miguel

      Delete
  3. Hey there! Would it be possible to use the library with ATTiny45 / 85 at a frequency 16,5MHz (internal oscillator). It will be a USB HID device.
    Thank you.

    ReplyDelete
    Replies
    1. Hello,

      I don't know, because the lib need a timer with 1MHz clock. If you set a prescaller of 16 using a 16.5MHz you will get a 1.5MHz clock. This addition 0,5MHz can cause errors during the measurement of the DHT22 pulses. But you can compensate for this by adjusting the thresholds in the external interrupt handler and also in the timer interrupt handler (DHT22int.c)

      Best regards,

      Miguel

      Delete
    2. Try to implement, because I can not do without interruption.
      Thank you!

      Delete
    3. Decided to switch to 16MNz, of course sacrificing the two legs of the controller, but it's easier. Not match interrupt rewrite header file.

      Delete
    4. Greetings! It did not happen to run the library, too many restrictions in the small controller. But the interest is not yet lost, trying to find a way out.

      Delete
    5. #define DHT22_PIN PIND3 // INT1
      #define DHT22_DDR DDRD
      #define DHT22_PORT PORTD

      /* User define macros. Please change this macros accordingly with the microcontroller,
      pin, the timer and also the external interrupt that you are using.

      IMPORTANT: You must configure the timer with a prescaler such that the tick
      is 1us, this means a timer clock freq. of 1MHz. With 8MHz clock, you
      can set the prescaler to divide by 8. */
      #define TIMER_SETUP_CTC TCCR0|=(1<<WGM01); // Code to configure the timer in CTC mode.
      #define TIMER_ENABLE_CTC_INTERRUPT TIMSK| = (1 << OCIE0); // Code to enable Compare Match Interrupt
      #define TIMER_OCR_REGISTER OCR0 // Timer output compare register.
      #define TIMER_COUNTER_REGISTER TCNT0 // Timer counter register
      #define TIMER_START TCCR0 = (1 << CS01); // Code to start timer with 8Mz/8=1MHz clock
      #define TIMER_STOP TCCR0& = ~(1 << CS01); // Code to stop the timer by writing 0 in prescaler bits.
      #define EXT_INTERRUPT_DISABLE GICR &= ~(1 << INT1); // Code to disable the external interrupt used.
      #define EXT_INTERRUPT_ENABLE GICR |= (1 << INT1); // Code to enable the external interrupt used.
      #define EXT_INTERRUPT_SET_RISING_EDGE MCUCR |= (1 << ISC11) | (1 << ISC10); // Code to set the interrupt to rising edge
      #define EXT_INTERRUPT_SET_FALLING_EDGE MCUCR |= (1 << ISC11)|~(1<<ISC10);// Code to set the interrupt to falling edge
      #define EXT_INTERRUPT_CLEAR_FLAG GIFR |= (1 << INTF1); // Code to clear the external interrupt flag.

      /* Interrupt vectors. Change accordingly */
      #define TIMER_CTC_VECTOR TIMER0_COMP_vect
      #define EXT_INTERRUPT_VECTOR INT1_vect

      Error 4 expected expression before '=' token F:\Desktop\AVR\AVR simulations\humidity\dht22am2303\AVRGCC1\AVRGCC1\dht22am2303\DHT22int.c 312 2 AVRGCC1
      Error 3 expected expression before '=' token F:\Desktop\AVR\AVR simulations\humidity\dht22am2303\AVRGCC1\AVRGCC1\dht22am2303\DHT22int.c 309 2 AVRGCC1
      Error 2 expected expression before '=' token F:\Desktop\AVR\AVR simulations\humidity\dht22am2303\AVRGCC1\AVRGCC1\dht22am2303\DHT22int.c 232 3 AVRGCC1
      Error 1 expected expression before '=' token F:\Desktop\AVR\AVR simulations\humidity\dht22am2303\AVRGCC1\AVRGCC1\dht22am2303\DHT22int.c 161 3 AVRGCC1

      the compiler places the cursor at the beginning of line: TIMER_STOP; // Stop timer as shownbelow. please help.
      state = DHT_ERROR_NOT_RESPOND; // Change to a error state
      TIMER_STOP; // Stop timer.
      EXT_INTERRUPT_DISABLE // Disable external interrupt
      SET_PIN_OUTPUT(DHT22_DDR,DHT22_PIN); // Set pin back to output.
      PIN_HIGH(DHT22_PORT,DHT22_PIN); // Set pin high to disable DHT22.
      bitcounter = 0; // reset bit counter.
      }
      }

      Delete
  4. i was trying to work the same for atmega 32 but got the above errors otherwise the above configuration in the adh22int.h is equivalent for atmega 32 with timer0 and int1.

    ReplyDelete
    Replies
    1. Hello Angehi,

      I think the cause of the errors are some typing errors in the #defines. For example, the line:

      TIMER_STOP TCCR0& = ~(1 << CS01);

      should be:

      TIMER_STOP TCCR0 &= ~(1 << CS01);

      Check the others (| = should be |=), no space between the equal sign and the logical operator.

      I am not 100% sure, because I am on vacation now and cannot test.

      Best regards and happy new year!

      Delete
  5. This comment has been removed by the author.

    ReplyDelete
  6. finally i have found proteaus 8.1 with dht22 sensor for simulation but the library doesnt put any result on the lcd,i suspect my code.Any one to help,here is the code.i also discovered it keeps executing one block only: else if (state == DHT_ERROR_CHECKSUM){ }
    #define F_CPU 8000000UL
    #include
    #include
    #include
    #include "lcd/lcd.h"
    #include "dht22am2303/DHT22int.h"


    int main(void)
    {

    DHT22_STATE_t state;
    DHT22_DATA_t sensor_data;
    sei();
    DHT22_Init();



    LCDInit(LS_NONE);
    _delay_ms(800);
    LCDClear();
    while(1)
    {

    state = DHT22_StartReading();//To start a new measurement, you have to call DHT22_StartReading() again.

    state = DHT22_CheckStatus(&sensor_data);//In your MAIN LOOP, check periodically when the data is available and in case of available, do something:

    if (state == DHT_DATA_READY){
    // Do something with the data.
    LCDWriteIntXY(1,0,sensor_data.humidity_decimal,3);
    while(1)
    {

    state = DHT22_StartReading();//To start a new measurement, you have to call DHT22_StartReading() again.

    state = DHT22_CheckStatus(&sensor_data);//In your MAIN LOOP, check periodically when the data is available and in case of available, do something:

    if (state == DHT_DATA_READY){
    // Do something with the data.
    LCDWriteIntXY(1,0,sensor_data.humidity_decimal,3);
    }
    }
    }

    ReplyDelete
  7. Hi! Thanks for library =)
    I've tested it on Attiny2313 (8MHz int.osc.) and AM2302 sensors. Works fine)

    ReplyDelete
  8. This comment has been removed by the author.

    ReplyDelete
  9. what this library can be use in code vision AVR ?

    ReplyDelete