&size(20){&color(,#FFE9C8){''SIRCS,Propeller流''};}; //邦題タイトル &size(15){''原題:'SIRCS,Propeller Style!'}; //洋題タイトル ----- 状態:''この文書は翻訳済みです、間違ってたら修正して下さい'' 状態:''この文書の翻訳作業は完了しています'' //もしくは''この文書の翻訳作業は完了しています'' 原文:nvp4.pdf //原文へのリンクなど 翻訳者:caskaz //翻訳協力者のTwitterID,ハンドル名 例:@ksksue 対象読者:SIRCSしたい人 //誰向けの文書か簡単に。例:アセンブラ超初心者 #contents //自動的に目次が生成されます //最初のページ製作者は以下目次のタイトルを先に翻訳しておいてください。 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プロトコル [#p892ba0d] 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をデコード [#p892ba0d] Propellerを使って良い点は1個のCogでSIRCSをデコードできることだ。 だから準備ができていない時に入ってくる信号の事やユーザがキーを押すのを待つ間プログラム実行をブロックすることについて割り込みの事を考える必要がない (そもそもPropellerには割り込み自体存在しないが) DMX512プロジェクトでコードを簡単にする為に各Cog内で多目的タイマーを利用したことを覚えているだろう。 rxsircs mov ctra, NEG_DETECT mov frqa, #1 mov ctrb, FREE_RUN mov frqb, #1 waitok rdlong tmp1, okpntr wz if_nz jmp #waitok waitstart waitpeq irmask, irmask mov phsa, #0 waitpne irmask, irmask mov phsb, #0 waitpeq irmask, irmask cmp START_BIT, phsa wc if_nc jmp #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 irwork, #0 mov bits,#0 checkframe cmp MS_044, phsb wc if_c jmp #irdone waitbit test irmask, ina wz if_nz jmp #checkframe measurebit mov phsa, #0 waitpeq irmask, irmask cmp ONE_BIT, phsa wc rcr irwork, #1 add bits, #1 cmp bits, #20 wc if_b jmp #checkframe irdone mov tmp1, #32 sub tmp1, bits shr irwork, tmp1 report wrlong irwork, codepntr wrlong bits, bitspntr wrlong DONE, okpntr jmp #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” [#p892ba0d] 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を送信 [#p892ba0d] 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 or outa, ircath or dira, ircath mov frqa, modfreq mov ctra, modctrl waitcmd rdlong frcount, fcpntr wz if_z jmp #waitcmd rdlong bitcount, bcpntr rdlong code, irpntr 送信はフレーム数、ビット数、コードを持つHub変数を読み出してから始める。 WaitcmdでSIRCSフレーム数がHubから読み込まれゼロより大きかったら以降へ進みbit count,device/key codeを読む。そして、次のステップへ続く、 startframe mov bcount, txbitcount mov testmask, #1 mov frametimer, MS_45 add frametimer, cnt txstart mov bittimer, BIT_START call #txbit 普通1フレーム以上を送信するのでbitcountをコピーしてビット0をセットアップするmask(testmask)を作っておく。 フレームタイミングはframtimerと言う名の変数に45ms(システムカウンタのticks数)をロードしてそれに現在のシステムカウンタ数を加算してセットされる。 全部のビット送信が終わった後waitcntでこの値を使う。 SIRCSフレームの最初の要素は2.4msのスタートビットだ。これを処理する為にスタートビットのタイミングで変数bittimerをロードしてサブルーチンtxbitをコールする。 以前のプロジェクトでPASMコードはサブルーチンを使わなかった。しかし、それは有益だしサブルーチンを使ってみる良い機会だ。 txbit add bittimer, cnt andn outa, ircath waitcnt bittimer, #0 or outa, ircath txpad mov bittimer, BIT_PRO add bittimer, cnt waitcnt bittimer, #0 txbit_ret ret 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 test txcode, mask wz if_z mov bittimer, BIT_0 if_nz mov bittimer, BIT_1 call #txbit shl mask, #1 checkdone djnz 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 waitcnt frametimer, #0 djnz frames, #startframe txdone wrlong ZERO, fcpntr jmp #waitcmd 最後のステップはフレーム数を保存しているHub変数にゼロを書くことだ。これで送信が終了したことを呼出し側に伝える。Hub変数をアップデートしてwaitcmdに戻って別のSIRCS device/keyコードフレームの送信リクエストがあるまでそこで待つ。 これでPropellerを使ってSony SIRCSコードの送受信ができた。パッケージをダウンロードしたら僕のTVのチャンネルを変えるデモがあると思う。 “sniffer”プログラムでチャンネルup/downのキーコードを確認したくなるかもしれない。 ちょっと楽しいかい? 友達がぼーっとしている時、慎重に仕掛けをしてデモボードのIR LEDに信号を送ってTVを切ったり手当たり次第にチャンネル変えたり・・・。 エレクトロニクスが好きな連中は時々技術を使ったいたずらをするって誰がいったんだ? 僕だよ! 次回までにPropellerをぶん回して人気者になって楽しもう Jon Williams jwilliams@efx-tex.com Parallax,Inc www.parallax.com