THE SPIN ZONE BASIC Propeller プログラミング By Jon Williams Nuts & Volts Magazine 5 章 2010 年 3 月 Parallax と BASIC はそれ自体素晴らしいけどそれがマカロニとチーズみたいに一緒になっ た。これを使うとまるで魔法使いみたいな気分になれるよ。 Propeller では BASIC を使えないので失望している人が少なからずいた。 私のようにずっと BASIC を使ってきた人達や組込みプロジェクトに多くの時間を割いてき たグループは PBASIC に感謝している。 今や Spin は私にとってオーケーであり、全く心地よいものとなった。 30 年ほど BASIC や他の言語でプログラミングしてきたがやはり BASIC は挫折する人が少な いといえる。 さて、よいおしらせだ。”Bean”の名で知られる Terry Hitt が率いた SX/B 開発チームが Propeller 専用の BASIC コンパイラ(PropBASIC)を作った。 もう「BASIC が使えないから Propeller は使わないよ」という言い訳はできないよ。 PropBASIC とはなにか はっきりしている事は PropBASIC はアセンブラコードに変換する1パスコンパイラなので BASIC よりも速いということだ。 そのコードは一つ以上の SPIN ファイルだ。Version1 でメインプログラムコードはひとつ の Cog に限定されているがこれで困ることはない。 Propeller アセンブラは強力なので他の CPU で数行のアセンブラコードを必要とするプロ セスは Propeller では一つのアセンブラコードですむからだ。 そのうえ、まだ7つの Cog が残っていて、PropBASIC はそれぞれにタスクを割り振る事が できる。そのうちに PropBASIC は Hub RAM のメモリを使って 1 個の Cog メモリ 2K バイトの 壁を越えるようコンパイルできる Bill Henning の LMM(large memory model)アーキテクチャ も実装するかもしれない。 SX/B を使ったことのある人にとっては PropBASIC はとてもよくわかり、少しの変更で今あ るプロジェクトを移植することができるし、もし BASIC Stamp を使ったことがあれば PBASIC に似ている印象を持つと思うよ。 私を含めて多くの人にとって BASIC Stamp から SX/B へ乗り換えるのは大変なことなんだ。 でも BASIC Stamp から PropBASIC に乗り換えるのはとっても簡単で楽しいことなんだ。 少し新しい事を学習しなくてはいけないけれど、すぐにマルチコアプロセッサを使えるよ うになるよ。 こんにちは、PropBASIC どの学習書でも必ず”Hello World”で始まるのがお決まりになってるけど、大きなプロ グラミングと格闘する前に基礎を覚えるのは大事だ。 マイクロコントローラの分野でそれに相当するのは LED の点滅だろ? LED の点滅さえもできないのに、どうやって GPS を使った多軸ロボット制御なんてできる んだい? ちらかって汚いプログラムリストは大嫌いだ。最後まできちんとしたプログラムをかく簡 単な方法はテンプレートを使うことだ。沢山のプロジェクトを始めるのに使った template.pbas をダウンロードできる。 template.pbas を使って PropBASIC プログラムの内容を紹介する hello.pbas を書いてみた が、それはあとで披露しよう。 あえて言おう。 SX/B や BASIC Stamp を使った事のある人なら PropBASIC に乗り換えるのはとっても簡単だ。 DEVICE 設定 DEVICE XIN P8X32A, XTAL1, PLL16X 5_000_000 上記は PropBASIC では定番だ。 これは 16 倍 PLL を使った外部クロックを入力するプロセッサを P8X32A を宣言している。 外部クロック周波数は 5MHz でシステムクロックは 80MHz だ(5 x 16) もちろん定番以外もある。時間にシビアでない用途なら RC モードを選べる.こいつは低 パワーというおまけもついてる。 RC モードのシステムクロックは RCFAST で大体 12MHz、RCSLOW で大体 20kHz だ。 RCFAST の設定は DEVICE P8X32A RCFAST の設定の場合はなにも指定しないけれど RCSLOW の場合は RCSLOW を指定しなければ ならない。 RC モードでは必ず PLL は 1 倍で XIN は無視される。 繰り返すが、RC 発信周波数はチップ単位で異なるので RC モードは時間にシビアでない用 途で使うべきだ。例えばシリアル通信が必要なプログラムでは RC モードを使わないのが 正しい。 スポックが今の文章を読んだら眉毛を少しつり上げるかもしれない。(カーク船長やドク ター・マッコイに対してするように) RC モードがシリアル通信に不向きなら、RC モードで動作している Propeller チップはど うやって IDE(Propeller Tool)と通信しているのか?と。 よい質問だ。 実際 IDE はダウンロードプロトコルの役割として Propeller と拍子を合わせて、個々のチッ プに適応するために IDE からボーレートを調整している。 だから、消費電流を減らすために低クロックスピードにしてシリアル通信を使いたい場合 は下記のステップが必要になる。 ・RC モードのクロックスピードを測定(FREQ ディレクティブはなし) オシロを使って出力パルスの巾を測定して計算 ・XIN の替わりに FREQ ディレクティブで RC 周波数を修正 10msec のパルスを出力するプログラムを作って(speed_test.pbas)オシロでパルスを計っ てみた。−やってみるとパルス巾は 9.06msec だった。 これはプロセッサは予想より少し速いクロックで動作しているということだ。 以下のように測定したパルス巾を予想していた 10msec で割った値を scale factor として 計算しよう。 Scale = 10.0/measured 私の場合、scale factor は 1.1038 だった。クロック周波数 20kHz の RCSLOW を選択してい るのでプログラムの先頭に下記の調整値を付け加える DEVICE FREQ P8X32A, RCSLOW 22_075 FREQ 設定でコンパイルすることにより、PAUSE や SEROUT でも正しい時間管理がされる。 Program 定数 テンプレートの次の項目は定数定義だ。数字とキャラクタ文字は同じように扱える。 OnTime OffTime IsOn IsOff Asterisk CON CON CON CON CON 250 750 1 0 “*” プログラムで SERIN,SEROUT を使う場合はボーモード定数の設定もここで扱う。 Baud CON “T9600” 上記では SERIN,SEROUT を True モードで使う場合の定義を示している。 PropBASIC では定数はグローバルとして扱われ、どの Cog でも使用できる。 I/O PIN LED PIN 16 LOW PropBASIC では I/O ピンの宣言は PBASIC2.5 やリセット後にピン状態を定義しておく SX/B とよく似ている。ピンのオプションは INPUT(default),OUTPUT,LOW,High などである。 通信プログラムでこのピン定義を使用している。 RX TX PIN PIN 31 INPUT 30 HIGH HIGH オプションの使用に注意:シリアル通信の TX ピンをアイドル状態(output,high)に しておくようにスタートアップコードにおいている。 PropBASIC は I/O ピンのグループを定義することもできる。例えば PropellerDemoBoard の LED は P16 から P23 に接続されている。これらの LED をグループとして定義している。 LEDs PIN 23..16 LOW ピングループ定義で最初の数字がグループの最上位ピン(MSB)、2 番目の数字がグループの 最下位ピン(LSB)となっている。数字の順番は重要でグループが入力としてセットされて いる場合は読み出し、出力でセットされている場合は書き込みのビットが影響をうける。 PropBASIC1.0 では出力ピングループにバイナリを割り当てることもできる。 LEDs = %100000 上記は OK、しかし LEDs = 1 << position は NG。これは将来実装されるかもしれない。現在のところは下記のようにする。 __temp = 1 << position LEDs = __temp 変数の共有 PropBASIC では変数及び配列を Hub に保存しており、それらはどの Cog からもアクセスで きる。 Hub の変数は bytes,words,longs,全部のタイプの arrays で定義される。 rxHead rxTail rxBuffer HUB HUB HUB Byte = 0 Byte = 0 Byte(64) = 0 PropBASIC では Hub 変数の初期値を設定できるが、初期値設定されない場合はゼロにセッ トされる。(配列の場合は全部の要素) Hub 変数は通常の変数のようには扱えず、それらの read/write には必ず RDxxxx/WRxxxx を 使用する。(xxxx は変数のサイズ) データの共有 変数と同じように Hub にあるデータも共有できる、そして 32K バイトの RAM には大きなテー ブルデータも作れてどの Cog からもアクセスできる。 Hub のデータは bytes,words,longs として、それぞれ DATA,WORD,LDATA ディレクティブで宣 言する。 Zip4 DATA DATA DATA DATA %0001 %0010 %0100 %1000 Hub 変数のように RDxxxx/WRxxxx を使って Hub のデータにアクセスする。 大きなテーブルデータの場合は FILE ディレクティブを使って外部データを取り入れる。 SFX1 FILE “BABY.WAV” DATA 0 タスク定義 PropBASIC で 2 個以上の Cog を使用して「裏プロセス」の処理を実行するには TASK 定義が 必要です。BASIC Stamp を使った経験があったらシリアルデータの入力データの wait 処理 で悩んだことがあると思う。SX/B ではシリアル入力データ処理の為に割り込みを使って いました。しかし、もう気にすることはない。 Propeller では Hub 変数を使ってシリアル入力処理を別のプロセッサに行わせます。TASK 定義は単純だがタスクが起動する前に設定する必要がある。 SERIAL_RX TASK ローカル(Cog)変数 プログラムやタスクで使う変数は Cog にとってローカルで Long としてのみ定義できます。 Hub 変数として配列宣言や初期値設定ができる。 Idx VAR Long PropBASIC はコンパイラによって生成されるアセンブラコードで使用されるいくつかのロー カル変数(サブルーチンや関数内部で使うパラメータのような)を生成する。 コンパイル出力を見ると 5 個の変数__temp1〜__temp5 があります。これらは PropBASIC キー ワード用に生成されたコードで使われます。又作成する PropBASIC プログラムでこれらの 変数を使用できますがその為には注意が必要だ。これらの変数はキーワードが使われたら 変化してしまうから。 変数__param1〜__param4 ぐらいは利用してもいいでしょう。これらはパラメータ渡しの時 だけ使い、PropBASIC キーワード用に生成されたコードで修正されたものを使ってはいけ ない。 サブルーチンと関数の宣言 始めに述べたように PropBASIC は1パスコンパイラでアセンブラコードを最適化しません。 良いニュースは出力コードを検査することでアセンブラを習得でき、コードスペースを最 適化できることだ。 SX/B の初期には意味がないと思われるコードを削除していたものだ。(PropBASIC でもきっ と同じ事が行われていると思う) 例えば PAUSE はよく使われる、この PAUSE 100 の PASM が下記だ mov adds mov __L001 waitcnt djnz __temp1, cnt __temp1, _1msec __temp2, #100 __temp1, _1msec __temp2, #__L001 オーケー、もし PAUSE が出現する度にアセンブラコードを生成したら酷いコードになるだ ろう。これはサブルーチンに PAUSE をカプセル化したら解決できる。 SX/B のように DELAY_MS という名前のシェルは下記 DELAY_MS SUB 1 DELAY_MS は 1 個の引数が必要だ。Propeller 変数は 32bit なのでそれはとても長い遅延時 間でも OK だ。 SX/B を知ってる人には PropBASIC の FUNC 宣言は少し違うと思うだろう。 1月に Propeller Forum メンバーが SIRCS の PropBASIC への移植について質問していた。 SIRCS オブジェクトは IR コードビットをビットカウントのようにリターンするものだ。 PropBASIC は関数が返すパラメータ数を気にしない。(通常は 1 個) それ故、定義は下記 SIRCS_RX FUNC 0 メインプログラム LED blinker プログラム本体は大体下記のようになる PROGRAM Start Start: FOR idx = 1 TO 3 LED = IsOn DELAY_MS OnTime LED = IsOff DELAY_MS OffTime NEXT DELAY_MS 1_000 GOTO Start PROGRAM ディレクティブはユーザが作ったプログラムの始点と、さらに時度生成 start-up コードの位置も示している。 Propeller では start-up コードはディレクティブの指すラベルへジャンプする前に I/O ピ ンの入力・出力と状態の設定も簡単にやってのけている。 見てのとうり、コードはまったくの BASIC だ。PBASIC や SX/B、他の言語を扱った人達にとっ ても簡単だろうと思う。 サブルーチンと関数の中身を作る サブルーチンと関数のコードはリストの最後に書くことになっているので DELAY_MS とい う名前のサブルーチンをリストの前部で定義してコードを書いてみた。 SUB DELAY_MS PAUSE __param1 ENDSUB 他の言語同様にサブルーチンは別のサブルーチンを call することもできる。ターミナル へのデータ送信のプログラムで2つのサブルーチンを書いた。 SUB TX_STR strAddr VAR __param2 strChar VAR __param3 strAddr = __param1 DO RDBYTE strAddr, strChar IF strChar = 0 THEN EXIT TX_BYTE strChar INC strAddr LOOP ENDSUB SUB TX_BYTE SEROUT TX, Baud, __param1 ENDSUB 2 番目のサブルーチンは SEROUT のシェルで、最初のサブルーチンはシリアルポートに文字 を送信するのに使う。 文字のアドレスを引数にして TX_STR を call するとその引数は__param1 に渡される。 TX_BYTE に文字を渡すのに__param1 が必要なのでサブルーチン内部で__param2,__param3 を使っている。 新しい変数を宣言しない理由は Propeller では全部が RAM にあるからだ。 その為、できるだけ__paramx 変数を使い変数宣言を少なくしてアセンブラコードスペース を節約するのはよい習慣だ。 多分キャラクタコードを得るのに RDBYTE を使ってるコードに気付いてると思うが PropBASIC では全ての文字が Hub RAM にある。もちろん、DATA ディレクティブで文字列の 定義もできる。 Banner DATA “PropBASIC”, 0 関数のコードはサブルーチンコードと比較すると FUNC..ENDFUNC ブロックと関数の呼出し 側に1つ以上の戻り値(ENDFUNC の直前に RETURN をおく)があることを除けばよく似てい る。下記は PropBASIC で書いた SIRCS 受信関数だ FUNC GET_SIRCS irCode VAR __param1 irBits VAR __param2 COUNTERA NEG_DETECT, IR, 0, 1 COUNTERB FREE_RUN, 0, 0, 1 Wait_Start: WAITPEQ IR, IR PHSA = 0 WAITPNE IR, IR PHSB = 0 WAITPEQ IR, IR IF PHSA < BIT_S THEN Wait_Start irCode = 0 irBits = 0 Check_Frame IF PHSB > MS_044 THEN IR_Done Wait_Bit: IF IR = 1 THEN Check_Frame PHSA = 0 WAITPEQ IR, IR irCode = irCode >> 1 Measure_Bit: IF PHSA > BIT_1 THEN irCode = irCode | $8000_0000 ENDIF INC irBits IF irBits = 20 THEN IR_Done GOTO Check_Frame IR_Done __temp1 = 32 – irBits irCode = irCode >> __temp1 RETURN irCode, irBits ENDFUNC まったくそのまんまのコードだと思う。これはアセンブリコードに変換されるので大変速 い。だから RC-5 のような別の IR プロトコルで実験したい場合は high-level- code(sircs_rx.pbas 参照)を使った方がいいよ。 PropBASIC は SIRCS の例のように PASM にある WAITPEQ、WAITPNE などの Spin や PASM のキー ワードもいくつか持っていることを指摘しておくよ。 タスクの中身を作る タスクは別の Cog で動いてるのでその構成はサブルーチンや関数よりも少し込み入ってる。 実際、タスクはそれ自身が 1 つのプログラムなのでその中でのみ定義されるサブルーチン や関数があったりもする。 これらの宣言やコードの全部が TASK..ENDTASK ブロック内にある。 TASK SERIAL_RX rxb VAR hPntr VAR DO SERIN RDBYTE WRBYTE INC hPntr = WRBYTE LOOP __param1 __param2 RX, Baud, rxb rxHead, hPntr rxBuffer(hPntr), rxb hPntr hPntr & $3F rxHead, hPntr ENDTASK そのとうり、これが全部だ。タスクは CON,PIN,HUB 宣言もできるしこれら全部の要素を含 んでいる。 DO..LOOP の最初で SERIN を使ってバイトデータを読んでいる。- PBASIC や SX/B でやって るように。 違いはこのタスクはそれ自身の Cog で起動されシリアルデータを待っていてもメインプロ グラムの邪魔をしないことだ。 現在のヘッドポインタのデータを RDBYTE で読み出し rxBuffer にそのオフセットを加えて WRBYTE で新しいシリアルデータを保存できる。 ヘッドポインタを+1 してバッファ内に収まるように$3F と AND して、呼出し側が使ってい る Hub メモリに書き戻している。 タスクをスタートするには 2 種類あるがたいてい COGSTART を使う。 COGSTART SERIAL_RX 別の Cog が立ち上がり準備が整うまで十分な時間は必要なので、タスクで供給したデータ にアクセスしようとする前に数ミリ秒待たせたいこともあるかもしれない。 PropBASIC コンパイラはタスクの名前で Spin ファイルも作れるので、同一フォルダにある かもしれない Spin プログラムを上書きしないようにタスクの名前に注意する必要がある。 オーケー。シリアルデータを受信しバッファリングする方法はわかった。ではメインプロ グラムでそのデータを使うにはどうしたらいいのだろう? バッファからデータを取り出す関数を書いたよ。 FUNC RX_BYTE rxh VAR rxt VAR rxchar VAR __param1 __param2 __param3 DO RDBYTE rxHead, rxh RDBYTE rxTail, rxt LOOP UNTIL rxh <> rxt RDBYTE INC rxt rxt = rxt WRBYTE RETURN ENDFUNC rxBuffer(rxt), rxchar & $3F rxTail, rxt rxchar 最初のループでヘッドとテイルポインタの値を比較してデータを得ている。 バッファが空だとこの値は等しく、この値が違うとバッファインデックスとしてテイルポ インタを使って受信データを得る。そしてヘッドポインタで行ったようにテイルポインタ を+1 して Hub メモリに書き戻している。 別の Cog がシリアルバッファにデータを保存するやっかい事をやってくれているのでバッ ファが空でもメインプログラムの邪魔をする事はない。 シリアルバッファで待つバイト数を戻す関数があるよ。 もしゼロならバッファが空なので受信データをただ待つだけの RX_BYTE をスキップする。 FUNC RX_CHECK head VAR tail VAR bufcnt VAR __param1 __param2 __param3 RDBYTE rxHead, head RDBYTE rxTail, tail IF head >= tail THEN bufcnt = head – tail ELSE bufcnt = tail – head ENDIF RETURN bufcnt ENDFUNC これはバッファの先頭と最後尾間の差をリターンする。バッファはリング形式で tail は head に追随し、head ポインタが tail ポインタより小さくなって負数にならないように IF..THEN を使っている。 アセンブラ? 難解なアセンブラ言語から解放してくれて速いプログラムをコーディングできるので沢山 の人がこのコンパイラを楽しむと思う。 でもそれでもアセンブラうを使うのはやっぱり便利だ。 PropBASIC では複数行なら ASM..ENDASM ブロック、1行なら”\”を使ってコーディングで きる。PropBASIC はとってもナイスなコードを作ってくれるのであまりインラインアセン ブラを使う必要はないけれど知っておいて損はないと思うよ。 まとめよう ここだけで多様な機能をもつ PropBASIC を述べる事はできない。でも PropBASIC とは何か とか初め方はわかってもらえたと思う。SX/B 同様、条件付きコンパイルディレクティブ もあるし PropBASIC ファイルに外部のアセンブラコードを読み込ませることもできる。 アプリケーション初期バージョンとしては本当によくできている。しかもプライスレスだ。 PropBASIC の将来 一言でいえばワクワクしているんだ。 SX/B を使ってた人達はもちろん、初心者にとってもこんなにナイスなツールでプログラ ミングを始められることを伝えたい。 PropBASIC1.0 の機能は SX/B のよりも少し進んでいる。 アセンブラ言語の速度で大きなプログラムを作れる LMM への移行など Bearn に色んな提案 をして言語を拡張するには時間がかかる。 Propeller で何かするのに BASIC を待ってたならもう待たなくていいんだ。 しかもコンパイラはプライスレスなのでエクスキューズはできないよ。 さあ、こっちにおいでよ、きっと楽しいから。保証するよ。 次回までに Propeller と PropBASIC を入手してブンブンぶんまわそうぜ!! Jon Williams [email protected] Parallax,Inc www.parallax.com
© Copyright 2024 ExpyDoc