見出し画像

RTCモジュール(DS1302)を試す(1)

 Arduinoで時刻情報を扱えるようにしたい、と買ってきたDS1302のモジュールですが、これを制御するための巷の定番ライブラリはどれなのか?ネット上の記事を探してもよくわからず、悩んでしまいました。
 ならいっそ、3線通信方法の勉強も兼ねて、自分で書いてみよう・・。

DS1302の技術情報源

(マキシム・インテグレーテッド社さんのサイトから)
・日本語での説明はこちら
 https://www.maximintegrated.com/jp/products/analog/real-time-clocks/DS1302.html

・データシート
 https://datasheets.maximintegrated.com/en/ds/DS1302.pdf
(CE、I/O、SCLK の3線で通信し、時刻情報を書き込んだり読みだしたりする。注:基板上のピン位置には CEがRST I/OがDAT SCLKがCLK と印刷されている)

・データ転送の方法(データシート8ページから引用)

画像1

・コマンド(データシート9ページから引用)

画像2

コマンド表のREAD またはWRITEの下に記載されている81h(16進表記で81ビット列では10000001)とか80hのコマンド(アドレス)を書き込み、続けて値を書き込むか読みだすかするわけですね。といってもまだピンとこないので手順を書き出してみます。

「秒」を読みだす例

 コマンドの表から、「秒」の数値を読む命令は81h(16進の81)だとわかります。これをビット列で示すと(10000001)です。これを低い方のビットから次の手順で送信。
(以下、電位をHIGHにするのを「上げ」、 LOWにするのを「下げ」、と表記します。Lチカの点灯、消灯と同じです。)

(SETUPでCE, SCLKのピンモードをOUTPUTにしておく)
CE 上げ
 I/OのピンモードをOUTPUTにする
 I/O 上げ(1)
 SCLK 上げ、下げ
 I/O 下げ(0)
 SCLK 上げ、下げ
 I/O 下げ(0)
 SCLK 上げ、下げ
 I/O 下げ(0)
 SCLK 上げ、下げ
 I/O 下げ(0)
 SCLK 上げ、下げ
 I/O 下げ(0)
 SCLK 上げ、下げ
 I/O 下げ(0)
 SCLK 上げ、下げ
 I/O 上げ(1)
 SCLK 上げ
(ここまででコマンド送信完了。あとはデータを引き出す。)
 I/OのピンモードをINPUTに切り替える
 SCLK 下げ
 I/O 読みこみ ⇒(BIT 0)
 SCLK 上げ、下げ
 I/O 読みこみ ⇒(BIT 1)
 SCLK 上げ、下げ
 I/O 読みこみ ⇒(BIT 2)
 SCLK 上げ、下げ
 I/O 読みこみ ⇒(BIT 3)
 SCLK 上げ、下げ
 I/O 読みこみ ⇒(BIT 4)
 SCLK 上げ、下げ
 I/O 読みこみ ⇒(BIT 5)
 SCLK 上げ、下げ
 I/O 読みこみ ⇒(BIT 6)
 SCLK 上げ、下げ
 I/O 読みこみ ⇒(BIT 7)
CE 下げ

サンプルコード

SCLKを6番ピン、IOを7番ピン、CEを8番ピン(この他にVccを+5V、GNDをGND)につないだ状態で書いてみたコードを走らせ、モニタを眺めると・・、やった!動いた。と思いきや 9の次が16だったり、25の次が32だったりしていました。上のコマンドの表で、秒は、BIT0からBIT3が1の位、BIT4からBIT6が10の位でしたから、
sec10 = (data>>4)&7; //上位4ビットを0111でマスクする。
sec1 = data & 15; //下位4ビットを1111でマスクする。
sec10*10+sec1 のようにする変換が必要です。このためにbytesec2decsec という関数を書き足しました。


#define SCLK  6   //(基板上CLK⇒6番ピン)

#define IO  7    //(基板上DAT⇒7番ピン)

#define CE  8    //(基板上RST⇒8番ピン)

void setup() {
 //シリアル通信準備
 Serial.begin(9600);
 //CEとSCLKのPINモードはずっとOUTPUTのまま。
 pinMode(CE, OUTPUT);
 pinMode(SCLK, OUTPUT);
}

void loop() {
 Serial.println(bytesec2decsec(read_1byte(0x81)));//読んだデータをシリアルに出力
 delay(1000);//1秒(1000ミリ秒)待ち。
}

//1バイトの読み出し命令(command)を送って、1バイト読みだす関数
byte read_1byte(byte command){
 byte recv_data=0;  //受信データ格納用
 
 //IOのpinmodeをまず送信用で準備
 pinMode(IO, OUTPUT);

 //最初はCE、SCLKを下げた状態にしておく
 digitalWrite(CE, LOW);wait();
 digitalWrite(SCLK, LOW);wait();

 //開始(CE 上げ)
 digitalWrite(CE, HIGH);wait();
 
 
 //コマンド送信 1ビットずつ繰り返し8ビット(1バイト)送る
 for (int i=0; i <= 7; i++){
   digitalWrite( IO, bitRead(command, i));
   delayMicroseconds(4); 
   //クロックを進める
   digitalWrite(SCLK, HIGH);wait();
   //読み出しに移るので最後だけはクロック下げる前にIOのピンモード切り替え
   if(i==7){
     pinMode(IO, INPUT);//ピンモード切替
   }
   digitalWrite(SCLK, LOW);wait();
 }
 //1バイトデータの受信
 for (int i=0; i<=7; i++){ 
   bitWrite( recv_data,i, digitalRead(IO));
   if (i<7){ //最後以外はクロック進める。
     digitalWrite(SCLK, HIGH);wait();
     digitalWrite(SCLK, LOW);wait();
   } 
 }
 //CE下げる
 digitalWrite(CE, LOW);wait();
 //値を返す
 return recv_data;  
}

void wait(){
 //データシート( https://datasheets.maximintegrated.com/en/ds/DS1302.pdf )11ページから
 //オンオフ操作後の待ち時間はとりあえず最長の4μ秒分とっておけばよいかと。
 delayMicroseconds(4);
}

int bytesec2decsec(byte bytesec){
 int sec10, sec1;
 sec10 = (bytesec>>4)&7; //上位4ビットを0111でマスクする。
 sec1 = bytesec & 15; //下位4ビットを1111でマスクする。
 return (sec10*10 + sec1);
}

このままコピペで実行できました。シリアルモニタ見ると

カウント2

これでどうにか、0から59まで秒を取得できるようにはなりました。えーと、ここまで、まだ「秒」だけですが(^_^;)

いいなと思ったら応援しよう!