「電子工作な日々」カテゴリーアーカイブ

電子工作に関連するお話など・・

RX65N Envision Kit オーディオプレイヤー(その2)

picojpeg デコーダーのポーティングが出来たので、ID3 タグ内のジャケット画像などを格納するタグ「APIC、PIC」内データ(JPEG)をデコードして画像表示は出来たのだが、画面サイズにフィットさせるのは後回しになっていた。

まず、仕組みを考える・・

画面デザインから、画面の右端に272×272ピクセル(液晶の縦ピクセル)で表示させるのが良さそうだ。
ただ、タグ内の画像サイズは様々なので、適切にスケーリングする必要がある。
スケーリングの方法も重要だ、RX65Nの内蔵メモリはあまり大きく無いので、画像のような大きなデータを扱う場合、出来れば、一時メモリを使用しない(利用できない)方が良い。

それらを踏まえて、画像のデコーダーテンプレートは、直接画像の操作を扱うAPIを叩かないで、中間に「スケーリング」を行うオブジェクト(クラス)を入れる事にした、こうしておけば、レンダリングを行う対象がどんな場合にも対応可能となる。

    typedef graphics::render<uint16_t, LCD_X, LCD_Y, AFONT, KFONT> RENDER;
    RENDER      render_(reinterpret_cast<uint16_t*>(0x00000000), kfont_);
    typedef img::scaling<RENDER> PLOT;
    PLOT        plot_(render_);
    typedef img::picojpeg_in<PLOT> JPEG_IN;
    JPEG_IN     jpeg_(plot_);

C++ では、「ファンクタ」と呼ばれる方法(通常「()」オペレーターを使う)で、クラスに対しての操作を一般化できる。

//-----------------------------------------------------------------//
/*!
    @brief  描画ファンクタ
    @param[in]  x   X 座標
    @param[in]  y   Y 座標
    @param[in]  r   R カラー
    @param[in]  g   G カラー
    @param[in]  b   B カラー
 */
//-----------------------------------------------------------------//
void operator() (int16_t x, int16_t y, uint8_t r, uint8_t g, uint8_t b) noexcept
{
・・・
}
まず、plot_ クラスに対して、スケールを設定する。
※スケールは、「整数比」で設定する。
※画像全体を収める為、縦か横で長い方を基準にする。

    auto n = std::max(ifo.width, ifo.height);
    plot_.set_scale(272, n);

jpeg_ クラス内から、plot_ クラスに対して、以下のように普通に描画すれば、設定された拡大、縮小比で描画される。
※ワークメモリを使わないのと引き換えに、描画する領域は、あらかじめ「0」クリアしておく必要がある。

    plot_(x, y, r, g, b);

また、描画時にオフセットを与えられるようにしてある。

    plot_.set_offset(vtx::spos(480 - 272, 0));

最初、縮小をさせる場合に、エイリアシングを考慮しない簡易的な実装を行ってみたのだが、当然のように描画品質はあまり良くない。
※ジャギジャギしてる・・

そこで、ピクセルの加重割合を考えずに平均化してみる事にした。
・フレームバッファはRGB565となっている。
・フレームバッファを読み出して、「0x0000」なら、初期に書き込むピクセルとして、そのまま書き込む。
・もし、新規に書き込むピクセルが「0x0000」なら、「0x0001」に直して書き込む。
・フレームバッファを読み出して、「0x0000」以外なら、既に書き込まれている値なので、新規に書き込む値との平均(1/2)を求めて書き込む。

この実装は、厳密な意味では正確ではないものの、「ジャギー」感が減り、かなりマシに見える。
とりあえず、これで良しとした、ピクセルサイズによる厳密な加重平均や、フィルタ的要素は、次の課題とする。

※現状では、「拡大」する場合は、簡易的なものになっていて「ジャギー」が目立つ。

RXマイコンの最適化に対する知見

RX65N Envision Kit が発売されてもうじき1年がたとうとしているが、アプリケーションを作っている人は非常に少ないように感じる・・
サーチエンジンで探しても、あまり有益な情報を探せない、現在、秋月では売り切れとなっており、それなりに購入している人はいると思うのだが、何か面白いアプリを作って発表し、ソースコードを公開している人はほとんどいないように感じる。
何がハードルになっているのか?・・・
それに比べて、M5Stack、ESP32、ARMなど海外のマイクロコントローラーは、非常に沢山あるようなのだが・・・
※何かあったら、リンクを教えて下さい。

さて、以前に「Arduino 用レイトレーサー」を実装している人がいて、コードが公開されていたので、RX65Nにポートしてみた。
その過程で、ルネサス公式のフォーラムにそのリンクを張った。
しばらくして、色々な指摘を受けた。
※自分のブログにも同じような指摘が返信されていたが、検証の材料が無く、ほぼスルー状態だった。
※自分の間違った知見もある・・

ルネサスのフォーラムでは、具体的な実験を行い、検証を行ってくれた。(感謝)
※単に思いつきで返信するだけじゃなく、このような、有益な内容のある濃い情報が重要だ、言うだけなら誰でもできる、何か発信するなら、せめて、自分で「手」を動かして、検証可能な情報を返して欲しいものだ。

そこで、指摘された事についてまとめておきたい。

  • gcc では RXv2 コアの最適化が不十分
  • double 定数を使っているのが余計
  • std::sqrt はdoubleの関数なので、sqrtf を使った方が良い
  • RXv2には、fsqrt 専用命令が用意されている
  • ESP32 などで公開されているベンチマークは、レイのサンプリングは「1」で行っているがRXでは、「4」で行っていて条件が異なる

gcc の RXv2 の最適化に関しては、自分の考え方が少しある。
まず、gcc のオフィシャルなソースコードを使う事を前提としているので、ルネサス社が、gcc に最適化のコードをコミットするまで待つしかない・・
現在ルネサス社は、独自にGNUツールを公開しているが、最新版は4.8系となっており、C++ の最新のコードをコンパイルする事が出来ない。
※ルネサス社が提供するGNUツールでコンパイルした場合、最適化「-O3」で、最大7~15%くらい良いコードが出る場合があるようだが、これは今後検証してみたい。
※オフィシャルな gcc では、「-mcpu=rx64m」など専用デバイスのオプションは無いが、標準で、RX600 系のコード出力となっている。

double の定数や、double 専用 API は、指摘を受ける前に既に、変更済みで、それで、ほんの少し高速になっていた。

標準の数学ライブラリでは、平方根を求める「sqrt、sqrtf」はニュートン法を使っており、RXv2 に備わっている、fsqrt 専用命令とは微妙に計算結果が異なっているものと思うので、標準では使わないのだと思っていた。
だが、この命令に切り替えた場合、倍の速度でレンダリングするようなので、この知見を素直に受け入れた。

#if defined(SIG_RX64M) || defined(SIG_RX71M) || defined(SIG_RX65N) || defined(SIG_RX24T)
static inline float sqrtf_(float x)
{
    __asm __volatile(
        "fsqrt %0, %0\n" \
        : "+r"(x) \
    );
    return x;
}
static inline int ceilf_(float x)
{
    int y;
    __asm __volatile(
        "pushc fpsw\n"
        "mvtc #0b10, fpsw\n"
        "round %0, %0\n"
        "popc fpsw\n"
        : "=r"(y) \
        : "r"(x) \
    );
    return y;
}
#else
static inline float sqrtf_(float x) { return sqrtf(x); }
static inline int ceilf_(float x) { return ceilf(x); }
#endif

レイのサンプリング数に関しては、全くノーマークだった、元ソースでは、標準「4」だったので、素直に「4」で行っていたが、ベンチマークを行っている Arduino のスケッチでは、「1」にして API を呼んでおり(卑怯なのではw)、大体4倍のレンダリング時間となっていた。
※「STM32F7 200MHz」では、0.62秒と非常に高速なので、この違いは、何なのか、疑問に思っていた・・・

以上の知見を受け、RX65N Envision Kit でレンダリングすると・・・

約0.83秒と、STM32F7 200MHz と比較しても、十分戦闘力のある値となったw
元は7.7秒とかだったので、素晴らしいパフォーマンスアップとなった。
※ESP32(160MHz)は、13秒みたいだが、これは、他にバリアとなる事象があるように思う
※このような有益な情報を提供してくれた「fujita nozomu」氏に感謝したい!

全ソースコードは以前から公開しているが、コンパイル環境を作り、フレームワークなどをチェックアウトする必要があるので、実行バイナリーも公開している。
・裏にあるスイッチを押す毎に、フルスクリーン(480×272)、レイのサンプリングを変更してのレンダリングを行う。

「raytracer.hpp」は、かなり柔軟性があり、ベンチマークには最適なので、PIC32や、他のマイコンでも十分実行できる、別のマイコンで試した人がいれば、是非情報を公開して欲しい~

PicoJPEG デコーダーを使う

以前に JPEG デコーダー「libjpeg」ライブラリーをポートしようと思ったものの、記憶割り当て不足で、実用的に使えなかった。
※libjpeg では、内部で画像サイズのRGBメモリを割り当てる為、当然のようにメモリ不足になってしまう。
ルネサスでは、オリジナルの JPEG(RXv2 DSP 命令で最適化した)ライブラリを公開しているようだが、肝心のコア部分ソースコードは未公開で、バイナリ化されており、gcc からは使う事が出来ないようだった。
そこで、libjepg を改修して、記憶割り当てを何とかしようと思ったが、既に、JPEGのデコードを少メモリで実現する「PicoJPEG」がある事に気が付いた。
なので、このライブラリを使わせてもらう事にした。

ポートは簡単で、「picojpeg.c」をコンパイルしてリンクするだけだ。
ただ、コンパイルエラーが出たので、多少修正した。
※ sequence-point のエラー
※このエラーは、式の評価順番に関係するもので、オリジナルの書き方は、C 言語の規約では「未定義」となるもののようだ。
https://www.jpcert.or.jp/sc-rules/c-exp30-c.htm

    *pDstR++ = addAndClamp(pDstR[0], crR);

以下のように書き換えた。

    *pDstR = addAndClamp(pDstR[0], crR);
    pDstR++;

サンプルを参考に C++ から簡潔に呼べるようにラッパーを実装した。
ラッパーは、テンプレートライブラリで、描画クラスを「参照」で与える仕様とした。
※BMP のデコーダーと同等の仕様。

ただ、PicoJPEG デコーダーは、「プログレッシブ」でセーブされたフォーマットをサポートしておらず「ベースライン」のみとなっている。
※「プログレッシブ」は、ブラウザなどで、最初荒く、後、詳細に表示するもので、通信速度が遅かった昔の名残で、最近のネット速度を考えると、ほぼ使う必要が無いモードと言える。
※プログレッシブ・エラーが出た場合には、フォームを確認して、「ベースライン」でセーブする必要がある。

480x272、ベースライン(最適)のJPEG表示サンプル
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  PicoJPEG デコーダー
    @param[in]  RENDER  描画ファンクタ
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <class RENDER>
class picojpeg_in {
.....
};

※描画を行うクラスを「RENDER」で渡す。
※「RENDER」クラスでは、「plot」APIを通して描画を行う。
picojpeg_in.hpp

今回、RX65N Envision Kit (AUDIO プレイヤー)でデコードと描画のテストを行った。
https://github.com/hirakuni45/RX/tree/master/RTK5_AUDIO_sample
※ターミナルからの「jpeg」コマンドでJPEGファイルを描画できる。

また、ID3 に含まれるPIC、APIC タグで埋め込まれたJPEG画像をデコードして表示させるようにした、ただ、適切なスケーリングをしていない為適切な大きさで表示されない。
※これは、今後対応する事にする。
又、PNG 画像もサポートされない・・

組み込みマイコンで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出力も必要だろうか・・(シリアル接続で、コマンドで出来れば良いのだが、このインバーターには機能が無いようだ・・・)

USBシリアル変換器を作る

お手製のUSBシリアル変換機は、ケースに入れた物が2個ある。
・FTDIのFT231XS
・Silicon LabsのCP2102
以前は、単品でデバイスを買って取り付けやすい事から、FT231XSを推していたのだが、RX64MマイコンとPC間でシリアル通信を頻繁に行うアプリを実験した時に、他のUSBシリアル(CP2102)と比較して能力が劣る事を体感した。
高いボーレートで、データが欠落するまでは調べていないが、CP2102と比較して明らかに転送能力が劣るようだ。(少し大きな容量を送る場合に分割されてしまい、転送時間が余分にかかる、これは、データを欠損していない事から、ドライバーの問題と思える)

少し調べると、「FT231XS」は、「FT232RL」より安い価格帯の製品なので、「FT232RL」より劣るように作ってあるようだった。
※そうしないと、より価格が高いデバイスの存在意義が無くなってしまう、もちろんFT232RLは、シリアル通信以外にも、色々フォーマットをサポートしており、その意味では差別化されているものの、USBシリアル以外で使う事は少ないと思うので、メーカーとして差別化をする必要があるのだと思える。

アマゾンでは中華製のUSBシリアルが沢山売られていて、その中でも、値段と機能が優れているCP2102のモジュールを数個買ったのだが、FT231XS単品より安価で、モジュールになっているのがありがたく、性能も良い、下記のモジュールは、全ての端子が配線されており、インジケーターLEDも付いている。

工作の実験では、電源を、3.3V、5V、の切り替えがほしい。
また、ケースに入れて、見栄えや、機能性、安全性を良くしたいなどの理由で、適当なケースに入るようにした。
http://www.takachi-el.co.jp/data/pdf/2016-01-057.pdf
※タカチ製CS75N
CP2102モジュールは、USBのコネクターが直に付いているので、外して、モジュール基板のみにして内部基板に組み込み、マイクロUSBコネクタに直す。
※最初から、デバイスだけ買った方が良さそうにも思うが、デバイスだけ買うよりモジュールを買って加工する方が安くて、扱いやすいし(QFPパッケージは工作しずらい)、既にバイパスコンデンサなど周辺回路が整っている。

AVRなどでは、5Vが主流だが、RXマイコンなどでは3.3Vが主電源になる為、USBシリアルから電源を供給出来た方が、何かと利便性が高い、デバイスにも3.3Vレギュレーターは内臓されているが、これは内部デバイス用で、外部で利用する用途ではないので、別途レギュレーターを載せる。(販売されている多くのUSBシリアル変換モジュールでは、3.3Vのレギュレーターが内臓された物がほぼ無い)
電源は、スイッチを付けて、切り替えを行う、ただ、3.3Vのマイコンに5Vを印加する間違いを起こす事はあるので、その点は十分注意する必要がある、以前に電源切り替えのスイッチにトグルスイッチを使ったが、物を上に載せた場合などに切り替わる場合があるので、スライドスイッチにした。

穴の加工が、面倒だが、プラスチックなので、ヤスリやドリルで忍耐さえあれば何とかなる、モジュールに載っているLEDの光を確認出来るように、LEDの真上に穴をあけてある、この穴は、グルーガンで塞ぐと、埃も入らないし、半透明で良い感じになる。

懸念事項として、シリアル出力(TXD、/RTS)からの電流問題がある。
通常、電源は、USBシリアルから供給するので、その場合は問題無いのだが、マイコン側で電源が用意してあり、信号のみ接続した場合に、マイコン側の電源が入って無い場合、USBシリアルの電源が入っていない場合の主に二りで、シリアル出力線を通して電源電流が流れてしまう事があった。

以前に、RX64Mでインサーネット接続用プログラムを組んでいた時に、シリアル接続をしているとネットワークが接続出来ない事があり、原因を調べたところ、USBシリアル出力からの電圧が、RXマイコンに流れて、それがインサーネットのPHYデバイスにも流れていた為、リセット時のbootstarp機能が正しく働かなくて、誤動作していた。
USBシリアルのTXDとRXマイコンのRXDに1K程度の抵抗を入れて、とりあえず回避したが本来はアイソレーションするべきと思う。
基板を起こす際には、それらを含めた回路を組むと思うが、今回は割愛した。

※一応、マジックで書いた上から、プリントした紙をはった。

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