電子工作のお部屋 | ||||
PIC 16F690とLM35Dで作る お風呂の温度、水高センサー |
きっかけ 最近アパートに引っ越して生活を始めたんですが、お風呂が昔ながらの給湯器が室内にあるタイプなんですね。 それでお湯を沸かすにはいちいちかき混ぜにいって温度確認してちょうどいい温度で止めたりだとか、 お湯を張る時にはあふれないように入れすぎないようにちょいちょい確認しに風呂場へ向かわねばなりません。 自動湯沸かし器の実家に慣れている私にとっては面倒極まりない感じです。 というわけで作りましょう。お風呂の温度、お湯の高さセンサー。 コンセプト PICマイコンで簡単に。7セグLED2桁の温度表示でわかりやすく。ボタンは4つ(上げ、下げ、スタート2つ)。 完成図 ![]() 分りやすいようにボタンを色分けしました。赤:温度↑ 青:温度↓ 白:水高センサーオン 緑:温度センサーオン 部品リスト
今回は良い電源が無かったのでとりあえず電池6本9V稼働です。 コネクタには余っていたUSBを使用しています。 パーツ類はすべて秋月電子通商さんで買わせていただきました。秋葉原の店頭です。 回路図 ![]() 初めて書いた回路図なので勝手がわかりませんでした。汚いですが一応配線はあっているはずです。 ソースコード ソースはHI-TECH C Compiler for PIC10-12-16 MCUs V9.83(PICC v9.83)で書かれております。 ソース中にも書いてありますが、コンフィグレーションビットの指定の仕方などがv9.81から変わっています。 古いコンパイラの時は少し書き換えてください。 //********************************************************************** #include<pic.h> //PIC 16F690はデフォルト4MHz #include "delay.h" //make sure that PIC_CLK in this file is correctly defined //コンフィグレーションビット //INTIO RA4をIOピンとして使用。|WDTDIS Watchdogtimerオフ|PWRTEN パワーアップタイマオン //MCLRDIS RA3をIOとして使用(リセットピンオフ)|UNPROTECT コード&EEプロテクトオフ //BORDIS ブラウンアウトリセット(電圧低下時)オフ|IESODIS クロック2段スタート無効(外部クロック無い) //FCMDIS 外部クロック破損フェイルセーフ無効 // ↓PICCv9.80までの定義 //__CONFIG(INTIO & WDTDIS & PWRTEN & MCLRDIS & UNPROTECT // & BORDIS & IESODIS & FCMDIS); // ↓PICCv9.81〜 __CONFIG(FOSC_INTRCIO & WDTE_OFF & PWRTE_OFF & MCLRE_OFF & CP_OFF & CPD_OFF & BOREN_OFF & IESO_OFF & FCMEN_OFF); #define SEG1 RB7 //7セグ1 #define SEG2 RB6 //7セグ2 #define dotLEDR RC0 //7セグドット右側 #define dotLEDL RC0 //7セグドット左側 #define PLED RB4 //動作中LED(パワーLED 点滅時水高計測中) ON=1 #define USW RA5 //温度UPスイッチ #define DSW RA4 //温度DOWNスイッチ #define SSW RA3 //計測スタートスイッチ #define WSW RA2 //水高計測スタートスイッチ #define Wsens RA1 //水高センサー(プルアップ) ON=0=swON #define swON 0 //スイッチ押下=0 #define swOFF 1 //オフ #define beep RB5 //ビープピン static unsigned char DLED=0; //1でオフ DLED=dotLEDL制御 //動作中かどうかのLED static unsigned char dat1, dat2, datCnt; //7セグ制御用 static unsigned char setTemp=38; //アラーム基準温度 電源入れるたびに38度設定になる。そのうちEEPROMに保存したい static unsigned char nowTemp; //今の温度(mainloopで代入) static unsigned char setMode=0; //温度設定中か 1=設定中 static unsigned char viewMode=0;//監視中の設定温度表示フラグ static unsigned char setCount=0;//カウント static unsigned char oldUSW=swOFF;//スイッチ押しっぱなしフラグ static unsigned char oldDSW=swOFF;// static unsigned char oldSSW=swOFF;// static unsigned char oldWSW=swOFF; static unsigned char checkStart=0;//1の時温度計測中。 2の時水高計測中 static unsigned char checkCount=0;//動作中点滅ランプのカウンタ static unsigned char viewCount=0;//監視中設定温度表示カウンタ static unsigned char Wcount=0; //水高誤動作防止カウンタ(100カウントしたら終了させる) //********************************************************************** //pin 7 6 5 4 3 2 1 0 portC // 右 真 右 真 左 左 真 点 // 上 中 下 下 下 上 上 々 //pin b g c d e f a dp 7seg HDSP-K211 // 8. 配置 // a // f b // g // e c // d dp static unsigned char segTbl[13] = {//7セグ定義 0b10111110, // 0 0b10100000, // 1 0b11011010, // 2 0b11110010, // 3 0b11100100, // 4 0b01110110, // 5 0b01111110, // 6 0b10100110, // 7 0b11111110, // 8 0b11110110, // 9 0b01000000, // - 0b11111110, // all-on(no dot) 0b00000000, // all-off }; //********************************************************************** //7セグ定義を与えられた文字によってリターン(アノードコモン用に反転) unsigned char ChangeSegData(unsigned char c) { switch (c) { case '0': c = (segTbl[0]); break; case '1': c = (segTbl[1]); break; case '2': c = (segTbl[2]); break; case '3': c = (segTbl[3]); break; case '4': c = (segTbl[4]); break; case '5': c = (segTbl[5]); break; case '6': c = (segTbl[6]); break; case '7': c = (segTbl[7]); break; case '8': c = (segTbl[8]); break; case '9': c = (segTbl[9]); break; case '-': c = (segTbl[10]); break; case '*': c = (segTbl[11]); break; case ' ': c = (segTbl[12]); break; default: c = (segTbl[10]); break; } return(~c); //アノードコモンなので反転 } //********************************************************************** //割り込み処理 static void interrupt isr(void){ unsigned int i; if (T0IF == 1) {//タイマー割り込み T0IE=0; //ディスプレイに表示 //一度7セグを両方オフに SEG1 = 0; SEG2 = 0; switch (datCnt) { case 0: PORTC = ChangeSegData(dat1); //dat1 10の位 if ( DLED == 0 ){ dotLEDL = 0; } //動作中ならLED点灯 SEG1 = 1; datCnt++; break; case 1: PORTC = ChangeSegData(dat2);//dat2 1の位 if ( setMode == 1 || viewMode ==1 ){ dotLEDR = 0; }//設定中か動作中の設定表示モードなら右のドットを点灯 SEG2 = 1; datCnt=0; break; default: datCnt=0; break; } T0IE = 1; T0IF = 0; } } //********************************************************************** //A2Dモジュールの設定 void init_a2d(void){ ADCON1=0b00010000; //FOSC/8 ADCON0=0b10000001; //アナログ監視スイッチオン 基準電圧は電源電圧に。 読み取り値右寄せ ANSEL=0b00000001; //アナログ入力ポート選択 ANSELH=0b00000000; } //A2D変換 unsigned int read_a2d(unsigned char channel){ unsigned int temp; channel&=0b00001111; //A2D入力チャネル指定 ADCON0&=0b11000011; ADCON0|=(channel<<2); GO_nDONE=1; //変換開始 while(GO_nDONE)continue; //古いコンパイラではGODONE temp = ADRESH ; // PICは読取った値をADRESHとADRESLのレジスターにセットする temp = ( temp << 8 ) | ADRESL ; // 10ビットの分解能力です return(temp); } //********************************************************************** //ビープ音を鳴らす関数 count回数(>1) beepmsビープの長さ(>1) waitmsビープ間の長さ(>1) void sBeep(unsigned char count,unsigned char beepms,unsigned char waitms){ static int i; for(i=1;i<count;i++){//一応マイナス比較なくした beep=1; DelayMs(beepms); beep=0; DelayMs(waitms); }//ループ一つ減らしてwaitmsを1回省略(終了時間早める) beep=1; DelayMs(beepms); beep=0; } //メイン関数*************************************************************** void main(void) { unsigned int ad1, tmp; unsigned char buf[6]; OPTION_REG = 0b00000001; // RABPU=0内部プルアップON timer 0プリスケラー 1:4 OSCCON =0b01110001; //おしれーた設定 8MHz //入力ピン=1 出力ピン=0 TRISA = 0b00111111;//入力ピン、温度、水高、スイッチ TRISB = 0b00000000;// 0b0000xxxx<−ここは16F690には存在しない 7,6番は7セグ切り換え用 5=ブザー 4= TRISC = 0b00000000;// 7セグメントLED用ポート(全使用)。 WPUA = 0b00111110;// Aportプルアップ設定1=ON RA0=温度計 IOCA = 0b00111110;// AportInterruptON設定 GIEも1にすること。 init_a2d(); // A/D モジュール初期化 // タイマー0を設定する。 T0IE = 1; T0IF = 0; // スイッチの割り込み設定(今回はスイッチ割り込み無し) //RABIE = 1; // IOインタラプトをON //RABIF = 0; PORTA = 0b00111111; PORTB = 0b00000000; PORTC = 0b00000000; DLED = 1;//アノード側なのでオフ PLED = 1;//パワーLED 1でON datCnt = 0; dat1 = ' '; dat2 = ' '; // GIE = 1; // これ以降の処理で割り込みを許可する。 // while (1) { ad1 = read_a2d(0); // 温度センサーの出力をそのままの値。 // 小数点以下の温度を四捨五入する。1度=10mV 5055/1024 = 4.9365234375 //ここは実際の基準電圧にしたがって計算してください。 //今回は3端子レギュレータが5.055Vを出力していたので //5055/1024の値をA2D値に掛けて実際の温度を算出しています。 //ad1 = (int)((double)ad1 * 4.9365234375); ad1 = (int)(ad1 * 4.9365234375)+12; //ケーブルが長いので温度補正 if ((ad1 - ((ad1 / 10) * 10)) >= 5) ad1 = (ad1 / 10) + 1; else ad1 = (ad1 / 10); //設定中であれば設定温度表示 if (setMode == 1){ dat2 = setTemp%10+'0'; //buf[1];10の位 dat1 = (setTemp/10)%10+'0'; //buf[2];1の位 if (setCount > 80){ //カウント80回過ぎたら setCount =0; //カウントを戻して setMode = 0; //設定モード終了 } else { setCount++;//残り時間カウント } } else if (viewMode == 1){//動作中の設定温度表示モードのとき dat2 = setTemp%10+'0'; //buf[1];10の位 dat1 = (setTemp/10)%10+'0'; //buf[2];1の位 } else {//通常状態なら今の温度表示 dat2 = ad1%10+'0'; //buf[1];10の位 dat1 = (ad1/10)%10+'0'; //buf[2];1の位 } nowTemp= ((ad1/10)%10)*10 + ad1%10; //そのまま取り出せると思うが、ごみを取り除いて今の温度を格納 //ボタン入力状態チェック if(DSW==swON && oldDSW==swOFF){ // ボタンが押されてるか 温度マイナス sBeep(1,20,0); DelayMs(20); // チャタリング防止遅延(約20mS) if(setTemp > 0 ) {setTemp--;} setMode = 1; setCount = 0; } oldDSW=DSW;//これを入れておくことによってボタンを押し続けても1度しか処理されない if(USW==swON && oldUSW==swOFF){ // ボタンが押されてるか 温度プラス sBeep(1,20,0); DelayMs(20); // チャタリング防止遅延(約20mS) if( setTemp<99 ) {setTemp++;} setMode = 1; setCount = 0; } oldUSW=USW; if(SSW==swON && oldSSW==swOFF){ // ボタンが押されてるか 温度計測スタート DelayMs(20); // チャタリング防止遅延(約20mS) if( checkStart==0 ){ //チェックモードじゃなければ sBeep(2,20,60); setCount =0; setMode = 0; checkStart=1; //温度チェックモードに } else { //温度か水高チェックモードなら sBeep(1,80,0); checkStart=0; //解除 viewMode = 0;//もどす viewCount=0;//カウンタ初期化 Wcount=0;//水高誤動作防止カウンタ初期化 DLED = 1;//オフ PLED = 1;//オン } } oldSSW=SSW; if(WSW==swON && oldWSW==swOFF){ // ボタンが押されてるか 水高計測スタート DelayMs(20); // チャタリング防止遅延(約20mS) if( checkStart==0 ){ //チェックモードじゃなければ sBeep(3,20,50); setCount =0; setMode = 0; checkStart=2; //水高チェックモードに } else { //温度か水高チェックモードなら sBeep(1,80,0); checkStart=0; //解除 viewMode = 0;//もどす viewCount=0;//カウンタ初期化 Wcount=0;//水高誤動作防止カウンタ初期化 DLED = 1;//オフ PLED = 1;//オン } } oldWSW=WSW; DelayMs(2); //温度チェック if (checkStart==1){//温度チェックモードなら if(setTemp<=nowTemp){//設定温度以上になったら DLED = 0;//オン checkStart=0; Wcount=0;//水高誤動作防止カウンタ初期化 setCount =0; setMode = 0; viewMode = 0;//もどす viewCount=0;//カウンタ初期化 sBeep(32,30,60);//呼び出しビープ! DLED = 1;//オフ } else {//監視中なら checkCount++; if (checkCount<10){ DLED = 0;//オン } else if (checkCount>50){ checkCount=0; viewCount++; viewMode = 0;//もどす } else { DLED = 1;//オフ } if (viewCount>=3){//監視中設定表示カウンタ超えたとき viewCount=0;//カウンタ初期化 viewMode=1;//設定表示モード } } } //水高チェック if (checkStart==2){//水高チェックモードなら if(Wsens==swON){//センサーが反応したらカウントアップ Wcount++; } if(Wcount>250){//250カウントたったら終了(1回だと誤動作の時があるため) PLED = 0;//オフ checkStart=0; Wcount=0;//水高誤動作防止カウンタ初期化 setCount =0; setMode = 0; viewMode = 0;//もどす viewCount=0;//カウンタ初期化 sBeep(32,30,60);//呼び出しビープ! PLED = 1;//オン } else {//監視中なら checkCount++; if (checkCount<10){ PLED = 1;//オン } else if (checkCount>40){ checkCount=0; viewCount++; viewMode = 0;//もどす } else { PLED = 0;//オフ } if (viewCount>=3){//監視中設定表示カウンタ超えたとき viewCount=0;//カウンタ初期化 viewMode=2;//設定表示モード } } } //ループ安定Wait DelayMs(20); } } //********************************************************************** ソース内で読み込んでいる、 delay.h は www.microchipC.com にある、 Delay and Timeout Routines for Hi-Tech C(PIC16Fx core)を使用しています。 ![]() |
このページについて このホームページは、管理人が趣味で作成した電子工作品などを公開するページです。作者はプロではありません。 したがって、このページを参考にして何か問題が起きたとしても 作者は責任を負いません。公開しているプログラムや回路図等は 一応動いている程度のものです。同様のものを作って動くとは思いますが、重大なミスを含んでいる可能性もありますのでご了承ください。 |
Copyright(c)2012 聡.All rights reserved. |