マイコン工作実験日記

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

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%になり無敵モードになります。

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

 


TLV320DAC3203

2024-05-23 12:51:37 | DoomPlayer

秋月の新製品情報を見ていたら、Nucleo-U575ZI-Qが出ていることに気づいたのですが、4,800円もすることにビックリ。円安の弊害をあらためて痛感しました。これじゃ、EP32やRas Picoに人気が流れてしまう傾向は強まるばかりですね。

DoomPlayer Nucleo-U575ZI にコーデックを載せようとして検討した際にも、コーデックチップがとても高くなっていることに気付かされました。TLV320AIC3204あたりが900円もする。そんななか、200円台で買えるTLV320DAC3203を見つけたので、これを使うことにしました。名前が示すようにTIの製品分類ではAudio CODECではなくAudio DACになっているのですが、アナログ入力やデジタルマイク入力も受けられるようです。96Kとか192Kのようなハイレゾオーディオには対応しておらず48KHzが上限のDACですが、DoomPlayer では44.1KHz 16bitステレオの音楽データをヘッドホンへの出力ができれば良いので、これで充分です。

DAC3203はDVddとして1.8Vが必要なので、NJM12877F18を載せてあります。アナログ電圧(AVdd)は内蔵LDOで生成するという使い方にしたので、AVdd端子はパスコンをつないだだけです。

STM32U575側では、SAIを使ってデジタルオーディオデータを送信。HSI 16MHzからPLLを使って生成した11.29MHzをMCLKとしてDAC3203に供給しています。この周波数は、44.1KHzの256倍になっています。DAC3203のReference Guideを参考にして初期化をおこなってから実際に音を出してみると、イヤフォンからはとても大きな音が出てきてびっくり。レジスタ設定を確認してみるとディフォルトのPowerTune ModeはPTM_P3になっており、アナログ出力の振幅が大きくなるようです。PTM_P1に変更することで、イヤフォン向けの大きさになりました。


新基板を作成

2024-05-15 12:50:34 | DoomPlayer

しばらくぶりの更新ですが、この1ヶ月の間にNucleo-U575ZI用の基板を作り直していました。

当初は使用する部品数を最低限に抑えて、簡単に作ることをひとつの目標としていたので、音の再生にはSTM32U5のDACを用いていました。その目的は達成することができて、音楽の再生もDOOMのゲームの実行もできるところまではできたのですが、実際に音を聞いてみるとやはり12bitのDACでは高域がカットされてしまうので、音の厚みが欠けており満足のできるものではありませんでした。

そんなわけで、部品数は増えてしまうけれども、やはりCODECを載せることに方針転換。基板を作り直しました。CODEC制御のためにSAIとI2Cを使う必要が生じたので、それに伴いSTM32U5側のピン割り当ても一部変更せねばならなくなりました。ひと通り動くようになったら、回路図とソースコードを開示するつもりです。


タスク構成 -- その2

2024-04-14 15:23:02 | DoomPlayer

前回の記事でタスク構成について書きましたが、その際にはCMSIS_OS2のAPIを使ってタスク情報を取得していました。DoomPlayerでは自分で書いたアプリ部分ではCMSIS_OS2のAPIを使っているので、自分が作成したタスク情報はこれで全て取得できているのですが、使用しているライブラリ部分でタスクが生成されていると、その情報は取得できていませんでした。

そこで、改めてFreeRTOSレベルのAPIを使ってタスク情報を取得するように変更しました。具体的にはuxTaskGetSystemState()を使用しています。また、タスク名に続いてそのタスクの優先度も表示することにしました。スタックサイズについては、ワード単位で表示されています。

この変更によりFreeRTOS内部で作成されているIDLEタスクとタイマー処理用の Tmr Svcタスクが表示されるようになりました。lv_conf.h において ''#define LV_USE_OS LV_OS_FREERTOS'' を指定するとlvglDrawという描画処理用のタスクも生成されるのですが、不必要に大きなスタックが消費される上に、タスクの優先度が高く設定されてしまい、他のタスクとの連携の観点で問題が生じするため、LV_OS_NONEを指定して使っています。この問題は、現在のlvglのgithub masterでは修正が加わっているようなので、次のリリースに期待したいところです。


CR1220とタスク構成

2024-03-20 12:16:46 | DoomPlayer

画面キャプチャ機能でSDカードに画像を保存できるようになったので、そのタイムスタンプとファイル名生成のためにRTCバックアップ用の電池としてCR1220を追加実装しました。Nucleo-U575ZIでは、ディフォルトでMCUのVBAT端子はVDDにジャンパされているのでSB50の0Ωを取り外してから、電池を装着してやります。これで、ようやくと計画していた全ての部品を実装したことになります。

RTCのカレンダー機能を使うので、日付と時刻の設定機能も必要となります。LVGLを使ってGUIとして実現するのがベストなのでしょうが、使用頻度が低い機能にわざわざGUIを用意するのも面倒です。そこでNT-Shellを導入してCLIのコマンドでdate, timeコマンドを用意しました。

せっかくNT-shellを入れたので、タスク状態を表示するpsコマンドとQSPIフラッシュ上に書き込まれているファイル情報を表示するflashコマンドも用意しました。

psコマンドで表示されている数字は、残りスタックサイズを示しています。各タスクの大まかな役割は次のとおりです。

 

タスク名 役割
defaultTask 起動時のSD, QSPI faslhの確認、スクリーンキャプチャ、タッチパネル処理を担当
guitask LVGLと画面更新処理
btstacktask BTstackとHIDホスト処理
shelltask NT-shellとコマンド処理
flacreader FLAC音楽データの読み出しと展開処理
mixplayer 音楽と効果音の合成とFFT処理

上記のコマンド実行例は、起動直後の状態を示しています。この後、Bluetoothでコントローラを接続して音楽を再生したりしているとスタックが消費され、DOOMのゲームを開始すると新たにdoomTaskが開始されます。


画面キャプチャ

2024-03-06 20:13:47 | DoomPlayer

作業中のNucleo0U575ZI版のDoomPlayerにもようやくと画面のキャプチャ機能を用意しました。青いUSERスイッチを押すことで、SDカード上に画面キャプチャを保存します。今回は、16ビットのパラレル接続LCDを使っていますので、画面情報はLCDのコントローラであるILI9488が持つGRAMから読み出すことにしました。

書き込みの際にはMomory Writeコマンドを使ってRGB565形式の画素データを16ビットで書き込むことができますが、Memory Readコマンドを使っての読み出しの際には常にRGB888形式になるんですね。知らなかった。そのため、画面データ量は480x320x3 = 460,800バイトとなります。STM32H7B3を使っていた時は、JPEGエンコーダを使って圧縮した画像データをSDカードに保存していましたが、STM32U575ZIにはJPEGエンコーダは備わっていません。LibJPEGを使ってソフトで圧縮しようかとも思いましたが、作業領域のSRAMも必要になるので、今回はRGB888形式のままで保存することにしました。ちょっと手間ではありますが、PC側でImageMagickのconvertをつかてJPEG/PNGに変換しています。

画面データの読み出しは、LVGLの画面更新やDOOMの画面更新作業と競合しないように配慮しています。また、SDカードへの書き込み動作は、音楽再生のための読み出し動作とSDカードアクセスが競合するので、一時的に音楽再生を止めてから読み出しを行うことにしています。

DualShock4やDualSense, 8bitdo Zero2をつなげた時には、480x320に拡大して画面表示をしますが、コントローラが接続されていない場合には、320x200で表示することにしてみました。

LVGLを使って作成した画面上のボタンを押すことで操作可能になってはいますが、画面タッチでの操作はかなりキツイ。画面表示も小さくてみづらいし、実用的ではありませんでした。


LED追加実装

2024-02-17 10:59:02 | DoomPlayer

ボードの右端に長らくと未実装だったLEDを追加実装。音楽プレーヤで再生時に、曲に合わせて点滅します。元々のlvgl音楽プレーヤーが 4つの帯域に分割したスペクトル表示をする仕組みになっているので、その強さに応じて4色のLEDの明るさをPWMで変化させています。PWM制御にはちょうど4チャンネルのPWM出力ができるTIM2を使用。PWM出力に割り当てた端子に抵抗を介してLEDを直結しているだけの簡単な仕組みです。

それなりに明滅してくれますが、緑が暗めで青が明るめに表示されている印象。電流制限抵抗を調節するとか、PWMのデューティ比を各チャンネル毎に調節するとかした方がいいのでしょうが、試行錯誤するのも大変そうなのでこれで我慢。

LEDには秋月で購入した1608サイズのロームのチップLEDを使用しました。このサイズなら手半田作業できるのですが、ピンセットで掴み損ねて弾いてしまって赤色をひとつ紛失。カーペットの上に落としてしまうと、もう発見不可能です。

袋の張られた品番シールに色付けされているのが、とっても助かる。こういう気配りが嬉しいですね。


8BitDo Zero 2

2024-01-27 14:10:24 | DoomPlayer

BT860-SAを使ったゲームパッドのBluetooth接続もできるようになったので、新たなコントローラーとして8BitDo の Zero 2のサポートを追加しました。DualSenseやDualShock4はアナログパッドや6軸センサー機能も有しており機能豊富なのですが、大きくてそこそこの重量もある上に高価でもあるので、もっと手頃なコントローラを探したところZero2を見つけました。

このコントローラは、Switch, Window, Android, MacOS, Keyboardの4つの動作モードを有しており、ベアリングの開始方法によって動作モードを選択できるようになっています。どのモードを使うのがいいのか迷うところですが、ちょっと調べたところMacOSモードを使うのが都合がいいことがわかりました。その理由は簡単でこのモードではデバイス名が 'Wireless Controller' となり、ペアリング時にDualSenseやDualShockと同じ名前になるためです。DoomPlayerではBluetoothでコントローラを検索する際にデバイス名とCoD (Class of Device)の情報をチェックしているのですが、このMacOSモードを使うとDualSense/DualShock4と同じデバイス名/CoDを使ってくれるので、容易に判別できるのです。

逆にデバイス名とCoDだけでは、DualSense/DualShock4/8BitDo Zero 2の違いは判別できないのですが、そこはさらにVendor ID/Product IDを調べて識別を行うようにしています。

さて、実際にZero 2 をつなげてみると、出力されるHIDのInput Reportは DualSense/DualShock4とはかなり異なります。ボタン数が少なくセンサーもないのでInput Reportのサイズが小さくなるのは当然なのですが、出力されるタイミングが全く異なります。DualSense/DualShock4では、毎秒800回ほどのレートで常時継続してInput Reportが出てくるのですが、Zero 2ではボタンを押したり/離したりして状態変化があった時にのみInput Reportが送られてきます。毎秒800回のInput Reportの受信処理はそれなりの負荷になりますので、小さなマイコンや省電力化を図りたい場合には、Zero 2の動作は好ましいとも言えます。