RXマイコン、TPUテンプレートクラス、その1

最近、仕事が忙しく、更新が無かった・・・

RXマイコンでタイマー割り込みに使うのに適したペリフェラルはCMTだろ
う、単機能、シンプルで扱いやすく、理解しやすい。

しかし、CMTのカウンターは16ビットだが、プリスケーラーの仕様により
最大で、PCLKBの1/8の周期になってしまう為、間隔の短いタイマー割
り込みを組みたい場合に、誤差が大きくなる場合がある。
※メインクロックとの相性もあるが・・

そこで、他の選択肢としてTPUを使ったタイマーを実装する事にした。
※TPUは、PCLKBの1/1を選択可能なので、より正確なタイミングを
生成でき、ポートの出力も制御できる。

TPUユニットは、RX24Tには無いが、RX64Mには、1ユニット、6
チャネルある。
※RX631、RX63Nなどにもあるようだ。

ただ、面倒なのが、割り込み設定で、RX64Mではモジュールが大量に増え
た為、TPUの割り込みは固定ベクターではなく、選択的なベクターになって
いる。
TPUの操作をテンプレートで実装する場合に、割り込みベクターの管理を自
動で行いたいので、工夫してみた。
※RX631のTPUは、固定ベクターなので、その辺りも上手く吸収できる
ようにしなければならない。

また、TPUの機能は、タイマー割り込みの他、色々な機能があるので、それ
も簡単に扱えるような工夫が必要となるが、全ての機能を盛り込むと、設定が
複雑になるので、ある程度は妥協も必要で、その辺りのバランスが難しい。
※現在は、拡張中・・


TPUを考える前に、省電力機能の管理を少し拡張した。
「省電力機能」は、内部ペリフェラルの電源を「On/Off」して、使わない電力
を削減できるもので、起動時、最低限必要なものしか有効になっていない。
※ドライバー実装時、この省電力を「無効」にするのを忘れて、ペリフェラル
が動作せずに、時間を浪費した事があるのでは無いだろうか?
※注意事項には書いてあるのと、「お約束」ではあるのだが・・

ペリフェラルのテンプレートでは、個別IDを設定しておいて、それを定義に
忍ばせておく事で、他の連携クラスが、固有の動作を切り替えて実現でき、ま
た、連携クラスの実装分担を、ペリフェラルの定義から分離できる。

この簡単な仕組みにより、ペリフェラルの実装時、ペリフェラルやチャネルに
より異なる実装を隠蔽できる。

自分は、「手」を動かす事が好きなので、あーでもない、こーでもないを繰り
返して今の実装になっている。

ペリフェラルの定義:

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  ペリフェラル種別
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
enum class peripheral : uint16_t {
    TPU0,   ///< 16 ビットタイマパルスユニット0
    TPU1,   ///< 16 ビットタイマパルスユニット1
    TPU2,   ///< 16 ビットタイマパルスユニット2
    TPU3,   ///< 16 ビットタイマパルスユニット3
    TPU4,   ///< 16 ビットタイマパルスユニット4
    TPU5,   ///< 16 ビットタイマパルスユニット5

...

};

TPUの定義:

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  16 ビットタイマパルスユニット(TPUa)
    @param[in]  base    ベースアドレス
    @param[in]  per     ペリフェラル型
    @param[in]  intra   割り込み要因A
    @param[in]  intrb   割り込み要因B
    @param[in]  intrc   割り込み要因C
    @param[in]  intrd   割り込み要因D
    @param[in]  intru   割り込み要因U
    @param[in]  intrv   割り込み要因V
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <uint32_t base, peripheral per,
    uint8_t intra, uint8_t intrb, uint8_t intrc, uint8_t intrd, uint8_t intru, uint8_t intrv>
struct tpux_t {

...

    //-----------------------------------------------------------------//
    /*!
        @brief  ペリフェラル型を返す
        @return ペリフェラル型
    */
    //-----------------------------------------------------------------//
    static peripheral get_peripheral() { return per; }
};

TPUチャンネル毎の定義:

typedef tpux_t<0x00088110, peripheral::TPU0, 15, 16, 17, 18,  0, 19> TPU0;
typedef tpux_t<0x00088120, peripheral::TPU1, 20, 21,  0,  0, 22, 23> TPU1;
typedef tpux_t<0x00088130, peripheral::TPU2, 24, 25,  0,  0, 26, 27> TPU2;
typedef tpux_t<0x00088140, peripheral::TPU3, 28, 29, 30, 31,  0, 32> TPU3;
typedef tpux_t<0x00088150, peripheral::TPU4, 33, 34,  0,  0, 35, 36> TPU4;
typedef tpux_t<0x00088160, peripheral::TPU5, 37, 38,  0,  0, 39, 40> TPU5;

※「割り込み要因」は、選択型割り込みを設定する際に必要なIDで、チャネル
毎に機能が微妙に異なるが、シンプルな定義になっている。(0は、割り込み要
因が存在しない場合)

省電力管理で、必要な要素として、チャンネルの起動と廃棄時、まだ、使ってい
るチャネルがある場合に、省電力機能を無効にしないようにする事で、リファレ
ンスカウンタのような仕組みが必要な点だ。

実装は、簡単なのだが、スタティック変数の定義問題がある。
現在、ほぼ全ての実装は、ヘッダーのみで行っているので、それを壊したくは無
いが、単純にスタティック変数を記述すると、その実態として、ソースコードに
も書く必要があり、そうすると、そのソースコードのオブジェクトをリンクしな
ければならなくなる。

そこで、簡単なテンプレート関数を書いて、その中で定義できるようにした。
テンプレートにすると、「実態」をヘッダーに書けるので、ソースとヘッダーに
分ける必要が無くなる。

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  スタティック・ホルダー・テンプレート・クラス
    @param[in]	ST	構造体
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <class ST>
class static_holder {
public:
    static ST    st;
};
template <class ST> ST static_holder<ST>::st;

実際の定義では以下のようになっている。

struct pad_t {

    uint8_t    tpu_;

    pad_t() : tpu_(0) { }
};
typedef utils::static_holder<pad_t> STH;

この構造体の具体的な操作は、「RX64M/power_cfg.hpp」で行っている。

これら関連したコードはGitHubにプッシュ済み。

今回はここまで。

カテゴリー: ソフトウェアー・エンジニアリング, 電子工作な日々 | コメントする

RL78関係をマルチデバイスに対応する

RL78を再開したのは、RL78関係の仕事を受注したのが大きい。

本来R8CやRXもまだまだなので、RL78をやるのは、現状では良く
ないが、仕事を請けたので、仕方がないが、丁度良かったかもしれない。

RL78は、他のマイコンに比べて、かなり魅力的なところがある。
・以外と高速で動作する。
※WAVファイル(16ビット、48KHz)をSDカードからSPIで
読み、再生する能力がある。
・とにかく電気を食わない。
・周辺機器(UART、I2C、SPI、ADCなど)が充実している。
・C++ で開発が可能。
・デバイスの値段が安く、バリエーションも多い。

マイナス要因:
・グループ(シリーズ)が多すぎる。
・内部は基本8ビットなので、32ビットなどの値を扱うと、肥大化して
遅くなる。
・アクセスできる範囲は16ビットが基本なので、64Kの空間を、特別
なポリシーを使って、RAM領域と、ROM領域に分けている為、設計の
段階で気を使う必要があり、リソースの再利用でも、注意を要する。
※これが結構、判りずらい場合がある。

仕事では、G13とL1Cグループに対応して欲しいとのオーダーを受け、
G13専用の構造を修正して、L1Cも含めるように改修した。

RXマイコンで複数グループ対応を行い、どのような構造にするとシンプル
(アプリケーション側でグループの違いを意識しなくて済む)になるのか、
実績があるので、それらの構造を継承している。

・デバイスのクラスでは、「ペリフェラル」型を設定して、それを取得でき
るようにした。

たとえば、SAU(シリアル・アレイ・ユニット)では、以下のテンプレート
として実装されている。

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  シリアル・アレイ・ユニット・テンプレート
    @param[in]  PER     ペリフェラル型
    @param[in]  UOFS    ユニット・オフセット(0x00、0x40)
    @param[in]  CHOFS   チャネル・オフセット(0x00, 0x02, 0x04, 0x06)
    @param[in]  SDR_O   SDR レジスターオフセット
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <peripheral PER, uint32_t UOFS, uint32_t CHOFS, uint32_t SDR_O>
struct sau_t {

...

    //-------------------------------------------------------------//
    /*!
        @brief  ペリフェラル種別を取得
        @return ペリフェラル種別
    */
    //-------------------------------------------------------------//
    static peripheral get_peripheral() { return PER; }
};
typedef sau_t<peripheral::SAU00, 0x00, 0x00, 0x00> SAU00;
typedef sau_t<peripheral::SAU01, 0x00, 0x02, 0x02> SAU01;
typedef sau_t<peripheral::SAU02, 0x00, 0x04, 0x34> SAU02;
typedef sau_t<peripheral::SAU03, 0x00, 0x06, 0x36> SAU03;

「peripheral」は、enum class で定義されている、RL78のデバイス機能
分類。

uart_io.hpp クラスのプロトタイプは、以下のようになっている。

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  UART 制御クラス・テンプレート
    @param[in]  SAUtx   シリアル・アレイ・ユニット送信・クラス(偶数チャネル)
    @param[in]  SAUrx   シリアル・アレイ・ユニット受信・クラス(奇数チャネル)
    @param[in]  BUFtx   送信バッファサイズ(8バイト以上のサイズである事)
    @param[in]  BUFrx   受信バッファサイズ(8バイト以上のサイズである事)
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <class SAUtx, class SAUrx, class BUFtx, class BUFrx>
class uart_io {

割り込み関係を設定する場合、以下のようにシンプル
に書ける。

    intr::set_level(SAUtx::get_peripheral(), level);
    intr::set_level(SAUrx::get_peripheral(), level);
    intr::enable(SAUrx::get_peripheral());

また、ポートの設定などは、デバイス毎にイレギュラー的に異なるが、これも
シンプルに書ける。

manage::set_uart_port(SAUtx::get_peripheral());
manage::set_uart_port(SAUrx::get_peripheral());

「L1C」は扱ってみると、「G13」と大きく違う事が判った。
・割り込みテーブルのエントリーがかなり異なり、完全に分ける必要がある。
・当然割り込み関係のハンドリングも異なるので、これらの違いを隠蔽して
ドライバーからは同じように扱えるように改修作業を行っている。
・L1CはUSB対応デバイスの為、最大動作周波数は24MHzとなって
いる。
※ソフトディレイをかなり強引な方法で実装している。
・A/Dコンバーターは、12ビットになっている。(G13は10ビット)
・DMAコントローラーが廃止され、DTC(データトランスファコントローラ)
と、ELC(イベントリンクコントローラ)が追加されている。
・USB2.0コントローラーがある(L1C特有)
※USBは、PC側のドライバーが関係するので、簡単では無いが、USB
接続機器を接続する事が出来るのは大きいかもしれない。
※2.0と言っても、フルスピード(12Mbps)、ロースピード
(1.5Mbps)しかサポートされない。
・LCDコントローラーを内臓している。(今回は使用しない)

今回使っているデバイスは、FlashROM:64K、RAM:8K、デー
タフラッシュ:8Kの仕様で、gcc リンクファイルの領域記述を誤り、謎の
挙動を示した、しばらく原因不明で苦労したが、ようやく原因を特定して、
思ったように走るようになった。

※個人的にはG14グループを使いたいなぁーと思うのだが・・・

—–
ハードウェアーマニュアルの書き方が、日立や三菱系では無く(多分NEC系)
慣れが必要で、レジスターの令名規則も、構造的になっていない為、テンプレート
クラスを実装する際、整理しないと駄目なところが痛い。
※アセンブラで、直接ビット操作をするような場合には都合が良いのかもしれ
ない・・
※RXの三菱系が一番スマートだと思う。

カテゴリー: ソフトウェアー・エンジニアリング, 電子工作な日々 | コメントする

RL78を再開

以前に、gcc では、内部データフラッシュメモリーへのアクセスがサポートされない
事で、開発をストールさせていたが、GR-Cotton でRL78/G13が使われており
WEBコンパイラは gcc となっている、また、データEEPROMのアクセスライブ
ラリーもあるようなので、これらを利用する事にした。
※以前に、ルネサスのIDEでは、RL78はC++をサポートしないので、gcc 用
ライブラリの提供をお願いした際「できません」と断られた事があった。

GR-Cotton では WEB コンパイラで gcc を使っていて、gcc 用ライブラリーを同梱し
ている。
それなら、それを教えてくれてもいいと思うのだが、ケチくさいと言うか、イジワル
と言うか、全く話にならない対応だった(経験的に海外のメーカーでは、このような
不親切な対応は行わないと思う)
※それでもルネサスを使うのは「ドM」と言われても仕方ないが・・・

今回、仕事でRL78のオファーを受けた事で、データ・フラッシュは必須なので、
検討を始めた。
以前に、データフラッシュライブラリのオブジェクト(CS+IDE)をダンプした
ら、リバースエンジニアできそうな感じだったのだが、gcc 判があれば話が早い。

早速、GR-Cotton のプロジェクトを作成して、ソースコード一式を入手した。

その中にデータEEPROM操作関係のAPIがあるので、関係する部分を取得して、
自分のシステムに組み込んでみた。
※肝心な部分は、やはり、ライブラリーになっており隠蔽されている。(pfdl.a)

「pfdl.a」は、2つのオブジェクトをアーカイブしたものであるようだ。

% rl78-elf-nm pfdl.a

pfdl.o:
0000009d R _PFDL_Close
00000025 R _PFDL_Execute
00000091 R _PFDL_Handler
00000000 R _PFDL_Open
0000009d R PFDL_Close
00000025 R PFDL_Execute
0000006b r PFDL_Execute_erase
0000008a r PFDL_Execute_exit
00000061 r PFDL_Execute_firmware
0000007c r PFDL_Execute_read
0000007f r PFDL_Execute_read_next
00000091 R PFDL_Handler
00000000 R PFDL_Open

pfdl_version.o:
00000000 R _PFDL_GetVersionString
00000009 R _PFDL_VERSION_STRING
00000000 R PFDL_GetVersionString

ライブラリから、オブジェクトを分解して、肝心なオブジェクトを逆アセンブルして
みた。

rl78-elf-ar x pfdl.a
rl78-elf-objdump -D pfdl.o > pfdl.lst
00000000 :
   0:	c1                            	push	ax
   1:	c3                            	push	bc
   2:	a8 08                         	movw	ax, [sp+8]
   4:	71 00 90 00                   	set1	!f0090 .0
   8:	c7                            	push	hl
   9:	16                            	movw	hl, ax
   a:	bf 04 08                      	movw	!f0804 , ax
   d:	8c 01                         	mov	a, [hl+1]
   f:	9f 01 08                      	mov	!f0801 , a
  12:	e5 03 08                      	oneb	!f0803 
  15:	f2                            	clrb	c
  16:	fc f8 ff 0e                   	call	!!efff8 
  1a:	c6                            	pop	hl
  1b:	cf 80 08 04                   	mov	!f0880 , #4
  1f:	62                            	mov	a, c
  20:	9d f0                         	mov	0xffef0, a
  22:	c2                            	pop	bc
  23:	c0                            	pop	ax
  24:	d7                            	ret

上記は、PFDL_Open API の逆アセンブルソースだが、妙なアドレスをアクセスしている。
「F0804、F0801、F0803、F0880」これは、データフラッシュ関係の
制御レジスターと思われる。(ハードウェアーマニュアルには記載は無い)
また、「EFFF8」をコールしている、この部分に、データEEPROMのファーム
があるものと思われる。(このファームの存在も、一切記述が無い)

※改めて思うが、何故、隠蔽して、情報をオープンにしないのか、全く理解に苦しむ。
※本当に知りたいのなら、リバースエンジニアリングできるし、制限を設ける理由も思
いつかない。(ユーザーは不便になり、メーカーはサポートする仕事が増えるだけ)

早速テスト・・・
PFDL_Open関数を呼び出すと、戻ってこない・・・
PFDLライブラリのドキュメントを調べると、暗黙的に利用するワークRAMがあるよ
うだ。(セルフRAM領域)
※不思議な事に、デバイスにより、必要な場合と不必要な場合がある。
しかし、自分が使っているRL78/G13(R5F100LG)では、セルフRAMは
必要無い。
また、これとは別に、デバイス共通で、RAMの最終アドレス付近は、(0xFFE20
~後ろ)はライブラリの利用領域として予約されている。
※スタックやデータバッファと記述がある。

自分は、スタックを共有する為、RAMの最終アドレスを設定している。

色々、悩んだ末、解決した。
どうやら、ライブラリ内では、スタックを再設定して、内部処理を行い、ライブラリを出
る時に元に戻しているようだ。
この動作の為、共有していると正常に動作しないようだ、アプリ側のスタックを完全に分
離して、オーバーラップしないようにしたら、あっけなく動作した・・・
※これだからブラックボックスは嫌いだ・・・(まぁ自分の思い込みが原因ではあるけど)

以前にRXマイコンで作成した、対話形式で、データフラッシュの操作を行うプログラム
を組み込んで、実際に操作してみた。

問題なく書き込める!

とりあえず、操作できるようだ、これで、RL78も他のマイコンと遜色無く使える事が
判ったので、色々利用してみたい。
RL78はコストが安いので、R8C/M120とRXの中間を埋める「駒」として重宝
しそうだー

RL78データフラッシュサンプル

カテゴリー: ソフトウェアー・エンジニアリング, 電子工作な日々 | コメントする

VL53L0Xを使ったR8C(100円マイコンM120AN)による距離測定

中華製、VL53L0Xのモジュールを入手したので、距離を測定する実験を行った。

元にしたソースは、Arduino の物で、ほぼ、そのまま利用させてもらった。
ただ、Arduino のソースは、ヘッダーとソースに分かれており、C++の有意義な部分
を使っていないものが多く、その辺りは、C++流に書き換えた。

たとえば・・
・「define」マクロ関数を使っている。
※C++では、define マクロを使って関数を定義するメリットは全く無いので、一般的
に使う必要性が無い。
※実行速度も改善されないし、「型」の検査や、定義を台無しにしてしまう。
・生の「enum」を使っている、C++では、「enum class」を使う事で、冗長な書き方
や不整合を回避できる。
※部分的には、逆に「冗長」になる場合があるが、「型」安全になるので許容する。
・(定義)ヘッダーと、(実装)ソースを分ける必要性が無い。
※Arduino では、ソースとヘッダーに分かれている。
※ヘッダーに全て書けば、管理や、修正などが簡単で、間違いが少ない。
・VL53L0XはI2Cインターフェースで通信を行うが、元のソースは、I2Cの
標準的なAPI(Cの関数)を呼び出して使っている。
※I2Cのインターフェースクラスは、テンプレートで渡す仕組みにしている。
・「参照」渡しが使われていない。
※わざわざ、ポインターにする必要が無い。

I2Cは電源を入れると、4ピンなので、写真のようなコネクターを使っている。
片側は4ピン、片側は2ピンx2となっており、電源、信号接続の違いを吸収できる。

メイン部分では、I2Cを初期化して、LX53L0Xを初期化する。
※R8C/M120ANでは、ソフトウェアー処理でI2Cを実現している。
距離の測定は、「単発」で行い、0.5秒おきに、コンソールに出力する。

・I2C、VL53L0Xの定義

    // I2C ポートの定義クラス
    // P4_B5 (12): SDA
    typedef device::PORT<device::PORT4, device::bitpos::B5> sda_port;
    // P1_B7 (13): SCL
    typedef device::PORT<device::PORT1, device::bitpos::B7> scl_port;

    typedef device::iica_io<sda_port, scl_port> iica;
    iica	i2c_;
    typedef chip::VL53L0X<iica> VLX;
    VLX		vlx_(i2c_);

・初期化

// I2C クラスの初期化
{
    i2c_.start(iica::speed::fast);
}

// VL53L0X を開始
if(!vlx_.start()) {
    utils::format("VL53L0X start fail\n");
} else {
    // 20ms
    vlx_.set_measurement_timing_budget(200000);
}

・メインループ

    ++itv;
    if(itv >= 50) {
        auto len = vlx_.read_range_single_millimeters();
        utils::format("Length: %d\n") % len;
        itv = 0;
    }

出力される距離には、50mmオフセットが加算されているようだが、正確だ!

VL53L0X.hpp

VL53L0X R8C/M120AN サンプル

カテゴリー: ソフトウェアー・エンジニアリング, 電子工作な日々 | コメントする

glfw_app 関連の更新(コラム:12平均音階率)

最近は、組み込み関係が多かったが、久しぶりにPCアプリ関係のフレームワ
ークを更新した。

実際は、組み込み関係も含んでいる。

最初は、簡単なアルゴリズムで、ピアノぽぃ音が出ないかを研究する為に始め
た、アプリケーションがトリガーだった。
組み込みマイコンで、簡単なシンセサイザーぽぃ事をして、ピアノ風演奏を行
うのが目的だ。
そこで、OpenAL関係のマネージメントに手をいれ、リアルタイムに波形
を合成して鳴らす事から始めた。

とりあえず、簡単な音が出せるようになったのだが、和音とかを鳴らしてみた
くなり、MIDI入力を行うクラスの実装を始めた、とりあえずWindows
のみ。
※後から調べたら、「portmidi」なるライブラリがあり、これを使えば、最初
からマルチプラットホームにできるようだ、時間が空いたら対応しようと思う。

久しく、GUI Widgetsのプログラムをしていないので、自分で実装
したものなのに、どうやって使うか、自分で作ったサンプルを参考にするとゆ
ー、何とも、痛い状況になっている。

MIDIデバイスのリストを widget_list に反映して、選択するようにしたい
が、widget_list はダイナミックにリストを作れない構造になっていた。
まず、それを改修する為に色々修正を行った。
Widgets関係は、中途半端な部分が多く、度々改修工事が入る。

これには、色々な用件を満たしていないといけない事に気がつき、かなり色々
修正した、結局、それで終わって、肝心な部分の研究が出来ないでいる・・

—–
12平均音階率:
現代の音楽では、基本となっているもので、ほとんどの音楽は、この「決まり」
に沿って作曲されており、売られている楽器の多くが、この「決まり」に沿って
作られている。
実際のコンサートでは、ピアノの調律を、計算された周波数とは微妙に変えた、
調音を行う事があるようで(和音の響きが変わる)、流派、考え方が色々ある
ように聞くが、一般的な計算方法は以外と簡単だ。

4A(ラ)の音は440Hz、1オクターブ上がると880Hz(5A)、下
がると220Hz(3A)となる。
1オクターブ分が12個に平均的に分割されている。
つまり、12乗すると2となる定数kを求めて、それを基準の周波数に掛け
ていくと、音階が出来上がる。

k = pow(2.0, 1.0 / 12.0);
// k: 1.059463094

※何故「ド」が「C」なのか(「A」では無く)
※何故「ラ」を基準にするのか(Aだから?)
不思議な決まりが色々ある。


261.6 Hz ド   C
277.2 Hz ド#  C# 261.6 * k
293.7 Hz レ   D    277.2 * k
311.1 Hz レ#  D#  293.7 * k
329.6 Hz ミ   E    311.1 * k
349.2 Hz ファ  F    329.6 * k
370.0 Hz ファ# F#  349.2 * k
392.0 Hz ソ   G    370.0 * k
415.3 Hz ソ#  G#  392.0 * k
440.0 Hz ラ   A    415.3 * k
466.2 Hz ラ#  A#  440.0 * k
493.9 Hz シ   B    466.2 * k

ドレミの音階は、非常に不思議で、周波数的には均等な分布では無い。
※「ミ」と「ファ」の間、「シ」と「ド」の間は「黒鍵」(半音)は無く、
飛び飛びになっている。

小学高や中学校では、音楽を聴くのは好きだったが授業は不得意だった。
この辺りの理屈は、会社に入り、ゲーム用の演奏プログラムを作るようになって
から勉強したが、「周波数」の話を最初にして欲しかったwww

黒鍵が歯抜けになっている事で、ある楽譜を、少し高い音や低い音で演奏する場
合(トランスポーズ)、「黒鍵」と「白鍵」の使い方が全く異なるのに、ピアニ
ストは瞬間的にそれに対応する様を観て、「凄い」なぁーと関心した事がある・・

カテゴリー: ソフトウェアー・エンジニアリング, 電子工作な日々 | コメントする

RXマイコン、ポート、システムクロック、テンプレートの考察

ちょっと横道にそれて、ソースコードをクリーンアップしている。

開発初期では、あまり深く考えないで、安全確実なシンプルなコードで運転
していたが、もうそろそろ、気になっていた、柔軟性に欠ける部分を更新す
る時期なのかもしれない。


まず、ポート関係、RXマイコンでは、非常に沢山のラインナップがあり、
パッケージによっても使えるポートが変化するので柔軟な創りが要求される。

ただ、全てのRXマイコンに対応させる品質にするのは大変だし、自分で使
うデバイスのみ定義を行い、新しいデバイスを使う必要性が生じ場合を考慮
して、「対応できるように」を目標に実装しておく。

とりあえず、カレントのデバイスは、RX64M、RX24Tなので、それ
らを中心に考える。
※最近は使わなくなった、RX621も(秋月@950)なので、CPはそ
れなりに高いから追加しておくべきかもしれない。

デバイスで異なる典型的なレジスタとして「ODR」(オープンドレイン制
御)がある。
RX64M、RX24Tで以下のようになっている

            P0 P1 P2 P3 P4 P5 P6 P7 P8 P9 PA PB PC PD PE PF PG PJ
RX24T ODR0  o  o  o  o  x  x  x  o  o  o  o  o  x  o  o  -  -  -
      ODR1  x  x  o  x  x  x  x  o  x  o  o  o  x  o  o  -  -  -
RX64M ODR0  o  o  o  o  o  o  o  o  o  o  o  o  o  o  o  o  o  o
      ODR1  o  o  o  o  o  o  o  o  o  o  o  o  o  o  o  o  o  o

※o: 有効、x: 無効、-: ポート無し

この事実を踏まえ、ポートのテンプレートを以下のように定義して、

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  ポート定義基底クラス(PDR, PODR, PIDR, PMR, PCR, DSCR)
    @param[in]	base	ベースアドレス
    @param[in]	option	オプション
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <uint32_t base, class option>
struct port_t : public option {

...

};

ODR の定義は、option クラスとして継承させる事にすると・・

typedef port_t<0x0008C002, odr_oo_t<0x0008C084> > PORT2;
typedef port_t<0x0008C003, odr_oo_t<0x0008C086> > PORT3;
typedef port_t<0x0008C004, odr_oo_t<0x0008C088> > PORT4;
...

※RX64M

...
typedef port_t<0x0008C002, odr_oo_t<0x0008C084> > PORT2;
typedef port_t<0x0008C003, odr_ox_t<0x0008C086> > PORT3;
typedef port_t<0x0008C004, odr_xx_t<0x0008C088> > PORT4;
...

※RX24T
のように最終的な定義とした。

ここで、ODR 定義の option クラスは、

odr_oo_t    --->    ODR0、ODR1 の定義がある
odr_ox_t    --->    ODR0 のみ定義がある
odr_xx_t    --->    ODR0、ODR1 両方の定義が無い

とする。
※「odr_xx_t」は、中身の何も無いテンプレートとなっている。

あと、ポートのビット割り当ては、パッケージのピン数などでも変化があるの
で、今回は見送った(そこまでする必要があるか?)、しかしながら対応する
事は可能。

RX600/port.hpp


次にクロック関係の定義:

本来、特別な理由が無い限り、マイコンを定格より低いクロックで動作させる
事は稀(消費電力など)と思うが、一応定義はスマートに出来るようにしてお
きたい。
※初期のRXマイコンでは、USBを使う場合、12MHzのn倍にしか設定
できないので、RX621などでは、100MHzまで動作が可能でも、96
MHzでしか動かせない制限がある。
※最近のRXではPLLの設定範囲や、構成を見直し、なるべく最大のパフォ
ーマンスが出せるような工夫がされている。

(1)外部接続のクリスタルを選べる事
(2)PLL動作周波数の選択
(3)内部ペリフェラルのクロック選択

RXマイコンでは、内部のペリファラルをグループ分けしてあり、それに対応
するクロックを個別に供給している。
※専用の分周期を持っている。

以前から、Makefile 内で、コンパイルオプションを利用して、変数定義として、
各モジュールのクロック周波数を定義していた。

-DF_ICLK=80000000 -DF_PCLKA=80000000 -DF_PCLKB=40000000
-DF_PCLKD=40000000 -DF_FCLK=20000000

※しかし、実際の分周非の設定は、適切な値を直接設定していた。

これは、(3)に相当する部分で、シリアルのボーレートや、タイマー周期、
時間待ちループなどの基数として利用してきた。
外部クリスタルの周波数を変更した場合、それに合わせて、適切な値を設定す
る必要があるので、イマイチ柔軟性に欠けるものの、通常は、クリスタルを頻
繁に交換する事も少ないので、特に仕様を変更せずにそのままにした。
※分周比の設定は、一応計算されるが、割り切れないような設定では問題とな
ってしまう。

(2)は、内部PLLの構成がより柔軟となったもので、RX64Mなどは、
最大速度は120MHzだが、より広範囲の基底周波数を選択できるように、
内部PLLは倍の240MHzまで設定可能となっている。
そこで、クロック設定の際に、内部PLLをどの速度にするかを設定できるよ
うにした。
※PLLの倍率には、制限があるので、入れた数字がそのまま内部基数になる
訳ではないのだが・・

// 10MHz X-Tal, 80MHz
typedef device::system_io<10000000> SYSTEM_IO;
SYSTEM_IO::setup_system_clock(80000000);

※RX24Tでは、内部PLLの最大速度は80MHzなので、上記のように
使用する。

RX24T/system_io.hpp

カテゴリー: ソフトウェアー・エンジニアリング, 電子工作な日々 | コメントする

RX24Tのマルチファンクションタイマー(MTU3)テンプレートの実装

最近RX64M関係の仕事で、忙しかったが、ようやく何とかなった感じで、
やっと、RX24T関係を再開する事ができた。

RX24T用の汎用基板を作ろうと、回路図を引いていて、そういえば、
MTU関係の実装が全く進んでいない事に気がつき、テンプレートを実装し
始めた。(なんか回路図引くのが、億劫)

MTU3は、非常に高機能なので、全てを網羅するような、汎用性の高い
テンプレートを作るのはかなり大変なので、必要な機能を少しづつ実装して
ゆき、その都度改修するしかないと思われる。

とりあえず、インプットキャプチャー機能を試してみた。

今回実装した「mtu_io.hpp」は、テンプレートパラメーターとして、
「mtu.hpp」テンプレートを内包する。

typedef device::MTU0 MTU0;
typedef device::mtu_io<MTU0> MTU0_IO;
MTU0_IO mtu0_io_;

MTUは、9チャネルあるが、全てが同じ機能ではなく、微妙に違う部分が
あり、それらを吸収する構造を考えるのはパズルのようだがそれなりに楽し
い。
※MTU5は、3相モーター用に特化しており、除外した。(指定すると
エラーになる)

「mtu.hpp」は、ルネサスのハードウェアーマニュアルに準拠したクラスな
ので、レジスターの名称は、マニュアルに準拠させている。
だが、割り込み、入出力ポートのマッピングなど、連携して動作する機構が
必要なので、それらの制御が簡潔に書けるように、ヘルパー関数を用意した。
※これは、SCIやI2C、SPIなどの知見を元に考えた構造で、さらに
MTU用に色々と拡張した。

    uint8_t intr_level = 3;		
    if(!mtu0_io_.start_capture(MTU0::channel::A, MTU0_IO::capture_type::positive, intr_level)) {
        utils::format("MTU0 input capture start fail...\n");
    }

こんな感じで書けるようにした。

※チャネルとポートのマッピングは、現在の実装では、標準的ポート固定に
なっているので、変更できる仕組みを考えないといけないが、シンプルな方
法を思いつかないので、そのうちじっくり考える事にする・・

const auto& cap = mtu0_io_.get_capture();
float a = static_cast<float>(mtu0_io_.get_base_clock()) / static_cast<float>(cap.all_count_);
utils::format("Capture: %7.2f [Hz]\n") % a;

こんな感じで、キャプチャーした周期を表示できる。

RX24Tでは、MTUモジュールの最大周波数は80MHzなので、精度
が高いと思ったのだが、かなりずれている・・、ただ、リファレンスの周波数
の精度が悪いだけかもしれないので調査が必要・・・
※R8C/M120ANで出力した信号(R8Cのクロックは内臓RCオシレーター)
※正確なオシレーターを持っていない・・

次は、単純な波形出力を実装してみようと思う。
出力も、ほぼ、キャプチャーと同等なので、実装してみた。

typedef device::MTU1 MTU1;
typedef device::mtu_io<MTU1> MTU1_IO;
MTU1_IO mtu1_io_;

※実験では、MTU1 を使った。

// MTU1 設定
{
    uint32_t frq = 1000;
    if(!mtu1_io_.start_output(MTU1::channel::A, MTU1_IO::output_type::toggle, frq)) {
        utils::format("MTU1 output start fail...\n");
    }
}

MTU1 の出力「PA5/MTIOC1A (36)」を「PB3/MTIOC0A (32)」に繋いで1000Hz
出力。
そしてキャプチャーしてみた。

概ね正確なようだ、昨日の「ズレ」は、R8C/M120ANの周期が不正確
なのが原因だったようだ・・
それでも、真っ当な信号発生器は買うか、作る必要があるようだ・・・

ソースコードは GitHub にプッシュした
インプット・キャプチャー、サンプル
※RX64MのMTUもほぼ同等な機能なので、そのまま使えそうだが、割り
込み関係のハンドリングがかなり異なるので、それを吸収する仕組みを考えな
いとならない。

カテゴリー: ソフトウェアー・エンジニアリング, 電子工作な日々 | コメントする

R8Cを使ったタッチスイッチ(センサー)

以前に、RL78で実験したものをR8Cで焼きなおしたものです。

通常、マイコンの入力ポートは、非常にインピーダンスが高い入力元である事
を利用しています。


ガラスエポキシのユニバーサル基板上に、10mm角の銅版を載せ、表面をポ
リイミドテープなどで絶縁します。
この銅版を入力ポートに接続しておきます。
この銅版は、1Mオームの抵抗でプルアップしておきます。

・この銅版は、等価的には微小なコンデンサと考える事ができます。
・まずポートを出力として、GNDに落とし、コンデンサの電荷をリセットします。
・次に、ポートを入力にすると、1Mオームの抵抗を通じて、コンデンサがチ
ャージされ、微小な時間差で「H」になります。
・この実験では、この時間は、数マイクロ秒~数十マイクロ秒だと考えられます。
※銅版の大きさ、ベースとなるユニバーサル基板の誘電率などの条件で変化する。
※ある程度再現性はあると思うが、環境によって調整が必要。
・上記で計測した時間の違いで、「タッチ」されているか、「開放」なのかを
判断する。
※サンプルでは、通常は常に出力、Lにしておき、コンデンサをショート状態
にしてあり、計測の時だけ、入力にしている。

プログラムを単純にする為、CPUループ数で計測している。

uint16_t count_input_()
{
    uint16_t n = 0;
    INPUT::DIR = 0;  // 検出する場合だけ、「入力」にする。
    do {
        ++n;
    } while(INPUT::P() == 0) ;
    INPUT::DIR = 1;  // 出力
    INPUT::P = 0;    // 仮想コンデンサをショートしてリセット
    return n;
}

実験では、「開放時14」、「タッチ時18以上」となった。

Github

※計測値を単純に表示する場合、「DISP_REF」を有効にする。

カテゴリー: ソフトウェアー・エンジニアリング, 電子工作な日々 | コメントする

RL78の開発は中断しています

RL78/G13系は、低価格で、高性能なマイコンで、R8CとRXの
中間を埋めるものとして期待して、始めたものの、以下の理由で、中断し
ている。

(1) 内臓データフラッシュのハードウェアー仕様などが公開されない。
(2) 内臓データフラッシュ操作ライブラリーは、gcc 環境では提供されな
い。

以上が、「中止」の主要因で、他にもある。
(3) 16ビットマイコン特有の、特殊な制限
(4) RX24Tの存在

(3) については、色々あるが、16ビットマイコンとは言っても、内部の
構成は8ビットマイコンと変わらず、リニアにアクセスできる領域は64
Kバイトとなっており、特別な命令により、広範囲にアクセスできる。
また、領域には特別な意味を持たせ、小さい空間を効率よくアクセスでき
るようにしているが、デバイス特有なもので、専用のコードになってしま
う。

(4) は、コストに関するものだ、RX24Tは、10個購入で@550円
なので、それでもRL78/G13に比べるとコスト高だが、32ビット
マイコンで、80MHz動作、256Kのフラッシュ、16KのRAMと、
かなりCPが高いマイコンではある。
また、あまり多くのデバイスに手を広げるのはリソースの集中の観点から
も良くないと感じる。
当面、R8C、RXマイコンに集中する。

それでも、RL78のリソースは、一通りあり、基本的にハードウェアー
特有の部分は隠蔽してあるので、特別な事をしない限り困らないと思う。
また、R8CやRXのテンプレートライブラリーと共通性がある為、コー
ドの再利用は可能と思う。

GitHub のリポジトリーはそのまま残す。

—–
RX24TはCPが高いが、RTCが無い、CAN、イーサーネットが無
いなど、ハードウェアーを制限している(元々インバーター向けの製品な
ので・・)為、後付すると、RX62系や、RX63系など、より上位の
マイコンとコストは変わらなくなるので、使い方は多少微妙だが・・・

カテゴリー: ソフトウェアー・エンジニアリング, 気になった事を・・・, 電子工作な日々 | コメントする

R8C の割り込みベクターを共有する

先日、R8C関係のブログにコメントが入り、「誰かの役に立ってるんだなぁ」
と実感、RXで得た知見を元に、R8Cにも少しばかり反映を行った。

AVRの「ATTINY2313-20PU」は、以前は、ちょっとした物を創
る場合の救世主だったが、最近は、単価が上がり、気軽に使えなくなった。
※それでも、USBの直接接続など、需要は少なく無い。
それに代わって、現れた救世主が、「ルネサスのR8C/M120AN」で、値
段も100円で、隠し機能のおかげで重宝している。

R8C関係のC++テンプレートも、それなりに充実してきて、何か実験やテス
トを行うのに困らなくなってきている。

—–
RXのテンプレートクラスライブラリーを実装する課程で、gcc 独自の「拡張」
機能を学んだ。

__attribute__((weak));

これは、シンボル名に対する拡張で、二重定義の場合に「後から宣言されたシン
ボル」を有効にするもので、割り込みベクターの定義に有用だと判った。

つまり、

void UART0_TX_intr(void) __attribute__((weak));
void UART0_TX_intr(void) { }

上記のように「仮」の関数を定義しておき、割り込みベクターを宣言しておく事
ができる。

const void* variable_vectors_[] __attribute__ ((section (".vvec"))) = {
...
    UART0_TX_intr,   NULL,    // (17) UART0 送信
...

アプリケーション側で、割り込みが必要なクラスを使いたい場合、同じ名前で、
再定義すると、そちらが優先される。


#include "common/uart_io.hpp"
#include "common/fifo.hpp"

namespace {
    typedef utils::fifo<uint8_t, 16> buffer;
    typedef device::uart_io<device::UART0, buffer, buffer> uart;
    uart uart_;
}

extern "C" {

    void UART0_TX_intr(void) {
        uart_.isend();
    }

...

};

この改修で、今まで、アプリケーション側に定義していた、割り込みベクター
テーブルを追い出す事ができ、冗長なコードが改善できる。

追記:
しかし・・・、問題が全く無い訳では無い。
アプリ側のコードで、割り込みエントリーのシンボル名を「タイポ」した場合、
「vect.c」で定義されたシンボル名が優先されるので、割り込みエントリーが
空のままとなる、そして、コンパイル、リンクはエラー無く終了するので、こ
の手のミスが明るみになる可能性はかなり低い・・・
これは、課題とする。

カテゴリー: ソフトウェアー・エンジニアリング, 電子工作な日々 | コメントする