Raspberry PIで、Cライブラリ「WiringPi」を用いて、リアルタイムクロック(RTC)モジュール「RTC-8564NB」を動作させました。西暦(二桁)・月・日・曜・時・分・秒の設定と読み出す時計機能と、32.768kHz,1024Hz, 32Hz, 1HzのCMOS出力が可能で、2番ピンから出力するクロック出力機能を実装しました。なお、RTC-8564NBとRaspberry PIの接続図は、「Raspberry PIへリアルタイムクロックモジュールのI2C接続 」に示します。また、RTC-8564NBのアラーム機能とタイマー機能は「RTC-8564NBによるタイマー作成 」に示します。
WiringPiのインストール
WiringPiを使用するには、I2CライブラリとWiringPi本体をインストールする必要があります。
次のコマンドでI2Cライブラリをインストールします。
# sudo apt-get install libi2c-dev
WiringPi本体はソースコードをgit経由で次のコマンドにより取得します。
# git clone git://git.drogon.net/wiringPi
ダウンロードが完了すると、カレントディレクトリにwiringPiディレクトリができるので、次のコマンドでビルドします。
# cd wiringPi # ./build
RTC-8564NBプログラム作成
RTC-8564NBが持つ機能のうち、時計機能とクロック出力機能を実装します。これらの機能は,RTC-8564NBに内蔵されているレジスタを設定することにより実現可能です。このRTC-8564NBはI2Cで制御するので、Cライブラリ「WiringPi」を用いて、I2Cを制御します。
プログラムのソースコード「rtc.c」を次に示します。setDateTime関数で時計を設定し、getDateTime関数で時計を読み出します。setClkoutFrequency関数は、クロック出力端子「CLKOUT」2ピンにデータを出力し、1秒ごとに点灯/消灯を繰り返すように設定します。
rtc.c
#include <stdio.h>
#include <wiringPi.h>
#include <wiringPiI2C.h>
// I2C Address
#define RTCS8564_I2C_ADDRESS 0x51
// Registers
#define RTC8564_CONTROL1 0x00
#define RTC8564_CONTROL2 0x01
#define RTC8564_SECONDS 0x02
#define RTC8564_MINUTES 0x03
#define RTC8564_HOURS 0x04
#define RTC8564_DAYS 0x05
#define RTC8564_WEEKDAYS 0x06
#define RTC8564_MONTH_CENTURY 0x07
#define RTC8564_YEARS 0x08
#define RTC8564_MINUTE_ALARM 0x09
#define RTC8564_HOUR_ALARM 0x0a
#define RTC8564_DAY_ALARM 0x0b
#define RTC8564_WEEKDAY_ALARM 0x0c
#define RTC8564_CLKOUT_FREQUENCY 0x0d
#define RTC8564_TIMER_CONTROL 0x0e
#define RTC8564_TIMER 0x0f
// Control1 register
#define RTC8564_STOP_BIT 0x20
// Control2 register
#define RTC8564_TIE_BIT 0x01
#define RTC8564_AIE_BIT 0x02
#define RTC8564_TF_BIT 0x04
#define RTC8564_AF_BIT 0x08
#define RTC8564_TITP_BIT 0x10
// Calendar registers
#define RTCS8564_CAL_VL 0x80
#define RTCS8564_CAL_CENTURY 0x80
// Alarm register
#define RTC8564_AE_NONE 0x00
#define RTC8564_AE_MINUTE 0x01
#define RTC8564_AE_HOUR 0x02
#define RTC8564_AE_DAY 0x04
#define RTC8564_AE_WEEKDAY 0x08
#define RTC8564_AE_ALL (RTC8564_AE_MINUTE | RTC8564_AE_HOUR | RTC8564_AE_DAY | RTC8564_AE_WEEKDAY)
#define RTC8564_AE_BIT 0x80
// Timer control register
#define RTC8564_CLK_244US 0x00
#define RTC8564_CLK_15MS 0x01
#define RTC8564_CLK_1SEC 0x02
#define RTC8564_CLK_1MIN 0x03
#define RTC8564_TE_BIT 0x80
// Clkout frequency register
#define RTC8564_CLKOUT_32768HZ 0x00
#define RTC8564_CLKOUT_1024HZ 0x01
#define RTC8564_CLKOUT_32HZ 0x02
#define RTC8564_CLKOUT_1HZ 0x03
#define RTCS8564_FE_BIT 0x80
// date and time
struct dateTime {
char second; // 0-59
char minute; // 0-59
char hour; // 0-23
char day; // 1-31
char month; // 1-12
char year; // 00-199
char weekday; // 0(Sun)-6(Sat)
};
// alarm
struct alarmTime {
char minute; // 0-59
char hour; // 0-23
char day; // 1-31
char weekday; // 0(Sun)-6(Sat)
};
static int dev;
static int init_dev(void)
{
if ((dev = wiringPiI2CSetup(RTCS8564_I2C_ADDRESS)) == -1)
{
return 1 ;
}
return 0;
}
void setRegisters(char address, int numData, char *data) {
char adr;
int i;
adr = address;
for (i = 0; i < numData; i++) {
wiringPiI2CWriteReg8 (dev, adr, *data);
adr ++;
data ++;
}
}
void getRegisters(char address, int numData, char *data) {
char adr;
int i;
adr = address;
for (i = 0; i < numData; i++) {
*data = wiringPiI2CReadReg8 (dev, adr);
adr ++;
data ++;
}
}
int decimalToBCD(int decimal) {
return (((decimal / 10) << 4) | (decimal % 10));
}
int BCDToDecimal(int bcd) {
return ((bcd >> 4) * 10 + (bcd & 0x0f));
}
void setDateTime(struct dateTime *dt) {
char data[7];
data[0] = RTC8564_STOP_BIT;
setRegisters(RTC8564_CONTROL1, 1, data);
data[0] = decimalToBCD(dt->second);
data[1] = decimalToBCD(dt->minute);
data[2] = decimalToBCD(dt->hour);
data[3] = decimalToBCD(dt->day);
data[4] = decimalToBCD(dt->weekday);
data[5] = decimalToBCD(dt->month);
if (dt->year > 100) {
data[6] = decimalToBCD(dt->year - 100);
data[5] |= RTCS8564_CAL_CENTURY;
} else {
data[6] = decimalToBCD(dt->year);
}
setRegisters(RTC8564_SECONDS, 7, data);
data[0] = 0x00;
setRegisters(RTC8564_CONTROL1, 1, data);
}
int getDateTime(struct dateTime *dt) {
char data[7];
getRegisters(RTC8564_SECONDS, 7, data);
if (data[0] & RTCS8564_CAL_VL) {
return -1;
}
dt->second = BCDToDecimal(data[0] & 0x7f);
dt->minute = BCDToDecimal(data[1] & 0x7f);
dt->hour = BCDToDecimal(data[2] & 0x3f);
dt->day = BCDToDecimal(data[3] & 0x3f);
dt->weekday = BCDToDecimal(data[4] & 0x07);
dt->month = BCDToDecimal(data[5] & 0x1f);
dt->year = BCDToDecimal(data[6]);
if (data[5] & RTCS8564_CAL_CENTURY) {
dt->year += 100;
}
return 0;
}
void setClkoutFrequency(char frequency) {
frequency |= RTCS8564_FE_BIT;
setRegisters(RTC8564_CLKOUT_FREQUENCY, 1, &frequency);
}
int main(int argc, char **argv) {
struct dateTime dt = {0, 0, 0, 1, 1, 13, 2}; // 2013 January 1st, Tue 00:00:50
struct dateTime get_dt;
int count ;
if(init_dev()==1) return 1;
setDateTime(&dt);
count = 5;
while(count--){
getDateTime(&get_dt);
printf("second:%d minute:%d hour:%d\n",get_dt.second,get_dt.minute, get_dt.hour);
sleep(5);
}
setClkoutFrequency( RTC8564_CLKOUT_1HZ); // Setup CLKOUT frequency.
return 0;
}
コンパイル・リンクは次のコマンドで実行します。
# gcc -o rtc rtc.c -lwiringPi
出来上がったプログラムを次のコマンドで実行します。
# sudo ./rtc
次のように結果が表示され、LEDが1秒ごとに点灯/消灯を繰り返します。
# ./rtc second:0 minute:0 hour:0 second:5 minute:0 hour:0 second:10 minute:0 hour:0 second:15 minute:0 hour:0 second:20 minute:0 hour:0
RTC-8564NBのプログラムは最初は「C library for Broadcom BCM 2835 as used in Raspberry Pi 」を使用して作成し、実際に動作させてみましたが、次の点が分からなくて断念し、違うRaspberry PI用のC言語ライブラリ「WiringPi」を使用して作成しました。
- 書き込みするレジスタの設定、読み出すレジスタの設定など、レジスタのアドレスの設定方法がわからない。
- 一つのAPIで複数のデータが指定できるように、バッファサイズが指定されているが、これは同じレジスタに対して繰り返してreadもしくはWriteするのかが分からない。