2008/11/10
PPMでパルス幅が測定できたので、同じ回路で送信機のモードをPCMにして
トレーナー信号のデータを採ってみました。送信機はJR製PCM9Xです。
これをデコードして、サーボ駆動パルスを作ってみようと思います。
参考にしたのは、SmartPropoPlusのソースコードです。
SmartPropoPlusのページはこちらです。
ここにはJRのPCM信号デコードして、8チャネル分の10bitデータを
取り出すアルゴリズムが記載されています。
SmartPropoPlusのソースコードによると、
JRのPCM信号は166usecという基本幅の整数倍のパルスで
構成されていると書かれています。
ただ、syncパルスだけは166[usec]の2.5倍の415[usec]のパルス幅と
規定されています。
PPM信号と同様にデータを採ってみると、syncパルスを見分けることができました。
送信機のトレーナー信号から取り出したデータを、時間単位に変換するために
16を掛け算します。
(PICは16[usec]クロックのタイマーでパルスの時間を測定しているため。)
この値が415[usec]に近いところがsyncパルスになります。
次は、デコードの第一段階です。
JRのPCMのRFの基本単位は166[usec]なので、パルス時間を
166で割って四捨五入しておきます。これがG列になります。
基本単位である166[usec]がいくつ分あるかを示します。
PIC用のコードは処理時間を短くするため、計算式を少し変えています。
オリジナルのコードは以下のように計算しています。
#define PW_JR 7.340
width = (int)floor((double)width / PW_JR + 0.5);
PCのオーディオI/Fのサンプリング周期44kHzが約22.7usecなので、
166usecを22.7usecで割ると、だいたい7.3となっています。
PIC用には、以下のようにしました。
tmp = pulse_width + 4;
width = tmp / 10;
PICの場合はpulse_widthにTMR0(16usecクロック)で数えた数値が入っています。
従ってこの時点では16usecがいくつあるかという時間情報になっています。
やっとTMR0のクロックを16usecに設定した意味がでてきました。
やりたいことは、以下の式を簡単にすることです。
int(pulse_width x 16usec / 166usec)
166で割るところを160という近似値を使うと、
int(pulse_width x 16usec / 160usec)
= int(pulse_width / 10)
となります。
四捨五入するには1/10にするまえに、割られる数に5を足しておけばよいです。
今回は割る数を166から160にしていますので、若干補正をするために4を足すように
しました。
ここからが少し面倒です。
第二段階は、第一段階で得た数値をビットストリームに変換します。
SmartPropoPlusのソースコードを日本語にしてみると、以下のようになります。
1)まず、初期値として変数を用意し、0を入れておきます。
2)この変数を1ビット左シフトし(0を1ビット左シフトするので、結果は0)
3)最下位ビットに1を設定します。
4)第一段階で得た数値の分だけ、この変数を左シフトします。
5)変数を1ビット左シフトします。
6) 3)に戻ります。
式で書くと以下のように一行なのですが。
bitstream = ((bitstream << 1) + 1) << (width - 1);
第三段階は、ビットストリームから有効データへの変換です。
これは、第二段階と平行して行ないます。
JRの送信機はエンコード時に5bitのデータを -> 8bit に変換しているので、
デコードではこの逆の変換を行ないます。
大まかな流れです。
ビットストリームが8ビット以上たまったら、最上位から8ビットを
取り出し、8bit -> 5bitの変換テーブルで 5bitのデータに変換します。
使った8bitを除いた残りのビットストリームに対して、
第二段階のビットストリーム生成を繰り返し、
再度8bit以上たまったらビットストリームの最上位
ビットから8bitを取り出し、8bit -> 5bit変換を繰り返します。
これらを図にすると、以下のようになります。
smartpropoplusのコードによると、取り出した5bitデータの中に
各チャネルデータが以下のように埋め込まれています。
従って、0から1023までの値を取ることが可能です。
Position[2] = 1023 - ((data[1] << 5) | data[2]);
Position[0] = 1023 - ((data[4] << 5) | data[5]);
Position[5] = 1023 - ((data[9] << 5) | data[10]);
Position[7] = 1023 - ((data[12] << 5) | data[13]);
Position[3] = 1023 - ((data[16] << 5) | data[17]);
Position[1] = 1023 - ((data[19] << 5) | data[20]);
Position[4] = 1023 - ((data[24] << 5) | data[25]);
Position[6] = 1023 - ((data[27] << 5) | data[28]);
PCで取り込んだデータをperlでデコードした結果です。
perlスクリプト
こちらがExcelファイルになります。
1チャンネルだけ動作するように送信機の操作を行ない、
そのときのトレーナー信号をデコードして、表にしました。
Excelファイル
この表のデータを採ったときは、エルロンの操作をしていました。
その様子がデータで確認することができます。
ここまでできたので、今度はデコード結果を使ってサーボを動かしてみます。
エルロンチャンネルのデコードして得られた値を、サーボ駆動信号に変換します。
デコード結果は、0-1023が理論上の最小最大になりますが、
実験結果では最小値が166、最大値は860でした。
PICのTMR1を2usecクロックで動かしてサーボ駆動信号のパルス幅を制御するようにします。
サーボ駆動信号は、センターの時に約1.5msec、最大角時に約2msec、
最小角時に約1msecとします。
デコード結果を2で割ってから500を足すと、だいたい良い感じになりそうです。
RB4にHIGH出力させるときにTMR1に値を設定し、オーバーフロー時の割り込みで
RB4をLOW出力とします。
実験風景
うまく動きました。PCMモードの送信機のスティックに合わせて
サーボが動作しています。
MediaFireというサーバーにファイルを置きました。
mp4形式で、約4MByteあります。
動画ファイル(pcm-decode.mp4)
MediaFireのページに飛ぶと、
”Click here to start download..”と左側に表示されますので、ここを
クリックしてファイルをダウンロードしてください。