RX72N Envision Kit での開発(その3)CMT 編

コンペアマッチタイマ・テンプレートの使い方(cmt_mgr.hpp)

前回の SCI 編で既に使っていますが、CMT を使う場合を、詳しく説明したいと思います。

CMT は一定間隔でイベントを発生するタイマーで、割り込みを発生させる事が出来ます。
通常、マスタークロックは、PCLKB が使われます。
PCLKB が 60MHz の場合、1.7Hz~7.5MHz 程度の時間間隔をプログラム出来ます。
※分周比は制限があり、自由な周波数を設定出来るわけではありません。

このテンプレートクラス(common/cmt_mgr.hpp)も、RX マイコンの C++ フレームワークの中では初期の段階から改修されてきているクラスです。
通常行いたい事を、最低限の手順で出来るように工夫してあります。

FreeRTOS のようなマルチタスクOSを使わない場合で、平行処理的な制御を扱う場合には欠かせない物です。
比較的簡単に平行処理的な概念を利用する事が出来るので、シンプルなアプリケーションには、逆に使いやすいものです。

RX マイコンでは、CMT は標準装備なので、どんな RX マイコンでも利用可能です。

通常 CMT0 から CMT3 までの4チャネルを使う事が出来ます。
※ICU の解説では、CMT0 は、OS 用で予約してあるのですが、OS(FreeRTOS と思われる)を使わない場合、どれを使っても問題無いと思えます。

FreeRTOS では、以下のように初期化しています。

    extern void vTickISR(void);
    extern void vSoftwareInterruptISR(void);

    void vApplicationSetupTimerInterrupt(void)
    {
        uint8_t intr = configKERNEL_INTERRUPT_PRIORITY;
        cmt_.start(configTICK_RATE_HZ, intr, vTickISR);

        device::icu_mgr::set_task(device::ICU::VECTOR::SWINT, vSoftwareInterruptISR);
        device::icu_mgr::set_level(device::ICU::VECTOR::SWINT, configKERNEL_INTERRUPT_PRIORITY);
    }

通常、タイマーの割り込み処理から呼ばれる関数をアタッチ出来ます。
C++ では、「ファンクタ」と呼ばれる手法を良く使います。
cmt_mgr も、割り込み内から呼ぶ関数を、ファンクタとして扱うようにしています。
cmt_mgr のプロトタイプは以下のようになっています。

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    /*!
        @brief  CMT マネージャー・クラス
        @param[in]  CMT チャネルクラス
        @param[in]  TASK    タイマー動作クラス
    */
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    template <class CMT, class TASK = utils::null_task>
    class cmt_mgr {

ここで、「TASK」はファンクタとして機能し、以下のように、() オペレータを使って動かす関数を定義しておきます。

    class my_cmt_task {
    public:
        void operator() () {
            // 処理
        }
    };

CMT1 を使い、cmt_mgr の定義は、上記クラスを使い、以下のようにします。

    typedef utils::cmt_mgr<device::CMT1, my_cmt_task> CMT;
    CMT     cmt_;

これで、割り込み処理で、この部分が処理されます。
※デフォルトでは、「utils::null_task」クラスが指定されます、これは、何もしないファンクタです。
※何もしないファンクタは、最適化で、完全に無くなります。
C++ のテンプレートでは、このような仕組みにする事で、処理する関数を呼び出すオーバーヘッドを節約出来ます。


CMT の起動

CMT は、間隔(周波数)と、割り込みレベルだけです。
※割り込みレベルが「0」の場合、割り込みを使いません。

    {
        uint8_t intr_lvl = 3;
        uint32_t freq = 1000;  // 1000Hz
        cmt_.start(freq, intr_lvl);
    }

割り込みとの同期

割り込み処理と、メイン側で同期を取りたい場合は、「sync()」を使います。

    while(1) {
        cmt_.sync();

    }

上記のループは、1000Hzの間隔でループする事になります。

割り込み回数の取得

また、割り込み回数が欲しいなら、「get_counter()」を使います。
※このカウンターは、外部割込み関数を登録した場合には無効になります。

    auto n = cmt_.get_counter();

※カウンターは32ビットで1周します。

設定周波数の取得(リアルな値)

「cmt_mgr」クラスでは、設定した周波数になるべく近い周期を生成しようと努力します。
ですが、マスタークロックに対して、割り切れない場合、実際の周期には誤差があります。

以下の関数で、設定した周波数と、実際に運用されている周波数を表示します。

    uint32_t freq = 850;
    cmt_.start(freq, 4);

    utils::format("Freq  (set): %u Hz\n") % cmt_.get_rate();
    utils::format("Freq (real): %u Hz\n") % cmt_.get_rate(true);
Freq  (set): 850 Hz
Freq (real): 849 Hz

C++ でのインスタンスの考え方

よく、例題などで、クラスのインスタンスを「new」を使って作成して、「delete」で廃棄する例を見ます。
また、C++ をあまり良く判っていない人は、インスタンスを作るコストが大きいので、C++ は小規模システム向きでは無いと思っている人が多いようです。
でも実際は、動的に作らなければ良いだけの事で、テンプレートなどを利用する事で、柔軟性があり、「動的な割り当て」をほぼ「0」に出来ます。

このような勘違いや思い込みは、C++ の場合非常に沢山あり、それが、C++ を敬遠する理由にもなっているようです。
※公開している RX マイコンフレームワークには「new、delete、malloc、free」は一切ありません。
※外部ライブラリで使われている場合があり、その対応としては多少あります。

    MyClass* my_class = new MyClass();

    my_class->xxx();

    delete my_class;

「new」すれば、必ず「delete」しなければならず、管理が面倒です、常に使う物は、単に宣言すれば良く、コンパイラが、メモリを自動で割り当てます。
※シェアードポインターで包む事で、廃棄忘れを防止出来ますが、「動的」に確保する必要が無い場合は、実態を書けば良いだけです。
※関数内の場合は、割り当てはスタック上ですが、関数外に置けば、ワークメモリに割り当ててくれます。

    {
        MyClass my_class;

        my_class.xxx();

    }

※上記の場合、MyClass の実態「my_class」は、スコープを抜ければ、自動で廃棄されます。

このように、C++ では、「割り当て」は極力減らし、それに伴うリスクを避ける事が出来ます。