Renesas Synergy™

FAQ 1007014 : ソフトウェアでの半二重3線式シリアル・スレーブ機能の実現について1(78K0S/Kx1+)

ソフトウェアでの3線式シリアルのスレーブ機能の検討」の結果から
・スレーブとして送信動作又は受信動作(半二重通信)のみとする
CSはエッジ検出割り込みを使用(データ転送まで約10μS必要)
CSに対して、BUSY信号をサポート
・通信タイプ1(SCK立ち下がりで送信、立ち上がりで受信)
・転送速度は180kbps程度までに対応

の条件での3線式シリアルのスレーブ機能をソフトウェアによるポート制御で実現してみます。

Ⅰ 仕様検討
1. 接続



ここでは、上記の端子構成例に示すように2本のハンドシェイク信号を含めて5本のポートを使用するものとします。

2. 動作概要
SPIのマスタからのCS信号の立ち下がりをエッジ検出割り込みにより検出し、ベクタ割り込み処理により通信を起動します。通信の方向は前もって、フラグに設定しておくものとします。通信動作は受信の場合には、ベクタ割り込み処理の中でSCK信号の立ち上がりでSI端子の状態を読み込みます。8ビットのデータを読み込むとその結果をバッファの中に保存します。そのとき、CS信号を確認して、もしハイ・レベルであれば処理を終了します。ロウ・レベルが継続しているときには、受信処理を継続します。送信の場合には、送信バッファの内容をSCK信号の立ち下がりでSO端子から出力します。
なお、データはMSBファーストで転送します。また、バッファは16バイトの容量を確保しておき、先頭から使用するだけとし、リング・バッファ構造にはしません。

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



初期状態では受信モードで待機します(状態遷移の待機状態)。その状態で、CS信号が立ち下がると、ベクタ割り込みを受け付けて、スレーブ受信となり、BUSY信号を立ち下げて、SCK信号の立ち下がりを待ち、BUSY信号を立ち上げます。その後、SCK信号の立ち上がりに同期してSI信号を読み込みます。8ビット分の受信が完了すると、データをバッファに保存して、BUSY信号を立ち下げて次の通信準備ができたことをマスタに通知して、次のデータか通信完了(CS信号の立ち上がり)を待ちます。
待機状態(プログラムとしては何もしていない)で、メイン処理から送信要求(送信したいデータをバッファに書き込む)があると、送信待機状態となります(これも、単にバッファに送信データがあるだけで、プログラムとして何か実行している訳ではありません)。この状態でCS信号が立ち下がると、割り込みを受け付けてスレーブ送信となり、BUSY信号を立ち下げます。SCK信号の立ち下がりに同期して出力データをSO信号に出力し、BUSY信号を立ち上げます。1バイトの送信が完了したら、次のデータを準備します。このあとは4つの条件に分かれます。

次のデータが残っている状態で、CS信号が引き続きロウ・レベルであれば、次データ送信のためのBUSY信号を立ち下げて次の通信準備ができたことをマスタに通知して、SCK信号の立ち下がり待ちとなります。
次のデータがあるのに、CS信号がハイ・レベルになった場合には、BUSY信号をHi-Zにしてから割り込み処理を抜けて、送信待機状態になります。
次のデータがなくなって、CS信号がハイ・レベルになった場合には、通信完了として、BUSY信号をHi-Zにしてから割り込み処理を抜けて、待機状態に戻ります。
次のデータがなくなったが、CS信号がロウ・レベルを継続している場合には、BUSY信号を立ち下げて、受信待機状態に移行します。



Ⅱ 制御プログラム検討
1. 通信の起動(CS信号の立ち下がり検出)
通信を開始するタイミングはCS信号の立下りをエッジ検出したベクタ割り込みで検出するものとします。このとき、送信データがあるかどうかのフラグで受信処理を行うのか送信処理を行なうか判断します。送信の場合には、バッファからデータを読み出して送信の準備を行います。CS信号の立下りから実際にデータを送受信(特に送信)できるまでには58クロック+割り込み応答時間(最大19クロック)で8MHz動作とすると10μsが必要になります。ただし、CS信号の立下りから6クロック+割り込み応答時間(最大19クロック)でBUSY信号が出力されますし、それ以前のBUSY信号はHi-Z(=ハイレベル)となっているので、BUSY信号を利用することで、この部分の応答速度をカバーすることができます。



この起動部分のプログラム例を以下に示します。BUSY(P42の出力ラッチ)には初期状態で1がセットされているので、1行目でPM4を設定した段階 (①)でハイ・レベルが出力されます。通信可能(下記の例では送信可能)になると(②)、BUSYを立ち下げて、マスタに対して通信準備ができたことを示 しています。なお、実際に送信するためのSCK信号確認までは、さらに13クロックかかります。最初のビットの処理を2ビット目以降と分けると、この時間を5クロックまで短縮することはできますが、マスタでのBUSY立ち下がり確認からSCK出力までの時間を考えるとあまり意味はないと考えられます。


__INTP1:
MOV     PM4,# 11011011b         ; 6:So(P45)とBUSY(P42)を出力に
PUSH    AX                      ; 4:使用レジスタをセーブ
PUSH    HL                      ; 4:
BF      TxFlag,$RxData ;10:フラグが0なら受信処理へ
MOVW    AX,TxPointer            ; 6:送信ポインタ読み出し
MOVW    HL,AX                   ; 4:
TxLoop:
MOV     A,[HL]                  ; 6:送信データ読み出し
MOV     TxBitCount,#8           ; 6:ビット長を設定
CLR     P4.2 ; 6:BUSYを立ち下げる
;
;  送信準備完了
;


2. 送信処理
送信待機状態で、CS信号の立ち下がりを検出したら送信処理に移ります。送信処理がスタートすると、SCK信 号の立ち下がりを検出して、データをSO端子に出力します。その際に同時にBUSY信号を立ち上げて、送信処理を開始したことを示します(③)。この処理 では実際にSO信号を出力する際のMOV命令でBUSYを立ち上げる処理も同時に行うことで、処理時間の増加をなくしています。その部分を含めた処理は以 下のようになります。ここでは、できるだけSCK信号の立ち下がりからの遅延を小さくするため、送信データが0か1かの判定を前もって行っておきます。また、8ビットのデータ送信が完了するまでは、CS信号の確認はしません。


TxLoop2:
ROL     A,1                     ; 2:MSBをキャリーへ
BC      $Data1                  ; 6:送信データ判定
;
;  出力データが0の場合の処理
;
TxLoop3:
BT      P4.0,$TxLoop3 ;10:SCKの立下り待ち
MOV     P4,#00000100b           ; 6:0を出力BUSY立ち上げ
BR      $NextBit ; 6:次のビット処理へ
;
;   出力データが1の場合の処理
;
Data1:
TxLoop4:
BT      P4.0,$TxLoop4 ;10:SCKの立下り待ち
MOV     P4,#00100100b           ; 6:1を出力BUSY立ち上げ
NextBit:
;
;  1ビットの送信完了
;
TxLoop5:
BF      P4.0,$TxLoop5           ;10:SCKの立ち上がり待ち
DBNZ    TxBitCount,$TxLoop2     ; 8:8ビット分繰り返す
INCW    HL                      ; 4:ポインタ更新
BT      P4.3,$TxExit            ;10:CSがハイなら抜ける
DBNZ    TxCount,$TxLoop         ; 8:残データあればループ


変数TxBitCountをループ・カウンタとして、TxLoop2までを8回ループすることで、1バイトのデータ送信が完了します。次にデータ・ポインタを更新してからCS信号の確認を行い、CS信号が引き続きロウ・レベルであれば次のデータに移ります。
上記のプログラムは通信速度をできるだけ高速化するために、CS信号は8ビット目のSCKの立ち上がりでしかチェックしていません。そのため、マスタは最後のデータの転送が始まったら(遅くとも、8ビット目の転送までに)CS信号を切るようにする必要があります。どうしても、このタイミングが不可能な場合には、転送を1ビット目と2ビット目以降で処理を変える必要があります。この場合の処理プログラム例は後で示します。

3. 送信完了処理
CS信号が切れる(ハイ・レベルになる)と、以降の通信は行わなくなります。また、送信するデータがなくなった場合には送信は完了となります。送信を継続しない場合としては以下の3つの場合があります。
①送信データはなくなったがCS信号はロウ・レベルを継続 → 受信処理へ
②送信データがなくなり、さらにCS信号も切れた場合 → 通信完了
③送信データはあるが、CS信号が切れた場合 → 通信中断

(1)送信完了して受信へ移行
①の場合の処理プログラムは以下のようになります。この処理の直前で、変数TxCountのカウントにより送信は完了していることが確認されています。そのため、送信関係のフラグやポインタを初期化してから、CS信号を確認します。この結果、ロウ・レベルを継続していると受信処理に分岐します。ハイ・レベル(②の場合)なら、割り込み処理から抜けますが、CS信号のタイミングからここで割り込みから抜けることは殆んど考えられません。

;
;  データ送信は完了
;
CLR1    TxFlag                  ; 6:送信フラグをクリア
MOVW    AX,#TxBuff              ; 6:ポインタを初期化
MOVW    SetPointer,AX           ; 8:セット・ポインタ初期化
MOVW    TxPointer,AX            ; 8:送信ポインタ初期化
BF      P4.3,$RxData ;10:送信完了でCSがロウなら受信
MOV     PM4,#0FFH               ; 6:ポートをHi-Zに
POP     HL
POP     AX
CLR1    PIF1
RETI


(2)全データ送信完了で通信完了
②の場合の処理プログラムは以下のようになります。この処理はCS信号がハイ・レベルなっていることが確認された状態で処理を開始します。最初にSO信号とBUSY信号出力をHi-Zにしています。SCK信号の立ち上がりを確認してからSO信号がHi-Zになるまでの時間は約33クロックとなります。マスタの受信データ保持時間がこれ以上必要な場合には1行目のMOV命令の前にNOP命令等を追加して実行時間を延ばしてください。2行目のDBNZ命令で残りデータ数を確認し、残りデータ数が0なら②として処理を完了して割り込みから復帰します。

;
;  CS立ち上がりでの処理中断
;
TxExit:
MOV     PM4,#0FFH               ; 6:ポートをHi-Zに
DBNZ    TxCount,$TxExit2 ; 6:残りデータあれば分岐
;
;  CSがoffし、データ送信も完了
;
MOVW    AX,#TxBuff
MOVW    SetPointer,AX           ;セット・ポインタ初期化
MOVW    TxPointer,AX            ;送信ポインタ初期化
CLR1    TxFlag                  ; 6:送信フラグクリア
POP     HL                      ; 6:レジスタを復帰
POP     AX                      ; 6:レジスタ復帰
CLR1    PIF1                    ; 6:割り込み要求クリア
RETI


(3)残りデータありの状態で通信終了
③の場合の処理プログラムは以下のようになります。ここでは、次の送信のためのポインタを保存し、割り込みから復帰します。

;
;  残りデータがあるのにCSがoffしたので、次の準備を行う。
;
TxExit2:
MOVW    AX,HL                   ; 4:ポインタを保存
MOVW    TxPointer,AX            ; 8:メモリに保存
POP     HL                      ; 6:レジスタを復帰
POP     AX                      ; 6:レジスタ復帰
CLR1    PIF1                    ; 6:割り込み要求クリア
RETI                            ; 6:処理完了


4. 送信開始でもCS信号を確認する処理例
これまで説明した処理ではCS信号はバイトの送信完了時のみで確認していましたが、これに加えて、バイトの送信開始待ちでも確認するようにしたプログラム例を以下に示します。1. 通信の起動TxLoopからTxLoop2の前までの部分が以下のようになります。下記のの部分が変更点となります。基本的に最初のビット(ビット7)のSCK信号の立ち下がり待ちが追加になり、残りの7ビット分のみをループで処理するだけです。

;
;       1バイトの送信処理
;
TxLoop:
MOV     A,[HL]                  ; 6:送信データ読み出し
MOV     TxBitCount,#7 ; 6:ビット長-1を設定
CLR     P4.2                    ; 6:BUSYを立ち下げる
;
;       最初のビット処理(BUSYのチェックを含む)
;
ROL     A,1                     ; 2:ビット7をキャリーへ
BC      $B7Data1                ; 6:データが1なら分岐
TxB7Loop:

BT      P4.3,$TxExit            ;10:CSがハイなら終了
BT      P4.0,$TxB7Loop          ;10:SCK立下り待ち
MOV     P4,#00000100b           ; 6:送信&BUSYセット
BR      $TxBit6                 ; 6:残り7ビット処理へ
B7Data1:

BT      P4.3,$TxExit            ;10:CSがハイなら終了
BT      SCKPort,$B7Data1        ;10:SCK立下り待ち
MOV     SIOPort,#00100100b      ; 6:送信&BUSYセット
TxBit6:
BF      P4.0,$TxBit6            ;10:SCK立ち上がり待ち



5. 受信処理
送信するデータがない状態で、CS信号がロウ・レベルになったら受信処理を行います。

(1)マクロの定義
ここでは、ビット毎の受信処理用にマクロを定義することで、プログラムの記述を簡素化(sin1 6の ように記述するだけ)します。このマクロは受信データを変数SIOのオペランドで指定されたビットにセットするものです。サブルーチンとは異なり、マクロ を参照したところにはマクロの内容そのものが展開されます。つまり、ビット毎の受信処理が並びます。なお、最初のビットについてはCS信号の確認を行うためにマクロは使用しません。


;***********************************************************************
;                                                                      *
;       ビット受信処理用マクロ                                         *
;                                                                      *
;***********************************************************************
;
sin1    macro   bitno
LOCAL   Datais0,Loop
BT      P4.0,$$                         ;SCK立ち下がり待ち
Loop:
MOV     A,P4                            ;SCKとSI読み出し
BF      A.0t,$Loop                      ;SCKがロウならループ
BF      A.4,$Datais0                    ;SIを確認
SET1    SIO.bitno
Datais0:
endm


(2)受信起動処理(及び完了処理
受信準備が完了したら、BUSY信号を立ち下げて、SCK信号の立ち下がりを待ってループします。SCK信号が立ち下がったなら、実際の受信処理に移行します。SCK信号の立下りの前にCS信号が立ち上がったら、受信完了となり、受信データを格納するためのポインタを更新して、割り込みから復帰します。


RxData:
MOVW    AX,RxSetPointer         ; 6:受信用ポインタ読み出し
MOVW    HL,AX                   ; 4:HLにポインタを設定
RxLoop:
MOV     SIO,#0                  ; 6:受信初期値を00に
CLR1    P4.2                    ; 6:BUSYを立ち下げ
RxLoop2:
MOV     A,P4                    ; 4:ポートのリード
BF      A.0,$RxBit7             ;10:SCKがロウなら受信へ
BF      A.3,$RxLoop2            ;10:CSがロウなら継続
;
;       CSの立ち上がりで、受信処理完了へ
;
RxExit:

SET1    P4.2                    ; 6:BUSYを立ち上げ
RxExit2:
MOV     PM4,0FFH                ; 6:ポートの出力禁止
MOVW    AX,HL                   ; 4:ポインタを作業領域に
MOVW    RxSetPointer,AX         ; 6:コピーする

CLR1    PIF1
POP     HL
POP     AX
RETI



(3)受信処理本体
SCK信号が立ち下がって、通信がスタートしたらBUSY信号を立ち上げます。引き続き、SCK信号の立ち上がりを待って、読み込んだSI信号を変数SIOのビット7に取り込みます。ビット6以降はマクロにより処理を行い、変数SIOに得られた受信データをバッファに格納し、受信データ数を+1して、次のデータ受信に移ります。

;
;       SCK立ち上がりを待って、データの読み出し開始
;
RxBit7:
SET1    P4.2                    ;BUSYを立ち上げる
RxLoop3:
MOV     A,P4
BF      A.0,$RxLoop3            ;SCKの立ち上がり待ち
BF      A.4,$RxDatais0
SET1    SIO.7                   ;ビット7をセット
RxDatais0:
sin1    6                       ;ビット6を受信
sin1    5                       ;ビット5を受信
sin1    4                       ;ビット4を受信
sin1    3                       ;ビット3を受信
sin1    2                       ;ビット2を受信
sin1    1                       ;ビット1を受信
sin1    0                       ;ビット0を受信
MOV     A,SIO                   ;受信データ読み出し
MOV     [HL],A                  ;バッファに格納
INCW    HL
INC     RxCount
BT      CSPort,$RxExit3
BR      RxLoop
RxExit3:
BR      RxExit2


Ⅲ 周辺プログラム検討

ここでは、これまで説明してきた制御プログラムを使用するためのサポート・プログラムについて示します。ここで触れるプログラムは以下の通りです。なお、ここで触れるプログラムはサブルーチン化され、C記述のプログラムからも関数呼び出しできるようにしてあります。

①初期化処理
②送信ステータス確認
③受信ステータス確認
④1バイトのデータを送信バッファに追加
⑤送信バッファ設定
⑥受信バッファから1バイトのデータ読み出し
⑦受信バッファ設定


1. 初期化処理
制御プログラムを使用するために、ポインタ類の初期化や受信データ・バッファのクリア及び割り込みの設定を行います。この処理はバッファを制御するためのポインタや変数を設定するため、割り込み禁止で処理させます。通常は起動後に1回使用します。


;***********************************************************************
;                                                                      *
;       送信/受信(バッファ)の初期化                                 *
;                                                                      *
;  送信/受信で使用するバッファを内部で定義したバッファに設定し、受信  *
;バッファはデータを0クリアしておきます。                              *
;  また、CS信号の検出で使用するINTP1に立ち下がりエッジ検出を有効*
;に設定し、割り込みマスクの解除までの処理を行います。                  *
;  入力データ   :なし                                                 *
;  戻り値       :なし                                                 *
; 使用レジスタ :なし                                                 *
;                                                                      *
;       extern void _init_buff(void);                                  *
;                                                                      *
;***********************************************************************
;
PUBLIC  __init_buff,INIT_BUFF
__init_buff:
INIT_BUFF:
PUSH    PSW                     ;IEフラグをセーブ
DI                              ;以下の処理は割り込み禁止で行う
PUSH    HL
PUSH    AX

MOVW    AX,#TxBuff
MOVW    TxPointer,AX            ;送信データ読み出しポインタ初期化
MOVW    SetPointer,AX           ;送信データ設定ポインタ初期化
MOV     TxCount,#0              ;データ数を初期化
CLR1    TxFlag                  ;送信フラグをクリア

MOVW    AX,#RxBuff              ;受信バッファの初期化初期化
MOVW    RxPointer,AX            ;受信データ読み出しポインタ初期化
MOVW    RxSetPointer,AX         ;受信データ格納用ポインタ初期化
MOV     RxCount,#16             ;データ数を初期化
MOVW    HL,AX
MOV     A,#0
Init_Loop:
MOV     [HL],A                  ;受信バッファをクリア
INCW    HL
DBNZ    RxCount,$Init_Loop
POP     AX
POP     HL
MOV     P4,#00100100b           ;SOとBUSY出力をハイにする
MOV     INTM1,#00000000b        ;INTP1を立ち下がりエッジ検出に
CLR1    PIF1
CLR1    PMK1
POP     PSW
RET


2. 送信ステータス確認
送信データ・バッファの未送信のデータ数を戻します。結果はCレジスタで確認できますが、それ以外にCYフラグでバッファが空かどうかの確認もできます。通常は、送信データ・バッファに送信データをセットした後で、送信が完了したかを確認するために使用します。


;***********************************************************************
;                                                                      *
;       送信データ・バッファのステータス確認                           *
;  入力データ   :なし                                                 *
;  戻り値       :Cレジスタ(空ならCY=0)                         *
;  使用レジスタ :なし                                                 *
;  バッファの残りデータ数を戻します。                                  *
;       extern unsigned char _check_txbuff(void);                      *
;                                                                      *
;***********************************************************************
;
PUBLIC  __check_txbuff,CHECK_TXBUFF
__check_txbuff:
CHECK_TXBUFF:
PUSH    AX
MOV     A,TxCount               ;バッファ中のデータ数読み出し
MOV     C,A                     ;戻り値にセット
SUB     A,#1                    ;空ならCYをセット
NOT1    CY                      ;CYを反転
POP     AX
RET


3. 受信ステータス確認
受信データ・バッファの中にある受信データの数を戻します。受信したデータ数はCレジスタに格納されて戻ります。データがあれば、1バイト読み出し等でバッファから読み出してください。


;***********************************************************************
;                                                                      *
;       受信データ・バッファのステータス確認                           *
;  入力データ   :なし                                                 *
;  戻り値       :Cレジスタ、CYフラグ(バッファ空)                 *
;  使用レジスタ :なし                                                 *
;  バッファ中のデータ数を戻します。データが存在するときにデータを読み  *
;出してください。                                                      *
;       extern unsigned char _check_rxbuff(void);                      *
;                                                                      *
;***********************************************************************
;
PUBLIC  __check_rxbuff,CHECK_RXBUFF
__check_rxbuff:
CHECK_RXBUFF:
PUSH    AX
MOV     A,RxCount               ;バッファ中のデータ数読み出し
MOV     C,A                     ;戻り値にセット
SUB     A,#1                    ;データ数0ならCYをセット
POP     AX
RET


4. 1バイトのデータを送信バッファに追加
Xレジスタにセットされたデータを送信データ・バッファに追加します。なお、バッファの大きさは管理していないので、送信バッファのサイズ以上にデータを追加しないようにする必要があります。送信バッファ内のデータ数は送信ステータス確認で確認してください。


;***********************************************************************
;                                                                      *
;       送信データ1バイトをバッファに追加                             *
;  入力データ   :Xレジスタ                                           *
;  戻り値       :なし                                                 *
;  使用レジスタ :なし                                                 *
;  バッファのデータが全て送信されると、TxFlagが0になります。          *
;       extern void _add_data(unsigned char);                          *
;                                                                      *
;***********************************************************************
;
PUBLIC  __add_data,ADD_DATA
__add_data:
ADD_DATA:
PUSH    HL
PUSH    AX
MOVW    HL,AX                   ;データをセーブ
MOVW    AX,SetPointer           ;セット・ポインタ読み出し
XCHW    AX,HL
MOV     A,X
MOV     [HL],A                  ;データをバッファにセット
INCW    HL                      ;ポインタ更新
XCHW    AX,HL                   ;H:データ、AX:ポインタ
MOVW    SetPointer,AX           ;ポインタ保存
PUSH    PSW                     ;PSWのセーブ
DI                              ;割り込み禁止
INC     TxCount                 ;データ数を+1
SET1    TxFlag                  ;送信要求フラグをセット
MOV     P4,#00100100b           ;SOとBUSY出力をハイにする
POP     PSW
POP     AX
POP     HL
RET


5. 送信バッファ設定
複数バイトのデータを送信する場合に1バイトのデータ送信を 必要な回数使用しても実現できますが、それよりもバッファを切り替えたほうが便利です。ここでは、AXレジスタに送信したいデータが格納されたバッファの アドレス、スタックにデータ数を設定してサブルーチン・コールします。このようなパラメータの渡し方はCC78K0Sの引数の渡し方とあわせるためのもの です。アセンブラで使用するだけの場合には、データ数もレジスタで渡すようにした方が処理は簡単になります。送信が完了したかは、送信ステータス確認で確認することができます。


;***********************************************************************
;                                                                      *
;       送信データNバイトをバッファにセット                           *
;  入力データ   :AXレジスタ(バッファの先頭アドレス):第一引数      *
;               :スタック(データ数)                  :第二引数      *
;  戻り値       :なし                                                 *
;  使用レジスタ :なし                                                 *
;  バッファのデータが全て送信されると、TxFlagが0になります。          *
;                                                                      *
;       extern void _set_data(unsigned int,unsigned char);             *
;                                                                      *
;***********************************************************************
;
PUBLIC  __set_data,SET_DATA
__set_data:
SET_DATA:
PUSH    PSW                     ;PSWをセーブ
DI                              ;割り込み禁止
PUSH    HL                      ;使用レジスタをセーブ
PUSH    AX
MOVW    TxPointer,AX            ;送信ポインタを変更
MOVW    SetPointer,AX           ;送信セット・ポインタを変更
MOVW    AX,SP                   ;スタック・ポインタ読み出し
MOVW    HL,AX
MOV     A,[HL+7]                ;データ数読み出し
;
;  ここでのスタックの内容は以下の通り
;       HL(SP)  ---> X
;       HL+1    ---> A
;       HL+2    ---> L
;       HL+3    ---> H
;       HL+4    ---> PSW
;       HL+5    ---> 戻り番地(下位)
;       HL+6    ---> 戻り番地(上位)
;       HL+7    ---> データ数
;       HL+8    ---> ダミー・データ
;
MOV     TxCount,A               ;データ数を設定
CLR1    TxFlag                  ;フラグをクリアしておく
AND     A,A                     ;データ数チェック
BZ      $Set_Data_End           ;データ数0ならポインタセットのみ
SET1    TxFlag                  ;送信要求フラグをセット
POP     HL
PUSH    HL
ADD     A,L
MOV     X,A
MOV     A,H
ADDC    A,#0
MOVW    SetPointer,AX           ;送信セット・ポインタを変更
Set_Data_End:
MOV     P4,#00100100b           ;SOとBUSY出力をハイにする
POP     AX                      ;レジスタの復帰
POP     HL
POP     PSW
RET


6. 受信バッファから1バイトのデータ読み出し
受信データ・バッファからもっとも古いデータを読み出して、Cレジスタに格納して戻ります。もし、受信データ・バッファにデータがない場合にはCレジスタには0が格納され、CYフラグがセットされます。読み出してデータがなくなったら、ポインタを初期値に戻します。


;***********************************************************************
;                                                                      *
;       受信データ1バイトをバッファから読み出す                       *
;  入力データ   :なし                                                 *
;  戻り値       :Cレジスタ(データなければCY=1)                     *
;  使用レジスタ :なし                                                 *
;  バッファから最も古いデータを読み出し、残りデータ数を-1します。    *
;       extern unsigned char _get_data(void);                          *
;                                                                      *
;***********************************************************************
;
PUBLIC  __get_data,GET_DATA
__get_data:
GET_DATA:
PUSH    AX
MOV     A,RxCount               ;データ数を読み出し
CMP     A,#1                    ;データ数チェック
MOV     C,A                     ;ダミーの戻り値をセット
BC      $GetEnd                 ;データなければ戻る
PUSH    HL
MOVW    AX,RxPointer            ;リード・ポインタ読み出し
MOVW    HL,AX                   ;HLレジスタにセット
INCW    AX                      ;ポインタ更新
MOVW    RxPointer,AX            ;ポインタ保存
MOV     A,[HL]                  ;データ読み出し
MOV     C,A                     ;戻り値をセット
POP     HL
DEC     RxCount                 ;データ数を-1
BNZ     $GetEnd                 ;残りデータ有りなら分岐
PUSH    PSW
DI                              ;以下の処理は割り込み禁止
MOVW    AX,#RxBuff              ;受信バッファの初期化
MOVW    RxPointer,AX            ;データ読み出しポインタ初期化
MOVW    RxSetPointer,AX         ;受信データ格納用ポインタ初期化
POP     PSW
GetEnd:
POP     AX
RET


7. 受信バッファ設定
受信バッファをパラメータとして渡されたアドレスに変更します。


;***********************************************************************
;                                                                      *
;       受信バッファの設定                                             *
;  入力データ   :AXレジスタ(受信用バッファの先頭アドレス)         *
;  戻り値       :なし                                                 *
;  受信データを格納するバッファを指定します。また、受信データ数を0に  *
;設定します。受信完了は受信データ・バッファのステータス確認で行います。*
;       extern void _set_rxbuff(unsigned int);                         *
;                                                                      *
;***********************************************************************
;
PUBLIC  __set_rxbuff,SET_RXBUFF
__set_rxbuff:
SET_RXBUFF:
PUSH    PSW
DI
MOVW    RxPointer,AX                    ;リード・ポインタをセット
MOVW    RxSetPointer,AX                 ;セット・ポインタをセット
MOV     RxCount,#0                      ;データ数をクリア
POP     PSW
RET


8. データ領域の定義
内部で使用している変数やポインタ及びバッファは以下のように定義してあります。これらを直接操作することはありませんが、作業用でこれらの領域を確保しています。そのため、メモリの使用量には注意が必要です。

;
;       データ・ポインタ領域
;
DSEG    saddrp
TxPointer:
DS      2       ;送信用データ読み出しポインタ
SetPointer:
DS      2       ;送信データ設定用ポインタ
RxPointer:
DS      2       ;受信データ読み出しポインタ
RxSetPointer:
DS      2       ;受信データ格納用ポインタ

DSEG    saddr
;
;       データ・カウンタ領域
;
TxCount:
DS      1       ;送信データ数カウンタ
RxCount:
DS      1       ;受信データ数カウンタ
BitCount:
DS      1       ;ビット数カウント用

;
;       データ領域
;
TxBuff:
DS      16      ;送信データ・バッファ領域
RxBuff:
DS      16      ;受信データ・バッファ領域
SIO:
DS      1       ;受信作業用

;
;       フラグ領域
;
BSEG
TxFlag          DBIT            ;送信要求フラグ


Ⅳ プログラムの使用方法
で説明した周辺プログラムを介して通信を行います。この際にサブルーチン群はPUBLIC宣言されているので、以下のようにEXTRN宣言することで、リンクします。


EXTRN           INIT_BUFF                       ;バッファ初期化
EXTRN           CHECK_TXBUFF                    ;送信バッファ確認
EXTRN           CHECK_RXBUFF                    ;受信バッファ確認
EXTRN           ADD_DATA                        ;送信データをバッファに
EXTRN           SET_DATA                        ;送信バッファを設定
EXTRN           GET_DATA                        ;受信データ読み出し
EXTRN           SET_RXBUFF                      ;受信バッファ設定


1. 1バイト送信の使用例
(1)プログラム例
ここでは、1バイトのデータを送信する例を示します。最初にサブルーチンINIT_BUFFを用いて、バッファ領域の初期化を行います。その後に、サブルーチンADD_DATAを用いてデータ(55H)をバッファにセットします。これで送信準備完了したので、割り込みを許可してINTP0割り込み待ちにします。送信完了はサブルーチンCHECK_TXBUFFを用いて確認します。

;
;       送信例1
;
;  標準の16バイトのバッファを使用して1バイト単位で送信
;
CALL            !INIT_BUFF              ;バッファ初期化
MOV             X,#055H
CALL            !ADD_DATA               ;送信データをセット
EI
TXLOOP1:
CALL            !CHECK_TXBUFF           ;送信バッファを確認
BC              $TXLOOP1                ;バッファが空になるまで待つ


(2)SM+での1バイト送信時のタイミング検証
実際にプログラムを動作させて、タイミングの確認を行ってみます。外部からの信号をシミュレートするために「信号データエディタ」を利用して、CS信号などの入力信号のタイミングを定義します。また、時間の単位としては、μ秒を選択します。リセット解除後からのデータを準備するのは大変なので、上記プログラムのEI命令にブレーク・ポイントを設定して、プログラムの実行をここで止めた状態で信号データエディタからの入力を開始するようにします。このときのデータの頭の部分を以下に示します。CS信号を入力してから(下記の図の)8μ秒後にSCK信号を立ち下げて通信を開始し、以降は3μ秒毎にSCK信号を8回反転させます()。これは周期が6μ秒、つまり、170kbps弱に該当します。その後にCS信号を立ち上げています()。



このときの信号波形を以下に示します。ここで、CS信号(一番上)が立ち下がると、SO信号(下から2つ目)はHi-Z状態(赤い表示)からハイ・レベル出力に推移しますが、BUSY信号(一番下)は抵抗でプルアップされているので、最初からハイ・レベルになっています。BUSY信号が立ち下がってから、SCK信号が立ち下がります()。それを確認して、BUSY信号が立ち上がると同時に送信データ55HのMSBが0であることから、SO信号が立ち下がります()。この波形で一番タイミング的に苦しいところが3ビット目になります()。この部分の遅れが21クロックで、約2.6μ秒となります。8ビットのデータの送信が完了するとCS信号が立ち上がり、それを受けて、SO信号がHi-Z状態になります()。



2. 複数バイト送信の使用例
(1)プログラム例
この場合の例は、以下に示すROM(CSEG)領域にDB疑似命令で定義したデータを送信するものです。このデータが格納された領域そのものを、送信バッファとして設定することで、このプログラム例では、余分なデータのコピー作業を省略しています。


CSEG
TXDATA:
DB              0AAH,0CCH,33H,00,0FFH,01,02,03


実際のプログラムは送信したいデータ数(この例では8バイト)をXレジスタに設定してPUSH命令でスタックにセットします。その後に、送信したいデータの先頭アドレス(この例ではTXDATA)をAXレジスタにセットして、サブルーチンSET_DATAを呼びます。それで、送信に必要な設定が完了するので、EI命令により割り込みを許可し、INTP0割り込み待ちします。送信データの設定が完了したら、サブルーチンCHECK_TXBUFFを用いて送信完了を待ちます。

;
;       送信例2
;
;  データ・テーブルTXDATAから8バイトを送信
;
DI
MOV             X,#8                    ;送信データ数は8キャラクタ
PUSH            AX                      ;スタックにセット
MOVW            AX,#TXDATA              ;バッファの先頭アドレス設定
CALL            !SET_DATA
POP             AX                      ;スタックを元に戻す
EI
TXLOOP2:
CALL            !CHECK_TXBUFF           ;送信バッファを確認
BC              $TXLOOP2                ;バッファが空になるまで待つ

 

注意
サブルーチンSET_DATAは排他制御のために割り込みの禁止を制御しているので、このようにメイン処理での制御は必要ないのですが、ここでは分かりやすくするためにメイン処理でも割り込みの制御を行っています。
全体の動作としては、上位側で割り込み制御した方が、EIDIだけで済むので処理は単純になります。


(2)SM+での複数(8)バイト送信時のタイミング検証
複数バイト送信を確認するためのタイミング・データは以下のようになります。3μs毎にSCK信号をロウ/ハイと切り替え、これを8回繰り返すことで、1バイト(1キャラクタ)の転送タイミング(SCK信号)を生成しています。1キャラクタの最後のSCK信号と次のキャラクタのSCK信号の間に6μsの間隔を設定して、8キャラクタ分の信号を生成します。



このときの信号波形は以下のようになります。指定されたデータが順にSO信号として出力されているのが分かります。また、BUSY信号も1キャラクタ毎にハイ/ロウが変化しているのがわかります。



3. 1バイト受信の使用例1
このプログラム例は、受信ステータスを確認して受信データが存在したならば、バッファからそれを読み出すプログラムです。受信データが03(ETXのコー ド)になるまで受信を継続するものです。なお、この受信処理はかなりの処理時間が必要になるため、1キャラクタのデータをバッファから読み出している間に 複数のデータが割り込み処理により受信バッファに格納されてしまいます。

(1)プログラム例

;
; 受信例1
;
;  標準のバッファに受信する。受信したデータがETX(03)なら
;終了する。
;
EI
RXLOOP1:
CALL            !CHECK_RXBUFF           ;受信ステータス確認
BC              $RXLOOP1                ;受信バッファ空ならループ
CALL            !GET_DATA               ;受信データ読み出し
MOV             A,#03                   ;ETX
CMP             A,C                     ;受信データを比較
BNZ             $RXLOOP1                ;ETXを受信するまで継続
DI


(2)SM+での受信時のタイミング検証
「信号データエディタ」の入力パターンを以下に示します。ここでは、8ビットのデータ(0FFH)を入力してからCS信号を立ち上げています(38行目の黄色で示された部分まで実行)。以降は01010101(55H)、10101010(AAH)、11001100(CCH)、00000011(03)をSI信号に入力しています。



このときのプログラムは以下に示すように受信バッファを確認して、その段階では受信が完了していなかったためにループしたところで、ブレークした状態で す。実際にはタイミング・チャートに示すように、既に受信は完了しているので、2回目のチェックでは、今度はループから抜けることになります。



このときの変数を確認すると、RXBUFFに0FFHが入っています(赤い丸で示す)。また、変数RXCOUNTのデータ数が1(青い丸で示す)になっています。



処理を継続して、受信データを読み出した段階まで実行させたのが以下の図の状態です。



この状態では、プログラムは最初のデータ(FFH)を読み出しただけで、その値がCレジスタに格納されています(赤丸で示す)が、受信バッファには5バイト(FFH、55H、AAH、CCH、03)のデータが格納されています(青丸で示す)。受信バッファには5バイトのデータが格納されていますが、既にプログラムで最初の受信データは読み出されているので、バッファに残ったデータ数は4(緑丸で示す)になっています。赤い下線部が読み出し用のポインタで、2バイト目のデータ(55H)が格納されたFE9CH番地を示しています。青い下線部が次の受信データを格納するアドレスを示しています。



ここまでの、信号波形を以下に示します。



このように、受信処理そのものは割り込みで処理され、受信したデータはバッファに格納されていくので、受信バッファが溢れない限りは、受信処理を意識する必要はありません。

4. 1バイト受信の使用例2
次の使用例は、サブルーチンGET_DATAだけで処理を行う例です。アセンブラ記述ではサブルーチンの戻り値としてCYフラグ(受信データの有無)とCレジスタの両方を利用できるので、このような使い方も可能です。
(1)プログラム例


EI
RXLOOP2:
CALL            !GET_DATA               ;受信データ読み出し
BC              $RXLOOP2                ;受信バッファ空ならループ
MOV             A,#03                   ;ETX
CMP             A,C                     ;受信データを比較
BNZ             $RXLOOP2                ;ETXを受信するまで継続
DI


(2)SM+での検証
上記のプログラムでサブルーチンGET_DATAを1回実行した後で以下のようにSI信号にデータを入力しています。03を受信した後の部分は、CS信号が切れているので、SCK信号の変化は無視されます。



プログラムが最初の受信バッファから受信データ55Hを読み出したときの状態を示します。



5. ベクタ・テーブル等の設定
このプログラムを使用するために、以下のようにベクタ・テーブルやスタック領域を確保しておきます。


RSTVECT CSEG    AT 0
DW              START
ORG     0AH
DW              __INTP1
STKADR  DSEG    IHRAM
DS              32                      ; スタック領域(32バイト)
STACK:


Ⅴ C言語プログラムでの使用方法
ここでは、C言語のプログラムからで説明した周辺プログラムを介して通信を行います。CC78K0Sでは最初にpragma指令により以下のように宣言を行います。


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


次にサブルーチン群(関数)はPUBLIC宣言されているので、以下のようにプロトタイプ宣言にextern宣言することで、リンクします。


/*--------------------------------------------------------------*/
/*                                                              */
/*                                                              */
/*      使用する関数の定義                                      */
/*                                                              */
/*                                                              */
/*--------------------------------------------------------------*/

/*      バッファ初期化処理関数          */

extern  void    _init_buff(void);

/*      送信ステータス確認関数          */

extern  unsigned char   _check_txbuff(void);

/*      受信ステータス確認関数          */

extern  unsigned char   _check_rxbuff(void);

/*      1バイト送信関数                 */

extern  void    _add_data(unsigned char data);

/*      ブロック送信関数                */

extern void _set_data(unsigned char *data,unsigned char size);

/*      1バイト受信関数                 */

extern unsigned char    _get_data(void);

/*      受信バッファ設定関数            */

extern void _set_rxbuff(unsigned char *data);


1. 割り込みベクタの設定
CC78K0Sの場合には、スタックはスタートアップ・ルーチンが自動的に設定するので、アセンブラ記述の割り込みを使用するには、ベクタを設定するだけです。ここでは、簡単のために、以下のようなアセンブラ記述のプログラムをリンクしておきます。

;
;       割り込みベクター・テーブル定義
;
EXTRN           __INTP1                         ;割り込み処理部

RSTVECT CSEG    AT 0AH
DW              __INTP1

END


2. 1バイト送信の使用例
使用するにはまず、_init_buff()関数を呼び出して、バッファの初期化や割り込み関係の設定を行います。


main(void)
{
unsigned char   RxData;

_init_buff(); /* バッファの初期化             */

RxData = 0;             /* 受信データ初期化             */

/****************************************************************

1バイトのデータ送信

****************************************************************/

_add_data(0x55); /* 1バイトのデータ(55H)送信     */
EI(); /* 割り込み許可                 */
/*
送信完了確認
*/

while(_check_txbuff() != 0){
NOP();
}
DI();



初期化が完了したら、送信データを引数として、_add_data()関数を呼び出します。これで、送信バッファにデータがセットされるので、後は割り込みを許可して転送可能にします。転送が完了したかは_check_txbuff()関数の戻り値(=残り送信データ数)が0になるまでwhile文で待ちます。while文を抜けたら、送信は完了です。

3. 複数バイト送信の使用例
送信データを設定する関数が異なるだけで、全体的な処理の流れは1バイトの送信と同じです。使用する関数は_set_data()になります。この関数では、第1引数で送信データの格納された配列のアドレス、第2引数でデータ数を示します。


/****************************************************************

ブロック(8バイト)データ送信

****************************************************************/

_set_data(TxData1,8); /* 送信データのセット           */
EI();

/*
送信完了確認
*/

while(_check_txbuff() != 0){
NOP();
}

DI();


このプログラムでは、以下のように定義した定数の配列TxData1の8バイトのデータを送信します。


const   unsigned char   TxData1[8] =
{0xAA,0xCC,0x33,0x00,0xFF,01,02,03};


4. 1バイト受信の使用例
受信処理で、データの受信そのものは割り込みで処理されるので、前もって受信バッファに格納されています。そのため、プログラムとしては受信バッファの制御を行うだけになります。具体的な処理は、_check_rxbuff()関数を用いて受信バッファのデータ数をチェックし、データがあればそのデータを_get_data()関数で読み出します。


/****************************************************************

データ受信

****************************************************************/

EI();

while(RxData != 03){
while(_check_rxbuff() == 0){
NOP();
}
RxData = _get_data();
}


このプログラム例では、受信データが03(ETX)になるまで、while文で待つような処理にしてあります。

適用製品

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