I2C通信
過去にいろいろと実験してきましたが関係ファイルの変更などがあって古いものではわかりにくいものが増えてきました。今後も同じことを繰り返すだろうと思いますが、一区切りを付けて新しい記述をします。
masterにmega328(168,88,48,8)を使い、スレーブに RTC,EEPROM,温度センサ,mega88,tiny2313を使うシングルマスタの通信にはこの項の実験結果から実用になるのではないかと考えます。
重大な注意:
しかし、重要な確認事項や変更すべき問題点は残っていますので実際に使用するときは十分な注意と変更が必要です。
現在明らかなところで、
1 i2c_mstr.c の中で i2c_read_d()関数とi2c_write_d()関数を定義していますが、この中の第2引数はスレーブのHiアドレスを示しています。Hiアドレスを持たないスレーブに対して初期は0xffを指定するよう計画しましたが、途中で0x00を指定するように変更しました。しかし、eepromの場合はHiアドレスが0x00であり、かつ指定する必要がありますので元に戻して0xffを指定する方が問題がありません。使用時はこれを確認して0xffに変更することが必要です。
2 i2c_slv88.h を使用するときは スレーブの d[0]が使えるかどうかの確認と内部アドレスが20を超えるときの確認ができていません。使用時にはこの確認が必要です。
1 mega328によるRTC8564の読み書き 2012.01.06
2 tiny2313スレーブテスト(mega328マスタ) 2012.01.12
3 mega88スレーブテスト(mega328マスタ) 2012.01.19
4 eeprom 24LC64 の読み書き(mega328マスタ) 2012.01.20
5 mega48によるI2CドライブのLCDモジュール 2017.05.13
1 mega328によるRTC8564の読み書き
別項でArduino-IDEを使った読み書きを実験しましたが、avr-gccによる同じ動作をテストすることと、mega328などをマスタとしてI2C動作させるルーチンの改めてのチェックを兼ねて実験しました。
ハードウエアはArduino-IDEで使ったものを変更せずに使っています。機能も同じように 時分秒の時計 として、TeraTermによる標示と時刻設定を持ちます。
以前に作ったmega328用のI2Cファイル i2c_mstr.h i2c_mstr.c のテストとWINAVRでない avr-gcc4.5.1の使用、これまた試行錯誤の makefile のテストも兼ねています。ファイルは作るときは周辺の条件を覚えているのですが時間が経つと多くを忘れて再利用できないケースが出てきます。そのためにこのヘッダファイルの使用上の注意も書いてみましたi2c_mstr_readme.txt。整理の都合上ファイル名は変更しています。
実際のプログラムは rtc8564-pc.c です(これも整理のために名前が変わっています)。別項のuartサンプルでまずuartのテストを行い、その上でI2Cの部分を書き加えました。できてみると簡単なことですが思い違いや勘違い、おまけに忘れていたことがあって思いの外に時間がかかってしまいました。
Arduino-IDEに比べると複雑なプログラムになりますがhexファイルは遥かに小さくなっています。プログラムが思うように動かないのは常なのですがArduino-IDEで動作しているのでハードウエアは問題がないはずだと確信して進めますからArduino-IDEがこの開発にも役立っているといえるでしょう。
忘れては思い出し、また忘れて・・・これも楽しみの一つかもしれません。しかし、そのお陰で進歩したファイルもあります。次にmega328をI2Cのマスタとして使うときは今よりはスムーズに進められると思います。
リセットボタンを押すと2つのメニューが表示されます。一つは hhmmss で時刻を設定する場合で、他はバックアップのRTCをそのまま読み出すものです。TeraTermに時分秒が表示されますが簡単に済ませるために990ミリ秒毎の表示になっています。
2012.01.08追記
同じソースで mega48 mega88p mega168p について試してみました。hexファイルはそのままでは使えませんが、それぞれのデバイスについて再コンパイルするとすべて正常に動作しました。このことから I2Cルーチン、UARTルーチンはそのまま使えそうです。
1 mega328によるRTC8564の読み書き おわり
2 tiny2313スレーブテスト(mega328マスタ)
下記1のmega328ブレッドボードとtiny2313専用ブレッドボードを使って過去に改変したtiny2313スレーブドライバの確認をしました。
Tiny2313のハードウエア構成は、クロック水晶8MHz、I2CSDAはPB5、SCLはPB7。
LEDアレイは負論理で MSB PD6 PD5 PD4 PD3 PB3 PB2 PB1 PB0 LSB の接続です。
マスタのmega328は1のボードに変更はありません。(クロック水晶16MHz)
実験の主たる目的は以前に改変して製作したtiny2313用I2Cスレーブドライバの確認です。完全に動いたと記憶しているのですがしばらく使わないのでわかりません。現にmega328マスタの取り扱いにもかなりの復習を要しました。
このドライバは16バイトの連続データを送受することによりマスタからはスレーブの16バイトの変数がスレーブと共有できる感じで扱えるものです。
したがってそのプログラム構成は
1 TeraTerm経由でマスタに文字列データを書き込む
2 マスタはその文字列データをスレーブに送る
3 スレーブは送られたデータの一部を変更する(スレーブの自由度)
4 マスタはデータを読み出し、一部変更されたものをTeraTermに表示する
としてデータは意図通り管理されていることを確認します。
実は、通信に使われるPB7,PB5とLEDをぶら下げたPB3:PB0の処理が十分理解できなくて難儀しました。PORTBの処理を間違えると想像できない誤動作をして原因解明に苦労しました。またしても昔の自分は他人になっていたようです。
最終的には思い通りの結果が得られてほっとしたところです。
このドライバは「Donald R. Blake氏作TWIドライバ(usiTwiSlave.h と usiTwiSlave.c)」が原典ですがポーリングで受けているためにポーリングの間隔が空くとデータを取り込めなくなる点で使いにくいものでしたが一度バラして再構築することでデータバッファの共有や割り込み処理で合理的に動くようにしたものです。
新しく作ったreadmeから抜粋します。
main.cのフォルダに i2c_slv2313.h i2c_slv2313.c の2つのファイルをコピーする。
Tiny2313のmain.cに次を設定する
1 #include "i2c_slv2313.h"
2 static const uint8_t TWI_slaveAddress = 0x22; // 7ビットで指定する(W=0x44,R=0x45)
3 main()の中に usiTwiSlaveInit( TWI_slaveAddress ); // I2C Slave initialization を書く
4 main()の中に sei() //割り込み許可
5 main()の中で DDRBの設定に注意する。 DDRB &= 0b0X0XXXXX; として bit7(SCL),bit5(SDA)はHiZにする。
6 同様に PORTB &= 0b0X0XXXXX; としてプルアップを防ぐ。
makefileに i2c_slv2313.c の同時コンパイルを書く。
ヘッダファイルに uint8_t t[16] が宣言されているのでmain.cでは定義しないこと。
mega328のマスタから i2c_write_d(0x44,0,0,16);を実行すると内部アドレスに関係なくtn2313の
t[16] に d[16] の16バイトが転送される(tn2313の割り込みで処理されるからdelayがあっても可)。
このとき16バイトを一括送信すること。
mega328のマスタから i2c_read_d(0x44,0,0,16);を実行すると内部アドレスに関係なくtn2313の
t[16] が読み出され d[16] の16バイトに読み出される。このとき16バイトを一括受信すること。
スレーブのプログラムとマスタのプログラムです。
.hファイルと.cファイルは整理の都合上readmeと合わせて一つのテキストファイルにしました。もし使われるならエディタで切り分けてください。作ってみて自分流には動きました、と言うだけですから完全の保証はありません。自己責任でお願いします。
スレーブ用です。ついでにマスタ用です。
2 tiny2313スレーブテスト(mega328マスタ) 完 筆者メモ F:\prA\_2012\i2c\m328-t2313
3 mega88スレーブテスト(mega328マスタ)
下記1のmega328ブレッドボードと過去に製作したmega88スレーブボードを使ってmega88用のスレーブドライバの確認をしました。
ハードウエアは回路図のとおり SCK とSDA に専用の 28番と27番ピンを使っています。クロックは内蔵RC発振の8MHzです。
この構成ではPORTBとPORTDがフリーになりますからIOの自由度が高くなります。ポートBにインジケーターのLEDをつなぎ、ポートDにスイッチ入力をつないでいますが今回の実験ではスイッチ入力は使っていません。
スレーブのドライバソフトは自分で改変したmega88用のものを使いました。mega88のほかmega168でも使用できるはずです。このドライバではスレーブの d[0]〜d[31]の32バイトがマスタとスレーブの共有変数になります。
下のmega328マスタで使っている i2c_write_d() と i2c_read_d() で通信ができました。
スレーブのプログラムでは
#include "i2c_slv_x8.h"
int main(void) {
MyAdrs = 0x22; // 自I2Cスレーブ・アドレス 7ビット
I2CSlInit(MyAdrs); // I2Cスレーブ初期化
sei();
}
と書くだけでスレーブとして動作できます。
Tiny2313に比べるとデバイスは大きく、高価になりますが、IOポートが多く使えて使いやすくて便利だと思います。またAD変換が必要なときも便利でしょう。アマチュアの単品製作にはこちらの方が便利かもしれません。
通信実験に使ったマスタのプログラムとスレーブのプログラムです。
ドライバファイルは 「 (07/11/15 www.wsnak.com T.Nakao)氏のプログラムを割り込みで使用するように変更して、 その中からソースファイルとヘッダファイルを作った。 (ソースファイルとして、ヘッダファイルとして正しいものかどうかは確認していない)」という状態にあること及び「内部アドレスは0〜20しか指定できない」「d[0]は読み書きできない」ということが製作段階であったので確認すべきですがそれができていません。実際に使うときはその点の確認が必要です。
.hファイルと.cファイルは整理の都合上readmeと合わせて一つのテキストファイルi2c_slv_x8.hcr.txtにしました。もし使われるならエディタで切り分けてください。作ってみて自分流には動きました、と言うだけですから完全の保証はありません。自己責任でお願いします。
3 mega88スレーブテスト(mega328マスタ) 完 筆者メモ F:\prA\_2012\i2c\m328-t2313
4 eeprom 24LC64 の読み書き(mega328マスタ)
I2C通信規格のEEPROMとして 24LC64 の読み書き実験をしました。
このデバイスは マイクロチップ製 64kビットEEPROM(8kx8)でI2Cインターフェース、動作電圧:2.5〜5.5V、8ピンDIP というものです。チップアドレスは 1 0 1 0 A2 A1 A0 R/W となっていて A2〜A0の設定で8個が同時に使えるようです。
pin番号と端子機能は 1:A0 2:A1 3:A0 4:GND 5:SDA 6:SCL 7:WP 8:Vcc (wp=write protect) です。
容量から内部アドレスはHIGH,LOWの2バイトを指定する必要があります。ここではA2〜A0をLoとして、W=0xa0 R=0xa1 を使用します。
書き込み時には時間がかかりますから1バイト書く毎に5msのウエイトを入れています。さらに最後に10msのウエイトを入れました。
ライブラリのI2C書き込みルーチンに埋め込みましたのでウエイトが不必要なRTCなどの書き込みでもウエイトがかかりますが、通常の使用では1回の書き込みでせいぜい0.1〜0.2秒ですから実用的には問題はありません。
EEPROMはI2Cの勉強のために購入しました(値段も安かった)が、音楽やグラフィックに興味が無いので使途は決まっていません。
この項で使っているmega328のマスタライブラリで問題なく読み書きできるようです。ただ、内部アドレスHiに0x00を指定する必要がありますので i2c_read_d() とi2c_write_d() 関数の第2引数は0xff時に無効とするようにi2c_mstr.cを書き換える必要があります。
回路図とテストプログラムは省略します。
4 eeprom 24LC64 の読み書き(mega328マスタ) 完
筆者メモ F:\prA\_2012\i2c\m328-t2313
5 mega48によるI2CドライブのLCDモジュール 2017.05.13
キャラクタLCDは便利な表示器ですが制御に6本のIOが必要です。ESP8266などはメモリや機能は豊富なのですがIOが多くありません。それを補うためにR8C/M12A(単価が安い)を使ったLCDモジュールを作りましたがMCUにI2Cスレーブ機能がないのでuartを使いました。
I2Cが使えれば専用のIOが必要なく、uartのように他に影響を及ぼすこともなく使えて便利です。過去にmega88をI2Cスレーブとして使う方法を考えたことがありますのでmega48で試してみました。mega48はTQFPタイプが手持ちにあるのでこれを使うと実装も小さくできます。
ジャンク箱に過去に使ったmega48のはんだ付けボードがあったのでそれに追加してテストしました。
久しぶりにAVRを使うので、まずはmega48でLCDが表示できるようにせねばなりません。もの忘れで少々難儀しました。マスターとしてESP8266を使うのでLCDは3.3Vで駆動できる必要があります。負電源を用意して3.3Vで使えるLCDを準備しました。
のち、mega88のスレーブ機能をそのまま移植しましたがありがたいことに問題なく動いてくれました。もっとも、渡すデータが良くわからないので試行錯誤に時間がかかりました。
最終的にArduino-IDEで最初に渡すバイトはゼロ(10進数のゼロ。数字ではない)を書き、その次のバイトデータからスレーブに渡ることがわかりました。
横着ですが簡単にするためにスレーブに送るデータは17バイトの文字として、最初のバイトデータは文字の1か2として、これによって行を決めます。残りの16バイトの文字が表示されることになります。文字列の最後に空白があればそれも含めて16バイト文字列に整えてから送ることにします。
マスタはArduino-IDEで書きますが、書式が簡単で嬉しくなります。
#include <Wire.h>
void setup() {
Wire.begin();//master
}
void loop() {
Wire.beginTransmission(0x22);
Wire.write(0);
Wire.write("1ASDF1234567890ab");
Wire.endTransmission();
}
これだけで送ることができます。uartの流用と違って幾つものI2Cデバイスと通信することができます。I2CのありがたさとArduino-IDEの便利さを痛感します。現在はボード上ですがめどがつきましたのでLCDに取り付ける形にする予定です。
スレーブのプログラムとマスタのプログラムです。
筆者メモ C:\_0MicroComp\Arduino181\sketchbook\201705\_8266_mega48lcd_1 C:\_0MicroComp\AVR\prA\_2017\m48_i2c_LCDtest01
5 mega48によるI2CドライブのLCDモジュール 完
筆者メモ C:\_0MicroComp\Arduino181\sketchbook\201705\_8266_mega48lcd_1
工事中
4 eeprom 24LC64 の読み書き(mega328マスタ)
4 eeprom 24LC64 の読み書き(mega328マスタ) 完
筆者メモ F:\prA\_2012\i2c\m328-t2313