今回は具体的なサンプル・プログラムを説明します。
●ソース掲載(“Rtc4543P1.ino”)
少し長いですが、関数毎に見ていくとそんなに難しくありません。
動作の概要は、最初に日時(固定値)をRTCへ書き込んで、それをすぐに読み出してシリアルで送信します。そこから一定時間経過後に再びRTCから日時を読み出してシリアルで送信し、設定した日時から一定時間経過した日時が表示されることを確認します。
/* リアルタイムクロック RTC-4543 評価 WSN366使用 copyright (c) 2013 www.wsnak.com 13/11/03 新規作成 */ /* ディジタル・ポート・アサイン RTC-4543制御ポート D2: SCK D3: DATA D4: WR D5: CE */ #define ON HIGH #define OFF LOW #define SCK_H digitalWrite(2, HIGH) #define SCK_L digitalWrite(2, LOW) #define WR_W digitalWrite(4, HIGH) #define WR_R digitalWrite(4, LOW) #define CE_ENA digitalWrite(5, HIGH) #define CE_DIS digitalWrite(5, LOW) #define DIR_IN pinMode(3, INPUT) // DATA (I/O) #define DIR_OUT pinMode(3, OUTPUT) #define GET_BIT digitalRead(3) #define PUT_BIT(x) digitalWrite(3, x) // 日時変数(読み出し用) byte dSec; byte dMin; byte dHour; byte dWeek; byte dDay; byte dMon; byte dYear; // 日時変数(書き込み用) byte sSec; byte sMin; byte sHour; byte sWeek; byte sDay; byte sMon; byte sYear; char strBuf[16]; // // 初期化 // void setup(void) { Serial.begin(19200); // シリアル初期化 pinMode(2, OUTPUT); // SCK pinMode(3, OUTPUT); // DATA (I/O) pinMode(4, OUTPUT); // RW pinMode(5, OUTPUT); // CE delay(500); if(getFDTBit()) { // FDTビット=1(日時不正) // 日付設定 sSec = 0; sMin = 0x34; sHour = 0x12; sWeek = 0x1; sDay = 0x24; sMon = 0x12; sYear = 0x13; putString(0); // 日時設定 Serial.println("set date & time"); } else { Serial.println("now"); } // 確認のためすぐに読み出す getString(); // 日時取得 printBcdDate(dYear, dMon, dDay); printBcdTime(dHour, dMin, dSec); delay(65000); Serial.println("after 65sec"); getString(); // 日時取得 printBcdDate(dYear, dMon, dDay); printBcdTime(dHour, dMin, dSec); } // // 年月日表示(BCD入力) // void printBcdDate(byte yr, byte mo, byte dy) { sprintf(strBuf, "%d%d/%d%d/%d%d ", yr>>4, yr&0x0F, mo>>4, mo&0x0F, dy>>4, dy&0x0F); Serial.print(strBuf); } // // 時刻表示(BCD入力) // void printBcdTime(byte hr, byte mn, byte sc) { sprintf(strBuf, "%d%d:%d%d:%d%d", hr>>4, hr&0x0F, mn>>4, mn&0x0F, sc>>4, sc&0x0F); Serial.println(strBuf); } // // メインループ // void loop(void) { } // // 8ビット取得 // byte get8Bit(void) { byte i, dat = 0; for(i = 0; i < 8; i++) { dat >>= 1; SCK_H; // SCKパルス SCK_L; if(GET_BIT) { // SCK立ち下がりで読み出し dat |= 0x80; // b7 } } return dat; } // // 4ビット取得 // byte get4Bit(void) { byte i, dat = 0; for(i = 0; i < 4; i++) { dat >>= 1; SCK_H; // SCKパルス SCK_L; if(GET_BIT) { // SCK立ち下がりで読み出し dat |= 0x08; // b3 } } return dat; } // // 54ビット全データ読み出し // byte getString(void) { byte dat, fdt; DIR_IN; WR_R; CE_ENA; delayMicroseconds(1); // セットアップタイム dat = get8Bit(); if(dat & 0x80) { fdt = 1; } else { fdt = 0; } dSec = dat & 0x7F; // FDTビットをクリア dMin = get8Bit(); dHour = get8Bit(); dWeek = get4Bit(); dDay = get8Bit(); dMon = get8Bit(); dYear = get8Bit(); CE_DIS; return fdt; // FDTビットの状態を返す } // // FDTビットの状態を読み出す // byte getFDTBit(void) { byte dat, fdt; DIR_IN; WR_R; CE_ENA; delayMicroseconds(1); // セットアップタイム dat = get8Bit(); // 最初の8bit取得 if(dat & 0x80) { fdt = 1; } else { fdt = 0; } CE_DIS; return fdt; // FDTビットの状態を返す } // // 8ビット出力 // void put8Bit(byte dat) { byte i; for(i = 0; i < 8; i++) { PUT_BIT(dat & 0x01); // DATAビット出力 (LSB) delayMicroseconds(1); // セットアップタイム SCK_H; // SCKパルス SCK_L; dat >>= 1; } } // // 4ビット出力 // void put4Bit(byte dat) { byte i; for(i = 0; i < 4; i++) { PUT_BIT(dat & 0x01); // DATAビット出力 (LSB) delayMicroseconds(1); // セットアップタイム SCK_H; // SCKパルス SCK_L; dat >>= 1; } } // // 54ビット全データ書き込み // byte putString(byte fdt) { byte dat; DIR_OUT; WR_W; CE_ENA; delayMicroseconds(1); // セットアップタイム dat = (fdt << 7) | sSec; put8Bit(dat); put8Bit(sMin); put8Bit(sHour); put4Bit(sWeek); put8Bit(sDay); put8Bit(sMon); put8Bit(sYear); CE_DIS; }
20~33行目はシリアル関係のポートを操作するマクロです。たとえば、”SCK_H”はSCKに接続されたポートをHレベルに、”SCK_L”は同ポートをLレベルに設定します。
“DIR_IN”/”DIR_OUT”はDATAポートの入出力を切り替えるマクロです。”GET_BIT”はDATAポートの状態を読み出すマクロ、”PUT_BIT”はDATAポートに値(0/1)を書き出すマクロです。
69行目で”getFDTBit()”によりFDTビットの状態を調べ、”1″なら日時不正(日時未設定)、”0″なら日時設定済と判断し、”1″のときは固定値を設定します。
87行目ですぐに日時を読み出して、現在日時または今設定された日時を表示(シリアル送信)します。”printBcdData()”、”printBcdTime()”はグローバル変数へ設定された日付または時間をシリアル送信する関数です。
91行目で約65秒待ってから94行目で日時を読み出して、それを表示(シリアル送信)します。
なお、リセットした場合や、RTCにバックアップ電池がつながった状態で電源の入り切りをした場合でも、RTCは作動し続けています。その場合は、プログラム初期化時の固定値設定はスキップされます。
163行目の”getString()”は54ビットの日時データを一括で読み出して、グローバル変数のdSec, dMin, … dYearへ読み込みます。
246行目の”putString()”は54ビットのsSec, sMin, … sYearのグローバル変数の内容を書き出します。
●日時の変数値(BCDコード)の扱い
書き込む値、読み出した値はBCDコードですので、16進数の1桁で0~9を表していることに注意してください。たとえば、0×12は12、0×24は24を表します。
今回設定している固定値は秒、分、時、曜、日、月、年の順に0, 0×34, 0×12, 0×1, 0×24, 0×12, 0×13ですので、13年12月24日の12時34分0秒ということになります。
表示の際も16進数の2桁を1桁ずつ0~9までの数値として表示しています(103~105行目と112~114行目)。
曜日の値は1~7で循環しますが、どの数値が何曜日というのは決まっていません。RTC-4543では単純に循環するカウンタという扱いですが(曜日カウンタは自動ではない)、奨励値として1が日曜日、2が月曜日…となっています。今回は無視しています。
●結果
初めて実行する際は”set date & time”、”13/12/24 12:34:00″と表示され、約65秒後に”13/12/24 12:35:04″と表示されました。”05″秒でないのは、ディレイの誤差による読出しタイミングのずれのためだと思います。
そのあと、リセットスイッチを押すと、”now”、”13/12/24 xx:xx:xx”と表示され、初期固定値の設定がスキップされたことが確認できます。
バックアップ用電池を使っている場合は、Arduino側の電源を切って、再投入したあとに実行させると”now”、”13/12/24 xx:xx:xx”と表示され、時計が継続して作動していることが確認できます。翌日の同時刻に見ると前日より「日」が+1されているはずです。
●その他
日時の直接設定、読出しは簡単なのですが、問題は日時の表示とスイッチ操作などによる日時の設定の部分です。ここが面倒ですね。
表示部や操作部なしで、シリアルで日時を設定させることも可能です。組み込み用の場合はそういう使い方もありかと思います。
WSN366は電池を抜かない限りポータブル(単独で作動)ですので、日時を設定して別の場所に単体でもっていって、そこで他の機器にセットして使うということも可能です。