「ソフトウェアー・エンジニアリング」カテゴリーアーカイブ

ソフトウェアー関係の話題など・・

最近は、github にプッシュしてます~

組み込みマイコンでstd::functionを~

少し落ち着いたので(実際は、やるべき事が山済み状態だけど・・)気になっていた部分をクリーンナップしている。
現在、俺俺フレームワークは、リアルタイムOSの助けを借りない方法で実装を行っているので、一連の長い処理を書く場合などにはコールバックの仕組みを与えている。
C++11 以降、STL には、そのような場合便利で安全な方法が多数用意されている。
しかし、その場合、通常は「stdc++」をリンクする必要があり、その場合、許容出来ない程大量のワークエリアを消費し、実用的では無い場合があった。

さて、コールバック機能を実装する場合、どうしても、「std::function」を使いたい。
・このテンプレートは、非常に強力かつ高機能だが、詳しい使い方は、検索してほしい。
・このテンプレートにする最大の理由は、ラムダ式が使える事で、コールバックの中身を「直」で実装できる。

しかしながら、これを使うとなると、関連する例外処理など他の機能を利用している為、stdc++ ライブラリのリンクが必須となってしまう。
しかし、よく調べると、g++では、「supc++」ライブラリが分離されておりこれをリンクすれば、最低限の消費で済む事が判った。
※「supc++」は「stdc++」ライブラリのランタイム部分のみ。
※例外の外部関数を実装する必要がある。

namespace std {
    void __throw_bad_function_call()
    {
        abort();
    }
}

※コンパイラオプションで例外を禁止していても、これだけはリンクする必要があるようだ。
※これは、「common/stdapi.cpp」に分離してあるので、Makefile にこのソースを追加する必要がある。

コールバック関数の登録で、重要となるのは、たとえば、クラス内からコールバックを設定するような場合だが、普通に書いた場合、「static」な関数しか登録できない。
※それでも別の逃げ方が無い訳では無いが、少し面倒・・
static だと、クラス内の public メンバーなどにアクセス出来ない。
そこで便利なテクニックとして、ラムダ式によるキャプチャー機能を利用する事で、以下のようにクラス内メソッドを呼び出す事が可能となる。

void play_loop_(const char* root, const char* start)
{
    static loop_t t;
    t.start = start;
    if(strlen(start) != 0) {
        t.enable = true;
    } else {
        t.enable = false;
    }
    sdc_.set_dir_list_limit(1);
    sdc_.start_dir_list(root,
        [&](const char* name, const FILINFO* fi, bool dir, void* option) { play_loop_func_(name, fi, dir, option); }, true, &t);
}

上記コードは、オーディオプレイヤーのコードで、ディレクトリーを巡って、オーディオファイルを再生する。
「sdc_.start_dir_list()」で、ファイル情報アクセス時に、設定されたコールバックが起動する。
「play_loop_func_」は、ラムダ式のキャプチャー「[&]」が あり、クラス内のリソースにアクセスする事が出来る。(これは本当に便利だ!)

まとめ:
「std::function」を使う事で、関数オブジェクトの利便性が強化された、これは、C++11以降の恩恵で、かなり大きなトピックと言えるが、組み込みマイコンのような制限されたリソースでもそれを活用できる事が判った事は大きい~

RX71Mを試す

かなり昔にデバイスは購入してあったのだが、中々試せないまま、RX65Nなどに寄り道していたのだが、240MHzの実力を評価したくて、一応試してみた。
RXv3
の発表があり、「RX71M」も最高性能のRXマイコンでは無くなってしまったが、それでも、240MHzは120MHz版に比べてどのくらいの性能があるのか試しておきたい。

169ピンのユニバーサル基板も手持ちが最後なので、次は基板を起こす方が良さそうだ・・(この基板、安くて小さくて便利なのだが、販売終了しているぽぃ)
以前にRX64Mの試作では、SDRAMの32ビットバスを試したが、あれは、ちょっとマズイ・・、32ビットのデータ幅を割り当てると、他に使うペリフェラルが極端に制限されてしまい、イーサーネットやSDHCに割り振る事が難しくなる、なので、今回は16ビットバスでSDRAMを載せようと思う。
※このデバイスは「もらった」もので、SDHC未対応なのだが・・・
RX65NのEnvisionキットでファミコンエミュレーターなどを試して、やはり外部メモリーは必須だ・・(内臓メモリが1Mバイトくらいあれば・・)
RX71Mには、RX65NのようなLCD制御機能は無いが、単純に接続するだけなら、EXDMA転送で代用できそうだし、SSI(シリアルサウンドインターフェース)など、デジタルオーディオ系にも強そうだ。

今回試作して判った事として、電源ラインの重要性だった、面倒なので、アナログの電源は接続しなかったのだが、それが原因で、ブートモード(SCIインターフェース)でデバイスのコネクションが正常に終了せず、接続できなかった、電源は全て接続してパスコンも付けていたのだが、原因がわからずにいたが、関係無いと思うけど的な感じで、アナログ電源を接続したら、あっけなく正常動作した。

240MHz動作は、既に実績があると(依然にRX71Mを使ったボードを設計して、納入した実績があり、その時は問題なかった・・)思っていたが、何か怪しい動作をする。
具体的には、240MHzで動かすと、SCI関係で文字化けを起こす・・
CPUのクロック以外は、120MHz版と変わらないので、原因が判らない・・
プログラムは暴走する訳ではなく、動いているようだ。
120MHzであれば問題無い・・
どうも、240MHz動作時は、最大で240mAの電流が必要らしいので、そのせいかと思ったが違うようだ・・
ただ、USBから供給する電源の品質はあんまし良くないのと、瞬間的に大きな電流を「引く」とかなりドロップする。

オシロで、SCIの信号を確認して、やっと解決、ベースクロックのPLL定数を求めるソフトのバグだった・・、240MHzと思っていたら180MHzだった。
※そりゃー文字化けするよなぁーwww
クロック設定クラスは、以前に収めた機器では、240MHz固定だったのだが、最近テンプレート化して、色々柔軟性を求める仕様に拡張した過程で、マズイ実装を行っており、120MHzだけは正確に機能していた・・・
ハードの問題だと思って色々対策して、結局ソフトのバグとゆーのは、まぁ良くある話なのだがー、最近視力が落ちて、配線の質が落ちており、ハードに自信が無いと思っているのもある・・
さらに、鉛フリーハンダは、温度に敏感で、少し低いと綺麗に付かない。

KIT-RX65Nラップタイマー(その1)

デジタルストレージオシロスコープは、部品が揃って無いので、保留状態となっており、新たにラップタイマーの実装を行っている、ゆくゆくはデータロガー的な物にしようと思う。
本当は「もて耐」で使う予定だったが、本業が忙しく間に合わなかった・・・

RX65N Envision Kit で何かを作る場合、ケースはどうするか?
ピッタリ収まるような汎用品を探したが、見つからない・・
※かなり大きく厚くなってしまう・・・
そうなると、作るしか無い、以前に「フォーレックス」と言う発砲プラスチックでケースを作った事がある、カッターで切れて軽くて作りやすいものの、意外と柔軟性が無く、簡単に割れたりもする。
そこで、専用ケースを作る為、とりあえず、一番安い3Dプリンターを買ってみた。
まだ組み立て中で調整も試運転もできていないが、前から欲しかった事もあり、ついついポチってしまったw
※比較的安い物を買ったので、安物買いのナントカにならないか不安ではあるのだが・・・

ラップタイマー的な物は、昔にAVRで作った事がある、液晶は128×64のモノクロで、今でも動作するのだが、かなり大きく(厚みがある)、バイクに付けるには少し大きすぎる・・
ラップタイマーだけなら、P-LAP などで十分だが、それなりの値段でもあるし、GPS を使った区間タイム表示、インジェクターパルスを収集して正確な燃費計など、やりたい事は色々あり、性懲りも無くまた作る事にした。
※今までに、何回もやり直してきたが、中途半端な状態でストールする苦い過去がある。
とりあえず、以前のリソースを元に液晶のGUIデザインなどをカットアンドトライで行っているのだが、解像度が高いので GUI のビットマップなどを作り直す必要がある、また、タッチパネルなので、それに合った操作性にする必要がある。

その前に、カラー画像を表示する為、BMPファイルのローダーをポーティングした。
BMPなら、比較的フォーマットが単純なので、メモリーを多く消費せず、また以前にアプリ用に実装したソースコードがほぼそのまま使える。
※少し前に JPEG のローダーをポーティングしたのだが、libjpeg では、構造上、一時メモリが必要で、メモリ不足で動作しなかった、libjpeg の API 操作だけで、一時メモリを必要としない方法もありそうだが、それに関連する情報が少なく、ライブラリソースを研究する時間的余裕が無く保留にしてある。
BMP でも、通常は、デコードした画像情報を一旦メモリーに蓄えて、フレームバッファにコピーする手法を取るのだが、余分なメモリーは無いので、デコードしながら直接フレームバッファに描画する。
BMP では、フルカラーは無圧縮、インデックスカラーはランレングス圧縮なので、ワーク用メモリを殆ど消費しない。
この為、BMP ローダーをテンプレート・クラスとして、描画クラスを参照で渡す仕組みに変更した。

template<class RENDER>
class bmp_in {
    RENDER&    render_;
...
    bmp_in(RENDER& render) : render_(render) ... { }
};

コンストラクターで、RENDER クラスの参照を渡す、BMP デコード時、このクラスを使ってフレームバッファに直接描画する。
※テストで使った BMP ファイルは24ビットカラーだけど、液晶は RGB565 なので、マッハバンドが気になる、またこの液晶は、視野角がかなり狭いのが痛い(低価格だから仕方無いのか・・)
※BMP 形式では、24、32、ビットフォーマットは圧縮されないので、やはりPNGなどもサポートする必要がありそうだー、その場合、Zライブラリーもポーティングする必要性がある。

GUI の部品は、bmc (ビットマップコンバーター)で変換した bitmap ストリームとして持っていて、他に、オブジェクトの横幅、高さ情報も保持している。
液晶が 128×64 くらいでは、この方法でも部品が小さいので問題無いが、480×272くらいだと、オブジェクトが大きく、速度の面でも、メモリーの面でも効率が悪い。
そこで、今後の事を考えて bmc に機能を追加して、簡単な圧縮フォーマットを実装する必要がありそうだー
とりあえず、カラーは考えないで、モノクロで、そこそこ、描画が効率良く行えるように、シンプルなフォーマットを考える必要がある。
※本来、モノカラーでも、「黒」、「白」の他に「透過」のアトリビュートが欲しいところではあるが、部品の描画を行うアプリ側で、細かく実装を行い、何とか見た目の問題を回避している、より複雑な GUI が沢山あると、これではマズイと思うのだが、その時考える事にしておく・・・

また、GPSから出てくるテキストのパースなどを実装する必要があり、実験したいのだが、部屋の中では、電波が弱くて受信出来ない、そこで外部アンテナを取り付けて、アンテナだけ外に出すようにした、実験してみると、窓枠に置いておけば、(窓が閉まっていても)ギリギリ受信出来る事を確認して、出てくる「経度、緯度」から、自宅の位置を確認した。
※以前にSUP500、GPSレシーバーを使っていたが、販売されていないので、10Hz出力が可能な物「GTPA013」を再度購入した、外部アンテナと、接続コネクターは秋月で入手出来た。
GPS関係では、データをロギングした後に軌跡を表示したりするアプリも作る必要があり、まだまだ調べたり実験する事が沢山あるので、これからの研究が必要な分野なので、まだまだ時間がかかりそうと思う。

RX24Tを使ったCNCコントローラーボード(その1)

CNCで、各軸を動かすモーター制御は、市販されているボードを使う事も出来るが、大抵はPCから制御するだけのボードで、PCを介さない状態で、手動で動かすインターフェースや、各軸の位置表示(PCの画面で行う)なども欲しいと思うので自作してみる事にした。
また、Gコードをパースするだけじゃない方法など色々なフォーマットや、インテリジェンスな制御系も試せる(カメラで画像認識を併用するなど)と思う、他にフライス盤をCNC化するのが中途な状態でもあり、最低2台は必要な事もある。

一般的に、Gコードのパース、制御では、「Mach3」が有名で、このソフトウェアーは非常に優れていて、柔軟性もあるのだが、ライセンス料が少し高い(自分にとっては)、また、ドライブのハードウェアーを簡単にする為、パラレルポートに直接駆動パルスを出力する仕組みで、PCの能力や、裏で動作するデーモン系の動作などに左右される印象を受ける(これは回避するHowToが十分整ってはいると思うのだが)、ハードやソフトを自作する方がよっぽど面倒で時間のかかる作業ではと思うのだが、「作ってみたかった」のが大きい。

マイコンは何にするか考えたが、自分の手持ちで手頃なのはRX24Tだ、このマイコン、元々はモーター制御などに適した構成のRXマイコンだけど、普通に他の用途に使っても十分な性能で、安く、CPの高いマイコンだと思う、ただ、USBは内臓されていないので、PCとのインターフェースはシリアル通信を使う事になる。(以前に10個まとめて買ったので、まだ沢山ある、@540)
・RX、32ビットコア
・80MHz動作
・PWMタイマーなどが80MHzで駆動できる。
・256Kのプログラムメモリーと16KのRAM
※最近では、CANインターフェース付きで、プログラムメモリーが512K、RAMが32Kの製品が、ほぼ同じ価格で販売されているようだ。(@570)

モーターはステップモーターなので、32段階のマイクロステップ動作が可能なドライバーを3台購入した。(1個7ドルほどだった)
「マイクロステップ」は、元々イギリスのベンチャー企業が始めたと思うのだが、磁気回路の特性に合わせて、相に流れる電流をベクトル制御する事で、見かけの分解能を再分割する技術だ、2相のステップモーターの場合、直交するXY軸の関係なので、それぞれ、サイン、コサインの電流割合で制御する事で、理論的には、無限大に分解能を上げる事が出来る。
今回買ったドライバーは、32段階までが可能なので、1回転200ステップのモーターなので、6400パルスで1回転の制御が出来る。
モーターの定格は、3Vで3Aのようだが、それは静特性で、動特性では、回転速度が速くなる程コイルのリアクタンスにより電流が流れにくくなるため、より高い電圧が必要になる、今回のドライバーは最大40Vで3.5Aまで可能なので、丁度良いと思う。
また、モーターを動かしたり停止したりを繰り返す場合、ドライバーはチョッパー動作でそれなりの効率ではあるが、モーターに蓄えらたエネルギーを吸収する必要があり、ドライバーはかなり発熱すると思うが、それなりに大きなヒートシンクが付いている。

とりあえず、モーターの回転は確認出来たので、ソフトを作りこんでいこうと思う。
まず、3軸同期で直線移動などだが、一般的には「ブレゼンハム」のアルゴリズムで、ステップパルスを生成するのが相場だろうが、自分は、分数を使う事にする(結果的にはほぼ同じだが・・)。
分数の場合、分母、分子を組で管理して、単純な足し算を使って行い、1以上になったら、パルスを発生させ、分子から分母を引く、このようにすると、切捨てが起こらないので、誤差が全く発生しない。
ハードウェアーは、リミットスイッチの入力インターフェースが無いので、それも載せる必要があるが、アイソレーションに使うフォトカプラが手元に無い・・
また、スピンドルモーターの制御用出力も載せる必要がある、スピンドルモーターは、三相モーターで、インバーターで制御するが、そのままだとOn/Offしか出来ない。
回転数の制御はボリュームなので、D/A出力も必要だろうか・・(シリアル接続で、コマンドで出来れば良いのだが、このインバーターには機能が無いようだ・・・)

KIT-RX65N、デジタルストレージオシロスコープ(その1)

RX65Nには、2ユニットのA/D変換器があり、分解能12ビット、最大2MHz程度のスループットがある。

このデバイスを利用して、デジタルストレージオシロスコープを作っている。
まだ、ソフトウェアーは開発中で機能追加を行っている段階ではあるものの、ハンドヘルドな測定器にする事が出来そうで楽しみが増えた。(前から作ってみたかった~)

画面もそこそこ広く、タッチスクリーンGUIで、操作も柔軟性があるので、ソフト次第で、かなり良いガジェットになりそうだー

実用的な物にするには、外部にプリアンプや、ゲインアンプを接続して、入力電圧の切り替えなども行う必要があるので、外部基板を作成する必要がある。

最初の問題は、LCDの描画だ、現在は16ピクセルRGB565シングルバッファで行っている。
これだと、描画の更新による書き換えが、画面に現れるので、具合が悪い。
理想的には、フレームバッファを二枚にして切り替える事だが、それだと、メモリーが足りない。
最初、256色のインデックスカラーにすれば、メモリーの問題は解決するものと思ったが、RX65NのLCDコントローラーは、フレームバッファのラインアドレスを64バイト単位で指定する為、8ビットだと、1ライン512バイト(32バイト無効になる)にしなければならず、やはり、メモリーが足りなくなる。
結局、1枚のバッファでも、書き換えの最適化を行う事で、リアルタイムの描画品質を改善する事ができた。
ただ、描画タイミングや配置を考慮する為、管理が面倒になる。

とりあえず、上記のような構成になっているが流動的な部分が多い為、仕様は変更になると思う。

基本的な操作では、CH0、CH1、時間軸、メニューの4つで画面分割をして、領域別に個々の移動を行うようにした。
※タッチすると、各領域を分ける赤い線が描画される。

肝心のA/D変換では、RX65Nに内臓のA/D変換ユニット(12ビット)を2つ同時に使い、ギリギリの変換時間(2MSPS)で2チャンネルのサンプリングが可能な事を確認した。

アナログ入力は、「AIN000」、「AIN114」を使った、これは、ボードのCN10から、又、電源は、CN8から出ている3.3V、5V、GNDを引き出し、5ピンコネクタを付けた、このコネクタを外部アンプボードに接続する。

外部アンプボードで、ゲインの切り替えや、リミッタ、電圧シフトなどを行う予定でいる。

A/D入力の配線や、A/D変換関係電源の品質などにより、S/Nがイマイチな気もするが、簡易的な物なので、目をつぶる事にする。

ただ、トリガー条件等を複雑なものにした場合、2MSPS(500ns)は厳しいのではないかと思うが、その場合は、トリガー条件を監視するループを遅くするしかない。

また、サンプリングするメモリーには余裕があるので、かなり大きなスパンで、波形を取得出来そうだが、波形をリコールする場合に工夫しないと、使い勝手が悪いと思う。

 

基本的な実験が出来たものの、小さい信号レベルから、大きい信号レベルを扱うプリアンプをどのような構成にするか、考える必要がある。

どのような部品を使って、どのような構成にするのが良いのか、検討中で、なるべく入手しやすく、性能とコストのバランスを取る必要があるので、この辺りは、もう少し時間がかかりそうー。

※アナログ回路は、最近あまり扱っていないので、知見や、技術が古く、最近のオペアンプのトレンドなどにも疎い・・

とりあえず、AD9833、DSDの波形をキャプチャーしてみた。

今回はここまで、作りこむには、アナログ部が必要なので、次回に、アナログ回路などの検討を行う。

まだあった、こんな初歩的なバグ・・・

以下のコードは、「format.hpp」にある、二進表記関数だ。
しかし、致命的なバグがある・・

void out_bin_(int32_t v) {
    char* p = &buff_[sizeof(buff_) - 1];
    *p = 0;
    uint8_t n = 0;
    do {
        --p;
        *p = (v & 1) + '0';
        v >>= 1;
        ++n;
    } while(v != 0) ;
    out_str_(p, 0, n);
}

バグの存在に気がついただろうか?

引数「v」が「負」の場合、「v」の右シフトは、符号が消えない為、「0」判定が正常
に機能しない為無限ループとなり、配列を超えて不正なアクセスが起こる。
※通常、プログラムはクラッシュするだろう・・

void out_bin_(uint32_t v) {

それで、上記のように、関数の引数を「int32_t」から「uint32_t」にすれば解決する。

「format.hpp」は、以前に、かなり広範囲にチェックしていたが、テストケースが抜けて
いた。
十分チェックしたつもりで、頑丈だと思っていたので、こんな危険なバグがあった事はショ
ックだが、仕方ない、よりよいテストを行う事と、危険な橋を避ける必要があるようだ。

format による二進表現は現在まであまり使わなかったので、この大きなバグによる弊害は
無かったが、危ない瞬間だった・・

KIT-RX65N、レイ・トレーサー

ESP32やArduino系で走る「レイ・トレーシング」プログラムがある。

これを、RX65Nで、どのくらいのパフォーマンスなのか知りたくて、ポートしてみた。

元プログラムはC++で書かれたもので、良くできている。
※ソースコードは、ココから拝借したが、多少修正してある。
※ポーティングは極めて簡単だった。
RXマイコンは、浮動小数点の演算には定評があるので、少し期待していた。
結果は7.7秒と、まずまずの値ではある。
※元ソースを大きく修正するとベンチマークにならないものの、いくつかの修正を行った。
追記(2018年11月16日):
・浮動小数点関係の関数をRXマイコンの専用命令で置き換えた(FSQRTにより、約1/2の改善)
・実数の定義を明示的に「float」に修正(改善は少ない)
・オリジナルでは、進捗割合(%表示)と、実時間表示があるので、それに対応
・時間計測用に1/1000(1ms)のタイマーを追加
・RX65Nのキャッシュを有効にした(約10%の改善)
以上の改修で、「3.1秒」まで改善した。
※一番利いているのは、平方根命令の変更
※ピクセルを描画しない場合、「2.1秒」となる。(これはあまり意味が無い)
※速度比較の為、解像度320×240でレンダリングしている。

追記(2018年11月18日):
・比較していたサイトのベンチマークと、条件が異なっているとの指摘から、やり直した。
※大本のソースでは「raysPerPixel = 4」だったので、その条件で行っていたが、実際のスケッチでは、「raysPerPixel = 1」で行っており、1/4のレンダリング時間になるようだ。
・その結果、「837ms」(0.8秒)と、かなり高速にレンダリングする事が判った。
この値は、200MHz動作のARM(STM32F7)の0.62秒より遅いものの、動作周波数が120MHzのRX65Nなので、十分良い値のように思う。
謝辞:
この問題を指摘してくれ、FSQRTのマクロを提供してくれた「」さんに感謝したい、ありがとうございます。

コンパイラのバージョン:

rx-elf-gcc (GCC) 6.4.0
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

最適化は「O3」

-------

Raytracing with ESP32
上記リンクには、各マイコンのレンダリングにかかる時間が載っている。
ESP32(160MHz)は、13秒みたいなので、それより低速(120MHz)のRXマイコン
はそれなりに優秀なのだと思う。
ただ、「STM32F7 (200MHz) 0.62 秒」に比べると、かなり見劣りする結果かもしれない・・
※クロックを差し引いても、かなり遅い・・・

全ソースは、GitHub にある。

-------
macsbug, cbm80amiga さんありがとう~

追記:
gcc の最適化によってどのくらい違うのかを確認しておいた。
・古い gcc-4.9.4 も確認したかったが、constexpr 関係がコンパイルできずにエラーとなったので、
コンパイラは gcc-6.4.0 のままだ。
・ベンチマークは「目安だ」プログラムの性質により大きく異なる場合が多いし、ほんの少し条件が変
わるだけで、結果が逆転する場合もある。
・ルネサス純正Cコンパイラでも評価したいところだが、環境を揃える事が難しいので、実験をしてい
ないが、ネットの情報を見ると、浮動小数点が多い場合は、gcc の方が優秀な場合があるようだ・・
※コメント欄のリンクを参照

コンパイラ 最適化オプション レンダリング速度 実行サイズ
gcc-6.4.0 -O3 7.7 140568
gcc-6.4.0 -O2 7.7 128180
gcc-6.4.0 -Os 13.2 106584

・O2、O3 でほとんど差が出ないが、バイナリーサイズは多少異なっている。

追記:(2018年、11月3日)
RX65のハードウェアーマニュアルを眺めていたら、RX65にはROMキャッシュ制御ビットがあり、通常「Disable」になっている事が判った。
※「フラッシュメモリ」、「59.3.2 ROM キャッシュ許可レジスタ( ROMCE )」
このビットを有効にして、レンダリングをしてみた、そうすると「6.8秒」と10%くらい速度改善している。
一応、標準で「有効」にするように設定したが、「キャッシュ」有効により、正常動作しない「実装」もあるかもしれないので、少し評価を徹底したい・・
※「volatile」キーワードが適切にあれば問題無いと思う。
※他のプロジェクト(ファミコンエミュレーター、音楽再生など)も確認したが問題ない。

RX65N Envision Kit で実現するオーディオプレイヤー

I2Cの実装が、まともになったので、タッチパネルの操作を反映して、ファイラー
にも対応した。
これで、画面タッチでファイルを選択して、音楽を再生するガジェットがまともにな
った。

また、曲の再生では、一時停止や、次の曲などをタッチ操作対応にした。

本来ボタンを描画して、GUIで操作させるべきなのかもしれないが、それは、又後
で考える。
GUIに関しては、このボードの正規なサンプルプログラムとして既に公開されている
が(SEGGER)、GUIのような複雑な動作をCで書くのは、自分的にはありえない。
また、GUIの見た目や、操作の動作をエミュレーションする環境も必要な事を考える
と、少し複雑なGUIを作ろうとすると、この環境だけではかなり厳しいと思う。

以前に実装したリソースとして、Windows、OS-X で動作する Open-GL ベースの GUI
フレームワークがあるので、それをマイコン用にダイエットしてポートする予定でいる。
その時に、PC上で、見た目やリソースを作成するアプリなども作る予定でいる。
※その前にDRW2Dのエンジンを何とかしないと駄目なんだけど・・・
※DRW2Dエンジンの操作は、他のデバイスとは異なっていて、ライブラリーで提供
される、また、このエンジンに関係すると思われるレジスター群は、読み出し専用か、
書き込み専用レジスターとなっており、詳しい説明は省略され、ライブライーを使う事
を前提としている。

話がそれたが、オーディオプレイヤーのタッチ操作は以下のようになっている。
- 3点タッチ(離れた時)で、ファイラーが有効になる。
- 上下のドラッグで、ファイルフォーカス
- 右ドラッグでファイル選択(ディレクトリーの場合、そのディレクトリーへ移動)
- 左ドラッグで、一つ手前のディレクトリーへ移動
- 再生中、右へドラッグで次の曲
- 再生中、左へドラッグでリプレイ
- 再生中、2点タッチ(離れた時)で一時停止
- 再生中、3点タッチ(離れた時)で再生中断
- 再生中は、曲の再生が終了したら、次の曲を再生

現在、MP3、WAV形式のファイルをサポートしてあり、タグの表示にも対応してい
る。
タグは、ID3V2に対応しているが、V1タグは過去の物であり、あっても無視され
る。
タグは、曲名、タイトル、アーティスト、トラック、リリース日付、などの対応になっ
ている。
※WAV形式のタグも同じように対応している。

ジャケット画像をサポートする予定で、JPEGライブラリーをRX用にポートしたが、
一般的な方法では、メモリ不足で、デコードする事が出来ないので、こちらはそのうち
対応する事にする。(多分、一時メモリを使わず、直接フレームバッファに描画するよ
うにすれば可能なハズだが、リアルタイムにスケーリングもする必要があり、少しハー
ドルが高い)

オーディオ出力は、RX65NのDA0、DA1から出力する(量子化は12ビットだ
が)、無音は、3.3VとGNDの中間電圧、1.65Vとなっている。
バイパスコンデンサと抵抗だけで直にラインに繋げるのは心苦しいので、自分は以下の
回路を組んでいる。


※アンプの参考回路(以前にRL78で実装したWAVプレイヤーのもの)

量子化は12ビットだが、自分の耳では、そんなに悪い音に感じない。
※ブラインドテストで16ビットとの違いを聞き分けるのは難しいと思う。

現在DMACでD/Aに出力している部分をRSPIに変更して、多少改造すれば、外
部D/Aで鳴らせると思う。(いずれ、それも対応する予定でいる)
※SCIを使った簡易SPIだと、クロックを高速に出来ないので難しいかもしれない。
RX64Mだと、I2Sインターフェースがあるので、もっと簡単なのだが・・・
※自作音楽プレイヤーを前から作りたかったので、丁度良い。

GitHub AUDIO sample

RXマイコンSCIにおける簡易I2Cを実装する

SCIで簡易I2Cを実装している。

前回、ポーリングによる実装を行い、動作する事を確認した。
動作周波数が遅いCPUなら、それでも良いのだが、120MHzで動作するRXマイコン
だと、話が変わってくる。
さすがに、100KBPS程度の通信速度をポーリングで行うのは、マシンサイクルを無駄
に消費してしまうので、許容できないと思う。
そこで、割り込み処理にするのだが、RXマイコンで、I2Cを本格的に扱うのは今回が初
めてとも言えるので、あまりよく考えていなかった。
※IICAの専用インターフェースの実装も、いまだにポーリングのみの実装になっている。
これもそのうち手を入れないといけない・・・

SCIの簡易I2Cでは、STI(I2Cアクノリッジ待ち)で、送信終了割り込みを使う
のだが、「sci.hpp」に定義をしていない。
定義を追加するのに、しばし問題があった、送信終了割り込みは、グループ割り込みとなっ
ていて、SCIのチャネルによってグループが異なっていたり、RX64MとRX65Nで
異なっていたりと、場合別けをしなければならない・・

グループベクターは、icu.hpp に定義をしているが、グループが異なると、全く別の型とな
るので、そのままでは、定義が出来ない。
そこで、SCI の基底クラスを継承させ、チャネルでグループが異なる場合を別けて実装する
ように修正した。

SCI定義:

typedef scig_t<0x0008A000, peripheral::SCI0, ICU::VECTOR::TXI0, ICU::VECTOR::RXI0, ICU::VECTOR_BL0::TEI0> SCI0;

 typedef scig2_t<0x0008A100, peripheral::SCI8, ICU::VECTOR::TXI8, ICU::VECTOR::RXI8, ICU::VECTOR_BL1::TEI8> SCI8;

※SCI1 の TE0 はグループ BL0 だが、SCI8 はグループ BL1 になっている。

クラス定義:

template <uint32_t base, peripheral t, ICU::VECTOR txv, ICU::VECTOR rxv, ICU::VECTOR_BL0 tev> struct scig_t : sci_t {

};

template <uint32_t base, peripheral per, ICU::VECTOR txv, ICU::VECTOR rxv, ICU::VECTOR_BL1 tev> struct scig2_t : sci_t {

};

※基底クラス sci_t は、そのままに、グループ別に異なったクラスを定義した。

また、SCI を使った簡易 I2C は、SCI の制御と根本的に異なるので、純粋な SCI の制御と
クラスを別け、新規に「sci_i2c_io.hpp」とした。
※RXマイコンのソースコードは、第三者を交えたレビューを行っていないので、意外と、
基本的な部分に問題がある事が多い・・・
※多分、コードレビューを行うと、かなり厳しく突っ込みを入れられる部分があるものと思
う、コードレビューを経験した人なら解ると思うが、最初は、厳しい駄目だしをされると、
不快に思ってしまう、だが、人間「慣れ」るもので、回数を重ねると、ありがたく思うよう
になり、真摯に助言を受け入れる度量と分析力が育つ。

よく、趣味でマイコンをやっている人で、コードが汚いので公開をしない人がいるが、他の
人に添削してもらえる可能性があるのなら、是非自分のコードを見てもらい、正しい道に修
正してもらった方が良い。(くれぐれも、指摘された事を自分に対する「否定」と思わない
事、冷静に何が最善なのかを考える事)
プロジェクトだと、他人に駄目だしされる事を不快に思う人種はいて、リーダーの手腕が重
要となるのだが・・

ポーリング動作で、全体の見通しがついていたので、割り込み動作に関しては、それなりに
順調に実装できた。
FT5206、I2Cの定義は以下のように行う。
※FT5206のリセットを制御するので、ポートを定義。

// FT5206, SCI6 簡易 I2C 定義
typedef device::PORT<device::PORT0, device::bitpos::B7> FT5206_RESET;
typedef utils::fixed_fifo<uint8_t, 64> RECV6_BUFF;
typedef utils::fixed_fifo<uint8_t, 64> SEND6_BUFF;
typedef device::sci_i2c_io<device::SCI6, RECV6_BUFF, SEND6_BUFF, device::port_map::option::FIRST_I2C> FT5206_I2C;
FT5206_I2C  ft5206_i2c_;
typedef chip::FT5206<FT5206_I2C> FT5206;

割り込みで処理するのは簡単で、以下のように、I2Cのインスタンスを起動する時に、割
り込みレベルを設定するだけで良い。(ポーリングの場合は「0」を設定)

{  // FT5206 touch screen controller
    FT5206::reset<FT5206_RESET>();
    uint8_t intr_lvl = 1;
    if(!ft5206_i2c_.start(FT5206_I2C::SPEED::STANDARD, intr_lvl)) {
        utils::format("FT5206 I2C Start Fail...\n");
    }
    if(!ft5206_.start()) {
        utils::format("FT5206 Start Fail...\n");
    }
}

割り込みでは、データ転送が非同期になるので、同期を行うメソッドを追加し、非同期にデ
ータが来ても誤動作しないように、FT5206の実装も見直した。

sci_i2c_io.hpp
FT5206.hpp

KIT-RX65N、AUDIOプレイヤー更新

ファイラーがそれなりに動くようになったので、オーディオプレイヤーを更新、
PCMファイルのサポートも追加した。

操作は、ファミコンパッドで行うのだが、もうそろそろ、タッチスクリーンを有効
にしないと駄目な感じだ・・・
タッチスクリーンで使用しているICはFT5206という物らしい。

I2Cは、専用ポートしか考えていなかったので、専用のドライバーが既に動作し
ているが、FT5206はSCI6に接続されている。
なので、SCIの簡易I2Cを実装するしかない。
一応、ハードウェアーマニュアルを見て、実装してみたけど動作しない・・・
※ルネサス提供のドライバーは設定が面倒で、使いたくないし、読みにくいので参
考にするのも億劫だ。

割り込みでやるのは、設定が複雑になり、ハードルが高いと思ったので、とりあえ
ずポーリングでやってみたが、どうにも動作しない。
波形を見たいところだが、このボードでは、プローブを接続するのが難しいので、
他のボードで確認してみる事にしようか・・・
と思ったけど、ソフトI2Cで動作の確認だけしてみたー
動いたー(当たり前だけど)
※最初、動かないなぁーって思ったけど、とあるレジスターの応答が自分が思って
たのと違うだけで、ちゃんと動作してた(最近、こんな、早とちり的な事が多い)

ソフトI2Cで動作し、FT5206のマネージメントもそれなりになったので、
SCIの簡易I2Cをもう一度詳細に確認。
どうも、ポーリングの場合、受信動作は問題無いが、送信動作では、送信レジスタ
ーがバッファされているので、フラグを見る場合は、多少の工夫が必要な事が判っ
た。

while(SCI::SSR.TDRE() == 0) {
    sleep_();
}
while(SCI::SSR.TEND() == 0) {
    sleep_();
}

それを修正して動作するのかと思ったが、受信動作がどうも、うまくない・・・
よくよくレジスターのダンプを見ると、1バイトずれて読めてるような・・・
ルネサスのハードウェアーマニュアルの説明に沿って実装したものの、どうやら
本家の説明に間違いがあるようだー、SCIをI2Cとして使う場合、送信と受
信は連結されている、I2Cのスレーブアドレスを送信後に、受信レジスタを空
読みしないと、スレーブアドレスが残っている。

SCI::TDR = (adr << 1) | 1;  // R/W = 1 (read)
while(SCI::SSR.TDRE() == 0) {
    sleep_();
}
while(SCI::SSR.TEND() == 0) {
    sleep_();
}
volatile uint8_t tmp = SCI::RDR();  // ダミーリード

if(SCI::SISR.IICACKR() != 0) {
    utils::format("I2C Recv: Ack fail...\n");
    i2c_stop_();
    return false;
}

空読みするコードを追加したら正しく受信動作した、ヤレヤレ。

I2Cのドライバーは、全体的に考え直す必要のあるデバイスだ(SPIもそうだ
が・・)特に120MHzで動作しているプロセッサの場合は、ポーリングでデー
タを取得するような低速ドライバーは問題外と思う。(もったいない)
ただ、デバイスによって、通信の手順が色々なので、単純に割り込みにしただけで
は駄目で、柔軟性のある機能を提供する必要がある。
また、手順が面倒で、新規のI2Cデバイスドライバーを実装する手間が大変だと、
それはそれで、痛いので、よくよく考えて、全体を設計しておく必要がある。

とりあえず、受信と送信データはバッファリングして、それぞれの終了で設定した
コールバックを呼び出すようにすれば良さそうだ。

割り込み対応のI2Cドライバーは、次の機会に紹介する。

ソフトI2Cドライバー
SCI/I2C ドライバー
タッチスクリーンテストサンプル

-----
C++ に関するコラム:
C++ で C のキャストを使う人が多い・・・
以前に理由を聞いたら「冗長」だからーとか言ってた人がいるが、機能が違うので、
そんな理由で、使っては駄目だ。
前提として、キャストは、なるべく使わないように設計する必要がある。
「const」を取り除くような方法もいけない。
C 言語ベースの人はこの辺りを全く気にしない人が多いので困る。