送受信リングバッファと、割り込みを使ったシリアル通信処理を作ります。送信リングバッファは、
送信コマンドを発行して直ぐメイン処理に戻すために、受信リングバッファは、メイン処理が多少
重くてもデータを取りこぼさないために、実装する必要があります。割り込みを使う理由も同じです。
リングバッファのサイズは計算しやすい 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 ; 送信バッファ
今回は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 送信 } } }