====== ハイハイスクールアドベンチャー PicoCalc版 ====== ===== 概要 ===== おい、[[https://www.clockworkpi.com/picocalc|PicoCalc]]出るってよ! ……と、いうわけで、この、見るからに、[[ハイハイスクールアドベンチャー]]を移植せよ、と、いわんばかりのデバイスを見たら、正座して待機しつつ、移植する準備をするでしょう? そして、ほぼ一日で移植しました。 {{:picocalc:hhsadv01.jpeg?400|HHSAdvPico}} ===== 導入 ===== 本プログラムは、VSCode + PlatformIOで開発しています。なので、遊ぶためには、まず VSCode と PlatformIOを準備してください。 ソース一式は、[[https://github.com/wildtree/HHSAdvPico|こちら]]から取得してください。 [[https://github.com/wildtree/HHSAdvPico/blob/master/README.md|README]]にも書いてありますが、色々なターゲットが定義されていますが、つまるところ、pico2 しか動作確認できていません((2025/4/4現在))ので、それ以外でbuildはしないでください。 ビルドできたら、data フォルダの下にあるHHSAdv フォルダーをまるっとSDカードのルートディレクトリにコピーしてください。 ルートにHHSAdv というフォルダーができて、その下に中身が全部コピーされる形です。 あとは、firmware.uf2 を書き込んでスタートです。 ===== まあいろんな話 ===== ==== 開発環境 ==== Raspberry Pi Picoの開発環境についてはいくつかある。 まずは、公式に提供されている[[https://github.com/raspberrypi/pico-sdk|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のおかげなので、ありがたいことに違いはない。 {{::picocalc:hhsadv85.jpeg?400|ハイハイスクールアドベンチャー(いさこちゃん)}} === 待っていた落とし穴 === Arduinoフレームワークで、LovyanGFXが使えて、SDカードもSPIのピン設定を済ませれば使える上に、キーボードもPicoCalc用のFUZIXのコードからちょいと拝借すればすぐに動いてしまう。 実は、動くようになるまでに一日しかかからなかった。 ほとんど M5Stack版と同じで済むからだ。 テストプレイでゲームクリアまで行ったので、あとはコードを整理して、GitHubに置けばいいなと思っていたら、そうは問屋が卸さなかった。 電源を切って、入れなおすと、画面が真っ黒のまま動かないのだ。 LovyanGFXのテスト用に書いた小さなアプリはそんなことはない。 何か触っちゃいけないところに触ってしまって、電源のOFF/ONではきれいに初期化されない問題があるのだという前提であれこれ調べた。 二日ほど調べたところで、どうもらちが明かないと、デバッグプローブを発注した。 {{ ::picocalc:デバッグプローブ.jpeg?400 |デバッグプローブ}} 三日目にはデバッグプローブで落ちる箇所は特定できた。 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 周りをきれいにする方法を模索して、あれこれやってみたが全く解決しない。 ((AIのいうことを鵜吞みにするとは、お前はバカか?でも、らびやんさんとわたしとだったらわたしの仕業の方がずっとありそうじゃん!!)) 結局最初に排除した「_spi_regsの未初期化」の可能性を考慮して、コードの修正を行うと、あっさり動いた。 結論としては、フラッシュ書き換え時に動くコードが、たまたま未初期化の _spi_regsが触る領域を作っていて、結果として、例外を出さずに動作していた。 電源OFF/ONの時はたまたま動く場合と、そうでない場合とがあったということなのだろう。 --- 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_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) なお、この変更は、[[https://x.com/lovyan03|らびやんさん]]が取り込んでくださって、現在((2025/4/4現在)) developブランチを利用することで、この問題を回避したコードが利用できるようになっています。 ハイハイスクールアドベンチャーも現状は developブランチを使用するようになっています。 素早く対応していただきらびやんさんには感謝しています。 ==== PicoCalcについて ==== [[https://www.clockworkpi.com/picocalc|PicoCalc]] はRaspberry Pi Picoが同梱されているが、Pico, Pico W, Pico2, そして Pico 2Wのいずれでも動くように作られている。((ただしバイナリは1/1Wと2/2Wとの間で互換性はない。)) Pico/Wは RP2040で264KB SRAM、Cortex-M0 dual core、Pico2/2Wは RP2350で520KB SRAM、Cortex-M3 dual core/RiscVという構成で、つまり2の方が性能的な上位であり、使えるRAMも大きい。 更に RP2350はPSRAMが利用できる。 PicoCalcは8MBのPSRAMを搭載しているので、これを活用するためにも 2ないしは 2Wを用意したい。 実際わたしは、2Wを用意して組み込んだ。 結果として、ハイハイスクールアドベンチャーは264KBのメモリでは動かないようなので、この選択は正解だったといえる。 === バイナリの互換性について === Clockwork Piが配布している PicoCalc用のファームウェアイメージは、Pico用である。 つまり、Pico2用ではないのでそのまま焼いても動かない。 Picoは、BOOTSELボタンを押しながら起動することで、内蔵フラッシュを切り離し、USBマスストレージとしてアクセス可能になる。 この状態で、uf2形式のなどでファームウェアイメージをコピーするか、picotoolで書き込むことができる。 正しいイメージが書き込まれると制御が渡されてプログラムが動作を開始する仕組みだ。 Pico2にPico用のイメージを焼いても動かない。 なので、Pico2を入れた PicoCalcは、Pico2用にバイナリを作らないといけない。 くみ上げて動作確認するにあたって、Pico用のイメージを書き込んで動かないって悩んでいたのは秘密なのだ。 === 操作性について === ひとたび動き出せば PicoCalcは操作しやすいデバイスだ。 4インチ320x320の液晶は、解像度こそ低いが大きく見やすい。 老害世代にはありがたい仕様だ。 問題はそこではない。 Picoシリーズは、プログラムの書き換えにBOOTSELスイッチを押しながら起動する必要があるのだが、ケースに空いたスリットからでないとアクセスできないのでボタンが押しにくいのだ。 また、電源ボタンはあるがリセットボタンはない。 リセットは3.3V ENピンをGNDに落とせばいいので、用意できないわけではないのだから、なぜないのかは不明である。 開発作業を考えればあってしかるべきものだ。 この点が改良されたモデルが出ることを期待している。 ==== SDカード ==== PicoCalcはSDカードスロットを内蔵していて、SPI接続している。 液晶とは別のバスを使っているのでコンフリクトはしていない。 ^名前^値^ |SPI|0| |RX(MISO)|16| |CS|17| |SCK|18| |TX(MOSI)|19| 以下のコードで初期化することで、ArduinoのSDライブラリでSDHC/FAT32でアクセスができる。 SPI.setTX(19); SPI.setSCK(18); SPI.setRX(16); SPI.setCS(17); while (false == SD.begin(17, SPI)) { //Serial1.println("SD Wait ..."); delay(500); } ==== キーボード ==== PicoCalcのキーボードはSMT32で制御されていて、PicoとはI2C接続している。 キーボードへのアクセス方法については、FUZIX用のコードが参考になるだろう。 Kernel/platform/platform-rpipico/i2ckbd.[ch] にコードがある。 Pico側から見たとき、キーボードは I2C1に接続している。 ^名前^値^ |I2C|1| |ADDRESS|0x1F| |SCL|6| |SDA|7|