VFO(6)

ディジタルVFOの場合,周波数ステップの切替えが必須かと思う.
ただ,実際操作してみるとステップ切替え操作はかなり煩わしいもので,なんとかしたいと思っていた.
切替え頻度を減らすにはパルス数の多いエンコーダを使えばよさそうだが,今一つスッキリ解決するような気がしない.それにエンコーダが高価.

そこで,特に目新しいものではないが回転速度を検出してそれに応じて連続的に周波数ステップを変化させるようにしてみた.ここに動画をUPしておく

今回試した方法は次のとおり.
ダイアル速度を vd,加速を始める速度しきい値を vth として,次式のように変数 L に積算していく.
(1) vd>vth のときのみ, L=L + (vd – vth);

また,速度 vdが 0 のときは,減速用の定数をRdec として,
(2) L=L – Rdec;   ただし,L<0 ならL=0;

次に,Lの値からRaccを加速率の定数として次式でステップ数を決める.

(3) ステップ=最小ステップ + Racc*L^n;

現状,n=2 としているが,他の定数も含め操作感に関わる値なので,いろいろと調整するといいと思う.また際限なくステップが大きくなるのを回避するためには,Lの上限を設定すればいい.

ダイアルの速度の検出は簡単で,エンコーダカウント値の絶対値が速度そのものなので,特に何か処理が必要なことはない.
公開しているソースで言えば,count という変数の絶対値が速度( vd=|count| ).
理由は,count はメインループの1回の処理時間当たりのカウント値なので,その絶対値はダイアルの速度に比例したものとなるため.

結局,メインループの処理1回で変化させる周波数Δfは,

(4)    Δf=count*( 最小ステップ + Racc*L^n );

動画は,
vth=2, Racc=0.002, Rdec=1.0, n=2, 最小ステップ=10[Hz]
で動作させたもの.

個人的には,ステップ切替えスイッチは無くてもいい,と思えるくらいの操作感になっていると思う.

VFO(5)

VFOの回路図とArduino Sketch はこちらに公開しておいた.(動画
画像表示の際アンチエイリアス処理を施しているので,まずその違いを以下に.

アンチエイリアス処理なし

アンチなし.jpg

アンチエイリアス処理あり

20190212_234133.jpg

(数字のフォントが違うのはアンチエイリアス処理とは関係ありません…)

この違いが重要かどうかは人それぞれかと思うが,個人的には上のアンチエイリアス処理なしの表示だとテンションが下がる.

次に,LCDとOLEDの比較を.

まず,LCD

20190212_220612.jpg

20190212_220500.jpg

入手できるほとんどのLCDは視野角が広くなく,見るべき方向が規定されている.
このLCDは向かって右から見ると非常にきれいだが,左からみると線は欠けるし色合いもおかしい.
アンチエイリアス処理による微妙な輝度を再現できていない感じ.

たぶん携帯電話用のLCDだと思うので,縦において少し下方から見るように作られているのだと思う.実際ほとんどのLCDのデータシートには,View angle : 6 o’clock と表記がある.

OLEDの場合.

20190212_224237.jpg

20190212_224229.jpg

当然ながら,どの角度から見ても見え方に変化はなくきれい.

OLEDが100%よさそうだが欠点もある.

20190209_021913.jpg

明るい色のベタはむらが出るし(写真ではわかりにくいが.縞が見えるのはスキャンによるもの),ドライブ電流が足りなくなるのか暗くなる.
「一部だけ白」などはきれいだか,「全面白」とかはあまりよくない.

結論としては,暗いバックグラウンド色で使うならOLED.
明るいバックグラウンド色にするなら見る角度限定でLCDか…

VFO(4)

LCDへの表示がうまくいったので,次はOLED(NHD-1.69-160128UGC3) に表示させてみる.
これのコントローラチップは,SEPS525
16bitごとにCSの区切りが必要なのかどうか読み切れなったが,なんとなく「当然必要でしょ」という雰囲気が感じられたので,LCDのときのようにはいかないかも… と思いながら,とりあえず同じ転送プログラムを試してみた.
やはりNG…
spi.write16() を使って1ピクセルずつ送るとOKだったので,16bitごとにCSの区切りが必要のようだ.
そこで転送プログラム以下のようにしてみた.

s2.png

ブロックサイズを16にしてやることで,16bitごとにCSがネゲートされうまくいった.

20190212_234133.jpg
しかし,CSが頻繁にネゲートされるのでその時間(1回あたり600ns程度)が加わり,転送時間が25ms程度になってしまった.
これにダイアルイメージ描画時間35msとあわせて,更新レートはおよそ60msとなった.
これくらいになると動きのスムーズさがいま一つと感じるのは否めない.
RX621のシステムでは8bitパラレル転送+描画時間20msで更新レートが約30ms程度だったので,それに比べるとかなり動きが悪く見える.

何とかしたいところだが,これ以上プログラムの最適化で何とかできるようにも思えないので,デュアルコアを活用し描画処理と転送を別のコアで行うことで速度UPを試みる.

こちらのサイトを参考にさせていただき,メインのLOOPとは別のコアで動作するもう一つのタスクを作った.

setup()内で,

xTaskCreatePinnedToCore(task0, “Task0”, 4096, NULL, 1, NULL, 0);

として,次の関数を用意すればいいようだ.

void task0(void* arg)
{
while (1)
{
//SPI転送処理
delay(1);
}
}

delay(1) がないとシステムリセットが繰り返し発生した.
よくわかっていないが,おそらくOSに処理を返す必要があるからだろうと推測.
また,同じディレイ時間にも関わらず delayMicroseconds(1000) ではNGだった.

とにかく,これでloop() とtask0() が別コアに割り当てられ同時に動く(と思う).
あとは,この2つの処理間でのデータの同期に気を付ければいい.

というわけで,
一時はあきらめて,IOピンがまだ余っているのでOLEDはパラレルでいこうか…,などと考えたが,OLEDでもSPIで更新レートを50ms以下になんとかすることができた.

もっと簡単にRX621からESP32に移行できると思っていたが,思いの外苦労してしまった.
とは言え,初めてESP32を使ったのにもかかわらず,短時間でそれなりのものが組めたのは,いろんな方がネットにUPしてくれている情報のおかげなのは間違いない.

感謝の意味を込めて今回作成したソース(VFOsys)をこちらに公開しておく(無保証,サポートなしで).
エンコーダを回せば,ダイアルが動き(動画)その周波数が出力されるだけのものなので,そこからはこのソースをベースに各々機能を追加していただければと思う.

とにかく,ようやくシステムがうまく動き出したので,次はLCDとOLEDの比較をしてみよう.

VFO(3)

RX621で開発を進めていたところESP32が気になってきて調べてみると,
さらに安価で動作クロックも速く,しかもデュアルコア…
これは乗り換えるしかない,ということで  ESP32-DevKitC  を入手.20190206_125309~2.jpg
Arduino環境で開発できるようで,こちらを参考にさせていただきセットアップ.

ESP32のIO数があまり多くないので,ディスプレイはSPIで制御することにする.
幸い,OLED(NHD-1.69-160128UGC3) は,シリアル/パラレルどちらの接続もできる.
同じ解像度(160 x 128)のシリアル接続のLCDも用意して比較することにした.

20190213_024447.jpg

回路は下記のとおり.
LCDとOLEDは排他利用となる.Si5351は秋月電子のモジュール.

VFO.PNG

20190212_233108.jpg 20190212_233146.jpg

ディスプレイのインターフェース信号の配置が気持ち悪いが,ESP32内蔵のSPIを使うには
こうなってしまうようだ.
任意のピンでSPIができるようだが,その場合ライブラリによるソフトウェアSPIになり,転送速度がかなり遅くなるらしい.
以前,ダイアル表示のカウンタを作ったとき経験だが,ダイアルイメージの更新レートがおおよそ50ms以上になってくるとダイアルの動きがスムーズに見えなくなってくる感じだったので,これ以下にはしたい.

SPIのレジスタを直接操作しないと無理だろうなと思い,いろいろ調べてみると,
こちらのサイトに手掛かりがあり参考になった.
ESP32のリファレンスマニュアルも読んでみると,SPIには32bit x 16 (512bit)のバッファがあって,そこにデータを貯めておき(例では,半分の256bit),これを1ブロックとして一機に送出する方法らしい.これを必要回数繰り返す.
今回は,16bit/pixelx128x160=327680 bit 転送しないといけないので,
バッファをフルに使えば,327680/512=640回 となる.

以下のようなプログラム(抜粋)を組んで動作を確認した.
配列GRAM65k[][](16bitカラーのイメージデータ)をすべてディスプレイに転送する.

s1.png

オシロで波形を見てみると,1ブロック転送中はCSはアサートされたままになるようだ.
16bitごとにCSを区切らないといけないのでは?と思ったが,どうやらST7735やILI93xxといったディスプレイコントローラはその必要はないようだ.

肝心の転送時間だが,SPIクロック周波数が27MHzでおよそ12ms だった.
クロック周波数がディスプレイコントローラの動作範囲を超えているが,
「実力的にOK」との情報が多数あったのでそれに期待.

結果としては,上手く動作した.
20190212_220612.jpg

ただ,転送時間は12msだがダイアルイメージの描画処理に35msほどかかっている.
RX621のときは20ms以下だったのでかなり遅くなってしまったのは予想外だった.
CPUクロックが2倍以上なのにどういうことだろう?
当然ながら,速くなることを期待してたのでかなりテンションが下がる…
バックグラウンドで動いているfreeRTOSのせいかもしれないし,
デュアルコアも活かせてないし仕方ないか…

でもまあ,CPU自体RXよりかなり安価でRAMが520KBもあるのは捨てがたい.
現状,更新レートは目標通り50ms以下(12ms+35ms)にはなっていて,視覚的にもあまり違和感はないのでこのまま進めることにしよう.

次は,OLEDで動作確認しないと.