MTU テンプレートクラスをクリーンアップ
RXマイコンの MTU は、非常に高機能で、シリーズが異なると微妙に仕様が異なっている場合もある。
以前に、RX24T で、MTU を使った時に、未完全な状態で実装したが、少し整理してみた。
これは、RX72N Envision Kit で、デジタルストレージオシロを作る場合に MTU の出力が必要な為だ。
※プローブのインピーダンスマッチングを行う場合など、基準波を出力する必要がある為だ。
Pmod2 コネクタに PD1 がアサインされており、PD1 は、MTIOC4B に設定可能となっている。
※この端子は、MTU4、コンペアマッチBに対応した出力として利用可能となっている。
テンプレートの複雑な型を回避
テンプレートクラスの場合、実装時の問題として、「にわとりの卵」問題が起こる場合がある。
これは、テンプレート内に、そのクラス特有の構造体や、enum class 型を定義している場合などで、起こる。
テンプレート内に定義すると、テンプレートの正確な型を知らないと、その定義にアクセス出来なくなる。
template <class A, class B, class C>
class asdfg {
public:
enum class TYPE {
AAA,
BBB,
CCC,
};
struct setting_t {
...
};
};
上記のテンプレートクラスでは、asdfg クラスの「TYPE 型」や、「setting_t 構造体」にアクセスしたい場合、class A、B、C の正確な「型」を知らないと出来ない。
そこで、この回避策として、これら定義を分離してプレーンなクラスとして定義する。
class asdfg_base {
public:
enum class TYPE {
AAA,
BBB,
CCC,
};
struct setting_t {
...
};
};
そして、そのクラスを継承する。
template <class A, class B, class C>
class asdfg : public asdfg_base {
...
};
こうすれば、クラス A、B、C の型を知らなくても、「asdfg_base::TYPE」、「asdfg_base::setting_t」として参照出来る。
※「asdfg_base」クラスは、メンバー変数などを置かない、「定義」だけのクラスにしておく、そうすれば、結びつきが緩和されて、全体的な構成がシンプルとなる。
※ C++ での「継承」は、クラスとの結びつきが強力で、なるべく使わない設計が好まれる。
タイマー関係のポート設定
現在のフレームワークでは、比較的簡単な定義で、ペリフェラル固有のポート設定を自動化している。
これは、外部プログラムの助けを借りて、設定を生成しなくても、簡単な指定で、ポートのアサインを自動で行う事が出来る仕組みとなっている。
また、ソースコードはオープンソースとしているので、足りない機能は、個々に追加できる事も大きい。
ポートのアサインは、非常に多義に渡り、複雑でボリュームが大きく、マイコン種別で異なる。
C++ では、コンパイラの最適化で、余分なコードは極限まで排除出来る為、最終的に必要無いコードは、実行バイナリーに含まれない。
※コンパイル時にパラメーターが決定されている必要がある。
arduino などでは、ポートは、決められた整数値を通して、普通の関数で行っている。
この場合、関数内では、switch 文などで、場合別けをして、それぞれの設定を行っている。
しかし、このような実装では、コンパイル時の最適化で余分なコードを排除する事は難しいし、速度が出ない場合がある。
テンプレートパラメーターの場合は、同じように場合別けで実装しても、コンパイル時に、決定された固定値以外のルートにあるコードは削除される。
これが、C++ テンプレートの強みであると思う。
ポートのアサインを行うクラスは、「port_map」で行っている、しかし、MTU 関係は、ポートの指定が複雑なので、専用のクラスを実装する事にした。
※「port_map_mtu.hpp」に移動した。
以下は、MTU 関係のポートを RX72N 176 ピンバージョンから、抜き出して、並べた。
※基本的に、144 ピンやそれ以下のバージョンでは、部分的にポートが無いだけで、対応は同じとなっている。
///< P34 ( 27) MTIOC0A
///< PB3 ( 98) MTIOC0A / MTIOC4A
///< P15 ( 50) MTIOC0B / MTCLKB
///< P13 ( 52) MTIOC0B
///< PA1 (114) MTIOC0B / MTCLKC / MTIOC7B
///< P32 ( 29) MTIOC0C
///< PB1 (100) MTIOC0C / MTIOC4C
///< P33 ( 28) MTIOC0D
///< PA3 (110) MTIOC0D / MTCLKD
///< P20 ( 45) MTIOC1A
///< P21 ( 44) MTIOC1B / MTIOC4A
///< P26 ( 37) MTIOC2A
///< PB5 ( 96) MTIOC2A / MTIOC1B
///< P27 ( 36) MTIOC2B
///< P17 ( 46) MTIOC3A / MTIOC3B / MTIOC4B
///< P14 ( 51) MTIOC3A / MTCLKA
///< PC7 ( 76) MTIOC3A / MTCLKB
///< PC1 ( 89) MTIOC3A
///< P22 ( 43) MTIOC3B / MTCLKC
///< PC5 ( 78) MTIOC3B / MTCLKD
///< P80 ( 81) MTIOC3B
///< PB7 ( 94) MTIOC3B
///< PJ3 ( 13) MTIOC3C
///< P56 ( 64) MTIOC3C
///< P16 ( 48) MTIOC3C / MTIOC3D
///< PC6 ( 77) MTIOC3C / MTCLKA
///< PC0 ( 91) MTIOC3C
///< P23 ( 42) MTIOC3D / MTCLKD
///< P81 ( 80) MTIOC3D
///< PC4 ( 82) MTIOC3D / MTCLKC
///< PB6 ( 95) MTIOC3D
///< PE0 (135) MTIOC3D
///< P24 ( 40) MTIOC4A / MTCLKA
///< P82 ( 79) MTIOC4A
///< PA0 (118) MTIOC4A / MTIOC6D
///< PE2 (133) MTIOC4A
///< P30 ( 33) MTIOC4B
///< P54 ( 66) MTIOC4B
///< PC2 ( 86) MTIOC4B
///< PE3 (132) MTIOC4B
///< PD1 (156) MTIOC4B
///< P25 ( 38) MTIOC4C / MTCLKB
///< P87 ( 47) MTIOC4C
///< P83 ( 74) MTIOC4C
///< PE5 (130) MTIOC4C / MTIOC2B
///< PE1 (134) MTIOC4C / MTIOC3B
///< P31 ( 32) MTIOC4D
///< P86 ( 49) MTIOC4D
///< P55 ( 65) MTIOC4D
///< PC3 ( 83) MTIOC4D
///< PE4 (131) MTIOC4D/MTIOC1A
///< PD2 (154) MTIOC4D
///< P12 ( 53) MTIC5U
///< PA4 (109) MTIC5U / MTCLKA
///< PD7 (143) MTIC5U
///< P11 ( 67) MTIC5V
///< PA6 (107) MTIC5V / MTCLKB
///< PD6 (145) MTIC5V / MTIOC8A
///< P10 ( 68) MTIC5W
///< PB0 (104) MTIC5W
///< PD5 (147) MTIC5W /MTIOC8C / MTCLKA
///< PJ1 ( 59) MTIOC6A
///< PE7 (125) MTIOC6A
///< PJ0 ( 60) MTIOC6B
///< PA5 (108) MTIOC6B
///< P85 ( 61) MTIOC6C
///< PE6 (126) MTIOC6C
///< P84 ( 62) MTIOC6D
///< PA2 (112) MTIOC7A
///< P67 (120) MTIOC7C
///< P66 (122) MTIOC7D
///< PD4 (148) MTIOC8B
///< PD3 (150) MTIOC8D
ピンのアサインで、痛いとこは、ピンの割り付けは自由に出来ない点だ・・
何故、自由に出来ないのか?、ノイズや、複雑度、回路設計上の事情など様々と思うが、内部の機能を全て有効に使う事は通常出来ない。
毎回、特定のボードで、色々な機能を割り振る場合に、ピンのアサインが可能かどうかを調べる作業が難航する。
使う機能が少ない場合は、難しくないのだが、色々な機能を使いたい場合に、いつも苦労する。
場合によっては、ピンのアサインが重複して、機能を使えない場合も多々起こる。
ボードが出来てから、機能を追加する場合などに、どう頑張ってもアサインが出来ずに、パターンを切って貼ってを行う場合も起こる・・
このような制約は、今後回避出来るようにしてもらいたいものだが・・
また、ハマリポイントとして、MTU3、MTU4 は、ポートが、他のチャネルと重複している為、特定のレジスタを有効にする必要がある。
TOERA レジスタは、出力端子の MTIOC4D、MTIOC4C、MTIOC3D、MTIOC4B、MTIOC4A、MTIOC3B
の出力設定の許可 / 禁止を行うレジスタです。
これらの端子は TOERA レジスタの各ビットの設定をしないと正しく出力されません。TOERA レジスタ
は MTU3、MTU4 の TIOR レジスタ設定の前に値をセットしてください。
MTU.TOERA レジスタは、MTU.TSTRA レジスタの CST3、CST4 ビットを “0” にした後で設定してくださ
い。
RX72N Envision Kit でのテスト
RX72N Envision Kit では、Pmod2 コネクタの 8 番ピンに、PD1 がアサインされている。
PD1 は、MTU4 の B チャネル出力として利用できる。
定義は以下のように行う:
※ポートの候補は「FIFTH」となっている。
typedef device::MTU4 MTU;
static const auto PSEL = device::port_map_mtu::option::FIFTH;
typedef device::mtu_io<MTU, utils::null_task, utils::null_task, PSEL> MTU_IO;
MTU_IO mtu_io_;
10000Hz、チャネル B、コンペアマッチでトグル出力としている。
※トグル出力の場合、内部では、倍の 20000Hz でカウンタが動作するように自動で設定される。
{
uint32_t freq = 10'000;
if(!mtu_io_.start_normal(MTU::channel::B, MTU_IO::OUTPUT::TOGGLE, freq)) {
utils::format("MTU4 not start...\n");
} else {
list_mtu_freq_();
}
}
内部のカウンタ誤差を表示する:
mtu_io クラスでは、MTU の分周器の機能を使い、なるべく設定値に近い周期を設定しようとする。
それでも、設定誤差が大きい場合はある。
void list_mtu_freq_()
{
utils::format("MTU rate (set): %d [Hz]\n") % mtu_io_.get_rate();
auto rate = 1.0f - static_cast<float>(mtu_io_.get_rate()) / mtu_io_.get_rate(true);
rate *= 100.0f;
utils::format("MTU rate (real): %d [Hz] (%3.2f [%%])\n")
% mtu_io_.get_rate(true) % rate;
}
ターミナル出力:
Start test for 'RX72N' 240[MHz]
SCI Baud rate (set): 115200
SCI Baud rate (real): 115355 (0.13 [%])
CMT rate (set): 100 [Hz]
CMT rate (real): 100 [Hz] (0.00 [%])
MTU rate (set): 10000 [Hz]
MTU rate (real): 10000 [Hz] (0.00 [%])
※この全ソースコードは、Github の RX/test にある。
※RX/test は、プロジェクトに組み込む単機能のテストを行うもので、場合によりコードが残らない場合がある。