''Spin 愛しのSpin' 状態:この文書の翻訳作業は完了しています 原文:NV134.pdf 翻訳者:caskaz 対象読者:Spin言語についての初歩知識 今月はPropellerマルチコントローラの紹介をしよう。 先月述べたように実際僕はアセンブラ言語に熟練するような仕事をしていないし Propellerを使えば僕でも(君にも)アセンブラを習得せずに複雑なマルチプロセッサのプロジェクトを作れるようになる。なぜそれをしたいのか? リアルタイムでデータ表示処理を行うプログラムが表側で動作し、Cogの一つがその裏側でセンサのモニタを行うようにPropellerが働いている、なんてクールだろう。 ポテンショメータの値を読む為にBASIC StampのRCTIME文を真似たrctimeと言う名のオブジェクトを作ろう。簡単だろう? 雑談はもう十分だ、始めよう。アーカイブを解凍してrctime.spinファイルをオープンしよう。これのオリジナルはBeau Schwabeが作ったプログラムだ。 PUB rctime(pin, state, zofs, div, rcAddr) | rc_temp if lookdown(pin : 0..27) state := 0 #> state <#1 zofs #>= 0 div #>= 1 repeat rc_temp~ outa[pin] := state dira[pin]~ waitcnt(clkfreq / 2000 + cnt) dira[pin]~ rc_temp := cnt waitpne(state << pin, |< pin, 0) rc_temp := || (cnt – rc_temp) rc_temp := (rc_temp – zofs) #> 0 rc_temp /= div long[rcAddr] := rc_temp if mode == 0 quit rctimeメソッドをpublicにしている。こうするとテスト時などにコールしたり、スタートメソッドを使ってCogで起動することもできる。 このメソッドにはいくつか引数がある。pin(使用するI/Oピン)、state(RC回路のコンデンサ初期状態0又は1)、zofs(直列接続した保護抵抗の影響を補償するゼロオフセット)、div(出力値をスケーリングする除数)そして最後はrcAddr(メソッドがアップデートする変数のアドレス) Basic Stampのようにテストでコールしてコードをみてみよう。Figure134.1のような標準回路でゼロオフセット、スケーリング除数を使わずにポテンショを読んでみる。 pot.rctime(0, 1, 0, 1, @potVal) rctimeメソッドのリターン値をコピーする代わりにターゲット変数のアドレス(オペレータ@を使用)を引数にしている。これには理由がある。(後述) rctimeメソッドのコードを見てみよう。ピン入力を制限してスタートしている。RC回路をEEPROM I2CラインやPropellerpプログラミングピンに接続するのはお薦めできない。 If (pin => 0) and (pin =< 27) 特にlookdown(lookdownz)はリストに区切り文字のコンマを使える。 pin を評価したら次のステップは stateとzero offsetとdivisorが有効な値であることを確認する。 ここでRC測定の仕組を説明しよう。 220オームの抵抗と0.1uFのコンデンサを使うと充放電は110us必要だ。もっと大きな抵抗にしたり部品を替える事で500usにすることもできる。 waitpne(target, mask, 0) targetはI/Oピンの目的の状態、maskはPropellerの入力とANDする値、そして”0”はポートAである(これは将来I/Oが64bitになったときの為のリザーブである) waitpne(%1010, %1111, 0) 4本全部A3..A0が比較に使われるので、%1111をmaskとして使う。 rc_timeメソッドでは1本のピンのみを監視するので、コンデンサの状態をピン番号でシフトすることでtargetをセットしている。pin maskはデコード演算子(|<)で作っている。これは指定したビット数だけ左シフトして働く。 オーケー、ピンの状態が変化したら、充放電サイクルの期間に進んだカウント数を得る為にシステムカウンタからrc_timeの値を減算する。 けれどもその前に 全てのコード全体がrepeat loopで囲まれているのでrctimeをどうやって終了させるのか不思議にかんじているかもしれない。 rctimeオブジェクトのスタートメソッドの詳細 PUB start(pin, state, zofs, div, rcAddr) : okay stop mode := 0 okay := cogon := (cog := cognew(rctime(pin, state, zofs, div, rcAddr) , @stack)) > 0 if okay mode := 1 startメソッドが最初にstopメソッドをコールしているのは奇妙だと思うだろう。 次のステップはmode変数をクリアすることだ。実際には動作していないのを除いてrctimeが別のCogで動作している事をトップオブジェクトで知るようなコードにしたくないので、それをcognewで行わせる 裏側のプロセスを動作させるのにアセンブラを習得する必要はない。それってクールだろ? オーケー、rctimeの利点を得る表側ののプログラム(トップオブジェクト)を見てみよう。 CON _clkmode = xtal1 + pll16x xinfreq = 5000_000 LCD_PIN = 0 LCD_BAUD = 19_200 LCD_LINES = 2 POT_PIN = 1 VAR long potVal OBJ lcd : “debug_lcd” pot : “rctime” PUB main if lcd.start( LCD_PIN, LCD_BAUD, LCD_LINES) lcd.cursor(0) lcd.cls lcd.backlight(true) if pot.start(POT_PIN, 1, 1520, 642, @potVal) repeat lcd.home lcd.def(potVal, 5) waitcnt(clkfreq / 5 + cnt) 上記は全く簡単なコードだ。表示デバイスにParallaxシリアルLCDを使ってる。 pot.startメソッドの後にあるrepeatループをチェックしてみよう。 このセクションを要約する前にzofs(1520)とdiv(642)の引数について不思議に思っているかもしれない。 二番めはzofsに1520を代入してpot.startをもう一度実行する Stacking It All Up かつてマルチスレッドのデバイスの仕事をしていた時、新しいスレッドの為のスタック設定の指導は大きめに取っておいて、それからスレッドがクラッシュするまでスタックを減らすというものだった。僕は全く正しい方法ではないと思っていたよ。 良いニュースがある:PropellerのCogの一つを使って別のオブジェクトの使用スタックを決める事ができる。僕にこれを作れるぐらいのスキル持ってたら良かったんだけどね。 これを使うとmulti-cogプロジェクトの開発を始めるのが楽になる。 stack_monitorは1個のCogで動作するオブジェクトだ。 rctimeオブジェクトのstartメソッドの最初の行に配置 stackmon.start(@stack, 32, 23, 16) これは32の初期サイズを持つスタックをコールした配列を指定している。 オーケー、動作したかい?stack_monitor.startメソッドはターゲットのスタック領域全部にあるパターン(FILLERという名の定数)を書き込んでおく。 PRI monitor(addr, size, ledMsb, ledLsb) | idx, used outa[ledMsb..ledLsb]~ dira[ledMsb..ledLsb]~~ repeat used := 0 repeat idx from addr to addr + size * 4 step 4 used -= long[idx] <> FILLER outa[ledMsb..ledLsb] := used メソッドは実際全く小さい。 減算してるだろう?実際オブジェクトのスタックで使うlongsの数を加算したいのにこれっておかしいよね。 オーケー、これで充分だと思う。 次回まで楽しくぶん回そう! |