SIRCS, Propeller Style!

THE SPIN ZONE
SIRCS,Propeller スタイル!
By Jon Williams
Nuts & Volts Magazine, コラム#4 2010 年 1 月
僕にはカメラの pan/tilt 制御をする周辺機器の設計をしている Lou という友人がいる。
Lou と SX48 を使って製品の為の電子回路を設計したんだ。
SPIN は SX/B より使い易いと考えていたので僕は次世代用の機器で Propeller の使用を Lou
に提案してみた。
Lou はすぐに採用を決めて簡単な TV リモートで pan/tilt 制御をできるか僕に聞いてきた。
「オーケー、できるよ」と叫んでしまったんだ、でも本当はこのときは Propeler でどう
やるか知らなかったんだ。でも実現するのにそんなに時間はかからなかったよ。
(大体1、2 時間ぐらい)
Propeller で SIRCS の送受信ができてよかった。
SIRCS プロトコル
SIRCS(Sony IR Control System)プロトコルを知らない人の為に説明しよう。
SIRCS とは IR(赤外線)で送信されるパルス幅変調信号のことだ。これは市販の電子機器
(TV,VCR,DVD player,etc)で利用されている。
SIRCS システムの受信側の復変調素子は Figure1 の様に active-L の信号を出力する。
SIRCS 信号列はスタートビットが 2.4ms のパルス幅で、これは他の信号ビットと同様に
0.6ms の pad(H-pulse)で分離されている。データビット列は LSB が最初に送信され、”1”
は 1.2ms、”0”は 0.6ms のパルス幅だ。
信号の全体(フレームと呼ばれる)は 45ms 以内に送信されている。ほとんどのリモート
コントローラは受信を確実にする為に最低 3 回 SIRCS コードを送信しているようだ。
でも僕の Sony カメラはコードを 5 回送信していたよ。
違う機器には違う長さのコードが使われている。
僕の経験では TV コードは 12 ビットだけれど DVD プレーやのような高機能機器は 20 ビット
コードになってるみたいだ。
IR コードは二つの要素に分かれる:デバイスコードとキーコードだ。
12 ビットシステムではデバイスコードは5ビット、キーコードは7ビットだ。
20 ビットシステムではそれぞれ8ビット、12ビットになっている。
キーコードが 20 ビットシステムで大きくなってるのはこのタイプの市販機器がさらに増
加すると考えられる。
Propeller で SIRCS をデコード
Propeller を使って良い点は 1 個の Cog で SIRCS をデコードできることだ。
だから準備ができていない時に入ってくる信号の事やユーザがキーを押すのを待つ間プロ
グラム実行をブロックすることについて割り込みの事を考える必要がない
(そもそも Propeller には割り込み自体存在しないが)
DMX512 プロジェクトでコードを簡単にする為に各 Cog 内で多目的タイマーを利用したこと
を覚えているだろう。
rxsircs
waitok
waitstart
mov
mov
mov
mov
rdlong
if_nz jmp
waitpeq
mov
waitpne
mov
waitpeq
cmp
if_nc jmp
ctra, NEG_DETECT
frqa, #1
ctrb, FREE_RUN
frqb, #1
tmp1, okpntr wz
#waitok
irmask, irmask
phsa, #0
irmask, irmask
phsb, #0
irmask, irmask
START_BIT, phsa wc
#waitstart
上記は 2 個のカウンターを使用している。CounterA は入力パルス巾を測定するために
negative detect モードで、counterB はフレームタイミングを追跡する為に free-run モー
ドでセットアップしている。プログラムはラベル waitok の箇所で Hub から okpntr を読ん
で、この okpntr で SIRCS デコーダを必要な時に使用できるようにしている。
z フラグが”1”の時、プログラム実行は waitstart へと進む。
君の推測どうり、ここで 2.4ms のスタートビットを待つ。
この部分の最初で waitpeq を使って入力ピンをマスクして IR 入力ピンが high になるのを
待つ。全部のビット巾測定が確実にできるように入力ピンのラインが high の時にこの処
理をスタートする必要がある。(不正確な測定をしない為に)
入力ピンのラインが high の時に PHSA をクリアし waitpne でラインが low になるのを待つ。
ラインが low になった時 PHSB をクリアするのはなぜか?
この時にスタートビットの立ち下がりを検出したのでフレーム期間を測定する PHSB レジ
スタをクリア(初期化)する必要がある。
もう一度ラインが high に戻ったら cmp でビット巾をテストして、もしそれが有効なスター
トビットなら以降に進み、無効なら waitstart へ戻って次のビット巾をテストする。
スタートビットを検出したら次のステップは device/key コードを構成する全ビットを収
集することだ。
mov
mov
checkframe
cmp
if_c jmp
waitbit
test
if_nz jmp
measurebit
mov
waitpeq
cmp
rcr
add
cmp
if_b jmp
irdone
mov
sub
shr
report
wrlong
wrlong
wrlong
jmp
irwork, #0
bits,#0
MS_044, phsb wc
#irdone
irmask, ina wz
#checkframe
phsa, #0
irmask, irmask
ONE_BIT, phsa wc
irwork, #1
bits, #1
bits, #20 wc
#checkframe
tmp1, #32
tmp1, bits
irwork, tmp1
irwork, codepntr
bits, bitspntr
DONE, okpntr
#waitok
デコーダプログラムはそれが何番目のデータかわからないので、受信ビット数と同様に受
信コード列(irwork)も保持する。
これらの値は呼出し側で使用される。
妙な配置の様に思えるが、ビット受信ループのトップ(checkframe)のフレームタイマー
(PHSB)でフレームが終了したかどうかをテストしている。
僕は 45ms フレームと思われる期間をテストするのに 44mseconds を使ってる。
これは 20 ビットコードのビット全部が”1”だとしても送信のスタートビットと他のビッ
トの合計時間が 39msenconds ほどなので大丈夫だ。
もしフレームがまだアクティブなら新しいビット(入力ラインは low)をテストする。入
力ラインが high なら正しく処理を終了させる為に checkframe へ戻らなければならない。
一旦入力ラインが low になったら新しいビットの為に PHSA をクリアしラインが high にな
るのを待つだけだ。
新しく得たビット巾をビット”1”の時間と比較して結果をキャリーフラグに書き込む。
このキャリービットは rcr(rotate carry right)で irwork にシフトされる。ビット数保存
の為にビット数をカウントして 20 ビット以下なら(if_b = if_c)checkframe へジャンプす
る。
20 ビット受信するかフレームタイマーが 44mseconds になったらプログラムは irdone へと
進み、結果を得る。ビット列は LSB から始まるがそれを irdone に左から右の方にシフト
するので結果として irdone には MSB が左となるように整頓されている。
ビット数を 32(Long)から減算することにより入力ビット列の結果を得る。
一つのコマンドでどんなビット数の値でもシフトできるのが PASM で大好きなところだ。
最後に irwork にある値と受信ビット数は Hub に書き戻され、フラグをイネーブルにする。
フラグが false にセットされると呼出し側が true にセットするまでコードは実行されな
い。
SIRCS”Sniffer”
1 年前 SX ベースの SIRCS"Sniffer”プログラムを作成して、僕の家中の色々な Sony リモー
トのコードを片っ端から測定した。−今度はこれを Propeller で作成してみた。
コードは本当に簡単だ。
上記の SIRCS 受信オブジェクトに検出した SIRCS コードをターミナル(Parallax Serial
Terminal)に送信するシリアル出力オブジェクトを追加する。
コードは下記
PUB main | code, bc
ir.init(0)
term.init(30, 115_200)
waitcnt(clkfreq / 1_000 + cnt)
term.str(string(CLS, “SIRCS Sniffer”, CR, CR))
repeat
code := ir.getir
bc := ir.bitcount
case bc
12:
term.str(string(“12 :: “))
term.bin(code >> 7, 5)
term.tx(“.”)
Term.bin(code, 7)
20:
term.str(string(“20 :: “))
term.bin(code >> 12, 8)
term.tx(“.”)
Term.bin(code, 12)
other:
if bc < 10
term.tx(“0”)
term.dec(bc)
term.bin(string(“ :: “))
Term.bin(code, 32)
term.tx(CR)
waitcnt(clkfreq / 4 + cnt)
上記のメインループで IR オブジェクトの.getir メソッドを呼び出している。このメソッ
ドは SIRCS 受信をイネーブルにして入力ビット列を待って結果を戻す。
ここで有効なビット数を得る為に.bitcount()メソッドを使う。
ビット列は画面で見えるようにしたいので出力部はビット数とそれからデバイスコードと
キーコードを分けて表示している。
おかしなビット数を受け取ったら(一度もなかったが)case 文で例外処理を行っている。
ループの最後で少し遅延させているのは送信バッファのオーバフローを防止している。
このプログラムのハードウェアは簡単だ
Figure2 は以前に使った PN4602N デコーダーだ。
2.2k の抵抗を付けて Propeller の I/O ピン保護ダイオードに流れる電流を制限している。
(このダイオードはデコーダー出力が 5V の時に働く)
PropellerDemoBoard に接続したのが Figure3 で Figur4 はデバイスコードとキーコードを
出力しているターミナル画面(PST)だ。
上の 2 つは TV/DVD のチャンネルの up/down で下の 2 つはボリュームの up/down のコードを
表示している。
君も色々実験したら、SIRCS を使っている同じブランドの製品だとしても多種多様なので
キーコードが同じでも固有のデバイスコードを持っているのがわかるだろう。
これを拡張したら、特定のブランドのリモートに対応したプログラムを作ることができる
よ。
Propeller で SIRCS を送信
Sony DSLR を制御するには SIRCS の送信をするプログラム必要だ。
SX を使って挑戦したことは Propeller では簡単なことだ。特に IR LED の変調周波数の処
理にカウンターモードを使えるのが利点だ。
Figur5 は Propeller と IR LED の接続図だ。アノードを 3.3V に繋いでカソード側でコント
ロールと変調を行うことに注目してほしい。SX では 2 本のピンを使ってたことを覚えてい
るかもしれない。
なぜ Propeller では 1 本なのか?何が変わったのか?
Propeler のアーキテクチャでは出力ピンを high にできる素子が Cog 内でワイヤード OR さ
れ、さらに他の Cog の信号とワイヤード OR されて出力段に繋がっている。
各 cog は出力ピンを high にすることができる4つの素子(出力レジスタ、カウンター A,
カウンター B、ビデオジェネレータ)を持っている。
カソードピンに対してカウンターを NCO モードにセットすることで LED に変調信号を出力
でき、そのカソードピンに”1”を書き込むことによっていつでも信号をディセーブルで
きる。こうするとカソードを high にすると LED もオフし、カソードを low にするとカウン
ターからの変調信号は LED をドライブする。
コード解析の前に一仕事あるんだ。カウンターを NCO モードで使う時、希望する出力周波
数を出力するために FRQx レジスタに値をセットしておかなければいけない。
NCO モードで FRQx 設定値の公式は以下:
FRQx =Hz X 2^32 / System Frequency
僕は Propeller プロジェクトを 80MHz で作っているので IR リモートで使う変調周波数をご
く一般的な値にした場合の定数を求めた。
オーケー、そうしよう。最初に IR カソードピンを出力にセットして LED をオフにするた
めに high を出力する。それから、カウンターを設定して変調信号を生成する。
txsircs
waitcmd
or
or
mov
mov
rdlong
if_z jmp
rdlong
rdlong
outa, ircath
dira, ircath
frqa, modfreq
ctra, modctrl
frcount, fcpntr wz
#waitcmd
bitcount, bcpntr
code, irpntr
送信はフレーム数、ビット数、コードを持つ Hub 変数を読み出してから始める。
Waitcmd で SIRCS フレーム数が Hub から読み込まれゼロより大きかったら以降へ進み bit
count,device/key code を読む。そして、次のステップへ続く、
startframe mov
mov
mov
add
txstart
mov
call
bcount, txbitcount
testmask, #1
frametimer, MS_45
frametimer, cnt
bittimer, BIT_START
#txbit
普通1フレーム以上を送信するので bitcount をコピーしてビット0をセットアップする
mask(testmask)を作っておく。
フレームタイミングは framtimer と言う名の変数に 45ms(システムカウンタの ticks 数)
をロードしてそれに現在のシステムカウンタ数を加算してセットされる。
全部のビット送信が終わった後 waitcnt でこの値を使う。
SIRCS フレームの最初の要素は 2.4ms のスタートビットだ。これを処理する為にスタート
ビットのタイミングで変数 bittimer をロードしてサブルーチン txbit をコールする。
以前のプロジェクトで PASM コードはサブルーチンを使わなかった。しかし、それは有益
だしサブルーチンを使ってみる良い機会だ。
txbit
txpad
txbit_ret
add
andn
waitcnt
or
mov
add
waitcnt
ret
bittimer, cnt
outa, ircath
bittimer, #0
outa, ircath
bittimer, BIT_PRO
bittimer, cnt
bittimer, #0
bittimer をシステムカウンタ(cnt)に同期させてカソードピンを low にすることで LED を
イネーブルにする。そして、タイマが停止するのを待つ。タイマが停止した時 LED がオフ
し txpad でビットの間に 0.6ms の pad を挿入する。
最後に重要な事をひとつ話そう。サブルーチンの最後の行で txbit_ret があるのに気がつ
いてると思う。これは特別なラベルだ。
Propeller のアーキテクチャとアセンブラでは PASM サブルーチンの最終行は_ret の付いた
サブルーチン名のラベルが必要なんだ。
サブルーチンのエントリーが複数あったらどのようにしたらいいのか?
問題ない。下記のように複数行に_ret ラベルを付ける事ができる。
name1_ret
name2_ret
ret
ret 文は最後の行のみに置けることに注意してくれ。
オーケー、スタートビットの送信が終わったら device/key コードを送信できる
これは bcount で制御する簡単なループで処理する。
txcmd
checkdone
test
if_z mov
if_nz mov
call
shl
djnz
txcode, mask wz
bittimer, BIT_0
bittimer, BIT_1
#txbit
mask, #1
bcount, #txcmd
PASM で好きなところは色々あるけれどその中の一つが条件文だ。最初の txcmd は test を
使って現在の bit の状態を調べている。test は and と同じ動作だけれど変数自身は変化さ
せない(ここでは txcode)
test の結果で z フラグが変化する。
ビットがゼロの時、z フラグは true となりビットがゼロでない時、z フラグは false とな
る。
次の 2 行は条件文で bittimer を特定の値に設定している。if_z なら bittimer に”bit 0”
の時のタイミングをコピーする。
ここで”bit 1”の時この行はどうなるのか不思議に思う人がいるかもしれない。
条件に逢わない時これは nop に置き換えられるので何もしない。
Txbit をコールする場所のタイミングで、次のビットの為に mask を1ビット左にシフトし、
そして最後に bit count をアップデートする。
もし、送信ビットが残っていれば txcmd へジャンプする。
全ビットが送信されたら waitframe 以降の 45ms のフレームが終了したかどうかをチェッ
クする。
waitframe
txdone
waitcnt
djnz
wrlong
jmp
frametimer, #0
frames, #startframe
ZERO, fcpntr
#waitcmd
最後のステップはフレーム数を保存している Hub 変数にゼロを書くことだ。これで送信が
終了したことを呼出し側に伝える。Hub 変数をアップデートして waitcmd に戻って別の
SIRCS device/key コードフレームの送信リクエストがあるまでそこで待つ。
これで Propeller を使って Sony SIRCS コードの送受信ができた。パッケージをダウンロー
ドしたら僕の TV のチャンネルを変えるデモがあると思う。
“sniffer”プログラムでチャンネル up/down のキーコードを確認したくなるかもしれな
い。
ちょっと楽しいかい?
友達がぼーっとしている時、慎重に仕掛けをしてデモボードの IR LED に信号を送って TV
を切ったり手当たり次第にチャンネル変えたり・・・。
エレクトロニクスが好きな連中は時々技術を使ったギャグを仕掛けると誰がいったんだ?
僕だよ!
次回までに Propeller をぶん回して人気者になって楽しもう
Jon Williams
[email protected]
Parallax,Inc
www.parallax.com