8.パケット取得(タイマ2)
18bitのパケットを取得するルーチンを検討します。
8.1. RS232Cとの類似点 —調歩同期方式—
MMプロトコルは0と1のパルスが等間隔で出力されますが、これは一定のボーレートで通信するRS232Cと似た形式です。そこでRS232Cと同様なデータ取得法を考えてみます。前章のようにパルスの立ち上がりを検出し、パケットの先頭であれば、1/2周期だけ待ち、RA4(RXPIN)の状態を調べます。このときHであれば1、Lであれば0となります。次からは1周期ごとにRA4(RXPIN)の状態を17bit分調べればパケットを取得できることになります。
8.2. タイマ2を使った等間隔のタイミング発生
タイマ2を使うと一定周期で割り込み(TMR2IF)を発生させることができます。PR2レジスタとTMR2カウンタが一致すると割り込みが発生し、TMR2がクリアされます。したがって周期はタイマ2の条件とPR2の値で決ります。タイマ2はクロック8 MHz(タイマ2クロックは1/4になる)、プリスケーラ1/4の場合、1カウントが2 μsとなります。文献によれば、アクセサリデコーダの周期は104 μsなので、TMR2が52カウントで1周期になります。周期分に相当するカウント数は変数BAUDに入れることとし、PR2=BAUDを設定します。
8.3. 通信速度とタイミングの微調整
パケットの取得し始めは、なかなか忙しいです。
立ち上がりの検知
Lレベルの長さによるパケット取得の判定
変数の初期化
という、もろもろの処理を行った後、1/2周期待ってRXPINの状態を調べる訳です。これらの処理には当然、ある程度の時間がかかるので、その分の時間をxとして、1/2BAUD – x だけ待ってRXPINの状態変化を調べるのがベターかと思われます。そこでxの値を決めるため、実際にxを振りながら、パケットが正常に受信できる範囲を調べてみました。その結果はxが3から25の範囲になりました。この測定結果は他の処理に47μsかかっていることを示しています。1/2周期の真ん中に合わせるため1/2BAUD – xが3μsとなるよう、xは22としました。
一方、周期を表すパラメータBAUDは文献値によれば52のはずですが、この値では正常にパケットを受信することができませんでした。そこで、BAUDについても値を振って調べてみたところ、BAUD=49 ~ 51の範囲で正常に受信できることがわかりました。こちらは誤差が累積する性質のものですが、それにしては許容範囲が広いな、という印象です。この実験結果より、BAUD=50としました。
8.4. プログラミング
18bitのパケットをアドレス部のDataA、ファンクション部のDataF、データ部のDataDの三つに分けて受信するルーチンを考えます。タイマ2がオンの間もRXPINの状態変化はありますが、ここで割り込みが発生してしまうとややこしいことになるので、パケット受信中は状態変化割り込みを不許可とします。(IOCIE=0)また、逆になりますが、パケットを受信していない状態ではタイマ2を停止しています。
割り込み部
タイマ2割り込みが発生すると、BitCounterをステートとして、RXPINの状態に応じて下位ビットからデータを入力します。DataF部は2bitしかないのでループにはしませんでした。最後の18bit目(BitCounter:17)では次のパケット取得に向けて、タイマ1の準備を行います。
/*
*Interrupt by timer2
*Pulse duration is 100us, but 50us for the first pulse
*total:18bit consisting of 8bit(DetaA), 2bit(DetaF) and 8bit(DetaD)
*/
if(TMR2IF){
if(BitCounter == 0){ //the first bit?
if(RXPIN) //if High, bit:1, Low, bit 0
DataA |= BitPos; //Set Data
BitPos = BitPos << 1; //Shift bit position
BitCounter++; //Increment bit counter
PR2 = BAUD; //change timer2 period to 1 pulse width
}
else{
if((BitCounter < 8)&&(BitCounter >0)){ //for DetaA
if(RXPIN) //if High, bit:1, Low, bit 0
DataA |= BitPos; //Set Data
BitPos = BitPos << 1; //Shift bit position
BitCounter++; //Increment bit counter
}
else{
if(BitCounter == 8){ //for DetaF, upper bit
if(RXPIN) //if High, bit:1, Low, bit 0
DataF |= 0x01; //Set Data
BitCounter++; //Increment bit counter
}
else{
if(BitCounter == 9){ //for DetaF, lower bit
if(RXPIN) //if High, bit:1, Low, bit 0
DataF |= 0x02; //Set Data
BitPos = 0x01; //Set bit position to 1
BitCounter++; //Increment bit counter
}
else{
if((BitCounter > 9)&&(BitCounter < 17)){
//for DataD
if(RXPIN) //if High, bit:1, Low, bit 0
DataD |= BitPos; //Set Data
BitPos = BitPos << 1; //Shift bit position
BitCounter++; //Increment bit counter
}
else{
if(BitCounter == 17){ //for DataD
if(RXPIN) //if High, bit:1, Low, bit 0
DataD |= BitPos; //Set Data
BitPos=BitPos << 1; //Shift bit position
BitCounter++; //Increment bit counter
TMR1H=0;//タイマ1の準備
TMR1L=0;//
TMR1ON=1;//タイマ1オン
IOCAF4=0;//フラグのクリア
IOCIE=1;//状態割り込み許可
}
}
}
}
}
}
TMR2IF=0; //timer2 interrupt flag clear
}
関数Get_Packet()
18bitのパケットをDataA、DataF、DataDに分散して取得する関数。まずパラメータの初期化を行いますが、最初の周期としてPR2 = BAUD/2-14としているのは前節のとおり。タイマ2をオンにした後、BitCounterが18になるまで待機、割り込み処理によるパケット取得が完了したらタイマ2をオフにして戻ります。
void Get_Packet(void){
BitCounter = 0; //total 18bit
DataA = 0; //Upper 8bit
DataF = 0; //Middle 2bit
DataD = 0; //Lower 8bit
BitPos = 0x01; //Position of Bit
TMR2 = 0; //Initialize Timer2
PR2 = BAUD/2-14; //A half of pulse width for the first bit
/*14引いているのは処理によるタイマ2タイミングのずれの補正用
*実験では3から25の間で正常にパケットを取得できたので中間の14とした*/
T2CON=0x05; //Timer2:on, prescaler1/4
/**wait here for interrupt by timer2*/
while(BitCounter < 18); //until 18bit input
TMR2ON = 0; //timer2 off
}