ATtinyのUSI-i2c通信(その2)


前回、ATtinyの USI-i2c 通信を紹介しました。
その後、双方向通信ができることがわかりましたので、USI-i2c を使ったデバッグ環境を紹介します。
全体のイメージは以下のようになります。



@i2c_print()では表示データをスレーブ(Nano)に送ります。
Aスレーブ(Nano)はCoolTermを使って受信したデータを表示します。
ここまでは前回紹介しました。
今回スレーブ(Nano)側でボタンが押されるまで待つ機能を追加しました。

Bi2c_wait()ではスレーブ(Nano)側でボタンが押されるまで待ちます。
Cスレーブ(Nano)側でボタンを押します。
Di2c_wait()から抜けます。

今回、スレーブ側にはNanoを使いました。
NanoのD2ピンに普通のボタンを接続します。



Nano側のスケッチは以下の通りです。
i2c応答関数を追加しています。
//
// arduino I2Cスレーブ
// ボタンが押されるまでマスタ側は待つ

#include <Wire.h>

#define BtnPin 2

char buf[100];
volatile boolean process_it;

// i2c受信関数
void receiveEvent(int howMany)
{
  int n = 0;
 
  // 送信された全てのデータを受信
  while(Wire.available()) {
    buf[n++] = (char)Wire.read();   
  }
  buf[n] = '\0';
  process_it = true;
}

// i2c応答関数
// ボタンが押されたときは"1"
// それ以外は"0"を応答する
void requestEvent() {
  if (digitalRead(BtnPin)) {
    Wire.write("1"); // respond with message of Go
    // as expected by master
  } else {
    Wire.write("0"); // respond with message of Wait
  }
}

void setup() {
  Serial.begin (115200);
  process_it = false;
  pinMode(BtnPin,INPUT);
  Wire.begin(8) ;                    // i2cの初期化、自アドレスを8とする
  Wire.onReceive(receiveEvent) ;     // 受信関数の登録
  Wire.onRequest(requestEvent);      // 応答関数の登録
}

void loop() {
  if (process_it) {
    Serial.print (buf);
    process_it = false;
  }  // end of flag set
}

このスケッチをNanoに書き込んだら、CoolTermを起動しNnaoのポートを開きます。
ボーレートは115200です。



ATtiny側に以下のスケッチを書き込みます。
//
// attiny I2Cマスタ
//

#include <TinyWireM.h> //https://github.com/adafruit/TinyWireM

#if defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  #if defined(__AVR_ATtiny44__)
    #define Model "ATtiny44"
  #else
    #define Model "ATtiny84"
  #endif  
#elif defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  #if defined(__AVR_ATtiny45__)
    #define Model "ATtiny45"
  #else
    #define Model "ATtiny85"
  #endif  
#elif defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)
  #if defined(__AVR_ATtiny461__)
    #define Model "ATtiny461"
  #else
    #define Model "ATtiny861"
  #endif  
#elif defined(__AVR_ATtiny4313__)
  #define Model "ATtiny4313"
#endif


// 最大10文字にデータを分割してデータの送信
void i2c_print(char *str) {
  int pnum;
  int jmax;
  pnum = (strlen(str)+9)/10;  //パケット数
  for (int i=0;i<pnum;i++) {
    jmax = i*10+10;
    if (strlen(str) < jmax) jmax = strlen(str);
    TinyWireM.beginTransmission(8) ;  // 通信の開始処理、スレーブのアドレスは8とする
    for (int j=i*10;j<jmax;j++) {  // 1パケットの最大文字数は10文字
      TinyWireM.send(str[j]) ;  // 通信データ送信
    }
    TinyWireM.send(0) ;  // 通信データ送信(終端文字)
    TinyWireM.endTransmission();
  }
}

// Slaveからの応答を待つ
void i2c_wait() {
  delay(100);
  i2c_print("<Push Button!!>\n");

  bool ok;
  ok = 1;
  while (ok) {
    TinyWireM.requestFrom(8, 1);    // request 1 bytes from slave device
    while (TinyWireM.available()) { // slave may send less than requested
      char c = TinyWireM.read();    // receive a byte as character
//      Serial.print(c);       // print the character
      if (c == '1') ok = 0;
    }
  }
  delay(500);
}

void setup()
{
  TinyWireM.begin() ;  // i2cの初期化、マスターとする
  delay(5000) ;        // 5秒後に開始
}

// 繰り返し実行されるメインの処理関数
void loop()
{
  static int num = 0;
  char sbuf[40];

  sprintf(sbuf,"TinyWireM num=%d model=%s\n",num,Model);
  num++;
  i2c_print(sbuf);
  if ((num % 2) == 0) i2c_wait();
//  delay(1000);
}

numが偶数のときだけ i2c_wait() を呼び出します。
i2c_wait() が呼ばれると、CoolTerm に <Push Button> が表示されます。
ボタンを押すと ATtiny側は処理を続行します。

【ATtiny84@1MHz】


【ATtiny85@1MHz】


【ATtiny861@1Mhz】


【ATtiny4313】


今回ボタンは普通のプッシュボタンを使いましたが、状態保持型のボタンを使うと、必要な場所だけで止めることができます。
ATtiny用のデバッグモニターが完成!!