データ取得の通信プロトコルは、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 吐き出します。その様子を結果の図に示します。 |
回路図
結果の図
実験風景
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; } |