受信プログラムの続きを行っていきます。
受信フェイズの検討
送信処理プログラムを作成したとき、送信フェイズの検討を行いました。受信プログラム側も同様にフェイズを分類することにします。
フェイズ名 | 説明 | 受信を期待する信号と回数 |
STOP | データ受信をストップ中 | 無し |
WAIT_START | 通信の開始(リーダ部の受信)を待っている | リーダ部の信号を1回 |
ADDRESS | ADRESS部を受信中 | ビットデータ信号を4回 |
DATA | DATA部を受信中 | ビットデータ信号を8回 |
CHECKSUM | CHECKSUM部を受信中 | ビットデータ信号を4回 |
ERROR | 受信エラー(不信信号を受信した) | なし |
プログラムの最初のフェイズはSTOPですが、TMR2などの初期設定が終わり、信号を受信する準備が整い次第WAIT_STARTに移行します。
WAIT_STARTではリーダ部の信号(パルスONが8T、OFFが4Tとなる信号)を受信するまで待ち続けます。そしてリーダ部の信号を受信したらADDRESSに移行します。
ADDRESSでは送信データのADDRESS部の信号を受信中であることを表します。ここではビットデータ信号(「パルスONが1T、OFFが1T」と「パルスONが1T、OFFが3T」の2種類あります。)を4回受信したらDATAに移行します。ただし、途中でパルスON/OFFの時間が2種類以外の状態になれば、「不正信号を受信した」と判断し、ERRORに移行します。
DATAやCHECKSUMでも同様に、送信データのDATA部やCHECKSUM部の信号を受信中であることを表します。ここでもビットデータ信号を適切な回数受信したら次の処理に移行し、途中で異なる信号を受信したらERRORに移行します。
またADDRESSとDATAでは、受信した信号に応じて、アドレス値やデータ値を保持します。CHECKSUMでは、アドレス値や受信データのチェックを行います。
ERRORでは、それまで受信していたデータを全て破棄し、WAIT_STARTに戻ります。
これらのフェイズはenum値で定義して、受信フェイズとして保持することにします。
/**
* 受信フェイズを示すenum型
*/
typedef enum
{
IRR_PHASE_STOP, // データ受信をストップ中
IRR_PHASE_WAIT_START, // 通信の開始(Leader部の受信)を待っている
IRR_PHASE_ADDRESS, // ADDRESS部を受信中
IRR_PHASE_DATA, // DATA部を受信中
IRR_PHASE_CHECKSUM, // CHECKSUM部を受信中
IRR_PHASE_ERROR, // 受信エラー(不信信号を受信した)
} IRR_PHASE_t;
// 受信フェイズ
static IRR_PHASE_t IRR_phase;
ビットデータ受信処理
ビットデータの判定
ビットデータの信号は2種類あります。1つはビットデータ0に対する信号で「パルスONが1T、OFFが1T」です。これはusec単位で表すと「Onが400usec、Offが400usec」です。
以前に「PICによる赤外線通信5 送信と受信のタイミングの違い」のところで説明したように、
「Lowの時間が(N-300)usec以上、かつLow+Highの時間が(M±200)usec」の信号を受け取った場合、「パルスONがN usec、パルスON+OFFがM usec」の信号を受信したと判定する。
として判定を行います。このNに400、Mに800を入れて整理すると、
Lowの時間が100usec以上、かつLow+Highの時間が600~1000)usec」の信号を受け取った場合、ビットデータ0の信号を受信したと判定する。
となります。
ビットデータ1に対する信号は「パルスONが1T、OFFが3T」なので、同様に条件を整理すると、
Lowの時間が100usec以上、かつLow+Highの時間が1400~1800)usec」の信号を受け取った場合、ビットデータ1の信号を受信したと判定する。
となります。
ON/Offの時間が上の2つの条件に当てはまらない場合は、不正信号を受信したことになります。
ビットデータ受信処理の共用関数
ビットデータを受信するのは、ADDRESS、DATA、CHECKSUMの3つのフェイズです。
ADDRESSでは4回のビットデータを受信し、それを4bit値に変換します。同様に、DATAでは8回のビットデータを受信して8bit値に変換しますし、CHECKSUMでも4bit値に変換します。これらは処理を共用化できるので関数化します。
下にコードを示します。
// 受信したデータをIRR_bufferに受け取る際のカウンタ
static uint8_t IRR_bitcounter;
// 受信したデータを最大8bit分受け取る為のバッファ
static IRR_DATA_t IRR_buffer;
IRR_bitcounterは受信するビットデータの数をカウントするための変数です。
ADDRESS、CHECKSUMでは4回、DATAでは8回のビットデータを受信するので受信回数が異なります。そこで、IRR_bitcounterに受信する予定の回数をセットし、ビットデータを受け取るごとにカウントダウンします。0になれば、そのフェイズで必要な回数を受信したことになり、次のフェイズに進みます。
IRR_bufferは、受信したビットデータを4bit値や8bit値に変換するための途中の値を保持するための変数です。
なお、IRR_DATA_t型は、送信処理のときのIRT_NextSignalの説明で出てきたIRT_DATA_t型とほぼ同じようなunion型なので詳細な説明は割愛します。
/**
* Adress部などビットデータに対する受信を行い、IRR_bufferに受け取る。
*
* 1ビット分のデータを受信する毎に、以下を繰り返す。
* IRR_bufferを右シフトする。
* IRR_bufferの最上位ビットに受信データ(0/1)を格納する。
* IRR_bitcounterをカウントダウンする。
* そして、IRR_bitcounterが0になったらtrueを返す。
*/
static __bit IRR_ReceiveBuffer(void)
{
// OnとOffの長さをチェックして、受信データを決定する。
// - Onが100usec以上 かつ On+Offが600~1000usecなら 0を受信
// - Onが100usec以上 かつ On+Offが1400~1800usecなら 1を受信
// - 上記以外なら、不正データを受信
if (IRR_OnTime < IRR_PULSE_WIDTH(100))
{
// On時間を満たさなかった場合は、不正データの受信としてERRORにする。
IRR_phase = IRR_PHASE_ERROR;
return false;
}
if (IRR_PULSE_WIDTH(600) <= IRR_BothTime && IRR_BothTime <= IRR_PULSE_WIDTH(1000))
{
// 0 を受信したので、IRR_bufferを右シフトして、最上位bitを0にする。
IRR_buffer.A8 >>= 1;
// 右シフトで自動的に0になるので、IRR_buffer.B7 = 0; は不要。
// カウンターを減らし、0になったらtrueを返す。
return (--IRR_bitcounter == 0);
}
else if (IRR_PULSE_WIDTH(1400) <= IRR_BothTime && IRR_BothTime <= IRR_PULSE_WIDTH(1800))
{
// 1 を受信したので、IRR_bufferを右シフトして、最上位bitを1にする。
IRR_buffer.A8 >>= 1;
IRR_buffer.B7 = 1;
// カウンターを減らし、0になったらtrueを返す。
return (--IRR_bitcounter == 0);
}
else
{
// Off時間を満たさなかった場合は、不正データの受信としてERRORにする。
IRR_phase = IRR_PHASE_ERROR;
return false;
}
}
受信した信号の判定と、上記2つの変数の管理を関数IRR_ReceiveBuffer()の中で行います。
ONおよびON+OFFの時間を判定し、ビットデータとして一致しない場合はフェイズをERRORにしています。
一致する場合は、対応するデータをIRR_bufferにセットし、IRR_bitcounterをカウントダウンします。
IRR_bufferは「右シフト→最上位ビットに0 or 1をセット」を繰り返していきます。フェイズがADDRESSのときはこれを4回繰り返すので、IRR_bufferの上位4bitにアドレス値が入ります。フェーズがDATAのときは8回繰り返すので、IRR_buffer全体で8bitのデータ値が入ります。
前述したように、IRR_bitcounterが0になったら「次のフェイズ」に移動する必要がありますが、それはこの関数の中では行いません。この関数をコールした側(後述のIRR_ReceiveRun()の中)で行います。ただ、0になったことを判定しやすくするためにtrueを返します。IRR_bitcounterが0でないときはfalseを返します。
赤外線受信処理のメイン部分
赤外線受信処理のメイン部分IRR_ReceiveRun()のコードを以下に示します。
/**
* 赤外線受信処理のメイン処理
*
* OnとOffを受信し、それぞれの時間を計測できた後でコールされる。
*/
void IRR_ReceiveRun(void)
{
switch (IRR_phase)
{
case IRR_PHASE_WAIT_START:
// Leader部の受信を待っていたので、OnとOffの長さをチェックする。
// On時間が2900usec以上、On+Off時間が4800usec±200usecなら
// OK(Leader部を受信できた)と判定する。
if (IRR_OnTime >= IRR_PULSE_WIDTH(2900)
&& IRR_PULSE_WIDTH(4600) <= IRR_BothTime && IRR_BothTime <= IRR_PULSE_WIDTH(5000))
{
// 次のフェイズ(Address部の受信)に進む。
IRR_phase = IRR_PHASE_ADDRESS;
// Address部はIRR_ReceiveBuffer()を使って処理を行うので、bitcounterをセットする。
// Address部は4bitなので、4をセット
IRR_bitcounter = 4;
}
// On/Off時間が満たさなかった場合は、Leader部を受け取っていないので、
// Leader部が来るのを待ち続ける。
// 状態は変化しない。
break;
case IRR_PHASE_ADDRESS:
if (IRR_ReceiveBuffer())
{
// 4bit分のデータをIRR_bufferに格納できたので、IRR_addressに格納する。
// ただし、データは IRR_bufferの上位4bitに格納されているので、シフトする必要がある。
IRR_address.A8 = IRR_buffer.H4;
// 次のフェイズ(Data部の受信)に進む。
IRR_phase = IRR_PHASE_DATA;
// Data部は8bitなので、8をセット
IRR_bitcounter = 8;
}
break;
case IRR_PHASE_DATA:
if (IRR_ReceiveBuffer())
{
// 8bit分のデータを格納できたので、dataにセットする。
IRR_data = IRR_buffer;
// 次のフェイズ(CheckSum部の受信)に進む。
IRR_phase = IRR_PHASE_CHECKSUM;
// CheckSum部は4bitなので、4をセット
IRR_bitcounter = 4;
}
break;
case IRR_PHASE_CHECKSUM:
if (IRR_ReceiveBuffer())
{
// 4bit分のデータを格納できたので、チェックサムを照合する。
// addressとdataからチェックサムを算出
// 本来なら、IRR_data.H4 + IRR_data.L4 + IRR_address.L4とするべきだが、
// checksumの上位4bitは使用しないので、下の式で問題ない。
// IRR_data.L4を使うと上位4bitを マスクする処理が入ってしまい冗長になる。
// IRR_address.L4も同様。
uint8_t checksum = (IRR_data.H4 + IRR_data.A8 + IRR_address.A8) & 0x0F;
// checksumが、IRR_bufferの上位4bitと一致すれば、照合OK
if (IRR_buffer.H4 == checksum)
{
// 正常にデータ受信したので、STOPにする。
IRR_phase = IRR_PHASE_STOP;
// データ受信が完了したイベントを発生
EventRise(Event_IRR_DataIsReceived)
}
else
{
// 異常データを受信したのでERRORにする。
IRR_phase = IRR_PHASE_ERROR;
}
}
break;
default:
// その他の条件(STOP/ERROR)では何もしない。
break;
}
// 結果がどうあれ、計測した時間をクリアする。
IRR_OnTime = 0;
IRR_BothTime = 0;
if (IRR_phase == IRR_PHASE_ERROR)
{
// ERRORになった場合は、受信処理を再開する。
IRR_ReceiveStart();
}
}
フェイズに応じて受信する信号や処理内容が変化するため、最初にswitch(IRR_phase)で処理を分岐しています。
フェイズがWAIT_STARTのときは、リーダ部の信号であるか判定し、一致すればフェイズをADDRESSに移行します。このとき、IRR_bitcounterもセットします。(ADDRESSでは4回のビットデータを受信するので4をセット。)
一致しない場合は、何もしません。
フェイズがADDRESSときは、前述したIRR_ReceiveBuffer()を使って処理します。
そしてIRR_ReceiveBuffer()がtrueを返したなら、IRR_bufferの上位4bitをアドレス値としてIRR_addressに保存します。そして次のフェイズ(DATA)に移行します。また次のフェイズに適切な値をIRR_bitcounterをセットします。
同様に、フェイズがDATAときもIRR_ReceiveBuffer()を使って処理、trueを返した場合は、IRR_bufferの値をIRR_dataに保存して、次のフェイズに移行します。
フェイズがCHECKSUMの時も同様ですが、CHECKSUMでは受信データの照合チェックを行います。そのチェックに失敗したら、(通信処理全体として)不正な信号受信と見なしフェイズをERRORにします。
チェックに成功したらフェイズをSTOPにし、データ受信が完了したイベントを発生します。
swicth文の後、次の判定のためにIRR_OnTimeとIRR_BothTimeを0クリアします。またフェイズがERRORになっている場合は、ここで受信処理を再開します。
IRR_addressやIRR_dataは下記のように宣言されています。
// 赤外線受信のアドレス
static IRR_DATA_t IRR_address;
// 受信したData部のデータ
static IRR_DATA_t IRR_data;
赤外線受信開始処理
受信処理を開始する時や、エラーから復帰する時には、以下のIRR_ReceiveStart()をコールします。
/**
* 赤外線受信を開始する
*
* STOPからの再受信やERRORから復帰するときに、この関数を実行する。
*
* すでに信号受信処理を進めている時にコールすると、それまでの受信データが破棄される。
*/
void IRR_ReceiveStart(void)
{
// ビットデータ受信処理で使用するメモリ
IRR_buffer.A8 = 0;
IRR_bitcounter = 0;
// 受信データ
IRR_address.A8 = 0;
IRR_data.A8 = 0;
// フェイズをSTARTにする
IRR_phase = IRR_PHASE_WAIT_START;
}
使用する変数や、受信結果を格納するための変数を0クリアし、フェイズをWAIT_STARTにしているだけです。
コメント