UARTの送受信をする場合、受信割り込みは必須項目ですが、送信割り込みはあまり使われていないかもしれません。
たとえば、38400bpsの通信の場合、1バイトの送信は、スタート/ストップビットを入れて10ビットで、約260μ秒かかります。ということは、次の送信データが送れるようになるまで260μ秒かかるということです。CPUを20MHzで動かしていて1命令あたり5クロックだと考えると1000命令近く実行できます。シリアル送信中、他にすることがないならそれでもいいのですが、ただループさせているだけあまりにも芸が無さ過ぎませんか?
送信割り込みを使えば、送信完了時に割り込みがかかります。その割り込みで次のデータをセットするということをすれば、待ち時間を別の処理に使えます。ちなみにA/D変換の場合、10ビットサンプル&ホールド有りで33サイクル、クロックは最大で10MHzですから、3.3μ秒となり、CPU20MHzでは、12命令程度です。割り込みをわざわざ使う必要はないでしょう。(割り込み先に飛んで戻ってくるだけで、それくらいのCPUサイクルを使ってしまいます。)
さて、実装ですが、例によって割り込み部分を"main.c"で明示します。
void send_int(void) /* UART送信割り込み用 */ { if (INT_FLAG & RS_SEND) /* 送信データ有りなら1文字送信する */ one_send(); }
割り込み用ユーザフラグに送信データがあるかどうかのフラグを用意して、データがあれば、つまりフラグが立っていれば、1バイト送信します。
次に受信割り込を用意します。
void recv_int(void) /* UART受信割り込み用 */ { if (one_read() == 0) /* エラーでないなら、1文字受信バッファに格納し、 */ INT_FLAG |= RS_RECV; /* 受信フラグセット */ }
こちらは、受信時にエラーがなければ、割り込み用ユーザフラグ内の受信フラグをセットします。
ここでは、1文字でも受信したら受信フラグを立てていますが、受信フォーマットが決まっているのであれば、特定の終了コードが来たときに受信フラグを立てる方法もあります。
次にone_send()とone_read()です。
送信用と受信用にそれぞれデータバッファとデータの位置を示すポインタを用意します。
送信では、送信開始アドレスS_SDBUFと送信終了アドレスE_SDBUFです。S_SDBUFとE_SDBUFが一致した時、送信終了です。データバッファは、リング・バッファとします。
void one_send(void) { if (S_SDBUF != E_SDBUF) { uart_one_send(*S_SDBUF); if (S_SDBUF == &SDBUF[BUF_MAX-1]) S_SDBUF = &SDBUF[0]; else S_SDBUF++; } else { INT_FLAG &= ~RS_SEND; } }
4行目のif文は、リング・バッファのため処理です。
S_SDBUFとE_SDBUFが不一致ならS_SDBUFの示すデータを送信し、S_SDBUFを更新します。一致したらなら送信フラグをクリアします。
送信割り込みは、送信終了時に発生しますから、最初の1文字は、割り込みを使わずに送る必要があります。以下がその開始関数です。
int send_rs(void) { INT_FLAG |= RS_SEND; one_send(); }
送信割り込みには、「送信バッファ空」と、「送信完了」したときの2種類あります。これは、R8C15に限らず、UART付のマイコンであれば大抵サポートとしています。今回は、送信バッファ空割り込みにしています。送信終了時に、RS-232CドライバICの電力制御をするのであれば、「送信完了」を選択する必要があります。それぞれの違いについては、ハードウェアマニュアルをご覧下さい。
次に受信です。
受信では、読取開始アドレスS_RDBUFと受信終了アドレスE_RDBUFです。データバッファは、送信と同じくリング・バッファとします。
unsigned char one_read(void) { unsigned int data; unsigned char error; data = uart_one_read(); error = (data >> 8) & R_ERR; if (error) return error; *E_RDBUF = (char) data; if (E_RDBUF == &RDBUF[BUF_MAX-1]) E_RDBUF = &RDBUF[0]; else E_RDBUF++; if (S_RDBUF == E_RDBUF) error = 1; return error; }
1文字受信し、エラーがなければ、E_RDBUFで示すデータバッファに格納し、E_RDBUFを更新します。リング・バッファですから、E_RDBUFがS_RDBUFに追いついたらエラーとします。
S_RDBUFは、受信データバッファからのデータ読み取り時に使用します。
uart_one_sendとuart_one_readは、レジスタの読み書きだけですから、解説しません。ソース(sample3.zip)とハードウェアマニュアルをご一読下さい。
割り込みの設定方法についても、別ページで解説していますのでここでは省略します。