CW Decoder 解説編 (4) もっとハード寄りの解説


[4] もっとハード寄りの解説

 

このページは、前回のプログラム構成を説明するものです。大部分の方には不要な情報かもしれませんが、備忘録として置いておきます。


1)   FFT計算について


      計算速度の検証 (RP2040からRP2350)


FFT計算の仕様を決定する上で、ライブラリの計算速度を検証しました。

RP2040 (Raspberry Pi Pico)の実測値

  サンプル数    計算時間   回数/

        128        9ms   111
        256                        19ms       53
        512                        42ms       24
       1024                       89ms       11

デコード速度の上限を50WPMとすると、1dot24msecですので、128サンプルの場合(9ms/)では、1dotの判定に2.5サンプルしかデータが無く50WPMのデコードの実現が厳しいとわかりました。(逆に言えば、1dotの判定に5サンプル必要と考えると、上限は25WPMと実用レベルにならないと判断。)

 

そのため、MCUをRP2350 (Raspberry Pi Pico2 浮動小数点演算機構有り)に変え、実測値をしてみました。


結果は、「サンプル数 128 の時、計算時間は 1.8ms」という「5倍速い」結果となりました。

最終的には、FFT計算後の信号シェーピング処理を加えて、全体で約4msの処理となっています。 (50WPM1dotに対し、6サンプルと判定に十分なデータ数を確保できています。)

 

      設定パラメタ (解析範囲、解像度) から FFT計算のパラメタ


CW Decoderは、解析周波数範囲を300Hz-900Hz、解像度を20Hzとしました。
FFT計算のサンプル数は128サンプルとしましたので、設定パラメタの解像度20Hzから、サンプリング周波数は2560Hzとなります。 これにより、FFT解析周波数範囲が 0-1280Hzとなり、必要な帯域 300Hz-900Hzをカバーできている事が確認できました。

 

      計算回数とデータシフト


FFT計算から信号シェーピングの処理は、約4ms (1/256)に1回必要です。
一方、サンプリング周波数 2560HzFFT計算用の128サンプルを取得する時間は、1/20 (50ms)ですので、FFT計算にサンプリングデータの準備が間に合わないという矛盾があります。

そのため、FFT計算用のデータは、直近の128サンプルを使用して、1/256秒毎に計算します。(128サンプル中、10サンプルは最新のもの、残りは前回のデータを利用)


 



  2)  割込みの優先順位


      Arduinoの割込み (優先度はすべて同じ)


CW Decoderでは、ADCのサンプリングにTimer割込みを使用しています。
また、グラフ表示のために、定期的な画面描画(更新)も必要ですので、Core1からCore0GPIO割込みをかけています。(割込みが2個ある)

Arduinoでは、すべての割込み処理の優先順位が同じ設定になっていますので、このままでは片方の割込み処理中は、もう一方の割込み処理は無視されてしまいます。

そのため、ADCTimer割込みの優先度を、GPIO割込みより相対的に上げる設定が必要です。

 

下はテストプログラムで処理の状況を確認した例です。(それぞれの割込み処理ルーチンの冒頭で、Test Pinの出力をHighにし、処理終了時にLowにして観測)

 


Arduinoの標準設定(すべての割込み処理の優先順位が同じ)












GPIO
割込み(Core0)の優先順位を下げた場合

GPIO割込み処理中でも、ADCTimer割込みを優先して処理している。 












CW Decoder
で使用しているカラー液晶(240x320x16bit)の画面消去には、153.6kByteを更新する(データ転送する)必要があります。CW DecoderではSPIの転送速度を通常の4(40MHz)で使用していますが、それでも、31msかかります。そのため、この優先順位の設定は必須となります。

(グラフィック制御に、既存のライブラリを使用していないのは、このあたりの設定を自由に変更できないためです)



3) 画面描画の錯綜


      グラフ画面表示を作ったら何が起こったか


最初のバージョンは、デコードした文字だけを表示する全画面表示だけでした。これはCore0のメインループから1文字ずつ出力するものです。(コンソールへの出力)
そこに、リアルタイムでグラフを表示するグラフ画面表示を追加しました。これは、Core1からの割込みで、Core0がグラフ表示する処理としました。


テストをしたところ、画面が崩れる(でたらめになる)現象が起きました。

画面表示(具体的にはILI9341へのコマンド/データの書き込み)は、両方ともCore0が出力しますので、競合によるデッドロックは発生しません。


よくよく考えたところ、メインループで文字を出力している最中やスクロールのために画面を書き換えている最中に、グラフの書き換えが発生した場合、ILI9341へのコマンド/データが、文字描画とグラフ描画のそれぞれから送られて混在(錯綜)してしまいます。これが原因で画面が崩れている事がわかりました。

 

      対策


対策として、どちらかが書き込んでいる間は、他方が待つことが考えられますが、グラフ描画はリアルタイムですので、待たせる=信号処理が遅れる ことにつながります。
そのため、文字とグラフの画面描画(更新)プログラムを1つにまとめると同時に、文字データもグラフデータもバッファに入れ、グラフ更新と同時に、バッファ内の文字データも表示する形にしました。

 

      画面描画とデコード処理の対策 (Core0内の処理)


割込みの優先順位でも書きましたが、LCDの画面更新には最大31msかかります。この間のCore0は優先度の高い割込み処理をしていますので、メインループのデコーダ処理が止まってしまいます。

デコーダ処理は、常にCore1から出るKeyOn/Off信号を監視している必要があり、このデータ(Keyのステータス)4ms毎に更新されます。そのためOn/Off監視が31ms間止まってしまうのは致命的です。

対策として、Core1のKey On/Off信号の直接監視をやめて、On/Offデータはバッファに書き込むことにしました。その結果、Core0のデコード処理は(リアルタイムでKeyのステータスを読むのではなく)、バッファのデータを(少し遅延して)読み込んで疑似的にKey On/Offの時間監視をするように変更しました。

(画面更新中のデータは、まとめて読み出す事ができます)

これにより、画面描画中のKey On/Offデータを取りこぼすことなく処理ができるようになりました。



以上でCW Decoderのソフトウェア構造に関する説明を終わります。




(3) 実際のプログラムの構造    <   [Home]   >   (5) まとめ


コメント