 
 
 
 
abc714ではI2C接続LCD表示を紹介したが、もう少し実用的な応用として、リアルタイムモジュール(RTC-8564NB)を使って、「年月日・時分秒」を液晶LCD(AQM0802M)に表示させることにした。
 ネット上で同様の応用例がないか調べたところ、すぐ使えそうなスケッチ例が紹介されており、これをそのまま読み込んでArduino-UNOに書き込んでみたところ、全く動作しなかった。スケッチを確認してみると、RTCのアドレスが32Hとなっており、付属マニュアルに記述された51Hと異なっていた。そこで、RTCアドレスを変更し、再度Arduino-UNOに書き混んでみたところ、今度はLCDに文字が表示されるが、数値はデタラメでコロコロと変わり安定しない。どうも正しいレジスタアドレスが設定されていないらしい。Wireの基本に戻って以下のようにRTCの「書き込み」と「読み出し」プログラムを作成したところ、何とか正しい表示が出来るようになった。
 なお、RTCの内部データはBCDデータとなっているので、整数データをBCDに変換して書き込み、読み出し時には2進数に変換した。また、プッシュボタンを押すと、「秒表示」と「曜日表示」を変更できる。
void rtc_write(byte adr, byte data){
  Wire.beginTransmission(RTC_Adr);
  Wire.write(adr);              // レジスタアドレス指定
  Wire.write(data);             // データ書込み
  Wire.endTransmission();
}
int rtc_read(byte adr){
  Wire.beginTransmission(RTC_Adr);
  Wire.write(adr);              // レジスタアドレス指定
  Wire.endTransmission();
  Wire.requestFrom(RTC_Adr,1);  // 1バイト送信要求
  return Wire.read();            // データ取得
}
【おまけ】
 RTC-8564NBには曜日の自動設定機能が無いので、曜日データ(0-6)を得るには、下記に示す「ツェラーの式」が便利。
 0(日曜日)〜6(土曜日)を返す(y:年、m:月、d:日)
  int subZeller(int y, int m, int d ){
    if(m <3) { y--;  m += 12;  }
    return (y + y/4 - y/100 + y/400 + (13*m + 8 )/5 + d )%7;
  }
|  |  |  | 
| ブレッドボード配線(モードボタン付) | RTCを秒表示例 (曜日表示例) | RTC表示回路図 | 
| // リアルタイムクロック( rtc-8564nb + LCD , SCL=A5 SDA=A4 ) #include | 
//********* BCD <-> Binary ( 1 byte用 ) *******
unsigned int bcd2bin(char dt){
  return (( dt >> 4 ) * 10 ) + ( dt & 0x0F);
}
unsigned int bin2bcd(unsigned num){
  num = num % 100;                  // numは0〜99
  return (( num / 10 ) << 4 ) | ( num % 10 );
}
//***** リアルタイムクロック *******
void rtc_write(byte adr, byte data){
  Wire.beginTransmission(RTC_Adr);
  Wire.write(adr);                  // アドレス指定
  Wire.write(data);                 // データ書込み
  Wire.endTransmission();
  delay(1);  
}
int rtc_read(byte adr){
  Wire.beginTransmission(RTC_Adr);
  Wire.write(adr);                  // アドレス指定
  Wire.endTransmission();
  Wire.requestFrom(RTC_Adr,1);      // データ1バイト送信要求
  return Wire.read();               // データ取得
}
void rtc_write_data(){
  rtc_write(R_SECONDS,bin2bcd(seconds));   // 秒
  rtc_write(R_MINUTES,bin2bcd(minutes));   // 分
  rtc_write(R_HOURS,  bin2bcd(hours));     // 時
  rtc_write(R_DAYS,   bin2bcd(days));      // 日
  rtc_write(R_WEEKS,  bin2bcd(weeks));     // 曜日
  rtc_write(R_MONTHS, bin2bcd(months));    // 月
  rtc_write(R_YEARS,  bin2bcd(years));     // 年  
}
void rtc_read_data(void){
  seconds = bcd2bin( rtc_read(R_SECONDS) & 0x7F );  // 秒
  minutes = bcd2bin( rtc_read(R_MINUTES) & 0x7F );  // 分
  hours   = bcd2bin( rtc_read(R_HOURS)   & 0x3F );  // 時
  days    = bcd2bin( rtc_read(R_DAYS)    & 0x3F );  // 日
  weeks   = bcd2bin( rtc_read(R_WEEKS)   & 0x07 );  // 曜日
  months  = bcd2bin( rtc_read(R_MONTHS)  & 0x1F );  // 月
  years   = bcd2bin( rtc_read(R_YEARS) );           // 年
}
void rtc_begin(void){               // クロック初期値設定
  Serial.println("RTC_begin");   
  rtc_write(R_CMND_1, 0x00);        // 時計機能ON
  rtc_write(R_CMND_2, 0x00);        // タイマーOFF
  Serial.println(weeks);   
  weeks = subZeller( 2000 + years, months, days );
  rtc_write_data();
  delay(1);   
}
int subZeller(int y, int m, int d ){
    if(m <3) { y--;  m += 12;  }
    return (y + y/4 - y/100 + y/400 + (13*m + 8 )/5 + d )%7;
  }
 | 
//***** 液晶表示 ********(使用電源によりどちらか選択) 
//#define BOOST 0x04            // 3.3V用 AQM0802 
  #define BOOST 0x00            // 5.0V用 AQM0802 
int  contrast = 0x20;           // コントラスト初期値
void lcd_cmd(byte cmd){ 
  Wire.beginTransmission(LCD_Adr); 
  Wire.write(0x00);             // コマンド指定 
  Wire.write(cmd);              // コマンド出力 
  Wire.endTransmission();       // ストップ 
  if((cmd == 0x01)||(cmd == 0x02)) // ClearかHomeか 
    delay(2);                   // 2msec待ち 
  else 
    delayMicroseconds(30);      // 30μsec待ち 
} 
 
void lcd_data(byte data){ 
  Wire.beginTransmission(LCD_Adr); // スタート 
  Wire.write(0x40);             // 表示データ指定 
  Wire.write(data);             // 表示データ出力 
  Wire.endTransmission();       // ストップ 
  delayMicroseconds(30);        // 遅延 
} 
 
void lcd_setCursor(byte clm,byte row){ 
  if(row==0) lcd_cmd(0x80+clm);  // カーソル位置 
  if(row==1) lcd_cmd(0xc0+clm); 
} 
void lcd_begin(void){            // LCDの初期化 
  Serial.println("LCD_begin"); 
  delay(10); 
  lcd_cmd(0x38);          // 8ビット2行 標準モード 
  lcd_cmd(0x39);          // 8ビット2行 拡張モード 
  lcd_cmd(0x14);          // OSC 183Hz BIAS 1/5 
  lcd_cmd((byte)(0x70 + (contrast & 0x0F))); 
  lcd_cmd((byte)(0x58 + BOOST + (contrast >> 4)));
  lcd_cmd((byte)0x6B);        // Follwer for 3.3V 
  delay(1); 
  lcd_cmd((byte)0x38);        // 標準モードへ 
  lcd_cmd((byte)0x0C);        // 表示開始 
  lcd_cmd((byte)0x01);        // 画面消去 
  delay(1); 
} 
 
void lcd_clear(void){ 
  lcd_cmd(0x01);              // 全消去コマンド出力 
  delay(2); 
} 
 
void lcd_print(char* ptr){    // 文字列表示関数 
  while(*ptr != 0)            // 文字取り出し 
    lcd_data(*ptr++);         // 文字表示 
} 
 
void lcd_print(int num){     // 整数表示関数
   sprintf(t_buf,"%d",num); 
   lcd_print(t_buf); 
}
 | 
| RTCデータ−LCD表示スケッチ (rtc_lcd.ino), (rtc_lcd.zip) | 
H170M-PlUSマザーボードのマシンで、電源をoffにしたにも関わらず、スロットに装着したカードリーダーのLED電源ランプが点灯したままとなる(起動スタンバイ状態)。
 PC/AT互換機時代からBIOSにはパソコンの電源を管理するAdvanced_Power_Managimentと呼ばれる機能がある。最近のマシンではOSレベルで電源を管理するACPIも増えているが、未だにAPMを備えているものも少なくない。H170M-PLUSもAPMがある。
 ユーザマニュアルを見てみると、delキーを押しながらパソコンを起動するとBIOSモードに入ることができ、さらにAdvancedとすすみ、2.6.8_APM_ConfigurationでErPReadyをクリックし、「S4+S5を有効にする」を選択し、f10で「更新して終了」を実行すると、シャットダウン時にLED電源が消えるようになりました。
 ASUSマザーボードの例ですが、他のマザーボードでも同様の機能がありそうなので、参考まで。
まだまだ現役のwindowsXpマシンが、突然応答が遅くなり、再起動を指示したところ、BIOS画面も表示されず、起動しなくなった。いよいよ寿命かと思いつつ、パソコンを持ち上げてみると、中で音がする。abc-545と同じように、再び放熱フィンが外れたようである。ケースを開けてみるとやはり、アルミのフィンが中で転がっていた。マザーボードを取り外し、フィンの固定ピンの穴に0.8mmのドリルを通して広げ、固定ピンを前回よりも深く差し込んで半田付けし、フィンを取り付けた。また、ノイズが発生していたCPUファン(40mm角、t=10mm)も新品に交換した。
 パソコンを元に組み立て直し通電するとマザーボードの電源LEDが点灯、電源ボタンを押してみるがファンも回らず、全く起動しない。やはりダメかと、バックアップバッテリ(CR2032)を新品に交換し、念のために、PowerSwitchに手持ちのプッシュスイッチをつけて押してみると、今度はファンが回り出し、windowsXpも起動した。故障の根本的原因はケースのプッシュスイッチの不良であった。ケースのスイッチを取り外し、新品のDipタイプのスイッチに交換し、再度組み立て直して、ケースの電源スイッチを押すと、BIOS画面が表示され、WindowsXpも正常に起動した。
 これで、Xpの寿命がまた延びることになった。
|  |  | 
| 外れたアルミ放熱フィン | ケースの不良スイッチ | 
|  |  |  |