湿度・温度センサ(DHT22 (AM2302))は湿度・温度センサより
湿度・温度データ(5バイト)を取り、表示データに加工してRS-232Cで出力してます。
この湿度・温度データ(5バイト)を「I2Cのスレーブ」で出力してみました。。 CPU(ATmega328P)のシステムクロックは内臓の8MHzを使用しました。(工場校正:8MHz±10%) 「I2Cのスレーブ」はCPU(ATmega328P)の内臓のTWIを使用しました。(従って、SCL、SDAのポートは固定) CPUが「I2Cのスレーブ」を行う場合、CPUの応答が(EEPROM、RTCなどに比べて)遅いのでクロックストレッチ動作が 採用されてます。(証拠はSCLのプルアップ) クロックストレッチとは、スレーブ側が送信の時、処理する間、強制的にSCLをLowとすることでマスターの クロックを待たせることです。(従って、SCLも入出力となる) マスターを汎用ポートで行う場合、クロックストレッチの処理を乗っけて下さい。 ディバイスアドレスは0x57です。(左に1ビットシフトしていません) シーケンシャル読み出し(読み出しデータ:00000010 10001100 00000001 01011111 11101110) +-+-------+-+=+--------+=+-+-------+-+=+========+-+========+-+ +========+-+-+ |S|1010111|0|0|00000000|0|S|1010111|1|0|00000010|0|10001100|0|...|11101110|1|P| +-+-------+-+=+--------+=+-+-------+-+=+========+-+========+-+ +========+-+-+ DA W A ADDRESS A DA R A DATA1 A DATA2 A DATA5 N S:START CONDITION P:STOP CONDITION R:READ W:WRITE A:ACK N:NAK DA:DEVICE ADDRESS ADDRESS:DATA ADDRESS BYTE DATA1〜5:DATA BYTE -:WRITE =:READ |
回路図(スレーブ)
回路図(マスター)
結果の図
How to Compile (コンパイルのしかた) スレーブプログラム /* * DHT22_I2C.c * システムクロック:8MHz * 湿度センサ「DHT22」から湿度、温度を得て、 * 得られた湿度、温度をI2C(スレーブアドレス0x57、 * データ5バイト)で出力する。 * ATmega328P * * * Fuses(システムクロック:8MHz) * * Int. RC Osc * EXTENDED 0xFD * HIGH 0xD9 * LOW 0xE2 */ #include <avr/io.h> #include <avr/interrupt.h> #define F_CPU 8.0E6 /* 8 MHz */ #include <util/delay.h> #define SLAVE_CPU_ADDRESS 0xae #define I2C_BufferSize 5 ///void delay_us(unsigned short t){while(t--) _delay_us(1);} void delay_ms(unsigned short t){while(t--) _delay_ms(1);} static void initI2C_SLAVE_CPU(void); unsigned char I2C_Buffer[I2C_BufferSize]; 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]; /* ATmega328系 */ DDRB = 0b00000000; /* XTAL2 XTAL1 SCK MISO MOSI ----- ----- ----- */ DDRC = 0b00000000; /* xxxxx RESET ----- ----- ----- ----- ----- DATA */ DDRD = 0b00000000; /* ----- ----- ----- ----- ----- ----- TXD RXD */ PORTB = 0b00111111; PORTC = 0b00111110; PORTD = 0b11111111; initI2C_SLAVE_CPU(); TCCR1A = 0b00000000; /* ノーマル */ TCCR1B = 0b00000001; /* 8MHz / 1 = 8MHz (125n) */ sei(); /* 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.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 > 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 > 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 > 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 > 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 > 800) data[4] += 1; i++; break; } } if(i == 42) break; } cli(); for(i=0; i<5; i++) I2C_Buffer[i] = data[i]; sei(); /* サンプリング周期 */ delay_ms(2000); } } /* * スレーブCPUの初期化 */ void initI2C_SLAVE_CPU(void) { unsigned char device_address; device_address = SLAVE_CPU_ADDRESS; device_address &= 0xfe; /// device_address |= 1; /* ジェネラル・コール・アドレス許可 */ TWAR = device_address; TWCR = (_BV(TWEA) | _BV(TWEN) | _BV(TWIE)); /* スレーブACK応答許可、割り込み許可 */ } ISR(TWI_vect) { static int I2C_DataIndex; static int i; if((TWCR & _BV(TWINT)) == 0) goto FIN; switch(TWSR & 0xf8){ case 0x60: /* 自宛SLA+W受信, ACK応答 */ i = 0; break; case 0x80: /* 自宛データバイト受信, ACk応答 */ if(i == 0) I2C_DataIndex = TWDR; /* 自宛データバイト受信の1バイト目は書き込み開始アドレス */ else{ I2C_Buffer[I2C_DataIndex++] = TWDR; if(I2C_DataIndex >= I2C_BufferSize) I2C_DataIndex = 0; } i++; break; case 0xa8: /* 自宛SLA+R受信, ACK応答 */ case 0xb8: /* データバイト送信, ACK受信 */ TWDR = I2C_Buffer[I2C_DataIndex++]; if(I2C_DataIndex >= I2C_BufferSize) I2C_DataIndex = 0; break; case 0xa0: /* 自指定中の停止条件または再送開始条件検出 */ case 0xc0: /* データバイト送信, NACK受信 */ break; } TWCR = (_BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE)); FIN:; } |
How to Compile (コンパイルのしかた) マスタープログラム /* * DHT22_I2C_master.c * DHT22_I2CのレジスタをI2Cで読み、 * 得られた湿度、温度を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(5) #define SDA_OUT DDRC |= _BV(4) #define SCL_IN DDRC &= ~_BV(5) #define SDA_IN DDRC &= ~_BV(4) #define SCL_H wait_scl_h() #define SCL_H2 PORTC |= _BV(5) #define SDA_H PORTC |= _BV(4) #define SCL_L PORTC &= ~_BV(5) #define SDA_L PORTC &= ~_BV(4) #define SCL (PINC & _BV(5)) #define SDA (PINC & _BV(4)) /* 変更無し */ #define ACK 0 /* ハードに合わせて変更する (SDAのビット) */ #define NAK _BV(4) #define RH_WRITE 0xae #define RH_READ 0xaf 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 RH_read_i2c(unsigned char addr, unsigned char *data, unsigned char size); int main(void) { static unsigned char data[5]; static char buffer[32]; unsigned char sum; short RH, T_; /* ATmega328P */ DDRB = 0b00000000; /* XTAL2 XTAL1 SCK MISO MOSI ----- ----- ----- */ DDRC = 0b00000000; /* xxxxx RESET ----- ----- ----- ----- ----- ----- */ DDRD = 0b00000000; /* ----- ----- ----- ----- ----- ----- TXD RXD */ PORTB = 0b00111111; PORTC = 0b00111111; PORTD = 0b11111100; initUSART0(); sei(); Send0("START\r"); /* DHT22が準備するのを待つ */ delay_ms(2000); /* DHT22がデータを取り終えるのを待つ */ delay_ms(2000); while(1){ RH_read_i2c(0, data, 5); /* チェックサムの確認 */ 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++; } } /* * クロックストレッチ以上の待ち時間を作る(100μS) * * ATmega328P内臓のTWIを使用したI2Cスレーブ(DHT22_I2C.c)の場合 * クロックストレッチの待ち時間は6μSでした */ void wait_scl_h(void) { unsigned char i; SCL_IN; for(i=0; i<10; i++){ if(SCL != 0) break; delay_us(10); } SCL_H2; SCL_OUT; } void start_i2c(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_OUT; SDA_IN; } void stop_i2c(void) { SCL_OUT; SDA_OUT; SCL_L; SDA_L; SCL_H; delay_us(1); /* TSU:STO = 600ns */ SDA_H; delay_us(1); /* TBUF = 1300ns */ delay_us(1); 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:失敗 * * コントロール・バイトの書き込み */ int send_ctrl(unsigned char ctrl) { unsigned char ret; start_i2c(); ret = writebyte(ctrl); if(ret == NAK) return -1; return 0; } /* * 戻り値 0:成功, -1:失敗 * * コントロール・バイトの書き込み以外はACKをチェックしてません */ int RH_read_i2c(unsigned char addr, unsigned char *data, unsigned char size) { int ret; unsigned char i; ret = send_ctrl(RH_WRITE); if(ret == -1) return -1; writebyte(addr); ret = send_ctrl(RH_READ); if(ret == -1) return -1; for(i=0; i<size-1; i++){ *data++ = readbyte(); ack(); } *data = readbyte(); nak(); stop_i2c(); return 0; } |