マイコン同士のI2C通信

コンピュータ、組み込み

古くから使われている通信方法で、CPUと周辺ペリフェラルとの通信に使われてきました。
Arduinoで使用できるモジュールの多くでも利用できます。
今回マイコン同士を配線して通信します。

やること

やること

Raspberry Pi Pico 同士でI2C通信をします。
マスター(M)にはパソコンから232Cで英数文字列を送信します。
マスターは受信した英数文字列をスレーブ(S)に送信します。
スレーブは受信した英文字列の大文字/小文字を変換してマスターへ返答します。
マスターはスレーブからの返答をパソコンへ送信します。

結果

注意
多くはマスター側にもスレーブ側にもUSBから電源を供給することになります。
スケッチ書き込み時や、動作確認では2枚分のCOM番号が表示されるので、COM番号を間違
えやすいので、よく確認して作業してください。

結果はTeratermで確認しています。
パソコンから入力したデータと読み取ったデータがわかるように、ローカルエコーにレ点が入っています。

赤枠は小文字での英字入力です。入力した’a’に対し’A’を読み取っています(以下省略)
黄枠は数字の入力です。英字ではないので’1’に対して’1’を読み取っています (以下省略)
青枠は大文字での英字入力です。入力した’X’に対して’x’を読み取っています (以下省略)

結果はTeratermで確認しています。
パソコンから入力したデータと読み取ったデータがわかるように、ローカルエコーにレ点が入っています。

使用感

少量のコードでマスターにもスレーブにもできることがわかりました。
マイコン同士(基板同士)の通信は、システム設計をする上で複雑になりトラブルが増えますが
趣味の範疇で行うこういったボードはピン数や性能が限られているので、入力専門であったり出力専門であったりと役割分担をしっかりとした設計を行うことで、モジュールとして独立して再利用できるのではないかと思います。

マイコン同士のI2C通信を利用したコントローラを作ってみました。

[2023/6/18] ロジックレベルが5VのArduino Nanoと 3.3VのRaspberry Pi Picoをロジックレベル変換モジュールを使用してI2C通信してみました。

準備するもの

用意するもの

使うMCUボード用途
Raspberry Pi Pico(M)※I2Cマスター側に使用
Raspberry Pi Pico(S)※I2Cスレーブ側に使用
※(M),(S)は識別のために付加しています。

配線

今回RP2040のI2C0を使用します。
SDAにはGP0、SCLにはGP1を指定していますが、任意で変更することができます。

Raspberry Pi Pico(A)配線 Raspberry Pi Pico(B)
GP0GP0
GP1GP1

スレーブ側もUSBで接続していますが、電源供給のためです。

スケッチ

スケッチの説明

マスター側

パソコンと通信するためのRS232C、I2C通信のマスターとしてポートをオープンします。
パソコンからのデータ受信状態を監視し、データを受信したらI2Cスレーブへスルーします。
I2Cスレーブへはデータを要求し、スレーブから読み取ったデータはパソコンへスルーします。

スレーブ側

スレーブとしてのI2C初期化を行います。
I2Cアドレスの設定と、データの受信、要求時に呼び出される関数を設定します。

オウム返しではデータの送受信が単調すぎるので、データ受信時にはASCコードの英文字であれば、
大文字小文字を入れ替える処理を実行します。

スケッチ

//Programing by たまねぎ
//【スケッチの説明】
//RP2040 CPU搭載基板で使用できます。
//
//Raspberry Pi pico 同士でI2C通信を行います。
//◆◆本スケッチはマスター側です。◆◆
//
//スレーブのI2Cアドレスは0x01です。
//パスコン側からの読み取り文字をスレーブにスルーします。
//スレーブから読み取った文字をパソコン側へスルーします。
//
//【準備】
//・Raspberry Pi pico を2枚用意します。
//Raspberry Pi pico のSDA にGP0, SCLにGP1を使用します。
//2枚のRaspberry Pi pico のGP0同士と、GP1同士を配線します。
//1枚に本スケッチを書き込みます。
//もう一枚にはスレーブ側のスケッチを書き込みます。
//
//【バージョン情報】
// 2022/7/2 : 新規

#include "Wire.h"

#define I2C_ADDR  0x01            //スレーブ側に指定するI2Cアドレス
#define I2C_SDA   0               //SDAにはGP0を使用する
#define I2C_SCL   1               //SCLにはGP1を使用する

uint32_t i = 0;

void setup() 
{

  Serial.begin(115200);           //パソコン側からの読み取りと書き込みに使用します。

  Wire.setSDA(I2C_SDA);           //I2Cアドレスピンを指定して通信開始
  Wire.setSCL(I2C_SCL);
  Wire.begin();
}

void loop() 
{
  char buf_com = 0;
  char buf_i2c = 0;

  //パソコンからの受信処理
  if(Serial.available() == true)
  {
    buf_com = Serial.read();

    //I2Cスレーブへのデータ送信処理
    Wire.beginTransmission(I2C_ADDR);
    Wire.write(buf_com);
    if(Wire.endTransmission(true) != 0)
    {
      Serial.printf("送信エラー\n");            //0 = 成功 / else = エラーとします
    }
    else
    {
      //スレーブへのデータ送信が成功したら、スレーブからの返答を要求する。
      //少し乱暴な書き方ですが、本件は1byte送信,1byte受信で行っています。
      if(Wire.requestFrom(I2C_ADDR, I2C_ADDR) != 0)
      {
        buf_i2c = Wire.read();
      }

      //パソコンへスレーブから読み取ったデータを送信する。
      if(Serial.availableForWrite())
      {
        Serial.write(buf_i2c);
      }
    }
  }  
}
//Programing by たまねぎ
//【スケッチの説明】
//RP2040 CPU搭載基板で使用できます。
//
//Raspberry Pi pico 同士でI2C通信を行います。
//◆◆本スケッチはスレーブ側です。◆◆
//
//スレーブのI2Cアドレスは0x01です。
//パスコン側からの読み取り文字をスレーブにスルーします。
//スレーブから読み取った文字をパソコン側へスルーします。
//
//【準備】
//・Raspberry Pi pico を2枚用意します。
//Raspberry Pi pico のSDA にGP0, SCLにGP1を使用します。
//2枚のRaspberry Pi pico のGP0同士と、GP1同士を配線します。
//1枚に本スケッチを書き込みます。
//もう一枚にはスレーブ側のスケッチを書き込みます。
//
//【バージョン情報】
// 2022/7/2 : 新規

#include "Wire.h"

#define I2C_ADDR  0x01            //スレーブ側に指定するI2Cアドレス
#define I2C_SDA   0               //SDAにはGP0を使用する
#define I2C_SCL   1               //SCLにはGP1を使用する

char gbuf = 0;                     //

//マスターから要求があれば返答する。
void onRequest()
{
  Wire.write(gbuf);
}

//マスターからデータ送られてくると読み取りをする。
//ASCコード上英文字であれば、大文字と小文字を入れ替えて保管する。
void onReceive(int len)
{
  while(Wire.available()){
    gbuf = Wire.read();

    //ASCコード上で、0b1000 0000ビットが0の時、0b0111 0000 のいずれかが 1なら英文字(正確ではありません。)
    if(((gbuf & 0x80) != 0x80) && ((gbuf & 0x40) == 0x40))
    {
      //ASCコード上で 0b0010 0000 のビットを反転させることで、大文字と小文字を入れ替える。
      gbuf = gbuf ^ 0x20;
    }
  }
}

void setup()
{
  //I2Cスレーブの設定
  Wire.setSDA(I2C_SDA);             //通信に使用するI2Cアドレスピンを指定する
  Wire.setSCL(I2C_SCL);
  Wire.onReceive(onReceive);        //マスターからデータを受信したときに呼び出される関数の登録
  Wire.onRequest(onRequest);        //マスターからデータを要求されたときに呼び出される関数の登録
  Wire.begin(I2C_ADDR);             //通信開始
}

void loop()
{
}

関連リンク

コメント

タイトルとURLをコピーしました