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

前回は、RTCCモジュールを動かしてみるところまでやりました。
alasixosaka.hatenablog.com

しかし、RTCCモジュールが動いていることは確認できたのですが、アラーム機能が正しく動作しているかは確認できませんでした。
RTCCはのアラームが発報すると割り込みをかけることができるので、割り込み処理でLEDを点滅させてアラームの動作を確認してみます。
また、前回はMCCを使ってプログラムを作りましたが、今回はMCCを使わず、main.cのみでプログラムを完結させてみました。まあ、実のところを言うと、MCCで生成された割り込み処理関数の使い方がいまいちよくわからんかったというのが実態ですが。とくに、引数をどうするかに引っかかってしまいました。ということでブラックボックスのまま先に進んでもどうもいまいちかなと思ったので、MCCが生成するコードは参考程度にして、メインルーチンのみのプログラムにしてみました。
ソースコードはこちらです。

#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;  // MCLR以外は出力に
    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;
    
    // SYSCMD SYSCLK enabled; ACTMD ACT enabled; FVRMD FVR enabled; IOCMD IOC enabled; NVMMD NVM enabled; 
    PMD0 = 0x00;
    // TMR0MD TMR0 enabled; TMR1MD TMR1 enabled; TMR4MD TMR4 enabled; TMR2MD TMR2 enabled; 
    PMD1 = 0x00;
    // ZCDMD ZCD enabled; DACMD DAC enabled; CMP1MD CMP1 enabled; ADCMD ADC enabled; CMP2MD CMP2 enabled; RTCCMD RTCC enabled; 
    PMD2 = 0x00;
    // CCP2MD CCP2 enabled; CCP1MD CCP1 enabled; CCP4MD PWM4 enabled; CCP3MD PWM3 enabled; 
    PMD3 = 0x00;
    // CWG1MD CWG1 enabled; UART2MD EUSART2 enabled; MSSP1MD MSSP1 enabled; UART1MD EUSART enabled; 
    PMD4 = 0x00;
    // CLC3MD CLC3 enabled; CLC4MD CLC4 enabled; SMT1MD SMT1 enabled; LCDMD LCD enabled; CLC1MD CLC1 enabled; CLC2MD CLC2 enabled; 
    PMD5 = 0x00;
    
    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;

    // 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 ;    // RA2番ピンにLOWを出力する(LED OFF)
        else             RA2 = 1 ;    // RA2番ピンにHIGHを出力する(LED ON)
        RA0 = 0;
        __delay_ms(950);            
        RA0 = 1;
        __delay_ms(50);
    }
    return;
}

初期設定の所ですが、ピンの設定までは前回までと同じです。そのあとに、オシレータの設定やらPMDの設定やらがだらだらと書いてありますが、基本的になくても動くと思います。この辺はMCCのコードからとってきたものです。RTCCONbits.RTCWREN = 1;以降がRTCC周りの設定です。
まず、RTCWRENビットを1にします。こうすることでRTCCの各レジスタにアクセスが可能になります。このあたりの設定も実はPIC18F24J50とは異なっています。PIC18F24J50では、RTCERENビットを変更するために、

EECON2 = 0x55;
EECON2 = 0xAA;

の2行が必要です。これは容易にビット変更できないようになっている仕様のようですが、PIC16F1955ではその必要がなく、RTCWRENビットは簡単に変更できるようになっています。
そして、

 RTCCONbits.RTCEN = 0;

として、RTCCをいったんストップしています。

RTCCONbits.RTCCLKSEL = 0;

は、RTCCのクロックソースを選択しています。0で水晶振動子を選択します。
その次が、RTCCのレジスタ設定で

YEAR     = 0x22;   // year
MONTH    = 0x9;    // month 
WEEKDAY  = 0x0;    // weekday 
DAY      = 0x11;    // day
HOURS    = 0x21;    // hours 
MINUTES  = 0x44;    // minutes 
SECONDS  = 0x10;    // seconds 

それぞれのレジスタに値を書き込みます。値は16進数のHEXではなく、BCDで書き込むことになっています。2022年9月11日21時44分10秒に設定しています。(MCCでRTCCのプログラムを作成した日時です)
レジスタへの書き込みも、PIC18F24J50では、ウィンドウレジスターというのを使って書き込む(読みだす)ことになっていますが、PIC16F19155ではウィンドウレジスターはなく、直接各レジスタに書き込むようになっています。
次はアラームの設定です。

ALRMCONbits.ALRMEN = 0;
    // ARPT 0; 
ALRMRPT = 0x00;

まず、ALRMENビットを0にして、アラームを無効にします。
ALRMRPTはアラームのリピート回数ですが、後ほどCHIMEを設定しますので、0回に設定します。
アラームの設定値は、

 ALRMMTH  = 0x9;  // month 
 ALRMWD   = 0x0; // weekday 
 ALRMDAY  = 0x11;  // day
 ALRMHR   = 0x21; // hours 
 ALRMMIN  = 0x44;  // minutes 
 ALRMSEC  = 0x10;  // seconds 

と各種の値を設定していますが、設定値に意味はありません。ここも、MCCが生成したプログラムからそのままコピーしています。
アラームの設定が終わったら

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

ALRMENビットを1にしてアラームを有効にしています。
そして、ALRMCONレジスタに0xC8を書き込んでいます。
第7ビットがさっき立てたALRMENビットで、また1を書き込んでいます。さっきの命令は意味がないように思うのですが、この辺もMCCのコードそのままなので...。第6ビットはCHIMEを設定するビットでビットを立てることでCHIME機能をONにします。こうすることでアラームが永久に繰り返されます。
アラームの間隔を設定するのが、第2ビットから第5ビットで、0010を書き込んでいるので10秒ごとにアラームが発報します。第1ビットと第0ビットは無効です。この設定で10秒間隔のアラームがずっと繰り返されることになります。
次はキャリブレーションの設定で

// CAL 0; 
RTCCAL = 0x00;

キャリブレーションは0に設定しています。キャリブレーションを適切に設定することで月差が数秒程度まで調整することができます。プログラムでどうするかはちょっと難しい問題でしょうが。一定間隔で正しい時刻を入手して、誤差をもとに新しいキャリブレーション値を設定するといったところでしょうか?
次は、

// Enable RTCC
RTCCONbits.RTCEN = 1;
while(!RTCCONbits.RTCEN);

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

で、RTCENビットを立ててRTCCを作動させています。一応、ビットが立ったのを確認するまで待ちます。
そして、RTCWRENビットをクリアして、レジスタへの書き込みを禁止します。
その次が割り込みに関する部分で

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

// Enable RTCC interrupt
PIE8bits.RTCCIE = 1;
    
INTCONbits.PEIE   = 1 ;             // 周辺装置割り込み有効
INTCONbits.GIE    = 1 ;             // 全割込み処理を許可する

まず、RTCCIFを0にして割り込みフラグをクリアします。そして、RTCCIEを立ててRTCC割り込みを許可、そのあとPEIEを立てて周辺装置割り込みを許可、最後にGIE立てて全割り込みを許可しています。
割り込み処理ルーチンは、

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

となっていて、割り込みがかかるごとに変数LEDflgを変更しています。
メインルーチンのループの部分は、

while(1){
        if (LEDflg == 0) RA2 = 0 ;    // RA2番ピンにLOWを出力する(LED OFF)
        else             RA2 = 1 ;    // RA2番ピンにHIGHを出力する(LED ON)
        RA0 = 0;
        __delay_ms(950);            
        RA0 = 1;
        __delay_ms(50);
    }

となっていて、LEDflgの値によって、RA2ピンのLEDをオン、オフしています。残りの部分は通常のLチカです。

コンパイラーのエラーではまる。

これでちゃんと動くのですが、最初はコンパイラーがエラーを出してコンパイルしてくれませんでした。理由は、一部のレジスタコンパイラに認識されないためです。インクルードの所で、”#include <PIC16f19155.h>” を書いているのは対策のためです。調子が悪いとこいつがそもそも認識されません。
そして、RTCC関連のレジスタ類は全部認識されません。

コンパイルエラーの状態

ネットでググっていろいろ対策を試みてみたのですが、いずれも効果なく、どうもXC8コンパイラかMPLAB XIDEのどちらかの動作が不安定なためのようです。
新しいプロジェクトを作成し、エラーになったプログラムを丸ごとコピペしたらちゃんと動いたりします。今のところ、これといった対処法がないので、プロジェクトを新規に作成する以外に手がない状態です。
あまり症状がひどい場合は、前のバージョンにダウングレードすることも考えないといけないかもしれません。
次回は、スリープを実行して、どのくらい消費電流を削減できるか試してみようと思っています。