今更恥ずかしくて聞けない、RL78/I1Eの簡易IIC通信を教えてください。

こんにちは、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フラグも立つので割込みも発生している感じです。


なんかあんぽんたんなことをやってる感じでしょうか?

Parents
  • チョコです。
    さっと見始めたところですが,2つほど気になるところがあります。
    スレーブは,MCUでしょうかそれとも専用のスレーブでしょうか。
    RL78の簡易I2Cではスレーブからウエイトをかけることができないので,1バイトごとの通信の間隔をマスタ側で確保(通信の間に時間を空ける)必要があります。
    次がI2Cの基本に関する重大な欠陥です。②の処理に引く続いて,③でスレーブ・アドレスを送信開始しているつもりのようですが,ここは,②の処理の後に,リスタート(スタート・コンディションを発行)してから③を処理すべきです。このままでは,単にレジスタアドレス0x03に0xEFというデータを書き込んでいるだけです。
  • チョコ先生 NAKAです。

    まだすっきりしてません。I2Cの基礎から教わる必要があるかも?です。(~_~;)

    ご指摘の通り、下図の①のスタートコンディション発行を忘れていました。また送受信毎にウエイトも入れてみました。

    (あと、最初のご確認のスレーブは専用のスレーブデバイスです。)

    質問ですが、

    リスタートかける場合は下図②の部分にストップコンディションを発行しておく必要があるのでしょうか?

    ストップコンディションを発行しないと、下図①部分のスタートコンデションを発行しても(SO00=0にしてウエイトしてるだけ)

    クロック発生のためのダミー送信した0xFFがSIO00から読めるだけです。(0xAAをダミー送信するとSIO00も0xAA)

    下図②部分にストップコンディションを入れると、ダミー送信の値にかかわらずSIO00から0x00が読めます。

    (※まだ、開発デバイスの仕様が読み切れてないので、他のレジスタを設定しないと動作しないため0x00が読めるのかもしれません。)

    お気づきの点がございましたら、アドバイス願えませんでしょうか?

  • チョコです。
    このように,スレーブ内部のレジスタアドレスを指定して,読み出すような場合には,ストップコンディションは不要です。
    レジスタアドレスの送信が完了した段階で,マスタが通信方向を切り替えるためにスタートコンディションを発行することで,スレーブは次がスレーブアドレスと認識します。つまり,通信方向を切り替えることができるようになります。

    >下図①部分のスタートコンデションを発行しても
    リスタートの場合と通常のスタートコンディションの場合では,SCL信号の状態が異なっています。RL78の簡易I2Cでは,まずSDA信号をHにして,SDA信号がHになったら,SCL信号を立ち上げます。SCL信号がHになってからSDA信号を立ち下げることで,スタートコンディションになります。RL78の簡易I2Cではここらの手順が面倒なので,よく波形を確認してみてください。

    スレーブが専用のデバイスならウエイトは必要ないかもしれません。
  • チョコ先生! 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です。

    すみません!急にハードの仕事が入ってしまって、資料を基板屋さん出さないと仕事が進まないからこちらを優先して!って言われてそちらをやってました。でも結局、下流のこっちのソフトにしわ寄せがくるんだからぁ!!と愚痴を言ってしまいました。(~_~;)

    さて、
    今日、基板屋さんに図面をだして、チョコ先生に教えてもらったサンプルを見ながら、SCR00のTXEやRXEの変更をスタートコンディション発行の前に入れたり、後にしたりコチョコチョ触っていたら、なんか動いちゃいました!!!レジスタに書けるし、レジスタの内容も読めちゃいました。まだ、どこが悪かったのか掴めてないですが、今回はもう少し触って、原因を調べてみようと思います。RXの時は「動いてるからこのまま行っちゃえ!!」でしたので。

    優しいチョコ先生が、気にかけて頂いているかと想像し(誰も気にしてないか?)とりあえず報告に来ました。

    いつも最後までお付き合いいただき感謝しかないです。

    少し触って、また疑問が出たら聞きにきます。(^^)/
  • チョコです。
    とりあえずは動作して,よかったですね。

    >⇒SDAやSCLをソフトで操作するためには、SEmn=0(チャンネル動作停止)やSOE00=0(シリアル出力禁止)にしないといけないので、結局ほぼストップコンディションを発行しているのと同じことになるのではないでしょうか?
    問題は出力される信号の波形です。リスタートをかけるときには,前回の通信が完了してSCLはLになっているので,そこでSDAを立ち上げてもスレーブはストップコンディションとは認識しません。単にデータが変化しただけと判断します。同じ処理をSCLがHのときにやれば,ストップコンディションの処理になりますが,SCLがHなのは通信が完了して既にSDAはどこからもドライブされずプルアップ抵抗でHになっているので,スレーブから見るとSDAは変化しないことになります。
    このように,同じ処理をしてもその時のI2Cバスの状態で結果が異なります。

    といったところです。
Reply
  • チョコです。
    とりあえずは動作して,よかったですね。

    >⇒SDAやSCLをソフトで操作するためには、SEmn=0(チャンネル動作停止)やSOE00=0(シリアル出力禁止)にしないといけないので、結局ほぼストップコンディションを発行しているのと同じことになるのではないでしょうか?
    問題は出力される信号の波形です。リスタートをかけるときには,前回の通信が完了してSCLはLになっているので,そこでSDAを立ち上げてもスレーブはストップコンディションとは認識しません。単にデータが変化しただけと判断します。同じ処理をSCLがHのときにやれば,ストップコンディションの処理になりますが,SCLがHなのは通信が完了して既にSDAはどこからもドライブされずプルアップ抵抗でHになっているので,スレーブから見るとSDAは変化しないことになります。
    このように,同じ処理をしてもその時のI2Cバスの状態で結果が異なります。

    といったところです。
Children
  • チョコ先生 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をどうかしてるような記載が無いようなきがします。

  • チョコです。
    >その中で(下記)//①*******START ~  //①**********END の部分って必要なのでしょうか?
    きちんとした処理を行っていれば,この部分はなくても正常に動作します。これは,フェールセーフのための処理です。
    コード生成のAPIは,通信が完了していないのにAPI関数から戻ってきます。これ自体は割り込みでの処理を行う場合には当然と言っていい処理方法です。最新のマニュアルは知りませんが,確認した範囲では,「エラーなしで戻ってきたら,通信完了」と記述されていました。これが,致命的な大問題です。しかも,Arduinoなどでは,通信が完了してから関数から戻ってくるのが一般的です(割り込みを使うことも可能ですが)。これでトラブルが起こらないと期待するのはかなり無理があります。
    そこで,フェールセーフのために,少なくとも通信が完了するのを待ってから処理するために入れたものです。この処理を入れることで,通信中に簡易IICを停止しようとしても実行中の通信は完了することができます(これは,あくまでコード生成の問題点をある程度リカバーしようとして作成した部分です)。

    とにかく,いろんな使い方にできるだけ対応しようとした結果がこの部分です。当然ながら,この部分が,あってもきちんと動作しますけど。