2015年8月30日日曜日

AVRをI2Cスレーブとして使う スレーブからバイト列の送信

シーケンス入力用のデバイスの仕様を検討した結果、AVRはI2Cのスレーブとして
スイッチの状態[1..8](8bit)|スイッチの状態[9..16](8bit)|POT1のADC値(8bit)|POT2のADC値(8bit)|ロータリーエンコーダーの状態(8bit)
ぐらいは送信しないといけない。

まだ1バイトの送信のテストしかしていないのでバイト列の送信をテストしてみた。

I2Cスレーブのバイト列の送信手順


またまたI2Cの通信仕様を確認した。AVRの参考書「AVRマイコン・リファレンス・ブック 」のTWIインターフェイスの項を読んでぼんやりと理解できた。バイト列のスレーブ送信の手順は

  • マスターからスレーブにデータの送信を要求(SLA+R)
  • スレーブがSLA+Rを受け取って自分のアドレス(SLA)だったらACK(L)を返す
  • スレーブはデータを1バイトごと送信
  • マスターは1バイト受け取るごとにACK(L)を返す
  • マスターは最後のバイトを受け取った時NACK(H)を返す

と、ややこしい(^q^;

最後にマスターがNACKを帰すというのを見落とすと結構こんがらがる。モードによって最後がACKの場合もある。

I2Cの通信自体はこんな感じだが、AVRのI2Cモジュールはこのバイトごとの通信結果をTWSR(TWIステータス・レジスタ)にステータスコードをセットして、TWCR(TWIコントロール・レジスタ)のTWINTビットをセットする。割り込みを有効化している場合はISR(インターラプト・サービス・ルーチン)が呼び出される。

ステータスコードは、AVRのDATASHEETに載っていた。

スレーブ送信モードのステータスコード表

が、わけがわからない(^q^; 「AVRマイコン・リファレンス・ブック 」に日本語訳された表が載っていてこれがなかったら挫折してた(^q^;

AVRのプログラムで使ったステータスコード
 #define SR_SLA_ACK  0xA8          // SLA_R 受信チェック  
 #define SR_DATA_ACK 0xB8          // 送信パケットチェック バイト列の送信  
 #define SR_DATA_NACK 0xC0          // 送信パケットチェック 最後のバイトを送信  

マスター/スレーブを切り替えて使うとかしない限り、スレーブ送信はこの3つのステータスコードを判定すればいいようだ。
→まったく自信はないです

ブレッドボード図



ArduinoはI2Cのマスターとして使う。

タクトスイッチの列でAVRに8bit入力し、マスターのArduinoに送信する。

赤色LED×8と黄色LEDはエラー表示用だ。

AVRのプログラム

Atmel StudioのプロジェクトはGithubで公開しました。
https://github.com/ryood/Arduino_master_AVR_sleve/blob/master/Atmel%20Studio/I2C_Slave_Sender_ATMega88V_Byte_Sequence/I2C_Slave_Sender_ATMega88V_Byte_Sequence.c


とりあえず、8bitの配列を用意して先頭と末尾のバイトにタクトスイッチの読み取り値を入れて、8バイト送信するようにした。

      uint8_t sdata[8] = { 0, 1, 2, 3, 4, 5, 6, 7 };  
      <中略>  
      for(;;) {  
           // swの押し下げ状態をパケットの先頭と末尾に代入  
           sdata[0] = ~PIND;  
           sdata[7] = sdata[0];  
           //データを送信する。  
           twi_send(sdata, 8);  
      }  

デバッグ中にエラーの原因を特定するためにエラー発生時にLEDでTWSRのステータスコードを表示するようにした。

 void twi_error(uint8_t code)  
 {  
      uint8_t twi_status = TWSR & 0xF8;  
      PORTC = (1 << PC0);  
      while(1) {  
           PORTB = code;  
           _delay_ms(1000);  
           PORTB = twi_status;  
           _delay_ms(1000);  
      }  
 }  

twi_error()を呼び出すときに、引数の「uint8_t code」にエラーコードを指定して呼び出す。

エラー状態の時には、エラーコードとエラー時のTWSRのステータスコードを交互にLEDに表示する。

この処理のおかげで結構あっさりバグの原因を特定できた(^q^/

Arduinoのスケッチ


こちらもGithubで公開しました。
https://github.com/ryood/Arduino_master_AVR_sleve/blob/master/Arduino/master_reader_byte_sequence/master_reader_byte_sequence.ino

スレーブから8Byteのデータを受け取って、シリアル通信でそのまま表示するというもの。


I2Cの通信状態


赤色がSDA、黄色がSCL


AVRのI2Cのの処理を割り込みで処理するようにしないといけないが、また次回。