AVRのI2Cモジュール / リアルタイムクロックDS3231 / 内部温度を見てみる

DS3231には、内蔵クリスタルを温度補償するための温度センサー(10ビット分解能)が内蔵されている。その温度を見てみる。値は64秒ごとに更新される。
f:id:ti-nspire:20200205092628p:plain:w500
f:id:ti-nspire:20200205105608p:plain:h250

#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); // AVRのI2Cモジュールを有効化する。

    while(1){
        printFloat(rtc.getTempFloat()); printString(" deg C\n");
        _delay_ms(1000);
    }

    myI2Cv2.disable();
    return 0;
}
    uint16_t getTempRaw();
    float    getTempFloat();
    uint8_t  isCalibrating();
    void     calibrate(); // TCXO (temperature-compensated crystal oscillator)を強制的に較正する。
uint16_t DS3231::getTempRaw(){
    uint8_t buff[2];

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

    myI2Cv2.start();                // スタートコンディションを生成して、
    myI2Cv2.writeByte(ADDRESS_R);   // スレーブに呼びかけて、readであることを示して、 
    myI2Cv2.readBytesNACK(2, buff); // 2バイトぶんの温度データを読み出して、

    myI2Cv2.stop(); // ストップコンディションを生成して、
    
    return ((uint16_t)buff[0] << 8) | (buff[1] & 0xC0); // 16ビットに並べ換えて返す(有効なのは上位10ビット)。
}
float DS3231::getTempFloat(){
    return (float)getTempRaw() / 256.0;
}
uint8_t DS3231::isCalibrating(){
    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(); // ストップコンディションを生成して、

    return (cs & (0b100)) == 0b100; // 目的のビット[2] (BSYビット)が立っているかどうか(較正中かどうか)を返す。
}
void DS3231::calibrate(){
    if(!isCalibrating()){             // (較正中でないときだけ実行する)
        myI2Cv2.start();              // スタートコンディションを生成して、
        myI2Cv2.writeByte(ADDRESS_W); // スレーブに呼びかけて、writeであることを示して、 
        myI2Cv2.writeByte(CONTROL);   // スレーブ側のレジスタポインタをセットして、

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

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

        myI2Cv2.stop(); // ストップコンディションを生成する。
    }
}