シリアル出力リアルタイムクロック(RTC-4543SA)を使ってみた

秋月で売っているシリアル出力方式のRTC(リアルタイムクロック)、RTC-4543SA(エプソントヨコム社製)を使ってみました。

概要

  • 年、月、日、曜日、時、分、秒をカウント
  • データ読み書きはシリアル通信(ただしちょっと特殊)
  • 通信には4本の通信線を使用


IC単体だと250円
http://akizukidenshi.com/catalog/g/gP-04019/


DIP変換基板に載ってると450円。ブレッドボードで実験したかったのでこちらの基板付き版を買いました。
http://akizukidenshi.com/catalog/g/gI-04593/


RTCと言えばI2Cの以下のをよく見ますが、今回はI2Cが利用できない条件を想定しているので上記のシリアル通信タイプのものを使っています。
http://akizukidenshi.com/catalog/g/gI-00233/

データシート
http://www.epsondevice.com/docs/qd/ja/DownloadServlet?id=ID000713

アプリケーションノート
http://www.epsondevice.com/docs/qd/ja/DownloadServlet?id=ID000516


また、kyuuzyuu9yenさんのブログに解説とArduinoの分かりやすいソースコードが掲載されています。

RTC-4543(RTC-4543SA/SB)を使う(前編)
http://d.hatena.ne.jp/kyuuzyuu9yen/20100803/1280828753

RTC-4543(RTC-4543SA/SB)を使う(後編)
http://d.hatena.ne.jp/kyuuzyuu9yen/20100912/1284283555

プロトコル

CE(Chip Enable入力)、WR(入出力切り替え)、CLK(クロック)、DATA(データ入出力共用)の4ピンを使用します。
データはDATA端子にクロックと同期してLSBファーストで1bitずつ読み書きします。ちなみにデータの中身はBCDコードです。
BCDコードは例えば「34」だったら十の位の3と一の位の4に分けて0011 0100とする形式です。


データを書き込む時は決められた順で52bitを連続で送ります。読む時も同様に52bit連続で読みます。実際は必要なデータに応じて途中まででもOKなようですが、ここでは全て読み書きすることにします。
52bitの内容は以下通りです。

FDTは電圧監視ビットで規定電圧より低くなるとセットされます。TMビットはテスト用だそうで、共に書き込む時は0を指定します。アスタリスクはユーザ定義ビットで好きに使っていいようです。
表を見て分かるように、3bit〜8bitで読み書きするようになっています。マイコン内蔵のUARTモジュールなどは使えないのでソフトで実装します。


送受信は以下のような手順です。

  • 書き込み
    • WR端子を1
    • CE端子を1
    • クロックと同期して1bitずつ送信
    • CE端子を0
    • WR端子を0
  • 読み込み
    • WR端子を0
    • CE端子を1
    • マイコン側のDATA端子を入力ピンに設定
    • クロックと同期して1bitずつ受信
    • CE端子を0
    • WR端子を0
    • マイコン側のDATA端子を出力ピンに設定


CE端子を0にすると他の通信用の端子はハイインピーダンスになります。ハイインピーダンスの端子同士を繋ぐのは良くないとどこかで見たことがあるので、最後に一応出力に設定していますが、実際はどうなのかよく分かっていません。

使ってみる

おなじみのmbedを使って、先ほどのkyuuzyuu9yenさんのソースコードを眺めつつ制御プログラムを組んでみました。kyuuzyuu9yenさんありがとうございました。
mbed向けに書いたプログラムを下の方に掲載しました。


実際の動作の様子です。はじめに書きこみをして、その後1秒ごとに読み出してStarBoard OrangeのLCDに表示しています。
精度はまだ検証できていませんが、とりあえず正常に動作させることができました。

ハマったところ

  • データの上下ビットがひっくり返る

単純にLSBファーストなことを忘れてMSBから読んでしまっただけでした

  • データが化ける

読み込みは正常にできるのに書き込みで全然違う数値が入ってしまう問題にしばらく悩まされました。
データ送出間隔が短すぎるのではと思って、3.3V駆動から5Vにしてみたところ正常に動作。
アプリケーションノート上では、5V時のクロック周期が最小0.75us、3V時では最小1.5usとなっています。どうやらmbedが速すぎてこれを下回っていたようです。
クロックを立ち上げた後に1usのwaitを入れたところ3.3Vでも正常に動作しました。

まとめ

はじめは面倒かなと思ってましたが実際はそうでもありませんでした。これを使えば正確な時計機能を簡単に実装できそうです。1つ250円という価格も魅力的だと思います。
時刻や日付と連携したものを作ってみたいと思います。

プログラム全文

#include "mbed.h"
#include "TextLCD.h"

DigitalOut cePin(p5);
DigitalInOut dataPin(p6);
DigitalOut clkPin(p7);
DigitalOut wrPin(p8);
TextLCD lcd(p24, p26, p27, p28, p29, p30);

class RTC4543
{
    private:
        void rtcIdle(void)
        {
            cePin = 0;
            wrPin = 0;
            clkPin = 0;
            dataPin.output();   
            dataPin = 0;     
        }
        
        void rtcWriteMode(void)
        {
            wrPin = 1;
            cePin = 1;
            dataPin.output();
        }
        
        void rtcReadMode(void)
        {
            wrPin = 0;
            cePin = 1;
            dataPin.input();
        }
        
        void clk(void)
        {
            clkPin = 1;
            wait_us(1);
            clkPin = 0;
        }
        
        void setBit(unsigned char in)
        {
            if((in & 0x01) == 0x01) {
                dataPin = 1;
            } else {
                dataPin = 0;
            }
            
            clk();
        }
        
        unsigned char readBit(void)
        {
            clk();
            
            return(dataPin & 0x01);
        }
        
        void setData(unsigned char inData, int inCount)
        {
            for(int i = 0; i < inCount; i++) {
                setBit(inData & 0x01);
                inData = inData >> 1;
            }
        }
        
        unsigned char readData(int inCount)
        {
            unsigned char inData = 0;
            unsigned char temp = 0;

            for(int i = 0; i < inCount; i++) {
                temp = readBit() << i;
                inData += temp;
            }
            
            return(inData);
        }
        
    public:
        int sec;
        int min;
        int hour;
        int date;
        int day;
        int month;
        int year;
        
        //constructor
        RTC4543()
        {
            sec = 0;
            min = 0;
            hour = 0;
            date = 0;
            day = 0;
            month = 0;
            year = 0;
        }
    
        void writeRtc(void)
        {
            rtcWriteMode();
            
            setData(sec % 10, 4);
            setData(sec / 10, 3);
            setData(0, 1);          // FDT bit(always set to 0)
            
            setData(min % 10, 4);
            setData(min / 10, 3);
            setData(0, 1);          // user bit(0)
            
            setData(hour % 10, 4);
            setData(hour / 10, 2);
            setData(0, 2);          // user bit(0)
            
            setData(date, 3);
            setData(0, 1);          // user bit(0)
            
            setData(day % 10, 4);
            setData(day / 10, 2);
            setData(0, 2);          // user bit(0)
            
            setData(month % 10, 4);
            setData(month / 10, 1);
            setData(0, 2);          // user bit(0);
            setData(0, 1);          // TM bit(always set to 0)
            
            setData(year % 10, 4);
            setData(year / 10, 4);
            
            rtcIdle();
        }
        
        void readRtc(void)
        {
            rtcReadMode();
            
            sec = readData(4) + (readData(3) * 10);
            readData(1);            // FDT bit
            
            min = readData(4) + (readData(3) * 10);
            readData(1);            // user bit
            
            hour = readData(4) + (readData(2) * 10);
            readData(2);            // user bit
            
            date = readData(3);
            readData(1);            // user bit
            
            day = readData(4) + (readData(2) * 10);
            readData(2);
            
            month = readData(4) + (readData(1) * 10);
            readData(2);            // user bit
            readData(1);            // TM bit
            
            year = readData(4) + (readData(4) * 10);
            
            rtcIdle();
        }
};

int main()
{
    RTC4543 rtc;
    
    rtc.sec = 30;
    rtc.min = 48;
    rtc.hour = 1;
    rtc.date = 5;
    rtc.day = 24;
    rtc.month = 2;
    rtc.year = 12;
    
    rtc.writeRtc();

    wait(1);

    while(1) {
        rtc.readRtc();
    
        lcd.cls();
        lcd.printf("%d/%d/%d(%d)", rtc.year, rtc.month, rtc.day, rtc.date);
        lcd.locate(0, 1);
        lcd.printf("%d:%d:%d", rtc.hour, rtc.min, rtc.sec);
        
        wait(1);
    }
}