今回は秋月電子で販売されているこちらのリアルタイムクロック(以下 RTC)の DS1307 について紹介します。
RTC とは
まずは RTC について説明します。電子工作で「クロック」といえば、普通は 〇Hz のような一定の周波数の信号のことを指すことが多いですが、RTC は〇時〇分といった時刻を数えています。
PIC 等のマイコンで時計を作ろうとしたときに、一定の周波数の信号を数えれば時刻を計算することが出来ますが、その仕事をやってくれる IC が RTC です。
RTC を使用することで PIC は他の計算に専念できたり、RTC にバックアップ電源を供給することで PIC の電源を切っても時刻を保持できたりといった利点があります。
DS1307 の特徴
秋月電子で取り扱っているものだけでもいくつかの RTC がありますが、DS1307 の特徴としては以下のものが挙げられます。
- I2C 接続
- バックアップ電源専用ピンあり
- 外付けの水晶発振子が必要
まずはインターフェースですが、I2C接続なので2本の信号線で接続できて便利です。
次に、バックアップ電源の専用ピンがついています。これがない場合は、主電源とバックアップ電源を切り替えたり逆流を防止したりする必要が生じますが、そのような問題を気にすることがなくなるのでとても便利です。
バックアップ電源には3Vのリチウム電池を繋ぐのが手軽で確実です。他にもダイオードとコンデンサを使って主電源から貯めておくという手段もあります。(主電源は5Vなので注意)
注意点としては、クロック源として 32.768kHz の水晶発振子を外付けする必要があります。ただ水晶発振子は秋月電子で30円で買えますし、実装スペースも大して必要ないので、それほど気にすることでもないと思います。
レジスタ
ここからは実際の制御に必要な情報を見ていきます。
DS1307 のレジスタは次のようになっています。
00h ~ 06h に日付や時刻のデータが入っていて、07h が設定のレジスタとなっています。08h ~ 3Fh は自由に使える RAM になっていて、バックアップ電源によって疑似的に不揮発メモリとして使っても良いかもしれません。
データは BCD (Binary Coded Decimal) という形になっていて、8ビットのうち上位4ビットが10の位、下位4ビットが1の位となっています。
扱いにくいと思うかもしれませんが、7セグLED など一桁ずつ制御する場合には都合が良いのだと思います。
次に「時」についてですが、12時間表示と 24時間表示の好きな方を選べるようになっています。Hoursレジスタの bit 6 が0であれば 24時間表示、1であれば 12時間表示となります。
12時間表示の場合、10の位は1bitで足りるので、bit 5 は PM/AM となります。普通の BCD 形式でデータを入力すれば、10の位の数は高々2なので bit 6 は0となり、24時間表示になります。
曜日を表す Dayレジスタは1~7の値を取りますが、何曜日がどの数字かは決まっていないので、月曜始まりでも日曜始まりでも好きなように決めていいです。
Controlレジスタは SQW/OUT ピンの矩形波出力に関するピンで、RS は周波数の設定、SQWE は矩形波出力の ON/OFF、OUT は矩形波出力が OFF の場合の出力を設定します。(下図参照)
1Hzの矩形波は Secondsレジスタと同期しているので、これをマイコンに送れば秒まで同期可能です。
最後に、Secondsレジスタの bit 7 に CH (Clock Halt) ビットがあります。リセット後はこのビットが1になっていてクロックが動かないので、必ず0にする必要があります。(私はこれに気付かず4~5時間頭を悩ませていました。なんでControlレジスタに入っていないんだよ~)
プログラム
DS1307 と通信してデータを送受信するライブラリのようなものを書いてみました。
日付や時刻のデータは通信の簡単のために配列として、インデックスを定数として定義することで、例えば秒だったら dateTime[SEC] のように使うことにしました。
また、BCD のデータをそのままインクリメントする関数や、BCD と普通の数字を変換する関数も一応用意しておきました。
この中で使っている I2Cライブラリについてはこちらの記事をご覧ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
#include "i2c.h" #define DS 0b1101000 #define SEC 0 #define MIN 1 #define HOUR 2 #define DAY 3 #define DATE 4 #define MONTH 5 #define YEAR 6 char dateTime[7]; void GetDateTime(){ SendI2C(DS, 0); GetDataI2C(DS, dateTime, 7); } void SetTimeDate(){ for(char i=0; i<7; i++){ CmdI2C(DS, i, dateTime[i]); } CmdI2C(DS, 7, 0x10); //output 1Hz } char BCDincrement(char c){ c++; if((c & 0x0F) == 10) c += 6; //繰り上がり(-10 + 16) return c; } char BCDtoBIN(char bcd){ char bin = ((bcd >> 4) & 0x0F) * 10 + (bcd & 0x0F); return bin; } char BINtoBCD(char bin){ char bcd = ((bin / 10) << 4 ) | (bin % 10); return bcd; } |
▼I2Cライブラリ
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
#include<xc.h> #define _XTAL_FREQ 1000000 void I2C_Master_Init(const unsigned long c){ SSP1CON1 = 0x28; //SSP1 Module as Master SSP1CON2 = 0x00; SSP1CON3 = 0x00; SSP1ADD = (_XTAL_FREQ/(4*c))-1; //Setting Clock Speed SSP1STAT = 0x80; //SSP1CLKPPS = 0x0E; //PPS Settings //SSP1DATPPS = 0x0C; // SCL : RB6 //RB6PPS = 0x18; // SDA : RB4 //RB4PPS = 0x19; } void I2C_Master_Wait(){ while ((SSP1STAT & 0x04) || (SSP1CON2 & 0x1F)); //Transmit is in progress } void I2C_Master_Start(){ I2C_Master_Wait(); SSP1CON2bits.SEN = 1; //Initiate start condition } void I2C_Master_RepeatedStart(){ I2C_Master_Wait(); SSP1CON2bits.RSEN = 1; //Initiate repeated start condition } void I2C_Master_Stop(){ I2C_Master_Wait(); SSP1CON2bits.PEN = 1; //Initiate stop condition } void I2C_Master_Write(unsigned d){ I2C_Master_Wait(); SSP1BUF = d; //Write data to SSP1BUF } unsigned short I2C_Master_Read(unsigned short a){ unsigned short temp; I2C_Master_Wait(); SSP1CON2bits.RCEN = 1; I2C_Master_Wait(); temp = SSP1BUF; //Read data from SSP1BUF I2C_Master_Wait(); SSP1CON2bits.ACKDT = a; //Acknowledge bit 1:Not Ack 0:Ack SSP1CON2bits.ACKEN = 1; //Acknowledge sequence return temp; } void SendI2C(char adrs, char data){ I2C_Master_Start(); I2C_Master_Write(adrs<<1); //SlaveAdress + Write I2C_Master_Write(data); //mainly RegisterAdress I2C_Master_Stop(); } void CmdI2C(char adrs, char reg, char data){ I2C_Master_Start(); I2C_Master_Write(adrs<<1); //SlaveAdress + Write I2C_Master_Write(reg); //RegisterAdress I2C_Master_Write(data); I2C_Master_Stop(); } void GetDataI2C(char adrs, char* buf, char cnt){ I2C_Master_Start(); I2C_Master_Write((adrs<<1)+1); //SlaveAdress + Read for(char i=0; i<cnt-1; i++) buf[i] = I2C_Master_Read(0); //ACK buf[cnt-1] = I2C_Master_Read(1); //NACK I2C_Master_Stop(); } |
1 2 3 4 5 6 7 8 9 10 |
void I2C_Master_Init(const unsigned long c); void I2C_Master_Wait(); void I2C_Master_Start(); void I2C_Master_RepeatedStart(); void I2C_Master_Stop(); void I2C_Master_Write(unsigned d); unsigned short I2C_Master_Read(unsigned short a); void SendI2C(char adrs, char data); void CmdI2C(char adrs, char reg, char data); void GetDataI2C(char adrs, char* buf, char cnt); |
今回の記事は以上です。ご覧いただきありがとうございました。
この記事では都合上実際に動作テストはしていませんが、この IC を使って卓上時計を作ったので実例はこちらをご覧ください。