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ページから引用)
・コマンド(データシート9ページから引用)
コマンド表の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);
}
このままコピペで実行できました。シリアルモニタ見ると
これでどうにか、0から59まで秒を取得できるようにはなりました。えーと、ここまで、まだ「秒」だけですが(^_^;)