マイコン工作実験日記

Microcontroller を用いての工作、実験記録

LVGLにおけるゲームパッドの扱い

2024-11-11 16:42:39 | DoomPlayer

LVGL Playerでは入力デバイスとしてLCD画面上に貼られたタッチパネルとBluetooth接続されたゲームパッド(DualShock4, DualSense, 8Bitdo Zero2)をサポートしています。LVGLにおいては、4種類の入力デバイス -- POINTER, KEYPAD, ENCODER, BUTTON -- をサポートすることができますので、LCDのタッチパネルは LV_INDEV_TYPE_POINTERとして接続し、ゲームパッドはLV_INDEV_TYPE_KEYPADとして接続することになります。

しかしながら、KEYPAD型は基本的にはキーボードとかテンキーを意識したものであって、必ずしもゲームパッドには向いているとは言えません。まず、全てのボタンは何らかのキーに対応づけて表現するしかありません。画面の制御用にいくつかのキー名が予め定義されていますが、ゲームパッド上の全てのボタンやキーを適切に表現できるわけでもありません。例えばゲームパッド上の方向キーをLV_KEY_LEFT, LV_KEY_RIGHT, LV_KEY_UP, LV_KEY_DOWNに対応づけるのは自然な割り当てだと思いますが、"L1/R1, L2/R2ボタンはどうしようか?" ということになります。どうしてもこれらのボタンを区別してLVGL上で使いたければ、自分でキーコードを定義して扱うしかありません。また、アナログジョイスティックのように、可変量を表す機能は用意されていないので、別途独自に処理を用意する必要があります。

このような制約のためにゲームパッド上の全てのボタンやスイッチをLVGLのKEYPADデバイスとして素直に表現することはできませんが、上述の画面制御用のキーへの対応付けができれば、画面上に配置された基本的なWidgetのGUI操作はゲームパッドを使って行うことができます。SliderのようなWidgetでも、方向キーを使って値を増減することができますが、増減はインクリメント/デクリメントしかできないので、値の範囲が広い場合には使い勝手が悪くなってしまいます。前記事で示した設定画面の場合には、音量/輝度はスライダーで操作できますが、キーパッドを使った場合の操作性を考慮して、0..10の11段階で値を変化させることにしました。 (Slider Widgetは、ディフォルトでは 0..100の範囲となっている)

ゲームパッドを使う際のもうひとつの問題は、Gesture操作の検出です。設定画面は、画面の上から下へのスワイプ操作で行うことにしましたが、これをゲームパッドの方向キーを使って検出させるためには、かなりの速度でキーを連打する必要があり、現実的ではありません。幸いなことに、DualSenseやDualshock4にはゲームパッド上にタッチパッドが用意されており、LCD画面上のタッチパネルと同じようにタッチされた座標の検出が可能です。そこで、タッチパッドがタッチされた時には、その座標をLCDのタッチパネルの座標系に変換してLVGLにはLCDタッチパネルが操作されたように見せかけてレポートすることにしました。この方法により、ゲームパッドを持ったまま、タッチパッド部分をスワイプすることで設定画面の呼び出し操作が行える様になりました。

以下、スワイプ操作による画面遷移の様子をキャプチャしてみました。


HIDとA2DP/AVRCPの同時サポート

2024-10-30 17:12:18 | DoomPlayer

前記事で書いたように、これまでのBluetoothサポートはBTStackのhid_host_demoとa2dp_sink_demoをベースにしたものを個別に使用していたために、HIDプロファイルとA2DP/AVRCPプロファイルを同時にサポートすることができず、その結果としてBluetooth Player機能においては音楽再生時にHIDプロファイルを使用してのゲームパッドからの操作が行えませんでした。この制限を撤廃するために、両者を同時に動かせるように修正を加えました。それに合わせて、設定画面も若干修正。

接続、動作中のプロファイル名を示すボタンを新たに追加しました。これらのボタンを押すことで、それぞれのプロファイルとの接続を切断することができます。

A2DPプロファイルが動くのはアプリケーションとしてBluetooth Playerを選択した時だけとしていますが、HIDプロファイルは起動後アプリケーションの選択画面が表示されている状態でも動作するようにしたので、選択操作もゲームパッドを使って行えるようになりました。

 

 


音量、画面輝度設定画面を追加

2024-10-16 14:03:57 | DoomPlayer

Doom Player改め、LVGL Playerに音量と画面輝度の設定画面を追加しました。STM32H7B3I-DKのDoom Playerでは、Music, Sound, Gameの選択画面において、音量調節のスライダーを用意していたのですが、DoomPlayer, Blueotth Player, Oscilloscope Musicの3つの機能を有するLVGL Playerでは、それぞれの機能毎に設定画面を設けていたのでは不便です。どの機能を使った場合でも、同じように操作できる設定画面が必要となります。

そこでスマホでコントロールパネル/コントロールセンターを引き出す操作に似せて、画面を上から下にスワイプすることで設定画面を表示するようにしてみました。具体的には、LVGLのジェスチャー検出機能を使ってスワイプ操作を検出し、下方向へのジェスチャーだった場合に、次のような設定画面に表示を切り替えています。アクティブなスクリーンオブジェクトに対してイベントコールバックを設定することで、画面上のどの箇所でスワイプを行なっても、ジェスチャー操作を検出することができます。

設定画面から戻るには、もちろん下から上へのスワイプ操作を検出して、元の画面に戻します。

Bluetoothのアイコン表示は、Bluetoothの接続状態を示すものであり、接続された場合にはアイコンが青く変化します。

青い状態でアイコンをタッチすると、接続を切断します。

ジェスチャーの検出は ほぼどんな画面を表示している時でも動作するのですが、例外としてスクロール可能な画面範囲では、スワイプ操作とスクロール操作の区別がつかないので、スクロール操作のイベントとして検出され、ジェスチャー操作としては認識されません。Music Playerの画面では、画面を下から上にスワイプすることで曲名リストの表示が行えますが、この機能はスクロール操作によって実現されているので、この画面ではジェスチャーの検出は行えません。そこで、スクロールイベントの検出処理部分で、下方向スクロールのイベントを検出して、設定画面への切り替えを行うこととしています。

Bluetoothの接続については、Doom PlayerとOscilloscope MusicではHIDプロファイルを使っており、現状ではBluetooth PlayerではA2DP/AVRCPプロファイルを使っていますが、HIDプロファイルはサポートできていません。そのため、Bluetooth Playerではゲームパッドを使っての画面操作が行えないという制限があります。HIDプロファイルとA2DP/AVRCPプロファイルを同時に並行して動くようにしたいところで、それに合わせて状態表示も改善したいところです。

 


Oscilloscope Music -- 画面表示 その2

2024-10-06 10:56:39 | DoomPlayer

前記事では、サウンドデータを36ms毎に区切って画面表示をしていると説明しましたが、その後メモリをやりくりした結果 256x256画素で深さ1ビットのバッファをもうひとつ確保することができたので、このふたつのバッファにプロットデータを蓄え、36ms間隔で交互に表示することができるようになりました。結果として、画面上には72ms分のサウンドサンプルに相当するデータが表示されていることになります。

10月末にリリース予定の新しいアルバム(N-SPHERES)が先行予約可能になっているので、予約購入してプレリリースされている Function.wav を早速再生してみました。この曲は7分もあるのですが、他の曲も3分から6分かかるので、Play/Pause の制御と、Next/Prev の操作ボタンを作りました。画面の中央部分にPlay/Pauseボタンが用意されているのですが、再生中は非表示状態になっているもののボタン押下の検出ができるようにしています。ボタンが押されると曲の再生が休止され、次の画像のように Play, Next, Prevボタンが表示されます。

再生を再開したり前後の曲を選択すれば、これらのボタンは非表示状態に戻ります。また、曲名表示部分をクリックするとMusic Playerデモと同じように曲名リストを表示する機能も付け足しました。

コードの大半は Music Playerデモからの流用ですし、アイコンデータもデモから拝借利用させてもらっています。

 


Oscilloscope Music -- 画面表示

2024-09-23 16:10:42 | DoomPlayer

前記事はオーディオ再生についてでしたので、本記事では画面表示について説明します。

画面描画処理は、基本的には各PCMサンプルの値をXY座標値として表示するだけの単純な処理です。元のPCMデータは16bit符号付き整数ですが画面は480x320しかありませんので、上位8bit分の値だけを使ってプロットする点の位置を決定しています。LVGLでは描画用のWidgetとしてcanvasが用意されていますが、lv_canvas_set_px_color() を使って1画素ずつプロットしていたのでは処理時間がかかり過ぎて 192Kのサンプリング速度に間に合いません。そこで 深さ1ビットの256x256画素画像(LV_COLOR_FORMAT_I1)をImage widgetを使って表示しています。

オーディオ再生時には、6ms分のPCMデータ(1152サンプル) を1フレームとして再生送信処理を行なっていますが、画面表示では6フレーム分のPCMデータを256x256画素のバッファにプロットした時点でそのImage画像を更新(lv_obj_invalidate())してからバッファをクリアすることにより、画面の更新を行なっています。したがって、画面上には常時36ms分のオーディオサンプルに相当する画像が表示されていることになります。画面の更新周期としてはおよそ27Hzですね。

本当は、もう少し短い周期で、連続するサンプル区間が半分くらい重なるようにずらしながら、表示イメージデータの蓄積をして表示更新を行いたかったのですが、処理負荷と使用メモリ量が大きくなってしまうので、今回は単純に36msごとに区切ってのデータ蓄積と表示更新を行なっています。何の工夫もない単純な処理ですが、幸いなことにそれっぽい画像変化を得ることができました。


Oscilloscope Music - オーディオの再生

2024-09-22 15:04:27 | DoomPlayer

本記事では、前記事で触れたOscilloscope Musicについて紹介します。この機能は、Oscilloscope Musicのサイトで紹介されている音源ファイルを再生するものです。今回は、2016年にリリースされたアルバムを連続して再生する機能を実装してみました。サイト上で紹介されているように、オシロスコープをXY表示モードに設定しておき、再生した音楽の左チャンネルをX方向の入力、右チャンネルをY方向入力につないでやると、その音に対応した図形が描画表示されるというものです。アナログスコープを使った方が、輝線の残像効果に風情があるので、わざわざテクトロの中古を購入して再生している画像もYouTubeにはいくつもあがっていますが、PC上で再生するためのソフトも用意されているので、オシロスコープを持っていなくとも充分に楽しむことができます。

音源ファイルは有料で配布されており、代金を支払うとダウンロードして入手することができます。2016年のアルバムの場合、ホームページの説明にあるように全10曲が WAVフォーマットで配布されています。192KHzのステレオで非圧縮の16ビットPCMなので総量は1.52GBになるのですが、実際に入手して確認してみると1曲目の''01 Dots'' だけは96Kサンプリングになっていました。データ量は大きいですが、今時のSDカードを使えば何の問題もなく、fatfsを使って読み出すことができます。幸いなことにDoomPlayerで使っているTLV320DAC3203は192KHzまで対応しているので、読み出した16ビットPCMデータをそのままSAIを使ってTLV320DAC3203に送ってやれば音を再生することができます。

実際にPCMデータを送信するには、サンプリング周波数に応じたクロックを生成するために、STM32U5のPLLを設定したり、CODECの設定を変更したりする必要もあります。これらの準備を済まして音源を再生してみたのですが、当初は 96Kサンプリングの1曲目は再生できるものの、192Kサンプリングの2曲目以降がまっとうな音にならないという問題に遭遇。しばらく悩んでいましたが、ロジアナをつないでSAIの出力信号を確認したところ、SCK, FS, SD信号はきちんと出ているものの、MCK信号は192Kに設定すると出力されていないことが判明。SAIの設定を確認すると GPIOの出力スピード設定が Lowになっていたので、これをMediumに修正することで正常に音が出るようになりました。


LVGL Player

2024-09-19 16:16:23 | DoomPlayer

これまで、DoomPlayerにA2DP Sink機能を追加してきましたが、SDカードの有無で起動する機能を切り替えるのはあまりにも使いにくいので、起動時にはメニューを表示して選択できるようにすることにしました。ついでに、もうひとつ新たに機能を追加することにしたので、起動時には3項目を表示することになりました。こうなると、Doom Playerはもはや選択機能のひとつになってしまったので、起動画面には LVGL Playerという名前のタイトルを入れることにしました。

新たに追加した機能は、Oscilloscope Musicです。YouTubeで検索すればスグに見つかりますが、個人的にかなり気に入ったので、この再生機能を簡易的に追加することにしました。まだ作業中ですが、次の記事でもう少し詳しく紹介することにします。


A2DP sink機能の追加 -- その2

2024-07-11 16:57:31 | DoomPlayer

曲名やアーティスト名が長い場合にスクロール表示が発生すると、表示の更新が遅くなってしまう原因を調べたところTTF(True Type Font)を使って表示しているために、その表示処理に時間がかかってしまっていることがわかりました。通常の埋め込みフォントであれば、描画の負荷が小さいのですが、TTFフォントを使うと展開と描画の負荷が重いようです。

ちょっと調べたところでは、LVGLのTiny TTF font engine では展開したビットマップイメージをキャッシュしているものの、Unicodeからglyph indexへの変換情報はキャッシュされていなかったために展開速度の低下を招いていたようです。ちょうど運良く、この問題を修正したPRが出ていてマージされたので、早速試してみました。確かに目で見てわかるほどにスクロール表示が改善されています。

TTFフォントを使った場合には文字のサイズを変更しても綺麗に表示されるのですが、A8フォーマットのビットマップに展開されているので、メモリと描画処理の負荷は重くなっていると思われます。フォントの描画処理をDMA2Dを使うように最適化すれば、もう少し早くできるかもしれません。

 


A2DP sink機能の追加

2024-06-25 20:02:32 | DoomPlayer

Nucleo-U575ZIのDoomPlayerは、随分前にフラッシュの使用量が1MBを超え、内蔵SRAMの使用量もすでに90%を超えているのですが、なんとかA2DP sink機能を追加したいと思い作業中です。この機能によりスマホで再生した楽曲ファイルやストリームをDoomPlayerで再生することができます。基本的にBTStackのa2dp_sink_demoを使用しているのですが、昨年試したAVRCP Cover Art 機能は盛り込まない方向です。理由はSTM32U575にはハードウェアでのJPEGデコード機能が無いのと、SRAMの容量やMCUの処理速度的にもキツそうなため。

A2DP sinkとして動かす場合には、上の写真に示したようにSDカードを抜いた状態で起動することで、動作モードを切り替えることとしました。

楽曲のタイトルやアーティスト名を普通に表示しているぶんには問題ないのですが、この文字列が長くなり水平スクロールしながら表示する必要が生じた場合に、表示処理の負荷が高くなりスペクトラム効果表示の更新が遅くなったり、タッチパネル操作の反応が悪くなったりという現象が発生してしまいます。使用するフォントとサイズを小さくすることで少しは改善できたのですが、それにも限りが。。。


チート・コード

2024-06-06 12:33:24 | DoomPlayer

DOOMにはチートコード(Cheat Codes)という機能があります。キーボードからidで始まるおまじない文字列を入力することで、体力を回復したりするズルが行える機能です。DoomPlayerでは、キーボードをつなげなくても上記の画像に示したようにLCDのタッチパネルに表示されたメニューや仮想キーボードから入力することが可能です。

この表示は、DOOMの画面表示の上にlvglで作成したGUI画面を重ね合わせて表示することで実現されています。STM32H7B3I-DKのようLTDCをサポートするデバイスであり十分なDRAMを搭載するボードの場合には、それぞれの画面を独立した2つのレイヤに割り当て、それらをLTDCの機能で重ね合わせ表示することで実現することができました。しかしながら、Nucleo-U575ZI-QのようにLTDCをサーボーとしておらず、メモリ容量にも制約がある環境では実現方法を考えなければなりません。今回は、次のような手段で重ね合わせ表示を実現しています。

  1. DOOMの実行時にもLVGLのGUI処理タスクは並行して走らせておく。
  2. GUI処理タスクが画面をタッチされたことを検出したならば、DOOMタスクの実行を休止し、メニュー画面を表示する。LVGLタスクは、DOOMの画面表示には関与していないので、画面に変更があったのは、メニュー表示の部分だけだと思い込んでいる。そのため、メニューに相当する矩形部分だけの表示が更新され、残りの部分にはDOOMのゲーム画面画像が残ってくれる。
  3. メニューの選択が終了したならば、DOOMタスクの実行を再開する。DOOMの画面更新処理は毎回全画面の書き直しを行うので、LVGLが表示したメニュー部分もすぐに書き直される。

本来であれば、DOOMの画面更新処理部分をLVGLの描画機能を使って処理するのが全体の構造としてはキレイになるのですが、DOOM画面表示処理では、320x240から480x320への拡大処理とL8からRGB565へのカラーフォーマット変換をDMA2Dを用いて行っています。LVGLで処理させるようにすると、LVGL v9ではDMA2Dがサポートされていないこともあり、画面の更新が遅くなってしまいます。

LTDCを使う場合には、メニュー画面を表示している間も、ゲーム進行は継続されますが、上記の方式ではゲーム進行が休止されるので、慌てずにチートコードの入力が行えます。上記の画面でiddqdを選択すれば、体力が100%になり無敵モードになります。

このようにチートコードの入力機能も用意してあるのですが、わたしが興味あるのはこのような機能の実現であり、ゲームのクリアにはさほど関心がないので、いまだにひとつのエピソードも最後まで到達したことがありません。。