平成27年11月27日
汎用の高速シリアル通信機能(19) RTCの動作検証
またしばらく更新が出来ませんでした。C++の言語機能習得と平行して基板の検証をしているので、どうしても脇道にそれる事が多くなります。
しかし新しい言語を覚えるのには試行錯誤がつきものです。目先の結果を求めるのではなく、正しい理解を求めることが最終的には近道になります。
基板に実装したRTCは二種類あります。今回はバス接続型のRTC-4543を検証します。
RTC-4543は独自仕様のシリアルバスでCPUと通信を行います。特徴としては
- 水晶を内蔵しており外付けする必要が無いため、基板面積が少なくて済む。
- 通信仕様としてはクロック同期式シリアルだが、データビットの送受信の順番はLSBファーストであり、これはUSARTと同じでSPI(MSIファースト)とは逆になる。
- 基本的な時計機能のみが実装されており非常に扱いやすい。通信コマンドは無く、日時の読み出しと書き込みをハードウェア信号のみで操作する。
- データの読み書きの単位は基本8ビットとだが、データ中には4ビットデータが含まれている。
このRTCを実際の製品に使用するときには、RTCとCPUの間にPLDを挟んで、CPUからはSPIインタフェースで読み書きすることを考えています。
これでRTCによる追加のピンはCSとWRの二本で済みます。反面、プログラムはそれなりに面倒になります。通信の前後でデータのビット配置を上下で逆転する必要があり、また4ビット長のデータが含まれるため、データ途中からデータのバウンダリーを変更する必要があります。
今回はそこまでは行わず、通常のIOポートに接続した状態での操作を行います。
プログラムは下記になります。C++での記述です。Cではコンパイルできません。以下の3つの関数で読み書きを行います。
void init_4543();
void read_4543(uint8_t* buf);
void write_4543(const uint8_t* buf):
注意:
実際の接続によってIOポートの設定や初期化の初期値は変更が必要です。
プログラム中のDELAYは1usだけ時間待ちするマクロです。リストには含まれていません。
私はハードウェアタイマを使って実装してありますが、面倒ならNop命令を使ったソフトウェアタイマで十分です。
#define RTC_DATA_OUT LATCbits.LATC1
#define RTC_DATA_IN PORTCbits.RC1
#define RTC_CLK LATCbits.LATC2
#define RTC_WR LATCbits.LATC3
#define RTC_CE LATCbits.LATC4
#define RTC_DATA_DIR TRISCbits.TRISC1
#define DIR_READ 1
#define DIR_WRITE (!DIR_READ)
void init_4543()
{
LATC = 0x0;
TRISC = 0xe3; //RTC_CLK, RTC_WR, RTC_CEを出力に
}
static void readRTC(uint8_t& data, const uint8_t limit)
{
data = 0;
uint8_t keta = 1;
for(uint8_t i = 0; i < limit; i++){
DELAY;
RTC_CLK = 1;
DELAY;
if(RTC_DATA_IN)
data += keta;
keta *= 2;
RTC_CLK = 0;
}
}
#define read1byte(d) readRTC(d, 8);
#define read4bit(d) readRTC(d, 4);
void read_4543(uint8_t* buf)
{
RTC_WR = 0;
RTC_DATA_DIR = DIR_READ;
DELAY; //CEに対するセットアップ時間を確保
RTC_CE = 1;
read1byte(*buf++); //秒
read1byte(*buf++); //分
read1byte(*buf++); //時
read4bit(*buf++); //曜日
read1byte(*buf++); //日
read1byte(*buf++); //月
read1byte(*buf++); //年
RTC_CE = 0;
}
static void writeRTC(uint8_t data, uint8_t limit)
{
for(uint8_t i = 0; i < limit; i++){
RTC_DATA_OUT = (data & 0x01);
data /= 2;
DELAY;
RTC_CLK = 1;
DELAY;
RTC_CLK = 0;
}
}
#define write1byte(d) writeRTC(d, 8);
#define write4bit(d) writeRTC(d, 4);
void write_4543(const uint8_t* buf)
{
RTC_WR = 1;
RTC_DATA_DIR = DIR_WRITE;
DELAY; //CEに対するセットアップ時間を確保
RTC_CE = 1;
write1byte(*buf++); //秒
write1byte(*buf++); //分
write1byte(*buf++); //時
write4bit(*buf++); //曜日
write1byte(*buf++); //日
write1byte(*buf++); //月
write1byte(*buf++); //年
RTC_CE = 0;
}