湿度・温度センサ(DHT22 (AM2302))


秋月で950円買い、実験してみました。

データは、マニュアル RHT22.pdf に書いてあるように、 パルスの幅より得ます。
@スタート信号の出力 (1mS以上Lを出力し、ポートを入力にしハイインピ (プルアップでH) の状態でAを待つ)
A応答の入力 (80μSのLを入力後、ハイインピ (プルアップでH) の状態でBを待つ)
B40回のデータの入力 (Aと同様にパルスを入力する。ただし、パルスの幅で0か1を意味する)
0は50μSのL、26〜28μSのH (プログラムでは立下りエッジ間が100μS以下を0と見る)
1は50μSのL、70μSのH (プログラムでは立下りエッジ間が100μS以上を1と見る)

プログラムの注意点は、データの取得中は割り込みなどで、パルス幅の取得が遅れてはなりません。


DHT22_kairo.gif

回路図


DHT22_result.jpg

結果の図


DHT22_bb.JPG

実験風景


How to Compile (コンパイルのしかた)

システムクロック:20MHzのプログラム
/*
 *  DHT22.c
 *  湿度センサ「DHT22」から湿度、温度を得て、
 *  得られた湿度、温度をRS-232C(115200 8-N-1)で出力する。
 *  ATmega328P
 *
 *
 *  Fuses(システムクロック:20MHz)
 *
 *              水晶
 *  EXTENDED    0xFD
 *  HIGH        0xD9
 *  LOW         0xF7
 */

#include <stdio.h>
#include <stdlib.h>                 /* abs() */
#include <avr/io.h>
#define F_CPU 20.0E6                /* 20 MHz */
#include <util/delay.h>

///void delay_us(unsigned short t){while(t--) _delay_us(1);}
void delay_ms(unsigned short t){while(t--) _delay_ms(1);}
void initUSART0(void);
void Send0(char *str);

int main(void)
{
    int i;
    static unsigned char pri_data, current_data;
    unsigned char change_data;
    unsigned short last_tcnt1;
    static unsigned char data[5];
    static char buffer[32];
    unsigned char sum;
    short RH, T_;

    /* ATmega328系 */
    DDRB = 0b00000000;      /* XTAL2 XTAL1 SCK   MISO  MOSI  ----- ----- ----- */
    DDRC = 0b00000000;      /* xxxxx RESET ----- ----- ----- ----- ----- DATA  */
    DDRD = 0b00000000;      /* ----- ----- ----- ----- ----- ----- TXD   RXD   */
    PORTB = 0b00111111;
    PORTC = 0b00111110;
    PORTD = 0b11111100;

    initUSART0();
    TCCR1A = 0b00000000;        /* ノーマル */
    TCCR1B = 0b00000010;        /* 20MHz / 8 = 2.5MHz (400n) */

    Send0("START\r");

    /* DHT22が準備するのを待つ */
    delay_ms(2000);

    while(1){
        /* 立下りエッジの検出回数リセット */
        i = 0;

        /* スタート信号を送信 */
        PORTC &= ~_BV(0);
        DDRC |= _BV(0);
        delay_ms(2);
        PORTC |= _BV(0);
        DDRC &= ~_BV(0);

        /* DHT22の出力波形より温湿度データを得る */
        while(1){
            /* 立下りエッジの検出 (42回) */
            pri_data = current_data;
            current_data = PINC & _BV(0);
            change_data = pri_data ^ current_data;
            if(change_data != 0 && current_data == 0){

                /* 立下りエッジ間の時間を得る */
                last_tcnt1 = TCNT1;
                TCNT1 = 0;

                /* 立下りエッジ間の時間の長短によりビット値を得る  */
                /* 100us / 0.4us = 250 */
                switch(i){
                case 0:
                case 1:
                    i++;
                    break;
                case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:
                    data[0] <<= 1;
                    if(last_tcnt1 > 250)
                        data[0] += 1;
                    i++;
                    break;
                case 10:case 11:case 12:case 13:case 14:case 15:case 16:case 17:
                    data[1] <<= 1;
                    if(last_tcnt1 > 250)
                        data[1] += 1;
                    i++;
                    break;
                case 18:case 19:case 20:case 21:case 22:case 23:case 24:case 25:
                    data[2] <<= 1;
                    if(last_tcnt1 > 250)
                        data[2] += 1;
                    i++;
                    break;
                case 26:case 27:case 28:case 29:case 30:case 31:case 32:case 33:
                    data[3] <<= 1;
                    if(last_tcnt1 > 250)
                        data[3] += 1;
                    i++;
                    break;
                case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 41:
                    data[4] <<= 1;
                    if(last_tcnt1 > 250)
                        data[4] += 1;
                    i++;
                    break;
                }
            }
            if(i == 42)
             break;
        }

        /* チェックサムの確認 */
        sum = (unsigned char)(data[0] + data[1] + data[2] + data[3]);
        if(sum == data[4]){

            /* 計算 */
            RH = data[0] * 256 + data[1];
            T_ = (data[2] & 0x7f) * 256 + data[3];
            if((data[2] & 0x80) != 0)
                T_ = -T_;

            /* 表示 */
            sprintf(buffer, "%d.%d %%    %d.%d ℃\r", RH / 10, RH % 10, T_ / 10, abs(T_ % 10));
            Send0(buffer);
        }

        /* サンプリング周期 */
        delay_ms(2000);
    }
}

void initUSART0(void)
{
    UBRR0 = 21;             /* U2X0 = 1, 115200ボー/20MHzクロック */
    UCSR0A = 0b00000010;    /* U2X0 = 1 */
    UCSR0B = 0b00001000;    /* 送信イネーブル */
    UCSR0C = 0b00000110;    /* 8ビット, パリティなし, ストップ・ビット1 */
}

/*
 *  1データ送信
 */
void Tx0(char data)
{
    while((UCSR0A & _BV(5)) == 0)
        ;
    UDR0 = (unsigned char)data;
}

/*
 *  文字列を送信
 */
void Send0(char *str)
{
    while(*str != '\0'){
        Tx0(*str);
        str++;
    }
}


How to Compile (コンパイルのしかた)

システムクロック:内臓8MHzのプログラム
/*
 *  DHT22_8MHz.c
 *  システムクロック:内臓8MHz
 *  湿度センサ「DHT22」から湿度、温度を得て、
 *  得られた湿度、温度をRS-232C(9600 8-N-1)で出力する。
 *  ATmega328P
 *
 *
 *  Fuses(システムクロック:8MHz)
 *
 *              Int. RC Osc
 *  EXTENDED    0xFD
 *  HIGH        0xD9
 *  LOW         0xE2
 */

#include <stdio.h>
#include <stdlib.h>                 /* abs() */
#include <avr/io.h>
///#define F_CPU 20.0E6                 /* 20 MHz */
#define F_CPU 8.0E6                 /* 8 MHz */
#include <util/delay.h>

///void delay_us(unsigned short t){while(t--) _delay_us(1);}
void delay_ms(unsigned short t){while(t--) _delay_ms(1);}
void initUSART0(void);
void Send0(char *str);

int main(void)
{
    int i;
    static unsigned char pri_data, current_data;
    unsigned char change_data;
    unsigned short last_tcnt1;
    static unsigned char data[5];
    static char buffer[32];
    unsigned char sum;
    short RH, T_;

    /* ATmega328系 */
    DDRB = 0b00000000;      /* XTAL2 XTAL1 SCK   MISO  MOSI  ----- ----- ----- */
    DDRC = 0b00000000;      /* xxxxx RESET ----- ----- ----- ----- ----- DATA  */
    DDRD = 0b00000000;      /* ----- ----- ----- ----- ----- ----- TXD   RXD   */
    PORTB = 0b00111111;
    PORTC = 0b00111110;
    PORTD = 0b11111100;

    initUSART0();
    TCCR1A = 0b00000000;        /* ノーマル */
/// TCCR1B = 0b00000010;        /* 20MHz / 8 = 2.5MHz (400n) */
    TCCR1B = 0b00000001;        /* 8MHz / 1 = 8MHz (125n) */

    Send0("START\r");

    /* DHT22が準備するのを待つ */
    delay_ms(2000);

    while(1){
        /* 立下りエッジの検出回数リセット */
        i = 0;

        /* スタート信号を送信 */
        PORTC &= ~_BV(0);
        DDRC |= _BV(0);
        delay_ms(2);
        PORTC |= _BV(0);
        DDRC &= ~_BV(0);

        /* DHT22の出力波形より温湿度データを得る */
        while(1){
            /* 立下りエッジの検出 (42回) */
            pri_data = current_data;
            current_data = PINC & _BV(0);
            change_data = pri_data ^ current_data;
            if(change_data != 0 && current_data == 0){

                /* 立下りエッジ間の時間を得る */
                last_tcnt1 = TCNT1;
                TCNT1 = 0;

                /* 立下りエッジ間の時間の長短によりビット値を得る  */
                /* 100us / 0.4us = 250 */
                /* 100us / 0.125us = 800 */
                switch(i){
                case 0:
                case 1:
                    i++;
                    break;
                case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:
                    data[0] <<= 1;
///                 if(last_tcnt1 > 250)
                    if(last_tcnt1 > 800)
                        data[0] += 1;
                    i++;
                    break;
                case 10:case 11:case 12:case 13:case 14:case 15:case 16:case 17:
                    data[1] <<= 1;
///                 if(last_tcnt1 > 250)
                    if(last_tcnt1 > 800)
                        data[1] += 1;
                    i++;
                    break;
                case 18:case 19:case 20:case 21:case 22:case 23:case 24:case 25:
                    data[2] <<= 1;
///                 if(last_tcnt1 > 250)
                    if(last_tcnt1 > 800)
                        data[2] += 1;
                    i++;
                    break;
                case 26:case 27:case 28:case 29:case 30:case 31:case 32:case 33:
                    data[3] <<= 1;
///                 if(last_tcnt1 > 250)
                    if(last_tcnt1 > 800)
                        data[3] += 1;
                    i++;
                    break;
                case 34:case 35:case 36:case 37:case 38:case 39:case 40:case 41:
                    data[4] <<= 1;
///                 if(last_tcnt1 > 250)
                    if(last_tcnt1 > 800)
                        data[4] += 1;
                    i++;
                    break;
                }
            }
            if(i == 42)
             break;
        }

        /* チェックサムの確認 */
        sum = (unsigned char)(data[0] + data[1] + data[2] + data[3]);
        if(sum == data[4]){

            /* 計算 */
            RH = data[0] * 256 + data[1];
            T_ = (data[2] & 0x7f) * 256 + data[3];
            if((data[2] & 0x80) != 0)
                T_ = -T_;

            /* 表示 */
            sprintf(buffer, "%d.%d %%    %d.%d ℃\r", RH / 10, RH % 10, T_ / 10, abs(T_ % 10));
            Send0(buffer);
        }

        /* サンプリング周期 */
        delay_ms(2000);
    }
}

void initUSART0(void)
{
    UBRR0 = 103;            /* U2X0 = 1, 9600ボー/8MHzクロック */
    UCSR0A = 0b00000010;    /* U2X0 = 1 */
    UCSR0B = 0b00001000;    /* 送信イネーブル */
    UCSR0C = 0b00000110;    /* 8ビット, パリティなし, ストップ・ビット1 */
}

/*
 *  1データ送信
 */
void Tx0(char data)
{
    while((UCSR0A & _BV(5)) == 0)
        ;
    UDR0 = (unsigned char)data;
}

/*
 *  文字列を送信
 */
void Send0(char *str)
{
    while(*str != '\0'){
        Tx0(*str);
        str++;
    }
}


真空管
戻る