RXマイコン、デジタルストレージオシロスコープ(その2)

シュミレーター:

マイコン内蔵A/Dコンバーター

RXマイコンのメリットとして、内蔵A/DコンバーターのSNが高い事があると思う。
※最近の他社マイコン、ARM、ESP32、PIC32などの現実はあまり詳しく無い。
※かなり昔に比較した時に、確かに優位性があった。

RXマイコン内蔵、A/Dコンバーターも、豊富な変換モード、サンプルホールド、細かいチャネル設定など、非常に柔軟な設定が可能になっている。
広範囲の事柄が関係するので、理解して応用するのは大変だ・・
また、アナログ回路も絡むので、その部分で、デジタル回路には無い難しさがある。


RX72N の場合:

項目 内容
ユニット数 2ユニット(S12AD, S12AD1)
分解能 12ビット
変換時間 1チャネル当たり(0.48μs)(12ビット変換モード)
1チャネル当たり (0.45μs)(10ビット変換モード)
1チャネル当たり (0.42μs)(8ビット変換モード)
備考 (A/D変換クロック ADCLK = 60MHz動作時)

12 ビットの変換時、チャネル辺り、最大 0.48 マイクロ秒で変換出来る。
RX65N Envision Kit、RX72N Envision Kit では、同時に2チャネルが変換可能なアナログチャネルが、pmod コネクタなどにアサイン可能となっている。
※多少残念なのは、電圧リファレンス端子が、内蔵電源に接続されている点がある・・

2M サンプルで同時 2 チャンネルなら、まぁ仕様としては何とか実用性があると思える。

以上の仕様で、簡易なデジタルストレージオシロが作れると思っていた。
そこで、RX65N Envision Kit で簡単な実験を行い、実際にキャプチャーしたデータをLCDに表示して基本的な実験は成功していた。
ただ、RX65N ではCPUのクロックが 120MHz なので、2M サンプルだと、複雑なトリガー条件を作成すると、割り込み処理がオーバーフローするかもしれない・・
DMA 転送にすれば良いと思うかもしれないが、トリガー条件により、電圧の変化を検出する必要があるので、ソフトの介入がどうしても必要となる。

また、アナログのフロントエンド部のハードウェアーとして、どのようなものが適当なのか、検討していた。

最近、PC 上で API レベルシュミレーターを実装した事で、ようやくプロジェクトが動き始めた。

ソフトが重要

データをキャプチャーするのは簡単だが、問題は GUI 関係のソフトウェアーだと思う。

デジタルストレージオシロを実用的に使うには、ソフトの機能、基本的なデザイン(操作の考え方)が重要で、複雑で、大きなボリュームになる。

以前に、工場で使う検査装置として、デジタルストレージオシロの機能が入った物を作った事がある。
その時は、波形のキャプチャーだけ、外部のFPGAとマイコンを組み合わせた機器で行い、波形の表示などは、PCで行った。
その時は PC 上のソフトなので、効率やメモリ、速度などは気にする必要が無く、自由に作れた。
それでも、非常に面倒な作業で、時間や電圧の変換、操作性など、色々苦労した事が蘇る・・

RX65N Envision Kit や RX72N Envision Kit の場合、液晶の解像度も小さいので、操作性も考慮する必要がある。
また、タッチパネルの操作が基本なので、そのようなデザインを考える必要がある。
加えて、外部に接続するハードウェアーもあまり大掛かりにならないようにする事も考慮する必要がある。
最近では、数万円出せば、高性能なデジタルストレージオシロが買えるので、作っても、あまり利用頻度は大きいとは思えないが、ソフトの構成や、基本的な構成などは、一つのアプリケーションとして参考になると思う。
教材として考えれば、それなりに利用価値があるものと思って進めている。

また、オープンソースとする事で、誰でも、「欲しい機能」を追加する事も出来る。
※特殊なトリガー条件などを実装する事が出来ると思う。


PC 上の、API レベルシュミレーションは便利で、暇を見つけてコツコツ進めている。
GUI Widget フレームワークも完成度と機能が充実しつつあるので、GUI などの面でも実装が楽になってきた。

PC では、A/D コンバーターの部分をシュミレーションにより作成して、それを A/D の変換値12ビット整数データとして与え、ソフトを実装している。
キリの良いとこで、実機でも動かして確認する。

A/D の疑似波形を生成

疑似波形としては、サイン波、三角波、矩形波などをサポートして実験を行っている。
※実機では、ノイズなども考慮する必要があるので、最終段階では、ソフトでノイズを混ぜる事も考える必要がある。

        //-----------------------------------------------------------------//
        /*!
            @brief  SIN/COS 波形を生成
            @param[in]  freq    周波数
            @param[in]  ppv     電圧 (peak to peak)
            @param[in]  num     生成数
            @param[in]  ch0     CH0 波形型
            @param[in]  ch1     CH1 波形型
        */
        //-----------------------------------------------------------------//
        void make_wave(uint32_t freq, float ppv, uint32_t num, PWAVE_TYPE ch0, PWAVE_TYPE ch1)
        noexcept
        {
            static int32_t count = 0;
            auto smpl = get_samplerate();
            auto& task = at_cap_task();
            auto unit = static_cast<float>(smpl) / static_cast<float>(freq);
            auto vgain0 = voltage_to_value(0, ppv);
            auto vgain1 = voltage_to_value(1, ppv);
            for(uint32_t i = 0; i < num; ++i) {
                auto a = static_cast<float>(count % static_cast<int32_t>(unit)) / unit;
                task.adv_.x = -pwave_(ch0, a, vgain0);
                task.adv_.y = -pwave_(ch1, a, vgain1);
                if(task.adv_.x < -CAP_OFS) task.adv_.x = -CAP_OFS;
                else if(task.adv_.x > (CAP_OFS-1)) task.adv_.x = CAP_OFS-1;
                if(task.adv_.y < -CAP_OFS) task.adv_.y = -CAP_OFS;
                else if(task.adv_.y > (CAP_OFS-1)) task.adv_.y = CAP_OFS-1;
                task();
                ++count;
                if(count >= CAP_NUM) {
                    count = 0;
                }
            }
        }

サイン波や、三角波、矩形波は、以下のような関数で生成する。

        static float ftri_(float in) noexcept
        {
            auto a = fmod(in, 1.0f);
            if(a >= 0.5f) {
                a = 1.0f - a;
            }
            a -= 0.25f;
            return a * 4.0f;
        }

        static float fsqu_(float in) noexcept
        {
            auto a = fmod(in, 1.0f);
            if(a < 0.5f) {
                return 1.0f;
            } else {
                return -1.0f;
            }
        }

        static float fsquf_(float in, float& back) noexcept
        {
            auto a = fsqu_(in);
            auto d = (a - back) * 0.707f;
            auto ans = back + d;
            back = a; 
            return ans;
        }

        static int16_t pwave_(PWAVE_TYPE pw, float phase, float gain) noexcept
        {
            static float fsqu_s_ = 0;
            static float fsqu_c_ = 0;
            int16_t ret = 0;
            switch(pw) {
            case PWAVE_TYPE::SIN:
                ret = static_cast<int16_t>(sinf(phase * vtx::radian_f_) * gain);
                break;
            case PWAVE_TYPE::COS:
                ret = static_cast<int16_t>(cosf(phase * vtx::radian_f_) * gain);
                break;
            case PWAVE_TYPE::TRI_C:
                ret = static_cast<int16_t>(ftri_(phase) * gain);
                break;
            case PWAVE_TYPE::TRI_S:
                ret = static_cast<int16_t>(ftri_(phase + 0.25f) * gain);
                break;
            case PWAVE_TYPE::SQU_C:
                ret = static_cast<int16_t>(fsqu_(phase) * gain);
                break;
            case PWAVE_TYPE::SQU_S:
                ret = static_cast<int16_t>(fsqu_(phase + 0.25f) * gain);
                break;
            case PWAVE_TYPE::FSQU_C:
                ret = static_cast<int16_t>(fsquf_(phase, fsqu_c_) * gain);
                break;
            case PWAVE_TYPE::FSQU_S:
                ret = static_cast<int16_t>(fsquf_(phase + 0.25f, fsqu_s_) * gain);
                break;
            default:
                break;
            }
            return ret;
        }

現在の実装は「capture.hpp」に含めている。
※これは、見直すべきかもしれないが・・

SIN、COS は、同じ物で、位相が 45度異なるだけとも言える。


A/D コンバーターのプリアンプ

サンプリングが 2MHz だと、実用的に扱える周期は 1/10 の 200KHz くらいだろうか・・
その場合でも、1 周期、10 サンプルしか無い・・

オシロスコープとしては、かなり低機能なものなので、あまり高性能なフロントエンドを考える必要は無いものの、経験が無いので、どのような構成にすべきなのか良く判らない。
一番良いのは、実際のオシロスコープを分解して、リバースエンジニアリングを行うのが手っ取り速い気がする・・
最初から最高の物を目指すと、完成しないので、改修していけばいいだろうと思う。

1X、プローブを接続する場合、入力インピーダンスは 1000K オーム程?
※10X プローブでは、9000K オームの入力抵抗が直列に入っているものと思う。

最初、1X、10X プローブを両方使えるようにしようと思ったが、回路が大掛かりになりそうなので、1X のみにしようと思う。
※10Xでは、既に1/10になるので、分圧は必要無いが、それを直でオペアンプに入れると、1X を接続した場合に、過電圧からの保護が難しい。
※アナログスイッチで、過電圧保護機能が充実しているデバイスを使えば良さそうだが、値段が高い。

何となく、機械式リレーを使うのが嫌なので、フォトモスリレーを使って、DC、AC を切り替えている。

オペアンプは、オーディオ用の物だが、以前にオーディオ用 DAC に使った部品が沢山余っている。

この回路には、過大な電圧を制限して、A/D の入力を制限する回路が含まれていない・・


まだ、実際の回路で試していないので、この回路では、駄目かもしれないが・・

レンジ切り替えや、AC、DC 切り替えは、機械式スイッチにすれば良いのかもしれないが、スマートでは無くなる気がする・・・

全体の機能

現在シュミレーターを使い、以下の機能を大体実装した。

チャネル電圧切り替え:

  • AC, GND, DC, OFF
  • 10V,5V,2V,1V,500mV,200mV,100mV,50mV,10mV

サンプリング切り替え:

  • 1us,2us,5us
  • 10us,20us,50us
  • 100us,200us,500us
  • 1ms,2ms,5ms
  • 10ms,20ms,50ms
  • 100ms,200ms,500ms

トリガー切り替え:

  • None,One,Run,CH0-Pos,CH1-Pos,CH0-Neg,CH1-Neg

計測:

  • Time Sub,CH0 Sub,CH1 Sub,Time Abs,CH0 Abs,CH1 Abs

まとめ

シュミレーターのおかげで、ソフト部はかなり進んだが、やはりハード部は、進捗が遅い・・・

現状のコードは、シュミレーター「glfw_app/rx_gui_emu」から、定期的に「RX/DSOS_sample」にマージしている。