AVRのI2CとSPI / 温度センサーLM75BDとEEPROMとによるデータロガー / ちゃんとしたデータロガーにする

pp.361ff.
今度はテキストを参考にメニューを追加するなどしてもっとちゃんとしたデータロガーにしてみる。これでChapter 17 I2Cはおおむね終わりである。

一度中断してもまたデータロギングが再開できるよう下のようにしておく。

  • EEPROMの0~1H番地に、次に書き込むべきアドレスを記録しておく。
  • EEPROMの2~3H番地に、前回までのデータ取得間隔(秒数)を記録しておく。

f:id:ti-nspire:20200128095359p:plain:h270 f:id:ti-nspire:20200128095557p:plain:h270

メインファイル:

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

#include "myI2Cv2.h"
#include "LM75v2.h"

#include "mySPIv2.h"
#include "EEPROM25_256v2.h"

#define CURRENT_LOCATION_POINTER  0 // 次に書き込むべきEEPROMアドレスをEEPROMの0-1h番地に記録しておく。
#define SECONDS_POINTER           2 // 前回までのデータの取得間隔    をEEPROMの2-3h番地に記録しておく。
#define MEMORY_START              4 // したがってデータはEEPROMの4h番地以降に記録することにする。
#define MENU_DELAY                5 // 何も操作しないで何秒経過したらメニューから抜けるかを決めておく。
#define DEFAULT_DELAY            30 // データ取得間隔のデフォルト秒数。
#define DEFAULT_DELAY_MIN         5 // データ取得間隔の最短デフォルト秒数。
#define DEFAULT_DELAY_MAX       600 // データ取得間隔の最長デフォルト秒数。

int main(){
    initUSART();

    LM75v2 tempSensor(3); // ユーザー定義アドレス3 (0~7)のスレーブ(LM75)を実体化する。
    myI2Cv2.enable(80);   // システムクロックの80分周をI2CクロックとしてAVRのI2Cモジュールを有効化する。

    EEPROM25_256v2 rom('B', 2); // PB2を!SS端子とするスレーブ(EEPROM)を実体化する。PB3~PB5は!SS端子に指定してはならない。
    mySPI.enable(128, 0, 'M');  // (システムクロックの分周比, モード0, MSBファースト)でSPIモジュールを有効化する。

    uint16_t currentMemoryLocation = rom.readWord(CURRENT_LOCATION_POINTER); // 次に記録すべきEEPROMアドレスをEEPROMから取り込む。
    uint16_t secondsDelay          = rom.readWord(SECONDS_POINTER);          // 前回までの読み取り間隔(秒数)をEEPROMから取り込む。
    
    uint8_t  enterMenu; // メニューへの出入りを決めるためのフラグ。

    // メニューを表示する。
    printString("Push any key within "); printNibble(MENU_DELAY); printString(" seconds to enter the menu,\n");
    printString("Or measurement will start.\n\n");
    _delay_ms(1000 * MENU_DELAY);                                         // 指定秒数以内に
    (bit_is_set(UCSR0A, RXC0) && (UDR0)) ? enterMenu = 1 : enterMenu = 0; // 何かキーが押されたらメニューに入る。
    while(enterMenu){

        // 現在EEPROMに何バイト記録してあるかを表示する。
        if(currentMemoryLocation < MEMORY_START){currentMemoryLocation = MEMORY_START;}
        printWord(currentMemoryLocation - MEMORY_START); printString(" byte(s) in the log.\n");

        // 現在設定してある読み取り間隔(秒数)を表示する。
        printWord(secondsDelay); printString(" seconds between readings.\n\n");

        printString("Push [<] to decrease the measurement interval.\n");
        printString("Push [>] to increase the measurement interval.\n");
        printString("Push [?] to reset the measurement interval to "); printWord(DEFAULT_DELAY); printString(" sec.\n");
        printString("Push [f] to print out the log (float values) over USART.\n");
        printString("Push [w] to print out the log (word values) over USART.\n");
        printString("Push [e] to erase the EEPROM.\n");
        printString("Push [s] to start logging.\n\n");
        switch(receiveByte()){
        case ',': // +Shift = '<'
            if(secondsDelay > DEFAULT_DELAY_MIN){
                rom.writeWord(SECONDS_POINTER, secondsDelay -= 5);
            }
            break;
        case '.': // +Shift = '>'
            if(secondsDelay < DEFAULT_DELAY_MAX){
                rom.writeWord(SECONDS_POINTER, secondsDelay += 5);
            }
            break;
        case '/': // +Shift = '?'
            rom.writeWord(SECONDS_POINTER, secondsDelay = DEFAULT_DELAY);
            break;
        case 'f':
            printString("TEMPERATURE (float values) DUMPED:\n");
            for(uint16_t i=MEMORY_START; i<currentMemoryLocation; i+=2){ // 温度データが2バイトなのでアドレスは2ずつインクリメントする。
                printFloat(rom.readWord(i) / 256.0); printString("\n");
            }
            break;
        case 'w':
            printString("TEMPERATURE (word values) DUMPED:\n");
            for(uint16_t i=MEMORY_START; i<currentMemoryLocation; i+=2){ // 温度データが2バイトなのでアドレスは2ずつインクリメントする。
                printWord(rom.readWord(i)); printString("\n");
            }
            break;
        case 'e':
            printString("CAUTION!! [e] will erase all of the data on your EEPROM.\n");
            printString("Push [y] to continue.\n");
            printString("Push any other key to cancel.\n\n");
            if(receiveByte()=='y'){
                printString("Deleting... (taking a little time)\n");
                rom.clearAll();                                        // データが全部消えてしまうので、
                currentMemoryLocation = MEMORY_START;                  // 次に書き込むべきEEPROMアドレスを再セットし、
                rom.writeWord(CURRENT_LOCATION_POINTER, MEMORY_START);
                rom.writeWord(SECONDS_POINTER, secondsDelay);          // 取得間隔(秒数)も再セットしておく。
            }else{
                printString("Deletion cancelled.\n");
            }
            break;
        case 's':
            enterMenu = 0; // switch()からbreakしたあとwhile()の条件が偽になるのでメニューから抜ける。
            break;
        default:
            printString("Push a correct key.\n");
            break;
        }
    }

    // ロギングを始める。
    int16_t temperature;
    printString("OK, let's start logging...\n");
    printString("To stop measuring, push the reset button on your AVR.\n\n");
    printString("CURRENT TEMPERATURES:\n");
    while(1){
        temperature = tempSensor.getTempRaw();             // 温度データ(2バイト)を取得して、
        rom.writeWord(currentMemoryLocation, temperature); // その2バイトをEEPROMに書き込んで
        printFloat(tempSensor.raw2float(temperature));     // 確認のためターミナルに表示する。
        printString("\n");

        if(currentMemoryLocation < BYTES_MAX-2){                                 // EEPROMが満杯でなければ、
            rom.writeWord(CURRENT_LOCATION_POINTER, currentMemoryLocation += 2); // 次に書き込むべきアドレスに更新するが、
        }else{                                                                   // EEPROMが満杯なら、
            printString("Your EEPROM is full, and the measurement loop will exit.\n");
            break;                                                               // プログラムを停止する。
        }

        for(int i=0; i<secondsDelay; i++) _delay_ms(1000);
    }

    mySPI.disable();
    myI2Cv2.disable();
    return 0;
}

f:id:ti-nspire:20200130101606p:plain:w500
f:id:ti-nspire:20200131095120p:plain:w500