焦電型人感センサは、センサモジュールの電源レギュレータICを交換してと手がかかったのだが、そのわりに、感度があまり良くなく、トイレに入っていると電気が消えてしまう。頭をちょっと動かせば、また点灯するのだが、それも、鬱陶しいので、消灯するまでのタイマーをちょっと長いめに調整し、安定運用と相成った。
今回は、PICの制御プログラムの処理の流れをご紹介。
プログラムの処理を表現するには、いろいろな方法があるが、学生時代からなじんでいる昔なじみのフローチャートがやっぱり自分では一番分かりやすい。
簡単に説明すると、
処理は、上から下に向かって進む。
○は、プログラムの最初と最後。
□は処理。
横が二重線になっている□は、まとまった処理。
まとまった処理は、別のところにその詳細を記述する。
◇は判断分岐。条件によって処理の行き先が変わる。
だいたい、こんなところを抑えておけば、概ね理解できる非常にシンプルな構造。
装置に組み込むプログラムで、特徴的なのは、割込みと言う処理。
通常、プログラムを書くと、最初に何やって、次に何やって…と順番に処理を記述する。でも、例えば、何か仕事をしていて、電話がかかってくる と、仕事を中断して、電話に出て、対応して、また仕事に戻るというようなことをするが、それと同じ事がプログラムでも記述できる。
それが、割込み処理。割込みを使わないで同じように処理する方法もあるが、割込みをうまく使うと、省電力にもできる。最近のCPU(コンピュータチップ)は、何もすることが無い場合はスリープモードと言って電力をほとんど消費しない仕組みが組み込まれている。省電力化するには、お仕事はちゃっちゃと済ませて、終わったらすぐ にスリープするように処理を記述すると、とってもエコになる。
今回の処理も、割込みをたくさん使っている。
動作を簡単に説明すると、、、、
まず、電気のスイッチが入ったら、電灯を点灯(電灯回路に電気を流すようにリレーを閉じる)する。
ある一定時間が過ぎるまでに、センサが人の動きを検知したら、点灯時間を延長する。
動きが無く、一定時間が経過したら電気を消灯(リレーを開く)する。
基本的には、それだけなのだが、お昼など、まわりが明るいときはどうするか。
昼でも、スイッチが入ったら、とりあえず、電気は点けた方が良いと思う。スイッチを入れても電気がつかなかったら、電球が切れていると思ってしまうし。
そこで、スイッチを入れて、人の動きがない状態がある一定時間過ぎたら、消灯。その後、人の動きを検知して、再点灯させるときには、照度センサで周囲の明るさを確認し、明るかったら、点灯させない。
さらに、夕方など、だんだん暗くなるようなときには、人の動きを検知する度に、照度センサで、周囲の明るさを確認して点灯するか消灯するかを判断すると。
だいたい、そんな動きになっている。
一定時間を、最初は2分にしていたのだが、すぐに消えてしまうので、8分ぐらいに調整した。人の頭が、10センチぐらい動けば検知するので、8分ぐらいの間には、1度ぐらいは動くだろうと言うこと。じ~~~~っとしていると、消えてしまうので、その場合は、ちょっと頭を動かせば、再点灯してくれる。
前回へ 次回へ
====================================================================
以下にプログラムのソースコードを紹介する。PICのアセンブラという言語。javaやCなどに比べると、低級言語なので、効率は悪いが、処理速度はぴかいち。
; ========================================================
; 人感センサ(割込み)によるACライン制御
; 人感センサ検知→cdsセルで暗さ検知→LED2(AC電源制御)
; PIC12F629
; clock:INTRC_OSC_NOCLKOUT 4MHz
; 電源:5V-DC
; 内部クロック4MHz
; 全てのピン(6ピン)をデジタルIOで使用
; GP0: LED1(出力)(人感センサ状況)
; GP1: LED2(出力)(AC電源制御)
; GP2: 人感センサ(GP2/INT)
; GP3: Cds(入力)
; 人感センサによりGP2/INT割込みを発生させ、LED2を5秒間点灯させる
; CdsをGP3で検知し、暗い時のみ動作
; 時間待ちは、TMR0割込みを使用
; 人の動きを検出していない時は、SLEEP状態(省電力待機)にする
LIST P=12f629
#INCLUDE <p12f629.inc>
ERRORLEVEL -302 ; 302警告を非表示にする
CB = _CPD_OFF ; データメモリ非保護
CB &= _CP_OFF ; プログラムメモリ非保護
CB &= _WDT_OFF ; ウォッチドッグタイマ未使用
CB &= _BODEN_ON ; 低電圧自動リセット
CB &= _PWRTE_ON ; 電源ON時ウェイトON
CB &= _MCLRE_OFF ; 外部リセット端子未使用
CB &= _INTRC_OSC_NOCLKOUT ; 4MHz内部クロック(クロック出力無し)
__CONFIG CB
CBLOCK 020h
save_st ; STATUSのセーブ領域
save_w ; W-regのセーブ領域
CNT76 ; TMR0割込みを約5秒(76回)カウントするカウンタ
; 1us(4MHzで4クロック分)×256×256=65.536ms 65.536ms×76=4.981s
DIM1 ; LED PWM 点灯時間用
DIM2 ; LED PWM 消灯時間用
CNT01 ; LEDディマー消灯用カウンタ1
CNT02 ; LEDディマー消灯用カウンタ2
CNT99 ; LEDディマー消灯用カウンタ2
f_int_tmr ; 割込種別フラグ
; xxxx x210
; 0:GP2/INT割込発生
; 1:TMR0割込発生
; 2:点灯継続タイマ満了
light_st ; 点灯状態フラグ
ENDC
ORG 0 ; ORG H’0’でも同じ
GOTO INIT ; 初期処理に行く
; ======================== 割込み処理 ========================
INTSTART
ORG 4 ; 割込みベクタ(4番地)を指定する。
; 割込み処理中は他の割込みを禁止
BCF INTCON,GIE ; 全ての割込みを禁止
; レジスタ待避定型処理
MOVWF save_w ; W-regをセーブ
SWAPF STATUS,W ; STATUSレジスタの上位下位を入れ替えW-regに
MOVWF save_st ; STATUSレジスタセーブ
; 割込みの種別判定
CLRF f_int_tmr ; 割込種別フラグクリア
BTFSC INTCON,INTF ; GP2/INT割込み?
GOTO GP2INT ; Yes
BTFSC INTCON,T0IF ; TMR0割込み?
GOTO T0INT ; Yes
GOTO INTEXIT ; No
GP2INT ; GP2/INT割込み処理(人感センサ検知)
BCF INTCON,INTF ; 外部割込みフラグクリア
CALL RESETTIMER ; 継続表示タイマをリセット
BSF f_int_tmr,0 ; 割込種別フラグセット(GP2/INT割込発生)
BSF INTCON,T0IE ; タイマ割込み許可
GOTO INTEXIT
T0INT ; TMR0割込み処理(点灯継続カウントダウンタイマ)
BCF INTCON,T0IF ; タイマ割込みフラグクリア
BSF f_int_tmr,1 ; 割込種別フラグリセット(TMR0割込発生)
DECFSZ CNT76,F ; CNT76-1が0か?(継続時間経過したか)
GOTO INTEXIT ; No
; Yes (点灯継続時間経過)
BCF INTCON,T0IE ; TMR0割込み不許可
; CALL RESETTIMER ; 継続表示タイマをリセット
BSF f_int_tmr,2 ; 5秒経過フラグをセット
INTEXIT
; レジスタ復帰定型処理
SWAPF save_st,W ; save_stの上位下位を入れ替えW-regに
MOVWF STATUS ; STATUSレジスタを復帰
SWAPF save_w,F ; SWAPを2回行うことでW-regを復帰
SWAPF save_w,W ; これはSTATUSレジスタに影響させないため
; 割込み許可
RETFIE ; GIEセットしてリターン
; ================= PICの初期化 =========================
INIT
BSF STATUS,RP0 ;■バンク1に切替え
MOVLW 0Ch ; B’0000 1100′
MOVWF TRISIO ; GP2(INT),3は入力,GP0,1は出力
MOVLW 087h ; OPTIONレジスタの設定 B’1000 0111′
MOVWF OPTION_REG ; ^GPPU:1 ,INTEDG:0 ,T0CS:0 ,PSA:0 ,PS2-PS0:111
BCF STATUS,RP0 ;■バンク0に切替え
MOVLW 07h ; B’00000111′ SETUP DIGITAL I/O MODE
MOVWF CMCON ; GP2-0はデジタルIO
CLRF GPIO ; GPIOをクリア(初期状態ではLED消灯)
CLRF light_st ; 人感センサ検知状態クリア
; GOTO SLEEPMODE ; テスト用
; ===== 電源ON後、人感センサの回路が安定するまで5秒待ちスリープ =====
BSF GPIO,GP0 ; 電源ON後人感センサが安定するまでLED1(GP0)点灯
CALL RESETTIMER ; 継続表示タイマをリセット
MOVLW 0A0h ; INTCONの設定(B’1010 0000′)
MOVWF INTCON ; GIE:1 ,T0IE:1(TMR0割込み許可)
WAIT5S ; 電源ON後、人感センサの回路が安定するまで5秒待つ
BTFSS f_int_tmr,2 ; 5秒経過フラグON?
GOTO WAIT5S ; No
CLRF INTCON ; 割込み全不許可
BCF GPIO,GP0 ; 電源ON後人感センサが安定したらLED1(GP0)消灯
GOTO SLEEPMODE ; 準備が整ったらスリープし人感センサ割込を待つ
; ================= メイン処理 =========================
LOOP
; CALL DISP ; 人感センサ状態をLEDで表示
; 割込種別の判定
BTFSC f_int_tmr,0 ; 人感センサ割込?
GOTO SENSORINT ; Yes:人感センサ割込処理に
BTFSC f_int_tmr,1 ; タイマ割込?
GOTO TIMERINT ; Yes:タイマ割込処理に
GOTO LOOP ; 割込がない場合はLOOP
; 人感センサ割込み処理
SENSORINT
BTFSS light_st,0 ; 点灯状態を確認
GOTO TENTOU ; No :消灯の場合は点灯可否判断に
GOTO LOOPEND ; Yes:既点灯の場合はループ終了へ
; 点灯処理
TENTOU
BTFSS GPIO,GP3 ; cdsセル:暗い?
GOTO SLEEPMODE ; No :明るい場合は何もせずスリープモードに
BSF GPIO,GP1 ; Yes:暗い場合はLED2を点灯
BSF light_st,0 ; 点灯状態フラグをセット
GOTO LOOPEND ; No :ループ終了へ
; タイマ割込み処理
TIMERINT
BTFSS f_int_tmr,1 ; タイマー割込による割込?
GOTO LOOPEND ; No :ループ終了へ
BTFSS light_st,0 ; 点灯状態を確認
GOTO LOOPEND ; No :ループ終了へ
; 消灯処理
SYOTOU
BTFSS f_int_tmr,2 ; タイマー満了?
GOTO LOOPEND ; No :ループ終了へ
BCF GPIO,GP1 ; Yes:タイマ満了の場合はLED2を消灯
BCF light_st,0 ; 点灯状態フラグをクリア
GOTO SLEEPMODE ; スリープモードに
; 割込種別フラグをクリア
LOOPEND
GOTO LOOP ; ループ先頭に
; スリープモードに
SLEEPMODE
MOVLW 0A0h ; INTCONの設定(B’1010 0000′)
MOVWF INTCON ; GIE:1 ,T0IE:1(TMR0割込み許可)
CALL DIMMEROFF ; LEDディマー消灯でスリープモードに入る
MOVLW 090h ; INTCONの設定(B’1001 0000′)
MOVWF INTCON ; GIE:1 ,INTE:1(GP2/INT割込み許可)
SLEEP ; 省電力モードで待機(GP2/INT割込み待ち)
NOP ; SLEEPから復帰したら実施された後割込み処理に
GOTO LOOP ; ループ先頭に
; ============== 人感センサ状態LED表示処理 ======================
RESETTIMER
CLRF TMR0 ; TMR0クリア
MOVLW d’76’ ; 人感で点灯継続タイマをリセット
MOVWF CNT76 ; 点灯継続タイマを初期化
RETURN
; ============== 人感センサ状態LED表示処理(未使用) ==============
DISP
BTFSC GPIO,GP2 ; 人感センサ = ON?(プルアップされてるためONでLow)
GOTO LED1OFF ; No(人感センサ非検知はLED消灯してリターン)
BSF GPIO,GP0 ; LED1(人感)を点灯する
GOTO DISPRET
LED1OFF
BCF GPIO,GP0 ; LED1(人感)を消灯する
DISPRET
RETURN
; ============== スリープする前にLEDをディマー消灯 =============
DIMMEROFF
CLRF TMR0 ; TMR0クリア
MOVLW 0ffh ; PWM点灯時間初期化
MOVWF DIM1 ;
MOVLW 01h ; PWM消灯時間初期化
MOVWF DIM2 ;
DIMLOOP
MOVF DIM1,W ; 点灯時間をカウンタにセット
MOVWF CNT01 ;
MOVF DIM2,W ; 消灯時間をカウンタにセット
MOVWF CNT02 ;
BSF GPIO,GP0 ; LED1を点灯する
WAIT01
CALL WAIT99 ; 時間稼ぎ
DECFSZ CNT01,F ; カウンタが0か?
GOTO WAIT01 ; Wait01ループ
BCF GPIO,GP0 ; LED1を消灯する
WAIT02 ;
CALL WAIT99 ; 時間稼ぎ
DECFSZ CNT02,F ; カウンタが0か?
GOTO WAIT02 ; Wait02ループ
INCF DIM2,F ; 消灯時間を増やす
DECFSZ DIM1,F ; 点灯時間を減らす
GOTO DIMLOOP ; 点灯時間が0になったら終了
DIMRET
RETURN
WAIT99
MOVLW 09h ; 時間待ちループ
MOVWF CNT99 ; ↓
WAIT91 ; ↓
DECFSZ CNT99,F ; ↓
GOTO WAIT91 ; ここまで
RETURN
END
; ========================================================