====== SCIで232C通信 ======
送受信リングバッファと、割り込みを使ったシリアル通信処理を作ります。送信リングバッファは、\\
送信コマンドを発行して直ぐメイン処理に戻すために、受信リングバッファは、メイン処理が多少\\
重くてもデータを取りこぼさないために、実装する必要があります。割り込みを使う理由も同じです。\\
\\
リングバッファのサイズは計算しやすい 64,128,256とかにしたほうが処理が楽なので今回は256に\\
しました。なので、一度に送れるのは256Byte以下となります。大きいデータは分割送信します。
===== プログラムを作る =====
==== ヘッダファイルの作成 ====
Cから簡単に使えるようにボーレート等を#defineしたヘッダファイルを作ります。
//=============================================================================
// 設定値(SCI)
//=============================================================================
//---------------------------------------------------------------------
// ビット長
//---------------------------------------------------------------------
#define BIT_LENGTH_8 (0 << 6) // 8 ビットデータ
#define BIT_LENGTH_7 (1 << 6) // 7 ビットデータ
//---------------------------------------------------------------------
// パリティ
//---------------------------------------------------------------------
#define PARITY_EVEN (2 << 4) // 偶数パリティ
#define PARITY_ODD (3 << 4) // 奇数パリティ
//---------------------------------------------------------------------
// ストップビット
//---------------------------------------------------------------------
#define STOP_BIT_1 (0 << 3) // ストップビット 1
#define STOP_BIT_2 (1 << 3) // ストップビット 2
//---------------------------------------------------------------------
// ボーレート
//---------------------------------------------------------------------
// clock / 32 / bps - 1
#define BPS_110 3,110 // 110 bps
#define BPS_150 3, 80 // 150 bps
#define BPS_300 2,162 // 300 bps
#define BPS_600 2, 80 // 600 bps
#define BPS_1200 1,162 // 1200 bps
#define BPS_2400 1, 80 // 2400 bps
#define BPS_4800 0,162 // 4800 bps
#define BPS_9600 0, 80 // 9600 bps
#define BPS_19200 0, 40 // 19200 bps
#define BPS_31250 0, 24 // 31250 bps
#define BPS_38400 0, 19 // 38400 bps
==== リングバッファ用のワーク作成 ====
次に、リングバッファ用のワークをRAMに作ります。
;******************************************************************************
; RAM 上のデータ(初期化なし)
;******************************************************************************
SEGMENT BSS
;------------------------------------------------------------------------------
; 受信用
;------------------------------------------------------------------------------
SciRecvRead: DC.B 0 ; 受信バッファの読み出し位置
SciRecvWrite: DC.B 0 ; 受信バッファの書き込み位置
SciRecvBuff: DS.B 256 ; 受信バッファ
;------------------------------------------------------------------------------
; 送信用
;------------------------------------------------------------------------------
SciSendRead: DC.B 0 ; 送信バッファの読み出し位置
SciSendWrite: DC.B 0 ; 送信バッファの書き込み位置
SciSendBuff: DS.B 256 ; 送信バッファ
==== I/Oの定義 ====
今回はSCI0用に作りますが、後で変更が楽なように、使用するI/Oを定義しておきます。
;------------------------------------------------------------------------------
; 設定値
;------------------------------------------------------------------------------
SMR EQU SMR0 ; B シリアルモードレジスタ(R/W)
BRR EQU BRR0 ; B ビットレートレジスタ(R/W)
SCR EQU SCR0 ; B シリアルコントロールレジスタ(R/W)
TDR EQU TDR0 ; B トランスミットデータレジスタ(R/W)
SSR EQU SSR0 ; B シリアルステータスレジスタ(R/W)
RDR EQU RDR0 ; B レシーブデータレジスタ(R/W)
==== 初期化ルーチンの作成 ====
まずは、初期化ルーチンを作ります。リングバッファの初期化もここで行います。\\
手順は、SCIを停止、通信方式の設定、1 bps Wait、エラーや割り込み要求のクリア、\\
割り込み許可という流れ。\\
SciInit0(BIT_LENGTH_8 | PARITY_EVEN | STOP_BIT_1 | BPS_9600) ; と記述すると\\
%%SciInit0((0 << 6) | (2 << 4) | (0 << 3) | 0, 80) ;%% と展開され\\
最後にはSciInit0(0x20,80)と展開されます。呼び出しやすいですよね?
;******************************************************************************
; SCI 0 の初期化
;
; _SciInit0
; 2004/12/28 : 初期バージョン完成
;
; [書式] void SciInit0(Uint8 mode,Uint8 bps) ;
;
; [IN] Uint8 mode = シリアルモードレジスタ値
; Uint8 bps = ボーレートのウェイト値
;
; [OUT] なし
;
; [DED] R0L,R1
;******************************************************************************
public _SciInit0
_SciInit0: MOV.B @(5,ER7),R1L ; R1L = mode
MOV.B @(7,ER7),R1H ; R1H = bps
SUB.W R0,R0 ; R0 = 0
MOV.B R0L,@SCR ; SCI の停止
MOV.B R1L,@SMR ; ビット長、パリティ、ストップビットの設定
MOV.B R1H,@BRR ; bps の設定
MOV.W R0,@SciRecvRead ; ワークのクリア
MOV.W R0,@SciSendRead
MOV.W #1,E0 ; 1 Bps Wait
BSR WAIT_1ms
MOV.B @SSR,R0L ; ダミーリード
MOV.B #H'80,R0L ; エラーのクリア
MOV.B R0L,@SSR
MOV.B #H'70,R1L ; 受信データフル割込み、受信エラー割込み許可
MOV.B R1L,@SCR ; 送信、受信動作を許可
RTS
==== エラー処理の作成 ====
先に割り込みルーチンを書いていきます。まずはエラー割り込み。単純にエラークリアのみ。\\
関数の見出しの後ろに書いてある(52)は、割り込みベクタ番号です。\\
後で見てもわかるように記述しておくと便利です。
;******************************************************************************
; エラー割り込み(52)
;
; INT_SCI_ER0
; 2004/11/09 : 初期バージョン完成
;
; [IN] なし
;
; [OUT] なし
;
; [DED] なし
;******************************************************************************
public INT_SCI_ER0
INT_SCI_ER0: BCLR.B #ORER,@SSR ; オーバランエラーのクリア
BCLR.B #FER,@SSR ; フレーミングエラーのクリア
BCLR.B #PER,@SSR ; パリティエラーのクリア
RTE
==== 受信割り込み処理の作成 ====
次に受信割り込み。受信バッファをチェックし、一杯だったら無視。空いていればバッファに\\
データを入れ、リングバッファの位置を更新します。割り込みルーチンを抜ける前に、割り込み\\
要因のクリアを忘れないように。
;******************************************************************************
; 受信割り込み(53)
;
; INT_SCI_RX0
; 2004/12/28 : 初期バージョン完成
;
; [IN] なし
;
; [OUT] なし
;
; [DED] なし
;******************************************************************************
public INT_SCI_RX0
INT_SCI_RX0: PUSH.W R0
PUSH.L ER1
MOV.B @SciRecvWrite,R1L ; 受信バッファが一杯?
MOV.B @SciRecvRead,R1H
DEC.B R1H
CMP.B R1L,R1H
BEQ INT_SCI_RX_END:8
MOV.B @RDR,R0L ; 1 Byte 受信
EXTU.W R1 ; データ保存
EXTU.L ER1
MOV.B R0L,@(SciRecvBuff,ER1)
INC.B R1L
MOV.B R1L,@SciRecvWrite
INT_SCI_RX_END: POP.L ER1
POP.W R0
BCLR.B #RDRF,@SSR ; 次の受信許可
RTE
==== 送信割り込み処理の作成 ====
送信割り込み処理は、受信よりちょっとだけ複雑です。送信用のリングバッファを調べ、\\
データがない場合は送信割り込みを止めます。少しでも処理を軽くするためです。\\
データがある場合は、データを1つ送信し、リングバッファを更新します。
;******************************************************************************
; 送信割り込み(55)
;
; INT_SCI_TX0
; 2004/12/28 : 初期バージョン完成
;
; [IN] なし
;
; [OUT] なし
;
; [DED] なし
;******************************************************************************
public INT_SCI_TX0
INT_SCI_TX0: PUSH.W R0
PUSH.L ER1
MOV.B @SciSendRead,R1L ; 送信データはもうない?
MOV.B @SciSendWrite,R1H
BCLR.B #TEIE,@SCR ; 送信割り込み禁止
CMP.B R1L,R1H
BEQ INT_SCI_TX_END:8
EXTU.W R1 ; データ取得
EXTU.L ER1
MOV.B @(SciSendBuff,ER1),R0L
INC.B R1L
MOV.B R1L,@SciSendRead
MOV.B R0L,@TDR ; データセット
BCLR.B #TDRE,@SSR ; 次の送信許可
BSET.B #TEIE,@SCR ; 送信割り込み許可
INT_SCI_TX_END: POP.L ER1
POP.W R0
BCLR.B #TEND,@SSR ; 送信割り込み許可
RTE
==== 受信処理の作成 ====
割り込み処理は全て揃いましたので、受信処理を作ります。\\
リングバッファをチェックしデータがあれば読み出しと、リングバッファの更新を行い、\\
空ならエラーを返します。
;******************************************************************************
; SCI0 から 1 Byte 受信
;
; _SciRead0
; 2004/12/28 : 初期バージョン完成
;
; [書式] BOOL SciRead0(char *code) ;
;
; [IN] char *code = 受信データを格納する場所のポインタ
;
; [OUT] BOOL = TRUE 受信成功
; = FALSE 受信バッファは空
;
; [DED] ER1,ER2,R3L
;******************************************************************************
public _SciRead0
_SciRead0: MOV.L @(4,ER7),ER2 ; ER2 = データ受信場所のポインタ
SUB.W R0,R0 ; R0 = FALSE
MOV.B @SciRecvRead,R1L ; 受信データはもうない?
MOV.B @SciRecvWrite,R1H
CMP.B R1L,R1H
BEQ SCI_READ_END:8
EXTU.W R1
EXTU.L ER1
MOV.B @(SciRecvBuff,ER1),R3L
MOV.B R3L,@ER2
INC.B R1L
MOV.B R1L,@SciRecvRead
INC.W #1,R0 ; R0 = TRUE
SCI_READ_END: RTS
==== 送信処理の作成 ====
送信処理は少し複雑です。 割り込みが止まっている、送信バッファが空いている場合は、直ちに\\
データを送信し割り込みを許可します。\\
送信割り込みが動いている場合は、リングバッファにデータを入れていきます。
;******************************************************************************
; SCI0 に 1 Byte 送信
;
; _SciWrite0
; 2004/12/28 : 初期バージョン完成
;
; [書式] BOOL SciWrite0(char code) ;
;
; [IN] char code = 送信データ
;
; [OUT] BOOL = TRUE 送信成功
; = FALSE 送信失敗
;
; [DED] ER1,R2L
;******************************************************************************
public _SciWrite0
_SciWrite0: MOV.B @(5,ER7),R2L ; R2L = 送信データ
SCI_WRITE0: SUB.W R0,R0 ; R0 = FALSE
BTST.B #TEIE,@SCR ; 送信割り込み許可?
BNE SCI_WRITE_SKIP:8
BTST.B #TDRE,@SSR ; 即送信はできない?
BEQ SCI_WRITE_SKIP:8
MOV.B R2L,@TDR ; データ送信
BCLR.B #TDRE,@SSR
BSET.B #TEIE,@SCR ; 送信割り込み許可
INC.W #1,R0
RTS
SCI_WRITE_SKIP: MOV.B @SciSendWrite,R1L ; リングバッファは空いている?
MOV.B @SciSendRead,R1H
DEC.B R1H
CMP R1L,R1H
BEQ SCI_WRITE_END:8
EXTU.W R1 ; データの書き込み
EXTU.L ER1
MOV.B R2L,@(SciSendBuff,ER1)
INC.B R1L
MOV.B R1L,@SciSendWrite ; 次の書き込み位置を保存
INC.W #1,R0 ; R0 = TRUE
SCI_WRITE_END: RTS
ついでに複数Byte送信ルーチンも作ります。送信データに0が含まれることを考慮し、引数に\\
データ長を渡します。また、バッファが一杯で送信エラーになる可能性があるので、送信できた\\
データ長を返すようにします。
;******************************************************************************
; SCI0 に複数 Byte 送信
;
; _SciWriteStr0
; 2004/12/28 : 初期バージョン完成
;
; [書式] int SciWrite0(Uint16 len,char *data) ;
;
; [IN] Uint16 len = データ長
; char *data = 送信データのポインタ
;
; [OUT] int = 送信 Byte 数
;
; [DED] E0,ER1,ER2,ER3
;******************************************************************************
public _SciWriteStr0
_SciWriteStr0: MOV.W @(4,ER7),E2 ; E2 = データ長
MOV.L @(6,ER7),ER3 ; ER3 = 送信データのポインタ
SUB.W E0,E0
WRITE_STR_LOOP: MOV.B @ER3+,R2L
BSR SCI_WRITE0:8
OR.W R0,R0
BEQ WRITE_STR_END:8
INC.W #1,E0
CMP.W E0,E2
BNE WRITE_STR_LOOP:8
WRITE_STR_END: MOV.W E0,R0
RTS
さらに、使いやすくするため printf 風な関数も用意します。
//*****************************************************************************
// SCI0 にフォーマット付き文字列出力
//
// SciPrintf0
// 2004/12/28 : 初期バージョン完成
//
// [IN] char *format = 書式文字列
//
// [OUT] int = 送信 Byte 数
//*****************************************************************************
int SciPrintf0(const char *format,...) {
char buff[256] ;
va_list argptr ;
va_start(argptr,format) ;
vsprintf(buff,format,argptr) ;
va_end(argptr) ;
return SciWriteStr0(strlen(buff),buff) ; // SCI0 に複数 Byte 送信
}
===== アセンブラ部分の全ソース =====
処理速度のバランスを考えメイン処理を作れば、送信と受信のバッファオーバーは起こらないので\\
バッファオーバー時の処理は軽めに実装しています。
;******************************************************************************
; シリアル通信処理(チャンネル 0)
; (C)YdlProg
;******************************************************************************
INCLUDE (../lib3052/3052.inc)
;------------------------------------------------------------------------------
; 設定値
;------------------------------------------------------------------------------
SMR EQU SMR0 ; B シリアルモードレジスタ(R/W)
BRR EQU BRR0 ; B ビットレートレジスタ(R/W)
SCR EQU SCR0 ; B シリアルコントロールレジスタ(R/W)
TDR EQU TDR0 ; B トランスミットデータレジスタ(R/W)
SSR EQU SSR0 ; B シリアルステータスレジスタ(R/W)
RDR EQU RDR0 ; B レシーブデータレジスタ(R/W)
segment TEXT
;******************************************************************************
; SCI 0 の初期化
;
; _SciInit0
; 2004/12/28 : 初期バージョン完成
;
; [書式] void SciInit0(Uint8 mode,Uint8 bps) ;
;
; [IN] Uint8 mode = シリアルモードレジスタ値
; Uint8 bps = ボーレートのウェイト値
;
; [OUT] なし
;
; [DED] R0L,R1
;******************************************************************************
public _SciInit0
_SciInit0: MOV.B @(5,ER7),R1L ; R1L = mode
MOV.B @(7,ER7),R1H ; R1H = bps
SUB.W R0,R0 ; R0 = 0
MOV.B R0L,@SCR ; SCI の停止
MOV.B R1L,@SMR ; ビット長、パリティ、ストップビットの設定
MOV.B R1H,@BRR ; bps の設定
MOV.W R0,@SciRecvRead ; ワークのクリア
MOV.W R0,@SciSendRead
MOV.W #1,E0 ; 1 Bps Wait
BSR WAIT_1ms
MOV.B @SSR,R0L ; ダミーリード
MOV.B #H'80,R0L ; エラーのクリア
MOV.B R0L,@SSR
MOV.B #H'70,R1L ; 受信データフル割込み、受信エラー割込み許可
MOV.B R1L,@SCR ; 送信、受信動作を許可
RTS
;******************************************************************************
; SCI0 から 1 Byte 受信
;
; _SciRead0
; 2004/12/28 : 初期バージョン完成
;
; [書式] BOOL SciRead0(char *code) ;
;
; [IN] char *code = 受信データを格納する場所のポインタ
;
; [OUT] BOOL = TRUE 受信成功
; = FALSE 受信バッファは空
;
; [DED] ER1,ER2,R3L
;******************************************************************************
public _SciRead0
_SciRead0: MOV.L @(4,ER7),ER2 ; ER2 = データ受信場所のポインタ
SUB.W R0,R0 ; R0 = FALSE
MOV.B @SciRecvRead,R1L ; 受信データはもうない?
MOV.B @SciRecvWrite,R1H
CMP.B R1L,R1H
BEQ SCI_READ_END:8
EXTU.W R1
EXTU.L ER1
MOV.B @(SciRecvBuff,ER1),R3L
MOV.B R3L,@ER2
INC.B R1L
MOV.B R1L,@SciRecvRead
INC.W #1,R0 ; R0 = TRUE
SCI_READ_END: RTS
;******************************************************************************
; SCI0 に 1 Byte 送信
;
; _SciWrite0
; 2004/12/28 : 初期バージョン完成
;
; [書式] BOOL SciWrite0(char code) ;
;
; [IN] char code = 送信データ
;
; [OUT] BOOL = TRUE 送信成功
; = FALSE 送信失敗
;
; [DED] ER1,R2L
;******************************************************************************
public _SciWrite0
_SciWrite0: MOV.B @(5,ER7),R2L ; R2L = 送信データ
SCI_WRITE0: SUB.W R0,R0 ; R0 = FALSE
BTST.B #TEIE,@SCR ; 送信割り込み許可?
BNE SCI_WRITE_SKIP:8
BTST.B #TDRE,@SSR ; 即送信はできない?
BEQ SCI_WRITE_SKIP:8
MOV.B R2L,@TDR ; データ送信
BCLR.B #TDRE,@SSR
BSET.B #TEIE,@SCR ; 送信割り込み許可
INC.W #1,R0
RTS
SCI_WRITE_SKIP: MOV.B @SciSendWrite,R1L ; リングバッファは空いている?
MOV.B @SciSendRead,R1H
DEC.B R1H
CMP R1L,R1H
BEQ SCI_WRITE_END:8
EXTU.W R1 ; データの書き込み
EXTU.L ER1
MOV.B R2L,@(SciSendBuff,ER1)
INC.B R1L
MOV.B R1L,@SciSendWrite ; 次の書き込み位置を保存
INC.W #1,R0 ; R0 = TRUE
SCI_WRITE_END: RTS
;******************************************************************************
; SCI0 に複数 Byte 送信
;
; _SciWriteStr0
; 2004/12/28 : 初期バージョン完成
;
; [書式] int SciWrite0(Uint16 len,char *data) ;
;
; [IN] Uint16 len = データ長
; char *data = 送信データのポインタ
;
; [OUT] int = 送信 Byte 数
;
; [DED] E0,ER1,ER2,ER3
;******************************************************************************
public _SciWriteStr0
_SciWriteStr0: MOV.W @(4,ER7),E2 ; E2 = データ長
MOV.L @(6,ER7),ER3 ; ER3 = 送信データのポインタ
SUB.W E0,E0
WRITE_STR_LOOP: MOV.B @ER3+,R2L
BSR SCI_WRITE0:8
OR.W R0,R0
BEQ WRITE_STR_END:8
INC.W #1,E0
CMP.W E0,E2
BNE WRITE_STR_LOOP:8
WRITE_STR_END: MOV.W E0,R0
RTS
;******************************************************************************
; エラー割り込み(52)
;
; INT_SCI_ER0
; 2004/11/09 : 初期バージョン完成
;
; [IN] なし
;
; [OUT] なし
;
; [DED] なし
;******************************************************************************
public INT_SCI_ER0
INT_SCI_ER0: BCLR.B #ORER,@SSR ; オーバランエラーのクリア
BCLR.B #FER,@SSR ; フレーミングエラーのクリア
BCLR.B #PER,@SSR ; パリティエラーのクリア
RTE
;******************************************************************************
; 受信割り込み(53)
;
; INT_SCI_RX0
; 2004/12/28 : 初期バージョン完成
;
; [IN] なし
;
; [OUT] なし
;
; [DED] なし
;******************************************************************************
public INT_SCI_RX0
INT_SCI_RX0: PUSH.W R0
PUSH.L ER1
MOV.B @SciRecvWrite,R1L ; 受信バッファが一杯?
MOV.B @SciRecvRead,R1H
DEC.B R1H
CMP.B R1L,R1H
BEQ INT_SCI_RX_END:8
MOV.B @RDR,R0L ; 1 Byte 受信
EXTU.W R1 ; データ保存
EXTU.L ER1
MOV.B R0L,@(SciRecvBuff,ER1)
INC.B R1L
MOV.B R1L,@SciRecvWrite
INT_SCI_RX_END: POP.L ER1
POP.W R0
BCLR.B #RDRF,@SSR ; 次の受信許可
RTE
;******************************************************************************
; 送信割り込み(55)
;
; INT_SCI_TX0
; 2004/12/28 : 初期バージョン完成
;
; [IN] なし
;
; [OUT] なし
;
; [DED] なし
;******************************************************************************
public INT_SCI_TX0
INT_SCI_TX0: PUSH.W R0
PUSH.L ER1
MOV.B @SciSendRead,R1L ; 送信データはもうない?
MOV.B @SciSendWrite,R1H
BCLR.B #TEIE,@SCR ; 送信割り込み禁止
CMP.B R1L,R1H
BEQ INT_SCI_TX_END:8
EXTU.W R1 ; データ取得
EXTU.L ER1
MOV.B @(SciSendBuff,ER1),R0L
INC.B R1L
MOV.B R1L,@SciSendRead
MOV.B R0L,@TDR ; データセット
BCLR.B #TDRE,@SSR ; 次の送信許可
BSET.B #TEIE,@SCR ; 送信割り込み許可
INT_SCI_TX_END: POP.L ER1
POP.W R0
BCLR.B #TEND,@SSR ; 送信割り込み許可
RTE
;******************************************************************************
; RAM 上のデータ(初期化なし)
;******************************************************************************
SEGMENT BSS
;------------------------------------------------------------------------------
; 受信用
;------------------------------------------------------------------------------
SciRecvRead: DC.B 0 ; 受信バッファの読み出し位置
SciRecvWrite: DC.B 0 ; 受信バッファの書き込み位置
SciRecvBuff: DS.B 256 ; 受信バッファ
;------------------------------------------------------------------------------
; 送信用
;------------------------------------------------------------------------------
SciSendRead: DC.B 0 ; 送信バッファの読み出し位置
SciSendWrite: DC.B 0 ; 送信バッファの書き込み位置
SciSendBuff: DS.B 256 ; 送信バッファ
END
===== 簡単なエコーバック試験用サンプル =====
// 必要なファイルを include して下さい
// SCI0 の初期化
extern void SciInit0(Uint8 mode,Uint8 bps) ;
extern BOOL SciRead0(char *code) ; // SCI0 から 1 Byte 受信
extern BOOL SciWrite0(char code) ; // SCI0 に 1 Byte 送信
void main(void) {
_di() ;
// SCI0 の初期化
// SciInit0(BIT_LENGTH_8 | PARITY_EVEN | STOP_BIT_1 | BPS_9600) ;
SciInit0(BIT_LENGTH_8 | STOP_BIT_1 | BPS_9600) ;
_ei() ;
while(1) {
char data ;
if(SciRead0(&data)) { // SCI0 から 1 Byte 受信
SciWrite0(data) ; // SCI0 に 1 Byte 送信
}
}
}