AVRのI2Cモジュール / リアルタイムクロックDS3231 / アラームを使う / アラームフラグをポーリングで監視する

DS3231にはアラームが2系統ある。どちらかのアラームが発動すると、INT/SQW端子がローになる。

  • アラーム1は
    「毎秒(正秒)」
    「1分間に1回(秒で指定)」
    「1時間に1回(分、秒で指定)」
    「いち日に1回(時、分、秒で指定)」
    「ひと月に1回(日、時、分、秒で指定)」
    「1週間に1回(曜、時、分、秒で指定)」
    のいずれかのタイミングでアラームが発動できる。
  • アラーム2は
    「毎分(正分)」
    「1時間に1回(分で指定)」
    「いち日に1回(時、分で指定)」
    「ひと月に1回(日、時、分で指定)」
    「1週間に1回(曜、時、分で指定)」
    のいずれかのタイミングでアラームが発動できる。

ここでは、アラーム1を毎分5秒のときに、アラーム2を毎分0秒のときに発動させて、そのタイミングでアラームフラグが立つかどうかをI2C経由でポーリングして確かめる。同時にロジアナでINT/SQW端子を観測する。
f:id:ti-nspire:20200206093927p:plain:w400 f:id:ti-nspire:20200206093431p:plain

#include <avr/io.h>
#include <util/delay.h>
#include "USART.h"
#include "myI2Cv2.h"
#include "DS3231.h"

int main(){
    initUSART();

    DS3231 rtc;         // リアルタイムクロックDS3231 (I2Cスレーブ)を実体化する。
    myI2Cv2.enable(80); // システムクロックの80分周をI2CクロックとしてAVRのI2Cモジュールを有効化する。

    rtc.setDateTimeMan(); // USART経由で手動で時刻合わせをして、
    
    printString("***** Set the alarm 1 *****\n"); // USART経由で手動でアラーム1の発動タイミングを設定して、
    rtc.setAlarmMan(1);
    printString("***** Set the alarm 2 *****\n"); // USART経由で手動でアラーム2の発動タイミングを設定して、
    rtc.setAlarmMan(2);

    rtc.enableAlarm(1); // アラーム1を有効化して、
    rtc.enableAlarm(2); // アラーム2を有効化して、

    while(1){
        if(rtc.alarmActivated(1)){ // アラーム1が発動したら、
                                   // 何かをして、
            rtc.getDateTimeBCD(); rtc.printDateTime(); printString(" Alarm 1 has been activated.\n");
            rtc.clearAlarmFlag(1); // アラーム1発動フラグを解除して、
        }
        if(rtc.alarmActivated(2)){ // アラーム2が発動したら、
                                   // 何かをして、
            rtc.getDateTimeBCD(); rtc.printDateTime(); printString(" Alarm 2 has been activated.\n");
            rtc.clearAlarmFlag(2); // アラーム2発動フラグを解除して、
        }

        _delay_ms(200); // またアラームの発動を待つ。を繰り返す。
    }

    myI2Cv2.disable();
    return 0;
}
    uint8_t alarmActivated(uint8_t whichAlarm); // アラームが出たかどうか
    void    enableAlarm(uint8_t whichAlarm);    // アラームを有効化する。
    void    disableAlarm(uint8_t whichAlarm);   // アラームを無効化する。
    void    clearAlarmFlag(uint8_t whichAlarm); // アラームフラグをクリアする。
    void    setAlarmBCD(uint8_t whichAlarm, uint8_t *buff); // アラーム発動タイミングを設定。buffは{秒,分,時,曜/日}または{分,時,曜/日}。
    void    setAlarmMan(uint8_t whichAlarm); // USART経由で手動でアラーム発動タイミングを設定。setAlarmBCD()も含めて実行する。
uint8_t DS3231::alarmActivated(uint8_t whichAlarm){
    myI2Cv2.start();                   // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_W);      // スレーブに呼びかけて、writeであることを示して、
    myI2Cv2.writeByte(CONTROL_STATUS); // スレーブ側のレジスタポインタをセットして、

    myI2Cv2.start();                      // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_R);         // スレーブに呼びかけて、readであることを示して、
    uint8_t cs = myI2Cv2.readByteNACK();  // 目的のCONTROL_STATUSレジスタの内容を読み出して、

    myI2Cv2.stop(); // ストップコンディションを生成して、

/*    if     (whichAlarm==1){return (cs & 0b01) == 0b01;} // アラーム1が出たかどうかを返す。
    else if(whichAlarm==2){return (cs & 0b10) == 0b10;} // アラーム2が出たかどうかを返す。*/
    return (cs & whichAlarm) && 1; // アラームが出たかどうかを返す。
}
void DS3231::enableAlarm(uint8_t whichAlarm){
    clearAlarmFlag(whichAlarm); // 念のためアラームフラグをクリアしておく。

    myI2Cv2.start();              // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_W); // スレーブに呼びかけて、writeであることを示して、
    myI2Cv2.writeByte(CONTROL);   // スレーブ側のレジスタポインタをセットして、

    myI2Cv2.start();                      // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_R);         // スレーブに呼びかけて、readであることを示して、
    uint8_t cnt = myI2Cv2.readByteNACK(); // 目的のCONTROLレジスタの内容を読み出して、

/*    if     (whichAlarm==1){cnt |= (0b101);} // INTCNビットとA1IEビットとをセットする。
    else if(whichAlarm==2){cnt |= (0b110);} // INTCNビットとA2Eビットとをセットする。*/
    cnt |= (0b100 | whichAlarm); // INTCNビット(ビット[2])と、A2EビットかA1Eビットかいずれかとをセットする。

    myI2Cv2.start();              // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_W); // スレーブに呼びかけて、writeであることを示して、
    myI2Cv2.writeByte(CONTROL);   // スレーブ側のレジスタポインタをセットして、
    myI2Cv2.writeByte(cnt);       // そのレジスタを書き換えて、

    myI2Cv2.stop(); // ストップコンディションを生成する。
}
void DS3231::disableAlarm(uint8_t whichAlarm){
    myI2Cv2.start();              // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_W); // スレーブに呼びかけて、writeであることを示して、
    myI2Cv2.writeByte(CONTROL);   // スレーブ側のレジスタポインタをセットして、

    myI2Cv2.start();                      // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_R);         // スレーブに呼びかけて、readであることを示して、
    uint8_t cnt = myI2Cv2.readByteNACK(); // 目的のCONTROLレジスタの内容を読み出して、

//    cnt &= ~(0b111); // INTCN、A2IE、A1IEの各ビットをクリアして、
    cnt &= ~whichAlarm; // A2IE、A1IEのどちらかのビットをクリアして、

    myI2Cv2.start();              // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_W); // スレーブに呼びかけて、writeであることを示して、
    myI2Cv2.writeByte(CONTROL);   // スレーブ側のレジスタポインタをセットして、
    myI2Cv2.writeByte(cnt);       // そのレジスタを書き換えて、

    myI2Cv2.stop(); // ストップコンディションを生成する。
}
void DS3231::clearAlarmFlag(uint8_t whichAlarm){
    myI2Cv2.start();                   // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_W);      // スレーブに呼びかけて、writeであることを示して、
    myI2Cv2.writeByte(CONTROL_STATUS); // スレーブ側のレジスタポインタをセットして、

    myI2Cv2.start();                     // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_R);        // スレーブに呼びかけて、readであることを示して、
    uint8_t cs = myI2Cv2.readByteNACK(); // 目的のCONTROLレジスタの内容を読み出して、

/*    if     (whichAlarm==1){cs &= ~(0b01);} // A1Fビットをクリアして、
    else if(whichAlarm==2){cs &= ~(0b10);} // A2Fビットをクリアして、*/
    cs &= ~whichAlarm; // A2FビットまたはA1Fビットをクリアして、

    myI2Cv2.start();                   // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_W);      // スレーブに呼びかけて、writeであることを示して、
    myI2Cv2.writeByte(CONTROL_STATUS); // スレーブ側のレジスタポインタをセットして、
    myI2Cv2.writeByte(cs);             // そのレジスタを書き換えて、

    myI2Cv2.stop(); // ストップコンディションを生成する。
}
void DS3231::setAlarmBCD(uint8_t whichAlarm, uint8_t *buff){ //
    myI2Cv2.start();                      // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_W);         // スレーブに呼びかけて、writeであることを示して、
    if     (whichAlarm==1){myI2Cv2.writeByte(ALARM_1_SECOND); myI2Cv2.writeBytes(4, buff);}
    else if(whichAlarm==2){myI2Cv2.writeByte(ALARM_2_MINUTE); myI2Cv2.writeBytes(3, buff);}
    myI2Cv2.stop();                       // ストップコンディションを生成する。
}
void DS3231::setAlarmMan(uint8_t whichAlarm){
    if(whichAlarm==1){
        uint8_t buff[4] = {0x80, 0x80, 0x80, 0x80}; // マスクビット(ビット[7])はとりあえず最初に全部マスクしておく。

        printString("Select from the followings:\n");
        printString("0: Alarm once per second\n");
        printString("1: Alarm when seconds match\n");
        printString("2: Alarm when minutes and seconds match\n");
        printString("3: Alarm when hours, minutes, and seconds match\n");
        printString("4: Alarm when date, hours, minutes, and seconds match\n");
        printString("5: Alarm when day, hours, minutes, and seconds match\n");

        uint8_t mode = contain(getNumber(), 0, 5);
        switch(mode){
        case 0: break;
        case 1: printString("Set seconds, 0-59: "); buff[0] = dec2bcd(contain(getNumber(), 0, 59));
                break;
        case 2: printString("Set minutes, 0-59: "); buff[1] = dec2bcd(contain(getNumber(), 0, 59));
                printString("Set seconds, 0-59: "); buff[0] = dec2bcd(contain(getNumber(), 0, 59));
                break;
        case 3: printString("Set hours, 0-23: ")  ; buff[2] = dec2bcd(contain(getNumber(), 0, 23));
                printString("Set minute, 0-59: ") ; buff[1] = dec2bcd(contain(getNumber(), 0, 59));
                printString("Set seconds, 0-59: "); buff[0] = dec2bcd(contain(getNumber(), 0, 59));
                break;
        case 4: printString("Set a date, 1-31: ") ; buff[3] = dec2bcd(contain(getNumber(), 1, 31));
                printString("Set hours, 0-23: ")  ; buff[2] = dec2bcd(contain(getNumber(), 0, 23));
                printString("Set minutes, 0-59: "); buff[1] = dec2bcd(contain(getNumber(), 0, 59));
                printString("Set seconds, 0-59: "); buff[0] = dec2bcd(contain(getNumber(), 0, 59));
                break;
        case 5: printString("Set a day, 1-7, e.g. Mon=1, Sun=7: "); buff[3] =         contain(getNumber(), 1,  7);
                printString("Set hours, 0-23: ")                  ; buff[2] = dec2bcd(contain(getNumber(), 0, 23));
                printString("Set minutes, 0-59: ")                ; buff[1] = dec2bcd(contain(getNumber(), 0, 59));
                printString("Set seconds, 0-59: ")                ; buff[0] = dec2bcd(contain(getNumber(), 0, 59));
                buff[3] |= (1 << 6); // 曜ビットに1を立てる。
                break;
        default:break;
        }
        setAlarmBCD(1, buff);
    }
    if(whichAlarm==2){
        uint8_t buff[3] = {0x80, 0x80, 0x80}; // マスクビット(ビット[7])はとりあえず最初に全部マスクしておく。

        printString("Select from the followings:\n");
        printString("0: Alarm once per minute\n");
        printString("1: Alarm when minutes match\n");
        printString("2: Alarm when hours and minutes match\n");
        printString("3: Alarm when date, hours, and minutes match\n");
        printString("4: Alarm when day, hours, and minutes match\n");

        uint8_t mode = contain(getNumber(), 0, 4);
        switch(mode){
        case 0: break;
        case 1: printString("Set minutes, 0-59: "); buff[0] = dec2bcd(contain(getNumber(), 0, 59));
                break;
        case 2: printString("Set hours, 0-23: ")  ; buff[1] = dec2bcd(contain(getNumber(), 0, 23));
                printString("Set minutes, 0-59: "); buff[0] = dec2bcd(contain(getNumber(), 0, 59));
                break;
        case 3: printString("Set a date, 1-31: ") ; buff[2] = dec2bcd(contain(getNumber(), 1, 31));
                printString("Set hours, 0-23: ")  ; buff[1] = dec2bcd(contain(getNumber(), 0, 23));
                printString("Set minutes, 0-59: "); buff[0] = dec2bcd(contain(getNumber(), 0, 59));
                break;
        case 4: printString("Set a day, 1-7, e.g. Mon=1, Sun=7: "); buff[2] =         contain(getNumber(), 1,  7);
                printString("Set hours, 0-23: ")                  ; buff[1] = dec2bcd(contain(getNumber(), 0, 23));
                printString("Set minutes, 0-59: ")                ; buff[0] = dec2bcd(contain(getNumber(), 0, 59));
                buff[2] |= (1 << 6); // 曜ビットに1を立てる。
                break;
        default:break;
        }
        setAlarmBCD(2, buff);
    }
}