USART 用の送受信ピン TRISC<6, 7> の設定
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 (送信ステータスおよびコントロールレジスタ) の設定
TXSTA (98h(Bank1)) レジスタは、送信ステータスおよびコントロールを設定するためのレジスタです。 このレジスタの構造は次のようになっています。
CSRC (7) | TX9 (6) | TXEN (5) | SYNC (4) | ─ (3) | BRGH (2) | TRMT (1) | TX9D (0) |
- CSRC (7): クロックソースセレクトビット
- 非同期モード
- Don’t care
- 同期モード
- 1 = マスターモード(内部でBRG から発生するクロック)
- 0 = スレーブモード(外部ソースからのクロック)
- 非同期モード
- TX9 (6): 9 ビット送信イネーブルビット
このビットをセットすると、TXSTA<TX9D(0)> にセットしたビットも同時に送信されるようになります。- 1 = 9 ビット送信を選択
- 0 = 8 ビット送信を選択
- TXEN (5): 送信イネーブルビット
注意: 同期モードではSREN/CREN はTXEN をオーバーライドします。- 1 = 送信はイネーブル
- 0 = 送信はディセーブル
- SYNC (4): USART モードセレクトビット
- 1 = 同期モード
- 0 = 非同期モード
- なし (3): 未使用
このビットを読み出すと、常に「0」とリードされます。 - BRGH (2): 高速ボーレートセレクトビット
- 非同期モード
- 1 = 高速
- 0 = 低速
- 同期モード(使用しません)
- 非同期モード
- TRMT (1): 送信シフトレジスタステータスビット
- 1 = TSR が空
- 0 = TSR がフル
- TX9D (0): 送信データの第 9ビット。パリティビットとして使用可能
TXSTA レジスタは Bank1 にあるので注意してください(普通は Bank1 にある TRISC の設定といっしょにやってしまいます)。 例えば、送信イネーブル、非同期モードにするなら次のようにします。
BSF STATUS, RP0 ; set bank1
MOVLW B'00100000' ; Async mode
MOVWF TXSTA ;
BCF STATUS, RP0 ; set bank0
他の設定にしたい時は、B'00100000
の部分を変更します。
RCSTA(受信ステータスおよびコントロールレジスタ)の設定
RCSTA レジスタ (18h(Bank0)) は受信ステータスおよびコントロールを設定するレジスタです。 RCSTA の構造は次のようになっています。
SPEN (7) | RX9 (6) | SREN (5) | CREN (4) | ADDEn (3) | FERR (2) | OERR (1) | RX9D (0) |
- SPEN (7): シリアルポートイネーブルビット
- 1 = シリアルポートはイネーブル(RC7/RX/DT およびRC6/TX/CK ピンはシリアルポートピンとして構成)
- 0 = シリアルポートはディセーブル
- RX9 (6): 9 ビット受信イネーブルビット
- 1 = 9 ビット受信を選択
- 0 = 8 ビット受信を選択
- SREN (5): シングル受信イネーブルビット
- 非同期モード (Don’t care)
- 同期モード-マスター(このビットは受信完了後クリアされます)
- 1 = シングル受信はイネーブル
- 0 = シングル受信はディセーブル
- 同期モード-スレーブ(使用しません)
- CREN (4): 連続受信イネーブルビット
- 非同期モード
- 1 = 連続受信はイネーブル
- 0 = 連続受信はディセーブル
- 同期モード
- 1 = イネーブルビットCREN がクリアされるまでは連続受信可能(CREN はSREN をオーバーライドする)
- 0 = 連続受信はディセーブル
- 非同期モード
- ADDEN (3): アドレス検出イネーブルビット
- 非同期モード9 ビット(RX9=1)
- 1 = アドレス検出はイネーブル、RSR<8> が 1 のとき、受信バッファの割り込みとロードがイネーブル
- 0 = アドレス検出はディセーブル、全バイトを受信、第 9 ビットはパリティビットとして使用可能
- 非同期モード9 ビット(RX9=1)
- FERR (2): フレーミングエラービット
- 1 = フレーミングエラー(RCREG レジスタのリードにより更新でき、次の有効バイトを受信する)
- 0 = フレーミングエラーなし
- OERR (1): オーバーランエラービット
- 1 = オーバーランエラー(ビット CREN のクリアによりクリア可能)
- 0 = オーバーランエラーなし
- RX9D (0): 受信データの第 9 ビット(パリティビットとして使用可能)
- RCSTA<RX9 (6)> をセットした場合、このビットに第 9 ビットが受信されます。送信側と受信側がこのことを認識してプログラムを作成する必要があります。
RCSTA<7> (SPEN) を 1 にセットすることで、TX, RX ポートをシリアルポートピンとして使用できるようになります。 RCSTA レジスタは Bank0(18h) にあるので、現在 Bank0 ならばバンクを切り替える必要はありません。
MOVLW B'10010000' ; Serial port is enable
MOVWF RCSTA
他の設定にしたい場合は、B'10010000'
の部分を変更します。
SPBRG(ボーレート発生器の設定レジスタ)の設定
調歩同期方式(非同期モード)を選択した場合は、ボーレートを決定するために 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ピン) を使います。
SPBRG レジスタの設定値の計算例
例として、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) |
データ送信の流れ
- TXSTA<TXEN> ビットを 1 に設定して送信を Enable にしておく。
- TXREG レジスタに送信したいデータ(8 ビット)をセットすると、その値が自動的に TSR レジスタにコピーされて TX ポートから送信が開始される。
- TSR の値は操作できません。
- TSR の内容は LSb から 1 ビットずつ送信されます。

TXREG レジスタに次のデータをセットできることを確認する方法(3 通り)
- TXREG の値が TSR にコピーされた瞬間に PIR1<TXIF> ビットがセットされる。このビットがセットされたら次のデータを TXREG に格納してよい。TXREG に次の値をセットすると、PIR1<TXIF> ビットは自動的にクリアされる(ソフトでクリアすることはできない)。
- PIE1<TXIE> を 1 にセットしておくと、TXREG の値が TSR にコピーされた瞬間に割り込みが発生するのでこれを利用する(PIR1<TXIF> ビットがセットされる)。
- TSR の値が空になると、TXSRA<TRMT> が 1 にセットされるので、ループでこのビットを監視する。(9 ビット送信を行う場合は、この方法で監視する必要がある?)
TXSTA<TX9D> ビットを使って 9 ビット送る時の注意
- TXSTA<TX9> ビットを 1 に設定して 9 ビット送信を Enable にする。
- TX9D ビット(9 ビット目のデータ)の設定は TXREG にデータをセットする前に行う必要がある。
サンプルコード
シリアル送信サブルーチン (SendUsart)
USART でデータを送信する時は、TXSTA レジスタの TRMT (TXSTA<1>) が 1 になるのを確認してから送信するようにします。 次のサブルーチンは、このルールに基づいて W レジスタのデータを送信します。
USART によるシリアル送信サブルーチン(方法 1)
;==============================================================================
; シリアル送信サブルーチン
; 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 によるシリアル送信サブルーチン(方法 2:おすすめ)
;******************************************************************************
; 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
0x01, 0x02, 0x03 を繰り返し送信する (PIC16F873)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;
; 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
シリアル受信 (Receive)
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