2015年8月11日火曜日

AVRをI2Cスレーブとして使う スレーブからのデータ送信

AVRの参考書『AVRマイコン・リファレンス・ブック―AVRのCPUアーキテクチャ、豊富な内蔵周辺機能を詳細解説 (マイコン活用シリーズ)』や、『トランジスタ技術2014年10月号』のI2Cの詳解記事、NXPのI2Cの仕様書(http://www.nxp.com/documents/user_manual/UM10204_JA.pdf)を睡眠導入剤代わりにパラパラと眺めてもう少しI2Cに深入りしてみました。

仕様は複雑ですが、ArduinoのexampleにはWire/master_readerというマスター側が受信するサンプルプログラムがあるし、AVR側のSlaveプログラムも読み取るレジスタの値を多少いじれば大丈夫そうなのでやってみました。

配線図


タクトスイッチが並んでる基板は、スイッチの押し下げで信号線がGNDに落ちる仕様です。

ArduinoとAVRの両方にプログラムを書き込まないといけないので、常にどちらかのUSBを引っこ抜いて実験しました。

※AVRのRESETピン(ピン1)がArduinoからの制御とAVRプログラマーの制御が干渉するのでちょっとハマってしまいました(^q^;


Arduinoをマスター/受信で使う


Wire/master_readerを元に多少改変しました。

#include <Wire.h>
#include <stdio.h>

void setup()
{
  Wire.begin();        // join i2c bus (address optional for master)
  Serial.begin(9600);  // start serial for output
}

void loop()
{
  Wire.requestFrom(0x7f, 1);    // request 1 bytes from slave device 0x7f

  while (Wire.available())   // slave may send less than requested
  {
    int x = Wire.read(); // receive a byte as character
    
    Serial.println(x);         // print the character
  }

  delay(500);
}

Wire.requestFrom()でスレーブのアドレスと読み込むバイト数を指定するので、アドレスの「0x7f」と「1」バイトを指定。

シリアル通信の出力は読み込んだバイトをそのまま10進数で出力するようにしました。タクトスイッチの押し方の組み合わせで2進→10進変換器みたいな感じになります。

AVRをスレーブ/送信で使う


前回の「スレーブ/受信」のプログラムから改変したところをかいつまんで


状態値の定義部
 //状態値  
 //#define     SR_SLA_ACK     0x60     //SLA_W 受信チェック  
 #define SR_SLA_ACK 0xA8          //SLA_R 受信チェック  
 //#define     SR_DATA_ACK     0x80     //受信パケットチェック  
 #define     SR_DATA_ACK     0xC0          //送信パケットチェック  
 #define     SR_ENDP_ACK     0xA0          //終了orリピートチェック  

I2Cの通信が終了した時、TWSR(TWIステータスレジスタ)に状態値がセットされていますが、アドレスを受け取ってACKを返した時、R/WがRの時(スレーブがマスターにデータを送信するモード)0xA8がセットされるので「SR_SLA_ACK」を0xA8と定義しました。

同様にデータ送信終了時はCxC0がセットされるので「SR_DATA_ACK」も変更しました。

データを送信する関数
 void twi_send(uint8_t sdata){  
      TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN);  
      // Wait for TWINT Flag set.  
      while( !( TWCR & (1<<TWINT) ) );  
      if( (TWSR & 0xF8 ) != SR_SLA_ACK )  
           twi_error();  
      // データを送信  
      TWDR = sdata;  
      TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWEN );  
      // Wait for TWINT Flag set.  
      while( ! ( TWCR & ( 1<<TWINT ) ) );  
      if((TWSR & 0xF8 ) != SR_DATA_ACK)  
           twi_error();  
 }  

ここは受信時とは手順が違うので、まあこうなったということで(^q^;


ソース等はGithubで公開しています。
https://github.com/ryood/Arduino_master_AVR_sleve

Arduino: master/send、AVR: slave/recieveのときは
Arduino/I2C_master_writer
AtmelStudio/I2C_Slave/Reciever

Arduino: master/recieve、AVR: slave/sendのときは
Arduino/master_reader
AtmelStudio/I2C_Slave/Sender

を使います。