続・Propeller日記(1)
長嶋 洋一
Propeller日記(1)
2012年8月27日(月)
以前に執筆してWebに上げていた Propeller日記(5) の最後の日付けは「2008年5月21日(水)」であり、それからもう、4年半近く経過してしまった。 この間、まぁそれなりに色々とやってきた。 「日記」モノとしては、「Propeller日記」と並行してチラッとやって見切ってしまった 「Arduino日記」 があったが、さらに終了宣言しないまま宙に浮いているものとして(^_^;)、 「Processing日記」 と 「SuperCollider日記」 がある。いずれも主に海外出張中(乗り継ぎ待ちの空港など、かなり時間があるため)など、 暇がある時に進めてきたものである。Propellerについては、上記の最後の日記の中で
などと書いていたが、「トランジスタ技術」誌に2月連続で寄稿したのが2008年10月号/11月号、 そして上記のリストの★印については、 ここ を探すと、それぞれ発表したPDFが発掘できる。 RFIDリーダについては楽をしてAKI-H8に譲ったが、 Propellerを13個使った、12画面ディスプレイ利用のインスタ作品 「電子十二影坊」 は盛大に展示発表した。
- 「トラ技」に原稿執筆
- 日本音楽知覚認知学会での発表で紹介(5/24)★
- 音楽情報科学研究会での発表で紹介(5/28)★
- Sketch03で紹介(6/25-27、米国RISD)★
- 音情研・夏シンポで詳細紹介(8/6-8)★
- FIT2008で紹介(9/2-4)★
- RFIDのリーダ製作予定(ゼミの学生作品)
- オープンキャンパス/MAF2008でのインスタレーション作品に応用(12画面ディスプレイ)
電子十二影坊さらにComputer Musicの方では、Propellerを使って2009年に開発した新楽器 「Peller-Min」 とその発展形に関して、
などの報告をしていて、 ここ を探すと、それぞれ発表したPDFが発掘できる。 この延長から ロシアツアー(2010) も実現できたので、Propellerとの出会いが大きく貢献している。
- 「シーズ指向による新楽器のスケッチング」2009年5月21日『音楽情報科学研究会』(筑波大)
- 「Parallel Processing System Design with "Propeller" Processor」2009年6月5日『International Conference on New Interfaces for Musical Expression』(カーネギーメロン大学)
- 「Some interactive works ('08-'09) - case study of my sketching」2009年7月18日『Sketching in Hardware 2009』(University College of London)
- 「Parallel Processing Platform for Interactive Systems Design」2009年9月2日『International Conference on Entertainment Computing』(Conservatoire National des Arts et Metiers, Paris)
- 「並列処理プロセッサ"Propeller"によるプラットフォームの検討」2009年12月5日『音楽情報科学研究会』(国立音大)
- 「Untouchable Instrument “Peller-Min”」2010年6月17日『International Conference on New Interfaces for Musical Expression』(シドニー工科大学)
- 「Technology for Computer Music / Interactive Multi-Media Performance with New Interfaces」2010年12月6日『Intertnational Festival/Competition SYNC.2010 Tutorial(1)』(The Ural State Conservatory, Yekaterinburg, Russia)
- 「Untouchable Instruments and Performances」2011年8月2日『International Computer Music Conference』(University of Huddersfield, UK)
- 「Untouchable Performance and Technology」2011年12月10日『Asia Computer Music Project 2011』(東京電機大)
Peller-Min2011-2012年には、PropellerでなくArduinoを使った ジャミネータと遊ぼう というプロジェクトが盛り上がり、 メイキング動画 はデザインスタジオIDEOで実際にJaminatorをデザインした本人からも オレゴン で好評を得た(写真右)。 そして今回の「続・Propeller日記」は、この「ジャミーズ娘+」から繋がっているのである。
東京では年に2回開催されている「Make Meeting」を、三輪さん赤松さん小林さんなどお友達の多いIAMAS(大垣)で開催する、 というので2010に「Make Ogaki Meeting 2010」に参加した。 その模様は ここ とか ここ で判ると思う。 そして沈黙の2011年を経て、2012年にまた「MOM2012」がある、というので、 ここに「ジャミーズ娘+」の再演と僕の新作公演、そしてインスタ展示を計画した。
インスタ展示は2作品で、その一つがPropellerを活用した大塚さんの卒制作品「万変鏡」である。 解説は この中 にある (YouTube) 。 そしてもう1作品は、SUACの国際交流提携校である韓国・ホソ大学からの交換留学生であるリュジュンヒー君が、 2012年前期に僕のゼミの「メディア造形演習II」の作品として制作したインスタレーション作品「カラーオーケストラ」である。 解説は この中 にある (YouTube) 。
万変鏡「Make Ogaki Meeting 2012」に参加したのが、2012年8月25-26日であった。 この日記を書いている8月27日は、学生は8/26にMOM終了後に浜松に帰ったが、 僕だけ機材を積んだSUAC公用車で朝帰りした日である。 ちなみにMOM2012のライブでの僕の新作「Joyful Boxes」は、 このリュ君の制作したサウンドインスタレーションを「楽器」として使い、 ライブグラフィクスを伴うComputer Music作品として作曲した (YouTube) 。 このMOM2012への参加の模様は ここ(リンク先を含めて) で判ると思う。
そして、 MOM2012出展者情報 の中に、たまたま発見したのが プロペラブ というものであった。 なんとPropellerにハマるという奇特な人達がいたのであった(^_^;)。 この「プロペラブ」にあった プロペラブ Propeller Wiki を見ていると、 先人の方々が記された文書などへのリンク というのがあり、なんと僕の「Propeller日記」がリンクされていた(^_^;)。 光栄である。 MOM2012の場では、展示ブースの「島」がちょうどお隣だったので、挨拶に行った。
そして プロペラブ の中に発見したのが、 「Windows/Linux/Macで動作する開発環境 - 標準のツールだとWindows用のしかないですが、BSTならLinuxやMacでもプロペラを楽しめます!ヤッタネ☆」 という BST - The multi-platform Propeller Tool Suite である。 ちょっと油断するとPropellerとご無沙汰になる最大の理由は、開発環境がWindowsだけ、というものであった。 いつも使うMacで開発できるというのは画期的な朗報である。 このbstとの出会いが、この「続・Propeller日記」のきっかけなのである。 全てはPropellerを軸として繋がっているのであった。
2012年8月28日(火)
(本当のところは上の「8/27」というのも、流れをスムースにするために形として分けただけで、実際には8/28の朝から書いたのであるが(^_^;)) さて、8月28日である。MOM2012が無事に終わって、 次には今週木曜日8/30の午後から9/12まで、 今年2度目の海外(リンツ・ウイーン・スロベニア)に出かける、という慌ただしい期間である。 あまり時間のかかる仕事も出来ないので、隣りのマシンで以下のようにデータの整理/バックアップをしているぐらいなので、 チラッとPropellerと遊んでみるには格好のチャンスである。とりあえずPropellerはもう忘却の彼方なので、bstの動作実験から、まずは「思い出し」である。 まずはさっそく、 BST - The multi-platform Propeller Tool Suite に行ってみる。
bstには以下の3種類のツールがあるというが、ここでは迷い無く「bst」を選ぶ。
- bstl - The command line loader
- bstc - The command line compiler
- bst - The GUI IDE
bstのダウンロードページ に行くと、LinuxとMacだけでなく、Windows版もあった(^_^;)。 OSX版の最新バージョンは「0.19.3」とあるので、 これ をダウンロードして解凍すると、ドキュメントも何もなくて「bst.app」(たった2.2MB)が出来た。 これでオシマイである。
manualのダウンロードページ からは、最新の0.04バージョンの これ をダウンロードした。 3種類のツール全てをまとめているが、チラッと見てみると、 なんと以下のように「spinをバイトコードで」などという、ソソラレル記述があった。 これはいずれ、きちんとチェックしてみたい。
さて、ツールを実験するにはサンプルソースが必要である。 そこでまず、1106研究室でPropeller開発に使っていた3台のWindowsXPマシン内にあった、 Propeller関連のディレクトリを全部まとめて、重複を消してデータ整理することにした。 かつてはSGI Indyワークステーション(IRIX)が4台とLinuxに入れ替えたノートPCが1台あったが既に廃棄していて、 現在、1106研究室にあるパソコンを数えてみると、Windowsが5台(XPが3台、98と95が各1台)で、 Macは全てOSXで14台(G4PowerBookが2台、iBookが4台、MacBookが2台、miniが5台、MacBookAirが1台)であった。 この写真 は技術造形学科3期生の僕のゼミの卒業アルバム写真(2005年)であるが、ここに写っているパソコン18台(大部分がMac)は全て寿命で廃棄して、 総どっかえになっている。7年もすればパソコンは消える運命にあるようだ。 整理したPropeller関係のディレクトリは以下のようなものである。
何はともあれ、まずはダウンロードしたbstを起動してみると、以下のようになった。 ここでようやく、bstとは「Brad's Spin Tool」、つまりたぶんブラッドさんが作ったspin用(Propeller開発用)ツールの事なんだ、 と判った(^_^;)。 試しのソースは、もっとも古いアーカイブ、つまり最初の「Propeller日記」のあたりのものだろう。 ちょっとMacの画面だと見にくい気もするが、これはスグに馴れるのかな。
開発ツールが立ち上がればさっそくPropellerを繋いでみよう。 しばらく眠っていた、最初のPropeller実験ボード(トラ技に書いた記事で紹介)を引っ張り出してきて、以下のように繋いでみた。 これまで「Propellerの開発ツールはWindows限定」と淋しい気持ちでいたので、この風景は画期的である。
このMacはGainerやXBeeの実験にも使っていて、シリアルのためのFTDIドライバはインストールされているためか、 メニューの「Compile」の中にあった「Detect Propeller」を選ぶと、何もしないでも以下のようにアッサリとPropellerを認識した(^_^)。
Propellerのソースコードのコンパイルも、Propeller上のRAMへの転送もあっさりと成功してしまったので、 次に、トラ技の記事の「テレビ(NTSC)出力」のソースコード(proto004.spin)に切り換えてみた。 こちらもコンパイルはOK、ところが「Compile and Load Ram」のところでヘンな現象が起きた。 まず、秒殺で完了する筈のPropellerへのロードで、以下のようにしばしストップする。これは既に異常である。
その後、10秒ほどすると、そのまま何もしないのに以下のように表示される。Crashとは嫌な情報である(^_^;)。
ここには「OK」ボタンしかないので、仕方なくクリックするとこのメッセージウインドウが消える。 ところが、この状態で、bstはハングアップして、ウインドウ内のクリックも、Quitのためのショートカットも、全て反応しなくなる(^_^;)。 「虹色グルグル」が出たままである。 ところが、他のアプリとかOSXは普通に生きている(Unixなのでこれは当然)。 ただし、OSXユーティリティの「アクティビティモニタ」からbstのプロセスをkillしようとしても、頑として無視する。 まさにハングアップである。(^_^;)
そして、Propellerと繋いでいたUSBケーブルを抜くと、以下のような謎のウインドウが出て、bstは生き返る。 この状態で「OK」をクリックすると、何事も無かったように復帰する。 この現象は何度となく試した結果、確実に同じことが起きることを確認した。 要するにUSBインターフェースの部分で、ハングアップというよりはタイムアウトエラーを起こしていた。
この現象は確実に起きるので、原因も明確である可能性が高い。 簡単なプログラムではエラーが起きないので、オブジェクトのサイズが関係するのか、 あるいは外部モジュールを参照すると問題なのか(ただしソースのコンパイルではエラーが起きない)。 ここまでが異常に順調だったが、ここで、楽しい(^_^;)試行錯誤タイムに突入した。
・・・ここでしばし、日常のお仕事の中断があったが、遂に原因が判明して解決した。 試しにMacBookAirで同じことをやったら、問題なくロード出来たのでピンと来たのが、 この「お仕事用Mac mini」は、Sketching in Hardwareなどに持参する「出張用MacBookAir」よりも、 開発環境としては「古い」、という点である。 そこで調べてみると、インストールしていたFTDIシリアルドライバのバージョンは「2.0.9」だった。 ここ に久しぶりに行ってみると、なんと現状は「2.0.18」と、マイナーバージョンが9段階も改訂されていた。 これをインストールしたところ、以下のように何事もなかったようにロードに成功して、ビデオ出力が表示できた。 ようやく、これで解決である。(^_^)
ちょっと思い出してきたので、ここでいよいよ、何か新しく作ってみることにした。 過去のプログラムなんてのは忘れていて思い出すのが大変なので、いっそ新しいものを作った方が早いのである。 考えてみると、MOM2012で展示した、OGの大塚さんの卒制作品「万変鏡」は、あまりドキュメントが残っていなかったので、 ここに深入りすることにした。 大塚さんの「万変鏡」のプレゼン(Flash)は これ である。
この「OLED」というのは、正式には「uOLED-96-PROP」というもので、 4D System社 が開発したフルカラーOLEDモジュールで、なんとPropellerがディスプレイ制御を行っている。 96*64ドット(アスペクト比3:2)に対して、「RGBあわせて8ビット」という疑似フルカラーを実現する小型モジュールであるが、 残念ながら製造中止となって、現在では入手できない。僕の手元にあるのは、実験機に組み込んだ2個を入れて、計4個だけである。 既にネットからは消えているが マニュアル もあるので、これをディスプレイとして使うことにしよう。
そしてもう一つ、ジャミーズ娘+では5台の改造ジャミネータからのMIDI出力をマージしていたが、 ちょうど最近、XBeeをやったところだった(XBeeはこれまで「エックスビー」と内心、呼んでいたのだが、MOMでは「ジグビー」と呼ぶ人がいた。それは規格のZigBeeじゃないのかなぁ)。 SUACの国際交流提携校である韓国・ホソ大学からの交換留学生であるイーギョンフン君も、 2012年前期に僕のゼミで「メディア造形演習II」に取り組み、パフォーマンス作品「日本の音風景」を制作した。 解説は この中 にある (YouTube) 。 ここで、中古の三味線を改造してセンサ情報をMax/MSP/jitterに伝えていたのがXBeeである。 もともと、M2の伊熊さんの修了制作に使うということで、 このように 実験していたものを、さっそくイー君の三味線に使ったわけである。 せっかくなので、このXBeeも、Propellerで料理してみることにしよう。
これでようやく、ここからハンダ付けである。 大体はここ でやった作業(電池ケースを単3から単4に小型化)なので詳細は省略するが、 XBee用のゲタ基板を作り、電池ケースに両面テープ(プロ仕様の硬化するやつ)で取り付け、今回は38400bpsで設定したXBeeを載せた、ということで、 以下のような流れである。
ホストのMax側のパッチはほとんど変わらないが、今回はPropellerにディスプレイデータを送る、という作業がメインとなる。 一方、Propellerモジュールの方は、前回はシリアルポートを内蔵しているArduinoだったところを、 PropellerのCogのソフトによって、1ビットごとに監視してXBeeからの38400シリアルを確実に受け取る必要がある。 MIDI受信モジュールをアセンブラで完全に自作しているので、31250から38400にちょっとだけ変換すればいい、 というのが基本だが、途中のデバッグはかなり困難なので、「作って試す」というループはとても長大である。 さてどうなるか、ここで18時になったので、続きは明日にやってみよう。
2012年8月29日(水)
さて翌日、渡欧前日となったので、研究室にある海外コンセントアダプターとかトランスとかを準備した。 午後には1回生の金重さんと「ドラえもん」制作の予定があるので、時間との勝負である。 どこまで進めるか。 スタートラインは以下のような風景である。 Macと2台のXbeeモジュール(片方にはPropellerのOLEDディスプレイモジュール)、という美しい風景である。いきなりXBeeを使ってMaxとPropellerが通信する、という確認に入るには、「Propellerの思い出し」というステップが障害となるので、まず最初に以下のように、「XBeeから何かデータが飛んで来たら表示する」というMaxパッチを作って走らせておくことにした。
そしていよいよPropellerである。 過去のアーカイブを調べて、ディレクトリ「project_OLED」とディレクトリ「project_Peller_Min」から、以下のモジュールをコピー/リネームして、今回の実験用に作ったディレクトリ「project_XBee」に置いた。
- OLED-Driver1.spin - OLED付属のディスプレイドライパを改造したもの
- OLED-Driver2.spin - OLED付属のディスプレイドライパを改造したもの
- MIdi_In.spin - オリジナル開発したMIDI入力ドライバ
- MIdi_Out.spin - オリジナル開発したMIDI出力ドライバ
- Demo_OLED.spin - 加速度センサで円表示を移動させ座標をMIDI出力したシステム
- PellerMin.spin - 新楽器「Peller-Min」のPropellerソフト。計32チャンネルのテルミン情報をMIDI出力
「OLED-Driver1.spin」は以下である。
「OLED-Driver2.spin」は以下である。CON _OUTPUT = 1 'Sets pin to output in DIRA register _INPUT = 0 'Sets pin to input in DIRA register _HIGH = 1 'High=ON=1=3.3v DC _ON = 1 _LOW = 0 'Low=OFF=0=0v DC _OFF = 0 _ENABLE = 1 'Enable (turn on) function/mode _DISABLE = 0 'Disable (turn off) function/mode ' _OLED_Data = 7..0 'OLED Data lines (This line is here for reference, not for code) _OLED_CS = 8 'OLED chip select (low = select) _OLED_RESET = 9 'OLED reset (low = reset) _OLED_DorC = 10 'OLED data or command select (low = command, high = data) _OLED_WR = 11 'OLED write _OLED_RD = 12 'OLED read _OLED_VCCE = 13 'OLED VCC enable _SET_COLUMN_ADDRESS = $15 _SET_ROW_ADDRESS = $75 _SET_CONTRAST_RED = $81 _SET_CONTRAST_GREEN = $82 _SET_CONTRAST_BLUE = $83 _SET_CONTRAST_MASTER = $87 _CONTRAST_RED_2ND = $8A _CONTRAST_GREEN_2ND = $8B _CONTRAST_BLUE_2ND = $8C _REMAP_COLOUR_SETTINGS= $A0 _DISPLAY_START_LINE = $A1 _DISPLAY_OFFSET = $A2 _DISPLAY_NORMAL = $A4 _DISPLAY_ALL_ON = $A5 _DISPLAY_ALL_OFF = $A6 _DISPLAY_INVERSE = $A7 _MUX_RATIO = $A8 _MASTER_CONFIGURE = $AD _DISPLAY_OFF = $AE _DISPLAY_ON = $AF _POWERSAVE_MODE = $B0 _PHASE_PRECHARGE = $B1 _CLOCK_FREQUENCY = $B3 _SET_GRAYSCALE = $B8 _RESET_GRAYSCALE = $B9 _PRECHARGE_RGB = $BB _SET_VCOMH = $BE _NOP = $E3 _LOCK_COMMAND = $FD _DRAW_LINE = $21 _DRAW_RECTANGLE = $22 _COPY_AREA = $23 _DIM_WINDOW = $24 _CLEAR_WINDOW = $25 _FILL_ENABLE_DISABLE = $26 _SCROLL_SETUP = $27 _STOP_SCROLL = $2E _START_SCROLL = $2F _256_COLOURS = $32 '%0100_0000 _65K_COLOURS = $72 '%0100_1000 VAR long OLED_cog 'Cog flag/ID long MemDispPtr 'A pointer to the beginning of the bitmap screen in HUB RAM long CSpin 'Pin number - so pin can be used in new cog long DorCpin 'Pin number - so pin can be used in new cog long WRpin 'Pin number - so pin can be used in new cog long RESpin 'Pin number - so pin can be used in new cog long VCCEpin 'Pin number - so pin can be used in new cog long RDpin 'Pin number - so pin can be used in new cog long FrameTime 'Variable holding time of last frame update PUB start(_MemDisp) : okay '' Start the ASM display driver after doing a Spin initialize '' Setup I/O pins, initiate variables, starts a cog '' returns cog ID (1-8) if good or 0 if no good stop 'Keeps two cogs from running at the same time MemDispPtr := _MemDisp 'Copy pointer passed from main program CSpin := _OLED_CS 'Store I/Os in variables for ASM to access DorCpin := _OLED_DorC WRpin := _OLED_WR RESpin := _OLED_RESET VCCEpin := _OLED_VCCE RDpin := _OLED_RD okay:= OLED_cog:= cognew(@ENTRY, @MemDispPtr) + 1 'Returns 0-8 depending on success/failure PUB stop '' Stops ASM display driver - frees a cog if OLED_cog 'Is cog non-zero? dira[7..0] := $FF 'Set data as outputs, 8-bits wide dira[_OLED_CS] := _OUTPUT 'Set cs as output dira[_OLED_RESET] := _OUTPUT 'Set reset as output dira[_OLED_DorC] := _OUTPUT 'Set DorC as output dira[_OLED_WR] := _OUTPUT 'Set write as output dira[_OLED_VCCE] := _OUTPUT 'Set VCCE as output outa[_OLED_DorC] := _HIGH 'Set the pins high (keeps ASM from being able to control the OLED outa[_OLED_RD] := _HIGH outa[_OLED_WR] := _HIGH outa[_OLED_CS] := _HIGH outa[_OLED_VCCE] := _HIGH cogstop(OLED_cog~ - 1) 'Stop the cog and then make value of flag zero powerDown 'Perform a power down sequence in Spin dira[7..0] := $00 'Set data as inputs dira[_OLED_CS] := _INPUT 'Set cs as input dira[_OLED_RESET] := _INPUT 'Set reset as input dira[_OLED_DorC] := _INPUT 'Set DorC as input dira[_OLED_WR] := _INPUT 'Set write as input dira[_OLED_VCCE] := _INPUT 'Set VCCE as input outa[_OLED_DorC] := _LOW 'Set the pins low outa[_OLED_RD] := _LOW outa[_OLED_WR] := _LOW outa[_OLED_CS] := _LOW outa[_OLED_VCCE] := _LOW PUB frameStat : value '' Returns the time (ms) that it took to paint the last screen to the OLED memory value := FrameTime/(clkfreq/1_000) PRI powerDown '' Power down of OLED display based on datasheet writeCMD(_DISPLAY_OFF) 'Send the display off command outa[_OLED_VCCE] := _OFF 'Turn off the OLED VCC voltage pauseMSec(100) 'Observe a waiting period before next action PRI writeCMD (cmd) '' Send a one byte command to OLED Display outa[_OLED_DorC] := _LOW 'Set line for command outa[_OLED_CS] := _LOW 'Select the OLED display outa[_OLED_WR] := _LOW 'Prepare to write to the register outa[7..0] := cmd.byte[0] 'Send the 8-bit data outa[_OLED_WR] := _HIGH 'Latch in the command outa[_OLED_CS] := _HIGH 'Deselect OLED display outa[_OLED_DorC] := _HIGH 'Restore state - data PRI pauseMSec(Duration) waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt) DAT ' Assembly Language display driver for uOLED-96-PROP ' copy HUB RAM bit-mapped memory to OLED Graphics RAM ' org ENTRY mov t0, par 'Load address of parameter list into t1 (par contains address) rdlong memptr, t0 'Read value of dispMemptr from main memory add t0, #4 'Increament address pointer by four bytes rdlong CSp, t0 'Read value of CSpin from main memory mov CSmask, #1 'Load mask with a 1 shl CSmask, CSp 'Create mask for the proper I/O pin by shifting add t0, #4 'Increament address pointer by four bytes rdlong DorCp, t0 'Read value of DorCpin from main memory mov DorCmask, #1 'Load mask with a 1 shl DorCmask, DorCp 'Create mask for the proper I/O pin by shifting add t0, #4 'Increament address pointer by four bytes rdlong WRp, t0 'Read value of WRpin from main memory mov WRmask, #1 'Load mask with a 1 shl WRmask, WRp 'Create mask for the proper I/O pin by shifting add t0, #4 'Increament address pointer by four bytes rdlong RESp, t0 'Read value of RESpin from main memory mov RESmask, #1 'Load mask with a 1 shl RESmask, RESp 'Create mask for the proper I/O pin by shifting add t0, #4 'Increament address pointer by four bytes rdlong VCEp, t0 'Read value of VCCEpin from main memory mov VCEmask, #1 'Load mask with a 1 shl VCEmask, VCEp 'Create mask for the proper I/O pin by shifting add t0, #4 'Increament address pointer by four bytes rdlong RDp, t0 'Read value of RDpin from main memory mov RDmask, #1 'Load mask with a 1 shl RDmask, RDp 'Create mask for the proper I/O pin by shifting add t0, #4 'Increament address pointer by four bytes mov FrmTm, t0 'Move pointer value for the Frame Per Second measurement mov t1, #0 'Initialize t1 (ensure it is zero) or t1, CSmask 'Create a composite mask for all pins (or them into t1) or t1, DorCmask ' or t1, WRmask ' or t1, RESmask ' or t1, VCEmask ' or t1, RDmask ' or t1, #$ff 'Data lines on pins 0..7 mov dira, t1 'Set CS, DorC, WR, RES, VCCE and data as outputs or outa, CSmask 'Set the CSpin high or outa, DorCmask 'Set the DorCpin high or outa, WRmask 'Set the WRpin high or outa, RDmask 'Set the WRpin high andn outa, RESmask 'Set the RESpin low andn outa, VCEmask 'Set the VCEpin low call #INITIALIZE 'Initialize the display 'Data sent left to right, top to bottom ScrnStart mov s0, cnt 'Get the current count for statistics CALL #STATCALC 'Go calculate the statistics mov rowcnt, #0 'Reset the row counter :row mov colcnt, #0 'Reset the column counter :col mov offset, colcnt 'Load the current column in offset shr offset, #2 'Divide by 4 (4 pixels wide is a tile) shl offset, #6 'Multiply by 64 (lines on the display) add offset, rowcnt 'Add in the row we are working with, offset is in long shl offset, #2 'Multiply by 4, offset is now in bytes (for HUB RAM) add offset, memptr 'Offset is now the entire HUB RAM address rdlong t3, offset 'Get a long from HUB RAM, 4 pixels mov t2, #4 'Load the counter for four bytes per long to clock out :bytechck mov cdata, t3 'Move the data over for sending call #SENDDATA 'Send the data shr t3, #8 'Shift the long from memory right by eight to get next byte djnz t2, #:bytechck 'Check if there is a next byte, if so repeat cmp colcnt, colpixel wz 'Test if all the columns have been written (0 to 95), but colpixel is 92 because of long clocking if_nz add colcnt, #4 'No, add another four columns (four pixels in a long if_nz jmp #:col 'No, clock out another column 'Run the row test if colcnt = colpixel (all columns clocked out) cmp rowcnt, rowpixel wz 'Test if all the rows have been written (0 to 63) if_nz add rowcnt, #1 'No, add another row if_nz jmp #:row 'No, clock out another row jmp #ScrnStart 'Yes - do it all over again SENDDATA or outa, DorCmask 'Ensure the data/command line is high andn outa, CSmask 'Ensure the OLED display is selected by putting line low andn outa, WRmask 'set the write line low andn outa, #$FF 'Clear the data on the output lines and cdata, #$FF or outa, cdata 'Set the data on the output lines or outa, WRmask 'Latch in the write SENDDATA_ret ret 'Return to calling program SENDCMD andn outa, DorCmask 'Ensure the data/command line is low andn outa, CSmask 'Ensure the OLED display is selected by putting line low andn outa, WRmask 'set the write line low andn outa, #$FF 'Clear the command on the output lines or outa, cdata 'Set the command on the output lines or outa, WRmask 'Latch in the write SENDCMD_ret ret 'Return to calling program INITIALIZE 'Perform line reset andn outa, RESmask 'Set the reset line low (resets the display) mov r0, #$1FF 'move clock ticks into R0 add r0, cnt 'add in the current clock state waitcnt r0, #0 'pause or outa, RESmask 'Set the reset line high (displays starts to init) 'Power on VCCE andn outa, VCEmask 'Set the VCCE pin low (no voltage) mov cdata, DISPLAY_OFF 'Ensure the display is off via software call #SENDCMD ' or outa, VCEmask 'Set the VCCE pin high (voltage) mov r0, #$1FF 'move clock ticks into R0 add r0, cnt 'add in the current clock state waitcnt r0, #0 'pause 'Send a barrage of commands for initializing mov cdata, DISPLAY_NORMAL 'Normal display call #SENDCMD ' mov cdata, CLOCK_FREQUENCY 'Clock & frequency call #SENDCMD ' mov cdata, #$F0 'Data for above command call #SENDCMD ' mov cdata, DISPLAY_OFFSET 'Set display offset call #SENDCMD ' mov cdata, #$00 'Data for above command call #SENDCMD ' mov cdata, MUX_RATIO 'Duty call #SENDCMD ' mov cdata, #63 'Data for above command (Yes, this is decimal) call #SENDCMD ' mov cdata, MASTER_CONFIGURE 'Master Configure call #SENDCMD ' mov cdata, #$8E 'Data for above command call #SENDCMD ' mov cdata, DISPLAY_START_LINE 'Master Configure call #SENDCMD ' mov cdata, #$00 'Data for above command call #SENDCMD ' mov cdata, REMAP_COLOUR_SETTINGS'Set re-map color/depth call #SENDCMD ' mov cdata, COLORS256 'Data for above command call #SENDCMD ' mov cdata, SET_CONTRAST_MASTER 'Set master contrast call #SENDCMD ' mov cdata, #$0F 'Data for above command call #SENDCMD ' mov cdata, SET_CONTRAST_RED 'Set contrast current for A call #SENDCMD ' mov cdata, #$FF 'Data for above command call #SENDCMD ' mov cdata, SET_CONTRAST_GREEN 'Set contrast current for B call #SENDCMD ' mov cdata, #$FF 'Data for above command call #SENDCMD mov cdata, SET_CONTRAST_BLUE 'Set contrast current for C call #SENDCMD ' mov cdata, #$FF 'Data for above command call #SENDCMD mov cdata, PRECHARGE_RGB 'Set pre-charge voltage of color A B C call #SENDCMD ' mov cdata, #$3E 'Data for above command call #SENDCMD mov cdata, SET_VCOMH 'Set VcomH call #SENDCMD ' mov cdata, #$3E 'Data for above command call #SENDCMD mov cdata, POWERSAVE_MODE 'Set power saving mode call #SENDCMD ' mov cdata, #$00 'Data for above command call #SENDCMD mov cdata, PHASE_PRECHARGE 'Set pre & dis charge call #SENDCMD ' mov cdata, #$11 'Data for above command call #SENDCMD mov cdata, DISPLAY_ON 'Set display on call #SENDCMD ' 'The following commands set parameters, but also ensure starting point on display mov cdata, SET_COLUMN_ADDRESS 'set column address command (and moves cursor) call #SENDCMD mov cdata, #$00 call #SENDCMD mov cdata, #$5F call #SENDCMD mov cdata, SET_ROW_ADDRESS 'set row address command (and moves cursor) call #SENDCMD mov cdata, #$00 call #SENDCMD mov cdata, #$3F call #SENDCMD INITIALIZE_ret ret 'Return to calling program STATCALC mov r1, s0 'Move the start time for a frame sub r1, s1 'Subtract the start time of the previous frame (includes stat calc) wrlong r1, FrmTm 'Write the value to the HUB RAM mov s1, s0 'Store the latest frame start time to the previous start time STATCALC_ret ret 'Return to calling program colpixel long 92 'zero based, OLED is 96 x 64, but because of column count by four, this is 92 rowpixel long 63 'zero based SET_COLUMN_ADDRESS long $15 SET_ROW_ADDRESS long $75 SET_CONTRAST_RED long $81 SET_CONTRAST_GREEN long $82 SET_CONTRAST_BLUE long $83 SET_CONTRAST_MASTER long $87 CONTRAST_RED_2ND long $8A CONTRAST_GREEN_2ND long $8B CONTRAST_BLUE_2ND long $8C REMAP_COLOUR_SETTINGS long $A0 DISPLAY_START_LINE long $A1 DISPLAY_OFFSET long $A2 DISPLAY_NORMAL long $A4 DISPLAY_ALL_ON long $A5 DISPLAY_ALL_OFF long $A6 DISPLAY_INVERSE long $A7 MUX_RATIO long $A8 MASTER_CONFIGURE long $AD DISPLAY_OFF long $AE DISPLAY_ON long $AF POWERSAVE_MODE long $B0 PHASE_PRECHARGE long $B1 CLOCK_FREQUENCY long $B3 SET_GRAYSCALE long $B8 RESET_GRAYSCALE long $B9 PRECHARGE_RGB long $BB SET_VCOMH long $BE NNOP long $E3 LOCK_COMMAND long $FD COLORS256 long $32 '256 colors, 8bit (%RRR_GGG_BB) COLORS65K long $72 t0 res 1 'temporary variable 0 t1 res 1 'temporary variable 1 t2 res 1 'temporary variable 2 t3 res 1 'temporary variable 3 r0 res 1 'temporary variable for use in subroutines r1 res 1 'temporary variable for use in subroutines s0 res 1 'temporary for statistics s1 res 1 'temporary for statistics CSp res 1 'Value of pin assignment CSmask res 1 'Mask of pin assignment DorCp res 1 'Value of pin assignment DorCmask res 1 'Mask of pin assignmenr WRp res 1 'Value of pin assignement WRmask res 1 'Mask of pin assignment RESp res 1 'Value of pin assignment RESmask res 1 'Mask of pin assignment VCEp res 1 'Value of pin assignment VCEmask res 1 'Mask of pin assignment RDp res 1 'Value of pin assignment RDmask res 1 'Mask of pin assignment rowcnt res 1 'Counter of rows for processing colcnt res 1 'Counter of columns for processing pxmask res 1 'Mask of pixel to process offset res 1 'Offset for use with memptr to read HUB RAM memptr res 1 'Pointer to HUB RAM of display memory cdata res 1 'byte variable to hold color data to be sent to OLED FrmTm res 1 'Pointer to FPS measurement「MIdi_In.spin」は以下である。CON _OUTPUT = 1 'Sets pin to output in DIRA register _INPUT = 0 'Sets pin to input in DIRA register _HIGH = 1 'High=ON=1=3.3v DC _ON = 1 _LOW = 0 'Low=OFF=0=0v DC _OFF = 0 _ENABLE = 1 'Enable (turn on) function/mode _DISABLE = 0 'Disable (turn off) function/mode VAR long GRAPHICS_cog 'Cog flag/ID long Xtiles 'Number of x tiles, each tile is 4 pixels by 4 pixels long Ytiles 'Number of y tiles, each tile is 4 pixels by 4 pixels long BitmapBase 'Address of the start of the bitmap video memory long BitmapLongs 'Number of longs in the bitmap video memory PUB setup(_xtiles, _ytiles, _baseptr) '' Setup the bitmap parameters for the graphics driver, must be run '' _xtiles - number of x tiles (tiles are 4 x 4 pixels each because 8-bits x 4 pixes = long) '' _ytiles - number of y tilesl '' _baseptr - base address of bitmap Xtiles := _xtiles Ytiles := _ytiles BitmapBase := _baseptr 'Calculate the values to be used by other routines BitmapLongs := _xtiles * (_ytiles << 2) PUB clear '' Clear bitmap (write zeros to all pixels) longfill(BitmapBase, 0, BitmapLongs) 'Fill the bitmap with zeros PUB copy(_destptr) '' Copy bitmap to new location for use as double-buffered display (flicker-free) '' _destptr - base address of destination bitmap longmove(_destptr, BitmapBase, BitmapLongs) 'Copy bitmap to new destination PUB plotPixel(_x0, _y0, _color) | videoOffset, pixelValue ''' Plot at pixel at x, y, with the appropriate 8-bit color '' _x - coordinate of the pixel '' _y - coordinate of the pixel '' _color - 8-bit color value (RRRGGGBB) 'This byte version work videoOffset := BitmapBase + (_x0 >> 2) * (Ytiles << 4) + (_x0 & %11) + (_y0 << 2) pixelValue := byte[videoOffset] pixelValue := (_color & $FF) byte[videoOffset] := pixelValue { 'This long version works videoOffset := (BitmapBase >> 2) + (_x >> 2) * (Ytiles << 2) + _y pixelValue := long[0][videoOffset] pixelValue := pixelValue & !($ff << ((_x & %11) << 3)) pixelValue := pixelValue | ((_color &$FF) << ((_x & %11) << 3)) long[0][videoOffset] := pixelValue } PUB plotLine(_x0, _y0, _x1, _y1, _color) | dx, dy, difx, dify, sx, sy, ds '' Plot a line from _x0,_y0 to _x1,_y1 with the appropriate 8-bit color '' _x0, _y0 - coordinate of the start pixel '' _x1, _y1 - coordinate of the end pixel '' _color - 8-bit color value (RRRGGGBB) '' Based on routine from Phil on Parallax Forum difx := ||(_x0 - _x1) 'Number of pixels in X direciton. dify := ||(_y0 - _y1) 'Number of pixels in Y direction. ds := difx <# dify 'State variable change: smaller of difx and dify. sx := dify >> 1 'State variables: >>1 to split remainders between line ends. sy := difx >> 1 dx := (_x1 < _x0) | 1 'X direction: -1 or 1 dy := (_y1 < _y0) | 1 'Y direction: -1 or 1 repeat (difx #> dify) + 1 'Number of pixels to draw is greater of difx and dify, plus one. plotPixel(_x0, _y0, _color) 'Draw the current point. if ((sx -= ds) =< 0) 'Subtract ds from x state. =< 0 ? sx += dify ' Yes: Increment state by dify. _x0 += dx ' Move X one pixel in X direciton. if ((sy -= ds) =< 0) 'Subtract ds from y state. =< 0 ? sy += difx ' Yes: Increment state by difx. _y0 += dy ' Move Y one pixel in Y direction. PUB plotCircle(_x0, _y0, _radius, _color) | sum, x, y '' Plot a circle with center _x0,_y0 and radius _radius with the appropriate 8-bit color '' _x0, _y0 - coordinate of the center of the circle '' _radius - radius, in pixels, of the circle '' _color - 8-bit color value (RRRGGGBB) '' Based on routines from Paul Sr. on Parallax Forum x := 0 y := _radius sum := (5-_radius*4)/4 circleHelper(_x0, _y0, x, y, _color) repeat while (x < y) x++ if (sum < 0) sum += 2*x+1 else y-- sum += 2*(x-y)+1 circleHelper(_x0, _y0, x, y, _color) circleHelper(_x0, _y0, x, y, _color) PUB plotSprite(_x0, _y0, _spritePTR) | xpix, ypix, x, y '' Plot a pixel sprite into the video memory. '' _x0, _y0 - coordinate of the center of the sprite '' _spritePTR - pointer to pixel sprite memory location '' long '' byte xpixels, ypixels, xorigin, yorigin '' long %RRRGGGBB, %RRRGGGBB, %RRRGGGBB, %RRRGGGBB '' long %RRRGGGBB, %RRRGGGBB, %RRRGGGBB, %RRRGGGBB '' long %RRRGGGBB, %RRRGGGBB, %RRRGGGBB, %RRRGGGBB '' long %RRRGGGBB, %RRRGGGBB, %RRRGGGBB, %RRRGGGBB '' .... xpix := byte[_spritePTR][0] ypix := byte[_spritePTR][1] repeat y from 0 to ypix-1 repeat x from 0 to xpix-1 plotPixel(_x0+x, _y0+y, byte[_spritePTR][4+x+(xpix*y)]) PUB plotChar(_char, _xC, _yC, _font, _color) | row, col '' Plot a single character into the video memory. '' _char - The character '' _xC - Text column (0-11 for 8x8 font, 0-15 for 5x7 font) '' _yC - Text row (0-7 for 8x8 and 5x7 font) '' _font - The font, if 1 then 8x8, else 5x7 '' _color - 8-bit color value (RRRGGGBB) '' Based on routines from 4D System uOLED driver _char := (_char - " ") << 3 if _font ' font 1 8x8 _xC <<= 3 ' x 8 _yC <<= 3 ' x 8 repeat row from 0 to 7 repeat col from 0 to 7 if font_8x8[_char+row] & $80 >> col plotPixel(_xC+col,_yC+row, _color) else ' font 0 5x7 _xC *= 6 ' x 6 _yC *= 8 ' x 7 repeat row from 0 to 7 repeat col from 1 to 6 if font_5x7[_char+row] & $01 << col plotPixel(_xC+col,_yC+row, _color) PUB plotText (_xC, _yC, _font, _color, _str) | t '' Plot a string of characters into the video memory. '' _xC - Text column (0-11 for 8x8 font, 0-15 for 5x7 font) '' _yC - Text row (0-7 for 8x8 and 5x7 font) '' _font - The font, if 1 then 8x8, else 5x7 '' _color - 8-bit color value (RRRGGGBB) '' _str - String of characters '' Based on routines from 4D System uOLED driver repeat strsize(_str) plotChar((byte[_str++]), _xC++, _yC, _font, _color) if _font if _xC > 95 / 8 _xC := 0 _yC += 1 elseif _xC > 95 / 6 _xC := 0 _yC += 1 PRI circleHelper(_cx, _cy, _x, _y, _color) '' helps to draw a circle on the screen, used with plotcircle '' Based on routiness from Paul Sr. on Parallax Forum if (_x == 0) plotPixel(_cx, _cy + _y, _color) plotPixel(_cx, _cy - _y, _color) plotPixel(_cx + _y, _cy, _color) plotPixel(_cx - _y, _cy, _color) else if (_x == _y) plotPixel(_cx + _x, _cy + _y, _color) plotPixel(_cx - _x, _cy + _y, _color) plotPixel(_cx + _x, _cy - _y, _color) plotPixel(_cx - _x, _cy - _y, _color) else if (_x < _y) plotPixel(_cx + _x, _cy + _y, _color) plotPixel(_cx - _x, _cy + _y, _color) plotPixel(_cx + _x, _cy - _y, _color) plotPixel(_cx - _x, _cy - _y, _color) plotPixel(_cx + _y, _cy + _x, _color) plotPixel(_cx - _y, _cy + _x, _color) plotPixel(_cx + _y, _cy - _x, _color) plotPixel(_cx - _y, _cy - _x, _color) DAT font_8x8 byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000 byte %00110000,%00110000,%00110000,%00110000,%00110000,%00000000,%00110000,%00000000 byte %01101100,%01101100,%01101100,%00000000,%00000000,%00000000,%00000000,%00000000 byte %01101100,%01101100,%11111110,%01101100,%11111110,%01101100,%01101100,%00000000 byte %00110000,%01111100,%11000000,%01111000,%00001100,%11111000,%00110000,%00000000 byte %00000000,%11000110,%11001100,%00011000,%00110000,%01100110,%11000110,%00000000 byte %00111000,%01101100,%00111000,%01110110,%11011100,%11001100,%01110110,%00000000 byte %01100000,%01100000,%11000000,%00000000,%00000000,%00000000,%00000000,%00000000 byte %00011000,%00110000,%01100000,%01100000,%01100000,%00110000,%00011000,%00000000 byte %01100000,%00110000,%00011000,%00011000,%00011000,%00110000,%01100000,%00000000 byte %00000000,%01100110,%00111100,%11111111,%00111100,%01100110,%00000000,%00000000 byte %00000000,%00110000,%00110000,%11111100,%00110000,%00110000,%00000000,%00000000 byte %00000000,%00000000,%00000000,%00000000,%00000000,%00110000,%00110000,%01100000 byte %00000000,%00000000,%00000000,%11111100,%00000000,%00000000,%00000000,%00000000 byte %00000000,%00000000,%00000000,%00000000,%00000000,%00110000,%00110000,%00000000 byte %00000100,%00001100,%00011000,%00110000,%01100000,%11000000,%10000000,%00000000 byte %01111100,%11000110,%11001110,%11011110,%11110110,%11100110,%01111100,%00000000 byte %00110000,%01110000,%00110000,%00110000,%00110000,%00110000,%11111100,%00000000 byte %01111000,%11001100,%00001100,%00111000,%01100000,%11001100,%11111100,%00000000 byte %01111000,%11001100,%00001100,%00111000,%00001100,%11001100,%01111000,%00000000 byte %00011100,%00111100,%01101100,%11001100,%11111110,%00001100,%00011110,%00000000 byte %11111100,%11000000,%11111000,%00001100,%00001100,%11001100,%01111000,%00000000 byte %00111000,%01100000,%11000000,%11111000,%11001100,%11001100,%01111000,%00000000 byte %11111100,%11001100,%00001100,%00011000,%00110000,%00110000,%00110000,%00000000 byte %01111000,%11001100,%11001100,%01111000,%11001100,%11001100,%01111000,%00000000 byte %01111000,%11001100,%11001100,%01111100,%00001100,%00011000,%01110000,%00000000 byte %00000000,%00110000,%00110000,%00000000,%00000000,%00110000,%00110000,%00000000 byte %00000000,%00110000,%00110000,%00000000,%00000000,%00110000,%00110000,%01100000 byte %00011000,%00110000,%01100000,%11000000,%01100000,%00110000,%00011000,%00000000 byte %00000000,%00000000,%11111100,%00000000,%00000000,%11111100,%00000000,%00000000 byte %01100000,%00110000,%00011000,%00001100,%00011000,%00110000,%01100000,%00000000 byte %01111000,%11001100,%00001100,%00011000,%00110000,%00000000,%00110000,%00000000 byte %01111100,%11000110,%11011110,%11011110,%11011110,%11000000,%01111000,%00000000 byte %00110000,%01111000,%11001100,%11001100,%11111100,%11001100,%11001100,%00000000 byte %11111100,%01100110,%01100110,%01111100,%01100110,%01100110,%11111100,%00000000 byte %00111100,%01100110,%11000000,%11000000,%11000000,%01100110,%00111100,%00000000 byte %11111000,%01101100,%01100110,%01100110,%01100110,%01101100,%11111000,%00000000 byte %01111110,%01100000,%01100000,%01111000,%01100000,%01100000,%01111110,%00000000 byte %01111110,%01100000,%01100000,%01111000,%01100000,%01100000,%01100000,%00000000 byte %00111100,%01100110,%11000000,%11000000,%11001110,%01100110,%00111110,%00000000 byte %11001100,%11001100,%11001100,%11111100,%11001100,%11001100,%11001100,%00000000 byte %01111000,%00110000,%00110000,%00110000,%00110000,%00110000,%01111000,%00000000 byte %00011110,%00001100,%00001100,%00001100,%11001100,%11001100,%01111000,%00000000 byte %11100110,%01100110,%01101100,%01111000,%01101100,%01100110,%11100110,%00000000 byte %01100000,%01100000,%01100000,%01100000,%01100000,%01100000,%01111110,%00000000 byte %11000110,%11101110,%11111110,%11111110,%11010110,%11000110,%11000110,%00000000 byte %11000110,%11100110,%11110110,%11011110,%11001110,%11000110,%11000110,%00000000 byte %00111000,%01101100,%11000110,%11000110,%11000110,%01101100,%00111000,%00000000 byte %11111100,%01100110,%01100110,%01111100,%01100000,%01100000,%11110000,%00000000 byte %01111000,%11001100,%11001100,%11001100,%11011100,%01111000,%00011100,%00000000 byte %11111100,%01100110,%01100110,%01111100,%01101100,%01100110,%11100110,%00000000 byte %01111000,%11001100,%11100000,%01111000,%00011100,%11001100,%01111000,%00000000 byte %11111100,%00110000,%00110000,%00110000,%00110000,%00110000,%00110000,%00000000 byte %11001100,%11001100,%11001100,%11001100,%11001100,%11001100,%11111100,%00000000 byte %11001100,%11001100,%11001100,%11001100,%11001100,%01111000,%00110000,%00000000 byte %11000110,%11000110,%11000110,%11010110,%11111110,%11101110,%11000110,%00000000 byte %11000110,%11000110,%01101100,%00111000,%00111000,%01101100,%11000110,%00000000 byte %11001100,%11001100,%11001100,%01111000,%00110000,%00110000,%01111000,%00000000 byte %11111110,%00000110,%00001100,%00011000,%00110000,%01100000,%11111110,%00000000 byte %01111000,%01100000,%01100000,%01100000,%01100000,%01100000,%01111000,%00000000 byte %11000000,%01100000,%00110000,%00011000,%00001100,%00000110,%00000010,%00000000 byte %01111000,%00011000,%00011000,%00011000,%00011000,%00011000,%01111000,%00000000 byte %00010000,%00111000,%01101100,%11000110,%00000000,%00000000,%00000000,%00000000 byte %00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000,%11111111 byte %00110000,%00110000,%00011000,%00000000,%00000000,%00000000,%00000000,%00000000 byte %00000000,%00000000,%01111000,%00001100,%01111100,%11001100,%01110110,%00000000 byte %11100000,%01100000,%01100000,%01111100,%01100110,%01100110,%11011100,%00000000 byte %00000000,%00000000,%01111000,%11001100,%11000000,%11001100,%01111000,%00000000 byte %00011100,%00001100,%00001100,%01111100,%11001100,%11001100,%01110110,%00000000 byte %00000000,%00000000,%01111000,%11001100,%11111100,%11000000,%01111000,%00000000 byte %00111000,%01101100,%01100000,%11110000,%01100000,%01100000,%11110000,%00000000 byte %00000000,%00000000,%01110110,%11001100,%11001100,%01111100,%00001100,%11111000 byte %11100000,%01100000,%01101100,%01110110,%01100110,%01100110,%11100110,%00000000 byte %00110000,%00000000,%01110000,%00110000,%00110000,%00110000,%01111000,%00000000 byte %00001100,%00000000,%00001100,%00001100,%00001100,%11001100,%11001100,%01111000 byte %11100000,%01100000,%01100110,%01101100,%01111000,%01101100,%11100110,%00000000 byte %01110000,%00110000,%00110000,%00110000,%00110000,%00110000,%01111000,%00000000 byte %00000000,%00000000,%11001100,%11111110,%11111110,%11010110,%11000110,%00000000 byte %00000000,%00000000,%11111000,%11001100,%11001100,%11001100,%11001100,%00000000 byte %00000000,%00000000,%01111000,%11001100,%11001100,%11001100,%01111000,%00000000 byte %00000000,%00000000,%11011100,%01100110,%01100110,%01111100,%01100000,%11110000 byte %00000000,%00000000,%01110110,%11001100,%11001100,%01111100,%00001100,%00011110 byte %00000000,%00000000,%11011100,%01110110,%01100110,%01100000,%11110000,%00000000 byte %00000000,%00000000,%01111100,%11000000,%01111000,%00001100,%11111000,%00000000 byte %00010000,%00110000,%01111100,%00110000,%00110000,%00110100,%00011000,%00000000 byte %00000000,%00000000,%11001100,%11001100,%11001100,%11001100,%01110110,%00000000 byte %00000000,%00000000,%11001100,%11001100,%11001100,%01111000,%00110000,%00000000 byte %00000000,%00000000,%11000110,%11010110,%11111110,%11111110,%01101100,%00000000 byte %00000000,%00000000,%11000110,%01101100,%00111000,%01101100,%11000110,%00000000 byte %00000000,%00000000,%11001100,%11001100,%11001100,%01111100,%00001100,%11111000 byte %00000000,%00000000,%11111100,%10011000,%00110000,%01100100,%11111100,%00000000 byte %00011100,%00110000,%00110000,%11100000,%00110000,%00110000,%00011100,%00000000 byte %00011000,%00011000,%00011000,%00000000,%00011000,%00011000,%00011000,%00000000 byte %11100000,%00110000,%00110000,%00011100,%00110000,%00110000,%11100000,%00000000 byte %01110110,%11011100,%00000000,%00000000,%00000000,%00000000,%00000000,%00000000 byte %00000000,%01100110,%01100110,%01100110,%01100110,%01100110,%01011100,%10000000 font_5x7 byte $00,$00,$00,$00,$00,$00,$00,$00 ' space byte $02,$02,$02,$02,$02,$00,$02,$00 ' "!" byte $36,$12,$24,$00,$00,$00,$00,$00 ' """ byte $00,$14,$3E,$14,$3E,$14,$00,$00 ' "#" byte $08,$3C,$0A,$1C,$28,$1E,$08,$00 ' "$" byte $22,$22,$10,$08,$04,$22,$22,$00 ' "%" byte $04,$0A,$0A,$04,$2A,$12,$2C,$00 ' "&" byte $18,$10,$08,$00,$00,$00,$00,$00 ' "'" byte $20,$10,$08,$08,$08,$10,$20,$00 ' "(" byte $02,$04,$08,$08,$08,$04,$02,$00 ' ")" byte $00,$08,$2A,$1C,$1C,$2A,$08,$00 ' "*" byte $00,$08,$08,$3E,$08,$08,$00,$00 ' "+" byte $00,$00,$00,$00,$00,$06,$04,$02 ' "," byte $00,$00,$00,$3E,$00,$00,$00,$00 ' "-" byte $00,$00,$00,$00,$00,$06,$06,$00 ' "." byte $20,$20,$10,$08,$04,$02,$02,$00 ' "/" byte $1C,$22,$32,$2A,$26,$22,$1C,$00 ' "0" byte $08,$0C,$08,$08,$08,$08,$1C,$00 ' "1" byte $1C,$22,$20,$10,$0C,$02,$3E,$00 ' "2" byte $1C,$22,$20,$1C,$20,$22,$1C,$00 ' "3" byte $10,$18,$14,$12,$3E,$10,$10,$00 ' "4" byte $3E,$02,$1E,$20,$20,$22,$1C,$00 ' "5" byte $18,$04,$02,$1E,$22,$22,$1C,$00 ' "6" byte $3E,$20,$10,$08,$04,$04,$04,$00 ' "7" byte $1C,$22,$22,$1C,$22,$22,$1C,$00 ' "8" byte $1C,$22,$22,$3C,$20,$10,$0C,$00 ' "9" byte $00,$06,$06,$00,$06,$06,$00,$00 ' ":" byte $00,$06,$06,$00,$06,$06,$04,$02 ' ";" byte $20,$10,$08,$04,$08,$10,$20,$00 ' "<" byte $00,$00,$3E,$00,$3E,$00,$00,$00 ' "=" byte $02,$04,$08,$10,$08,$04,$02,$00 ' ">" byte $1C,$22,$20,$10,$08,$00,$08,$00 ' "?" byte $1C,$22,$2A,$2A,$1A,$02,$3C,$00 ' "@" byte $08,$14,$22,$22,$3E,$22,$22,$00 ' "A" byte $1E,$22,$22,$1E,$22,$22,$1E,$00 ' "B" byte $18,$24,$02,$02,$02,$24,$18,$00 ' "C" byte $0E,$12,$22,$22,$22,$12,$0E,$00 ' "D" byte $3E,$02,$02,$1E,$02,$02,$3E,$00 ' "E" byte $3E,$02,$02,$1E,$02,$02,$02,$00 ' "F" byte $1C,$22,$02,$02,$32,$22,$1C,$00 ' "G" byte $22,$22,$22,$3E,$22,$22,$22,$00 ' "H" byte $3E,$08,$08,$08,$08,$08,$3E,$00 ' "I" byte $20,$20,$20,$20,$20,$22,$1C,$00 ' "J" byte $22,$12,$0A,$06,$0A,$12,$22,$00 ' "K" byte $02,$02,$02,$02,$02,$02,$3E,$00 ' "L" byte $22,$36,$2A,$2A,$22,$22,$22,$00 ' "M" byte $22,$22,$26,$2A,$32,$22,$22,$00 ' "N" byte $1C,$22,$22,$22,$22,$22,$1C,$00 ' "O" byte $1E,$22,$22,$1E,$02,$02,$02,$00 ' "P" byte $1C,$22,$22,$22,$2A,$12,$2C,$00 ' "Q" byte $1E,$22,$22,$1E,$0A,$12,$22,$00 ' "R" byte $1C,$22,$02,$1C,$20,$22,$1C,$00 ' "S" byte $3E,$08,$08,$08,$08,$08,$08,$00 ' "T" byte $22,$22,$22,$22,$22,$22,$1C,$00 ' "U" byte $22,$22,$22,$14,$14,$08,$08,$00 ' "V" byte $22,$22,$22,$2A,$2A,$2A,$14,$00 ' "W" byte $22,$22,$14,$08,$14,$22,$22,$00 ' "X" byte $22,$22,$14,$08,$08,$08,$08,$00 ' "Y" byte $3E,$20,$10,$08,$04,$02,$3E,$00 ' "Z" byte $3E,$06,$06,$06,$06,$06,$3E,$00 ' "[" byte $02,$02,$04,$08,$10,$20,$20,$00 ' "\" byte $3E,$30,$30,$30,$30,$30,$3E,$00 ' "]" byte $00,$00,$08,$14,$22,$00,$00,$00 ' "^" byte $00,$00,$00,$00,$00,$00,$00,$7F ' "_" byte $10,$08,$18,$00,$00,$00,$00,$00 ' "`" byte $00,$00,$1C,$20,$3C,$22,$3C,$00 ' "a" byte $02,$02,$1E,$22,$22,$22,$1E,$00 ' "b" byte $00,$00,$3C,$02,$02,$02,$3C,$00 ' "c" byte $20,$20,$3C,$22,$22,$22,$3C,$00 ' "d" byte $00,$00,$1C,$22,$3E,$02,$3C,$00 ' "e" byte $18,$24,$04,$1E,$04,$04,$04,$00 ' "f" byte $00,$00,$1C,$22,$22,$3C,$20,$1C ' "g" byte $02,$02,$1E,$22,$22,$22,$22,$00 ' "h" byte $08,$00,$0C,$08,$08,$08,$1C,$00 ' "i" byte $10,$00,$18,$10,$10,$10,$12,$0C ' "j" byte $02,$02,$22,$12,$0C,$12,$22,$00 ' "k" byte $0C,$08,$08,$08,$08,$08,$1C,$00 ' "l" byte $00,$00,$36,$2A,$2A,$2A,$22,$00 ' "m" byte $00,$00,$1E,$22,$22,$22,$22,$00 ' "n" byte $00,$00,$1C,$22,$22,$22,$1C,$00 ' "o" byte $00,$00,$1E,$22,$22,$1E,$02,$02 ' "p" byte $00,$00,$3C,$22,$22,$3C,$20,$20 ' "q" byte $00,$00,$3A,$06,$02,$02,$02,$00 ' "r" byte $00,$00,$3C,$02,$1C,$20,$1E,$00 ' "s" byte $04,$04,$1E,$04,$04,$24,$18,$00 ' "t" byte $00,$00,$22,$22,$22,$32,$2C,$00 ' "u" byte $00,$00,$22,$22,$22,$14,$08,$00 ' "v" byte $00,$00,$22,$22,$2A,$2A,$36,$00 ' "w" byte $00,$00,$22,$14,$08,$14,$22,$00 ' "x" byte $00,$00,$22,$22,$22,$3C,$20,$1C ' "y" byte $00,$00,$3E,$10,$08,$04,$3E,$00 ' "z" byte $38,$0C,$0C,$06,$0C,$0C,$38,$00 ' "{" byte $08,$08,$08,$08,$08,$08,$08,$08 ' "|" byte $0E,$18,$18,$30,$18,$18,$0E,$00 ' "}" byte $00,$2C,$1A,$00,$00,$00,$00,$00 ' "~" byte $7F,$7F,$7F,$7F,$7F,$7F,$7F,$7F ' --「MIdi_Out.spin」は以下である。VAR long rx_Head, rx_Tail, rx_Buff[64] PUB start(_midiPin) : status midiPin := _midiPin rx_top := @rx_Head rx_end := @rx_Tail rx_fifo := @rx_Buff bitticks := clkfreq / 31_250 * 80 / 64 '##### special setting !! ##### halfticks := bitticks / 2 longfill(@rx_Head,66,0) status := cognew(@asm_entry, 0) PUB event : status status := -1 if rx_Tail <> rx_Head status := rx_Buff[rx_Tail] rx_Tail := (rx_Tail + 1) & $3F DAT org asm_entry mov midiMask,#1 shl midiMask,midiPin getMidiByte waitpeq midiMask,midiMask mov bitClk,cnt add bitClk,halfticks add bitClk,bitticks mov testBits,#9 :check_loop waitcnt bitClk,bitticks test midiMask,ina wc rcr rx_data,#1 djnz testBits,#:check_loop shr rx_data,#32-9 xor rx_data,#$FF and rx_data,#$FF test rx_data,#%10000000 wz if_z jmp #:running mov t1,rx_data and t1,#%11110000 cmp t1,#%11110000 wz if_z jmp #getMidiByte mov rsb,rx_data mov dcb,#0 jmp #getMidiByte :running mov t1,rsb and t1,#%11100000 cmp t1,#%11000000 wz if_z jmp #:byte_2 tjnz dcb,#:byte_3 add dcb,#1 mov keyno,rx_data jmp #getMidiByte :byte_2 mov event_data,rsb shl event_data,#16 or event_data,rx_data jmp #:write_event :byte_3 mov dcb,#0 mov event_data,rsb shl event_data,#16 mov t1,keyno shl t1,#8 or event_data,t1 or event_data,rx_data :write_event rdlong t1,rx_top mov rx_pointer,t1 shl rx_pointer,#2 add rx_pointer,rx_fifo wrlong event_data,rx_pointer add t1,#1 and t1,#$3F wrlong t1,rx_top jmp #getMidiByte t1 long 0 midiMask long 0 testBits long 0 bitClk long 0 bitticks long 0 halfticks long 0 midiPin long 0 rx_top long 0 rx_end long 0 rx_fifo long 0 rx_data long 0 rx_pointer long 0 event_data long 0 rsb long 0 dcb long 0 keyno long 0 fit「Demo_OLED.spin」は以下である。VAR long tx_Head, tx_Tail, tx_Buff[64] PUB start(_midiPin) : status midiPin := _midiPin tx_top := @tx_Head tx_end := @tx_Tail tx_fifo := @tx_Buff bitticks := clkfreq / 31_250 longfill(@tx_Head,66,0) status := cognew(@asm_entry, 0) PUB fifoset(_tx_data) tx_Buff[tx_Head] := _tx_data tx_Head := (tx_Head + 1) & $3F DAT org asm_entry mov midiMask,#1 shl midiMask,midiPin or dira,midiMask :fifo_check rdlong t1,tx_end rdlong t2,tx_top cmp t1,t2 wz if_z jmp #:fifo_check mov t2,t1 shl t1,#2 add t1,tx_fifo rdlong event_data,t1 mov t1,t2 add t1,#1 and t1,#$3F wrlong t1,tx_end mov tx_data,event_data shr tx_data,#16 call #send_event and tx_data,#%11100000 cmp tx_data,#%11000000 wz if_z jmp #:byte_2 mov tx_data,event_data shr tx_data,#8 call #send_event :byte_2 mov tx_data,event_data call #send_event jmp #:fifo_check send_event xor tx_data,#$FF and tx_data,#$FF shl tx_data,#1 or tx_data,#1 mov testBits,#10 mov bitClk,cnt add bitClk,bitticks :bit_send shr tx_data,#1 wc muxc outa,midiMask waitcnt bitClk,bitticks djnz testBits,#:bit_send send_event_ret ret t1 long 0 t2 long 0 midiMask long 0 testBits long 0 bitClk long 0 bitticks long 0 midiPin long 0 tx_top long 0 tx_end long 0 tx_fifo long 0 tx_data long 0 event_data long 0 fit「PellerMin.spin」は以下である。CON _clkmode = xtal1 + pll8x 'Use the PLL to multiple the external clock by 8 _xinfreq = 8_000_000 'An external clock of 8MHz. is used (64MHz. operation) _OUTPUT = 1 'Sets pin to output in DIRA register _INPUT = 0 'Sets pin to input in DIRA register _HIGH = 1 'High=ON=1=3.3v DC _ON = 1 _LOW = 0 'Low=OFF=0=0v DC _OFF = 0 _ENABLE = 1 'Enable (turn on) function/mode _DISABLE = 0 'Disable (turn off) function/mode _xpixels = 96 'Screen width _ypixels = 64 'Screen height _pixelperlong = 4 'Each tile requires 4 longs _xtiles = _xpixels/_pixelperlong 'Each tile is 4 pixels x 4 pixels _ytiles = _ypixels/_pixelperlong _screensize = (_xtiles * _ytiles * _pixelperlong) 'Size needed for arrays _red = %11100000 _green = %00011100 _blue = %00000011 _yellow = %11111100 _purple = %11100011 _turq = %00011111 _white = %11111111 _black = %00000000 VAR long MemDisp[_screensize] 'OLED display driver variables long MemWork[_screensize] 'graphics driver variables OBJ OLED : "OLED-Driver1" Graphics : "OLED-Driver2" midiIn : "Midi_In" PUB main | mode, x, d, p, dummy, i, color, eraser_count OLED.start(@MemDisp) Graphics.setup(_xtiles, _ytiles, @MemWork) midiIn.start(21) dira[18..20]~ mode := 1 p := 0 repeat case mode 0: color := 0 repeat while mode == 0 if color > 127 color := 0 i := 1 repeat while mode == 0 if i > 45 i := 1 Graphics.clear Graphics.plotCircle(48,32,i, color) color++ Graphics.plotCircle(48,32,i+5, color) color++ Graphics.plotCircle(48,32,i-5, color) Graphics.copy(@MemDisp) i++ dummy := midiIn.event if INA[19] == 0 mode := 1 Graphics.clear Graphics.copy(@MemDisp) elseif INA[20] == 0 mode := 2 p := 0 Graphics.clear Graphics.copy(@MemDisp) 1: plines[0] := 10 plines[1] := 3 plines[2] := 50 plines[3] := 60 plines[4] := 10 plines[5] := 3 plines[6] := 50 plines[7] := 60 vlines[0] := 1 vlines[1] := 2 vlines[2] := -3 vlines[3] := 5 vlines[4] := 1 vlines[5] := 2 vlines[6] := -3 vlines[7] := 5 Graphics.clear eraser_count := 0 repeat while mode == 1 color++ if (++eraser_count > 7) Graphics.plotLine(plines[4], plines[5], plines[6], plines[7], _black) plines[4] += vlines[4] plines[5] += vlines[5] plines[6] += vlines[6] plines[7] += vlines[7] if (plines[4] > 95) vlines[4] := -vlines[4] plines[4] += vlines[4] if (plines[6] > 95) vlines[6] := -vlines[6] plines[6] += vlines[6] if (plines[5] > 63) vlines[5] := -vlines[5] plines[5] += vlines[5] if (plines[7] > 63) vlines[7] := -vlines[7] plines[7] += vlines[7] Graphics.plotLine(plines[0], plines[1], plines[2], plines[3], color) plines[0] += vlines[0] plines[1] += vlines[1] plines[2] += vlines[2] plines[3] += vlines[3] if (plines[0] > 95) vlines[0] := -vlines[0] plines[0] += vlines[0] if (plines[2] > 95) vlines[2] := -vlines[2] plines[2] += vlines[2] if (plines[1] > 63) vlines[1] := -vlines[1] plines[1] += vlines[1] if (plines[3] > 63) vlines[3] := -vlines[3] plines[3] += vlines[3] Graphics.copy(@MemDisp) dummy := midiIn.event if INA[18] == 0 mode := 0 Graphics.clear Graphics.copy(@MemDisp) elseif INA[20] == 0 mode := 2 p := 0 Graphics.clear Graphics.copy(@MemDisp) 2: dummy := midiIn.event if dummy <> -1 d := (dummy & $FF0000) >> 16 Graphics.plotChar(HexConv(d/16), 3*(p//5)+1, p/5, 0, _green) Graphics.plotChar(HexConv(d//16), 3*(p//5)+2, p/5, 0, _green) Graphics.copy(@MemDisp) p := place(p) if (d > $DF) or (d < $C0) d := (dummy & $00FF00) >> 8 Graphics.plotChar(HexConv(d/16), 3*(p//5)+1, p/5, 0, _white) Graphics.plotChar(HexConv(d//16), 3*(p//5)+2, p/5, 0, _white) Graphics.copy(@MemDisp) p := place(p) d := dummy & $0000FF Graphics.plotChar(HexConv(d/16), 3*(p//5)+1, p/5, 0, _white) Graphics.plotChar(HexConv(d//16), 3*(p//5)+2, p/5, 0, _white) Graphics.copy(@MemDisp) p := place(p) if INA[18] == 0 mode := 0 Graphics.clear Graphics.copy(@MemDisp) elseif INA[19] == 0 mode := 1 Graphics.clear Graphics.copy(@MemDisp) PUB place(p) p := ++p//40 if p == 0 Graphics.clear Graphics.copy(@MemDisp) return(p) PUB HexConv(d) if d<10 return("0"+d) else return("A"+d-10) PRI pauseMSec(Duration) waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt) DAT ' data structure is array of points, each point is in x,y form, each line needs two points plines word 10,3 ' line 1, endpoint 1 word 50,60' line 1, endpoint 2 word 10,3 ' line 2, endpoint 1 word 50,60' line 2, endpoint 2 'data structure is an array of velocity vectors in vx, vy form, each pair of numbers used to translate a point vlines word 1,2 ' line 1, endpoint 1 velocity word -3,5 ' line 1, endpoint 2 velocity word 1,2 ' line 2, endpoint 1 velocity word -3,5 ' line 2, endpoint 2 velocityこれで準備が出来たので、まずは「Demo_OLED.spin」をコピーして「XBee_001.spin」として、 ここに外部オブジェクトモジュールとして「Midi_Out.spin」を加えて、 mainではMIDI入力処理をいったんコメントアウトして、 「PellerMin.spin」からMIDI出力の呼び出しを加えつつXBeeから出力して、 同時にOLEDに何か描画する、という最初のプログラムに挑戦した。 XBeeからのデータをMaxでモニタしているので、まずはXBeeで送信側のPropellerの「生存証明」を送ろう、 という作戦である。{{ Inst010.spin }} CON _clkmode = xtal1 + pll16x _xinfreq = 5_000_000 OBJ midiOut : "Midi_Out" PUB main | dummy, i[3], j[6], mode, ad, addr, k[4], value1[32], value2[32], value3, value4[32], value5[32] midiOut.start(27) port_initial repeat ad from 0 to 3 k[ad] := 0 ' k[ad] = A/D channel (0-7) ad_initial(ad,k[ad]) repeat i from 0 to 32 value4[i] := threshold_init(i) value5[i] := max_value_init(i) j[5] := 8 repeat if mode <> 0 repeat ad from 0 to 3 outa[10..8] := 5 ' EOC check port (3..0) dummy := ina[ad] ' EOC bit (0=EOC) outa[10..8]~~ if dummy == 0 addr := ad<<3 + k[ad] value2[addr] := value1[addr] outa[10..8] := ad value3 := (255 - ina[7..0]) >>1 ' value3 = A/D data outa[10..8]~~ if value3 < value4[addr] value3 := value4[addr] dummy := ( (value3 - value4[addr])<<7 ) / ( value5[addr] - value4[addr] ) value1[addr] := dummy if value2[addr] <> value1[addr] dummy += $B00000 + ad<<16 + k[ad]<<8 midiOut.fifoset(dummy) k[ad] := ((k[ad]+1) & %111) ad_initial(ad,k[ad]) i[0] := cnt & $6000000 if i[0] <> i[1] i[1] := i[0] i[2] := cnt>>25 dummy := $B40000 + i[2] midiOut.fifoset(dummy) out_574( %001, seg7_conv( (i[2])//10 ) ) j[0] := cnt & $7FC00000 ' Switch Scan if j[0] <> j[1] j[1] := j[0] outa[10..8] := 4 j[2] := ina[5..0] ' Non Lock Type SW 6 bits outa[10..8]~~ if j[3] <> j[2] j[3] := j[2] dummy := $B60000 + j[2] midiOut.fifoset(dummy) outa[10..8] := 5 j[4] := ina[6..4] ' Toggle Type SW 3 bits outa[10..8]~~ if j[5] <> j[4] j[5] := j[4] mode := j[4] dummy := $B50000 + mode midiOut.fifoset(dummy) out_574( %111, seg7_conv(mode) ) if mode > 3 out_574(4,$FF) out_574(5,$FF) else out_574(4,$00) out_574(5,$00) PUB threshold_init(id) case id 0: return(13) 1: return(13) 2: return(13) 3: return(13) 4: return(13) 5: return(13) 6: return(12) 7: return(12) 8: return(11) 9: return(25) 10: return(20) 11: return(14) 12: return(10) 13: return(11) 14: return(11) 15: return(16) 16: return(104) 17: return(79) 18: return(58) 19: return(53) 20: return(72) 21: return(58) 22: return(37) 23: return(75) 24: return(45) 25: return(56) 26: return(74) 27: return(62) 28: return(87) 29: return(65) 30: return(56) 31: return(78) PUB max_value_init(id) case id 0: return(79) 1: return(79) 2: return(79) 3: return(79) 4: return(79) 5: return(79) 6: return(79) 7: return(79) 8: return(79) 9: return(79) 10: return(79) 11: return(79) 12: return(79) 13: return(79) 14: return(79) 15: return(79) 16: return(121) 17: return(121) 18: return(121) 19: return(121) 20: return(121) 21: return(115) 22: return(100) 23: return(121) 24: return(115) 25: return(115) 26: return(121) 27: return(116) 28: return(121) 29: return(121) 30: return(121) 31: return(121) PUB out_574(sel,data) outa[18..11] := data outa[21..19] := sel outa[22]~ outa[22]~~ PUB seg7_conv(data) case data 0: return(%00000011) 1: return(%10011111) 2: return(%00100101) 3: return(%00001101) 4: return(%10011001) 5: return(%01001001) 6: return(%01000001) 7: return(%00011011) 8: return(%00000001) 9: return(%00001001) PUB port_initial dira[7..0]~ ' Input Bus <-- 245 dira[10..8]~~ ' Input Select : %110 - %000 (disable = %111) outa[10..8]~~ ' --> normal disable %111 dira[18..11]~~ ' Output Bus --> 574 dira[21..19]~~ ' Output Select : %111 - %000 dira[22]~~ ' Output Select Enable (active low) outa[22]~~ ' --> normal High out_574(%001,$FF) ' 7seg Red LED off out_574(%111,$FF) ' 7seg Green LED off out_574(4,0) ' Blue LED(1) off out_574(5,0) ' Blue LED(2) off PUB ad_initial(ad,addr) case ad 0: out_574(0,addr+%00000000) out_574(0,addr+%00001000) out_574(0,addr+%00011000) out_574(0,addr+%00010000) out_574(0,addr+%00000000) return 1: out_574(0,addr+%00000000) out_574(0,addr+%00001000) out_574(0,addr+%00101000) out_574(0,addr+%00100000) out_574(0,addr+%00000000) return 2: out_574(0,addr+%00000000) out_574(0,addr+%00001000) out_574(0,addr+%01001000) out_574(0,addr+%01000000) out_574(0,addr+%00000000) return 3: out_574(0,addr+%00000000) out_574(0,addr+%00001000) out_574(0,addr+%10001000) out_574(0,addr+%10000000) out_574(0,addr+%00000000) return・・・それから数時間、午後には途中で金重さんが研究室に来て 飛ぶドラえもん を作ったりしたが、 あれこれPropellerの思い出しに苦闘しつつも、なんとか以下のようにシステムが動いてきた。 過去のPropeller日記で作ったオリジナルのMIDI受信/MIDI送信モジュールは、 外部回路の関係でビットが反転していることを思い出すのに2時間かかったが(^_^;)、 これを解決しつつ、さらにMIDI送信ドライパのバグを発見して解決してしまった。
ここで稼働しているシステムの仕様は以下である。
- OLED-PROPモジュールのPropellerからは、38400bpsで「MIDIプログラムチェンジ(C0 nn)」を、n=0, n=2, ..., n=127、と繰り返しXBeeから送信している。これは受信側のMaxから生存確認するためのものである
- OLED-PROPモジュールのPropellerは、XBeeから38400bpsでMIDIメッセージをMIDI規約に従ったものとして(速度だけが違う)解釈して受信する。MIDI規約違反の場合には無視する。この情報をOLEDでHEX(16進)表示する
- Max側のパッチ(XBee_001.maxpat)は38400bpsでシリアルポートと通信し、Propellerの生存証明の情報(およそ1秒ごとにインクリメント)を表示し、またとりあえずMIDIコントロールチェンジ(B0 nn dd)として0≦nn≦127、0≦dd≦127、のデータを送る
- OLED-PROPモジュールのPropellerプログラムは「XBee_001.spin」で、あわせてXBee用ドライバとして「XBee_Out.spin」「XBee_In.spin」を開発した
「XBee_001.spin」は以下である。
「XBee_In.spin」は以下である。CON _clkmode = xtal1 + pll8x 'Use the PLL to multiple the external clock by 8 _xinfreq = 8_000_000 'An external clock of 8MHz. is used (64MHz. operation) _OUTPUT = 1 'Sets pin to output in DIRA register _INPUT = 0 'Sets pin to input in DIRA register _HIGH = 1 'High=ON=1=3.3v DC _ON = 1 _LOW = 0 'Low=OFF=0=0v DC _OFF = 0 _ENABLE = 1 'Enable (turn on) function/mode _DISABLE = 0 'Disable (turn off) function/mode _xpixels = 96 'Screen width _ypixels = 64 'Screen height _pixelperlong = 4 'Each tile requires 4 longs _xtiles = _xpixels/_pixelperlong 'Each tile is 4 pixels x 4 pixels _ytiles = _ypixels/_pixelperlong _screensize = (_xtiles * _ytiles * _pixelperlong) 'Size needed for arrays _red = %11100000 _green = %00011100 _blue = %00000011 _yellow = %11111100 _purple = %11100011 _turq = %00011111 _white = %11111111 _black = %00000000 VAR long MemDisp[_screensize] 'OLED display driver variables long MemWork[_screensize] 'graphics driver variables OBJ OLED : "OLED-Driver1" Graphics : "OLED-Driver2" midiIn : "XBee_In" midiOut : "XBee_Out" PUB main | dummy, i[3], p, num OLED.start(@MemDisp) Graphics.setup(_xtiles, _ytiles, @MemWork) midiIn.start(21) midiOut.start(20) num := 0 repeat i[0] := cnt & $8000000 if i[0] <> i[1] i[1] := i[0] num := ++num & 127 dummy := $C00000 + num midiOut.fifoset(dummy) dummy := midiIn.event if dummy <> -1 p := Hex_Display(dummy, p) PUB Hex_Display(dummy, p) | d if dummy <> -1 d := (dummy & $FF0000) >> 16 Graphics.plotChar(HexConv(d/16), 3*(p//5)+1, p/5, 0, _green) Graphics.plotChar(HexConv(d//16), 3*(p//5)+2, p/5, 0, _green) Graphics.copy(@MemDisp) p := place(p) if (d > $DF) or (d < $C0) d := (dummy & $00FF00) >> 8 Graphics.plotChar(HexConv(d/16), 3*(p//5)+1, p/5, 0, _white) Graphics.plotChar(HexConv(d//16), 3*(p//5)+2, p/5, 0, _white) Graphics.copy(@MemDisp) p := place(p) d := dummy & $0000FF Graphics.plotChar(HexConv(d/16), 3*(p//5)+1, p/5, 0, _white) Graphics.plotChar(HexConv(d//16), 3*(p//5)+2, p/5, 0, _white) Graphics.copy(@MemDisp) p := place(p) return(p) PUB place(p) p := ++p//40 if p == 0 Graphics.clear Graphics.copy(@MemDisp) return(p) PUB HexConv(d) if d<10 return("0"+d) else return("A"+d-10) PRI pauseMSec(Duration) waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt) DAT plines word 10,3 ' line 1, endpoint 1 word 50,60' line 1, endpoint 2 word 10,3 ' line 2, endpoint 1 word 50,60' line 2, endpoint 2 vlines word 1,2 ' line 1, endpoint 1 velocity word -3,5 ' line 1, endpoint 2 velocity word 1,2 ' line 2, endpoint 1 velocity word -3,5 ' line 2, endpoint 2 velocity「XBee_Out.spin」は以下である。VAR long rx_Head, rx_Tail, rx_Buff[64] PUB start(_midiPin) : status midiPin := _midiPin rx_top := @rx_Head rx_end := @rx_Tail rx_fifo := @rx_Buff bitticks := clkfreq / 38_400 * 80 / 64 halfticks := bitticks / 2 longfill(@rx_Head,66,0) status := cognew(@asm_entry, 0) PUB event : status status := -1 if rx_Tail <> rx_Head status := rx_Buff[rx_Tail] rx_Tail := (rx_Tail + 1) & $3F DAT org asm_entry mov midiMask,#1 shl midiMask,midiPin getMidiByte waitpne midiMask,midiMask mov bitClk,cnt add bitClk,halfticks add bitClk,bitticks mov testBits,#9 :check_loop waitcnt bitClk,bitticks test midiMask,ina wc rcr rx_data,#1 djnz testBits,#:check_loop shr rx_data,#32-9 ' xor rx_data,#$FF and rx_data,#$FF test rx_data,#%10000000 wz if_z jmp #:running mov t1,rx_data and t1,#%11110000 cmp t1,#%11110000 wz if_z jmp #getMidiByte mov rsb,rx_data mov dcb,#0 jmp #getMidiByte :running mov t1,rsb and t1,#%11100000 cmp t1,#%11000000 wz if_z jmp #:byte_2 tjnz dcb,#:byte_3 add dcb,#1 mov keyno,rx_data jmp #getMidiByte :byte_2 mov event_data,rsb shl event_data,#16 or event_data,rx_data jmp #:write_event :byte_3 mov dcb,#0 mov event_data,rsb shl event_data,#16 mov t1,keyno shl t1,#8 or event_data,t1 or event_data,rx_data :write_event rdlong t1,rx_top mov rx_pointer,t1 shl rx_pointer,#2 add rx_pointer,rx_fifo wrlong event_data,rx_pointer add t1,#1 and t1,#$3F wrlong t1,rx_top jmp #getMidiByte t1 long 0 midiMask long 0 testBits long 0 bitClk long 0 bitticks long 0 halfticks long 0 midiPin long 0 rx_top long 0 rx_end long 0 rx_fifo long 0 rx_data long 0 rx_pointer long 0 event_data long 0 rsb long 0 dcb long 0 keyno long 0 fitだいぶ、Propellerの勘が蘇ってきた。 バグが無ければ、XBeeドライバはほぼこのままなので、あとはメインのspinで色々と遊べる筈である。 うまく行けば、これを持参して渡欧して、暇なときにやってみよう・・・というのが構想である。 明日はセントレアに前泊ということで、SUACを出発するのは午後なので、もう少しだけ進めてみることにしよう。VAR long tx_Head, tx_Tail, tx_Buff[64] PUB start(_midiPin) : status midiPin := _midiPin tx_top := @tx_Head tx_end := @tx_Tail tx_fifo := @tx_Buff bitticks := clkfreq / 38_400 * 80 / 64 longfill(@tx_Head,66,0) status := cognew(@asm_entry, 0) PUB fifoset(_tx_data) tx_Buff[tx_Head] := _tx_data tx_Head := (tx_Head + 1) & $3F DAT org asm_entry mov midiMask,#1 shl midiMask,midiPin or dira,midiMask :fifo_check rdlong t1,tx_end rdlong t2,tx_top cmp t1,t2 wz if_z jmp #:fifo_check mov t2,t1 shl t1,#2 add t1,tx_fifo rdlong event_data,t1 mov t1,t2 add t1,#1 and t1,#$3F wrlong t1,tx_end mov tx_data,event_data shr tx_data,#16 mov status_d,tx_data call #send_event and status_d,#%11100000 cmp status_d,#%11000000 wz if_z jmp #:byte_2 mov tx_data,event_data shr tx_data,#8 call #send_event :byte_2 mov tx_data,event_data call #send_event jmp #:fifo_check send_event and tx_data,#$FF or tx_data,#$100 shl tx_data,#1 mov testBits,#10 mov bitClk,cnt add bitClk,bitticks :bit_send shr tx_data,#1 wc muxc outa,midiMask waitcnt bitClk,bitticks djnz testBits,#:bit_send send_event_ret ret t1 long 0 t2 long 0 midiMask long 0 testBits long 0 bitClk long 0 bitticks long 0 midiPin long 0 tx_top long 0 tx_end long 0 tx_fifo long 0 tx_data long 0 status_d long 0 event_data long 0 fit2012年8月30日(木)
渡欧の出発日となった。 いつもは浜松駅からe-wingに乗るところを、先月の Sketching2012 で試したように、今回も浜松西インターにクルマを置いてe-wingである。 これは、帰りの成田→セントレアのフライトに乗り継ぐe-wingが浜松駅行きでなく、 西インターから掛川に行ってしまうためである。 次の浜松駅行きのe-wingを1時間半も待つのがカッタルイ、という事である。 ウイーンまで同行する院生3人のうち伊熊さんとSUACを出発するのは15:00、 それまで朝から一仕事、出来そうである。世間はお盆が終わって動き出したのか(小学校も今日から2学期スタートの模様)、 いろいろなメイルが届いてきた。 10月には水戸一高同窓会の「歩く会」があるという。例年は時期的に無理なところ、 今年は行けそうなので「参加」をメイルした。 さらにビッグニュースとして、ICMAから
というメイルが届いた。 URLは http://quod.lib.umich.edu/i/icmc/ である。 なんと、ICMCの1975年から去年までの全てのProceedingsが検索対応でWeb公開である。 これは素晴らしい。さすがミシガン大学である。Fwd: ICMC online proceedings - complete. Thanks to the hard work of Sandra Neal and the University of Michigan Library. We now have all of the ICMC conference proceedings from 1975 to 2011 online and searchable. Check it out!そしてANAからは、いつもように以下の搭乗前日メイルが届いた。 今日はe-wingでセントレアに行って前泊、明日の朝イチのフライトで成田へ、 そしてフランクフルト経由でリンツに行くのである。
座席指定を取っているので安心である。成田発着の長距離便は、 いつものように最後尾の席である。後ろに人がいないので楽である。 ・・・などとあれこれして、さらにスーツケースにパッキングしていたら、もう11時である。 出発は15時前なので、あと3時間ちょっとである。いつもANAをご利用いただきありがとうございます。 いよいよご出発は明日となりました。準備はお済みですか? お客様の予約内容をご案内します。ご搭乗を心よりお待ち申し上げております。 [1] 8月31日(金) NH338 名古屋(中部) - 東京(成田) 08:20発09:25着 飛行時間:01:05 座席番号: 8H [2] 8月31日(金) NH209 東京(成田) - フランクフルト 11:25発16:35着 飛行時間:12:10 座席番号: 42G [3] 8月31日(金) NH6113 フランクフルト - リンツ 21:05発22:10着 飛行時間:01:05 ルフトハンザ ドイツ航空(ルフトハンザ・シティライン)運航のコードシェア便です。 [4] 別の交通手段により移動 [5] 9月11日(火) JP108 リュブリャナ - ミュンヘン 18:30発19:30着 飛行時間:01:00 [6] 9月11日(火) NH208 ミュンヘン - 東京(成田) 21:00発15:25着(翌日) 飛行時間:11:25 座席番号: 36C [7] 9月12日(水) NH337 東京(成田) - 名古屋(中部) 16:55発18:05着 飛行時間:01:10 座席番号: 6C ANAマイレージクラブ・サービスセンターここで、いつものお仕事デスクから1106研究室の真ん中のテーブルに移動した。 旅行中の執筆は、いつもの2画面(1280*1024ドット)から、1画面(1280*800ドット)と窮屈になるので、 リハーサルである。 以下のように並べてみたが、Propeller/XBeeモジュールは、iPod touchの箱が丈夫そうだったので、これに入れた。 ただし機内で動かしたらXBeeからWiFiがワラワラと飛ぶので(^_^;)、いいところ、乗り継ぎの空港までである。
また、写真に写っている「超小型HDD」型USBメモリ(^_^;)を経由して、以下のようなXBeeの技術資料もコピーした。 「紙」の資料は旅行先ではアクセス出来ないからである。
まずは昨日の「XBee001.maxmap」と「XBee001.spin」をコピーして「002」にリネームして、Maxパッチを先に走らせてbstでPropellerをコンパイルして転送すると、無事に以下のようにちゃんとXBeeで通信しつつPropellerOLEDモジュールに描画した。ここから先の改良については、すでに作戦がある。 機内ではXBeeが稼働できないが、Max側のパッチだけは機内プログラミングできる。 そこで、Propellerモジュール側のOLEDドライバとXBee受信ドライバを改訂して、 まずは基本的な情報をMaxから受け取ると描画する、というところを目指したい。 Max側でその情報を送る、というパッチのプログラミングと分離するという事である。
今回はXBeeでのデータ通信の能力を実験してみたい、という目的があるので、 XBeeのモードは「素通し」である。 これをATモードやAPIモードにするかどうかは、後回しである。 OLEDの描画については、XとYの座標と、そのドットのカラーを指定すればよい。 OLEDの色についてはかなり特殊で、「OLED-Driver1.spin」の中に
COLORS256 long $32 '256 colors, 8bit (%RRR_GGG_BB)と記述されているように、「赤」3ビット、「緑」3ビット、「青」2ビット、という8ビットにパックして、 全体として1画素は256種類のカラー指定を行っている。 ところで今回のXBee通信では、過去にPropellerで実績のある、 オリジナルのMIDI通信のドライバとなっているが、 ここ にあるように、通信速度だけでなく、MIDIではステータスバイトに続くデータバイトが1バイトと2バイトの2種類があったり、ランニングステータスのルールがあったりする。 非同期通信でただデータが行き来するのに、複数バイトのメッセージがちゃんと区別できるためには「MIDI風規約」は有効だが、 OLEDを描画するためには、そのままMIDIのプロトコルに畳み込むと、かなり情報伝達効率が悪くなる。
OLEDは「96*64ドット」なので、ビクセルごとに描画する座標は、MIDIで送れる2バイトの7ビットデータでいいが、 画素のカラー情報は上述のように8ビットなので、MIDIデータバイトには1ビット過剰である。 そこでちょっと考えてみると、「X」「Y」「カラー」の3バイトのうち、「Y」は0-63の6ビットなので、このbit 6を例えばカラーのMSBにすれば、ちょうど7ビットのデータ3バイトで送れることになる。 さらに「X座標は」0-95までであり、7ビットデータとしてはあと32アドレスが無駄になっている。 このあたりを考えた結果、ランニンクステータスのルールを捨て、全てのメッセージをステータスとデータ2バイトで表す、 という、以下のような新しいプロトコルの発想に行き着いた。
かなりトリッキーであるが(^_^;)、データの無駄がなく、将来の拡張性も十分にあるプロトコルである。 詰め込みの尻拭いはMaxとPropellerで行えばいい。
- メッセージは全て3バイトを単位とする
- 先頭データ(ステータス)のMSBは1で、続く2バイトのMSBは0、つまり7ビットデータ(0-127)である
- ステータスの最初の96バイト(%10000000-%110111111)では、下位7ビットがOLEDのX座標(0-95)を表し、次のデータバイト(7ピット)の下位6ビットがOLEDのY座標(0-63)を、bit 6がカラーのMSBを表し、最後のデータバイト(7ビット)にこのカラーのMSBを加えた8ビットで、その画素のカラーを表す
- 残り32バイトのステータス(%11100000-%11111111)は、今後の定義を待つreservedである
昼食を挟んで、さっそくこのアイデアを実装してみることにした。 「XBee_In.spin」をコピーして「XBee_In2.spin」にリネームして、これを大幅に改変することになるが、 とりあえずMIDIの3バイトメッセージと同様のデータを受け取れば、Propellerメインプログラム側としては既に動いているので、 この部分が突破口となる。 だいぶPropellerアセンブラにも慣れてきたのか、以下のように「XBee_In2.spin」を改訂して、 15分ほどで無事に新しいプロトコルでのXBee受信とデータのHEX表示に成功した。
いよいよ次はピクセル表示である。 こちらも15分ほどで、合わせて「ステータスバイトがFFでOLEDのクリア」という定義も追加して作ったのが、以下である。 とりあえずMaxから適当なデータを送ってみただけだが、OLEDのクリアと、何やら座標の指定に対応してプロットしている。VAR long rx_Head, rx_Tail, rx_Buff[64] PUB start(_midiPin) : status midiPin := _midiPin rx_top := @rx_Head rx_end := @rx_Tail rx_fifo := @rx_Buff bitticks := clkfreq / 38_400 * 80 / 64 halfticks := bitticks / 2 longfill(@rx_Head,66,0) status := cognew(@asm_entry, 0) PUB event : status status := -1 if rx_Tail <> rx_Head status := rx_Buff[rx_Tail] rx_Tail := (rx_Tail + 1) & $3F DAT org asm_entry mov midiMask,#1 shl midiMask,midiPin getMidiByte waitpne midiMask,midiMask mov bitClk,cnt add bitClk,halfticks add bitClk,bitticks mov testBits,#9 :check_loop waitcnt bitClk,bitticks test midiMask,ina wc rcr rx_data,#1 djnz testBits,#:check_loop shr rx_data,#32-9 and rx_data,#$FF test rx_data,#%10000000 wz if_z jmp #:running mov rsb,rx_data mov dcb,#0 jmp #getMidiByte :running tjnz dcb,#:byte_3 add dcb,#1 mov keyno,rx_data jmp #getMidiByte :byte_3 mov dcb,#0 mov event_data,rsb shl event_data,#16 mov t1,keyno shl t1,#8 or event_data,t1 or event_data,rx_data :write_event rdlong t1,rx_top mov rx_pointer,t1 shl rx_pointer,#2 add rx_pointer,rx_fifo wrlong event_data,rx_pointer add t1,#1 and t1,#$3F wrlong t1,rx_top jmp #getMidiByte t1 long 0 midiMask long 0 testBits long 0 bitClk long 0 bitticks long 0 halfticks long 0 midiPin long 0 rx_top long 0 rx_end long 0 rx_fifo long 0 rx_data long 0 rx_pointer long 0 event_data long 0 rsb long 0 dcb long 0 keyno long 0 fitなんと、あっさりとピクセル表示が出来てしまった。 ここにMaxから送るものについては、既に構想があり、ちょっとだけjitterで苦労する必要がある。 とりあえず、これをPropellerモジュールのEEPROMに送って確定させた。 出発前のプログラミングとしては、なかなか上出来である。(^_^)CON _clkmode = xtal1 + pll8x 'Use the PLL to multiple the external clock by 8 _xinfreq = 8_000_000 'An external clock of 8MHz. is used (64MHz. operation) _OUTPUT = 1 'Sets pin to output in DIRA register _INPUT = 0 'Sets pin to input in DIRA register _HIGH = 1 'High=ON=1=3.3v DC _ON = 1 _LOW = 0 'Low=OFF=0=0v DC _OFF = 0 _ENABLE = 1 'Enable (turn on) function/mode _DISABLE = 0 'Disable (turn off) function/mode _xpixels = 96 'Screen width _ypixels = 64 'Screen height _pixelperlong = 4 'Each tile requires 4 longs _xtiles = _xpixels/_pixelperlong 'Each tile is 4 pixels x 4 pixels _ytiles = _ypixels/_pixelperlong _screensize = (_xtiles * _ytiles * _pixelperlong) 'Size needed for arrays _red = %11100000 _green = %00011100 _blue = %00000011 _yellow = %11111100 _purple = %11100011 _turq = %00011111 _white = %11111111 _black = %00000000 VAR long MemDisp[_screensize] 'OLED display driver variables long MemWork[_screensize] 'graphics driver variables OBJ OLED : "OLED-Driver1" Graphics : "OLED-Driver2" midiIn : "XBee_In2" midiOut : "XBee_Out" PUB main | dummy, i[3], p, num, d, x, y, color OLED.start(@MemDisp) Graphics.setup(_xtiles, _ytiles, @MemWork) midiIn.start(21) midiOut.start(20) num := 0 repeat i[0] := cnt & $8000000 if i[0] <> i[1] i[1] := i[0] num := ++num & 127 dummy := $C00000 + num midiOut.fifoset(dummy) dummy := midiIn.event if dummy <> -1 ' p := Hex_Display(dummy, p) d := (dummy & $FF0000) >> 16 if d == $FF Graphics.clear Graphics.copy(@MemDisp) elseif d < $E0 x := d & $7F d := (dummy & $007F00) >> 8 y := d & $3F color := d & $40 d := dummy & $00007F color := color + d Graphics.plotPixel(x, y,color) Graphics.copy(@MemDisp) PUB Hex_Display(dummy, p) | d if dummy <> -1 d := (dummy & $FF0000) >> 16 Graphics.plotChar(HexConv(d/16), 3*(p//5)+1, p/5, 0, _green) Graphics.plotChar(HexConv(d//16), 3*(p//5)+2, p/5, 0, _green) Graphics.copy(@MemDisp) p := place(p) if (d > $DF) or (d < $C0) d := (dummy & $00FF00) >> 8 Graphics.plotChar(HexConv(d/16), 3*(p//5)+1, p/5, 0, _white) Graphics.plotChar(HexConv(d//16), 3*(p//5)+2, p/5, 0, _white) Graphics.copy(@MemDisp) p := place(p) d := dummy & $0000FF Graphics.plotChar(HexConv(d/16), 3*(p//5)+1, p/5, 0, _white) Graphics.plotChar(HexConv(d//16), 3*(p//5)+2, p/5, 0, _white) Graphics.copy(@MemDisp) p := place(p) return(p) PUB place(p) p := ++p//40 if p == 0 Graphics.clear Graphics.copy(@MemDisp) return(p) PUB HexConv(d) if d<10 return("0"+d) else return("A"+d-10) PRI pauseMSec(Duration) waitcnt(((clkfreq / 1_000 * Duration - 3932) #> 381) + cnt) DAT plines word 10,3 ' line 1, endpoint 1 word 50,60' line 1, endpoint 2 word 10,3 ' line 2, endpoint 1 word 50,60' line 2, endpoint 2 vlines word 1,2 ' line 1, endpoint 1 velocity word -3,5 ' line 1, endpoint 2 velocity word 1,2 ' line 2, endpoint 1 velocity word -3,5 ' line 2, endpoint 2 velocity過去のPropeller日記などを見ると、HTMLソースがおよそ100kBを越えると新しいページに移動している。 この日記も既に100kBを越えているので、ここを区切りとして、次は「続・Propeller日記(2)」としていこう。
続・Propeller日記(2) へ
「日記」シリーズ の記録