i2c_slv2313.hcr.txt このファイルは .h .c readme をまとめたものである。 それぞれの区切りは 1212121212121212121212121212121212121212121212121212121212121212121212121212121212121212 であるから それぞれのファイルが必要なときはエディタで切り出して使う。 1212121212121212121212121212121212121212121212121212121212121212121212121212121212121212 /* **************************************************************************** i2c_slv2313.h Tiny2313をI2Cスレーブとして使う Donald R. Blake氏作TWIドライバ(usiTwiSlave.h と usiTwiSlave.c)をkumanが我流で変更したもの。 使い方 main.cのフォルダに i2c_slv2313.h i2c_slv2313.c の2つのファイルをコピーする。 Tiny2313のmain.cに次を設定する 1 #include "i2c_slv2313.h" 2 static const uint8_t TWI_slaveAddress = 0x22; // 7ビットで指定する(W=0x44,R=0x45) 3 main()の中に usiTwiSlaveInit( TWI_slaveAddress ); // I2C Slave initialization 4 main()の中に sei() //割り込み許可 5 main()の中で DDRBの設定に注意する。 DDRB = 0b0X0XXXXX; として bit7(SCL),bit5(SDA)はHiZにする。 6 同様に PORTB = 0b0X0XXXXX; としてプルアップを防ぐ。 makefileに i2c_slv2313.c の同時コンパイルを書く。 ヘッダファイルに uint8_t t[16] が宣言されているのでmain.cでは定義しないこと。 mega328のマスタから i2c_write_d(0x44,0,0,16);を実行すると内部アドレスに関係なくtn2313の t[16] に d[16] の16バイトが転送される(tn2313の割り込みで処理されるからdelayがあっても可)。 このとき16バイトを一括送信すること。( d[0]〜d[15] → t[0]〜t[15]) mega328のマスタから i2c_read_d(0x44,0,0,16);を実行すると内部アドレスに関係なくtn2313の t[16] が読み出され d[16] の16バイトに読み出される。( t[0]〜t[15] → d[0]〜d[15]) このとき16バイトを一括受信すること。 条件を整えれば マスタ 及び スレーブ で t[16] の16バイトSRAMが共有されることになる。 ********************************************************************************/ #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 /******************************************************************************** functions implemented as macros ********************************************************************************/ #define SET_USI_TO_SEND_ACK( ) \ { \ /* prepare ACK */ \ USIDR = 0; \ /* set SDA as output */ \ DDR_USI |= ( 1 << PORT_USI_SDA ); \ /* clear all interrupt flags, except Start Cond */ \ USISR = \ ( 0 << USI_START_COND_INT ) | \ ( 1 << USIOIF ) | ( 1 << USIPF ) | \ ( 1 << USIDC )| \ /* set USI counter to shift 1 bit */ \ ( 0x0E << USICNT0 ); \ } #define SET_USI_TO_READ_ACK( ) \ { \ /* set SDA as input */ \ DDR_USI &= ~( 1 << PORT_USI_SDA ); \ /* prepare ACK */ \ USIDR = 0; \ /* clear all interrupt flags, except Start Cond */ \ USISR = \ ( 0 << USI_START_COND_INT ) | \ ( 1 << USIOIF ) | \ ( 1 << USIPF ) | \ ( 1 << USIDC ) | \ /* set USI counter to shift 1 bit */ \ ( 0x0E << USICNT0 ); \ } #define SET_USI_TO_TWI_START_CONDITION_MODE( ) \ { \ USICR = \ /* enable Start Condition Interrupt, disable Overflow Interrupt */ \ ( 1 << USISIE ) | ( 0 << USIOIE ) | \ /* set USI in Two-wire mode, no USI Counter overflow hold */ \ ( 1 << USIWM1 ) | ( 0 << USIWM0 ) | \ /* Shift Register Clock Source = External, positive edge */ \ /* 4-Bit Counter Source = external, both edges */ \ ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | \ /* no toggle clock-port pin */ \ ( 0 << USITC ); \ USISR = \ /* clear all interrupt flags, except Start Cond */ \ ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \ ( 1 << USIDC ) | ( 0x0 << USICNT0 ); \ } #define SET_USI_TO_SEND_DATA( ) \ { \ /* set SDA as output */ \ DDR_USI |= ( 1 << PORT_USI_SDA ); \ /* clear all interrupt flags, except Start Cond */ \ USISR = \ ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | \ ( 1 << USIDC) | \ /* set USI to shift out 8 bits */ \ ( 0x0 << USICNT0 ); \ } #define SET_USI_TO_READ_DATA( ) \ { \ /* set SDA as input */ \ DDR_USI &= ~( 1 << PORT_USI_SDA ); \ /* clear all interrupt flags, except Start Cond */ \ USISR = \ ( 0 << USI_START_COND_INT ) | ( 1 << USIOIF ) | \ ( 1 << USIPF ) | ( 1 << USIDC ) | \ /* set USI to shift out 8 bits */ \ ( 0x0 << USICNT0 ); \ } typedef enum { USI_SLAVE_CHECK_ADDRESS = 0x00, USI_SLAVE_SEND_DATA = 0x01, USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA = 0x02, USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA = 0x03, USI_SLAVE_REQUEST_DATA = 0x04, USI_SLAVE_GET_DATA_AND_SEND_ACK = 0x05 } overflowState_t; volatile uint8_t id; volatile uint8_t adr_flg; volatile uint8_t t[16]; uint8_t slaveAddress; // volatile overflowState_t overflowState; // void usiTwiSlaveInit( uint8_t ); 1212121212121212121212121212121212121212121212121212121212121212121212121212121212121212 /* **************************************************************************** i2c_slv2313.c tiny2313をI2Cスレーブとして使う Donald R. Blake氏作TWIドライバ(usiTwiSlave.h と usiTwiSlave.c)をkumanが我流で変更したもの。 ********************************************************************************/ #include #include #include #include "i2c_slv2313.h" /******************************************************************************** USI Start Condition ISR ********************************************************************************/ ISR( USI_START_VECTOR ){ // set default starting conditions for new TWI package overflowState = USI_SLAVE_CHECK_ADDRESS; id=0; adr_flg=0; //00 // set SDA as input DDR_USI &= ~( 1 << PORT_USI_SDA ); // wait for SCL to go low to ensure the Start Condition has completed (the // start detector will hold SCL low ) - if a Stop Condition arises then leave // the interrupt to prevent waiting forever - don't use USISR to test for Stop // Condition as in Application Note AVR312 because the Stop Condition Flag is // going to be set from the last TWI sequence while ( // SCL his high ( PIN_USI & ( 1 << PIN_USI_SCL ) ) && // and SDA is low !( ( PIN_USI & ( 1 << PIN_USI_SDA ) ) ) ); if ( !( PIN_USI & ( 1 << PIN_USI_SDA ) ) ) { // a Stop Condition did not occur USICR = // keep Start Condition Interrupt enabled to detect RESTART ( 1 << USISIE ) | // enable Overflow Interrupt ( 1 << USIOIE ) | // set USI in Two-wire mode, hold SCL low on USI Counter overflow ( 1 << USIWM1 ) | ( 1 << USIWM0 ) | // Shift Register Clock Source = External, positive edge // 4-Bit Counter Source = external, both edges ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | // no toggle clock-port pin ( 0 << USITC ); } else { // a Stop Condition did occur USICR = // enable Start Condition Interrupt ( 1 << USISIE ) | // disable Overflow Interrupt ( 0 << USIOIE ) | // set USI in Two-wire mode, no USI Counter overflow hold ( 1 << USIWM1 ) | ( 0 << USIWM0 ) | // Shift Register Clock Source = external, positive edge // 4-Bit Counter Source = external, both edges ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | // no toggle clock-port pin ( 0 << USITC ); } // end if USISR = // clear interrupt flags - resetting the Start Condition Flag will // release SCL ( 1 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) |( 1 << USIDC ) | // set USI to sample 8 bits (count 16 external SCL pin toggles) ( 0x0 << USICNT0); } // end ISR( USI_START_VECTOR ) ISR( USI_OVERFLOW_VECTOR ){ switch ( overflowState ){ // Address mode: check address and send ACK (and next USI_SLAVE_SEND_DATA) if OK, // else reset USI case USI_SLAVE_CHECK_ADDRESS: if ( ( USIDR == 0 ) || ( ( USIDR >> 1 ) == slaveAddress) ){ if ( USIDR & 0x01 ){ overflowState = USI_SLAVE_SEND_DATA; }else{ overflowState = USI_SLAVE_REQUEST_DATA; } // end if SET_USI_TO_SEND_ACK( ); }else{ SET_USI_TO_TWI_START_CONDITION_MODE( ); } break; // Master write data mode: check reply and goto USI_SLAVE_SEND_DATA if OK, // else reset USI case USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA: if ( USIDR ) { // if NACK, the master does not want more data SET_USI_TO_TWI_START_CONDITION_MODE( ); return; } // from here we just drop straight into USI_SLAVE_SEND_DATA if the // master sent an ACK // copy data from buffer to USIDR and set USI to shift byte // next USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA case USI_SLAVE_SEND_DATA: // Get data from Buffer if ( id < 16 ) { USIDR = t[id]; // id = ( id + 1 ) & 15; } else { // the buffer is empty SET_USI_TO_TWI_START_CONDITION_MODE( ); return; } // end if overflowState = USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA; SET_USI_TO_SEND_DATA( ); break; // set USI to sample reply from master // next USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA case USI_SLAVE_REQUEST_REPLY_FROM_SEND_DATA: overflowState = USI_SLAVE_CHECK_REPLY_FROM_SEND_DATA; SET_USI_TO_READ_ACK( ); break; // Master read data mode: set USI to sample data from master, next // USI_SLAVE_GET_DATA_AND_SEND_ACK case USI_SLAVE_REQUEST_DATA: overflowState = USI_SLAVE_GET_DATA_AND_SEND_ACK; SET_USI_TO_READ_DATA( ); break; // copy data from USIDR and send ACK // next USI_SLAVE_REQUEST_DATA case USI_SLAVE_GET_DATA_AND_SEND_ACK: if ( id < 16 ) { if(adr_flg>0){t[id] = USIDR; id = ( id + 1 ) & 15;} //txTail 1111111111111111111111111111111111 adr_flg=1; } // next USI_SLAVE_REQUEST_DATA overflowState = USI_SLAVE_REQUEST_DATA; SET_USI_TO_SEND_ACK( ); break; } // end switch } // end ISR( USI_OVERFLOW_VECTOR ) void usiTwiSlaveInit(uint8_t ownAddress){ slaveAddress = ownAddress; // In Two Wire mode (USIWM1, USIWM0 = 1X), the slave USI will pull SCL // low when a start condition is detected or a counter overflow (only // for USIWM1, USIWM0 = 11). This inserts a wait state. SCL is released // by the ISRs (USI_START_vect and USI_OVERFLOW_vect). // Set SCL and SDA as output DDR_USI |= ( 1 << PORT_USI_SCL ) | ( 1 << PORT_USI_SDA ); // set SCL high PORT_USI |= ( 1 << PORT_USI_SCL ); // set SDA high PORT_USI |= ( 1 << PORT_USI_SDA ); // Set SDA as input DDR_USI &= ~( 1 << PORT_USI_SDA ); USICR = // enable Start Condition Interrupt ( 1 << USISIE ) | // disable Overflow Interrupt ( 0 << USIOIE ) | // set USI in Two-wire mode, no USI Counter overflow hold ( 1 << USIWM1 ) | ( 0 << USIWM0 ) | // Shift Register Clock Source = external, positive edge // 4-Bit Counter Source = external, both edges ( 1 << USICS1 ) | ( 0 << USICS0 ) | ( 0 << USICLK ) | // no toggle clock-port pin ( 0 << USITC ); // clear all interrupt flags and reset overflow counter USISR = ( 1 << USI_START_COND_INT ) | ( 1 << USIOIF ) | ( 1 << USIPF ) | ( 1 << USIDC ); } // end usiTwiSlaveInit 1212121212121212121212121212121212121212121212121212121212121212121212121212121212121212 ************************************************************************************************** i2c_slv2313_hc_readme.txt Tiny2313をI2Cスレーブとして使うドライバである。 Donald R. Blake氏作TWIドライバ(usiTwiSlave.h と usiTwiSlave.c)の使用例でプログラムを一部変更してマスターからの書き込みは成功したが(7セグ4桁LED及びLCD表示器)、読み出しは失敗に終わった。 「usiTwiTransmitByte関数」でデータを渡すだけなのですがうまく実行できない。スレーブプログラムが他の作業中に読み出しが行われるとハングしてしまうことが判明した。delayはもちろんのこと、簡単なfor文でも支障をきたす。ほぼ待ち時間無く連続でマスターからの呼び出しに対応すれば正常にデータを渡すことができた。 そこで、ライブラリを強引に分解してスレーブプログラムを作ってみた。 リードバッファ、ライトバッファ、スレーブプログラムの配列変数の各16バイトを共通の配列1つのみとして、16バイトに固定して、常にこの16バイトの先頭から16バイトを読み書きするものと簡略化して、割り込みで処理するとマスターからの通常読み書きの方法で通信が可能であって、メインプログラムに待ち時間があっても問題なく動作する事が確認できた。 プログラムが見にくいので自己流にライブラリを分離して、(ライブラリの書き方がわからないまま)これをincludeして書くと見やすく簡単なものとなった。プログラム量も650バイト程度である。 マスタから見ると内部アドレスが0x00から始まる16バイトの通信領域がスレーブにあって、通常はこの16バイトを一括して送受することになる(内部アドレス指定は無視される)。 スレーブプログラムでは、この領域は t[0]〜[15] となるからマスタからの読み書きがあるという前提でこれらのデータの読み書きをすることになる。 通信に必要な手続きは、  @ スレーブアドレスを定めること(7ビット)  A 初期化関数 usiTwiSlaveInit( TWI_slaveAddress ); を実行するだけである。 使い方 main.cのフォルダに i2c_slv2313.h i2c_slv2313.c の2つのファイルをコピーする。 Tiny2313のmain.cに次を設定する 1 #include "i2c_slv2313.h" 2 static const uint8_t TWI_slaveAddress = 0x22; // 7ビットで指定する(W=0x44,R=0x45) 3 main()の中に usiTwiSlaveInit( TWI_slaveAddress ); // I2C Slave initialization を書く 4 main()の中に sei() //割り込み許可 5 main()の中で DDRBの設定に注意する。 DDRB &= 0b0X0XXXXX; として bit7(SCL),bit5(SDA)はHiZにする。 6 同様に PORTB &= 0b0X0XXXXX; としてプルアップを防ぐ。 makefileに i2c_slv2313.c の同時コンパイルを書く。 ヘッダファイルに uint8_t t[16] が宣言されているのでmain.cでは定義しないこと。 mega328のマスタから i2c_write_d(0x44,0,0,16);を実行すると内部アドレスに関係なくtn2313の t[16] に d[16] の16バイトが転送される(tn2313の割り込みで処理されるからdelayがあっても可)。 このとき16バイトを一括送信すること。 mega328のマスタから i2c_read_d(0x44,0,0,16);を実行すると内部アドレスに関係なくtn2313の t[16] が読み出され d[16] の16バイトに読み出される。このとき16バイトを一括受信すること。 条件を整えれば マスタ 及び スレーブ で t[16] の16バイト(t[0]:t[15])のSRAMが共有されることになる。 *********************************************************************************************************