Mikrokontrolery - Jak zacząć?

... czyli zbiór praktycznej wiedzy dot. mikrokontrolerów.

środa, 9 marca 2011

DHT11 - Czujnik wilgotności i temperatury


Autor: Michał Smalera
Redakcja: Dondu

DHT11 - Czujnik wilgotności temperatury - Przyklad AVR ATmega.
Witam ponownie, zachęcony sukcesem mojego pierwszego artykułu o akcelerometrze LSI302 postanowiłem przybliżyć czytelnikom tani czujnik wilgotności i temperatury DHT11 (ang Humidity & Temperature Sensor).

DHT11 to czujnik temperatury i wilgotności względnej. Dostępny w obudowie z 4 wyjściami (single row) i może być zasilany napięciem od 3,5V do 5,5V. Może odczytywać temperaturę w zakresie 0-50 st.C z dokładnością +/- 2 st.C oraz względną wilgotność w zakresie 20-95% z dokładnością +/- 5%.


DHT11 - Czujnik wilgotności temperatury - Parametry techniczne.


Dokumentacja: DHT11 (kopia)

Ponieważ sensor komunikuje się za pomocą "modyfikowanego" protokołu 1-wire, dlatego nie jest możliwa jego współpraca z innymi, urządzeniami 1-wire, które korzystają z "prawdziwego" 1-wire.


DHT11 - Wygląd czujnika
DHT11 - Wygląd czujnika

Poniższy rysunek obrazuje przebieg impulsów wymagany do rozpoczęcia transmisji. Master (mikroprocesor) inicjuje przesył danych podając stan niski na linię DATA na co najmniej 18ms, a następnie stan wysoki na 20-40µs po czym zwalnia linię. Teraz sensor odpowiada na sygnał z mikroprocesora przez wymuszenie na linii stanu niskiego na 80µs, po czym stanu wysokiego na następne 80µs. To jest tak zwany presence pulse (sygnał obecności).


Presence Pulse


Należy pamiętać, żeby zmienić kierunek PIN-u mikroprocesora sterującego linią DATA zaraz po wysłaniu sygnału startu.

Po odebraniu sygnału obecności od sensora nasz mikroprocesor musi być gotów na odbieranie danych z DHT11.

Sensor wyśle 40 bitów danych (5 bajtów) w jednym ciągu. Sensor wysyła dane z najważniejszym bitem (MSB - Most Significant Bit) na początku.

40-bitowy zestaw danych ma następującą strukturę (w nawiasie nazwy angielskie):

Dane (40-bitów) = Całkowity Bajt Wilgotności (Integer Byte of RH) +
Dziesiętny Bajt Wilgotności (Decimal Byte of RH) + 
Całkowity Bajt Temperatury (Integer Byte of Temp) + 
Dziesiętny Bajt Temperatury (Decimal Byte of Temp) + 
Bajt Sumy Kontrolnej (Chacksum Byte)


Dla czujnika DHT11 dziesiętne bajty wilgotności oraz temperatury są zawsze równe zero. Dlatego pierwszy i trzeci bajt transmisji dają numeryczne wartości zmierzonej wilgotności względnej oraz temperatury. Bajt sumy kontrolnej używany jest do sprawdzenia, czy dane zostały wysłane i odebrane prawidłowo.

Jeśli wszystkie pięć bajtów zostały przesłane z sukcesem, wtedy bajt sumy kontrolnej musi być równy ostatnim 8 bitom sumy pierwszych 4 bajtów, czyli: 


Suma Kontrolna = 8 ostanich bitów (Integer Byte of RH + Decimal Byte of RH + Integer Byte of Temp. + Decimal Byte of Temp.)


Teraz ważna sprawa. Kiedy DHT wysyła 1 a kiedy 0? 

Jeśli jest wysyłany bit danych, sensor wymusza stan niski na linii DATA na 50µs. Następnie wymusza stan wysoki na 26-28µs jeśli wysyła "0", lub na 70µs jeśli wysyła "1". Na rysunku poniżej pokazano szerokości impulsów w przypadku wysyłania 0 i 1.


Przebiegi dla zera i jedynki

Początek transmisji razem z inicjacją transmisji i dwoma pierwszymi bitami danych.


Przebieg czasowy

Po wysłaniu ostatniego bitu, sensor wymusza stan niski na 50µs a następnie zwalnia linię.


Sensor DHT11 wymaga podłączenia rezystora pomiędzy linią DATA a Vcc, w celu wymuszenia stanu wysokiego, kiedy linia jest "wolna" (żadne z podłączonych urządzeń nie nadaje).


Jest to tak zwany pull-up resistor. Po wysłaniu paczki danych i zwolnieniu linii DHT11 przechodzi do stanu niskiego poboru energii aż do momentu pojawienia się sygnału inicjującego transmisję (z mikroprocesora).

Poniżej schemat połączeń:


Podłączenie do mikrokontrolera


Software

Ze względu na wymagane przebiegi czasowe najlepszym wyjściem będzie użycie przerwań. Użyjemy jednego aby określić czas (szerokość) impulsów. W zależności od tej szerokości będziemy w stanie określić, czy DHT wysłał 1 czy 0.

Będziemy obserwować stan na linii DATA. Jeśli zauważymy przejście ze stanu niski na wysoki, czyścimy daną time i zaczynamy zliczanie. Kiedy zauważymy, że DATA jest znowu w stanie niskim zatrzymujemy zliczanie. Wartość zmiennej timer mówi nam o szerokości impulsu. Użyjemy wartości 40µs, aby zdecydować, czy mikroprocesor odebrał 1 czy 0.

Jeśli zmienna timer jest większa niż 40, odebraliśmy 1, jeśli mniejsza - 0.


main.c
#define LED_DIR_SET_OUT DDRC|=(1<<PC0)
#define LED_ON PORTC|=(1<<PC0)
#define LED_OFF PORTC&=~(1<<PC0)
#define LED_TOGGLE PORTC^=(1<<PC0)
#define DHT_PORT PORTD
#define DHT_PIN PD2
#define DHT_DIR DDRD
#define DHT_PIN_STATE PIND

#include <avr/io.h>
#include <util/delay.h>

void init_dht(void);
uint8_t fetchData(uint8_t* arr);

int main(void) 
{
 uint8_t data[4];

 init_dht();

 for(;;) {
  LED_OFF;
  _delay_ms(500);
  LED_ON;
  if (fetchData(data)) {

   //tutaj dostępne są dane w postaci tablicy data[]
  }
 }
 return 0;
}

void init_dht(void) //done
{
 /* Set LED as output */
 LED_DIR_SET_OUT;

 /* Zgodnie z datasheet DHT11 potrzebuje jakieś 1 do 2 sekund
  * po włączeniu zasilania dla osiągnięcia stanu stabilnego
  */
 _delay_ms(2000);
 LED_ON;
}

uint8_t fetchData(uint8_t* arr)
{
 uint8_t data[5];
 uint8_t cnt, check;
 int8_t i, j;

 /******************* Inicjalizacja  *******************/

 /* Ustaw pin danych jako wyjście */
 DHT_DIR |= (1 << DHT_PIN);

 /* Najpierw potrzebujemy opóźnienia 20 milisekund, więc preskaler clk/1024 */
 TCCR0 = 0; //wyzerowanie TCCR0
 TCCR0 |= (1 << CS02) | (1 << CS00); //prescaler 1024
 TCNT0 = 0; //licz do 215 dla 20ms (przy clk=11059200Hz)

 /* stan niski na 20 ms */
 DHT_PORT &= ~(1 << DHT_PIN);

 /* czekaj około 20 ms */
 while (TCNT0 < 217)
  ;

 /* Teraz ustaw Timer0 z preskalerem clk/8 */
 TCCR0=0;
 TCCR0 |= (1<<CS01); // clk/8
 TCNT0 = 0;

 /* Stan wysoki */
 DHT_PORT ^= (1 << DHT_PIN);

 /* Ustaw pin danych jako wejście */
 DHT_DIR &= ~(1 << DHT_PIN);

 /* Czekaj na odpowiedź z sensora 20-40µs */
 while (DHT_PIN_STATE & (1 << DHT_PIN)) {
  if (TCNT0 >= 82)
   return 0;    // 60us --> TCNT0 musi liczyć do 82 z 11059200CPU_CLK
 }
 /************************* Rozpoczęcie transmisji *************************/

 TCNT0 = 0;

 /* Czekamy aż sensor odpowie. Najpierw stan niski ~80µs */
 while (!(DHT_PIN_STATE & (1 << DHT_PIN))) {
  if (TCNT0 >= 137)
   return 0;    //jeśli więcej niż 100us, fail
 }

 TCNT0 = 0;

 /* Teraz stan wysoki: ~80µs */
 while (DHT_PIN_STATE & (1 << DHT_PIN)) {
  if (TCNT0 >= 137)
   return 0; //if more than 100us - fail
 }

 /********************* Przesył danych **********************/

 for (i = 0; i < 5; ++i) {
  for (j = 7; j >= 0; --j) {
   TCNT0 = 0;

   /* Najpierw 50µs stanu niskiego */
   while (!(DHT_PIN_STATE & (1 << DHT_PIN))) {
    if (TCNT0 >= 96)
     return 0;            //70us
   }

   TCNT0 = 0;

   /* Teraz dane. 26 to 28µs stanu wysokiego wskazuje
    zero, a około 70µs to wysłana jedynka */

   while (DHT_PIN_STATE & (1 << DHT_PIN)) {
    if (TCNT0 >= 123)
     return 0;            //90us
   }

   /* Żeby nie psuć zmiennej TCNT0, skopiujemy ją sobie */
   cnt = TCNT0;

   if (cnt >= 27 && cnt <= 47) {       //pomiędzy 20 i 35us
    data[i] &= ~(1 << j); //ustaw zero
   }
   else if (cnt >= 82 && cnt <= 110) { //pomiędzy 60 and 80us
    data[i] |= (1 << j); //jedynka
   }

   else
    return 0;
  }
 }

 /********************* Zakończenie komunikacji, CRC *********************/

 check = (data[0] + data[1] + data[2] + data[3]) & 0xFF;

 if (check != data[4])
  return 0;

 for (i = 0; i < 4; ++i) {
  arr[i] = data[i];
 }

 return 1;
}
Do pobrania: main.c (kopia)


Wynik

Dokładność czujnika DHT11 nie jest zbyt wysoka, jednak zastosowanie takiego czujnika jest łatwe i stosunkowo tanie. Dzięki dostępności dwóch danych (wilgotność i temperatura) istnieje możliwość łatwego zastosowania do obliczenia np. punktu rosy.

Powodzenia w pomiarach!
:-)

Oceń artykuł.
Wasze opinie są dla nas ważne, gdyż pozwalają dopracować poszczególne artykuły.
Pozdrawiamy, Autorzy
Ten artykuł oceniam na:

7 komentarzy:

  1. Super artykuł, oby więcej takich. Krótko i na temat, do tego przykład kodu... Istne MIODZIO ;)

    OdpowiedzUsuń
  2. W kontekście utrzymywania dobrej atmosfery w pokoju to jeszcze mi się marzy czujnik dwutlenu węgla i tlenu, ale obawiam się, że mało tanich chińczyków tego typu się znajdzie. Jakbyście jednak znaleźli to pochwalcie się :)

    OdpowiedzUsuń
    Odpowiedzi
    1. Może ktoś z czytelników ma dostęp?

      Usuń
    2. Pa katastrofie elektrowni w Japonii na różnorodnych stronach traktujących o elektronice pojawiło się całe mnóstwo detektorów promieniowania radioaktywnego. Efekt dobry, tylko nieco spóźniony (no, chyba że coś podobnego wydarzy się znowu - oby nie).
      Chciałbym zbudować urządzenie do detekcji innego "czynnika" zabijającego, a stanowiącego realne zagrożenie - dwutlenku siarki. Wspominam o tym ze względu na sporą aktywność wulkaniczną na Islandii. Dla zainteresowanych: polecam zapoznanie się z danymi dotyczącymi erupcji wulkanu Laki w 1783r.
      Niestety, detektory SO2 są małodostępne, no i drogie :(
      Niemniej z chęcią napiszę jakiś artykuł dot. czujnika CO2 lub tlenu, niech mi tylko coś w łapki wpadnie.

      Usuń
  3. Fajny artykuł, krótko, treściwie :-)

    OdpowiedzUsuń
  4. Witaj, czy takie odebranie danych jest poprawne, ponieważ nie mogę odczytać żadnych poprawnych danych:
    if (fetchData(data)) {
    zm=data[2];
    //tutaj dostępne są dane w postaci tablicy data[]
    }
    uart_putint(zm,10);

    OdpowiedzUsuń
  5. Super tego właśnie szukałem. Wielkie dzięki!!

    OdpowiedzUsuń

Działy
Działy dodatkowe
Inne
O blogu




Dzisiaj
--> za darmo!!! <--
1. USBasp
2. microBOARD M8


Napisz artykuł
--> i wygraj nagrodę. <--


Co nowego na blogu?
Śledź naszego Facebook-a



Co nowego na blogu?
Śledź nas na Google+

/* 20140911 Wyłączona prawa kolumna */
  • 00

    dni

  • 00

    godzin

  • :
  • 00

    minut

  • :
  • 00

    sekund

Nie czekaj do ostatniego dnia!
Jakość opisu projektu także jest istotna (pkt 9.2 regulaminu).

Sponsorzy:

Zapamiętaj ten artykuł w moim prywatnym spisie treści.