RaspberryとATtinyのi2c通信


RapberryとATtinyのi2c通信も色々なところで紹介されていますので、私が改めて紹介することもありませんが、
忘備(自分のため)にまとめておきます。
一番単純なRaspberry側がマスター、Arduino側がスレーブで、1文字のデータをやりとりするサンプルです。

ATtinyはレベルシフトが面倒なので3.3Vで駆動しています。
ATtiny側のスケッチは以下の通りです。
Raspberryからi2c経由で1バイト受け取って、値によりLEDをON/OFFしています。
受信確認のためモデル番号を応答します。

ATtiny4313では動かなかったのでTinyWireSフォルダーに有る usiTwiSlave.c を以下のように変更しています。
修正前(抜粋)
#if defined( __AVR_ATtiny2313__ )
#  define DDR_USI             DDRB
#  define PORT_USI            PORTB
#  define PIN_USI             PINB
#  define PORT_USI_SDA        PB5
#  define PORT_USI_SCL        PB7
#  define PIN_USI_SDA         PINB5
#  define PIN_USI_SCL         PINB7
#  define USI_START_COND_INT  USISIF
#  define USI_START_VECTOR    USI_START_vect
#  define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
#endif

修正後(抜粋)
#if defined( __AVR_ATtiny2313__ )  | \
     defined(__AVR_ATtiny4313__)
#  define DDR_USI             DDRB
#  define PORT_USI            PORTB
#  define PIN_USI             PINB
#  define PORT_USI_SDA        PB5
#  define PORT_USI_SCL        PB7
#  define PIN_USI_SDA         PINB5
#  define PIN_USI_SCL         PINB7
#  define USI_START_COND_INT  USISIF
#  define USI_START_VECTOR    USI_START_vect
#  define USI_OVERFLOW_VECTOR USI_OVERFLOW_vect
#endif

//
// ATtiny I2Cスレーブ sample
//

#include <TinyWireS.h>  // https://github.com/rambo/TinyWire
#define SLAVE_ADDRESS 0x08

// The default buffer size, Can't recall the scope of defines right now
#ifndef TWI_RX_BUFFER_SIZE
#define TWI_RX_BUFFER_SIZE ( 16 )
#endif

#if defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
  #if defined(__AVR_ATtiny44__)
    #define LED1  2
    #define LED2  3
    #define MODEL 44
  #else
    #define LED1  2
    #define LED2  3
    #define MODEL 84
  #endif  
#elif defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
  #if defined(__AVR_ATtiny45__)
    #define LED1  3
    #define LED2  4
    #define MODEL 45
  #else
    #define LED1  3
    #define LED2  4
    #define MODEL 85
  #endif  
#elif defined(__AVR_ATtiny461__) || defined(__AVR_ATtiny861__)
  #if defined(__AVR_ATtiny461__)
    #define LED1  3
    #define LED2  4
    #define MODEL 46
  #else
    #define LED1  3
    #define LED2  4
    #define MODEL 86
  #endif  
#elif defined(__AVR_ATtiny2313__) || defined(__AVR_ATtiny4313__)
  #if defined(__AVR_ATtiny461__)
    #define LED1  3
    #define LED2  4
    #define MODEL 23
  #else
    #define LED1  3
    #define LED2  4
    #define MODEL 43
  #endif  
#endif

int status = 0;

void setup() {
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  digitalWrite(LED1, LOW); // set the LED off
  digitalWrite(LED2, LOW); // set the LED off

  // initialize i2c as slave
  TinyWireS.begin(SLAVE_ADDRESS);

  // define callbacks for i2c communication
  TinyWireS.onReceive(receiveEvent);
  TinyWireS.onRequest(requestEvent);
}

void loop() {
  TinyWireS_stop_check();
}

// callback for received data
void receiveEvent(uint8_t byteCount){
  int number;

  while(TinyWireS.available()) {
    number = TinyWireS.receive();
    if (number == 1){
      digitalWrite(LED1, HIGH); // set the LED on
      status=HIGH;
    } else if (number == 2) {
      digitalWrite(LED1, LOW);  // set the LED off
      status=LOW;
    } else if (number == 3) {
      digitalWrite(LED2, HIGH); // set the LED off
      status=HIGH;
    } else if (number == 4) {
      digitalWrite(LED2, LOW);  // set the LED off
      status=LOW;
    }
  }
}

// callback for sending data
void requestEvent(){
  TinyWireS.send(MODEL);
}

Raspberry側のプログラムは以下の通りです。

#include <wiringPi.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>

/*
cc -o test2 test2.c -lwiringPi
*/

int main (void)
{
    int fd;
    int regad;
    int sdata;
    int Val;

    if ((fd = wiringPiI2CSetup(0x08)) < 0)
    {
        printf("wiringPiI2CSetup failed:\n");
    }

    while(1) {
      printf("Enter i2c register address=");
      scanf("%d",&regad);
      if (regad < 0) break;
      printf("Enter i2c send data=");
      scanf("%d",&sdata);
      if (sdata < 0) break;
      wiringPiI2CWriteReg8(fd,regad,sdata);
      Val = wiringPiI2CReadReg8(fd,regad);
      printf("Val=%d\n",Val);
    }
}

【ATtiny84@1MHz】
以下の結線で動作しますが100%確実に通信できませんでした。
FemtoCowのAddOnを使ってコンパイルしています。


このように何回かは応答コードが期待しない値で戻ります。
モデル番号(この場合84)が戻らないときはリトライする必要があります。


【ATtiny85@1MHz】
以下の結線で動作しますが、こちらも100%確実に通信できませんでした。
FemtoCowのAddOnを使ってコンパイルしています。


このように何回かは応答コードが期待しない値で戻ります。
モデル番号(この場合85)が戻らないときはリトライする必要があります。


【ATtiny861@1MHz】
以下の結線で動作しますが、こちらも100%確実に通信できませんでした。
FemtoCowのAddOnを使ってコンパイルしています。


このように何回かは応答コードが期待しない値で戻ります。
モデル番号(この場合86)が戻らないときはリトライする必要があります。


【ATtiny4313@1MHz】
以下の結線で動作しますが、こちらも100%確実に通信できませんでした。
FemtoCowのAddOnを使ってコンパイルしています。


このように何回かは応答コードが期待しない値で戻ります。
モデル番号(この場合43)が戻らないときはリトライする必要があります。


こちら
でレジスターアドレスを使用した通信を紹介しましたが、
ATtinyの場合も同じようなコードでレジスターアドレスを使用した通信を行うことが可能です。
但し、ATtinyの場合100%確実に通信ができないことから、必ず応答コードを判定し、
期待しない応答コードの時はリトライする必要があります。

リトライを行う事で、ATtinyにデータを通知することはできますが、例えばATtinyのアナログポートの
値を読み出す場合は、値が正しいのかどうか保証できません。