BASIC Propeller Programming

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