湿度・温度センサ(SHT11)


データ取得の通信プロトコルは、I2C に似た 2 線式通信ですが、若干異なります。
通信開始 (TS) は I2C の START + STOP 、最後は ACK 又は NACK です。
ただし、接続リセットの方法 (DATA を H に保った状態で SCK の H/L を 9 回以上交互に 切り換える) は、このデバイス独自のものです。
また、A/D 変換が完了し測定データの準備ができると DATA ラインを L にして知らせる機能は I2C の規格にないものです。

センサ起動のための電圧スルーレートは 1V/ms を下回ってなりません。起動後、センサが スリープ状態に入るまでに約 11ms の時間が必要です。この時間が経過するまではコマンドを 送信することができません。Soft reset の場合も同様です。

コマンドは
温度測定: 00011
相対湿度測定: 00101
ステータスレジスタの読み出し: 00111
ステータスレジスタの書き込み: 00110
Soft reset : 11110

通信していないときは、自動的にアイドルモードに入ります。

温湿度測定の通信手順
@通信開始条件を送信する。 (TS)
Aアドレス "000" とコマンド "XXXXX" 計 8 ビットを送信する。
B SHT11 が応答する ACK 用の SCK を送信する。
C SHT11 はコマンドを受け取ると ACK として DATA ラインを L にする。
Dその後、温湿度の測定を開始すると同時に DATA ラインを H にする。
E CPU 側は DATA ラインが L になるのを待つ。 (測定には最大 320ms 掛かる)
F SHT11 は温湿度の測定が終了すると DATA ラインを L にする。
G CPU 側は上位 8 ビットの読み込み SCK を発行する。 データは SCK を H にしてから読み込む。
H CPU 側はデータを無事受け取った事を知らせるために DATA ラインを L にして ACK の SCK を 発行する。
I CPU 側は下位 8 ビットの読み込み SCK を発行する。 データは SCK を H にしてから読み込む。
J CRC チェックサムは必要ないので、CPU 側 DATA ラインを H にして NACK の SCK を発行する。
K通信終了

ステータスレジスタの読み出しの通信手順
@
A
B
C
I CPU 側はステータスレジスタ 8 ビットの読み込み SCK を発行する。 データは SCK を H にしてから読み込む。
J
K

ステータスレジスタの書き込みの通信手順
@
A
B
C
Aステータスレジスタの書き込みデータ 8 ビットを送信する。
B
C SHT11 はステータスレジスタの書き込みデータを受け取ると ACK として DATA ラインを L に する。
K

Soft reset の通信手順
@
A
B
C
K

上記の通信プロトコルで温湿度データを取得し、 提供されてる計算式に入れて真の温湿度データを得ます。
そのデータを RS-232C 吐き出します。その様子を結果の図に示します。


SHT11_kairo.gif

回路図


SHT11_result.jpg

結果の図


SHT11_bb.JPG

実験風景


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

プログラム
/*
 *  SHT11.c
 *  湿度センサ「SHT11」から湿度、温度を得て、
 *  得られた湿度、温度を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>
#include <avr/interrupt.h>
#define F_CPU 20.0E6                /* 20 MHz */
#include <util/delay.h>
#define SCL_OUT DDRC |= _BV(1)
#define SDA_OUT DDRC |= _BV(0)
#define SCL_IN DDRC &= ~_BV(1)
#define SDA_IN DDRC &= ~_BV(0)
#ifndef STRETCH
#define SCL_H PORTC |= _BV(1)
#else
#define SCL_H wait_scl_h()
#endif
#define SCL_H2 PORTC |= _BV(1)
#define SDA_H PORTC |= _BV(0)
#define SCL_L PORTC &= ~_BV(1)
#define SDA_L PORTC &= ~_BV(0)
#define SCL (PINC & _BV(1))
#define SDA (PINC & _BV(0))
/* 変更無し */
#define ACK 0
/* ハードに合わせて変更する (SDAのビット) */
#define NAK _BV(0)

#define round(x) ((x)>=0?(long)((x)+0.5):(long)((x)-0.5))

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);
void initTIMER(void);
void get_tim(void);

char send_T_command_1x(void);
char send_RH_command_1x(void);
short read_SOt_SOrh_data_1x(char *SOt_SOrh);
float calcu_T14_1x(short SOt);
float calcu_T12_1x(short SOt);
float calcu_RH12_1x(short SOrh, float T);
float calcu_RH8_1x(short SOrh, float T);
unsigned short read_status_1x(void);
char write_status_1x(unsigned char status);
char sreset_1x(void);

unsigned long ltime, tim;
char SOt_SOrh_flag;             /* 0:計測可, 1:温度計測中, 2:湿度計測中 */

int main(void)
{
    static unsigned long ot[1];
    static unsigned char count; /*bit0 = 0:T計測開始, 1;RH計測開始 */
    char SOt_SOrh;              /* 0:計測可, 1:温度計測中, 2:湿度計測中 */
    short SOt_SOrh_data;
    float RHfloat, Tfloat;
    short RH, T_;
    char buffer[16];

    /* ATmega328P */
    DDRB = 0b00000000;      /* XTAL2 XTAL1 SCK   MISO  MOSI  ----- ----- ----- */
    DDRC = 0b00000010;      /* xxxxx RESET ----- ----- ----- ----- SCL   SDA   */
    DDRD = 0b00000000;      /* ----- ----- ----- ----- ----- ----- TXD   RXD   */
    PORTB = 0b00111111;
    PORTC = 0b00111100;
    PORTD = 0b11111100;

    initTIMER();
    initUSART0();
    sei();

    Send0("START\r");

    /* HST11の初期設定 */
    /* センサ起動のための電圧スルーレートは 1V/ms を */
    /* 下回ってはなりません */
    delay_ms(20);       /* 電源投入後 11ms 以上待つ */
    sreset_1x();        /* 念のため Soft reset をかける */
    delay_ms(20);       /* Soft reset 後 11ms 以上待つ */
    Tfloat = 25.0;

    get_tim();
    ot[0] = tim;
    while(1){
        get_tim();
        if((unsigned long)(tim - ot[0]) >= 1000){
            ot[0] = tim;
            if((count & 0x01) == 0)
                send_T_command_1x();
            else
                send_RH_command_1x();

            count++;
        }

        if((SOt_SOrh_data = read_SOt_SOrh_data_1x(&SOt_SOrh)) != -1){
            if(SOt_SOrh == 1){
                Tfloat = calcu_T14_1x(SOt_SOrh_data);
                T_ = (short)round(Tfloat * 10);
                sprintf(buffer, "T = %d.%d ℃\r", T_ / 10, abs(T_ % 10));
                Send0(buffer);
            }
            else if(SOt_SOrh == 2){
                RHfloat = calcu_RH12_1x(SOt_SOrh_data, Tfloat);
                RH = (short)round(RHfloat);
                if(RH > 99)
                    RH = 99;
                sprintf(buffer, "RH = %d %%\r", RH);
                Send0(buffer);
            }
        }
    }
}

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++;
    }
}

void initTIMER(void)
{
    TCCR1A = 0b00000000;    /* CTC動作 */
    TCCR1B = 0b00001010;    /* clk / 8 */
    OCR1A = 2500 - 1;       /* (8 / 20MHz) * 2500 = 1000uS */
    TIMSK1 = 0b00000010;    /* 比較A割り込み要求有効 */
}

ISR(TIMER1_COMPA_vect)
{
    ++ltime;
}

void get_tim(void)
{
    cli();
    tim = ltime;
    sei();
}

/*
 * Connection Reset
 */
void creset_1x(void)
{
    unsigned char i;

    SCL_OUT; SDA_OUT;

    SDA_H;
    for(i=0; i<10; i++){
        SCL_H;
        delay_us(1);        /* THIGH = 600ns */
        SCL_L;
        delay_us(1);        /* TLOW = 1300ns */
        delay_us(1);
    }   

    SCL_OUT; SDA_IN;
}

/*
 * Transmission Start
 */
void ts_1x(void)
{
    SCL_OUT; SDA_OUT;

    SCL_L;
    SDA_H;
    SCL_H;
    delay_us(1);            /* TSU:STA = 600ns */

    SDA_L;
    delay_us(1);            /* THD:STA = 600ns */
    SCL_L;

    SCL_L;
    SDA_L;

    SCL_H;
    delay_us(1);            /* TSU:STO =  600ns */
    SDA_H;

    SCL_L;

    SCL_OUT; SDA_IN;
}

/*
 * 戻り値 ACK:0, NAK:SDA
 */
unsigned char writebyte(unsigned char data)
{
    unsigned char i;

    SCL_OUT; SDA_OUT;

    for(i=0; i<8; i++){
        if(data & 0x80)
            SDA_H;
        else
            SDA_L;

        SCL_H;
        delay_us(1);        /* THIGH = 600ns */
        SCL_L;
        delay_us(1);        /* TLOW = 1300ns */
        delay_us(1);

        data <<= 1;
    }   

    SCL_OUT; SDA_IN;

    SCL_H;
    delay_us(1);            /* THIGH = 600ns */
    i = SDA;
    SCL_L;
    delay_us(1);            /* TLOW = 1300ns */
    delay_us(1);

    return i;
}

/*
 * 戻り値 読み込みデータ(8ビット)
 */
unsigned char readbyte(void)
{
    unsigned char i, data;

    data = 0;
    for(i=0; i<8; i++){
        data <<= 1;

        SCL_H;
        delay_us(1);        /* THIGH = 600ns */
        if(SDA)
            data++;
        SCL_L;
        delay_us(1);        /* TLOW = 1300ns */
        delay_us(1);
    }

    return data;
}

void ack(void)
{
    SCL_OUT; SDA_OUT;

    SDA_L;
    SCL_H;
    delay_us(1);            /* THIGH = 600ns */
    SCL_L;
    delay_us(1);            /* TLOW = 1300ns */
    delay_us(1);

    SCL_OUT; SDA_IN;
}

void nak(void)
{
    SCL_OUT; SDA_OUT;

    SDA_H;
    SCL_H;
    delay_us(1);            /* THIGH = 600ns */
    SCL_L;
    delay_us(1);            /* TLOW = 1300ns */
    delay_us(1);

    SCL_OUT; SDA_IN;
}

/*
 * 戻り値 成功:0, 失敗:-1
 */
char send_T_command_1x(void)
{
    unsigned char ret;

    creset_1x();
    ts_1x();
    ret = writebyte(0x03);
    if(ret == NAK)
        return -1;

    SOt_SOrh_flag = 1;
    return 0;
}

/*
 * 戻り値 成功:0, 失敗:-1
 */
char send_RH_command_1x(void)
{
    unsigned char ret;

    creset_1x();
    ts_1x();
    ret = writebyte(0x05);
    if(ret == NAK)
        return -1;

    SOt_SOrh_flag = 2;
    return 0;
}

/*
 * 成功:読み取りデータ, 失敗:-1
 */
short read_SOt_SOrh_data_1x(char *SOt_SOrh)
{
    short SOt_SOrh_data;

    *SOt_SOrh = SOt_SOrh_flag;

    if(SDA != 0 || SOt_SOrh_flag == 0)
        return -1;
    else{
        SOt_SOrh_data = (short)readbyte();
        SOt_SOrh_data <<= 8;
        ack();
        SOt_SOrh_data |= (short)readbyte();
        nak();

        SOt_SOrh_flag = 0;
        return SOt_SOrh_data;
    }
}

float calcu_T14_1x(short SOt)
{
    float T;

    T = -40.1 + 0.01 * SOt;

    return T;
}

float calcu_T12_1x(short SOt)
{
    float T;

    T = -40.1 + 0.04 * SOt;

    return T;
}

float calcu_RH12_1x(short SOrh, float T)
{
    float RHlinear, RHtrue;

    RHlinear = -2.0468 + 0.0367 * SOrh - 1.5955 * 0.000001 * SOrh * SOrh;
    RHtrue = (T - 25) * (0.01 + 0.00008 * SOrh) + RHlinear;
    if(RHtrue < 0.0)
        RHtrue = 0.0;
    if(RHtrue > 100.0)
        RHtrue = 100.0;

    return RHtrue;
}

float calcu_RH8_1x(short SOrh, float T)
{
    float RHlinear, RHtrue;

    RHlinear = -2.0468 + 0.5872 * SOrh - 4.0845 * 0.0001 * SOrh * SOrh;
    RHtrue = (T - 25) * (0.01 + 0.00128 * SOrh) + RHlinear;
    if(RHtrue < 0.0)
        RHtrue = 0.0;
    if(RHtrue > 100.0)
        RHtrue = 100.0;

    return RHtrue;
}

/*
 * 戻り値 成功:上位バイト0x00, 下位バイトstatus
 *        失敗:0xff00
 */
unsigned short read_status_1x(void)
{
    unsigned char ret;
    unsigned short status;

    ts_1x();
    ret = writebyte(0x07);
    if(ret == NAK)
        return 0xff00;
    status = (unsigned short)readbyte();
    nak();

    return status;
}

/*
 * 戻り値 成功:0, 失敗:-1
 */
char write_status_1x(unsigned char status)
{
    unsigned char ret;

    ts_1x();
    ret = writebyte(0x06);
    if(ret == NAK)
        return -1;
    ret = writebyte(status);
    if(ret == NAK)
        return -1;

    return 0;
}
/*
 * soft reset
 *
 * 戻り値 成功:0, 失敗:-1
 */
char sreset_1x(void)
{
    unsigned char ret;

    creset_1x();
    ts_1x();
    ret = writebyte(0x1e);
    if(ret == NAK)
        return -1;

    return 0;
}


真空管
戻る