#include #include <htc.h> ".h" * マスターモード専用 I2C 関数 教育 ホビー用 * 営利目的 商用への利用は禁止 * 詳しいタイミングは NXP の資料参照のこと 関数の説明 * ストップ状態にします : クロックをHにしてデータをL->Hします _stop() SDA_LOW(); 初めはデータを L 確認 SCL_HIGH(); SCL を H にする // delay_us(i2c_tm_data_su); セットアップ時間待つ // SCL_DIR = I2C_INPUT; クロックをHにフロート delay_us(i2c_tm_stop_su); ホールド時間待つ SDA_HIGH(); SDAをLからHにデータ遷移 delay_us(i2c_tm_bus_free); バス開放時間 // SDA_DIR = I2C_INPUT; データ線をHにフロート * スタート状態にします : まずはデータ線を確実に H にして * スタート状態にします _Start() マクロも同一定義です _restart() SCL_HIGH(); クロックは確実に H とする SDA_HIGH(); データ線を H とする delay_us(i2c_tm_data_su); セットアップ時間待つ SCL_DIR = I2C_INPUT; クロックパルスを H に SDA_LOW(); SDA を H から L に変化させる delay_us(i2c_tm_start_hd); * バイトをスレーブに送ります * エラー時には TRUE を返します _sendbyte( byte) signed char i; for(i=7; i>=0; i--) 8 ビット分処理します SCL_LOW(); クロックを L に データのホールドタイムは 0 で すぐデータ送る SDA_DIR = ((byte>>i)&0x01); データ線のフロートするか if ((byte>>i)&0x01) 送るビットは SDA_HIGH(); else SDA_LOW(); ページ (1)
delay_us(i2c_tm_data_su); SCL_DIR = I2C_INPUT; クロック線をHにフロート if(_waitforscl()) クロック開放しているか return TRUE; バスエラー クロックの H レベル時間を確保 return FALSE; * スレーブへアドレスとデータ方向指定ビットを送ります * 7 ビットアドレス ( 最下位無視 ) 方向 (read:1,write:0) _sendaddress( address, rw) return _sendbyte(address (rw?1:0)); * スレーブ側からのアックをチェックします * アックを返すか アックなしか バスエラーなら ERROR です signed char _readack() ack; SCL_LOW(); クロック線をLにする SDA_DIR = I2C_INPUT; データ線を開放 - アック応答をみる delay_us(i2c_tm_scl_to_data); データ出力を確定のためSCLをLにする SCL_HIGH(); クロック線をH delay_us(i2c_tm_data_su); セットアップ時間待ち ack = SDA; アック応答よみ バイト処理の後クロック線をスレーブが開放したか確認 if(_waitforscl()) スレーブ動作エラー SCL_LOW(); // append return ack; * スレーブからバイトリードします * 読んだバイトを返すか もしバスエラーだったら ERROR を返します _readbyte() i; byte = 0; for(i=0; i<8; i++) SCL_LOW(); クロック線を L にする delay_us(i2c_tm_scl_low); クロックの L 時間を保証 SDA_DIR = I2C_INPUT; r データ線を開放 SCL_DIR = I2C_INPUT; クロック線をフロートしてH if(_waitforscl()) byte = byte << 1; 次のビットを読む byte = SDA; return ()byte; * スレーブ側にアックあるいは非アックを送ります * I2C_LAST という status を送ると これで最後バイトを送りますという意味です ページ (2)
_sendack( status) SCL_LOW(); クロック線をLにする if ( status & 0x01) SDA_LOW(); drive line low -> more to come else SDA_HIGH(); delay_us(i2c_tm_data_su); SCL_DIR = I2C_INPUT; クロック線をHに return; * スレーブに 1 バイトを送ります I2C_ERROR アック 非アックを返します * signed char _putbyte( data) if(_sendbyte(data)) return _readack(); アック 非アックを読み取り返します * スレーブから 1 バイト読み取り 転送のアックを確認 * 戻り値は I2C_ERROR なら true であり それ以外は byte _getbyte( more) byte; if((byte = _readbyte()) == I2C_ERROR) _sendack(more); return byte; * スレーブにバイト列を送り 転送のアックを確認する * もし転送不成功なら転送残バイト数を返す _putstring(const *str, length) signed char error; while(length) if((error = _putbyte(*str)) == I2C_ERROR) return -()length; バスエラー時の戻り値 else if(error) return ()length; アックが有りません str++; length--; return FALSE; 処理すべて OK * スレーブから指定されたバイト数を str 文字列に格納 転送のアックを返す * 読み込みに成功しなかった文字数を返す _getstring( *str, number) ページ (3)
byte; while(number) if((byte = _getbyte(number-1)) == I2C_ERROR) return number; バスエラーなので残バイト数返す else *str = ()byte; str++; number--; return FALSE; 処理すべて OK * 指定アドレスのデバイスと通信を開始する モードは * I2C_READ あるいは I2C_WRITE で指定される * もし指定アドレスに対してアック無いと TRUE を返す _open( address, mode) _start(); _sendaddress(address, mode); if(_readack()) return TRUE; return FALSE; 処理すべて OK * 遅いスレーブの場合用にクロック線が開放されるのをまつ * タイムアウト時間の後も SCL が開放されないときは TRUE を返す * もしそうでないときは SCL が開放された時に FALSE を返す _waitforscl() SCL_DIR をここでは入力にする if(!scl) delay_us(i2c_tm_scl_tmo); もしまだクロックが L のままならバスエラー if(!scl) return TRUE; return FALSE; * バスを開放する _free() uci; SDA_DIR=I2C_INPUT; for(uci=0;uci!=9;uci++) SCL_HIGH(); delay_us(5); SCL_LOW(); delay_us(5); * 1 文字読んで ucadr が 0 ならば読み取り終了する _read( ucadr) ucdat; if (_readfrom(ucadr)==0) ページ (4)
ucdat=_getbyte(i2c_more); _stop(); return(ucdat); ページ (5)