SIRCS,Propeller流
''原題:'SIRCS,Propeller Style!'


状態:この文書の翻訳作業は完了しています

原文:nvp4.pdf

翻訳者:caskaz

対象読者:SIRCSしたい人

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		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”

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		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


トップ   編集 凍結解除 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2011-12-22 (木) 11:05:40 (2373d)