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の寿命がまた延びることになった。
外れたアルミ放熱フィン | ケースの不良スイッチ |