目次

RTC制御

 ソーラーパネルの発電量を測定するために
 RTC(Real Time Clock)を使い、日付と時刻
 を記録できるようにしてみました。

 RTCの基本動作を調べることに注力しました。

 利用したRTCは、秋月電子から入手。



 発電量を計算して、EEPROMに日付とともに
 保存したいとして、次のように1枚の基板
 にまとめました。



 RTC内部のレジスタにパラメータを設定する
 パラメータを取得するという2機能を含め
 次のコマンドを用意しました。

 コマンドを決めたなら、インタプリタを定義。

  /* 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バスで信号線を次のように割り当て。
  1. Vdd
  2. SCL
  3. SDA
  4. Vss
 SCL、SDAは、2.2kΩでプルアップしています。  LCDは、データバスを4ビットとして  信号線を次のように割り当て。
  1. Vdd
  2. D7
  3. D6
  4. D5
  5. D4
  6. E
  7. RS
  8. (no connection)
  9. (no connection)
  10. Vss
 LCDは14ピンのコネクタがあるので、中継基板で  10ピンから14ピンに変換し、コントラスト調整  に使う可変抵抗器をつけました。

目次

inserted by FC2 system