I2Cの実験(4/6)

1/6 )( 2/6 )( 3/6 ) ( 5/6 )( 6/6 )  〔マイコンのトップに戻る〕


《PICでのマルチマスターの通信》

 通常の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
 にて動作させています。


実験の風景

実験の風景
 写真の左側上から、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.