2015年8月28日金曜日

AVRのI2Cを割り込みで使う

AVRをI2Cのスレーブとして使って、割り込みで処理するテストをしてみた。

ブレッドボードの配線は、「AVRをI2Cスレーブとして使う スレーブからのデータ送信」と同じだが、今回はATMega328PではなくてATMega88Vを使っている。


AVRのI2Cのライブラリはいくつか公開されているようだが、テストなのでベタ書きでやってみた。

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

<一部抜粋>
 #define      F_CPU 8000000UL // 8MHz  
 #include      <avr/io.h>  
 #include     <avr/interrupt.h>  
 #include     <util/delay.h>  
 //TWI状態値  
 #define SR_SLA_ACK 0xA8          //SLA_R 受信チェック  
 #define     SR_DATA_ACK     0xC0          //送信パケットチェック  
 #define     SR_ENDP_ACK     0xA0          //終了orリピートチェック  
 volatile uint8_t sdata;  
 void twi_error(){  
      PORTB = 0x02;  
      while(1);  
 }  
 void twi_init(){  
      // 8MHz clk, bit rate 100kHz  
      TWBR = 2;  
      TWSR = 0x02;  
      //slave address  
      TWAR=0xfe;  
      // 割り込みを許可  
      TWCR = ((1<<TWEN) | (1<<TWEA) | (1<<TWIE) | (1<<TWINT));     // Enable TWI port, ACK, IRQ and clear interrupt flag  
 }  

スレーブなのでI2Cのクロック設定は必要なのかどうなのかよくわからないが、I2Cのクロック(F_SCL)は
F_SCL = F_CPU/(16 + 2*TWBR*Prescaler)
とTWBRレジスタとTWSRレジスタのプリスケーラで設定。I2Cのクロックはスタンダードで100kHzなので、AVRを1MHz駆動すると
TWBR*Prescaler = (F_CPU / 2*F_SCL) - 8 = (1MHz / 2*100kHz) - 8 = -3
となってしまって設定できない。AtmelのI2Cのドキュメント「AVR311: Using the TWI module as I2C slave」(doc2565.pdf)の4Pを見ても、SCLを100kHzにするならCPUクロックは1.6MHz以上にしなさい、みたいなことが書いてある。

なのでFuse Bitを書き換えて内蔵RCの8MHz駆動にしてSCLを100kHzに設定した。
Fuse Bit: lFuse:0xE2 hFuse:0xDF eFuse:0x01
I2C割り込みの有効化はTWCRのBITで
TWCR = ((1<<TWEN) | (1<<TWEA) | (1<<TWIE) | (1<<TWINT));     // Enable TWI port, ACK, IRQ and clear interrupt flag  
TWEN、TWEA、TWIE,TWINTに1をセットすると割り込みが有効化されるようだ。

ほんと全然良くわかっていない(^q^;

 ISR (TWI_vect)  
 {  
      // 割り込みごとにLEDを点滅  
      PORTB ^= 0x02;  
      switch (TWSR & 0xF8) {  
      case SR_SLA_ACK:  
           TWDR = sdata;  
           break;  
      case SR_DATA_ACK:  
           break;  
      default:  
           twi_error();  
      }  
      TWCR |= (1<<TWINT);     // Clear TWI interrupt flag  
 }  

割り込みルーチンはかなりシンプル。TWSRレジスタの状態値はいろいろあるが、最低限っぽいこれ動いた。
TWCR |= (1<<TWINT); // Clear TWI interrupt flag
TWINTは割り込み時の処理がひと通り終わったあとに1をセットするらしい。1をセットしてクリアしないと(ややこしい(^q^;)割り込みが続かない。

冒頭の
// 割り込みごとにLEDを点滅
PORTB ^= 0x02;
は割り込みがかかっているかどうかをLEDの点滅で確認するためのデバッグ用の処理だ。

マスター側のArduinoのスケッチは、「AVRをI2Cスレーブとして使う スレーブからのデータ送信」と同じものです。

https://github.com/ryood/Arduino_master_AVR_sleve/tree/master/Arduino/master_reader

動いたからいいもののAVRのI2Cを把握するのはなかなか大変だ.。しんどい話やで(^q^;;;