USART (Universal Synchronous Asynchronous Receiver Transmitter) は次のモードで構成できます。
USART による通信は TX (送信)、RX (受信) ピンを使用します(TX を出力ピン、 RX を受信ピンに設定します)。 TRISC<6> が RC6/TX/CK、TRISC<7> が RC7/RX/DT の入出力設定ビットなので、これらの値を次のようにセットします。
INIT_TX_TR
BSF STATUS, RP0 ; set bank1
BCF TRISC, 6 ; PortC<6> (TX) is an output
BSF TRISC, 7 ; PortC<7> (RX) is an input
BCF STATUS, RP0 ; set bank0
TRISC<6> をクリアして、 TRISC<7> をセットするだけですね。
TXSTA (98h(Bank1)) レジスタは、送信ステータスおよびコントロールを設定するためのレジスタです。 このレジスタの構造は次のようになっています。
CSRC (7) |
TX9 (6) |
TXEN (5) |
SYNC (4) |
─ (3) |
BRGH (2) |
TRMT (1) |
TX9D (0) |
TXSTA レジスタは Bank1 にあるので注意してください(普通は Bank1 にある TRISC の設定といっしょにやってしまいます)。 例えば、送信イネーブル、非同期モードにするなら次のようにします。
BSF STATUS, RP0 ; set bank1
MOVLW B'00100000' ; Async mode
MOVWF TXSTA ;
BCF STATUS, RP0 ; set bank0
他の設定にしたい時は、B'00100000
** の部分を変更します。
RCSTA レジスタ (18h(Bank0)) は受信ステータスおよびコントロールを設定するレジスタです。 RCSTA の構造は次のようになっています。
SPEN (7) |
RX9 (6) |
SREN (5) |
CREN (4) |
ADDEN (3) |
FERR (2) |
OERR (1) |
RX9D (0) |
RCSTA<7> (SPEN) を 1 にセットすることで、TX, RX ポートをシリアルポートピンとして使用できるようになります。 RCSTA レジスタは Bank0(18h) にあるので、現在 Bank0 ならばバンクを切り替える必要はありません。
MOVLW B'10010000' ; Serial port is enable
MOVWF RCSTA
他の設定にしたい場合は、B'10010000'
の部分を変更します。
調歩同期方式(非同期モード)を選択した場合は、ボーレートを決定するために SPBRG (99h(Bank1)) レジスタを設定する必要があります。 SPBRG レジスタは、希望のボーレートによって 0~255 の値を設定します。 希望のボーレートとクロック周波数 (Hz) が決まれば、以下の式から SPBRG にセットすべき値 (X) を求めることができます(データシートから抜粋)。 X を求める式は SYNK ビットと BRGH ビットにより変化します。
TXSTA<BRGH(2)> = 0 (低速) | TXSTA<BRGH(2)> = 1 (高速) | |
TXSTA<SYNC(4)> = 0 (非同期) | ボーレート = クロック / (64 (X + 1)) | ボーレート = クロック / (16 (X + 1)) |
TXSTA<SYNC(4)> = 1 (同期) | ボーレート = クロック / (4 (X + 1)) | NA |
これを X = の式に変換すると次のようになります。
TXSTA<BRGH(2)> = 0 (低速) | TXSTA<BRGH(2)> = 1 (高速) | |
TXSTA<SYNC(4)> = 0 (非同期) | X = (クロック / (ボーレート * 64)) - 1 | X = (クロック / (ボーレート * 16)) - 1 |
TXSTA<SYNC(4)> = 1 (同期) | X = (クロック / (ボーレート * 4)) - 1 | NA |
ちなみに PIC16F873 (28ピン) と PIC16F874 (40ピン) は BRGH は低速しか使えないらしいです。 高速に設定したい場合は、PIC16F876 (28ピン) とか PIC16F877 (40ピン) を使います。
例として、10MHz、非同期モード (SYNC=0)、低速ボーレート (BRGH=0) の時に 9600bps 出したい時の X の値を求めてみます。
X = (10000000 / (9600 * 64)) - 1
= 15.276・・・
≒ 15
つまり、この場合は SPBRG に 15 (0x0F) をセットすればよいことになります。 SPBRG レジスタは Bank1 にあるので、次のように設定します。 Bank1 にある TRISC、TXSTA レジスタといっしょに設定してしまえばよいでしょう。
BSF STATUS, RP0 ;Bank1 に切り替え
MOVLW 0x0F ;9600bps
MOVWF SPBRG ;
BCF STATUS, RP0 ;Bank0 に戻す
よく使うクロック周波数とボーレートでの SPBRG の値は次のようになります。 同じ 9600bps の場合でも、高速ボーレート (BRGH = 1) にした方がエラーレートは小さくなります(高速モードが使えるなら)。
BRGH = 0 (低速) | BRGH = 1 (高速) | |||
---|---|---|---|---|
10MHz | 20MHz | 10MHz | 20MHz | |
1200bps | 129 (0x81) | 255 (0xFF) | ─ | ─ |
2400bps | 64 (0x40) | 129 (0x81) | ─ | ─ |
9600bps | 15 (0x0F) | 32 (0x20) | 64 (0x40) | 129 (0x81) |
19200bps | 7 (0x07) | 15 (0x0F) | 32 (0x20) | 64 (0x40) |
38400bps | ─ | ─ | 15 (0x0F) | 32 (0x20) |
USART でデータを送信する時は、TXSTA レジスタの TRMT (TXSTA<1>) が 1 になるのを確認してから送信するようにします。 次のサブルーチンは、このルールに基づいて W レジスタのデータを送信します。
;==============================================================================
; シリアル送信サブルーチン
; TXSTA<1> (TRMT) を監視し、W レジスタのデータをシリアルで送信します。
; (TEMP を Bank0 の汎用レジスタに指定してあることを前提とします。
; このルーチンを呼び出すときは Bank0 に切り替えてから呼び出すこと。)
;==============================================================================
SendUsart
MOVWF TEMP ;W レジスタを保存
BSF STATUS, RP0 ;Bank1 に切り替え
SendUsartLoop
BTFSS TXSTA, TRMT ;TXSTA<1> (TRMT) が 1 になるまで待機
GOTO SendUsartLoop
BCF STATUS, RP0 ;Bank0 に戻す
MOVF TEMP, W ;W レジスタの復旧
MOVWF TXREG ;TXREG レジスタにセット (実際の送信開始)
RETURN
ここでは TXSTA レジスタの TRMT(1) ビットを監視して、TSR レジスタが空になったかどうかを調べていますが、次のように PIR1 の TXIF(4) ビットをポーリングすることでも USART によるシリアル送信処理を行うことができます。 そのようにした方が、Bank0 にあるレジスタを監視するので扱いが容易であり、送信バッファ自体の状態を見ているので若干高速です(2003-11-02 に追記。木下隆さん情報提供ありがとうございました)。
;******************************************************************************
; USART によるシリアル送信サブルーチン
; PIR1<TXIF(4)> を監視し、W レジスタのデータ (8 bit) をシリアルで送信します。
; (このルーチンを呼び出す時は Bank0 に切り替えてから呼び出すこと)
;******************************************************************************
SendUsart
btfss PIR1, TXIF ;PIR1<TXIF(4)> が 1 になるまで待機
goto SendUsart
movwf TXREG ;TXREG レジスタにセット
return
ただ、データシートを見てもよくわからないのが、USART で 9 ビット送信をイネーブルにして、TX9D(9ビット目)のデータも送信する場合に、TX9D がどのタイミングで TSR にコピーされるかということです。 TXREG に値をセットした瞬間に TX9D も TSR にコピーされるのなら、TXIF フラグが 1 になった時に TX9D をセットしてよいのですが、TX9D に値をセットした瞬間に TSR にそのビットがコピーされるとしたら、TRMT フラグが 1 になるまでは TX9D に値をセットしてはいけないことになるようなならないような(9 ビット目が上書きされそう)。 マイクロチップテクノロジーのアプリケーションノートの参考例を見ると次のように書いてあるので、まぁ大丈夫なんでしょう。
;------------------------------------------------------------------------------
;Transmit data with parity when the transmit register is empty.
;------------------------------------------------------------------------------
TransmitSerial: Bank0 ; select bank 0
btfss PIR1,TXIF ; check if transmitter busy
goto $-1 ; wait until transmitter is not busy
movf TxData,W ; get data to be transmitted
call CalcParity ; calculate parity
rrf CalcParity,W ; get parity bit in carry flag
Bank1 ; select bank 1
bcf TXSTA,TX9D ; set TX parity to zero
btfsc STATUS,C ; check if parity bit is zero
bsf TXSTA,TX9D ; if not then set TX parity to one
Bank0 ; select bank 0
movf TxData,W ; get data to transmit
movwf TXREG ; and transmit the data
return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; USART を使ってシリアルで PC に送信するサンプルプログラム
; 0x01, 0x02, 0x03 を繰り返し送信します。
;
; 通信スピード : 9600bps
; データ長 : 8bit
; ストップビット: 1bit
; フロー制御 : なし
;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
LIST P=PIC16F873
INCLUDE "P16F873.INC"
;==============================================================================
; Variable Definition
;==============================================================================
TEMP EQU 020H
ORG 0
;==============================================================================
; 初期設定
;==============================================================================
INIT
BSF STATUS, RP0 ; Bank1 に切り替え
;**** USART 用入出力ポートの設定 (TRISC) ****
BCF TRISC, 6 ; PortC<6> (TX) is an output
; BSF TRISC, 7 ; PortC<7> (RX) is an input
;**** USART 送信ステータスの設定 (TXSTA) ****
MOVLW B'00100000' ; Async mode
MOVWF TXSTA ;
;**** USART ボーレートの設定 (SPBRG) ****
MOVLW 0FH ;9600bps 10MHz BRGH=0(低速)
MOVWF SPBRG
;**** USART 受信ステータスの設定 (RCSTA) ****
BCF STATUS, RP0 ; Bank0 に戻す
MOVLW B'10010000' ;
MOVWF RCSTA ;
;==============================================================================
; メインルーチン
;==============================================================================
MAIN
MOVLW 1
CALL SEND_USART ;シリアルで W の内容を送信
MOVLW 2
CALL SEND_USART
MOVLW 3
CALL SEND_USART
GOTO MAIN
;==============================================================================
; シリアル送信サブルーチン
; TXSTA<1> (TRMT) を監視し、W レジスタのデータ (8 bit) をシリアルで送信します。
;==============================================================================
SEND_USART
MOVWF TEMP ;W レジスタを保存
BSF STATUS, RP0 ;Bank1 に切り替え
SEND_USART_LOOP
BTFSS TXSTA, TRMT ;TXSTA<1> (TRMT) が 1 になるまで待機
GOTO SEND_USART_LOOP
BCF STATUS, RP0 ;Bank0 に戻す
MOVF TEMP, W ;W レジスタの復旧
MOVWF TXREG ;TXREG レジスタにセット (実際の送信開始)
RETURN
;==============================================================================
; End Program
;==============================================================================
END
0x01 を受信したら LED On、0x02 を受信したら LED Off (PIC16F873) するサンプルコードです。
;==============================================================================
;
; シリアルポートから 9600bps で 1byte のデータを受信し、
; 受信データが 1 なら PORTB<0> (RB0) を High (LED On) にし、
; 受信データが 2 なら PORTB<0> (RB0) を Low (LED Off) にする。
;
;==============================================================================
;
; File : usart_recv.asm
; Author: Masatoshi OHTA <ohta@hakoten.com>
; Update: 2003/5/15
;
; デバイス : PIC16F873
; 通信スピード : 9600bps
; データ長 : 8bit
; ストップビット: 1bit
; フロー制御 : なし
;
;==============================================================================
LIST P=PIC16F873
INCLUDE "P16F873.INC"
__CONFIG _HS_OSC & _WDT_OFF
;ウォッチドッグ・タイマ OFF
;==============================================================================
; 変数定義
;==============================================================================
CBLOCK H'20'
RECV_BUF ; シリアルからの受信データを一時退避するバッファ
ENDC
;==============================================================================
; エントリ・ポイント
;==============================================================================
ORG 0
goto StartUp
;==============================================================================
; StartUp ルーチン
;==============================================================================
StartUp
bsf STATUS, RP0 ; Bank1 に切り替え
;[TRISB (bank1)] PORTB の入出力の設定
bcf TRISB, 0 ; PORTB<0> is an output.
;[TRISC (bank1)] USART 用入出力ポートの設定
bcf TRISC, 6 ; PortC<6> (TX) is an output.
bsf TRISC, 7 ; PortC<7> (RX) is an input.
;[TXSTA (bank1)] USART 送信ステータスの設定
movlw B'00100000' ; Async mode
movwf TXSTA ;
;[SPBRG (bank1)] USART ボーレートの設定
movlw 0FH ; 9600bps 10MHz BRGH=0(低速)
movwf SPBRG
bcf STATUS, RP0 ; Bank0 に戻す
;[RCSTA (bank0)] USART 受信ステータスの設定
movlw B'10010000' ;
movwf RCSTA ;
;******************************************************************************
; シリアルポートからの受信を監視するループ
;******************************************************************************
UsartRecvLoop
btfss PIR1, RCIF ; PIR1<5>(RCIF) がセットされたら受信完了
goto UsartRecvLoop
movf RCREG, w ; 受信したデータを w に取得
;******************************************************************************
; 受信したデータで処理を振り分け
; 1 を受け取ったら LED を On
; 2 を受け取ったら LED を Off
;******************************************************************************
movwf RECV_BUF ; 受信データ保存
sublw 1 ; 1 - w -> w
btfsc STATUS, Z ; 0 なら Case_1 へ
goto Case_1
movf RECV_BUF, w ; RECV_BFU -> w
sublw 2 ; 2 - w -> w
btfsc STATUS, Z ; 0 なら Case_2 へ
goto Case_2
goto UsartRecvLoop
Case_1
bsf PORTB, 0 ; PORTB<0> (RB0) を High に (LED On)
goto UsartRecvLoop
Case_2
bcf PORTB, 0 ; PORTB<0> (RB0) を Low に (LED Off)
goto UsartRecvLoop
;==============================================================================
; Directive 'End of Program'
;==============================================================================
END