Renesas Synergy™

FAQ 1006569 : ソフトウェアでI2Cバスのスレーブ機能の実現について(78K0S/Kx1+)

ソフトウェアでI2Cバスのスレーブ機能の検討」の結果の

・標準モード(クロックが100kHz以下)

・シングル・マスタ

・シングル・スレーブ(通信中以外に不要な信号変化がない)

・通信途中(8ビット目まで)でスタート・コンディションやストップ・コンディションが発行されない(通信前か9クロック目が完了した後のみで発行可能)

・他の割り込みは使用しない(割り込み応答の余裕が1μSしかないため)

の条件でのI2Cバスのスレーブ機能をソフトウェアによるポート制御で実現してみます。

(1)処理概要
以下に実際のプログラム例を示しながら、説明します。ここで対象にするプログラムはスタート・コンディションを割り込みで検出(実際には、単にSDAの立 下りで割り込みが発生するので、その後にソフトで判定)して処理を開始します。処理を開始すると、通信完了するまではスタート・コンディションを検出した 割り込みの中で処理を行い、スレーブ送信の場合にはマスタからのNACK応答で、スレーブ受信の場合にはデータ受信後のストップ・コンディション検出で通 信を完了して割り込み処理から抜け出します。
データ転送の方向(受信か送信か)はI2Cバスの規格に従い、アドレスのビット0で判断しています。I2Cバスに関しては、FAQの「I2Cバスについての概要」を参照してください。データの送受信は内部に用意された送信/受信用バッファに対して行っています。このプログラムで使用する変数は以下のようになります。このプログラムを使用する場合には、これらを前もって設定しておく必要があります。

 

RXPOINT : 受信データを格納するアドレスを保持している。データを受信することで自動的に更新される。
TXPOINT : 送信するデータの格納アドレスを保持している。データを送信することで、自動的に更新される。
SAV0 : スレーブとしてのアドレスを保持しています。マスタからのアドレス(上位7ビット)がこの値と一致すると、実際の通信を行います。ビット0は必ず 0にしておく必要があります。アドレスが一致しなかったときには下記のACKEをクリアすることで、ACK応答をしなくなります。
IICC0 : ビット2がACK応答するかどうかのフラグ(ACKE)です。このビットがセットされていると、マスタからのアドレスがSAV0の値と一致したら ACK応答します。マスタからデータを受信したとき、受信したデータを処理する時間は次の通信を行わないように、フラグ(ACKE)をクリアします。その ため、受信データの処理が完了して、次のデータ転送が可能になったら、フラグ(ACKE)をセットする必要があります。


(2)全体の処理
ここで考えている通信処理全体の概略状態遷移図を以下に示します。



処理の開始はSDA信号の立ち下がり(INTP3) とします。SCL信号のとの関係をチェックしてスレーブ・アドレス受信に移ります。受信したスレーブ・アドレスが一致して応答許可状態のときにACK応答 を行います。何か内部処理を実行中で通信処理したくないときには応答禁止状態にしておき、アドレスが一致してもACK応答しないようにします。このように ACK応答をしないことで、マスタに処理中であることを知らせます。スレーブ受信の場合には、受信準備をして、「バイト切れ目での処理」になります。ここ で、ストップ・コンディションを検出すると、通信処理を完了します。スレーブ送信の場合には、マスタからACK応答があれば次のデータを送信し、NACK 応答の場合(ACK応答がなければ)通信を完了します。
以下では詳細な処理について説明します。

(3)通信の起動(スタート・コンディション検出)
通信を開始するタイミング(スタート・コンディション)はSDA信号の立下りをエッジ検出したベクタ割り込みで検出するものとします。このとき、以下に示すプログラム例では、SCL信号がハイでなければ処理は行わないようにしても十分にスタート・コンディション検出が可能です。スタート・コンディションを検出したら、SCL信号が一旦立ち下がったことを確認してから、SCL信号の立ち上がりを待ちます

__INTP3:
BF      P4.0,$NOTSTART ;SCLがハイか確認
PUSH    AX ;AXレジスタをセーブ
MOV     IIC0,#00000000B ;データ初期値を設定
LOOP0:

MOV     A,P4 ;SDAとSCL読み込み
BF      A.0,$ADDRESS0 ;SCLの立下りで分岐
BF      A.1,$LOOP0 ;SDAがロウなら継続
NOTACK:
POP     AX ;AXレジスタを復帰
NOTSTART:
RETI                            ;ストップコンディション検出



(4)アドレスの受信
SCL信号がロウになったことを確認してから、アドレス受信のためにSCL信号の立ち上がりを待ちます。このとき、MOV命令でSCL信号とSDA信号を一緒に取り込むことで、立ち上がりの確認間隔は12クロックとBF命令に比べて長くなります。しかし、SDA信号も同時に取り込めるので、タイミングずれを気にする必要がなくなります。
このときの処理プログラムは以下の例のようになります。SCL信号がハイのときにSDA信号が有効として、そのデータがハイの場合には変数IIC0の該当するビットを1にします。
ここでの最悪のケースとして、読み込みの直後にSCL信号が立ち上がったとしても、36クロック(4.5μS)でビットの受信処理は完了できます。その後のSCL信号の立ち下がり確認を含めて46クロック(約6μS)となり、ビットの周期10μSに対して十分なマージンで処理可能です。

ADDR7:          MOV     A,P4 ;SDAとSCL読み込み
BF      A.0,$ADDR7 ;SCLの立ち上がり待ち
BF      A.1,$lowdata7 ;SDAのチェック
SET1    IIC0.7 ;SDAがハイならビットをセット
lowdata7:
BT      P4.0,$$ ;SCLの立下りを待つ



次のビット以降についても、同様にSCL信号の立ち上がりを待って処理を行います。上記の処理はビット位置を変えて処理する必要があるので、同じような処理を繰り返し記述する必要があります。ここでは記述を簡単にするためにマクロ機能を利用し、以下のように1ビットの受信処理を行うマクロsin1を定義します。

sin1 macro   bitno
LOCAL   DATAis0,LOOP
LOOP:
MOV     A,P4                    ;SCLとSDAを読み込む
BF      A.0,$LOOP               ;SCL立ち上がり待ち
BF      A.1,$DATAis0            ;SDAを確認
SET1    IIC0.bitno              ;1ならビットをセット
DATAis0:
BT      P4.0,$$                 ;SCLの立下り待ち
endm

これで、アドレスの受信処理は、マクロ名とビット番号(マクロの引数)を組み合わせて以下のように記述することができます。

ADDRESS0:
PUSH    HL
ADDRESS:
sin1    7                       ;ビット7受信
sin1    6                       ;ビット6受信
sin1    5                       ;ビット5受信
sin1    4                       ;ビット4受信
sin1    3                       ;ビット3受信
sin1    2                       ;ビット2受信

これで、ビット7~ビット2までの受信処理を行います。ビット1(アドレスの最後のビット)についてはアドレスの比較が必要なためにできるだけ無駄な待ちループが発生しないよう最後のSCL信号の立ち下がり待ちの前にアドレス比較を行います。以下の処理を実行しても、ウェイトをかけることなくSCL信号のロウ期間に処理を完了して最後の転送方向受信処理に間に合います。

LOOP1:
MOV     A,P4 ;SCLとSDAを読み込む
BF      A.0,$LOOP1 ;SCL立ち上がり待ち
BF      A.1,$bit1is0 ;SDAを確認
SET1    IIC0.1 ;1ならビットをセット
bit1is0:
MOV     A,SVA0                  ;アドレスを確認
SUB     A,IIC0                  ;結果をゼロ・フラグに
BZ      $RESACK ;アドレスが一致しないと
CLR1    ACKE ;ACK応答禁止に設定
RESACK:
BT      P4.0,$$ ;SCL立下り待ち



(5)アドレスの受信完了からACK応答
アドレスの8ビット目(転送方向)を受信したら、アドレスが一致してなおかつACK応答可能なときにSCL信号の9クロック目でACK応答を行います。アドレスの一致は最初の7ビットでチェックが完了しているので、ここではその結果のACKEフラグを参照して応答するかどうかを判定します。アドレスが一致していても、ACKEフ ラグがクリアされている場合(別の処理を行っていて、応答できない場合)にはACK応答しません。従って、スレーブからACK応答がなかったときには、マ スタはある程度時間を置いて再度スタート・コンディションから開始する必要があります。ACK応答しないときにはスレーブの処理はこれで完了となります。
転送方向を受信したSCL信号の立ち上がりから約7μSでACK応答を戻すことができます。これは次のSCL信号の立ち上がりに対して十分なマージンを確保できています。

sin1    0                       ;ビット0受信
BF      ACKE,$NOTACK0 ;ACK応答しないなら抜ける
CLR1    PM4.1 ;ACK応答



(6)ACK応答完了
ACK応答が完了したら、ACKの転送完了(SCL信号の立ち上がりと立ち下がり)を待ち、ACK応答を切ります。これでアドレス受信処理は完了したので、後は転送方向に応じた処理となります。SCL信号の変化待ちでは処理に余裕があるので、前もって転送方向で処理を分けておきます。

MOV     A,IIC0 ;アドレスをAレジスタに
ROR     A,1 ;CY=0:受信、1:送信
BNC     $RXDATA ;受信なら分岐

(7)スレーブ送信でのアドレス9クロック目以降の処理(送信準備)
スレーブ送信では、事前にデータを準備する必要があります。その処理時間が必要なので、前サイクルのACK応答後に、すぐ処理を開始しておきます。SCL信号の立ち上がり周期10μsに対して、ACK応答は7.7μsで完了しているので、2.3μsの余裕があります。そこで、ACK応答完了処理のMOV A,IIC0命令から下記MOVW HL,AX命令までの実行後、SCL信号が立ち上がったかどうかを確認します。これらの処理に22クロック(2.8μs)必要なので、SCL信号の立ち上がりから0.5μsで、立ち上がり確認のBF P4.0,$$命令を実行することになります。これで、SCL信号の立ち上がりは1回だけの確認で次に進めます。この部分の処理は、7.5μsで完了します。

TXDATA:
MOVW    AX,TXPOINT ;データ・ポインタを読み出し
MOVW    HL,AX ;HLにポインタをセット
BF      P4.0,$$ ;SCL立ち上がり確認
TXLOOP:
MOV     A,[HL] ;送信データ読み出し
INCW    HL                      ;ポインタ更新
MOV     TXCOUNT,#8              ;ビット数をセット
TXBLOOP:
ROL     A,1 ;MSBをキャリーへ
BT      P4.0,$$ ;SCL立ち下がり待ち
SET1    PM4.1 ;ACK応答解除
BC      TXBIT ;データが1なら分岐
CLR1    PM4.1 ;SDAをロウに
TXBIT:



(8)データ送信処理~ACK確認
スレーブ送信処理では、SCL信号の変化を待つだけなので、ループで処理しても十分に処理可能です。そこで、ループ・カウンタ(変数TXCOUNT)を用いて制御します。8ビットの送信が完了すると、SDA信号を開放してACK応答を待ちます。マスタからACK応答があった場合には次のデータに進みます。ACK応答がなかった場合には更新されたデータ・ポインタを保存して、処理を終了します。

TXBIT:
BF      P4.0,$$ ;SCL立ち上がり待ち
DBNZ    TXCOUNT,$TXBLOOP ;8ビット分繰り返し
BT      P4.0,$$                 ;SCL立ち下がり待ち
SET1    PM4.1                   ;SDA開放
NOTACK0:
BF      P4.0,$$ ;SCL立ち上がり待ち
BF      P4.1,$TXLOOP ;ACKなら次データへ
MOVW    AX,HL                   ;ポインタをAXへ
MOVW    TXPOINT,AX              ;ポインタを保存する
SET1    STATUSFLAG.6            ;送信完了フラグをセット
BT      P4.0,$$ ;SCL立ち下がり待ち
POP     HL                      ;レジスタを復帰
POP     AX                      ;AXレジスタ復帰
CLR1    PIF3                    ;割り込み要求をクリア
RETI                            ;処理完了

スレーブ送信処理では、マスタからのNACK応答を確認した段階で処理を完了しています。この後、マスタはストップ・コンディションかスタート・コンディションを発行することになります。ここで抜けてしまうと、ストップ・コンディションを発行するときには、以下のような信号の動きとなり、SDAを立ち下がりだけを見ているので、スタート・コンディションを誤検出します。しかし、INTP3割り込み処理の中でSCLがロウなら何もしないで抜けるようになっていることと、SCLがハイならSDAの立ち上がり(ストップ・コンディション)で抜けるので、問題はありません。



(9)スレーブ受信での9クロック目以降の処理

RXDATA:
BF      P4.0,$$ ;SCL立ち上がり待ち
MOVW    AX,RXPOINT ;データ・ポインタを読み出し
MOVW    HL,AX ;HLにポインタをセット
BT      P4.0,$$ ;SCL立ち下がり待ち
SET1    PM4.1 ;ACK応答解除
MOV     IIC0,#00000000B ;データ初期値を設定

9クロック目のSCL信号が立ち下がったら、ACK応答を解除してSDA信号を解放します。引き続いて、データの受信処理に移ると以降は繰り返し処理となります。

ここでは以下の3つの場合があり、タイミング的に一番苦しくなります。
①次のデータを受信する
②スタート・コンディション(リスタート)を受け付ける
③ストップ・コンディションを受け付ける

①の場合にはSCL信号がロウの状態でSDA信号が変化して、SCL信号が立ち上がります。その後SDA信号は保持したままでSCL信号が立ち下がります。SCL信号が立ち下がって始めてデータだと分かります。この場合にはSCL信号のハイ幅が4μS以上で、SDA信号は250nSのセット・アップ時間と0nSのホールド時間で変化する可能性があります。この時間は判別できる時間間隔よりはるかに小さいので、単独または、SDA信号立ち下がりと同時にSCL信号の立ち下がりが検出できたらデータ受信と判断することになります。



②の場合にはSCL信号がロウの状態でSDA信号が立ち上がり、SCL信号がハイの状態でSDA信号が立ち下がります。SDA信号の立ち下がりでスタート・コンディションであることは分かりますが、SCL信号が立ち下がるまでは次には移れません。



③の場合にはSDA信号をどのタイミングで立ち下げるかで、さらに2つの場合が考えられます。SCL信号がロウの状態で立ち下がれば、単純にストップ・コンディションの発行だけとなります。SCL信号の立ち上げと同時またはその後でSDA信号が立ち下がった場合には、そこがスタート・コンディションと同じになります。その後でストップ・コンディションが発行されることになります。つまり、スタート・コンディションを検出した場合にはSCL信号が立ち下がるか、SDA信号が立ち上がるかを待つ必要があります。



この部分の状態遷移図を以下に示します。



この処理のプログラム例を示します。

①SDA信号がハイであったときの処理
まず、SCL信号が立ち上がったときに、SDA信号がハイの場合の処理を行います。青がデータ受信の場合で、赤がスタート・コンディションの場合です。SCL信号の立ち上がりとSDA信号の組み合わせでの場合分け(下記プログラムのLOOP21以降の3命令)は、SCL信号の立ち上がりから30クロック(3.7μs)以内に処理できるので、SCL信号の周期が短い(4μs)データ転送の場合でも、タイミングに問題はありません。STCLOOP以降の3命令で、データ受信開始かスタート・コンディション(リスタート)を待っています。データ受信では、SCL信号の立ち下がりから30クロック(3.7μs)以内に検出できるので、SCL信号がロウの期間(4.7μs)に処理可能です(以降の処理は)。

;
;       アドレス受信、データ受信した後の処理の判断を行う。
;
LOOP21:
MOV     A,P4 ;SCLとSDAを読み込む
BF      A.0,$LOOP21 ;SCL立ち上がり待ち
BF      A.1,$SDALOW ;SDAがロウなら分岐
;
;       スタート・コンディションかデータ待ち
;  SCLが立ち下がれば通常データ受信(⑤)
;  SDAが立ち下がればスタート・コンディション(②)
;  ここは必ずSCLの確認してからSDAを確認する必要がある。
;
STCLOOP:
MOV     A,P4 ;SCLとSDAを読み込む
BF      A.0,$DATAREAD ;SCLが立ち下がれば受信へ
BT      A.1,$STCLOOP ;変化なければループ



②リスタート
リスタートの場合、信号の読み取りが20クロックのループになり、1回は確実に確認できますが、確認処理完了に最大38クロックかかります。このとき、次データのためにSCL信号が立ち下がっている可能性があります。その後、4クロックで信号を確認します(次図の)が、このときの状態で、次が何かを判断する必要があります。
SCL信号が立ち下がれば(実線)、リスタート後のアドレス受信()の開始です。SCL信号がハイのままSDA信号が立ち上がれば(破線)、ストップ・コンディション()です。ここでは、どちらかの信号が変化するまで待ちます。



;
;       スタート・コンディション検出
;  スタート・コンディションを検出したらSCLの立下り(通信開始)か
;SDAの立ち上がりを待つ
;
MOV     IIC0,#0                 ;受信ワークをクリア
STCLOOP2:
MOV     A,P4 ;SCLとSDAを読み込む
BF      A.0,$ADDRESS2 ;SCLの立下りでアドレス受信
BF      A.1,$STCLOOP2 ;ストップ・コンディション以外

③アドレス受信
アドレス受信時の分岐先のADDRESS2は、本来ADDRESSですが、ここからの条件分岐では直接飛べず、また、いくつかの処理が必要なためのクッションです。RXDATAの直前に配置しておく必要があります。

;
;       リスタート時にアドレス受信へ戻る処理
;
ADDRESS2:
MOVW    AX,HL
MOVW    RXPOINT,AX ;データ・ポインタを保存
BR      ADDRESS

④ストップ・コンディション
受信を完了して、ストップ・コンディションを検出したら、処理を完了します。ここでは、その後の受信データ処理を考慮して、ACKEをクリアして、以降はマスタが通信を開始し、そのアドレスが一致してもACK応答しないようにしています。受信データの処理が完了したら、ACKEをセットしておく必要があります(変数IICC0に4を設定)。

;
;       ストップ・コンディション検出
;  通信完了フラグをセット、ACK応答禁止にして処理を完了します。
;
STPCND:
SET1    STATUSFLAG.7 ;通信完了フラグをセット
CLR1    ACKE ;ACK応答禁止に設定
MOVW    AX,HL
MOVW    RXPOINT,AX ;データ・ポインタを保存
POP     HL                      ;HLレジスタ復帰
POP     AX                      ;AXレジスタ復帰
CLR1    PIF3                    ;割り込み要求をクリア
RETI                            ;処理完了

⑤データ受信処理(SDA=Hのとき)
次データ受信の処理です。最初のビットは受信完了済みなので、残り7ビットの受信でACK応答し、受信データをバッファに書き込みます。その後、(9)スレーブ受信での9クロック目以降の処理の最初に戻ります。

;
;       データ受信処理
;  最上位ビットは既に受信済みのため、残り7ビット分を受信する。
;受信が完了して、ACK応答後9クロック目でウェイトして処理を抜ける。
;
DATAREAD:
MOV     IIC0,#10000000B ;ビット7は1であった
DATAREAD2:
sin1    6 ;ビット6を受信
sin1    5                       ;ビット5を受信
sin1    4                       ;ビット4を受信
sin1    3                       ;ビット3を受信
sin1    2                       ;ビット2を受信
sin1    1                       ;ビット1を受信
sin1    0                       ;ビット0を受信
CLR1    PM4.1 ;ACK応答
MOV     A,IIC0 ;受信データ読み出し
MOV     [HL],A ;受信データの保存
INCW    HL                      ;ポインタ更新
BF      P4.0,$$ ;SCL立ち上がり待ち
SET1    STATUSFLAG.6 ;受信フラグをセット
BT      P4.0,$$ ;SCL立下り待ち
SET1    PM4.1 ;ACK解除
BR      !LOOP21                 ;継続処理へ



上記のように、ビット6以降の処理へ分岐してきた場合も、SCL信号の立ち上がり前に処理を開始できるので、タイミングの問題はありません。また、SDA信号がロウの場合に、この処理を共通で使用しようとすると、分岐命令で6クロック分追加されますが、それでも十分にSCL信号がハイの状態でサンプリング可能で、処理でのタイミングの問題もありません。
最後のビット(ビット0)を受信したら、ACK応答して、受信データをバッファに保存します。この処理も、SCL信号の変化に対して十分にマージンがあります。



⑥SDA信号がロウであったときの処理
ここでは、SCL信号が立ち上がったときに、SDA信号がロウの場合の処理を行います。このときの次の状態は、データ受信()かストップ・コンディション()かのどちらかです。

;
;       ストップ・コンディションかデータ待ち
;  SCLが立ち下がれば通常データ受信(⑦)
;  SDAが立ち上がればストップ・コンディション(④)
;  ここは必ずSCLを確認してからSDAを確認する必要がある。
;
SDALOW:
STCLOOP3:
MOV     A,P4 ;SCLとSDAを読み込む
BF      A.0,$DATAREAD3 ;SCLが立ち下がれば受信へ
BF      A.1,$STCLOOP3 ;変化なければループ
BR      $STPCND                 ;ストップ・コンディションへ

⑦データ受信処理(SDA=Lのとき)
次データ受信の処理です。最初のビットは受信完了済みなので、残り7ビットの受信でACK応答し、受信データをバッファに書きこみます。この例では、最初のビットを0にして、のビット6受信に分岐させています(共通の処理)。

DATAREAD3:
MOV     IIC0,#00000000B ;ビット7は0であった
BR      $DATAREAD2              ;受信処理継続

(10)各種の宣言
このプログラムを使用する上で使用する変数や定数は以下のように定義しています。

;************************************************************************
;                                                                          *
;               データ領域(変数やフラグ)の定義                            *
;                                                                          *
;                                                                          *
;************************************************************************
PUBLIC          TXDATABUFF                      ;送信データ用
PUBLIC          RXDATABUFF                      ;受信データ用
PUBLIC          _RXDATABUFF                     ;受信データ用(C言語用)
PUBLIC          TXPOINT                         ;送信ポインタ
PUBLIC          _TXPOINT                        ;送信ポインタ(C言語用)
PUBLIC          RXPOINT                         ;受信ポインタ
PUBLIC          _RXPOINT                        ;受信ポインタ(C言語用)
PUBLIC          SVA0                            ;アドレス
PUBLIC          _SVA0                           ;アドレス(C言語用)
PUBLIC          IICC0                           ;動作指定
PUBLIC          _IICC0                          ;動作指定(C言語用)
PUBLIC          STATUSFLAG                      ;動作結果フラグ
PUBLIC          _STATUSFLAG                     ;動作結果フラグ(C言語用)
PUBLIC          __INTP3                         ;割り込み処理部

;
;       データ・バッファ
;  送信用、受信用で各々16バイト分を確保しておく
;
DSEG
TXDATABUFF:
DS      16                      ;送信データ用バッファ
RXDATABUFF:
_RXDATABUFF:
DS      16                      ;受信データ用バッファ

;
;       データ・ポインタ
;  送受信のデータ・バッファをアクセスするポインタの保存用
;
DSEG            saddrp
TXPOINT:
_TXPOINT:
DS      2                       ;送信用ポインタ
RXPOINT:
_RXPOINT:
DS      2                       ;受信用ポインタ
;
;  作業用のフラグ、変数
;
DSEG            saddr
IIC0:
_IICC0:
DS      1                       ;受信データ作業用
SVA0:
_SVA0:
DS      1                       ;スレーブ・アドレス保持用
IICC0:
_IICC0:
DS      1                       ;動作指定用
ACKE            EQU     IICC0.2                 ;ACK応答制御用
_ACKE           EQU     IICC0.2                 ;ACK応答制御用
;  1:ACK応答許可、0:ACK応答禁止
PUBLIC          ACKE
STATUSFLAG:
_STATUSFLAG:
DS      1                       ;ステータス・フラグ
;
;ビット 76543210
;       00000000        :動作していない
;       01000000        :送信完了
;       10000000        :受信完了

TXEND           EQU     STATUSFLAG.6
RXEND           EQU     STATUSFLAG.7


TXCOUNT:
DS      1                       ;送信のビット・カウント用

(11)このプログラム例を使う方法(アセンブラ)

このプログラムを使用するには初期設定を行う必要があります。以下の例は、そのための初期化を行う例で、初期化完了後にはループしているだけです。実際の使用に当たっては、MLOOP以降に実際に処理させたいプログラムを記述してください。この例では、送信の場合には、ラベルTXDATAに定義されたデータを送信します。受信の場合にはRXDATABUFFに受信データが格納されます。なお、送信するか、受信するかはマスタから送られてくるアドレスのLSBで指定されるので、通信が始まるまではわかりません。通信が行われたかはSTATUSFLAG(ビット7,6)で知ることができるので、このフラグを必要に応じて参照してください。

EXTRN           TXDATABUFF                      ;送信データ用
EXTRN           RXDATABUFF                      ;受信データ用
EXTRN           TXPOINT                         ;送信ポインタ
EXTRN           RXPOINT                         ;受信ポインタ
EXTRN           SVA0                            ;アドレス
EXTRN           IICC0                           ;動作指定
EXTRN           STATUSFLAG                      ;動作結果フラグ
EXTRN           __INTP3                         ;割り込み処理部
EXTBIT          ACKE                            ;ACK応答許可

RSTVECT CSEG    AT 0
DW              START
ORG     18H
DW              __INTP3                 ;割り込みベクタの設定

STKADR  DSEG    IHRAM
DS              32                      ; スタック領域(32バイト)
STACK:

CSEG
TXDATA:                                         ;送信テスト用のデータ
DB              055H,0AAH,0CCH,00,33H,01,02,03
DB              04,05,06,07,08,10H,20H,40H,80H
DB              0C0H,0E0H,0F0H,0F8H,0FCH,0FEH,0FFH
DB              99H,66H,44H,22H,11H,08,04,02
START:
MOVW            AX,#STACK
MOVW            SP,AX
MOV             PCC,#00000000B
MOV             WDTM,#01110000b         ;ウォッチドッグ・タイマ停止
SET1            LSRSTOP                 ;低速クロック停止
SET1            LVIMK
MOV             LVIM,#00000000b
MOV             LVIS,#00000000b         ;電圧を4V以上に設定
SET1            LVION                   ;低電圧検出回路起動
MOV             B,#0
DBNZ            B,$$
BT              LVIF,$$                 ;電源立ち上がり待ち
MOV             PPCC,#00000000b         ;8MHz動作に
MOV             SVA0,#10100000b         ;スレーブのアドレス設定
SET1            ACKE                    ;ACKEをセット
MOVW            AX,#TXDATA
MOVW            TXPOINT,AX
MOVW            AX,#RXDATABUFF
MOVW            RXPOINT,AX
MOVW            HL,AX
MOV             B,#16
MOV             A,#0
MEMCLRLOOP:                                     ;受信バッファのクリア
MOV             [HL],A
INC             L
DBNZ            B,$MEMCLRLOOP

MOV             PM4,#11111111b
MOV             P4,#00000000b
MOV             INTM1,#00000000b        ;INTP3を立ち下がりエッジ検出に
CLR1            PIF3
CLR1            PMK3
EI
;
;  初期設定を完了して、通信可能に
;

MLOOP:                                          ;SDA立下りの割り込み待ち
NOP
;
;  ここに必要な処理が入るが、この例では何も行わずループさせている。
;通信結果はSTATUSFLAGのビット7,6で示される。また、受信した場合には
;ACKEビットがクリアされ、マスタからのアドレス送信に対して、NACK
;応答するので、受信データを処理した後で受信ポインタ(RXPOINT)の再設定
;ACKEビットのセット、STATUSFLAGのクリアを行う必要がある。
;
BR              $MLOOP

END

また、オプション・バイトは以下の例のように定義しておきます。

@@OPTB  CSEG    AT      0080H
DB      10011100b
DB      11111111b
END

(12)このプログラム例の動作確認(アセンブラ)

このプログラムの動作確認やデバッグには、通信相手となるI2Cバスのマスタが必要です。ここでは簡単な動作確認手段として、SM+を利用して動作の確認を行う例を示します。
SM+を起動したら、I2Cバスの信号波形を確認するために、「シミュレータ(S)」のプルダウン・メニューから「タイミングチャート(T)」を選択します。タイミング・チャートの「編集」コマンドのプルダウン・メニューから「端子選択(P)」を選択して、以下のように確認したい端子を定義します。ここでは、P40端子をSCLの名前で、P41/INTP3端子をSDAの名前で定義します。



次に「シミュレータ(S)」のプルダウン・メニューから「入出力パネル(P)」を選択し「入出力パネルウィンドウ」を開きます。引き続き、「入出力パネル」がアクティブな状態で「部品(P)」コマンドのプルダウン・メニューから「プルアップ/プルダウン設定(W)」を選択し、下記のようにプルアップ指定を行います。



PM+でリスト・コンバータを起動する設定にして、ビルドすると絶対的なアドレスを確認でき、変数の定義部分からは以下のようなリストが得られます。



これから、変数を確認するために、SM+の「ブラウズ(B)」コマンドのプルダウン・メニューから「メモリ(M)」を選択し、FE80~の領域が見られるようにしておきます。スタート・コンディションで割り込み処理に分岐したときのRAMの内容は以下のようになります。緑の下線をつけた部分が送信データのポインタ、黄色の下線をつけた部分が受信データの格納用ポインタ、赤い下線をつけた部分はスレーブとしてのアドレス、青い下線をつけた部分がACKEフラグとなります。



動作の確認をするにはマスタからの信号をシミュレーションする必要があります。このためには、「シミュレータ(S)」のプルダウン・メニューから「信号データエディタ(E)」を選択し、その「編集」のプルダウン・メニューから「端子選択(P)」を選択します。「端子選択パネル」で下記のように端子を定義します。



端子の選択が完了したら、次は時間の単位設定を行います。「編集」のプルダウン・メニューから「時間単位(N)」を選ぶと選択肢が表示されるので、「マイクロ秒(U)」を選択します。時間の単位設定が完了したら、信号パターンを入力します。ここで、使用するデータ・パターンは初期値としてSCLがハイ(1)でSDAをハイ・インピーダンス(Z)にします。2行目では、5μS経過したらSDAを0にします。これはスタート・コンディションになります。3行目で、さらに5μS経過したら、SCLを0にしてアドレス送信の準備を行います。標準モードを意識して、SCLのハイ期間とロウ期間は5μSにします。



ここで準備した先頭部分のパターンを以下に示します。このデータ・パターンを使用するには、一旦メイン・ルーチンのループ部分にブレーク・ポイントを設定して実行させ、ブレークがかかったら、「編集」プルダウン・メニューの「信号入力(I)」で「開始(S)」を選択します。
その後、ステップ実行を何回かクリックすると、スタート・コンディションが生成され、それを検出して割り込み処理に分岐します。このときのウィンドウの状態を以下に示します。黄色い部分が出力中のデータを示します。





このデータ・パターンはスレーブ送信でアドレスA0を出力し、スレーブから2バイトのデータを受け取るものです。1バイト目にはACK応答することで、引き続きスレーブから2バイト目が送信されます。2バイト目にはNACK応答することでスレーブは送信を止めて、通信処理を終了し、割り込みから復帰します。
上記のスタート・コンディション検出の割り込みが起動した状態で「RUN」させると、メイン処理に戻りブレークがかかります。ここまでの波形を確認したのが下のタイミング・チャートになります。上の信号がSCLで下の信号がSDAになります。スタート・コンディションの生成から、スレーブ・アドレス転送、それに対するスレーブからのACK応答と、その後のスレーブからの送信データ及びマスタからのACK及びNACK応答までが見られます。これはプログラムとしてマスタからのNACK応答のSCLの立ち下がりを確認して割り込み処理から復帰していることと一致しています。



このときのRAMの内容を確認すると、送信データのポインタが2バイト分更新されているのがわかります。また、FE87番地のSTATUSFLAGが送信完了のステータスになっています。



その後、ストップ・コンディションとスタート・コンディションの後にスレーブ受信させたときのタイミング・チャートを以下に示します。ここでは、マスタからAAHと55Hが送信された後にリスタート(スタート・コンディション)が発行されています。



リスタート以降のタイミング・チャートを以下に示します。ストップ・コンディションではなく、リスタートでスレーブ受信が完了しているため、引き続いて、アドレスに対してACK応答を行い、スレーブ送信を開始しています。



以上の処理が完了した段階で、RAMの内容は以下のようになっています。送信データのポインタがさらに2バイト分更新されているのがわかります。また、受信データ用ポインタも更新され、受信データ・バッファにAAHと55Hが格納されています。



(13)このプログラム例を使う方法(C言語記述)

このプログラムは基本的に割り込みで起動し、そのときのI2Cのスレーブ・アドレスのLSBで転送方向が決まってしまいます。そのため、メイン処理は基本的に初期化だけとなります。また、オプション・バイトの定義部分に、使用する割り込み(INTP3)のベクタも合わせて定義しておきます。

EXTRN   __INTP3
RSTVECT CSEG    AT 18H
DW              __INTP3
@@OPTB  CSEG    AT      0080H
DB      10011100b
DB      11111111b
END

C記述のメイン処理のプログラム例は以下のようになります。

#pragma SFR
#pragma EI
#pragma DI
#pragma NOP

extern unsigned char *TXPOINT;
extern unsigned char RXDATABUFF[16];
extern unsigned char *RXPOINT;
extern unsigned char STATSFLAG;
extern unsigned char SVA0;
extern bit ACKE;


/*--------------------------------------------------------------*/
/*                                                                */
/*                                                                */
/*      Function Body                                            */
/*                                                                */
/*                                                                */
/*--------------------------------------------------------------*/
/****************************************************************/
/*                                                                */
/*                                                                */
/*      Hardware initialize                                      */
/*                              (call by start up routine)       */
/*                                                                */
/****************************************************************/
void hdwinit(void)
{
unsigned char   work1;
DI();
WDTM = 0b01110000;              /* stop WDT             */
LVIMK = 1;
LVIS = 0x00;                    /* select VLI=4V        */
LVIM = 0b10000000;              /* Start VLI detection  */
PCC = 0x00;                     /* CPU clock is fx/4    */
/*
wait for 0.17ms(=0.2ms-(32+30)*52/1000)
*/
for (work1 = 0; work1 < 10; work1++){
NOP();
}

while ( LVIF ){
NOP();
}       /* wait VDD > 4V */

PPCC = 0;                       /* CPU clock is fx=fR   */

PM4 = 0b11111111;               /* set PORT4 as input   */
P4 = 0b00000000;                /* clear port data to 0 */
INTM1 = 0b00000000;             /* detect falling edge  */
PIF3 = 0;
PMK3 = 0;

SVA0 = 0b10100000;              /* set slave address    */
ACKE = 1;                       /* enable ACK           */
}

/****************************************************************/
/*                                                                */
/*                                                                */
/*      Main                                                      */
/*                                                                */
/*                                                                */
/****************************************************************/
void main(void)
{
const unsigned char txdata[8] =
{0x55,0xAA,0xCC,0x00,0x33,0x01,0x02,0x03};
unsigned char   count;
TXPOINT = txdata;
RXPOINT = RXDATABUFF;
for(count = 0 ; count<16 ; count++){
RXDATABUFF[count] = 0;
}
EI();

while(1){

NOP();                  /* wait interrupt       */
NOP();

}

}

 

適用製品 

78K0S/Kx
他にご質問がございましたら、リクエストを送信してください