目次
前
次
RTC制御
ソーラーパネルの発電量を測定するために
RTC(Real Time Clock)を使い、日付と時刻
を記録できるようにしてみました。
RTCの基本動作を調べることに注力しました。
利用したRTCは、秋月電子から入手。
発電量を計算して、EEPROMに日付とともに
保存したいとして、次のように1枚の基板
にまとめました。
RTC内部のレジスタにパラメータを設定する
パラメータを取得するという2機能を含め
次のコマンドを用意しました。
- ? ヘルプ
- G 日時時刻連続表示開始
- g 日時時刻連続表示停止
- S 日時時刻表示
- D 日付時刻設定
コマンドを決めたなら、インタプリタを定義。
/* command interpreter */
if ( uflag == ON ) {
/* clear flag */
uflag = OFF ;
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help(); }
/* enable */
if ( cmd == 'G' ) { eflag = ON ; }
/* disable */
if ( cmd == 'g' ) { eflag = OFF; }
/* show */
if ( cmd == 'S' ) { show_val(); }
/* date */
if ( cmd == 'D' ) { putDateTime(); }
/* new line */
crlf() ;
}
コマンド受信をイベント通知フラグuflagのセットで
確認しておき、受信バッファからコマンドを取得して
個々の処理に分岐します。
コマンドは受信割込みを使えばよいので、定番のコード
にて対応しました。
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
個々の関数と手続きを、定義。
ヘルプ
ヘルプは、1文字コマンドと機能を説明だけにします。
void show_help()
{
rs_puts("? help") ; crlf();
rs_puts("G enable") ; crlf();
rs_puts("g disable") ; crlf();
rs_puts("S show") ; crlf();
rs_puts("D set date time"); crlf();
}
文字列表示は、自前で定義。
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
日時時刻連続表示開始
日時時刻の連続表示は、フラグを利用して、タイマー
割込み発生時に動く関数に一任します。
フラグとしてeflagを用意し、セットして対応。
loopの中に、割込みハンドラを入れておきます。
if ( tflag == ON ) {
/* clear flag */
tflag = OFF ;
/* get date time */
getDateTime();
/* update */
makeString();
lcd.setCursor(0,0);
lcd.print( line0 );
if ( eflag == ON ) { show_val(); }
}
eflagがセットされているときだけ、関数show_valが
使われます。
日時時刻を常に表示したいので、LCDを使うことにしました。
表示の仕様は、次のように単純にしています。
西暦年の下2桁、月日、時分秒を区切り文字を
入れないで、表示させます。
RTCから情報を引出すための関数getDateTimeを
定義して、呼出す仕様にまとめておきます。
関数getDateTimeの内容は、以下。
void getDateTime()
{
Wire.requestFrom(RTC_ADRS,8);
Wire.read(); /* 0x0f (fixed dummy */
*(xdat+5) = Wire.read(); /* ss */
*(xdat+4) = Wire.read(); /* mm */
*(xdat+3) = Wire.read(); /* hh */
Wire.read(); /* weekday */
*(xdat+2) = Wire.read(); /* DD */
*(xdat+1) = Wire.read(); /* MM */
*(xdat+0) = Wire.read(); /* YY */
}
選んだRTCは、IICバスで情報交換できるので
TWIクラスを組み込んで、メソッドでデータ
をバッファに格納しています。
データがバッファにさえ入ってしまえば
1バイトごとに取り出すのは、簡単。
配列に格納します。
データシートに、便利なリード方法があったので
その方法を使って、楽をしました。
データシートの内容は、以下。
レジスタ番号15の1バイトから、ロールして0番、1番と
順序良くパラメータを取得できるとなっていたので、この
機能を使いました。
データ取得が出来たなら、LCDに表示するため
変換処理に移ります。変換処理は、以下。
void makeString()
{
char msg[2] ;
byte tmp ;
byte ii ;
byte jj ;
/* clear */
for ( ii = 0 ; ii < 16 ; ii++ ) { *(line0+ii) = ' ' ; }
/* copy */
for ( ii = 0 ; ii < 6 ; ii++ ) { *(ydat+ii) = *(xdat+ii) ; }
/* YY-MM-DD hh mm ss */
for ( ii = 0 ; ii < 6 ; ii++ ) {
/* get */
tmp = *(ydat+ii) ;
/* seperate */
*(msg+1) = (tmp & 15) + '0' ;
tmp >>= 4 ;
*(msg+0) = tmp + '0' ;
/* calcuate pointer */
jj = (ii << 1) ;
/* store */
*(line0 + jj ) = *(msg+0);
*(line0 + jj + 1) = *(msg+1);
}
}
BCD形式で1バイトに2桁の数値を入れる
仕様なので、上位、下位の4ビットに分け
数字に変換してます。
eflagがセットされているときは、次の関数を
呼び出しています。
void show_val()
{
rs_print( *(xdat+0) ); rs_putchar('-');
rs_print( *(xdat+1) ); rs_putchar('-');
rs_print( *(xdat+2) ); rs_putchar(' ');
rs_print( *(xdat+3) ); rs_putchar(':');
rs_print( *(xdat+4) ); rs_putchar(':');
rs_print( *(xdat+5) ); crlf();
}
この関数を定義しておくと、ワンショットでも
連続であっても、同じ仕様で使えます。
TeraTermを利用しての表示は、次のようになります。
日時時刻連続表示停止
フラグeflagをクリアして対応。
フラグがセットされていれば、連続表示になりますが
リセットされているので、対応関数を呼び出さず表示
が実行されません。
日時時刻表示
日時時刻のワンショット表示なので、関数show_valを
呼び出すだけで済ましています。
日付時刻設定
日付時刻設定は、次の関数を呼び出して対応。
void putDateTime()
{
byte ptr ;
byte ii ;
byte xdt[6] ;
byte xtmp ;
/* set base pointer */
ptr = 1 ;
/* generate BCD code */
for ( ii = 0 ; ii < 6 ; ii++ ) {
xtmp = *(sbuf+ptr) - '0' ;
ptr++ ;
xtmp <<= 4 ;
xtmp += *(sbuf+ptr) - '0' ;
*(xdt+ii) = xtmp ;
ptr++ ;
}
/* store */
Wire.beginTransmission(RTC_ADRS);
Wire.write(0x00); /* start register addess */
Wire.write(*(xdt+5));
Wire.write(*(xdt+4));
Wire.write(*(xdt+3));
Wire.write(0x00); /* Sunday */
Wire.write(*(xdt+2));
Wire.write(*(xdt+1));
Wire.write(*(xdt+0));
Wire.endTransmission();
}
受信バッファに、日付時刻が格納されているので
取り出して配列に格納。
このとき、BCD形式になるように、細工します。
RTCにパラメータを転送するには、データシートに
書かれていた手順を利用。
この説明だけでは、連続してパラメータを書き込む
ことができるかは判断できませんでした。
次の記述があったので、連続してもよいと理解。
ここまでで、必要な内容を一通り定義したので
setupを記述しておきます。
void setup()
{
Serial.begin(9600);
rs_puts("Hello !");
crlf();
/* port value */
PORTB = 0x00 ;
PORTC = 0x00 ;
PORTD = 0x00 ;
/* port direction */
DDRB = 0xff ;
DDRC = 0xf0 ;
DDRD = 0xfe ;
/* flags */
tflag = OFF ;
eflag = OFF ;
uflag = OFF ;
/* initialize LCD */
lcd.begin( 16, 2 );
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Measure");
lcd.setCursor(0, 1);
lcd.print("Hello, world!");
/* LCD string default */
for ( byte i = 0 ; i < 16 ; i++ ) { *(line0+i) = ' ' ; }
*(line0+16) = '\0';
/* enable RTC */
Wire.begin();
/* set informations */
Wire.beginTransmission(RTC_ADRS);
Wire.write(0xE0);
Wire.write(0x20); /* 24hourMode */
Wire.write(0x00); /* PON Clear */
Wire.endTransmission();
delay(1);
/* set YYMMWDhhmmss */
Wire.beginTransmission(RTC_ADRS);
Wire.write(0x00); /* start register addess */
Wire.write(0x55); /* 55 */
Wire.write(0x59); /* 59 */
Wire.write(0x02); /* 2 */
Wire.write(0x00); /* Sunday */
Wire.write(0x27); /* 27 */
Wire.write(0x12); /* 12 (December) */
Wire.write(0x09); /* year */
Wire.endTransmission();
delay(1);
/* 1000ms period */
MsTimer2::set(XINTERVAL,update_trigger);
/* enable */
MsTimer2::start();
}
RTCを24時間制で使うのと、パワーオンしたときの
日時時刻設定を入れてます。日時時刻は、コマンドで
いつでも変更できるので、思いつくままの値を格納。
1秒ごとにRTCから情報取得するため、MsTimer2による
割込みで、イベントフラグを使った通知をするため
初期化を入れてます。
イベント通知フラグをセットするだけの関数なので
次のように単純。
void update_trigger()
{
tflag = ON ;
}
まとめてスケッチにすると、次のようになります。
/*
rtctst.ino
PORTB
PB5 (output) LED
PB4 (output) --
PB3 (output) --
PB2 (output) --
PB1 (output) --
PB0 (output) --
PORTC
PB5 (output) SCL
PB4 (output) SDA
PB3 (input) --
PB2 (input) --
PB1 (input) --
PB0 (input) --
PORTD
PD7 (output) D7
PD6 (output) D6
PD5 (output) D5
PD4 (output) D4
PD3 (output) E
PD2 (output) RS
PD1 (output) TxD
PD0 (input) RxD
*/
#include <MsTimer2.h>
#include <LiquidCrystal.h>
#include <Wire.h>
#define OFF 0
#define ON OFF+1
#define XINTERVAL 1000
#define XLIMIT 35
#define RTC_ADRS 0x32
#define LED_BIT 5
/* variables */
boolean tflag ;
boolean eflag ;
boolean uflag ;
char sbuf[16] ;
byte sindex ;
byte cmd ;
byte xdat[6] ;
byte ydat[6] ;
char line0[17];
/* LCD rs , e , D4 , D5 , D6 , D7 */
LiquidCrystal lcd( 2, 3, 4, 5, 6, 7 );
void update_trigger();
void show_help();
void rs_putchar(char x);
void rs_puts(char *ptr);
void crlf();
void show_val();
void rs_print(byte x);
void getDateTime();
void putDateTime();
void makeString();
void setup()
{
Serial.begin(9600);
rs_puts("Hello !");
crlf();
/* port value */
PORTB = 0x00 ;
PORTC = 0x00 ;
PORTD = 0x00 ;
/* port direction */
DDRB = 0xff ;
DDRC = 0xf0 ;
DDRD = 0xfe ;
/* flags */
tflag = OFF ;
eflag = OFF ;
uflag = OFF ;
/* initialize LCD */
lcd.begin( 16, 2 );
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Measure");
lcd.setCursor(0, 1);
lcd.print("Hello, world!");
/* LCD string default */
for ( byte i = 0 ; i < 16 ; i++ ) { *(line0+i) = ' ' ; }
*(line0+16) = '\0';
/* enable RTC */
Wire.begin();
/* set informations */
Wire.beginTransmission(RTC_ADRS);
Wire.write(0xE0);
Wire.write(0x20); /* 24hourMode */
Wire.write(0x00); /* PON Clear */
Wire.endTransmission();
delay(1);
/* set YYMMWDhhmmss */
Wire.beginTransmission(RTC_ADRS);
Wire.write(0x00); /* start register addess */
Wire.write(0x55); /* 55 */
Wire.write(0x59); /* 59 */
Wire.write(0x02); /* 2 */
Wire.write(0x00); /* Sunday */
Wire.write(0x27); /* 27 */
Wire.write(0x12); /* 12 (December) */
Wire.write(0x09); /* year */
Wire.endTransmission();
delay(1);
/* 1000ms period */
MsTimer2::set(XINTERVAL,update_trigger);
/* enable */
MsTimer2::start();
}
void loop()
{
/* command interpreter */
if ( uflag == ON ) {
/* clear flag */
uflag = OFF ;
/* get command */
cmd = *(sbuf+0) ;
/* help */
if ( cmd == '?' ) { show_help(); }
/* enable */
if ( cmd == 'G' ) { eflag = ON ; }
/* disable */
if ( cmd == 'g' ) { eflag = OFF; }
/* show */
if ( cmd == 'S' ) { show_val(); }
/* date */
if ( cmd == 'D' ) { putDateTime(); }
/* new line */
crlf() ;
}
if ( tflag == ON ) {
/* clear flag */
tflag = OFF ;
/* get date time */
getDateTime();
/* update */
makeString();
lcd.setCursor(0,0);
lcd.print( line0 );
if ( eflag == ON ) { show_val(); }
}
}
void update_trigger()
{
tflag = ON ;
}
void show_help()
{
rs_puts("? help") ; crlf();
rs_puts("G enable") ; crlf();
rs_puts("g disable") ; crlf();
rs_puts("S show") ; crlf();
rs_puts("D set date time"); crlf();
}
void rs_putchar(char x)
{
Serial.write(x);
}
void rs_puts(char *ptr)
{
while ( *ptr ) {
rs_putchar( *ptr );
ptr++;
}
}
void crlf()
{
rs_putchar('\r');
rs_putchar('\n');
}
void show_val()
{
rs_print( *(xdat+0) ); rs_putchar('-');
rs_print( *(xdat+1) ); rs_putchar('-');
rs_print( *(xdat+2) ); rs_putchar(' ');
rs_print( *(xdat+3) ); rs_putchar(':');
rs_print( *(xdat+4) ); rs_putchar(':');
rs_print( *(xdat+5) ); crlf();
}
void rs_print(byte x)
{
char msg[3] ;
/* dummy */
*(msg+2) = '\0' ;
/* lower */
*(msg+1) = (x & 15) + '0' ;
/* upper */
*(msg+0) = ((x >> 4) & 15) + '0' ;
/* show */
rs_puts( msg );
}
void putDateTime()
{
byte ptr ;
byte ii ;
byte xdt[6] ;
byte xtmp ;
/* set base pointer */
ptr = 1 ;
/* generate BCD code */
for ( ii = 0 ; ii < 6 ; ii++ ) {
xtmp = *(sbuf+ptr) - '0' ;
ptr++ ;
xtmp <<= 4 ;
xtmp += *(sbuf+ptr) - '0' ;
*(xdt+ii) = xtmp ;
ptr++ ;
}
/* store */
Wire.beginTransmission(RTC_ADRS);
Wire.write(0x00); /* start register addess */
Wire.write(*(xdt+5));
Wire.write(*(xdt+4));
Wire.write(*(xdt+3));
Wire.write(0x00); /* Sunday */
Wire.write(*(xdt+2));
Wire.write(*(xdt+1));
Wire.write(*(xdt+0));
Wire.endTransmission();
}
void getDateTime()
{
Wire.requestFrom(RTC_ADRS,8);
Wire.read(); /* 0x0f (fixed dummy */
*(xdat+5) = Wire.read(); /* ss */
*(xdat+4) = Wire.read(); /* mm */
*(xdat+3) = Wire.read(); /* hh */
Wire.read(); /* weekday */
*(xdat+2) = Wire.read(); /* DD */
*(xdat+1) = Wire.read(); /* MM */
*(xdat+0) = Wire.read(); /* YY */
}
void makeString()
{
char msg[2] ;
byte tmp ;
byte ii ;
byte jj ;
/* clear */
for ( ii = 0 ; ii < 16 ; ii++ ) { *(line0+ii) = ' ' ; }
/* copy */
for ( ii = 0 ; ii < 6 ; ii++ ) { *(ydat+ii) = *(xdat+ii) ; }
/* YY-MM-DD hh mm ss */
for ( ii = 0 ; ii < 6 ; ii++ ) {
/* get */
tmp = *(ydat+ii) ;
/* seperate */
*(msg+1) = (tmp & 15) + '0' ;
tmp >>= 4 ;
*(msg+0) = tmp + '0' ;
/* calcuate pointer */
jj = (ii << 1) ;
/* store */
*(line0 + jj ) = *(msg+0);
*(line0 + jj + 1) = *(msg+1);
}
}
/* receive interrupt */
void serialEvent()
{
char ch;
if ( Serial.available() > 0 ) {
/* get 1 character */
ch = Serial.read();
/* store */
*(sbuf+sindex) = ch ;
/* increment */
sindex++ ;
/* judge */
if ( ch == '\r' ) {
sindex = 0 ;
uflag = ON ;
}
}
}
3つのライブラリを利用したので、IICデバイス、LCD
の操作が簡単になりました。
Arduinoは、ユニバーサル基板上に実装し
RTC、LCDとは、ピンヘッダで接続します。
RTCは、IICバスで信号線を次のように割り当て。
- Vdd
- SCL
- SDA
- Vss
SCL、SDAは、2.2kΩでプルアップしています。
LCDは、データバスを4ビットとして
信号線を次のように割り当て。
- Vdd
- D7
- D6
- D5
- D4
- E
- RS
- (no connection)
- (no connection)
- Vss
LCDは14ピンのコネクタがあるので、中継基板で
10ピンから14ピンに変換し、コントラスト調整
に使う可変抵抗器をつけました。
目次
前
次