最終更新時間:2008年04月07日 16時08分16秒
[公開:any]
[電子工作/AVR]
[AVR,HS-15P,ATmega88,湿度計,LM35DZ]
湿度が80%以上で値が高めに測定されるので、「AVRを使った温度計・湿度計の改修」で対応しました。(2008-04-18)
「PICを使った温度計・湿度計の製作」では、湿度センサーとしてTDK CHS-UGRを使いましたが、今回は秋月電子で購入したHS-15Pを使った湿度計を作ってみます。
温度センサーは、前回と同じLM35DZとして、ワンチップマイコンは前回と同じPICでは面白くないので、今回はAVRを使ってみることにします。
湿度センサーHS-15Pを使った回路は、先日にこちらで実験済みなので、同じ基板上に温度センサーのLM35DZを追加します。
LM35DZの出力は、そのままでは1℃あたり10mVとなるので、湿度センサーの出力に使ってあるオペアンプの空き使って10倍に増幅します。つまり、1℃あたり100mVの出力となります。
当初、オペアンプにLM358を使用していましたが、このオペアンプは出力電圧が電源電圧から1.5V低い値までしか出力できません。気温にすると35℃までしか測定できません。もう少し高い気温まで測定できたほうが良いので、Rail-to-RailなオペアンプのLMC662に変更してあります。
なお、湿度センサーの出力は、約+2.5Vのオフセットされた電圧となるので、0から100%が2.5Vから3.5Vの出力となります。
ブレットボードで実験回路を作ってAVRのソフトウェアを作成します。
AVRは、28ピンDIPパッケージのATmega88を使用します。秋月電子で250円という廉価でラインナップされたので早速購入しました。
7セグメントLEDは、3桁のダイナミック点灯用C-533SRを使用したので配線も簡単です。
今回の回路図です。前回は、18ピンのPICなのでIOポートの不足を3-8デコーダで補いましたが、今回はAVRだけで十分足ります。・・というか余ります。
PCBEで作った実装図です。
良く見ればわかると思いますが、上の回路図とIO周りの接続がかなり変更になっています。ブレットボードの実験回路や紙に書く回路図は、都合よく配線できますが、基板実装では、なるべくジャンパーなどが少なくなるように接続して変更部分はソフトウェアで対応しようという考えです。
ユニバーサル基板に部品を実装しました。
AVRには、ブレットボードでテストしたプログラムが書き込んであるので、電源をつないで動作させると、正常に表示されていた7セグメントLEDが無意味な点灯となります。ブレットボードと接続が異なるので当然です。AVRISPmk2をつないで、正常な表示になるようにソフトウェアの修正を行います。
表示が正常となったら、センサー基板を接続して温度と湿度を表示してみます。
前回は、USBを電源として使用するようにしていましたが、今回はリビングで使う予定なのでACアダプタ仕様とします。
ACアダプタは、古い携帯電話で使用していたものです。ラベルには出力が5.8V 730mAとあります。電流は問題ないのですが、電圧が微妙です。AVRのAD変換用リファレンス電圧にも電源電圧を使用するので出来るだけ安定化されたものが必要です。
今回は、CMOSレギュレータのXC6202を使用します。データシートから入出力電位差が600mVで出力電流100mAが得られるようなのでなんとか電圧は足りていると思います。
壁掛けにする予定なので、ケースは前回同様100円均一ショップで購入したプラスティックのフォトスタンドを加工して使用します。
スペーサーで2つのプラスティック板をを保持すれば、十分な通気が得られるので温度センサーや湿度センサーの動作には問題ないと思います。
最後に、乾湿計と比較観測し、温度と湿度を校正します。温度はかなり正確に校正できますが、湿度は±5%程度まで校正できれば良しとしました。
今回のプログラムです。前回のPICで使用したものを単純にAVRへコンバートしただけです。
AVR Stuido4とWinAVR-20071221を使用しました。
1 |
#include <avr/io.h> #include <util/delay.h> #include <avr/pgmspace.h> #include <avr/interrupt.h> #include <stdio.h> #define ADC_ENABLE (_BV(ADEN)|_BV(ADATE)|_BV(ADIF)|0b110) #define ADC_START (ADC_ENABLE|_BV(ADSC)) void delay_ms(unsigned int t) { while(t--) _delay_ms(1); } unsigned int seg[6]; unsigned int mx; // Returns mask 7-seg. display unsigned int mask(unsigned int num) { switch (num) { case 0 : return 0xfa; case 1 : return 0x42; case 2 : return 0x79; case 3 : return 0x6b; case 4 : return 0xc3; case 5 : return 0xab; case 6 : return 0xbb; case 7 : return 0x62; case 8 : return 0xfb; case 9 : return 0xeb; case 10 : return 0x01; // - case 99 : return 0x00; // space } } // Timer1 Compare match interrupt ISR(TIMER1_COMPA_vect) { PORTB = seg[mx]; switch(mx) { case 0: PORTD = 0x01; break; case 1: PORTD = 0x04; PORTB |= _BV(PB2); break; // with DP. case 2: PORTD = 0x02; break; case 3: PORTD = 0x20; break; case 4: PORTD = 0x08; PORTB |= _BV(PB2); break; // with DP. case 5: PORTD = 0x10; break; } mx++; if(mx > 5) mx = 0; } unsigned int get_adc(char ch) { ADMUX = _BV(REFS0)|ch; // Vref = AVcc ADCSRA = ADC_START; loop_until_bit_is_set(ADCSRA, ADIF); return ADCW; } int main() { unsigned int temp, humd; unsigned int i, j; DDRD = 0b11111111; // 7seg Multiplex Control DDRB = 0b11111111; // 7seg Segment Control DDRC = 0b11110011; // PC2,PC3 ADC input PORTD = 0b00000000; PORTB = 0b00000000; TCCR1B = 0b01001011; // Timer1 プリスケーラ 1/64 TIMSK1 = _BV(OCIE1A); // Timer1 compare match A interrupt OCR1A = 256; // 8MHz / 64 = 125000 .... 8us 8*256=2.048ms ADCSRA = ADC_ENABLE; sei(); i = 0; while(1) { temp = 0; humd = 0; for(j = 0; j < 10; j++) { temp += get_adc(2); delay_ms(10); humd += get_adc(3); delay_ms(10); } temp = temp / j * 0.48828; humd = humd / j * 4.8828 - 2484; // *10 and -offset if(temp > 999 || temp < 0) { seg[3] = mask(10); seg[4] = mask(10); seg[5] = mask(10); } else { i = (temp / 100) % 10; if(i == 0) i = 99; seg[3] = mask(i); i = (temp / 10) % 10; seg[4] = mask(i); i = temp % 10; seg[5] = mask(i); } if(humd > 999 || humd < 0) { seg[0] = mask(10); seg[1] = mask(10); seg[2] = mask(10); } else { i = (humd / 100) % 10; if(i == 0) i = 99; seg[0] = mask(i); i = (humd / 10) % 10; seg[1] = mask(i); i = humd % 10; seg[2] = mask(i); } } } |