PICとLEDを使ってバッテリーの電圧低下を監視する

## 目的
バッテリーで駆動するデバイスの電源電圧が低下したことを検知して回路が動作しなくなる前に通知したい。
これをなるべく手頃かつ低消費電流で実現したいと思い検索していたところ、下記のWebサイトがやりたいことにマッチしていました。

- [電源電圧低下の判定](http://www.a-phys.eng.osaka-cu.ac.jp/hosoda-g/site1/BatterySense.html)

マイコンに接続したLEDに電流を流した時のVF（順方向電圧）を読み取ることで電源電圧の低下を判定できるとのこと。
また、常時ではなく一定間隔や任意のタイミングに一瞬だけLEDに電流を流すだけで判定できるため、低消費電流での動作も期待できそうです。
今回はPICマイコンを使って実験回路を組み、電源電圧の低下を判定するプログラムを書いてみます。

## 開発環境
- MPLAB X IDE v5.10
- PICKit3

## 実験回路
下図の実験回路を組みました。

- PIC12F1822
- フィルムコンデンサ0.1μF（PIC電源安定用）
- 抵抗器×2（22kΩ、10kΩ）
- LED×2（電圧読み取り用：緑色、通知用：赤色、いずれも高輝度タイプ）
- 単3電池（正常時は3本、電圧低下状態は2本で模擬）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305999/67c682ec-c58d-6184-dba8-126233c4d1ff.png)

正常時の電源は単3電池3本（今回は約4.2V）、電圧低下状態の電源は単3電池2本（今回は約2.8V）で模擬します。

電圧読み取り用LEDはRA5ピン、通知用LEDはRA2ピンに接続します。
また、電圧読み取り用LEDと並列に信号線をRA4ピンに接続します。
RA4ピンはアナログ入力ピンに設定でき、AD変換でLEDのVFを読み取ることができます。

## プログラム
各種コンフィグレーションについては下記ページが詳しいです。

- [PIC12F1822使い方：１２Ｆ１８２２覚書](http://zattouka.net/GarageHouse/micon/MPLAB/12F1822/memo.htm)

RA5ピンの出力を1秒おきに0と1交互に切り替えていて、RA5ピンが1(Hi)の時はLED両端の電位差が0Vになるので電流は流れませんが、0(Lo)の時は電位差が生じて電流が流れます。
RA5ピン出力が0の時にLEDのVFを読み取り、VFが閾値以上の場合はRA2ピンに接続した通知用LEDを点滅させます。
読み取った電圧値はAD変換により0～1023の間の数値に変換されるので閾値はこの間で設定します。
今回の実験回路における電圧読み取り用LEDのVFは2.3V程度で、電圧低下時の2.8Vより少しだけ高い3.0Vで通知させようとすると2.3V/3.0V×1023≒784となるためキリの良い780を閾値としました。
ちなみに正常電圧におけるVFのAD変換値は2.3V/4.2V×1023≒560となります。

```c:main.c
#include <xc.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>

// コンフィグレーションBIT1 設定
#pragma config FOSC     = INTOSC
#pragma config WDTE     = OFF
#pragma config PWRTE    = ON
#pragma config MCLRE    = OFF
#pragma config CP       = OFF
#pragma config CPD      = OFF
#pragma config BOREN    = ON
#pragma config CLKOUTEN = OFF
#pragma config IESO     = OFF
#pragma config FCMEN    = OFF

// コンフィグレーションBIT2 設定
#pragma config WRT      = OFF
#pragma config PLLEN    = OFF
#pragma config STVREN   = OFF
#pragma config BORV     = LO
#pragma config LVP      = OFF

// 動作周波数
#define _XTAL_FREQ      4000000

// AD変換
unsigned int adconv()
{
    __delay_us(5);              // アナログ変換情報が設定されるまで待機
    ADCON0bits.GO = 1;          // PICにアナログ値読取り開始を指示
    while(ADCON0bits.nDONE);    // PICが読取り完了するまで待つ

    return ADRES;
}

int main() {
    
    // PIC設定
    OSCCON = 0b01101010;        // 内部クロック4MHz
    ANSELA = 0b00010000;        // アナログはRA4(RN3)に割り当て、残りをすべてデジタルに割り当てる
    TRISA  = 0b00010000;        // RA4(RN3)を入力、その他のピンは出力に割り当てる(ただしRA3は入力専用のため設定は無効)
    PORTA  = 0b00000000;        // すべてのピンの出力をLoとする
    WPUA   = 0b00000000;        // プルアップ設定なし
    
    // AD変換設定
    ADCON1 = 0b11000000;        // 読取値は右寄せ、A/D変換クロックはFOSC/4、VDDをリファレンスとする
    ADCON0 = 0b00001101;        // アナログ変換情報設定、RA4(RN3)から読み込む
    
    unsigned int value = 0;
    bool flag = false;
    
    while(1) {
        if (!flag){
            RA5 = 0;            // 電圧読み取り用LED点灯
            __delay_ms(1000);
            value = adconv();   // AN3(RA4)から電圧値を読み取る
            RA5 = 1;            // 電圧読み取り用LED消灯
            __delay_ms(1000);
            if(value > 780){    // 閾値以上で電圧低下を通知
                flag = true;
            }
        }else{
            RA2 = 1;            // 電圧低下通知用LED点灯
            __delay_ms(1000);
            RA2 = 0;            // 電圧低下通知用LED消灯
            __delay_ms(1000);
        }
    }
    
    return 0;
}

```

## 動作確認
4本用の電池ボックスしか持ち合わせがなく見た目の悪い配線をしていますがご容赦ください。
電源電圧が正常の場合は電圧読み取り用のLEDが点滅し、電源電圧が低下している場合は通知用のLEDが点滅します（写真だと分かりづらいですがわずかに赤色が見えます）。

- 電源電圧が正常の場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305999/34545496-341f-e13e-3fd7-10a0f250b77b.png)

- 電源電圧が低下している場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/305999/3e2cdb5f-016d-f617-b22a-92a8b5c4b1d0.png)

## まとめ
電圧読み取り時にLEDに流れる電流は約80μAで、低消費電流で電圧低下監視を実現できています。
ただし回路全体でみるとマイコン動作時の消費電流が占める割合の方がほとんどで（実測で1.5～2mA程度）、実用の際はスリープモードと割り込みを使用して間欠動作をさせる必要があります。