AVRを使った温度計・湿度計の製作

[公開:any]

[電子工作/AVR]
[AVR,HS-15P,ATmega88,湿度計,LM35DZ]

origin 2008-04-07
midify 2008-04-18


湿度が80%以上で値が高めに測定されるので、「AVRを使った温度計・湿度計の改修」で対応しました。(2008-04-18)


PICを使った温度計・湿度計の製作」では、湿度センサーとしてTDK CHS-UGRを使いましたが、今回は秋月電子で購入したHS-15Pを使った湿度計を作ってみます。
温度センサーは、前回と同じLM35DZとして、ワンチップマイコンは前回と同じPICでは面白くないので、今回はAVRを使ってみることにします。

センサー基板

湿度センサーHS-15Pを使った回路は、先日にこちらで実験済みなので、同じ基板上に温度センサーのLM35DZを追加します。
LM35DZの出力は、そのままでは1℃あたり10mVとなるので、湿度センサーの出力に使ってあるオペアンプの空き使って10倍に増幅します。つまり、1℃あたり100mVの出力となります。

HS-15Pセンサー基板

当初、オペアンプに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だけで十分足ります。・・というか余ります。

AVR温度計・湿度計の回路図

PCBEで作った実装図です。
良く見ればわかると思いますが、上の回路図とIO周りの接続がかなり変更になっています。ブレットボードの実験回路や紙に書く回路図は、都合よく配線できますが、基板実装では、なるべくジャンパーなどが少なくなるように接続して変更部分はソフトウェアで対応しようという考えです。

AVR温度計・湿度計の基板配線図

製作

ユニバーサル基板に部品を実装しました。

基板実装(表)基板実装(裏)

AVRには、ブレットボードでテストしたプログラムが書き込んであるので、電源をつないで動作させると、正常に表示されていた7セグメントLEDが無意味な点灯となります。ブレットボードと接続が異なるので当然です。AVRISPmk2をつないで、正常な表示になるようにソフトウェアの修正を行います。

AVR温度計・湿度計1

表示が正常となったら、センサー基板を接続して温度と湿度を表示してみます。

AVR温度計・湿度計2

電源


前回は、USBを電源として使用するようにしていましたが、今回はリビングで使う予定なのでACアダプタ仕様とします。
ACアダプタは、古い携帯電話で使用していたものです。ラベルには出力が5.8V 730mAとあります。電流は問題ないのですが、電圧が微妙です。AVRのAD変換用リファレンス電圧にも電源電圧を使用するので出来るだけ安定化されたものが必要です。

携帯電話のACアダプターACアダプターの出力電圧測定

今回は、CMOSレギュレータのXC6202を使用します。データシートから入出力電位差が600mVで出力電流100mAが得られるようなのでなんとか電圧は足りていると思います。

ディスプレイケース


壁掛けにする予定なので、ケースは前回同様100円均一ショップで購入したプラスティックのフォトスタンドを加工して使用します。

フォトスタンド加工

スペーサーで2つのプラスティック板をを保持すれば、十分な通気が得られるので温度センサーや湿度センサーの動作には問題ないと思います。

フォトスタンド加工2

校正


最後に、乾湿計と比較観測し、温度と湿度を校正します。温度はかなり正確に校正できますが、湿度は±5%程度まで校正できれば良しとしました。

AVR温度計・湿度計の校正

プログラム


今回のプログラムです。前回のPICで使用したものを単純にAVRへコンバートしただけです。
AVR Stuido4とWinAVR-20071221を使用しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
#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);
        }
    }
}

▲ページ Top へ...