こんにちは、NAKAといいます。
すみませんどうもI2C通信とは相性が悪いみたいで、なかなかゆうことを聞いてくれません。以前RXの時もつまづいて、原因もあやふやなまま動いてしまったのでそのままの経緯があります。
今回、RL78/I1Eで初、簡易I2Cですがなんか上手く受信できてないような気がします。
もし、愛と少々のお時間がありましたら、お付き合いお願いできませんでしょうか?
まず、RL78をマスタにし、接続した開発中のデバイス(スレーブ)の仕様はスレーブアドレス 0x77レジスタアドレス 0x03、0x04、0x05 の 3Byte のデータを読みたいのですがオートインクリメントができるみたいで、こんな感じ
そして/*************************************************************************// 関数名 : fn_Init_IIC00(void)// 動作 : I2Cシリアル通信の初期化// 引数 : // 作成 : NAKA 19.10.14// 備考 : SAU0 ユニット=0 ch0=IIC00 (P14⇒SDA00 P15⇒SCL00)// ***********************************************************************/void fn_Init__IIC00(void){ SAU0EN = 1; //クロック供給 SPS0 = 0x0000; //クロック選択
ST0 |= 0x0001; //IIC00停止 IICMK00 = 1; //IIC00割り込み禁止 IICIF00 = 0; //IIC00割り込み要求フラグクリア IICPR100 = 1; //IIC00割り込みプライオリティ設定 IICPR000 = 1; SIR00 = 0x0003; //全てエラーフラグのクリア SMR00 = 0x0024; //簡易I2Cモードに設定 SCR00 = 0x8017; //1stopビット、8ビットデータ長 SDR00 = 0xC600; //ボーレート設定 SO0 |= 0x0101; //_シリアルクロックを1から | シリアルデータを1から POM1 |= 0x10; //ポートの設定 P1 |= 0x30; PM1 &= 0xCF; IICMK00 = 0; //IIC00割り込み許可 }
※割込みの中では f_IIC00というフラグを立ててるだけです。(ベクタの設定もやってます。)/************************************************************************//* IIC00の割り込み関数 *//************************************************************************/void __near IIC00_INT(void){ IICIF00 = 0; //IIC00割り込み要求フラグクリア f_IIC00 = 1; //IIC通信完了フラグON!}
こんな感じで設定して、
MAINの中で1秒毎に
//①アドレス・フィールド送信 SO0 &= 0xFFFE; //スタートコンデション発行 SO00=0 for(j=0;j<=10;j++) //ウエイト { __nop(); } f_IIC00 = 0; //IIC完了フラグクリア SO0 &= 0xFEFF; //クロックを"0"にして通信準備 CKO00=0 SOE0 |= 0x0001; //シリアル出力許可レジスタを許可 SOE00=1 SS0 |= 0x0001; //シリアル動作許可状態にする SS00=1 SCR00 &= ~0xC000; //送受信を止める TXE00=0 RXE00=0 SCR00 |= 0x8000; //送信状態にする TXE00=1 SIO00 = (0x77 << 1) | 0x00; //スレーブアドレスデータを入力****暫定 スレーブアドレス0x77 Wite:0 Read:1 while(!f_IIC00) //IIC完了割込み待ち { __nop(); } f_IIC00 = 0; //IIC完了フラグクリア if((SSR00 & 0x0002) != 0) //ACK確認 { c_ARK_ERR++; //エラー処理 } //②レジスタアドレスデータ送信 SIO00 = 0x03; //レジスタアドレスデータを入力 while(!f_IIC00) //IIC完了割込み待ち { __nop(); } f_IIC00 = 0; //IIC完了フラグクリア if((SSR00 & 0x0002) != 0) //ACK確認 { c_ARK_ERR++; //エラー処理 } //③データ受信 SIO00 = (0x77 << 1) | 0x01; //スレーブアドレスデータを入力****暫定 スレーブアドレス0x77 Wite:0 Read:1 while(!f_IIC00) //IIC完了割込み待ち { __nop(); } f_IIC00 = 0; //IIC完了フラグクリア if((SSR00 & 0x0002) != 0) //ACK確認 { c_ARK_ERR++; //エラー処理 } ST0 |= 0x0001; //シリアル動作停止状態にする ST00=1 SCR00 &= ~0xC000; //送受信を止める TXE00=0 RXE00=0 SCR00 |= 0x4000; //受信状態にする RXE00=1 SS0 |= 0x0001; //シリアル動作許可状態にする SS00=1 SIO00 = 0xFF; //ダミーデータ書き込み while(!f_IIC00) //IIC完了割込み待ち { __nop(); } f_IIC00 = 0; //IIC完了フラグクリア IIC_RX_DATA[0] = SIO00; SIO00 = 0xFF; //ダミーデータ書き込み while(!f_IIC00) //IIC完了割込み待ち { __nop(); } f_IIC00 = 0; //IIC完了フラグクリア IIC_RX_DATA[1] = SIO00; //最後なので SOE0 &= 0xFFFE; //シリアル出力許可レジスタを停止 SIO00 = 0xFF; //ダミーデータ書き込み while(!f_IIC00) //IIC完了割込み待ち { __nop(); } f_IIC00 = 0; //IIC完了フラグクリア IIC_RX_DATA[2] = SIO00;
//④ストップコンディション発行 ST0 |= 0x0001; //シリアル動作停止状態にする ST00=1 SOE0 &= 0xFFFE; //出力禁止状態 SOE00=0 SO0 &= 0xFFFE; //SDA出力を0にする SO00=0 for(j=0;j<=100;j++) //ウエイト HWM-P649 必要らしい { __nop(); } SO0 |= 0x0100; //クロック出力を1にする CKO00=1 for(j=0;j<=10;j++) //ウエイト HWM-P649 必要らしい { __nop(); } SO0 |= 0x0001; //SDA出力を1にする SO00=1
ってやるとIIC_RX_DATA[0]~[2]に受信データが入っていることを期待していますが全部 ダミーと同じ 0xFF が入ってます。
エラー処理は取り合えず保留でACKが帰らなかったったらc_ARK_ERRっていうカウンタをインクリメントすることにしてます。c_ARK_ERRがインンクリメントされないので、ACKは受信できてそうだし、(※スレーブアドレスを0x77以外にするとACKは来ずにc_ARK_ERRはインクリメントされる)f_IIC00フラグも立つので割込みも発生している感じです。
なんかあんぽんたんなことをやってる感じでしょうか?
チョコ先生 NAKAです。
まだすっきりしてません。I2Cの基礎から教わる必要があるかも?です。(~_~;)
ご指摘の通り、下図の①のスタートコンディション発行を忘れていました。また送受信毎にウエイトも入れてみました。
(あと、最初のご確認のスレーブは専用のスレーブデバイスです。)
質問ですが、
リスタートかける場合は下図②の部分にストップコンディションを発行しておく必要があるのでしょうか?
ストップコンディションを発行しないと、下図①部分のスタートコンデションを発行しても(SO00=0にしてウエイトしてるだけ)
クロック発生のためのダミー送信した0xFFがSIO00から読めるだけです。(0xAAをダミー送信するとSIO00も0xAA)
下図②部分にストップコンディションを入れると、ダミー送信の値にかかわらずSIO00から0x00が読めます。
(※まだ、開発デバイスの仕様が読み切れてないので、他のレジスタを設定しないと動作しないため0x00が読めるのかもしれません。)
お気づきの点がございましたら、アドバイス願えませんでしょうか?
チョコ先生! NAKAです。 手ごわいです。 >リスタートの場合と通常のスタートコンディションの場合では,SCL信号の状態が異なっています。 >RL78の簡易I2Cでは,SDA信号をHにして,SDA信号がHになったら,SCL信号を立ち上げます。 >SCL信号がHになってからSDA信号を立ち下げることで,スタートコンディションになります。 ⇒SDAやSCLをソフトで操作するためには、SEmn=0(チャンネル動作停止)やSOE00=0(シリアル出力禁止)にしないといけないので、結局ほぼストップコンディションを発行しているのと同じことになるのではないでしょうか? //③データ受信 weit_100(); //ウエイト ST0 |= 0x0001; //シリアル動作停止状態にする ST00=1(SE00=0) SOE0 &= ~0x0001; //シリアル出力許可レジスタを禁止 SOE00=0 SO0 |= 0x00001; //SDAをHに weit_100(); //ウエイト SS0 |= 0x0001; //シリアル動作許可状態にする SS00=1(SE00=1) SO0 |= 0x0100; //SCLをHにする weit_100(); //ウエイト SO0 &= 0xFFFE; //スタートコンデションもう一度発行 SO00=0 weit_100(); //ウエイト SOE0 |= 0x0001; //シリアル出力許可レジスタを許可 SOE00=1
チョコです。
追加の情報です。
サンプルプログラム等にある「IIC通信のマスタ側(RL78/G13の簡易IIC版)改 2C」に掲載しているプロジェクトの中に「r_iic_lib.c」というIIC00関係のライブラリがあります。
https://japan.renesasrulz.com/cafe_rene/m/sample_program/408
そこの303行目~334行目にスタートコンディションを発行するための関数(r_iic00_startcondition)があります。
そこでは,IIC00の動作を停止し,SO0のSDA相当のビットが0ならば,1にしています。この時,SCLがHならストップコンディションになり,SCLがLならば,単にSDAが立ち上がるだけです(リスタートの場合にはSCLがLなので,単にSDAが立ち上がるだけになります)。
その後,SO0のSCL相当のビットが0ならば,SCLを立ち上げます。これで,スタートコンディション発行の準備ができたので,スタートコンディション発行のために,SDAを立ち下げます。その後でSCLを立ち下げて,通信(スレーブアドレス送信)の準備をして完了しています。
参考として,その部分の画面イメージを以下に示します。
汎用に作っているので,処理が多いですが,必要な部分だけでも構いません。
チョコ先生 NAKAです。 いつもありがとうございます。 ①色々みてましたが、結局スタートコンディション発行する場合に、発行する前の状態を確実に作ってから発行するというのが結論のような気がします。 if ( 0 == (SO0 & 0x0001) ) //SDAが0だったら { SO0 |= 0x0001; //SO00(SDA)を1にする SDA↑ weit(); //ウエイト } if ( 0 == (SO0 & 0x0100) ) //SCLが0だったら { SO0 |= 0x0100; //CK00(SCL)を1にする SCL↑ weit(); //ウエイト } SO0 &= (~0x0001); //SCL=Hの状態で SDA=0↓ weit(); //ウエイト SO0 &= (~0x0100); //SDA=L の状態で SCL=0↓ ②チョコ先生から教えていただいたサンプルで疑問なところがあります。 スタートコンディションやストップコンディションの一番はじめにIIC00を一旦止める関数があり その中で(下記)//①*******START ~ //①**********END の部分って必要なのでしょうか? 通信状態フラグTSF00で止まってるか確認しているようなんですが、どういう意味があるのでしょうか?結局下のほうで ST0L = IIC00_TRG; /* stop channel 0 operation */のように止めてるし? この部分を無くしても良いのでしょうか? 試しに、無くしても普通に動いているようです。 FALSE は#define で 0 のようです。 /******************************************************************************* * Function Name: r_iic00_stop * Description : This function stop IIC00 operation * Arguments : none * Return Value : none ********************************************************************************/ void r_iic00_stop(void) { uint8_t work; wait_time(1); /* dummy wait 20us */ //①***********************************START work = 0x40 & SSR00L; if ( FALSE != work ) { while ( FALSE != work ) /* wait for previous execution */ { work = SSR00L; /* get IIC00 status */ work &= 0x40; /* get transfer status (TSF) */ } wait_5us(); } //①***********************************END SOE0 &= (~IIC00_TRG); /* disable IIC00 output */ ST0L = IIC00_TRG; /* stop channel 0 operation */ }
ハードマニュアルの中のフロー図なんかでもSSRをどうかしてるような記載が無いようなきがします。