RXマイコン、割り込み関係整理

RX72T で CAN の検証で気がついた・・

RX72T で CAN の動作確認をした際、思ったように動作しない・・
※以前に CAN のサンプルを作成する際、RX64M 1 台で行っており、複数台で互いに通信する確認はしていなかった・・

  • データ送信しても、データが送られない。
  • データの受信もしない。

この感じは、割り込みぽぃなぁーと思い、
割り込み関係を確認したら、単純に CAN 関係割り込みが設定されていないだけだった・・
※本来、割り込みが正しく設定されない場合、初期化時にエラーを返す必要がある・・

  • 標準割り込みは、ちゃんと実装していない事を思い出す。
  • 新規にペリフェラルのマネージャーを実装した時に、追加していた。
  • なので、デバイスによりバラバラで、統一性が無い・・・
  • ここらで、ちゃんと実装しておこうと思う。

RX64M では、CAN 関係は、選択型割り込みBなので、問題無かった。
RX66T/RX72T では、CAN の割り込みは通常ベクターなので、通常ベクターの登録関数に CAN 関係の割り込みを追加する必要がある。
通常割り込みで、厄介なのは、割り込みベクター番号から、IER、IPR などの割り込み設定関係へのアクセスでは規則性が無い場合がある。
その為、対応する通常ベクターを全て実装しておく必要がある・・・
※単純には、割り込みベクター番号から、IER、IPR レジスターを推定出来ない・・

また、IPR は、シェアされて共通になっている場合もある。


割り込み関係の整理

初期の実装では、割り込み設定は、ペリフェラル別に行っていた。
※初期の実装では、ペリフェラルの定義に、割り込みベクターを「定数」として含めていなかったので、個別に対応する必要があった。
現在の実装では、割り込みベクター型や番号は、ペリフェラルの定義を参照する事で得られるようになっている。

↓現在のSCIクラステンプレートの実装:

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    /*!
        @brief  SCI 定義基底クラス
        @param[in]  base    ベース・アドレス
        @param[in]  per     ペリフェラル型
        @param[in]  txv     送信割り込みベクター
        @param[in]  rxv     受信割り込みベクター
        @param[in]  INT     送信終了割り込みベクター型
        @param[in]  tev     送信終了割り込みベクター
        @param[in]  pclk    PCLK 周波数
    */
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    template <uint32_t base, peripheral per, ICU::VECTOR txv, ICU::VECTOR rxv,
        typename INT, INT tev, uint32_t pclk>
    struct sci_t {

        static const auto PERIPHERAL = per; ///< ペリフェラル型
        static const auto TX_VEC = txv;     ///< 受信割り込みベクター
        static const auto RX_VEC = rxv;     ///< 送信割り込みベクター
        static const auto TE_VEC = tev;     ///< 送信終了割り込みベクター
        static const uint32_t PCLK = pclk;  ///< PCLK 周波数

SCI などでは、割り込みを使う場合、受信と送信、同時に使う仕様なので、それでも良かった。
しかし、割り込みベクター別に設定を行うべきなので、その仕様を改めた。

        //-----------------------------------------------------------------//
        /*!
            @brief  割り込みレベルを設定する
            @param[in]  per 周辺機器タイプ
            @param[in]  lvl 割り込みレベル(0の場合、割り込み禁止)
            @return 成功なら「true」
        */
        //-----------------------------------------------------------------//
        static bool set_level(peripheral per, uint8_t lvl) noexcept
        {
            bool ena = lvl != 0 ? true : false;
            switch(per) {

            case peripheral::SCI1:
                ICU::IPR.RXI1 = lvl;
                ICU::IER.RXI1 = ena;
                ICU::IPR.TXI1 = lvl;
                ICU::IER.TXI1 = ena;
                break;
            case peripheral::SCI5:
                ICU::IPR.RXI5 = lvl;
                ICU::IER.RXI5 = ena;
                ICU::IPR.TXI5 = lvl;
                ICU::IER.TXI5 = ena;
                break;

↑以前の実装では、ペリフェラル毎に割り込みを設定していた・・(今後、廃止する予定)

↓今後、割り込みベクター毎に登録する・・

        //-----------------------------------------------------------------//
        /*!
            @brief  割り込みレベルを設定する
            @param[in]  vec 割り込み要因
            @param[in]  lvl 割り込みレベル(0の場合、割り込み禁止)
            @return 成功なら「true」
        */
        //-----------------------------------------------------------------//
        static bool set_level(ICU::VECTOR vec, uint8_t lvl) noexcept
        {
            bool ena = lvl != 0 ? true : false;
            switch(vec) {

            case ICU::VECTOR::RXI1:
                ICU::IER.RXI1 = 0;
                ICU::IPR.RXI1 = lvl;
                ICU::IER.RXI1 = ena;
                break;
            case ICU::VECTOR::TXI1:
                ICU::IER.TXI1 = 0;
                ICU::IPR.TXI1 = lvl;
                ICU::IER.TXI1 = ena;
                break;

かなり、大掛かりな修正なので、時間がかかりそう・・・


作業実績

RX24T RX64M RX71M RX65N RX72N RX66T RX72T
icu.hpp O O O O O O O
icu_mgr.hpp O O O O O O O

all_compile (Debug)

Pass.

all_compile (Release)

Pass.

追記 (2021-05-11 08:12:55 Tuesday)

  • ハードウェアーマニュアルを良く読むと、IR レジスタ、IER レジスタは、割り込み要因の番号と一致するようだ。
  • IPR レジスタに関しては、割り込み要因番号が32以下の場合と、特定のレジスタ(RX24T)の場合に、例外的なアクセスを行えば良いらしい。

そこで、IPR クラスの [] オペレーターによるアクセスを少々工夫する事で、余分なコードを削減する事が出来た。

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  IPR レジスタ @n
                    全て、下位4ビットが有効
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        template <uint32_t base>
        struct ipr_t {

...

            //-------------------------------------------------------------//
            /*!
                @brief  []オペレータ
                @param[in]  vec     標準割り込みベクター型
                @return IPR レジスターの参照
            */
            //-------------------------------------------------------------//
            volatile uint8_t& operator [] (VECTOR vec) noexcept {
                uint32_t idx = 0;
                switch(vec) {
                case VECTOR::BUSERR: idx = 0; break;
                case VECTOR::RAMERR: idx = 0; break;
                case VECTOR::FIFERR: idx = 1; break;
                case VECTOR::FRDYI:  idx = 2; break;
                case VECTOR::SWINT2: idx = 3; break;
                case VECTOR::SWINT:  idx = 3; break;
                case VECTOR::CMI0:   idx = 4; break;
                case VECTOR::CMI1:   idx = 5; break;
                case VECTOR::CMWI0:  idx = 6; break;
                case VECTOR::CMWI1:  idx = 7; break;

                default: idx = static_cast<uint32_t>(vec); break;
                }
                return *reinterpret_cast<volatile uint8_t*>(base + idx);
            }
        };
        typedef ipr_t<0x00087300> IPR_;
        static IPR_ IPR;

icu_mgr クラスの「set_level()」関数では、以下のようにシンプルとなった。

        //-----------------------------------------------------------------//
        /*!
            @brief  割り込みレベルを設定する
            @param[in]  vec 通常割り込みベクター型
            @param[in]  lvl 割り込みレベル(0の場合、割り込み禁止)
        */
        //-----------------------------------------------------------------//
        static void set_level(ICU::VECTOR vec, uint8_t lvl) noexcept
        {
            bool ena = lvl != 0 ? true : false;
            ICU::IER.enable(vec, 0);
            ICU::IPR[vec] = lvl;
            ICU::IER.enable(vec, ena);
        }

RX72N Envision Kit での開発(その7)GPTW を使う

GPTW とは?

  • RX66T、RX72T、RX72N には、汎用 PWM 機能(GPTW)が備わっています。
  • 一般に PWM 生成は MTU を使いますが、GPTW はさらに細かい設定が可能で、MTU の拡張版的な扱いのようです。
  • カウンタは32ビットになっており、高いクロックでの駆動が可能なようになっています。
  • RX66T、RX72T は GPTW は 10 チャネル、RX72N には 4 チャネルあります。
  • RX66T、RX72T はより高いクロック(PCLKC、最大160MHz、200MHz)を分周器のクロックとして使えます。
  • RX72N は(PCLKA、最大120MHz)を分周器のクロックとして利用します。
  • 「汎用」とありますが、かなり細かい設定が可能で、主に FET や IGBT などのパワーデバイスの制御に向いています。
  • RX66T、RX72T には、さらに、高分解能波形成型器を通す事で、より細かい PWM 波形を生成できます。
  • インプットキャプチャーや、位相入力(エンコーダー入力)などにも使えます。
  • A/D コンバーターを同期して動かす事が出来るので、正確な電圧、電流の検出が行えます。

GPTW 用ポートの設定クラスを作る。

  • 最近の RX マイコンは、ポートのアサインを行う候補が増えて、より柔軟性が増したと思えます。

  • そこで、「port_map_gptw」クラスを新規に追加して、専用クラスを用意しました。

  • 候補のポリシーは、ハードウェアーマニュアルにある「MPC」にある説明に沿った物にしてあります。

  • 自分のフレームワークでは、別プログラムで設定を生成しないので、判りやすさと柔軟性を与える為、「候補」(ORDER)型を使い、設定します。

  • ポートマッピングオーダー型

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ポート・マッピング・オーダー型
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        enum class ORDER : uint8_t {
            BYPASS,     ///< ポートマップの設定をバイパスする場合
            FIRST,      ///< 第1候補
            SECOND,     ///< 第2候補
            THIRD,      ///< 第3候補
            FOURTH,     ///< 第4候補
            FIFTH,      ///< 第5候補
            SIXTH,      ///< 第6候補
            SEVENTH,    ///< 第7候補
        };
  • GPTW0 A チャネルのポート候補
        static bool gptw0_(CHANNEL ch, bool ena, ORDER opt) noexcept
        {
            bool ret = true;
            uint8_t sel = ena ? 0b011110 : 0;
            switch(ch) {
            /// GTIOC0A (入出力)
            ///       224 176 144 100
            /// P23     ○   ○   ○   ○
            /// P83     ○   ○   ○   ×
            /// PA5     ○   ○   ○   ○
            /// PD3     ○   ○   ○   ○
            /// PE5     ○   ○   ○   ○
            /// PH6     ○   ×   ×   ×
            case CHANNEL::A:
                switch(opt) {
                case ORDER::FIRST:
                    PORT2::PMR.B3 = 0;
                    MPC::P23PFS.PSEL = sel;
                    PORT2::PMR.B3 = ena;
                    break;
                case ORDER::SECOND:
                    PORT8::PMR.B3 = 0;
                    MPC::P83PFS.PSEL = sel;
                    PORT8::PMR.B3 = ena;
                    break;
                case ORDER::THIRD:
                    PORTA::PMR.B5 = 0;
                    MPC::PA5PFS.PSEL = sel;
                    PORTA::PMR.B5 = ena;
                    break;
                case ORDER::FOURTH:
                    PORTD::PMR.B3 = 0;
                    MPC::PD3PFS.PSEL = sel;
                    PORTD::PMR.B3 = ena;
                    break;
                case ORDER::FIFTH:
                    PORTE::PMR.B5 = 0;
                    MPC::PE5PFS.PSEL = sel;
                    PORTE::PMR.B5 = ena;
                    break;
                case ORDER::SIXTH:
                    PORTH::PMR.B6 = 0;
                    MPC::PH6PFS.PSEL = sel;
                    PORTH::PMR.B6 = ena;
                    break;
                default:
                    ret = false;
                    break;
                }
                break;

制御クラス(gptw_mgr)

  • MTU のマネージャークラスと同じような構成にして、「gptw_mgr」クラスを実装しました。
  • 現状では、PWM 波形を出力するだけですが、テンプレートを使い設定を参照する事で、複数のマイコンでもシンプルな構成にする事が出来ます。

動作モード

  • 動作モードとして、以下の型があります。(今後拡張予定)
  • 現状、のこぎり波以外は実装されていません。
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  動作モード型
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        enum class MODE : uint8_t {
            PWM_S_HL,       ///< のこぎり波 PWM (AB: H --> L) 
            PWM_S_LH,       ///< のこぎり波 PWM (AB: L --> H)
            PWM_S_HL_LH,    ///< のこぎり波 PWM (A: H --> L, B: L --> H)
            PWM_S_LH_HL,    ///< のこぎり波 PWM (A: L --> H, B: H --> L)
            SINGLE,         ///< ワンショット・パルス
            PWM_T1,         ///< 三角波 PWM 1(谷32ビット転送)
            PWM_T2,         ///< 三角波 PWM 2(山/谷32ビット転送)
            PWM_T3,         ///< 三角波 PWM 3(谷64ビット転送)
        };

出力制御と型

  • 出力制御では、以下の型のどれかを設定出来ます。
  • RX66T、RX72T では「反転出力」を指定出来ます。
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  出力型 @n
                    ※反転出力は、RX66T、RX72T の場合にのみ有効
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        enum class  OUTPUT : uint8_t {
            NONE,   ///< 無効

            A,      ///< A を利用
            B,      ///< B を利用
            AB,     ///< AB

            NA,     ///< 反転 A を利用
            NB,     ///< 反転 B を利用
            NA_B,   ///< 反転 A, 正 B を利用
            A_NB,   ///< 正 A, 反転 B を利用
            NA_NB,  ///< 反転 A, 反転 B を利用
        };

gptw_mgr テンプレートクラス

  • gptw_mgr テンプレートのプロトタイプは以下のようになっています。
  • パラメータとして、GPTW のチャネル型、割り込み時に起動させる事が可能なファンクタクラスを指定します。
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    /*!
        @brief  GPTW マネージャー・クラス
        @param[in]  GPTWn   GPTW[n] ユニット
        @param[in]  CMTASK  コンペアマッチタスク型
    */
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    template <class GPTWn, class CMTASK = utils::null_task>
        class gptw_mgr : public gptw_base {

...

    };
  • gptw_mgr の宣言では、以下のようにします。(RX72N GPTW1 を使った場合)
  • RX72N Envision Kit では、PMod コネクタ CN6 に (7)PD0、(8)PD1 がアサインされており、GPTW1 の出力をマッピング出来ます。
    /// PMOD Connector: PD0(CN6-7), PD1(CN6-8)
    typedef device::GPTW1 GPTW_CH;
    const auto ORDER_A = device::port_map_gptw::ORDER::FOURTH;
    const auto ORDER_B = device::port_map_gptw::ORDER::FOURTH;

    typedef device::gptw_mgr<GPTW_CH, utils::null_task> GPTW;
    GPTW    gptw_;

GPTW の開始

  • プロトタイプは以下のようになっています。
  • 周期は、整数値で周波数を指定します。
  • RX72T、RX72N ではベースクロックが異なりますが、その定義は、GPTx インスタンスで指定されている為、自動で最適な値を計算します。
  • ポート候補で、A、B 出力の「候補」を指定します。
  • 「バッファー動作」は、DUTY 設定をバッファリングする事で、DUTYを変更した場合にノイズが出ません。
  • 通常、PWM 周期とは非同期に DUTY を変更すると思うので、標準でバッファー動作になっています。
  • 設定に反故があると「false」を返して失敗します。
        //-----------------------------------------------------------------//
        /*!
            @brief  開始
            @param[in]  mode    動作モード
            @param[in]  out     出力型
            @param[in]  freq    周期
            @param[in]  ord_a   ポート候補A
            @param[in]  ord_b   ポート候補B
            @param[in]  ilvl    割り込みレベル(0 なら割り込み無し)
            @param[in]  buffer  バッファー動作を無効にする場合「false」
            @return 設定が適正なら「true」
        */
        //-----------------------------------------------------------------//
        bool start(MODE mode, OUTPUT out, uint32_t freq, typename port_map_gptw::ORDER ord_a, typename port_map_gptw::ORDER ord_b,
            uint8_t ilvl = 0, bool buffer = true) noexcept

  • GPTW の開始では、動作モード、出力ポート候補、PWM 周波数、などを指定します。
  • モードは、PWM_S_HL(初期'H' で 'L' になる)
  • 出力は AB
  • 周期は 100KHz
  • 初期状態で、33%、66%のデューティー幅のパルスを出力します。
    {  // GPTW の開始 (PWM / AB 出力)
        uint32_t freq = 100'000;  // 100KHz
        if(gptw_.start(GPTW::MODE::PWM_S_HL, GPTW::OUTPUT::AB, freq, ORDER_A, ORDER_B)) {
            utils::format("GPTW%d start: freq: %u\n") % GPTW::value_type::CHANNEL_NO % freq;
            duty_a_ = 0.33f;
            gptw_.set_duty_a(duty_a_);
            duty_b_ = 0.66f;
            gptw_.set_duty_b(duty_b_);
        } else {
            utils::format("GPTW%d start fail...\n") % GPTW::value_type::CHANNEL_NO;
        }
    }

DUTY の変更

  • サンプルでは、ターミナルを接続して、コマンド入力で、A、B チャネルの DUTY を指定できるようにしてあります。
  • a duty(0 to 1.0)
  • b duty(0 to 1.0)
  • help
Start GPTW sample for 'RX72N Envision Kit' 240[MHz]
SCI PCLK: 60000000
SCI Baud rate (set):  115200
SCI Baud rate (real): 115384 (0.16 [%])
CMT rate (set):  100 [Hz]
CMT rate (real): 100 [Hz] (0.00 [%])
GPTW1 start: freq: 100000
# a
A: duty: 0.330
# b
B: duty: 0.660
# b 0.5
#

まとめ

  • PWM 波形の出力は、意外と複雑なので、マネージャークラスの介入が欠かせません。
  • 出来る範囲で、柔軟な設定が可能なように工夫してありますが、十分ではありません。
  • 足りない設定は、gptw_mgr クラスの構成に習って、改修すれば良いと思います。
  • ソースコードはコメントも多く、動作の概要を掴みやすいように実装されています。
  • 詳細な解説が無くてもソースコードやサンプルコードを少し眺めれば理解できるものと思います。
  • gpt_mgr クラスは、単一のソースなので、他のコードを余り意識しなくても構造が判ると思います。
  • 他に「RX600/gptw.hpp」、「RX72N/port_map_gptw.hpp」を参照する必要があるかもしれません。
  • 「レジスター名」はハードウェアーマニュアルと同一にしてあるので、何かの機能を追加する場合に実装しやすいと思います。

GPTW_sample