/* リアルタイムクロックRTC8564BNと秋月のI2C液晶AQM0802A(8文字2行)のテスト CPUをパワーダウンモードでスリープさせ、RTCからの割込みで処理を起動 RTCの時刻合わせ機能付き RTC8564ライブラりは修正が必要 2016/2/2 ラジオペンチ http://radiopench.blog96.fc2.com/ */ #include // 待機中はスリープさせるために使用 #include // #include // I2Cの液晶用ライブラリ オレ工房さん http://ore-kb.net/archives/195 #include // なんでもつくっちゃう、かもさんのRTCライブラリ #include ST7032 lcd; char dtString[20] = "yyyy/mm/dd hh:mm:ss"; // テンプレート用文字列で初期化 byte RtcADR = 0XA2 >> 1; int entB = 11; // EnterボタンのピンNo. int selB = 9; // SelectボタンのピンNo. void setup() { pinMode(2, INPUT); // RTCの /INT信号を接続 digitalWrite(2, HIGH); // 相手はオープンドレインなのでプルアップ pinMode(13, OUTPUT); // 動作表示LED pinMode(entB, INPUT); // Enterボタン pinMode(selB, INPUT); // Selectボタン digitalWrite(entB, HIGH); // プルアップ digitalWrite(selB, HIGH); // プルアップ Serial.begin(9600); lcd.begin(8, 2); // 8文字2行のI2C液晶を使用 lcd.setContrast(32); // 液晶コントラスト設定 Rtc.begin(); if (digitalRead(entB) == LOW) { // リセット時にEnterボタンが押されていたら ClockSet(); // RTCの時刻を合わせる } // AdjustRtcTime(); // 時刻合わせが必要な場合に使う SetRtcTimer(); // 定周期タイマーの設定 set_sleep_mode(SLEEP_MODE_PWR_DOWN); // スリープはパワーダウンモードで行う } void loop() { waitExtIRQ(); // RTCからの1秒パルスを待つ Rtc.available(); // 時刻を取得 convString(); // RTCの時刻(BCD)を文字列に変換してdtStringに格納 Serial.println(dtString); // シリアルに書き出し dispClock(); // 液晶に表示 delay(20); // シリアル出力完了まで待つ } void convString() { // RTCの2バイトのBCDを文字に変換し時刻の文字列を作る byte x; dtString[0] = '2'; dtString[1] = '0'; x = Rtc.years(); dtString[2] = upper2chr(x); dtString[3] = lower2chr(x); x = Rtc.months(); dtString[5] = upper2chr(x); dtString[6] = lower2chr(x); x = Rtc.days(); dtString[8] = upper2chr(x); dtString[9] = lower2chr(x); x = Rtc.hours(); dtString[11] = upper2chr(x); dtString[12] = lower2chr(x); x = Rtc.minutes(); dtString[14] = upper2chr(x); dtString[15] = lower2chr(x); x = Rtc.seconds(); dtString[17] = upper2chr(x); dtString[18] = lower2chr(x); } char upper2chr(byte x) { // 上位をAsciiコードに変換 return (x >> 4) + 0x30; } char lower2chr(byte x) { // 下位をAsciiコードに変換 return (x & 0x0f) + 0x30; } void waitExtIRQ() { ADCSRA &= ~(1 << ADEN); // ADENビットをクリアしてADCを停止(120μA節約) attachInterrupt(0, trigerd, FALLING); // Pin2のネガエッジで割込み sleep_mode(); // 指定したモードでスリープ detachInterrupt(0); ADCSRA |= (1 << ADEN); // ADC動作再開 } void trigerd() { // 割込み時の処理 } void AdjustRtcTime() { // 時計の時刻決め打ち用。数値は修正要 byte date_and_time[7]; date_and_time[0] = 0x00; // 0秒 date_and_time[1] = 0x48; // 40分 date_and_time[2] = 0x11; // 11時 date_and_time[3] = 0x01; // 1日 date_and_time[4] = 0x04; // X曜日 date_and_time[5] = 0x02; // 2月 date_and_time[6] = 0x16; // 16年 Rtc.sync(date_and_time); } void SetRtcTimer() { // RTCの設定 Wire.beginTransmission(RtcADR); Wire.write(0x0e); // タイマー停止 Wire.write(0x00); Wire.endTransmission(); Wire.beginTransmission(RtcADR); Wire.write(0x01); // コントロールレジスタ-2の設定 Wire.write(0x11); // TI/TP=on, TIE=on Wire.endTransmission(); Wire.beginTransmission(RtcADR); Wire.write(0x0f); // タイマー設定値 Wire.write(0x01); // 1回毎 Wire.endTransmission(); Wire.beginTransmission(RtcADR); Wire.write(0x0e); // タイマースタート Wire.write(0x82); // TEビットon, カウンタソースクロックは1秒を設定 Wire.endTransmission(); } void ClockSet() { // 時計合わせ int n, nen, tsuki, dayCount; lcd.setCursor(0, 0); lcd.print("ClockSet"); // モード開始メッセージ lcd.setCursor(0, 1); lcd.print("Sel,Ent"); // SElectで選択、Enterで決定 while (digitalRead(entB) == LOW) { // EnterスイッチがOFFになるまで待つ } delay(30); Rtc.available(); // 現在の時刻を取得 convString(); // RTCの時刻(BCD)を文字列に変換してdtStringに格納 dispClock(); // 液晶に表示 //データ位置とmin,max値を指定して数値設定プログラムを呼ぶ setV(3, 15, 35); // 年(2015〜2035年まで) setV(6, 1, 12); // 月 // 月の大小と閏年を反映した日数で呼ぶ nen = (dtString[2] & 0x0f) * 10 + (dtString[3] & 0x0f); tsuki = (dtString[5] & 0x0f) * 10 + (dtString[6] & 0x0f); dayCount = 31; if (tsuki == 4 | tsuki == 6 | tsuki == 9 | tsuki == 11) { dayCount = 30; } if (tsuki == 2) { // 2月の処理 dayCount = 28; if ((nen % 4) == 0) { dayCount = 29; // うるう年 } } setV(9, 1, dayCount); // 日 setV(12, 0, 23); // 時 setV(15, 0, 59); // 分を合わせたら秒はゼロで時計あわせ updateClock(); // RTCに日時を設定 } // Set ボタンが押される毎にdtStringの指定位置の値をインクリメント、液晶を更新 // Eent ボタンが押されたら値を確定して戻る void setV(int p, int minV, int maxV) { // p:ポインタ、minV:最小値、maxV:最大値 int xx; char upper; char lower; lcd.setCursor((p - 2) % 9, (p - 2) / 9); // 液晶のカーソルを移動 8文字2行に変換 lcd.cursor(); // カーソル表示 xx = (dtString[p - 1] & 0x0f) * 10 + (dtString[p] & 0x0f); //文字列から初期値を計算。BCDなので上位は10倍 while (digitalRead(entB) == LOW) { // entBがOFFになるまで待つ delay(30); } while (digitalRead(entB) == HIGH) { // entBがNOでなければ以下の処理行う if (digitalRead(selB) == LOW) { delay(30); // selBが押された! xx++; // xxをインクリメント if (xx >= maxV + 1) { // 上限超えてたらゼロに戻す xx = minV; } upper = (xx / 10) | 0x30; // 上位をASCIIへ変換 lower = ( xx % 10) | 0x30; // 下位をASCIIへ変換 lcd.setCursor((p - 3) % 9, (p - 2) / 9); lcd.print(upper); // 表示更新 lcd.print(lower); lcd.setCursor((p - 2) % 9, (p - 2) / 9); // 液晶のカーソルを元の位置に戻す dtString[p - 1] = upper; // データ文字列更新 dtString[p] = lower; while (digitalRead(selB) == LOW) { // selBが離されるまで待つ } delay(30); } } delay(30); } void updateClock() { // RTCに値をセット byte data[7]; int yy, mm, dd, ww; yy = (dtString[2] & 0xf) * 10 + (dtString[3] & 0xf) + 2000; mm = (dtString[5] & 0xf) * 10 + (dtString[6] & 0xf); dd = (dtString[8] & 0xf) * 10 + (dtString[9] & 0xf); if ( mm <= 2) { // ツェラーの式で曜日を求める mm += 12; yy--; } ww = (yy + yy / 4 - yy / 100 + yy / 400 + (13 * mm + 8) / 5 + dd) % 7; for (int n = 0; n <= 15; n++) { // デバッグ用 Serial.print(dtString[n]); } Serial.print(" Week= "); Serial.println(ww); // print week No. delay(200); data[0] = 0x00; // 00秒にリセット data[1] = ((dtString[14] & 0xf) << 4) | (dtString[15] & 0x0f); // 分 data[2] = ((dtString[11] & 0xf) << 4) | (dtString[12] & 0x0f); // 時 data[3] = ((dtString[8] & 0xf) << 4) | (dtString[9] & 0x0f); // 日 data[4] = ww; // 曜日 data[5] = ((dtString[5] & 0xf) << 4) | (dtString[6] & 0x0f); // 月 data[6] = ((dtString[2] & 0xf) << 4) | (dtString[3] & 0x0f); // 年 Rtc.sync(data); } void dispClock() { // 液晶にRTCの内容を表示 lcd.clear(); lcd.setCursor(0, 0); for (int n = 2; n <= 9; n++) { // yy/mm/dd lcd.print(dtString[n]); } lcd.setCursor(0, 1); for (int n = 11; n <= 18; n++) { // hh:mm:ss lcd.print(dtString[n]); } }