PIC16F19155を使ってみる(その4)

スリープモードを試す

PICのシリーズが続きます。今回は、スリープをしてみてどのくらい電力消費するかの計測です。
前回はRTCCを使った10秒間隔の割り込みをテストしました。
alasixosaka.hatenablog.com

この時は、スリープを使ってないので、CPUは動作し続けていて電力を消費し続けます。電池駆動を考える時は不要な時はなるべくスリープに入れて電力を消費しないようにします。
PICではスリープの手順は簡単で、コマンドのSLEEP()を使うだけです。一つ注意しないといけないのは、スリープに入る前に1コマンド先まで進むことがあるということです。ですので、念のためスリープコマンドの後にNOP()コマンドを入れておきます。

PMDを設定する

PIC16F19155には、PMD(Peripheral Module Disable)コントロールレジスタというのがあり、不要な周辺装置を無効にすることができます。デフォルトでは有効になっていますので、使わない機能があっても装置が動き続けて電力を消費します。ですので、このレジスタを設定して不要な機能は停止することで消費電力を削減できます。
PMDコントロールレジスタは5つあって、下記のように設定しました。まず、PMD0です。

    // SYSCLKは有効、 ACTを無効、FVRを無効、IOCを無効、NVMを無効 
    PMD0 = 0x65;

SYSCLK、周辺装置でシステムクロックで動作するものはこれを有効にしないと動かない(と思います)。今回は使いませんが、電子ペーパーとの通信にSPIを使いますので有効にします。(SPIはスレーブはマスターのクロックで動作するのでスレーブ動作の時は不要です。マスター動作の時はクロックが必要)
ACTはActive Clock Tuningで、外部クロックで内臓オシレータをチューニングする機能のようです。主にはUSB通信を安定にするためのようです。USBは使いませんというかPIC16F19155にはそもそも搭載されていないので必要ないかと思います。
FVRはFixed Voltage ReferenceでADコンバータを使うときに使うものです。ADCは使わないので無効にします。
IOCはInterrupt on Change Bitで外部ピン割り込みを使うときに外部ピンの変化を監視する機能です。外部ピン割り込みも使いませんので無効にします。
NVMはNon Volatile Memoryのことで、フラッシュメモリにアクセスするときに使うようです。この機能も使わない(たぶん)ので無効にします。
PMD1は、すべてタイマー関連です。タイマーを使う予定はないのですべて無効にします。

    // TMR0は無効、TMR1は無効、TMR4は無効、TMR2は無効
    PMD1 = 0x17;

PMD2は、

    // ZCD は無効。DACは無効、CMP1は無効、ADCは無効、CMP2は無効、RTCCは有効 
    PMD2 = 0x67;

ZCDは交流が0Vを横切るポイントを測定するモジュール、使わないので無効にします。
DACはDAコンバーター、使わないので無効にします。
CMP1、CMP2はコンパレータです。使わないので無効にします。
ADCはADコンバーター、使わないので無効にします。
RTCCは必要ですので有効にします。
PMD3です。PMD3はPWMとCCP関連ですべて無効にします。

    // CCP2は無効、CCP1は無効、PWM4は無効、PWM3は無効
    PMD3 = 0x0F;

PMD4です。

    // CWG1は無効、EUSART2は無効、MSSP1は有効、EUSARTは無効 
    PMD4 = 0xC1;

CWGもPWM関係のモジュールのようです。使用しないので無効にします。
EUSART(EUSART2も)はシリアル通信用モジュールで使用しないので無効にします。
MSSPはI2CやSPI通信を行うモジュールです、後々使用するので有効にします。
PMD5です。

    // CLC3は無効、CLC4は無効、SMT1は無効、LCDは無効、CLC1は無効、CLC2は無効 
    PMD5 = 0x7E;

CLCはConfigure Logic Cellのことで論理回路をソフト的に構成できるモジュールです。今回は使いませんので無効にします。
LCDは液晶ドライバのモジュールです。今回は使用しないので無効にします。
SMTはパルスカウンタです。今回は使用しないので無効にします。
あいまいな記述が多くてすいません。どうもよくわかっていないところが多いもので。

プログラムの全文です。

#include <xc.h>
#include <PIC16f19155.h>

// CONFIG1
#pragma config FEXTOSC = OFF    // External Oscillator mode selection bits->Oscillator not enabled
#pragma config RSTOSC = HFINT1    // Power-up default value for COSC bits->HFINTOSC (1MHz)
#pragma config CLKOUTEN = OFF    // Clock Out Enable bit->CLKOUT function is disabled; i/o or oscillator function on OSC2
#pragma config VBATEN = OFF    // VBAT Pin Enable bit->VBAT functionality is disabled
#pragma config LCDPEN = OFF    // LCD Charge Pump Mode bit->LCD Charge Pump is disabled.
#pragma config CSWEN = ON    // Clock Switch Enable bit->Writing to NOSC and NDIV is allowed
#pragma config FCMEN = ON    // Fail-Safe Clock Monitor Enable bit->FSCM timer enabled

// CONFIG2
#pragma config MCLRE = ON    // Master Clear Enable bit->MCLR pin is Master Clear function
#pragma config PWRTE = OFF    // Power-up Timer selection bits->PWRT disable
#pragma config LPBOREN = OFF    // Low-Power BOR enable bit->ULPBOR disabled
#pragma config BOREN = ON    // Brown-out reset enable bits->Brown-out Reset Enabled, SBOREN bit is ignored
#pragma config BORV = LO    // Brown-out Reset Voltage Selection->Brown-out Reset Voltage (VBOR) set to 1.9V on LF, and 2.45V on F Devices
#pragma config ZCD = OFF    // Zero-cross detect disable->Zero-cross detect circuit is disabled at POR.
#pragma config PPS1WAY = OFF    // Peripheral Pin Select one-way control->The PPSLOCK bit can be set and cleared repeatedly by software
#pragma config STVREN = ON    // Stack Overflow/Underflow Reset Enable bit->Stack Overflow or Underflow will cause a reset

// CONFIG3
#pragma config WDTCPS = WDTCPS_31    // WDT Period Select bits->Divider ratio 1:65536; software control of WDTPS
#pragma config WDTE = OFF    // WDT operating mode->WDT Disabled, SWDTEN is ignored
#pragma config WDTCWS = WDTCWS_7    // WDT Window Select bits->window always open (100%); software control; keyed access not required
#pragma config WDTCCS = SC    // WDT input clock selector->Software Control

// CONFIG4
#pragma config BBSIZE = 512    // Boot Block Size Selection bits->Boot Block Size (Words) 512
#pragma config BBEN = OFF    // Boot Block Enable bit->Boot Block disabled
#pragma config SAFEN = OFF    // SAF Enable bit->SAF disabled
#pragma config WRTAPP = OFF    // Application Block Write Protection bit->Application Block NOT write-protected
#pragma config WRTB = OFF    // Boot Block Write Protection bit->Boot Block NOT write-protected
#pragma config WRTC = OFF    // Configuration Register Write Protection bit->Configuration Words NOT write-protected
#pragma config WRTD = OFF    // Data EEPROM Write Protection bit->Data EEPROM NOT write-protected
#pragma config WRTSAF = OFF    // Storage Area Flash Write Protection bit->SAF NOT write-protected
#pragma config LVP = OFF    // Low Voltage Programming Enable bit->High Voltage on MCLR/Vpp must be used for programming

// CONFIG5
#pragma config CP = OFF    // UserNVM Program memory code protection bit->UserNVM code protection disabled

int LEDflg ;

#define _XTAL_FREQ 1000000

void __interrupt() isr(void)
{
    PIR8bits.RTCCIF = 0;
    if (LEDflg == 0) LEDflg = 1 ;
    else             LEDflg = 0 ;
    return;
}

void main(void) {

    ANSELA = 0b00000000;  // ?????????????????
    ANSELB = 0b00000000;
    ANSELC = 0b00000000;
    TRISA  = 0b00001000;  // ???????????????(???RA3??????????)
    TRISB = 0x00;
    TRISC = 0x00;
    
     // NOSC HFINTOSC; NDIV 1; 
    OSCCON1 = 0x60;
    // CSWHOLD may proceed; SOSCPWR Low power; 
    OSCCON3 = 0x00;
    // MFOEN disabled; LFOEN disabled; ADOEN disabled; SOSCEN enabled; EXTOEN disabled; HFOEN disabled; 
    OSCEN = 0x08;
    // HFFRQ 1_MHz; 
    OSCFRQ = 0x00;
    // MFOR not ready; 
    OSCSTAT = 0x00;
    // TUN 0; 
    OSCTUNE = 0x00;
    // ACTUD enabled; ACTEN disabled; 
    //ACTCON = 0x00;
    
    
    // SYSCLKは有効、 ACTを無効、FVRを無効、IOCを無効、NVMを無効 
    PMD0 = 0x65;
    // TMR0は無効、TMR1は無効、TMR4は無効、TMR2は無効
    PMD1 = 0x17;
    // ZCD は無効。DACは無効、CMP1は無効、ADCは無効、CMP2は無効、RTCCは有効 
    PMD2 = 0x67;
    // CCP2は無効、CCP1は無効、PWM4は無効、PWM3は無効
    PMD3 = 0x0F;
    // CWG1は無効、EUSART2は無効、MSSP1は有効、EUSARTは無効 
    PMD4 = 0xD1;
     // CLC3は無効、CLC4は無効、SMT1は無効、LCDは無効、CLC1は無効、CLC2は無効 
    PMD5 = 0x7E;
    
    RTCCONbits.RTCWREN = 1;

    // Disable RTCC
    RTCCONbits.RTCEN = 0;
    
    RTCCONbits.RTCCLKSEL = 0;
    
       // set RTCC time 2022-09-11 21-44-10
    YEAR     = 0x22;   // year
    MONTH    = 0x9;    // month 
    WEEKDAY  = 0x0;    // weekday 
    DAY      = 0x11;    // day
    HOURS    = 0x21;    // hours 
    MINUTES  = 0x44;    // minutes 
    SECONDS  = 0x10;    // seconds 
        
    // set Alarm time 2022-09-11 21-44-10
    ALRMCONbits.ALRMEN = 0;
    // ARPT 0; 
    ALRMRPT = 0x00;
    
    ALRMMTH  = 0x9;  // month 
    ALRMWD   = 0x0; // weekday 
    ALRMDAY  = 0x11;  // day
    ALRMHR   = 0x21; // hours 
    ALRMMIN  = 0x44;  // minutes 
    ALRMSEC  = 0x10;  // seconds 

    // Re-enable the alarm
    ALRMCONbits.ALRMEN = 1;
    
    // AMASK Every 10 Second; CHIME enabled; ALRMEN enabled; 
    ALRMCON = 0xC8;

    // CAL 0; 
    RTCCAL = 0x00;
    
    // Enable RTCC
    RTCCONbits.RTCEN = 1;
    while(!RTCCONbits.RTCEN);

    // Disable write operations on RTCC timer registers
    RTCCONbits.RTCWREN = 0;

    // Clear the RTCC interrupt flag
    PIR8bits.RTCCIF = 0;

    // Enable RTCC interrupt
    PIE8bits.RTCCIE = 1;
    
    INTCONbits.PEIE   = 1 ;             // 周辺装置割り込み有効
    INTCONbits.GIE    = 1 ;             // 全割込み処理を許可する
    
    RA0 = 0;
    
    while(1){
        if (LEDflg == 0) RA2 = 0 ;    // 10番ピンにLOWを出力する(LED OFF)
        else             RA2 = 1 ;    // 10番ピンにHIGHを出力する(LED ON)
        //RA0 = 0;
        //__delay_ms(950);            
        //RA0 = 1;
        //__delay_ms(50);
        SLEEP();
        NOP();
    }
    return;
}

あまり、大きく変えていません。PMD関連の設定と、ループ関数内でのLチカをやめて、その代わりにスリープコマンドを記述しています。
これで、RTCCで10秒間隔で割り込みがかかり、すぐにスリープに入るという動作を繰り返します。ループ関数内でLEDのオン、オフをしていますので、RA2に繋いだLEDが10秒ごとに点滅を繰り返します。LEDはスリープしていても点灯時はつきっぱなしです。つまり、電流を消費します。ですので、今回のテストプログラムは省電力という観点では良いプログラムではありません。スリープする前にLEDは消灯して消費電力を下げてスリープするのが基本です。今回は、LED点灯時と消灯時の消費電流の差を測るためにあえてこういうプログラムにしてあります。
実際に消費電流を測ってみました。電源電圧は3.3Vにしました。

LED点灯時の消費電流。3.6mA
LED消灯時の電流。65.5μA

ちょっとわかり難いですが右側のLEDがついている状態で3.6mA。消えている状態で65.5μAでした。以外に電流を消費していますねえ。
もう少し工夫の余地があるのかもしれません。データシートを読むと、Low Powe Sleep Modeというのがあるようです。ピンにつながっているLDOを止めることで消費電流を下げることができるようです。また、周辺装置を完全にストップしてしまえばもっと消費電力を下げれそうです。

低消費電力を極める

それでは、RTCC以外の全部の周辺装置をストップして、LDOを止めた状態でどうなるかテストしてみました。
PMDはめんどくさいので下記のように、PMD2以外は0xFFを、PMD2は0x7Fとしました。

    PMD0 = 0xFF;
    PMD1 = 0xFF;
    PMD2 = 0x7F;
    PMD3 = 0xFF;
    PMD4 = 0xFF;
    PMD5 = 0xFF;

LDOを止めるには、

VREGCONbits.VREGPM = 1

と一文を書き加えます。
これで、42.4μAまで下がりました。

RTC以外を停止して42.4μA

LFシリーズなら

部屋をあさっていたら、PIC16LF19157というチップが見つかりました。これは、PIC16F19155と同じシリーズで、40ピンのタイプです。(PIC16F19155は28ピン)。しかも、型番がFではなくLFなので、更に省電力が期待できます。時計を作ろうとしていた時に購入したもののようです。
早速実験してみました。
同じシリーズなのでプログラムはほぼ同じでいけます。ピン数が多いので、ピンの設定の部分だけ書き足しました。
ところが、プログラムを書きこもうとすると電力不足と言われます。こんなメッセージが出ます。
The target circuit may require more power than the debug tool can provide.
An external power supply might be necessary. Connection Failed.
PIC16F19155は何の問題もないに、PICLF19157はエラーになってしまいます。LF品番なので、PicKitから供給する電圧を3.25Vに設定してます。PIC16F19155でも3.25Vでやってみましたが、こちらは何の問題もなく書き込めました。
仕方がないので、電源を3.3VのACアダプタから供給してみました。ところが、今度は
Conection Failed
と出て、うまくいきません。何度やってもダメで、USBポートを変えてもダメでした。
ノートパソコンから繋いでいるのがダメなのかと思い、デスクトップパソコンから繋いでみると、PicKitからの電源供給で問題なく書き込めてしまいました。
ちなみに、ノートPCの方は、Windows11で、MPLAB XIDEのバージョンが6.0、デスクトップの方は、Windows10で、MPLAB XIDEのバージョンが5.2でした。OSの違いなのか、MPLAB XIDEのバージョンの違いなのか、はたまた、デスクトップとノートの違いなのか、ちょっと判らないですが。自分の場合、デスクトップPCの周りはスペースがほとんどないので、マイコンへの書き込みとか電子工作関連はノートPCで行うのが基本なのですが、ちょっと厄介です。
とりあえず、プログラムが書けたので、電流値を測定してみました。

PIC16LF19157のスリープ時の電流。12.7μA

写真のように12.7μAまで下がりました。
かなり下がりましたが、思ったほどではないかなという印象。マイクロチップの資料によると、ディープスリープ時の電流消費は、60nAで、RTCCが900nAということなので、約1μAというのが公式資料のデータです。この値は、おそらく最低電流をとったチャンピオンデータと思われます。最低電圧の1.8Vで動作したときのデータではないかと思われます。
今回は3.3Vで動作させたので、そのあたりの差なのかもしれません。あとは、使ったテスターが中華製なのでどこまで測定値に信頼性があるかという問題もあります。いずれにしても、LFにすることでスリープ時の消費電流も半分以下に抑えられるということがわかりました。まあ、PICの消費電流をいくら抑えても、電子ペーパー側の消費電流が問題になりそうなので、そのあたりを測定してみないとトータルでどのくらい消費するかはわからないですが。