wtorek, 10 lipca 2018

[37] STM32F4 - DHT22

W tym poście chciałbym opisać sposób obsługi czujnika DHT22.

[Źródło: http://www.st.com/en/evaluation-tools/stm32f4discovery.html]

DHT22 jest to czujnik temperatury (-40 do 80 st. C) oraz wilgotności powietrza (0 - 100%RH). Do komunikacji wykorzystywany jest interfejs jednoprzewodowy.

Parametry układu:

  • Napięcie zasilania: 3,3V do 6V
  • Średni pobór prądu: 0,2 mA
  • Pomiar temperatury:
    • Zakres: -40 do 80 st. C
    • Rozdzielczość: 8 bitów (0,1 st. C)
    • Dokładność pomiarowa: +/- 0,5 st. C 
    • Czas odpowiedzi: około 2s.
  • Wilgotność:
    • Zakres: 0 do 100% RH
    • Rozdzielczość: 8 bitów (+/- 0,1 % RH)
    • Dokładność pomiarowa: +/- 2%RH
    • Czas odpowiedzi: około 2s.

Zasada działania:


http://controllerstech.com/temperature-measurement-using-dht22-in-stm32/

Cała komunikacja odbywa się na jednej linii danych. Najpierw zadawana jest sekwencja uruchamiająca pomiar a następnie czujnik przesyła odpowiednie informacje.

Na samym początku należy przesłać sygnał startu czyli zmienić stan linii z wysokiego na niski na co najmniej 1 ms. Po tej operacji należy linie postawić w stan wysoki na 20-40 us.

Następnie zmieniamy stan linii z wyjściowej na wejściową i sprawdzamy czy linia danych zostało podciągnięta przez czujnik w stan niski na przynajmniej 80 us. Po tych operacjach czujnik zacznie przesyłać dane.

Linia danych powinna zawierać podłączony rezystor podciągający (np. 10kOhm).

Komunikacja jest dosyć prosta. Za każdym razem układ przesyła 40 bitów danych. Przed wysłaniem kolejnego bitu układ podaje stan niski na 50 us. Po tym czasie podciąga pin do stanu wysokiego. W przypadku otrzymania 1 układ podciągnie linie na 70 us, dla 0 czas ten będzie wynosił 26 - 28 us. 

Wobec tego aby odczytać otrzymane dane należy poczekać aż pin zostanie ustawiony w stan wysoki, po czym odczekać np. 40us, po czym sprawdzić stan pinu. Jeśli będzie ciągle wysoki to znaczy, że przesyłana jest 1.

Cała przesłana ramka danych składa się z dwóch bajtów wilgotności, dwóch bajtów temperatury oraz jednego bajtu sumy kontrolnej.

Suma kontrolna oblicza się poprzez zsumowanie bajtów danych do pojedynczej wartości 8 bitowej i porównanie jej z przesłaną sumą kontrolną.

Program:


Opóźnienia 1 us wykonałem w oparciu o DWT opisane pod tym linkiem.

Uruchomienie licznika:

  1. uint8_t DWT_COUNTER_ENABLE(void)
  2. {
  3.   uint32_t c;
  4.   //Wlacz TRC,
  5.   //Ustawienie bitu TRCENA
  6.   //Wlacza takie bloki jak DWT, ITM, ETM, TPIU
  7.   CoreDebug->DEMCR &= ~0x01000000;
  8.   CoreDebug->DEMCR |=  0x01000000;
  9.    
  10.   //Wlacz DWT w rejestrze kontrolnym
  11.   DWT->CTRL &= ~0x00000001; //Czyszczenie
  12.   DWT->CTRL |=  0x00000001; //Ustawienie
  13.    
  14.   //Ustawienie licznika na wartosc 0
  15.   DWT->CYCCNT = 0;
  16.    
  17.   //Wartosci z CYCCNT do zmiennej c
  18.   c = DWT->CYCCNT;
  19.   //Czekanie   
  20.   __ASM volatile ("NOP"); __ASM volatile ("NOP")__ASM volatile ("NOP");
  21.    
  22.   //Zwraca roznice pomiedzy DWT->CYCCNT a ta wartoscia kilka cykli wczesniej
  23.   //Jesli wynosi ona 0 to licznik nie dziala
  24.   if((DWT->CYCCNT - c) == 0)
  25.   { return 0; }
  26.   return (DWT->CYCCNT - c);
  27. }

Odliczanie zadanej ilości mikrosekund:

  1. __STATIC_INLINE void delayTime(uint32_t us)
  2. {
  3.     volatile uint32_t base = (SystemCoreClock/1000000);
  4.     volatile uint32_t c = base*us;                  
  5.     volatile uint32_t s = DWT->CYCCNT;              
  6.     while((DWT->CYCCNT - s) < c)
  7.     {;}
  8. }

Obliczenia temperatury:

  1. float Dht22_calculateTemperature(uint8_t rawRecTemp_1, uint8_t rawRecTemp_2)
  2. {
  3.     uint16_t rawRecTemp = ((uint16_t)rawRecTemp_1 << 8) | rawRecTemp_2;
  4.  
  5.     float calculateTempVal = (float)(((uint16_t)rawRecTemp) & 0x3FFF) / 10;
  6.  
  7.     if(((uint16_t)rawRecTemp) & 0x8000)
  8.     {
  9.         calculateTempVal *= -1.0;
  10.     }
  11.  
  12.     return calculateTempVal;
  13. }

Obliczanie ciśnienia:

  1. float Dht22_calculateHumidity(uint8_t rawRecHum_1, uint8_t rawRecHum_2)
  2. {
  3.     uint16_t rawRecHum = ((uint16_t)rawRecHum_1 << 8) | rawRecHum_2;
  4.  
  5.     float calculateHumVal = (float)((int16_t)rawRecHum) / 10.0;
  6.  
  7.     return calculateHumVal;
  8. }

Funkcje odczytywania danych z czujnika:

  1. uint8_t Dht22_ReadData(uint8_t *data, uint8_t tableSize)
  2. {
  3.     for(uint8_t clearLoop = 0; clearLoop < tableSize; clearLoop++)
  4.     {
  5.         data[clearLoop] = 0;
  6.     }
  7.  
  8.     dht22_InitSensorBeforeRead();
  9.  
  10.     if(dht22_checkIfSensorReact() == 0)
  11.     {
  12.         return 0;
  13.     }
  14.  
  15.     for (uint8_t j=0; j<5; j++)
  16.     {
  17.         data[4-j]=0;
  18.  
  19.         for(uint8_t i=0; i<8; i++)
  20.         {
  21.             while(HAL_GPIO_ReadPin(GPIO_DHT_PORT, GPIO_DHT_PIN) == GPIO_PIN_RESET);
  22.  
  23.             delayTime(40);
  24.  
  25.             if(HAL_GPIO_ReadPin(GPIO_DHT_PORT, GPIO_DHT_PIN) == GPIO_PIN_SET)
  26.             {
  27.                 /* Read result after 30 microsekund, if pin is high then add 1 to result */
  28.                 data[4-j] |= (1<<(7-i));
  29.             }
  30.             else
  31.             {
  32.                 data[4-j] |= (0<<(7-i));
  33.             }
  34.             while(HAL_GPIO_ReadPin(GPIO_DHT_PORT, GPIO_DHT_PIN) == GPIO_PIN_SET);
  35.         }
  36.     }
  37.     return 1;
  38. }
  39.  
  40. static void dht22_InitSensorBeforeRead(void)
  41. {
  42.     /* Reset port */
  43.     HAL_GPIO_WritePin(GPIO_DHT_PORT, GPIO_DHT_PIN, GPIO_PIN_RESET);
  44.     HAL_GPIO_WritePin(GPIO_DHT_PORT, GPIO_DHT_PIN, GPIO_PIN_SET);
  45.  
  46.     delayTime(10000);
  47.  
  48.     HAL_GPIO_WritePin(GPIO_DHT_PORT, GPIO_DHT_PIN, GPIO_PIN_RESET);
  49.  
  50.     delayTime(600);
  51.  
  52.     HAL_GPIO_WritePin(GPIO_DHT_PORT, GPIO_DHT_PIN, GPIO_PIN_SET);
  53.  
  54.     delayTime(40);
  55.  
  56.     initialSinglePortOutput();
  57. }
  58.  
  59. static uint8_t dht22_checkIfSensorReact(void)
  60. {
  61.     //If sensor don't do anythin then error
  62.     if(HAL_GPIO_ReadPin(GPIO_DHT_PORT, GPIO_DHT_PIN) == GPIO_PIN_SET)
  63.     {
  64.         return 0;
  65.     }
  66.  
  67.     delayTime(80);
  68.  
  69.     //If sensor keeps pin low then error again
  70.     if(HAL_GPIO_ReadPin(GPIO_DHT_PORT, GPIO_DHT_PIN) == GPIO_PIN_RESET)
  71.     {
  72.         return 0;
  73.     }
  74.  
  75.     delayTime(79);
  76.  
  77.     return 1;
  78. }

Przykładowe użycie może wyglądać następująco:

  1. uint8_t rawReadedData[5];
  2. Dht22_ReadedData_T DHTReadData;
  3. Dht22_Initial();
  4.  
  5. while(1)
  6. {
  7.     if(Dht22_ReadData(rawReadedData, sizeof(rawReadedData))
  8.     {
  9.         DHTReadData.dht22_Temp = Dht22_calculateTemperature(rawReadedData[1], rawReadedData[2]);
  10.         DHTReadData.dht22_Hum = Dht22_calculateHumidity(rawReadedData[3], rawReadedData[4]);
  11.     }
  12. }

Cały kod można pobrać z dysku Google pod tym linkiem.

Bibliografia:


[1] Datasheet DHT22