I2Cの実験(4/6)
(
1/6
)(
2/6
)(
3/6
) (
5/6
)(
6/6
)
〔マイコンのトップに戻る〕
通常のI2C通信では、同一バス上にはマスターが1個でスレーブが複数個の構成がほとんどですが、
I2Cの仕様ではマスターを複数個設置出来る様になっています、ですのでPICもこのマルチマスターに
対応しているので実験を行い少し記事にして置きたいと思います。
I2Cの基本話は前の実験記事(1/6 , 2/6 , 3/6)を参照下さい。
マルチマスターでの動作
同一バス上にマスターを複数個設置すると、マスター同士が同時に送信するタイミングが重なる時が
有り"バスの衝突"が発生します。
これが発生するとPICは送信を中断してI2Cポートをアイドル状態にリセットします、
またこの時、"バス衝突割り込みフラグ(BCLIF)"をセットして割り込みを発生させます。
ですので、この割り込みが発生した場合は、スタート コンディションからやり直さないとダメです。
尚、マルチマスターでもマスター同士の通信は出来ませんよ、あくまでマスターとスレーブ間のみです。
バスの衝突が発生するタイミングは、以下の場合らしぃ。
・スタート コンディション時
・リピート スタート コンディション時
・ストップ コンディション(アクノレッジ コンディション)時
・アドレス転送/データ転送時
詳しい動作等は、データシートを参照下さい、こちらに12F1822/1823日本語データシートが有ります。
実験の構成図
マスター1(PIC12F1822)は、
スレーブ2(EEPROM)からデータを読み、
スレーブ1(LCD)にて表示を行う動作を
500ms毎に繰り返します。
マスター2(PIC16F1827)は、
スレーブ2(EEPROM)からデータを読み、
別途作成のLCDモニターにて表示を行う
動作を400ms毎に繰り返します。
尚、マスター2は16F1938でも行っています。
また、I2C通信クロックは、
マスター1が100KHzでマスター2が400KHz
にて動作させています。
実験の風景
![実験の風景](Fig42.jpg)
写真の左側上から、I2C接続小型LCD、
12F1822、EEPROM、16F1827の順です。
写真の右側が別途作成のLCDモニター
でこのモニターのみ電源5Vです。
I2Cプルアップ抵抗は1KΩを使用です。
マルチマスター未対応のプログラムを
それぞれのPICで動作させて見た、
LCDの表示が止まったり、
表示されなかったり等の現象が発生し
"バスの衝突"が起こっているのが
確認出来ました。
でぇ、対応のプログラムを走らせ
表示など正常に更新しているのを
確認しました。
マルチマスター対応のプログラムについて
Myサイトでの過去(2014年11月現在)のI2C関連記事は下記です。
PIC12F1822
秋月電子I2C接続小型LCDモジュールに表示を行う
音声合成LSI(ATP3011F4-PU)を使ってみます
PIC16F1827
EEPROM(24LC256/1024)と接続して読み書きを行って見ます
RTC(リアルタイムクロック)と接続して読み書きを行って見ます
3軸加速度センサで傾斜角度を測定してみます
測距モジュール(GP2Y0E03)で物体の距離を測ってみます
PIC16F1938
気圧センサー(MPL115A2)で大気圧と標高を測定して見ます
この上記の記事で使用した[skI2Clib.c/skI2Clib.h]のライブラリをマルチマスター対応に改造しました。
改造ライブラリを上記記事のライブラリと入換えても動作(シングルマスター)は可能です。
但し、マルチマスターで動作させる場合は、"skI2Clib.c"を利用しているプログラムを変更しないと
マルチマスターでは動作しません。
上記の記事をマルチマスターでも動作する様にプログラムを変更する事は現在考えていません、
それは、マルチマスターで動作させる場面が少ないと言う事と、変更すればプログラムのステップ数が
増える為です。(マルチマスターで利用したい人は自分で変更するかご相談下さい)
ですので、マルチマスター対応のプログラムは[skI2Clib.c/skI2Clib.h]のライブラリのみ行います。
ダウンロードは↓から
12F1822/16F18xx/18F25K22の場合は、"skI2Clib1.lzh"をダウンロード *1)
16F1938/19xxの場合は、"skI2Clib2.lzh"をダウンロード *1)
"skI2Clib.lzh"、12F1822/16F182x/16F193x/18F25K22 共用ライブラリに変更(統合)しました。 *2)
"skI2Clib.lzh"、スレーブデバイスのレジスタから読書きを行う関数を追加。 *3)
"skI2Clib.zip"、
"InitI2C_Master"関数の速度指定は、"_XTAL_FREQ"を見て切替える様に変更。 *4)
現在以降(2018/03/19)これからのI2C関連記事はこのライブラリに切り替えて行います。
skI2Clib.h
I2C通信の関数ライブラリ用ヘッダファイルです。
"skI2Clib.c"を利用する場合に
#include "skI2Clib.h" をプログラムの先頭で記述しないとだめです。
デフォルトではMSSP1を使う様になりますが、PICによってはMSSP2が有ります。
MSSP2側を使う場合は、"#define I2C_MSSP2_USE"を記述して下さい。 *2)
skI2Clib.c
マルチマスター対応の"skI2Clib.c"を使い、マルチマスターにて利用する場合の方法は?
今までは下記の様に記述していました。(skI2CLCDlib.cのLCD_Putc関数での例)
void LCD_Putc(char c)
{
int ans ;
ans = I2C_Start(ST7032_ADRES,RW_0); // スタートコンディションを発行する
if (ans == 0) {
// command word の送信
I2C_Send(0b11000000) ; // control byte の送信(データを指定)
I2C_Send(c) ; // data byte の送信
}
I2C_Stop() ; // ストップコンディションを発行する
__delay_us(26) ;
}
シングルマスターなら上記述でOKですが、
これをマルチマスター対応にプログラムを変更するには
void LCD_Putc(char c)
{
int i ;
// バスの衝突は10回までリトライしたら諦める
for (i=0 ; i < 10 ; i++) {
if (lcd_putc(c) != -1) break ;
__delay_ms(100) ; // 100ms後に再トライ
}
}
int lcd_putc(char c)
{
int ans ;
ans = I2C_Start(ST7032_ADRES,RW_0); // スタートコンディションを発行する
if (ans == 0) {
// command word の送信
ans = I2C_Send(0b11000000) ; // control byte の送信(データを指定)
if (ans == -1) return -1 ; // バスの衝突発生
ans = I2C_Send(c) ; // data byte の送信
if (ans == -1) return -1 ; // バスの衝突発生
}
if (ans == -1) return -1 ; // バスの衝突発生
ans = I2C_Stop() ; // ストップコンディションを発行する
__delay_us(26) ;
return ans ;
}
と、この様に"バスの衝突"が発生した場合は、再度送信を行う仕掛けが必要になります。
I2C通信の関数ライブラリの使い方を説明します。
InitI2C_Master(speed) *1)
I2C通信のマスターモードで初期化を行う処理です。
I2C通信用の速度クロック周波数は100KHz/400KHzでの設定です、
この速度はFOSC(PICのCPU周波数)8MHzの場合で初期化されています。
speed  :I2Cの通信速度を指定します。(0=100KHz 1=400KHz)
尚、機器がI2C通信用の速度クロック周波数400KHzに対応しているならば選択も可能です。
又、通信速度を400KHzにしても、実際は250KHz程しか速度は出ません。
※ I2C通信用の速度設定は、"#define _XTAL_FREQ"の内容を見てプログラムを切替える様に変更
しましたので書換える必要は有りません。
但し、8/16/32/40/48/64MHzの場合だけです、それ以外のクロック(Fosc)を利用する場合は、
こちらかデータシートを参考にSSPADDレジスタを変えて下さい。 *4)
ans = I2C_Start(adrs,rw)
スレーブにスタートコンディションを発行する処理です。
adrs  :スレーブのアドレス(7ビット)を指定します
rw  :スレーブに対する動作の指定をします
0 = スレーブに書込みなさい要求 1 = スレーブに送信しなさい要求
ans   : 0 = 正常 1 = 異常(相手からACKが返ってこない)
  -1 = 他のマスターとのバス衝突発生
ans = I2C_rStart(adrs,rw)
スレーブにリピート・スタートコンディションを発行する処理です。
adrs  :スレーブのアドレス(7ビット)を指定します
rw  :スレーブに対する動作の指定をします
0 = スレーブに書込みなさい要求 1 = スレーブに送信しなさい要求
ans   : 0 = 正常 1 = 異常(相手からACKが返ってこない)
  -1 = 他のマスターとのバス衝突発生
ans = I2C_Stop()
スレーブにストップコンディションを発行する処理です。
ans   : 0 = 正常
  -1 = 他のマスターとのバス衝突発生
ans = I2C_Send(dt)
スレーブにデータを1バイト送信する処理です。
dt  :送信するデータを指定します
ans   : 0 = 正常 1 = 異常(相手からACKが返ってこない又はNOACKを返した)
  -1 = 他のマスターとのバス衝突発生
例)slave_adrsへの2バイト送信(シングルマスターでの記述例)
ans = I2C_Start(slave_adrs,RW_0); // スタートコンディションを発行する
if (ans == 0) {
I2C_Send(dt) ; // 1バイト目を送信する
I2C_Send(dt) ; // 2バイト目を送信する
} else 異常 ;
I2C_Stop() ; // ストップコンディションを発行する
c = I2C_Receive(ack)
スレーブからデータを1バイト受信する処理です。
ack  :スレーブからデータを受信した後に返答するデータを指定します
0 = ACKをスレーブに返す 1 = NOACKをスレーブに返す(受信データが最後なら1)
c  :受信した1バイトのデータを返す
  -1 = 他のマスターとのバス衝突発生
例)slave_adrsから2バイト受信する(シングルマスターでの記述例)
ans = I2C_Start(slave_adrs,RW_1) ; // スタートコンディションを発行する
if (ans == 0) {
dt1 = I2C_Receive(ACK) ; // 1バイト目を受信する
dt2 = I2C_Receive(NOACK) ; // 2バイト目を受信する
} else 異常 ;
I2C_Stop() ; // ストップコンディションを発行する
ans = I2C_SlaveRead(slv_adrs,reg_adrs,*data,kosu) *3)
スレーブのデバイスから指定個数のデータを読み込む処理です。
センサー等のデバイスのレジスターから値を読み取ります。
マルチマスターに対応しています。
slv_adrs :スレーブのデバイスアドレスを指定します(7bitで指定)
reg_adrs :読出すデータのレジスターアドレスを指定します
連続的に読出す場合は、読出すレジスターの先頭アドレスを指定
*data :読出したデータの格納先を指定します
kosu :読出すデータのバイト数を指定します
ans :0=正常 1=異常(相手からACKが返ってこない)
-1=他のマスターとのバス衝突が発生してリトライオーバー
ans = I2C_SlaveWrite(int slv_adrs,char reg_adrs,unsigned char *data,char kosu) *3)
スレーブのデバイスに指定個数のデータを書き込む処理です。
センサー等のデバイスのレジスターに値を書き込みます。
マルチマスターに対応しています。
slv_adrs :スレーブのデバイスアドレスを指定します(7bitで指定)
reg_adrs :書出すデータのレジスターアドレスを指定します
連続的に書出す場合は、書出すレジスターの先頭アドレスを指定
*data :書出すデータの格納先を指定します
kosu :書出すデータのバイト数を指定します
ans :0=正常 1=異常(相手からACKが返ってこない)
-1=他のマスターとのバス衝突が発生してリトライオーバー
InterI2C()
I2C関連の割り込み処理です。
メインプログラムの割込み処理で必ず呼びます。
例)
void interrupt InterFunction( void )
{
// I2C関連の割り込み処理
InterI2C() ;
}
※ 実験(2/6)で"skI2Cmaster.c"を作成していますがこれもマルチマスターには対応しません、
"skI2Clib.c"を使いましょう。
その他
ん〜...、マルチマスターの記事は書いた物の...
マルチマスターで利用する場合のシチュエーションが思い浮かばない...だめだぁ、頭が固い!!
マスター同士も通信が出来れば利用価値も有るのでは?.....
となるとぉ、EEPROMはデータのやり取りなどにも使えそうなのでぇ、
"skMC24xxx.c"のライブラリぐらいはマルチマスター対応にしていても良さげな気もするがぁ....
PS.
この記事を書いた後ふと思い、再度実験を行った。
それは"バスの衝突"はスタート コンディション(I2C_Start)時と
リピート スタート コンディション(I2C_rStart)時のみチェックすれば良いのでは?と。
正常に動作しています、もっと色々なパターン等で動作させて見ないとですがぁ、
もしかしたらスタート時のみチェックでOKかもぉ!、ステップ数が減るね!。
[前ページ"複数バイトの送受信"へ]
[次ページ"ArduinoでソフトI2C通信"へ]
ライブラリ変更(*4) 2018/03/19
スレーブデバイスのレジスタから読書きを行う関数を追加(*3) 2016/09/30
ライブラリ変更(*2) 2015/04/24
変更(*1) 2015/03/04
【きむ茶工房ガレージハウス】
Copyright (C) 2006-2020 Shigehiro Kimura All Rights Reserved.