PRS-DE07MS通信基礎実験・割込み処理で送受信
すいません m(_ _)m、本記事はブログ引越時に書式が崩れました。順次修正中です。
今までの反省を踏まえて、少々真面目に割込みによるPRS-DE07MSとの送受信プログラムを作ってみました。最初は受信だけ、とか思ったりなんかもしたんですが、まあこの際、ということで、送受信とも割り込み処理にしてみました。とりあえず、384kbps通信(ATmega128の方は400kbps通信)での動作確認まで完了したのでアップします。
基本的なソースコードは、最初に作成した構造と変わっていませんが、割込み処理による送受信を行うようにしました。そのため、ISR(Interrupt Service Routine)を追加し、バッファリングのために用意した配列変数に送信データをセットして送信処理を開始するようにし、受信も同様に1バイト毎に受信完了割込みが発生するたびにバッファ変数にセットするようにしました。具体的には以下の関数です。
また、これに伴い、_txM()、_txD()、_rxD()を廃止し、マルチプロセッサービットのマーク、スペースの設定は送信割込み処理の中で行うように変更しました。また、今回から、改行のみ入力すると、受信バッファの内容を表示するようにしました。タイムアウト処理で受信処理を抜けた後、なんか入っているかな?と確認するためです。
■プログラム解説
ISR( USART1_UDRE_vect )
送信データレジスタ空き割込み処理です。ATmega128での送信処理は、厳密にはプログラムから1バイトデータを書き込むと、それが送信シフトレジスタというところに移され、その後、シリアルデータとして送信されます。この割り込みは、移された後に発生します。バッファ変数の1バイト目は「ID」として扱い、マルチプロセッサビットをマーク設定して送信します。また、バッファ変数からすべてのデータを送信し終わったら、送信割込みを停止します。
- ISR( USART1_RX_vect )
受信完了割込み処理です。1バイト分のデータ受信が完了したときに呼び出され、データをバッファ変数に移します。バッファ変数は5バイトだけですので、とりあえずオーバーしないようにはコードが書いてあります。 - init()
倍速許可、9ビットデータフレーム形式を使用するように初期化します。また、受信のみ割込み有効にします。送信については、送信データがセットされたときに割込み許可するようにしています。このあたりは、GDL同梱のライブラリを参考にさせていただいています。 - send()
送信データを送信バッファにセットし、送信割込み処理を開始します。この関数が実行されたときに、まだ送信バッファが空ではない場合は、空になるのを待ちます。送信は、一方的に送っているだけなので、永遠に待たされることは無いでしょう。 - recv()
受信データを受信バッファから取り出します。相変わらずタイムアウトで受信完了をチェックしている手抜き処理です。この関数が呼び出されたら、とりあえず0.1秒待ち、受信バッファからデータを取り出します。受信異常フラグは調査のためもあって、フレーミングエラー、データオーバランエラーについては返すようにしています。 - main()
USART0(RS232Cでパソコンに接続されている方のポート)にて16進数で入力された数値を、チェックサム付きでPRS-DE07MSに送信します。キー入力処理が多くなってしまい、ちょっとソースコードが見辛くなってしまいましたが、やっていることは「入力を受け付けて、PRS-DE07MSと通信を行い、その状況を表示する」だけです。
これから、どれくらいの速度までPRS-DE07MSと通信できるか試してみようと思います。USAR1を115kbsp通信しながら500kbpsぐらいいけるとうれしいな。それができたら、デイジーチェーンとパラレル接続のテストをしてみようと思います。
■ソースコード
1: //---------------------------------------------------------------------------------------- 2: // PRS-DE07MS通信テストプログラム(割込処理版) 3: // USART0に接続した端末から入力した16進数データをPRS-DE07MSへ送信し結果を表示します。 4: // ISR対応(interrupt service routine) 5: // 6: // 環境 BTC068(ATmega128)、GDL V2.0.0.0 7: // 説明 USART0に接続されたシリアル通信端末から入力した16進数データを、USART1に接続した 8: // PRS-DE07MSへ転送します。また、結果を受信したら端末に表示します。送信データが2 9: // バイト以下の場合はチェックサム無しで送信を行い、2バイトを超える場合はチェック 10: // サムを自動的に付加します。 11: // ビルドはPRS-DE07MS対応プログラム開発用に設定された環境(GDLの通信ライブラリから 12: // USART1用のプログラムを削除したライブラリ…AVR(ATmega128 ExtRAM NRS1))にて行い 13: // ます。また、最適化レベルは2でコンパイル確認しています。 14: // AUTHORED BY SISO JUNK STDUIO 15: // HISTORY R00 2009/03/16 16: //---------------------------------------------------------------------------------------- 17: #include <avr/io.h> 18: #include <avr/interrupt.h> 19: #include <avr/pgmspace.h> 20: 21: #include <rs.h> 22: 23: 24: 25: //---------------------------------------------------------------------------------------- 26: // 定数定義 27: //---------------------------------------------------------------------------------------- 28: // 通信処理返値定義 29: #define _RECV_OK 0x00 // 正常完了(ACK応答を含みます) 30: #define _RECV_TIMEOUT 0x01 // 受信タイムアウト発生 31: #define _RECV_RVFRER 0x03 // 受信フレーミングエラー発生 32: #define _RECV_RVDOER 0x04 // 受信データオーバーランエラー発生 33: 34: // 通信速度定義(初期化で使用) 35: typedef enum { 36: BR400K = 4, 37: BR500K = 3, 38: BR1M = 1, 39: } EbaudRate; 40: 41: //---------------------------------------------------------------------------------------- 42: // データ定義 43: //---------------------------------------------------------------------------------------- 44: // USART0 送受信用バッファ 45: char GacTxb[100], GacRxb[100]; 46: 47: // USART1 送受信用バッファ 48: volatile unsigned char GucPsmTxbIdx; // 送信時読み出し位置カウンタ 49: volatile unsigned char GucPsmTxbLen; // 送信バッファデータ長 50: unsigned char GaucPsmTxb[5]; // PRS-DE07MS送信データバッファ 51: volatile unsigned char GucPsmRxbIdx; // 受信時書き込み位置カウンタ 52: unsigned char GaucPsmRxb[5]; // PRS-DE07MS受信データバッファ 53: unsigned char GucPsmRxStat; // PRS-DE07MS受信状態 54: 55: 56: 57: //---------------------------------------------------------------------------------------- 58: // 割込送信処理 59: //---------------------------------------------------------------------------------------- 60: ISR( USART1_UDRE_vect ) 61: { 62: if( GucPsmTxbIdx < GucPsmTxbLen ){ 63: // 1バイト目を送信するときはID送信なので、マスタービットをONする。 64: if( GucPsmTxbIdx == 0 ) UCSR1B |= _BV(TXB8); // 9ビット目ON 65: else UCSR1B &= ~_BV(TXB8); // 9ビット目OFF 66: UDR1 = GaucPsmTxb[GucPsmTxbIdx++]; // データ送信開始 67: } 68: else{ 69: UCSR1B &= ~_BV(UDRIE); // データ最後で割込処理停止 70: GucPsmTxbIdx = GucPsmTxbLen = 0; 71: } 72: } 73: 74: //---------------------------------------------------------------------------------------- 75: // 割込受信処理 76: //---------------------------------------------------------------------------------------- 77: ISR( USART1_RX_vect ) 78: { 79: // ステータスフラグ取得 80: // if( GucPsmRxStat != 0x00 )…とかするのが正統な気がするが、まあ、手抜き&高速 81: // 処理ということで、ORで済ませる。 82: GucPsmRxStat |= UCSR1A & (_BV(FE)|_BV(DOR)); 83: 84: // 受信バッファへのデータセット(ちょっと手抜きだけど…。) 85: // 受信バッファに取り込めなくてもとりあえず読み出しておく。たぶん、読み出さないと 86: // エラーフラグがクリアされないので。最後のデータが崩れていくことになるけけど、 87: // 5バイト超えて受信するほうが変なので、放っておく。 88: GaucPsmRxb[GucPsmRxbIdx] = UDR1; 89: if( GucPsmRxbIdx < 5 ) GucPsmRxbIdx++; 90: } 91: 92: 93: 94: //---------------------------------------------------------------------------------------- 95: // USART1(シリアルサーボ通信)ポートオープン 96: // USART1を初期化し、送受信バッファを初期化する。 97: // 引数 EbaudRate 通信速度 98: // 戻値 void 99: //---------------------------------------------------------------------------------------- 100: void init( EbaudRate br ){ 101: // USART1初期化 102: // 受信完了割込許可、9ビット形式、受信許可、送信許可 103: UCSR1B = _BV(RXCIE)|_BV(RXEN)|_BV(TXEN)|_BV(UCSZ2); 104: // ボーレート設定 105: UBRR1H = ( br >> 8 ) & 0xff; // ボーレート設定(上位バイト) 106: UBRR1L = br & 0xff; // ボーレート設定(下位バイト) 107: UCSR1A = _BV(U2X); // 倍速許可 108: 109: // 送受信バッファクリア 110: GucPsmTxbIdx = GucPsmTxbLen = GucPsmRxbIdx = 0; 111: GucPsmRxStat = 0x00; 112: } 113: 114: //---------------------------------------------------------------------------------------- 115: // PRS-DE07MSへのデータ送信 116: // PRS-DE07MSへのデータ送信を行います。送信は割込処理にて行われるため、本処理では 117: // バッファにデータを渡すだけです。もしバッファが空いていない場合、待ちます。 118: // 送信データ配列は5バイト確保してください(チェックサム自動付加のため) 119: // チェックサムは必要に応じて、呼び出し側でセットします。 120: // 引数 unsigned char* 送信データ 121: // unsigned char 送信データ長 122: // 戻値 unsigned char 送信データ長(チェックサム追加後) 123: //---------------------------------------------------------------------------------------- 124: unsigned char send( unsigned char* pucData, unsigned char ucLen ){ 125: unsigned char ucCnt, ucChkSum; 126: 127: while( GucPsmTxbLen != 0 ); // 送信バッファ空き待ち 128: ucChkSum = 0; 129: 130: // バッファへのデータコピー及びデータ長設定 131: for( ucCnt = 0; ucCnt < ucLen; ucCnt++ ){ 132: GaucPsmTxb[ucCnt] = pucData[ucCnt]; 133: ucChkSum += pucData[ucCnt]; 134: } 135: 136: // 2バイトを超えるデータの場合はチェックサムも追加する。呼び出し元に送信内容を伝える 137: // ため、チェックサム追加があった場合、送信データ引数にもチェックサムを追加する。 138: if( ucCnt > 2 ){ 139: GaucPsmTxb[ucCnt] = pucData[ucCnt] = ucChkSum; 140: ucCnt++; 141: } 142: 143: // ucLenは引数なのでここで代入しても呼び出し元には返せないが、返り値を保持する 144: // ために、一時的に借用する。 145: GucPsmTxbLen = ucLen = ucCnt; 146: GucPsmTxbIdx = 0; 147: 148: UCSR1B |= _BV(UDRIE); // 割込送信開始 149: 150: // 実際の送信データ長(チェックサムがある場合はそれも含む)を返す。 151: return ucLen; 152: } 153: 154: //---------------------------------------------------------------------------------------- 155: // PRS-DE07MSからデータ受信 156: // PRS-DE07MSからデータ受信を行います。受信は割込により実装していますが、受信完了は 157: // タイムアウト(約0.1秒)でイージーにやっています。なんとなく取り合いが完璧じゃない 158: // ですが、実用上、問題ないでしょう。 159: // 引数 unsigned char* 受信データ 160: // unsigned char* 受信データ長 161: // 戻値 unsigned char 受信結果 162: //---------------------------------------------------------------------------------------- 163: unsigned char recv( unsigned char* pucData, unsigned char* pucLen ){ 164: volatile unsigned long ulCnt; 165: unsigned char ucCnt; 166: unsigned char ucStat; 167: 168: // 0.1秒待つ。 169: for( ulCnt = 0L; ulCnt < 46729L ; ulCnt++ ); 170: 171: if( GucPsmRxbIdx != 0 ){ 172: ucStat = _RECV_OK; 173: // 受信異常チェック 174: if( GucPsmRxStat & _BV(FE)) ucStat = _RECV_RVFRER; 175: if( GucPsmRxStat & _BV(DOR)) ucStat = _RECV_RVDOER; 176: // 受信データの読み出し 177: for( ucCnt = 0; ucCnt < GucPsmRxbIdx; ucCnt++ ) pucData[ucCnt] = GaucPsmRxb[ucCnt]; 178: *pucLen = GucPsmRxbIdx; 179: 180: UCSR1B &= ~_BV(RXCIE); // 受信割込停止 181: GucPsmRxStat = 0x00; 182: GucPsmRxbIdx = 0; 183: UCSR1B |= _BV(RXCIE); // 受信割込再開 184: 185: return ucStat; 186: } 187: else{ 188: *pucLen = 0; 189: return _RECV_TIMEOUT; 190: } 191: } 192: 193: //---------------------------------------------------------------------------------------- 194: // メイン 195: // 196: // 197: //---------------------------------------------------------------------------------------- 198: void main(void) 199: { 200: char cRecv; // 入力文字データ 201: unsigned char aucInpBuff[10]; // 入力文字データ格納用バッファ 202: unsigned char ucBuffPos; // 入力文字データ格納用バッファ位置 203: unsigned char ucCnt; // forループ用カウンタ 204: 205: unsigned char aucPsmBuff[5]; // サーボ通信用バッファ 206: unsigned char ucLen; // サーボ送受信データ長 207: unsigned char ucRecvRes; // 受信結果 208: 209: ucBuffPos = 0; // 入力文字バッファの書き込み位置を先頭 210: 211: // USART0通信ポートを初期化&割込処理開始 212: rs0_init( br115200, GacTxb, sizeof(GacTxb), GacRxb, sizeof(GacRxb)); 213: init( BR400K ); 214: 215: sei(); 216: 217: // 起動メッセージの表示 218: rs0_puts_P( PSTR("PRS-DE07MS COMMUNICATION TEST PROGRAM\n")); 219: rs0_puts_P( PSTR("PLEASE INPUT HEX DATA. [Enter]=EXECUTION, [ESC]=CANCEL INPUT\n")); 220: rs0_puts_P( PSTR("INPUT DATA> ")); 221: 222: // メインループ 223: while( 1 ){ 224: switch( cRecv = rs0_getc()){ // 1文字受信(入力があるまで待つ) 225: case '\x0d': // ENTER入力時は処理実行 226: if( ucBuffPos >= 4 ){ 227: // データ送信 228: // 入力されたデータをバイナリデータに変換し、データが2バイトを超える 229: // 場合はチェックサムを付加して送信 230: rs0_puts_P( PSTR("\n...SEND [")); 231: // 送信データの準備をする。 232: for( ucCnt = 0; ucCnt < ucBuffPos/2; ucCnt++ ){ 233: aucPsmBuff[ucCnt] =(unsigned char)(aucInpBuff[ucCnt*2]<<4) 234: + (unsigned char)aucInpBuff[ucCnt*2+1]; 235: } 236: 237: // コマンドの送信と表示 238: ucLen = send( aucPsmBuff, ucCnt ); 239: for( ucCnt = 0; ucCnt < ucLen; ucCnt++ ) 240: rs0_printf_P( PSTR("%02X"), aucPsmBuff[ucCnt] ); 241: 242: // データ受信 243: // データ受信を実行し、受信結果が問題なければそのまま受信データを 244: // 表示し、異常の場合は異常値を表示する。 245: rs0_puts_P( PSTR("]...RECV [")); 246: ucRecvRes = recv( aucPsmBuff, &ucLen ); 247: 248: if( ucRecvRes != _RECV_OK ) rs0_printf_P( PSTR("RECV ERROR %02X"), ucRecvRes ); 249: else for( ucCnt = 0; ucCnt < ucLen; ucCnt++ ) 250: rs0_printf_P( PSTR("%02X"), aucPsmBuff[ucCnt] ); 251: rs0_puts_P( PSTR("]\n")); 252: } 253: else{ 254: if( ucBuffPos == 0 ){ 255: // もし入力文字数0ならば、読み込みバッファを表示する。 256: ucRecvRes = recv( aucPsmBuff, &ucLen ); 257: rs0_puts_P( PSTR("RECV BUFF [")); 258: for( ucCnt = 0; ucCnt < ucLen; ucCnt++ ) 259: rs0_printf_P( PSTR("%02X"), aucPsmBuff[ucCnt] ); 260: rs0_puts_P( PSTR("]\n")); 261: } 262: else rs0_puts_P( PSTR("...DATA IS NOT ENOUGH.\n")); 263: } 264: ucBuffPos = 0; 265: rs0_puts_P( PSTR("INPUT DATA> ")); 266: break; 267: case '\x1b': // ESC入力時は入力キャンセル 268: rs0_puts_P( PSTR("...CANCELED\nINPUT DATA> ")); 269: ucBuffPos = 0; 270: break; 271: default: 272: if( ucBuffPos < 10 ){ // 入力文字列最大長チェック 273: // aucInpBuffに、1バイトあたり16進数一桁として入力文字をセットしていく。 274: if(( cRecv >= '0' )&&( cRecv <= '9' )) aucInpBuff[ucBuffPos++] = cRecv - '0'; 275: if(( cRecv >= 'a' )&&( cRecv <= 'f' )) aucInpBuff[ucBuffPos++] = cRecv - 'a' + 0x0a; 276: if(( cRecv >= 'A' )&&( cRecv <= 'F' )) aucInpBuff[ucBuffPos++] = cRecv - 'A' + 0x0a; 277: rs0_putc( cRecv ); 278: } 279: else{ // 最大を超えたら入力処理をリセット 280: rs0_puts_P( PSTR("...DATA LENGTH OVER\nINPUT DATA> ")); 281: ucBuffPos = 0; 282: } 283: } 284: } 285: }
Your Message