文書の過去の版を表示しています。
ハイハイスクールアドベンチャー PicoCalc版
概要
おい、PicoCalc出るってよ! ……と、いうわけで、この、見るからに、ハイハイスクールアドベンチャーを移植せよ、と、いわんばかりのデバイスを見たら、正座して待機しつつ、移植する準備をするでしょう? そして、ほぼ一日で移植しました。
導入
本プログラムは、VSCode + PlatformIOで開発しています。なので、遊ぶためには、まず VSCode と PlatformIOを準備してください。 ソース一式は、こちらから取得してください。
READMEにも書いてありますが、色々なターゲットが定義されていますが、つまるところ、pico2 しか動作確認できていません1)ので、それ以外でbuildはしないでください。
ビルドできたら、data フォルダの下にあるHHSAdv フォルダーをまるっとSDカードのルートディレクトリにコピーしてください。 ルートにHHSAdv というフォルダーができて、その下に中身が全部コピーされる形です。
あとは、firmware.uf2 を書き込んでスタートです。
まあいろんな話
開発環境
Raspberry Pi Picoの開発環境についてはいくつかある。
まずは、公式に提供されているpico-sdkを利用する方法である。 cmake を手書きして、プロジェクトを構成する。 なんでもできるが、むき出しで、決して使いやすいわけではない。
次に Arduino IDEを利用して、Arduinoフレームワークでプログラムを作成する方法。 Arduino用の多くのライブラリを利用できるので、チョイスとしては悪くない。
だが、結局のところ Visual Studio Code + Platform IOが鉄板だといえるだろう。 コーディングを支援してくれるintellisense はいうに及ばず、Copilotや Geminiを利用したコーディング支援は、一度利用してしまったらもう戻れないくらいの絶対的な差別化要因だ。
当然、Visual Studio Code + Platform IOで開発することにする。
LovyanGFX
ハイハイスクールアドベンチャーを移植するにあたってはグラフィックス機能がキモとなる。 PicoCalcに組み込まれている液晶は ILI9488 で駆動されているので、LovyanGFXのパラメータだけ調整すれば制御可能である。
SPIのピンアサインと、向き、色の制御あたりができていれば問題なく動く。
名前 | 番号 |
---|---|
SPI | 1 |
PIN_SCLK | 10 |
PIN_MOSI | 11 |
PIN_MISO | 12 |
PIN_CS | 13 |
PIN_DC | 14 |
PIN_RST | 15 |
なお、offset_rotationが 6で、invertを trueにする。 どういう都合でかはわからないが、下端が上で、座標系は上下反転、色も反転するという状態がデフォルトになっているので、上記の設定で、左上が(0,0)になるように設定される。
PicoCalcが届いた日に、LovyanGFXが動いて画面に表示ができたので、もう勝ったも同然と、移植作業を開始したのだったが、落とし穴が待っているとはこの時はまだ気づいてなかった。
落とし穴があったとはいえ、スムースに動かすことができたのは、LovyanGFXのおかげなので、ありがたいことに違いはない。
待っていた落とし穴
Arduinoフレームワークで、LovyanGFXが使えて、SDカードもSPIのピン設定を済ませれば使える上に、キーボードもPicoCalc用のFUZIXのコードからちょいと拝借すればすぐに動いてしまう。
実は、動くようになるまでに一日しかかからなかった。 ほとんど M5Stack版と同じで済むからだ。 テストプレイでゲームクリアまで行ったので、あとはコードを整理して、GitHubに置けばいいなと思っていたら、そうは問屋が卸さなかった。
電源を切って、入れなおすと、画面が真っ黒のまま動かないのだ。
LovyanGFXのテスト用に書いた小さなアプリはそんなことはない。 何か触っちゃいけないところに触ってしまって、電源のOFF/ONではきれいに初期化されない問題があるのだという前提であれこれ調べた。
二日ほど調べたところで、どうもらちが明かないと、デバッグプローブを発注した。
三日目にはデバッグプローブで落ちる箇所は特定できた。
LovyanGFX の中の Bus_SPI::init()の中で、_spi_regs にアクセスするところで不正アクセスの例外で落ちていた。 原因としては何が考えられるか?
一番簡単なのは未定義なんだが、少なくともフラッシュ書き換え直後は動くし、テスト用のプログラムは何度でも動いたので、それはないだろうと真っ先に除外した。
LM Studioに入れた Gemma3 12Bに聞いてみると、考えられる原因としてこんなものを挙げてきた。
- SPI ピンの競合: LovyanGFX が使用する SPI ピンが、他の周辺機能 (UART, I2C など) で既に利用されている可能性があります。この場合、SPI のハードウェアリソースを確保できていないため、アクセス時に不正なアドレスにアクセスしようとしてしまうことがあります。
- SPI 周辺の初期化不足: Bus_SPI::Init() が完全に実行されていない可能性があります。例えば、クロックの設定や割り込みの設定などが不十分だと、予期せぬ動作を引き起こすことがあります。
- メモリマップのアドレス誤り: _spi_regs のアドレスが正しくない可能性があります。コンパイラ最適化によってレジスタの配置が変わる場合もあるので、アセンブリコードを確認したり、デバッガで _spi_regs の値を調べてみるのも有効です。
- SPI モードの設定ミス: SPI のクロック速度や極性・相位が、接続するデバイスと合っていない可能性があります。
- LovyanGFX の設定ミス: LovyanGFX の設定ファイル (config.h など) で、SPI ピンの割り当てなどが誤っている可能性があります。
なるほど。 そんなわけで、懸命に SPI 周りをきれいにする方法を模索して、あれこれやってみたが全く解決しない。
結局最初に排除した「_spi_regsの未初期化」の可能性を考慮して、コードの修正を行うと、あっさり動いた。
結論としては、フラッシュ書き換え時に動くコードが、たまたま未初期化の _spi_regsが触る領域を作っていて、結果として、例外を出さずに動作していた。
電源OFF/ONの時はたまたま動く場合と、そうでない場合とがあったということなのだろう。
- Bus_SPI.cpp.diff
--- Bus_SPI.cpp.orig 2024-11-22 16:12:20.561052300 +0900 +++ Bus_SPI.cpp 2025-04-04 16:13:20.533910740 +0900 @@ -60,14 +60,14 @@ return false; } - uint32_t temp = _spi_regs->cr0 & ~(SPI_SSPCR0_SCR_BITS | SPI_SSPCR0_DSS_BITS); - _clkdiv_write |= temp; - _clkdiv_read |= temp; - // DCピンを出力に設定 lgfxPinMode(_cfg.pin_dc, pin_mode_t::output); _spi_regs = reinterpret_cast<spi_hw_t *>(_spi_dev[_cfg.spi_host]); + uint32_t temp = _spi_regs->cr0 & ~(SPI_SSPCR0_SCR_BITS | SPI_SSPCR0_DSS_BITS); + _clkdiv_write |= temp; + _clkdiv_read |= temp; + int dma_ch = dma_claim_unused_channel(true); _dma_ch = dma_ch; if (dma_ch >= 0)
なお、この変更は、らびやんさんが取り込んでくださって、現在2) developブランチを利用することで、この問題を回避したコードが利用できるようになっています。
ハイハイスクールアドベンチャーも現状は developブランチを使用するようになっています。
素早く対応していただきらびやんさんには感謝しています。