前回の記事では、UARTでの送受信とコマンド制御までを実装しました。
今回の記事では、
まず、割り込み制御によるコマンド制御を行います。
次に、リンクバッファを使って複数文字を同時に受信し、
コマンド制御を行います。
さらに、大量データが一気に流入して処理が追い付かない状態に発生する
OERRの処理を行い、
通信フォーマットが崩れて起きるFERR(フレーミングエラー)についての処理も
学びました。
回路図

以前までと同じ回路図です。
割り込み制御
前回の記事のメインに入れていたものを単純に
interrupt()に入れるだけのことです。
c
void __interrupt() isr(void) {
if (PIR1bits.RCIF) {
rx_data = RCREG;
rx_flag = 1;
}
}
変数はグローバルで設定済みです。
c
while (1) {
if (rx_flag) {
rx_flag = 0;
while (PIR1bits.TXIF) {
TXREG = rx_data;
}
if (rx_data == 'a') {
LATAbits.LATA1 = 1;
} else if (rx_data == 'b') {
LATAbits.LATA1 = 0;
} else if (rx_data == 't') {
LATAbits.LATA1 = !LATAbits.LATA1;
}
}
}
割り込み制御でのフラグが立ったら、
コマンド制御をさせるというだけのことです。
前回の記事と大きな違いはありません。
そのため特に動画などは撮りませんでした。
リングバッファ
通信は、1文字ずつ受信することばかりではなく、
むしろまとめて受信することばかりです。
これまでのようなやり方では、
1文字しか受信できないため、
まとめてもれなく受信する方法が
リングバッファ
になります。
下がイメージ図です。

c
//割り込み内での処理
if (PIR1bits.RCIF) {
unsigned char next = (head + 1) % BUFFER_SIZE;
if (next != tail) {
buffer[head] = RCREG;
head = next;
} else {
unsigned char dummy = RCREG;
}
}
c
//mainループ内での処理
if (head != tail) {
unsigned char data = buffer[tail];
tail = (tail + 1) % BUFFER_SIZE;
while (!PIR1bits.TXIF);
TXREG = data;
if (data == 'a') {
LATAbits.LATA1 = 1;
} else if (data == 'b') {
LATAbits.LATA1 = 0;
} else if (data == 't') {
LATAbits.LATA1 = !LATAbits.LATA1;
}
}
上の図と、コードの様に
headが書き込み位置を決めて、
受信したコードを順番に書き込みます。
それをmainのループ内で
tailが読み込み位置をなぞり、
読み込んでいきます。
OERR(オーバーランエラー)対策
リングバッファを使うことで、受信したデータを一時的に保存できるようになりました。
しかし、これだけではまだ完全ではありません。
UARTでは、受信データを読み出す前に次々とデータが入ってくると、
受信バッファがあふれてしまうことがあります。
この状態が OERR(Overrun Error)です。
OERR が発生すると、USARTの受信が停止してしまいます。
つまり、一度OERRになると、RCREGを読んだだけでは復旧できません。
そのため、CRENビットを一度クリアして、再度セットします。
if (RCSTAbits.OERR) {
RCSTAbits.CREN = 0;
RCSTAbits.CREN = 1;
}
CRENは連続受信を有効にするビットです。
OERRが発生した場合は、
一度受信機能をOFFにしてからONに戻すことで、
USARTの受信状態を復旧させます。
今回の割り込み処理では、最初にOERRを確認するようにしました。
void __interrupt() isr(void) {
if (RCSTAbits.OERR) {
RCSTAbits.CREN = 0;
RCSTAbits.CREN = 1;
}
if (PIR1bits.RCIF) {
unsigned char next = (head + 1) % BUFFER_SIZE;
if (next != tail) {
buffer[head] = RCREG;
head = next;
} else {
unsigned char dummy = RCREG;
}
}
}
OERRは、リングバッファとは別の問題です。
リングバッファはソフト側で受信データをためる仕組みですが、
OERRはUSART内部の受信処理が追いつかなかったときに発生するエラーです。
そのため、リングバッファを使っていても、
OERR対策は必要になります。
FERR(フレーミングエラー)対策
次に、FERR(Framing Error)についても確認しました。
UART通信では、1文字のデータは
スタートビット → データビット → ストップビット
という形で送られます。
通常、ストップビットは「1」になります。
しかし、ノイズやボーレートのズレなどで、
ストップビットが正しく受信できない場合があります。
この状態が FERR(フレーミングエラー)です。
PICでは、FERRが発生しているかどうかを
RCSTAbits.FERR で確認できます。
if (RCSTAbits.FERR) {
unsigned char dummy = RCREG;
return;
}
FERRは、受信した1文字に対して発生するエラーです。
そのため、FERRが立っていた場合は、
そのデータは壊れている可能性があるため、RCREGを読んで捨てます。
ここで重要なのは、
RCREGを読まないと受信データが残ったままになるという点です。
つまり、エラーが出たデータであっても、
一度RCREGを読んで処理を進める必要があります。
今回のコードでは、
FERRが発生した文字はリングバッファには入れず、
そのまま捨てるようにしました。
最終的な割り込み処理
OERRとFERRの処理を加えた割り込み処理は、以下のようになります。
void __interrupt() isr(void) {
if (RCSTAbits.OERR) {
RCSTAbits.CREN = 0;
RCSTAbits.CREN = 1;
}
if (PIR1bits.RCIF) {
if (RCSTAbits.FERR) {
unsigned char dummy = RCREG;
return;
}
unsigned char next = (head + 1) % BUFFER_SIZE;
if (next != tail) {
buffer[head] = RCREG;
head = next;
} else {
unsigned char dummy = RCREG;
}
}
}
まとめ
今回は、UARTの割り込み受信を実装し、
さらにリングバッファを使って複数文字の受信に対応しました。
最初は1文字ずつの受信だけを想定していましたが、
実際にはTera Termからの貼り付けやファイル送信のように、
短時間で複数文字が連続して送られてくることがあります。
そのため、受信したデータを一時的に保存するリングバッファが必要になります。
また、大量データが一気に流れた場合には、
OERRによってUSARTの受信が止まる可能性があるため、
CRENをクリアして復旧する処理も追加しました。
さらに、通信フォーマットが崩れた場合に発生するFERRについても確認し、
エラーが発生したデータはRCREGを読んで捨てるようにしました。
今回の内容で、UART受信処理はかなり実用的な形に近づいたと思います。
次回は、このUARTの知識をもとに、
いよいよDMX通信の入口に進んでいきます。


コメント