Renesas Synergy™

FAQ 1007010 : 汎用のポートだけを用いて、UARTを実現するプログラムはないか。

ここでは、調歩同期通信で、9600~19200ボーの通信速度、データ長は8ビット、パリティはなし、ストップ・ビットは1ビットで、LSBファーストの通信に対応するものとします。また、通信は半二重で行なうものとします。非同期での受信データに対応するためにスタート・ビットの検出にINTP1割り込みを使用するものとします。
ソフトウェアによるUARTを実現する際の考え方は、「ソフトウェアによるUART機能」を参照して下さい。
タイマHも用いる場合の例は「ソフトウェアによるUARTプログラム例1」を参照して下さい。


[プログラムの概要]
このプログラム例はデータの受信の起動だけは割り込みを使用しますが、それ以外は割り込み禁止状態で動作します。処理は大きく分類して、以下の部分で構成されています。

①ボー・レート・キャリブレーション部
②INTP1によるデータ受信処理部
③受信処理起動部
④データ送信処理部

①のボー・レート・キャリブレーション部は高速内蔵発振クロックを使用する際の補正を行なうための部分です。外部クロックやセラミック発振子等を用いる場合には必要ありません。

②がスタート・ビットの立下りによるINTP1で起動される処理で、調歩同期通信の受信処理を行なう部分です。基本的に割り込み処理であるために、結果はレジスタではなく、RAMに受信したデータと結果のフラグを格納します。半二重通信であることから、常にスタート・ビットを待っている訳ではありません。必要なときに③の処理で起動する必要があります。データの受信を開始すると、以降のINTP1割り込みを含めてすべての割り込みは禁止されます。

③では受信(実際にはスタート・ビット待ち)を起動するために、必要なフラグをセットしてINTP1割り込みを許可します。ここでは、受信処理の起動だけを行なうので、実際にスタート・ビットがくるまでは送信以外の他の処理を実行可能です。

④はデータ送信処理を行ないます。送信タイミングは命令の実行時間をカウントすることで実現しているので、他の割り込みは使用できません。



[サンプリング・タイミングの発生]
このプログラムでは、受信でのサンプリング・タイミングや送信のためのタイミング生成用にソフトウェア・タイマを用いています。そのため、送受信の処理中には、処理時間に影響するような割り込みは一切受付けられません。


[ボー・レートのキャリブレーション]
高速内蔵発振器からの誤差が大きいクロックを使用することを考慮して、通信動作の前にキャリブレーションを行ないます。このための簡単な方法としては、通信相手から8ビット分の連続したロー・レベル(データとしては80H)を受信して、その時間を計測します。考え方については、「ソフトウェアによるUART機能」を参照して下さい。ここでは、INTP1割り込みで立ち下がりエッジを検出して割り込み処理に分岐し、そこでキャリブレーション・フラグがセットされていたら、RxD端子の立ち上がりまでの時間を計測します。INTP1割り込み受付けからカウント開始までの時間はカウントに用いるHLレジスタの初期値を3にすることで補正しておきます。
(データ受信もキャリブレーションも共にINTP1割り込みで起動するようにしています。そこで、どちらの処理かを判別できるようにフラグを準備しておきます。このフラグが変数TRXSTATのビット0でこれがセットされていればキャリブレーションであると定義しています。)

P1INT:
BT      RxDPIN,$P1INTEND        ;10:ノイズなら抜ける
BT      TRXSTAT.0,$CALIBSTART   ;10:キャリブレーション開始


CALIBSTART:
MOVW    HL,#3                   ; ここまでの時間補正用
MLOOP:
NOP                             ; 2:
INCW    HL                      ; 4:時間計測
BF      RxDPIN,$MLOOP           ;10:割り込み要求待ち

ここで得られた回数をクロック数(1カウント=16クロック)に変換し、測定したビット数(8)で割ることで、(最終的に2倍すると)実際のデータ送受信タイミング(間隔)を得ることができます。この時間間隔は下図では赤い矢印となります。



この時間間隔でサンプリングするには、サンプリング処理にかかる時間を引いた残りの部分をソフトウェアで計測処理すればいいことになります。実際の時間計測処理は「変数を減算してゼロになるまでループ」することになるので、以下の関係となります。

サンプリング間隔 = サンプリング処理 + 計測準備処理 + 計測ループ

計測ループはできるだけ短い方がいいのですが、処理(計算)を簡単にするために、「DBNZ saddr」命令を用います。これでループは8クロックとなります。そこで、サンプリング処理と計測準備処理の合計が8クロックの倍数になるようにしておくと、ループ回数は以下の式で求められます。

ループ回数 = サンプリング時間/8 ―(サンプリング処理+計測準備処理)/8

つまり、上記プログラムで測定した値を1/4して、上の式の第2項を引けばいいことになります。この第2項は後で説明するプログラム例では6となります。また、スタート・ビット検出のための1/2ビット時間については定数SOFFSETとして以下のように設定しています。

SOFFSET EQU     (14+20+12+10)/8 ; スタート・ビット検出補正用

上記のプログラムで得られたパルス幅(クロック数)と補正値を使って、実際にソフトタイマのループ回数を求める処理プログラム例は以下のようになります。除算や乗算のような手間のかかる処理は使わず、代わりにシフト処理で済ますようにしています。

;
;  19200~9600なら1/2した値がマージンをみて、90~255の範囲を正常と
;みなす。
;
MOVW    AX,HL           ; 計測結果をAXレジスタへ
CLR1    CY
RORC    A,1             ; 結果を1/2する
XCH     A,X
RORC    A,1
XCH     A,X
ADD     A,#0FFH         ; 上位が0?
BC      $CALEXIT        ; 遅すぎるなら抜ける
XCH     A,X
CMP     A,#90           ; 下限のチェック
BC      $CALEXIT
CALNEXT:
RORC    A,1             ; これで1/4に
MOV     CMPDATA1,A      ; CMP01設定値を保存領域に
SUB     CMPDATA1,#6     ; 送受信の48クロック分を補正
RORC    A,1
SUB     A,#SOFFSET      ; スタート・ビット処理補正
MOV     CMPDATA0,A      ; スタート・ビット処理用に
CALEXIT:
MOV     TRXSTAT,#0      ; キャリブレーション済に
RETI

このようにして得られたソフトタイマのループ回数が変数CMPDATA1(ビット間隔計測用)と変数CMPDATA0(スタート・ビット検出用)に格納されます。8MHzの外部クロックやセラミック発振子を用いて、9600ボーでの通信を行なう際にはCMPDATA1は98、CMPDATA0は45となります。


[受信割り込み処理]
実際の受信は、スタート・ビットの先頭による、INTP1のエッジ検出割り込みで起動します。割り込みが受付けられたら、信号を確認し、ロー・レベルになっているかを確認します。その後、キャリブレーションかどうかを判断して、受信処理の場合ではスタート・ビットの中央を待ちます(下記のプログラムで、コメント欄の頭に記載した数値が命令の処理クロック数です)。スタート・ビットの中央が確認できたら、データ受信のための準備をします。

P1INT:
BT      RxDPIN,$P1INTEND        ;10:ノイズなら抜ける
BT      TRXSTAT.0,$CALIBSTART ;10:キャリブレーション開始
PUSH    AX                      ; 4:レジスタをセーブ
MOV     A,CMPDATA0              ; 4:設定値の読み出し
MOV     TIMECOUNT,A             ; 4:ビット中央までをセット
STBLOOP:
DBNZ    TIMECOUNT,$STBLOOP      ; 8:スタート・ビット中央待ち
BT      RxDPIN,$NOTSTART        ;10:スタート・ビット未検出なら抜ける
;
;  スタート・ビット検出で受信処理をスタート
;
NOP                             ; 2:タイミング調整用
MOV     A,#10000000b            ; 6:初期データをセット

スタート・ビットの検出ができなかったら、なにもしないで割り込み処理から抜けます。

NOTSTART:
POP     AX
RETI

スタート・ビットを検出したら、引き続いて、データ・ビットの受信処理となります。


[データ受信処理]
データの受信は、ビット単位での受信のための処理をサブルーチン化し、それを用いて処理しています。サブルーチン化したのは、メインの処理を見やすくするためと、データにより処理時間が変動するのを避けるためです。ビット受信の具体的なプログラムを以下に示します。

RXBIT:
NOP ; 2:送信処理との時間調整用
MOV     TIMECOUNT,A ; 4:データをセーブ
MOV     A,CMPDATA1 ; 4:受信時間カウント値をゲット
XCH     A,TIMECOUNT ; 6:カウント値セット&データ復帰
BITLOOPR:
DBNZ    TIMECOUNT,$BITLOOPR ; 8*n:時間待ち
BF      RxDPIN,$RXDLOW ;10: データ確認
SET1    CY ; 2:
RET ; 6:
RXDLOW:
CLR1    CY ; 2:
RET ; 6:

ここで、最初のNOP命令は受信でのサンプリング処理と送信処理とで処理時間を合わせる(つまり、ループ回数を同じにする)ためのものです。オレンジの部分が時間待ちのための準備で、薄いオレンジ部分が時間調整用のループです。赤い部分が実際にデータを確認して、結果をキャリ・フラグに反映している部分です。このようにサブルーチン化してRET命令で抜けることで、データの0/1で処理時間が変わらないようにしています。
このビット受信処理をつかって、以下のプログラムのように処理することで、8ビットのデータ及びストップ・ビットを受信します。このプログラムで赤い部分が時間の制限がきつい部分です。

RXDATALOOP:
CALL    !RXBIT ; 6:ビット受信
RORC    A,1 ; 2:新しいビットを左から取り込む
BNC     $RXDATALOOP ; 6:8ビット分繰り返す
CALL    !RXBIT ; 6:ストップ・ビット受信
MOV     RXDATA,A                ; 4:受信データを保存
NOT1    CY                      ; 2:CY:フレーミング・エラー
MOV     A,#01000000b
ROLC    A,1                     ; 結果をエラーにセット
MOV     RXSFLAG,A               ; フラグにセット
MOV     TRXSTAT,#0              ; 受信中フラグをクリア
NOTSTART:
POP     AX
P1INTEND:
DUMMY:
RETI

受信したデータは変数RXDATAに格納し、受信ステータスを変数RXSFLAGにセットし、変数TRXSTATの受信中フラグをクリアして処理を終了します。


[受信起動処理]
受信処理そのものはこれまで説明したように、INTP1のエッジ検出割り込みで起動されますが、通常はINTP1のエッジ検出割り込みは禁止状態になっています。調歩同期の受信を開始するときに割り込みを許可したり、必要なフラグをセットしたりするのが受信起動処理です。このプログラムは以下のようになります。これは、単に受信処理を起動するだけですので、受信が完了したかはRXSFLAGのビット7がセットされたかで確認します。

RXSTART:
MOV     RXSFLAG,#0              ; エラー・フラグをクリア
MOV     TRXSTAT,#SRXMODE        ; 受信起動をセット
CLR1    PIF1                    ; 割り込みをクリア
CLR1    PMK1                    ; マスク解除
EI                              ; RxD立下り待ち
RET


[受信処理サブルーチン]
簡単に使うために以下のような、受信起動から受信完了までを処理するサブルーチンを準備しています。

UARTRX:
CALL    !RXSTART                ; 受信起動
RXLOOP0:
MOVW    AX,RXDATA               ; 受信ステータスを読み出す
ROL     A,1                     ;
BNC     $RXLOOP0                ; 完了まで待つ
AND     A,#00000010b            ; 完了フラグをマスク
XCH     A,X
DI
RET

ここで、作業に使用する変数は以下のように定義されているものとします。特に、RADATARXSFLAGは16ビットでアクセスできるようにしています。saddrp属性でRAMに配置しています。

DSEG    saddrp ; 16ビットアクセスのため
RXDATA:
DS      1               ; 受信データ。ステータスとペア
RXSFLAG:
DS      1               ; 受信ステータス・フラグ
;
;   76543210
;   |      +----: フレーミング・エラー
;   +-----------: 受信完了
;
DSEG    saddr
TRXSTAT:
DS      1               ; 受信中ステータス
;
;  受信動作状態を示すステータス・フラグ
;  76543210
;  x0000000
;  |      +--------: 1:未キャリブレーション
;  |
;  +---------------: 1:受信中
;

なお、このサブルーチンは下記のプログラムで示すように、CALLT 命令のテーブルにRXDATASUBの名前で登録してあります。

;
;  サブルーチンをCALLTで使用するためのテーブル領域
;
TABLESUB        CSEG    CALLT0
TXDATASUB:
DW      TXDATA          ; バイト送信
RXDATASUB:
DW      UARTRX          ; バイト受信

そこで、CALLT [RXDATASUB]と記述することで、1バイトのコール命令で使用することができます。受信したデータはAレジスタに格納され、Xレジスタ及びゼロ・フラグにエラー・ステータスが入ります。


[データ送信処理]
基本的に半二重通信しかできないので、処理が終わるまでは割り込み禁止にしています。
送信を開始(TXSTARTサブルーチンで処理ビット長を10に設定、TxD端子をロー・レベルにしてスタート・ビットを送信開始)します。ここまでが下記のプログラムの赤字で示した処理です。その後、次のビットを送信するために1ビット送信サブルーチンを呼び出して、送信中のビットの転送完了をソフトウェア・タイマで待ちます。ストップ・ビットの送信まで完了すると、処理を完了します。

TXDATA: PUSH    PSW
PUSH    BC
DI ; 割り込みを禁止しておく
CALL    !TXSTART ; 送信開始
TXDATALOOP:
CALL    !TXBIT                  ; 6: 1ビット送信
DBNZ    B,$TXDATALOOP           ; 6:出力ビット数をカウント
POP     BC
POP     PSW
RET

ここで呼び出している1ビット送信サブルーチンは下記のプログラムに示すように、送信すべきビット(AレジスタのLSB)をキャリーにセットした状態で前のビットの送信完了をソフトウェア・タイマにより待ちます。指定された時間が経過したら、キャリーをチェックし、TxD端子をビット操作してデータを出力します。
このプログラムでは、キャリーをセットした状態でキャリーを含めてデータを右回転させることで、送信したいビットをキャリーに転送すると同時にデータには1を埋めるようになっています。そのため、データを送信した後にこのサブルーチンをコールすれば、ストップ・ビットを送信したり、ストップ・ビットの送信完了を待つことができます。

TXBIT:
MOV     TIMECOUNT,A             ; 4:データをセーブ
MOV     A,CMPDATA1              ; 4:送信時間カウント値をゲット
XCH     A,TIMECOUNT             ; 6:データを
SET1    CY ; 2:出力後にデータを1にする
RORC    A,1                     ; 2:データをキャリーに
BITLOOPT:
DBNZ    TIMECOUNT,$BITLOOPT     ; 8*n:時間待ち
BC      $SEND1DATA              ; 6:キャリーの値により
CLR1    TxDPIN                  ; 6:TxD端子をクリアまたは
RET                             ; 6:
SEND1DATA:
SET1    TxDPIN                  ; 6:セットする
TXNEXT:
RET                             ; 6:


[送信起動処理]
送信起動処理はデータ送信処理から呼び出されるサブルーチンで、データ送信で必要なパラメータ(送信処理を行なうスタート・ビットからストップ・ビットまでのビット数)の設定を行ない、スタート・ビットの送信を開始してもどります。ビット送信処理と処理時間の整合をとるために、サブルーチン化しています。

TXSTART:
MOV     B,#BLENGTH+2 ; ビット長を作業用に設定
CLR1    TxDPIN                  ; スタート・ビット送信
RET                             ; 6:


[通信を行なうための準備]
以上のプログラムを使用するために、以下のように定数を定義します。ここでは、78K0S/KU1+や78K0S/KY1+を高速内蔵発振クロックで使う場合にあわせた定義になっています。

RxDPIN  EQU     P3.2
RxDMODE EQU     PM3.2
RxDPUP  EQU     PU3.2
TxDMODE EQU     PM4.0
TxDPIN  EQU     P4.0
INTP1FALL       EQU     00000000b
BLENGTH EQU     8               ; ビット長
SOFFSET EQU     (14+20+12+10)/8 ; スタート・ビット検出補正用
B9600   EQU     104-6           ; 9600ボーのカウント値
HB9600  EQU     52-SOFFSET      ; 9600ボーでの1/2ビット時間

また、リセットや割り込みのベクタ・テーブルは以下のように定義しています。基本的にはINTP1だけベクタ割り込みを使用し、INTTMH1はスタンバイの解除にだけ使用します。

RSTVECT CSEG    AT      0
DW      START           ;00:リセットスタート処理のベクタ
DW      DUMMY           ;02:
DW      DUMMY           ;04:
DW      DUMMY           ;06:LVI
DW      DUMMY           ;08:INTP0
DW      P1INT           ;0A:INTP1
DW      DUMMY           ;0C:INTTMH1
DW      DUMMY           ;0E:INTTM000
DW      DUMMY           ;10:INTTM010
DW      DUMMY           ;12:INTAD

使用するRAM領域(タイミング・カウント関係)は以下のような定義になっています。

DSEG    saddr
TXBCOUNT:       DS      1               ; 送信用ビット・カウンタ
CMPDATA0:       DS      1               ; スタート・ビット確認用
CMPDATA1:       DS      1               ; ビット間隔用設定値

端子等は以下の例のように初期化します。端子としては、TxD端子をハイレベルにして出力に設定し、RxD端子では内蔵プル・アップ抵抗を有効に設定(青字の部分)します。また、タイマHに設定する時間データの初期値を変数に設定(緑字の部分)します。
高速内蔵発振器を使用する場合には、キャリブレーションが必要なので、赤字のように処理を呼び出しておきます。

MOV     CMPDATA1,#B9600 ; タイマの初期値設定
MOV     CMPDATA0,#HB9600
SET1    RxDPUP
SET1    TxDPIN ; TxDをハイに設定しておく
CLR1    TxDMODE ; TxD端子を出力に
CALL    !CAL            ; キャリブレーション

実際のキャリブレーションは既に説明したように、INTP1割り込みにより起動しますが、INTP1割り込みを有効にするための処理が必要です。そのための処理を行なうのがサブルーチンCALです。具体的なプログラム例を以下に示します。処理内容は、変数TRXSTATにキャリブレーション・モードを示すフラグをセットして、INTP1割り込みを許可します。これで、キャリブレーション用のデータ(80H)を待ちます。キャリブレーションが完了すると、変数TRXSTATのキャリブレーション・モードを示すフラグがクリアされるので、それを待ちます。

CAL:
MOV     IF0,#0          ; 割り込み要求をクリア
MOV     TRXSTAT,#00000001b
MOV     MK0,#11110111b  ; INTP1のマスク解除
EI
CALLOOP:
NOP
BT      TRXSTAT.0,$CALLOOP      ; キャリブレーション完了待ち
RET


[使用方法]
①UART機能を使用するための初期化処理の例です。この例では、パラメータ領域に8MHzクロックを用いた場合の9600ボー用の初期値を設定し、ポート関係の初期化を行なっています。その後キャリブレーション処理を行ないます。

MOV     CMPDATA1,#B9600 ; タイマの初期値設定
MOV     CMPDATA0,#HB9600
SET1    RxDPUP
SET1    TxDPIN          ; TxDをハイに設定しておく
CLR1    TxDMODE         ; TxD端子を出力に
CALL    !CAL            ; キャリブレーション

②8キャラクタのデータを受信するための例を以下に示します。ここではHLレジスタをデータ格納用のRAMのポインタとして使用し、Bレジスタでデータ数を指定します。

DSEG
;
;  データ・バッファ
;
RXDATABUFF:     DS      8               ; 受信用のデータ・バッファ

CSEG
MOVW    HL,#RXDATABUFF
MOV     B,#8
RXLOOP:
CALLT   [RXDATASUB]     ; データ受信
MOV     [HL],A          ; 受信データをバッファへ
INC     L               ; 次のデータへ
DBNZ    B,$RXLOOP       ; 8回繰り返す

③同様に送信は以下のようになります。

DSEG
;
;  データ・バッファ
;
TXDATABUFF:     DS      8               ; 送信用のデータ・バッファ

CSEG
MOVW    HL,#TXDATABUFF
MOV     B,#8
TXLOOP:
MOV     A,[HL]          ; 送信データの読み出し
CALLT   [TXDATASUB]     ; データ送信
INC     L               ; 次のデータへ
DBNZ    B,$TXLOOP       ; 8回繰り返す

適用製品

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