arduinoとTWI(I2C)で接続する機器を使って
遊んでいます。
丁度ぼのぐらしさんが掲示板に書き込んでいただいた
秋月のPIC−16F877ボードとCCS−Cを
使って、何年か前に作ったプログラムがあったので、
それをarduino用に移植してみようという目論見です。
TWIi/fについてはarduinoのofficial libraryies
にWireライブラリが公開されているので、きっと
簡単に行くだろうと。
ただ、16F877とCCS−Cで秋月製RTCを
弄った時には、ACK(アクノリッジ)の有無の制御
で凄くハマッた記憶があります。
http://akizukidenshi.com/catalog/items2.php?q=%22I-00233%22&s=score&p=1&r=1&page=
これですね。500円。引っ張り出してきました。
TWI(I2C)は、オープンドレイン(オープン
コレクタ)出力でバスに参加し、バスは抵抗で
プルアップして使うんですが、秋月のRTCには
このプルアップ抵抗が内蔵されています。
回路図上の2箇所をハンダで短絡させちゃえば、
外付けの抵抗を使わずにscl、sdaの2本線
だけで繋がります。

手前に2箇所こんもりとハンダが盛ってあるところが
その短絡させるところ。これでプルアップが有効に
なります。

配線も簡単。この写真では黄色と青の2本だけ。
WTI(I2C)はこれがいいところ。
さて、プログラムは以前使ったCCS−C用を流用します。
↓こんな感じ。16F877用です。(不等号は全角に変換済み)
#include <16f877.h>
#fuses HS,NOWDT,NOPROTECT,PUT,BROWNOUT,NOLVP
#use delay(CLOCK = 20000000) //クロック周波数指定
#use fast_io(B)
#use i2c(master, sda=PIN_C4, scl=PIN_C3, address=0xa0)
////// 液晶表示ライブラリ設定
#define mode 0
#define input_x input_B //ポートB使用
#define output_x output_B
#define set_tris_x set_tris_B
#define stb PIN_A0
#define rs PIN_A2
#include <lcd_lib.c>
int RTCDATA[16]; //レジスタテーブル 読み込み先配列
///// メイン関数
void main()
{
long data;
int i;
output_float(PIN_C3); //SCLピン定義(ハイインピーダンス)
output_float(PIN_C4); //SDAピン定義(ハイインピーダンス)
set_tris_a(mode); //モードセット
lcd_init(); //LCD初期化
lcd_clear(); //LCD全消去
lcd_cmd(0x80); //1行目の先頭へ
printf(lcd_data,"RTC init."); //初期メッセージ
delay_ms(1000); //立ち上げ〜RTC初期化までの待ち時間
i2c_start();
i2c_write(0xa2); //書き込みモード
i2c_write(0x00); //レジスタアドレス=0をセット
i2c_write(0x00); //コントロールレジスタ1=0x00
i2c_write(0x02); //コントロールレジスタ2=0x02
i2c_write(0x15); //seconds=0x15
i2c_write(0x20); //minutes=0x20
i2c_write(0x12); //hours=0x12
i2c_write(0x25); //days=0x25
i2c_write(0x04); //weekdays=0x04
i2c_write(0x92); //month/century=0x92
i2c_write(0x03); //years=0x03
delay_us(300); //delay 300usec
i2c_stop();
delay_ms(10);
lcd_clear(); //LCD全消去
lcd_cmd(0x80); //1行目の先頭へ
printf(lcd_data,"main loop"); //メインループの処理開始メッセージ
delay_ms(2000); //2秒待ち
lcd_clear(); //LCD全消去
while(1) //永久ループ
{
i2c_start();
i2c_write(0xa2); //書き込みモード
i2c_write(0x00); //レジスタアドレス=0をセット
i2c_start();
i2c_write(0xa3); //読み込みモード
for (i=0; i<15; i++)
{
RTCDATA[i] = i2c_read();
}
RTCDATA[15] = i2c_read(0); //ACK Nothing
i2c_stop(); //stop condition
lcd_cmd(0x80); //1行目の先頭へ
printf(lcd_data,"20%x %x/%x (%x)",RTCDATA[8] ,RTCDATA[7] & 0x1f ,RTCDATA[5] & 0x3f ,RTCDATA[6] & 0x07 );
lcd_cmd(0xC0); //2行目の先頭へ
printf(lcd_data,"%x:%x:%x",RTCDATA[4] & 0x3f,RTCDATA[3] & 0x7f,RTCDATA[2] & 0x7f);
delay_ms(100); //0.1秒待ち
}
}
大昔に作ったプログラムなので、細かいところは
ご勘弁ください。
まぁ、ここまで材料が揃えば、後は簡単でしょう!
と思ったのが大間違え!
やっぱり填まりました。ACK。
arduinoのWireライブラリではACKが付くのか
付かないのかが良く解りません。というか、
明示指定が出来ません!
いや、正確には多分ACKでトラブっているんだと
思うんですけどね。
実はそれ以外にももう一箇所填まっているので。
色々とテストパターンを作ってみて、トラブルの大元が
どこなのかを切り分けてみる作戦に出たんですが、
イマイチ完全には追いきれてません。(TへT)
ライブラリの解説ページもよく読みなおしてみたんですが、
その解説によると、ACKはデータ受信前にあらかじめ
設定しておいて、一気に9個のsclを送りながら、
9個目のsclで事前に決めておいたACKを送出
するらしいことが書いてあるんですが、上手く動きません。
(上手く動かせてないだけ?)
受信文字数は明示指定するので、それを元に内部で
よきに計らってくれているものだと思うのですが…
例によってライブラリも少し覗いてみたんですが、
結局どうすればいいのやら状態。こまった…
他にも色々と実験してみたんですが、この続きは
また次回。
(後日追記)
tokoyaさんの日記を眺めていて、スケッチの間違えに
気付き、訂正したら動きました。↓
http://brown.ap.teacup.com/nekosan0/361.html
http://nekosan0.bake-neko.net/connection_rtc.html
ありがたい!

4