ニュースソースは2ちゃんねるw.
http://akizukidenshi.com/catalog/g/gM-09432/
80MHz駆動のPSoC5LPがお手軽に堪能できる時代に!
5V設計になっているのでそこは注意.3.3V駆動にするには…IOだけ3.3V駆動するには…回路図(Kit Guide P.35,36)をにらんで対策してください.
ニュースソースは2ちゃんねるw.
http://akizukidenshi.com/catalog/g/gM-09432/
80MHz駆動のPSoC5LPがお手軽に堪能できる時代に!
5V設計になっているのでそこは注意.3.3V駆動にするには…IOだけ3.3V駆動するには…回路図(Kit Guide P.35,36)をにらんで対策してください.
PSoC4 Prototyping Kit (CY8CKIT-049-42xx)は,安くて手に入るボードとして手軽に求めることができますが,実はちょっと注意が必要な(罠にはまりやすい)ところがあります.どこに注意しなければならないかを説明しながら,最新のPSoC Creator 3.2 を使った開発方法をご紹介します.
この資料を作るきっかけになったのは,PSoCまつり2015で,お隣同士になった方から,049ボードにうまく書き込めない,いろいろ調べたが情報が少ない,ということを伺ったことです.その場で2人であれこれいじくっているうちに,無事に書き込めるようになって,よかったよかった,と思っていたのですが,あとで疑問に思ったことをのりたんさんに質問している過程で,自分の理解が間違っていたことに気がつき,それを正す意味もかねて,ちょうどこの間出たばかりのCreator 3.2で開発する方法としてまとめてみたものです.
PDFファイルにしてこちらにおきましたので,ご覧ください.
Cypressから発売されたCY8CKIT-059 PSoC5LP Prototyping Kit.10ドル基板です.日本でもSwitchScienceさんやITえとせとらさんで,2000円未満で手に入ります.(ただしこの記事を書いた時点では品切れ中.)より手軽にPSoC5LPが楽しめるようになりました.
この基板は5V駆動なので,3.3Vデバイスをつなぐときは3.3V電源を用意してレベル変換をして…などの注意が必要です.
外付けの部品なしで遊べる例題として,CQのPSoC5LP基板で動いていたUSBMIDIを用いた簡易プレーヤを移植してみました.
ほとんど手間もかからず移植できてしまいました.エクスプレッションまで対応した最新版ソースをここにおきます.PSoC Creator 3.1 SP3です.そういえば昨日3.2が出ましたね.更新早すぎw.
前の曲の演奏の影響(ピッチベンドなど)をリセットするために,SW1(P2[2])がリセットボタンになっています.
本当はPSoC5LPからの出力をAFアンプに入れる前にコンデンサを挟むべきですが,さぼっています.アンプによってはしっかりいれないとだめかもしれません.ご注意ください.
USBUARTで,演奏モニターがシリアル出力されています.デバイスマネージャでCOMポートを確認して,TeraTermなどでご覧ください.改行コードがLFになっていることに注意.115200ボー,パリティなし,ストップビット1です.
追伸 CQのPSoC5LP基板版の最新ソースはこれです.P3[0]からSW0かSW1につないでリセットボタンとしてください.
PSoCまつり2015に参戦してきました!
twitterで名前をお見かけする有名人にたくさんお会いすることができました.
CQ出版のPSoC5LP基板の作者の圓山宗智さんにお会いできたのはうれしかった.
日本の人力飛行機やロケットの飛行ログを取るのにPSoCが大活躍しています.その立役者のHiraku TOIDAさんにもお会いできました.
中の人ののりたんさん.名刺もらった.やったね.
参加者はみなさん若ーい…ばかりでもなく,けっこう幅広い年齢層の方が参加されてました.
主催者は大学院生さんの猫村はなさん.がんばって準備されたのでえらい.きっと社会人になってからの血肉になりますよ.手が動く人,状況を変えていける人,というのは,どんな分野でも重要なはず.お世話になりました.どうもありがとうございました.
当日の配布資料はここです.プレゼンのパワポ(PDF)はこちらです.おまけで過去に作ってきたものの簡単なまとめをここに置きます.
PSoCまつり2015に参加されたすべてのみなさまに感謝いたします.ありがとうございました.
次の記事でCY8CKIT-059で動くUSBMIDIのプロジェクトをご紹介します.しばしご猶予を.
PSoCまつり2015開催通知がでましたね.
MIDIプレーヤーねたでプレゼンします.
ブログの内容から新しいことはあまりありません.新情報と言えるのは
ぐらいかなあ.
何より圓山さんのあとってのがいっとう辛い(笑).ええい,こうなったらゆるさ勝負だ.いちばん「ないようがないよう」なプレゼンをめざしてやる!(やけくそ)
CQ出版社のPSoC5LP基板,MARY-VB基板(カメラ),MARY-OB基板(OLED)の組み合わせでごそごそ遊んでいます.
前回の記事では,全画面表示で7.5fpsの表示速度でした.白坂さんから,PSoC5LPにしては遅いかも,10fpsは出るはず,とアドバイスをいただいて,通信の高速化にチャレンジしてみました.
目標は達成され,15fpsを出すことができました.後述しますが,これはこの組み合わせにおけるMAXのフレームレートとなります.
ソースコードをここに置きます.PSoc Creator 3.0 SP1です.
高速化にあたり,まずフレームレートについて考え直してみました.
MARY-VBにおけるOV7670(カメラ)のフレームレートは最大30fpsのようです.MARY-VBはOV7670から通常12MHzの速度で撮影信号を受け取りVRAMに転送しています.VRAMの容量が512kbytesで,画素サイズは2バイトなので,640x480のVGAサイズの画像のうち,640x400分だけをVRAMに転送しています.つまり
640×400×2=512000≦2^19=524288≦640×480×2=614000
ということです.12MHzのときのフレームレートは
12000000/512000=23.4375
となり,30fpsに達しません.トラ技の記事には,24MHzで転送してもVRAMに正しく書き込めたとあります.24MHzのときのフレームレートは
24000000/512000=46.875
ですから,大丈夫そうです.私のMARY-VBでもうまく書き込めました.なお,転送速度を12MHzから24MHzに上げるために,OV7670のレジスタに登録する初期値のうち,アドレス0x11の値を0x81から0xC0にしました.(0x80でもいいかもしれませんが,試していません.)
白坂さんの記事によると,MARY-VBのステータスレジスタにbit0=0なりbit0=1が返ってくるのは,VSYNCのタイミングに一致する,すなわち1/30sec単位で発生します.OV7670からVRAMの転送に必ず1フレーム時間かかるので,フレームバッファからPSoC5LPへの転送時間+PSoC5LPからOLEDの転送時間で,表示フレームレートが決まる計算になります.下の表のように,表示フレームレートはとびとびの値をとります.
VRAM→PSoC5LP +PSoC5LP→OLED に要するフレーム数 | OV7670→VRAM | 表示フレームレート(fps) |
---|---|---|
1 | 2 | 30/2=15 |
2 | 3 | 30/3=10 |
3 | 4 | 30/4=7.5 |
4 | 5 | 30/5=6 |
5 | 6 | 30/6=5 |
6 | 7 | 30/7≒4.29 |
7 | 8 | 30/8=3.75 |
8 | 9 | 30/9≒3.33 |
この表に従えば,前回時点でのデータ転送(7.5fps)のようすは,左図のようになります.
さて,MARY-VBもMARY-OBもSPI通信をどちらも16.5MHzに設定しました.したがって,画像1枚分のデータ(128×128×2)を隙間なく送ったときの理論的な最小値は,VRAM→PSoC5LPでは,
16 x 128 x 128 =262144bits,262144 / 16.5 = 15.888msec
となり,PSoC5LP→OLEDでは
18 x 128 x 128 = 294912 bits,294912 / 16.5 = 17.873msec
となるので,両方の和は
15.888+17.873=33.761msec
となって,1フレーム時間に収まりません.なので,10fps以上に表示フレームレートは上げられないのかな,と考えていました.
さて,図をつらつら見ているうちに,ふと気が付きました.VRAMの更新を停止する必要があるのは,VRAMからPSoC5LPにデータを転送している間だけでよいのではないかと.つまり,データの取り込みが終わり,VRAMのロックが外れて,次の画像をカメラから読み込んでいる間に,PSoC5LP→OLEDの転送をやってしまえばいいのではないか,ということです.先ほど計算した転送時間から考えても,SPI通信を効率よく行えば,なんとかなりそうです.
ただし,この戦略がとれるのは,PSoC5LP側に表示1画面分のメモリを確保した場合です.128×128×2=32786bytes必要です.PSoC5LPのメインメモリは64kbytesなので問題なくとることができましたが,マイコンらしい戦略ではないかもしれない…
図にするとこんな感じです.さて,絵に描いたようにうまくいくでしょうか…
OLEDのSPI通信の高速化から取り組みました.OLEDへのデータ転送は8bitデータに,データ/コマンドの別を表す1bitを付与した9bit転送です.PSoC5LPのハードウェアSPIを使おうとすると,この1bit分のために,1データの転送にFIFOバッファ2バイト分を使ってしまいます.PSoC5LPのFIFOバッファは4バイトなので,せいぜい2バイト単位でデータの転送を効率化するしかありません.
改良したソースコードは以下のようになりました.
static void _MARY_OB_Transmit(uint16 x) {
// wait during Tx buffer is full
while (!(OLED_SPI_ReadTxStatus() & OLED_SPI_STS_TX_FIFO_NOT_FULL))
;
OLED_SPI_WriteTxData(x);
}
/* send 2 bytes */
static void _MARY_OB_Transmit2(uint16 x1, uint16 x2) {
// wait until Tx buffer is empty
while (!(OLED_SPI_ReadTxStatus() & OLED_SPI_STS_TX_FIFO_EMPTY))
;
OLED_SPI_WriteTxData(x1);
OLED_SPI_WriteTxData(x2);
}
/* VRAM -> OLED */
void MARY_OB_Refresh(uint8 x, uint8 y, uint8 w, uint8 h) {
int i, j;
uint16 c;
_MARY_OB_Transmit(0x15); _MARY_OB_Transmit(_D(x)); _MARY_OB_Transmit(_D(x + w - 1));
_MARY_OB_Transmit(0x75); _MARY_OB_Transmit(_D(y)); _MARY_OB_Transmit(_D(y + h - 1));
_MARY_OB_Transmit(0x5c);
for (j = 0; j < h; j++)
for (i = 0; i < w; i++) {
c = MARY_OB_PGet(x + i, y + j);
_MARY_OB_Transmit2(_D(c >> 8), _D(c & 0xff));
// _MARY_OB_Transmit(_D(c >> 8));
// _MARY_OB_Transmit(_D(c & 0xff));
}
}
こちらの計測では,これだけの工夫で約25%転送時間が短縮されました.デジタルオシロでトリガーをかけて波形を見ればもう少し直接的に効果が確認できると思いますが,まだやっていません.
さて,いろいろ苦心したのはMARY-VBからPSoC5LPへのデータ転送です.これは4バイト単位で転送しようということで,
static void _CPLD_Transmit(uint8 x) {
// wait during Tx buffer is full
while (!(CPLD_SPI_ReadTxStatus() & CPLD_SPI_STS_TX_FIFO_NOT_FULL))
;
CPLD_SPI_WriteTxData(x);
}
static uint8 _CPLD_TransmitandReceive(uint8 x) {
_CPLD_Transmit(x);
while (!CPLD_SPI_GetRxBufferSize())
;
return CPLD_SPI_ReadRxData();
}
に対して,最初こんな関数を作りました.
// optimizing: 4-bytes transfer
static void _CPLD_TransmitandReceive4(uint8 *x, uint8 *y) {
int i;
// wait during Tx buffer is not empty
while (!(CPLD_SPI_ReadTxStatus() & CPLD_SPI_STS_TX_FIFO_EMPTY))
;
// send 4 bytes
for (i = 0; i < 4; i++) {
CPLD_SPI_WriteTxData(x[i]);
CyDelayCycles(2); // wait 2 cycles: CyDelayCycles(1) is failed
}
// read data
for (i = 0; i < 4; i++) {
while (!CPLD_SPI_GetRxBufferSize())
;
y[i] = CPLD_SPI_ReadRxData();
}
}
コマンドを送った後にちょっとだけdelayを入れています.これがないと転送されるデータが化けてしまいました.しかし,実は,これではあまり速度向上が実感できませんでした.それで,
ことを考えて,こんなふうに改良しました.
// optimizing: 4-bytes transfer + no array use + no stack use
static uint8 x1, x2, x3, x4;
static uint8 y1, y2, y3, y4;
static void _CPLD_TransmitandReceive4() {
// wait during Tx buffer is not empty
while (!(CPLD_SPI_ReadTxStatus() & CPLD_SPI_STS_TX_FIFO_EMPTY));
// send 4 bytes
CPLD_SPI_WriteTxData(x1);
CyDelayCycles(10);
CPLD_SPI_WriteTxData(x2);
CyDelayCycles(10);
CPLD_SPI_WriteTxData(x3);
CyDelayCycles(10);
CPLD_SPI_WriteTxData(x4);
CyDelayCycles(10);
// read data
while (!CPLD_SPI_GetRxBufferSize());
y1 = CPLD_SPI_ReadRxData();
while (!CPLD_SPI_GetRxBufferSize());
y2 = CPLD_SPI_ReadRxData();
while (!CPLD_SPI_GetRxBufferSize());
y3 = CPLD_SPI_ReadRxData();
while (!CPLD_SPI_GetRxBufferSize());
y4 = CPLD_SPI_ReadRxData();
}
コマンド送信のあとに入れるdelayが10サイクル分になりました.インデックス計算を省くことで8サイクル分節約になる?ということでしょうか.この改良で,カメラから送られる画像が110ラインまでなら15fpsで表示できるようになりました.
まだほかに無駄はないか…と考えてみたら,データの送信が完了していれば,受信のFIFOバッファには受信データが入っているはずですから,受信バッファが空でなければ,などというチェックは不要のはず!と考えて,最終的に下記のようにしました.
// optimizing: 4-bytes transfer + no array use + no stack use + no read-ready flag check
static uint8 x1, x2, x3, x4; // transmit data
static uint8 y1, y2, y3, y4; // receive data
static void _CPLD_TransmitandReceive4() {
// wait during Tx buffer is not empty
while (!(CPLD_SPI_ReadTxStatus() & CPLD_SPI_STS_TX_FIFO_EMPTY));
// send 4 bytes
CPLD_SPI_WriteTxData(x1);
CyDelayCycles(10); // wait 10 cycles (10/(66MHz) = 0.152usec).
// if it isn't, receive data are broken. a limit of MARY-VB's performance?
CPLD_SPI_WriteTxData(x2);
CyDelayCycles(10);
CPLD_SPI_WriteTxData(x3);
CyDelayCycles(10);
CPLD_SPI_WriteTxData(x4);
// read data: no read-ready flag check
y1 = CPLD_SPI_ReadRxData();
y2 = CPLD_SPI_ReadRxData();
y3 = CPLD_SPI_ReadRxData();
y4 = CPLD_SPI_ReadRxData();
}
これで見事128ライン転送で15fps達成!嬉しかった~.
ちなみにデータの読み込む部分は以下のようになります.4の倍数になっていないときは最後の端は1バイトずつ送っています.3バイト転送,2バイト転送用の関数を作ってもいいですが,まあ不要だろうと.
void MARY_VB_ReadCameraData(int n, uint16 *buf) {
int d;
int i;
x1 = x2 = x3 = x4 = CPLD_COMMAND_ReadRegister + CPLD_COMMAND_Increment_AddressCounter + CPLD_REGISTER_CameraData;
CPLD_SPI_ClearRxBuffer();
// _CPLD_TransmitandReceive(CPLD_COMMAND_ReadRegister + CPLD_COMMAND_Increment_AddressCounter + CPLD_REGISTER_CameraData);
// _CPLD_TransmitandReceive(CPLD_COMMAND_ReadRegister + CPLD_COMMAND_Increment_AddressCounter + CPLD_REGISTER_CameraData);
for (i = 0; i < n; i += 2) {
// _CPLD_TransmitandReceive4(tx, rx);
_CPLD_TransmitandReceive4();
#ifdef DATA_SWAP_FLAG
d = y1 + (y2 << 8);
*buf++ = d;
d = y3 + (y4 << 8);
*buf++ = d;
#else
d = y2 + (y1 << 8);
*buf++ = d;
d = y4 + (y3 << 8);
*buf++ = d;
#endif
}
for (; i < n; i++) {
#ifdef DATA_SWAP_FLAG
d = _CPLD_TransmitandReceive(CPLD_COMMAND_ReadRegister + CPLD_COMMAND_Increment_AddressCounter + CPLD_REGISTER_CameraData);
d += (_CPLD_TransmitandReceive(CPLD_COMMAND_ReadRegister + CPLD_COMMAND_Increment_AddressCounter + CPLD_REGISTER_CameraData) << 8);
#else
d = (_CPLD_TransmitandReceive(CPLD_COMMAND_ReadRegister + CPLD_COMMAND_Increment_AddressCounter + CPLD_REGISTER_CameraData) << 8);
d += _CPLD_TransmitandReceive(CPLD_COMMAND_ReadRegister + CPLD_COMMAND_Increment_AddressCounter + CPLD_REGISTER_CameraData);
#endif
*buf++ = d;
}
}
DATA_SWAP_FLAGというのは,mbed版でも報告されているのですが,1バイト目と2バイト目が逆に送られてくる場合があるので,その対策です.転送レートに依存しているようですが,はっきりしません.
SPIでのデータ受信は,最初の送信に対する受信データは意味がなくて,次に送信をしたときに前回の送信に対応する受信データが送られてくるわけですが,このMARY-VBからのデータ転送の部分については,白坂さんのソースでもそうなのですが,そのようにはプログラミングされておらず,最初の送信に対する受信データも使っているように見えます.このあたりをどう考えればいいのか,悩んでいます.
</object>
YouTube: CQ PSoC5LP+MARY-VB+MARY-OBで15fps出た
やりはじめると,気になって,いろいろ遊んじゃうなあ.忙しいはずなのに.#逃避行動絶好調?
やっぱり,ちょっといろいろヤバいので,このタスクはいったん閉じたいと…ああ,でもデジタルオシロでSPI通信の様子をチェックしたほうがいいよなあ,むむむ…
最後になりましたが,いろいろアドバイスをいただいた,MARY-VB基板設計者の白坂一郎さんに感謝申し上げます.
[20140906] MARY-VB基板の作者の白坂さんからコメントをいただき,記事の内容を再検討し,誤りを正しました.誤った内容で白坂さんにご迷惑をおかけしましたことを,記してお詫び申し上げます.
--------------------------------------------------------------------------
トランジスタ技術2014年7月号で紹介されたMARY-VB基板を買ってきました.
これは圓山さんの開発ではないのね…
コンデンサC1からC6に飛んでましたので,回路図を見ると,カメラユニットのVddに1.8Vを供給する線のようです.
誌面にはこのように書いてあるのですが,容量が足りなかったんですかねえ.
[追記20140903] モジュールを設計した白坂さんのコメント:「布線があるのは、カメラモジュールメーカの手違いで、1.8Vを外部から供給するモジュールが混じってしまったためで、供給能力等の問題ではありませんので安心して下さい。」とのこと.
なにはともあれ,動かしてみることにしました.
回路図はこんな感じです.VB基板はI2CとSPIを両方使う仕様です.ボタンがひとつ欲しかったので,Debounceモジュールを初めて使ってみました.昔はこんな便利なものはなかったので,いい時代になったものだ(爺モード).まだ慣れていないので,なんとなくもたもたしている感じがありますが,見逃してください.また,今回は割り込みを使っていないので,ボタンを押しても,プログラムがステータスレジスタを見る部分を実行していないと感知されないという手抜き仕様です.このモジュールだと1->0と0->1で別々の割り込みを発生することもできたりして,かなり高度な押し方(長押しなど)でもスマートにプログラムが書けそうです.割り込みを使わないなら,Glitch Filterというモジュールが同じように使えそうですが,今回は試しませんでした.
[20140906] 最初はPSB基板のソケットにMARY-OB基板(OLED)をセットし,MARY-VB基板をブレッドボードに置いて配線していたのですが,通信エラーが頻発して動作が安定しません.ハードウェアの問題を取り除くため,MARY-VBをソケットに差すようにし,MARY-OBをブレッドボードに置くことにしました.
I2CでもSPIでも順調にはまりましたが,少しづつプログラムの間違いや理解の誤りを修正し,プログラムが動き出しました.
しかしどうもうまくいかないのです.1画面読んだあと次の画面に更新がかからない,などです.
手持ちのmbedとmaple mini基板をひっぱり出してきて,mbed版のサンプルプログラムを動かしてみると,表示抜けは起こらず見事に動きます.
トラ技の著者(白坂さん)のサンプルプログラムを一生懸命見直して,先に開発されたmbedのMARY-VBライブラリのソースコードも読んでいったところ,相違点を見つけました.
MARY-VB基板は,カメラのフレームバッファを読みに行くとき,制御レジスタにフレームバッファの更新を停止してくれというリクエストを出し,ステータスレジスタに「止めたよ」という返事が返ってきたらデータを読み込みます.ステータスレジスタのbit0=1がその合図のはず.逆に読み込みが終わったのでフレームバッファの更新を再開していいよと制御レジスタにコマンドを送ると,ステータスレジスタのbit0=0が更新を再開したことの通知になります.ところが…
mbedのソースでは,0x51(データ読み込み開始時)と0x50(終了時)を待っています.さては隠しフラグにちがいない,と思い,やってみたところうまくいくので,ああこれが正解だった,と思い込んでしまったのでした.
ところが,この記事のコメントにもあるとおり,それはおかしい,bit0=0を待っていればうまくいくはず,とのこと.さあて,どこがおかしいんだろう…必死になって調べた結果…
見つかりました.プログラムミス.SPIでデータを読むときは,こちらからデータリクエストのコマンドをMOSIに出すと,同じクロックタイミングでコマンドの長さの分だけMISOからデータがやってくるので,それを読むわけですが,最初に出すときは,スレーブ側からみれば何のコマンドがくるかわからないので,ダミーのデータをだし,次にコマンドがやってきたタイミングで前のコマンドに対するデータを返してきます.PSoCのSPIはハードウェアで管理してくれていて,送られてきたデータは4バイトのバッファに入れてくれます.そのバッファの管理を誤ったのと,ステータスレジスタの読み取りに関する,最初のダミーデータの読み飛ばしを忘れてしまったのが,失敗の原因でした.
ごちゃごちゃ言葉でいってもかえってわかりにくいので,以下のソースで,どこを間違えたか,雰囲気をつかんでください.
誤:
void MARY_VB_DataRead_End() {
CPLD_SPI_ClearRxBuffer();
// Release frame buffer data
_CPLD_Transmit(CPLD_COMMAND_WriteRegister + CPLD_REGISTER_Control);
_CPLD_Transmit(0);
// wait until flag = 0
_CPLD_Transmit(CPLD_COMMAND_ReadRegister + CPLD_REGISTER_Status);
while ((_CPLD_TransmitandReceive(CPLD_COMMAND_ReadRegister + CPLD_REGISTER_Status) & 0x01) != 0)
;
}
正:
void MARY_VB_DataRead_End() {
// Release frame buffer data
_CPLD_Transmit(CPLD_COMMAND_WriteRegister + CPLD_REGISTER_Control);
_CPLD_Transmit(0);
// Data Read Start
CPLD_SPI_ClearRxBuffer();
// wait until flag = 0
_CPLD_TransmitandReceive(CPLD_COMMAND_ReadRegister + CPLD_REGISTER_Status);
while ((_CPLD_TransmitandReceive(CPLD_COMMAND_ReadRegister + CPLD_REGISTER_Status) & 0x01) != 0)
;
}
あー,もう,情けない.がっかりしました…
開発したプログラム(修正版)をここに置きます.PSoC Creator 3.0 SP1で開発しました.
DebugモードでもReleaseモードでも問題なく動くようになりました.更新レートは,
全ライン | 奇/偶ライン | |
---|---|---|
Debug | 5fps | 7.5fps |
Release | 7.5fps | 10fps |
となりました.半分づつの描画にしてもレートは倍にはなりません.そりゃそうだ,カメラ側は全ライン分用意しているわけだから.
</object>
YouTube: CQ PSoC5LP基板でMARY-VB基板を動かしてみた(訂正版)
トラ技の記事中の記述ですが,PSoCならR/Wビットはコマンドを介して立てることになるので,デバイスIDは0x21として考えます.また,「レジスタを読み出す場合は…」以下のくだり,これだとレジスタに0x43を書きこむ命令とどう違うの?と思ってしまいました.デバイスID+Wフラグとレジスタ番号を送ったらいったん送信を終了し,新しいI2CコマンドとしてデバイスID+Rフラグを送って,レジスタの値を受信する,という手順で示していただいたほうがよいように思います.
いい気分転換にはなりましたが,忙しいときに限って遊びたくなるのは困ったものだ…orz
追記 SPIの転送速度が遅いとき,フレームバッファデータ(2バイト)の送信順序が逆に見える(上位,下位の順のはずが下位,上位の順になる)現象が起きます.mbedのサンプルプログラムでもそうなったときの対策ルーチンが含まれており,どの環境でも起きうる現象のようです.なお現在,VB基板のSPIは16.5Mbpsで,OB基板のSPIも16.5Mbpsで動かしています.