RX63T を試用してみた

RX621 関係は、別件で忙しくて、中々進まない・・

前から、マイコン制御のインバーターやら、DC/DCコンバーターなどを実験してみたくて、色々、構想を練ったり、試作したり、壊したりしてたー

ブラシレスモーターの制御では、一般的には 20KHz 程度の PWM で、直接コイルをドライブする方式が主流で、ラジコンのモーターコントローラーとかがそれだ。
※大抵、8KHz と 16KHz を切り替え出来るようである。

また、正確なサイン波を作る 100V 用のインバーターを作りたいと思っていた。

それと、リチウムイオン電池の充電や、昇圧、降圧のような、アナログ回路が主流の分野をマイコンでどうにかする事を考えていた。
この辺のICとしてはリニアテクノロジーとかが多くのICをラインナップしているが、意外と割高で使うのをためらっていたのが現状。

DC/DC コンバーターはパワー MOS-FET の高速化と、コアの材料改良によって、かなり高いスイッチング周波数でも効率が落ちなくなってきている。

マイコンで、DC/DC のような物を実現する場合に問題となるのは PWM の周波数である、たとえば、20MHz のクロックでは、8 ビットの分解能だと、78.125KHz にしかならない、78KHz 程度では、小型のコイルを使う事が出来ない為、不十分だ・・

そんなこんなで RX マイコンであるー
RX63T の PWM は 100MHz で駆動できる、8 ビットの分解能なら 390KHz、9 ビットでも 195KHz で駆動できる、しかも、コアも 100MHz で動作する為、かなり高速な応答に備える事が出来そうである。
これを前から実験してみたくて、ウズウズしてたら、R5F563T6EDFMを小売してる事が判り、早速購入してみた 582円、構成は、64KFlash、8KRAM、と丁度手頃な感じで、RX621 のリソースを流用出来ると考えていた。

最近、ようやく、試作して、Lチカまで出来たので紹介する。

IMG_0520ss

それで判った事は、RX63T(63系) は 62系 と構成が変わり、62 系のプログラムでは動作しない事だ。
大きな違いは以下
・クロック回路が大幅に拡張されている。
内部発振で125KHzのクロックが使える。
PLL 回路の構成が拡張され、外部発振のクリスタルや、入力クロックの柔軟性が上がった。
各モジュールの分周回路が拡張された。
柔軟性が上がったのは良いが、クロックの設定が面倒になった。

static volatile uint8_t dummy_;

static void wait_()
{
    // とりあえず無駄ループ
    for(uint32_t i = 0; i < 5000; ++i) {
        i += dummy_ & 0;
    }
}

int main(int argc, char** argv)
{
    using namespace root;

    device::SYSTEM::PRCR = 0xa503;  // クロック、低消費電力、関係書き込み許可

    device::SYSTEM::MOSCWTCR.MSTS = 0b01101;    // 131072 サイクル待ち
    device::SYSTEM::MOSCCR.MOSTP = 0;           // メインクロック発振器動作
    dummy_ = device::SYSTEM::MOSCCR.MOSTP();
    device::SYSTEM::MOFCR.MOFXIN = 1;           // メインクロック強制発振
    dummy_ = device::SYSTEM::MOFCR.MOFXIN();
    wait_();

    device::SYSTEM::PLLCR.STC = 0xf;            // PLL 16 倍(192MHz)
    device::SYSTEM::PLLWTCR.PSTS = 0b01010;     // 131072 サイクル待ち
    device::SYSTEM::PLLCR2.PLLEN = 0;           // PLL 動作
    wait_();
    device::SYSTEM::SCKCR = device::SYSTEM::SCKCR.FCK.b(3)      // 1/8
                          | device::SYSTEM::SCKCR.ICK.b(1)      // 1/2
                          | device::SYSTEM::SCKCR.BCK.b(2)      // 1/4
                          | device::SYSTEM::SCKCR.PCKA.b(1)     // 1/2
                          | device::SYSTEM::SCKCR.PCKB.b(1)     // 1/4
                          | device::SYSTEM::SCKCR.PCKC.b(3)     // 1/8
                          | device::SYSTEM::SCKCR.PCKD.b(3);    // 1/8
    device::SYSTEM::SCKCR3.CKSEL = 4;   ///< PLL 回路選択

...

}

※以前は、USB を使いたい場合、12MHz の基準周波数を外せなかったが、今度はもう少し柔軟性がある。

・プロテクト回路が新設された。
これは、自動車の ECU や大型機械の制御など、より信頼性が必要な分野にコミット出来るようにしたものだと思われるが、重要なレジスター関係(クロック設定や消費電力削減関係など)には、プロテクト回路をセーフ状態にしないとアクセス出来ないようになった。
※これが判らなくて、はまった・・・www

・シリアルインターフェースが拡張された。
本来シリアルインターフェースの考え方(送信や受信の手続き)は、I2C や、SPI 通信と共有できる部分が多い、今回「簡易」とついているが、シリアルインターフェースが拡張されて、これらの機能が使えるようになった。

※他にもあるけど、とりあえずこんな感じ。
※現状のソースコードを github にプッシュしてある。

さて、次はいよいよ、とりあえず、降圧の DC/DC コンバーターを作ってみる。

※追記:2013年12月2日(基本回路)

回路図まで手が回らないので、とりあえず、ピン接続を書いておきます。
※ピン番号は 64 ピン版です(R5F563T6EDFM) 64K Flash、8K RAM

基本的な接続:
EMLE(1)  ---> GND (オンチップエミュレーター許可端子、許可する場合 VCC へ)
P01(4)   ---> 10K でプルダウン(ユーザーブートとブートの切り替え用)
VCL(3)   ---> 0.1uF のコンデンサを介して GND へ
RES#(6)  ---> プルアップ又は 3.3V 系リセットICへ
XTAL(7)  ---> 12MHz クリスタル(22pF のコンデンサで GND)
EXTAL(9) ---> 12MHz クリスタル(22pF のコンデンサで GND)
※PLL 回路や、周辺回路がリッチなので、色々な周波数を選択出来ます。

電源:
VSS(8)  ---> GND
VSS(22) ---> GND
VSS(44) ---> GND
VCC(10) ---> 3.3V
VCC(20) ---> 3.3V
VCC(42) ---> 3.3V
※各電源端子には 0.1uF のバイパスコンデンサを入れる。(3個)

セルフブート(Flash への SCI からの書き込み)に必要なピン:
MD(5) ---> Low: ブートモード、High: シングルチップモード

オプション設定メモリー:
FFFFFF80 ~ FFFFFF83: はシングルチップ時のエンディアンを選択します。
※通常何も設定しなければ、Flash のイレース値が 0xFF となる為リトルエンディアンとなります。
※その他の初期状態設定(OFS0、OFS1)は「オプション設定メモリー」の項を参照して下さい。

LCD用ビットマップデータ変換

LCD などを使った機器では、文字や、グラフィックスなど比較的小さい物を扱う事が出来れば便利です。
AVR マイコンなどに LCD を繋いだ場合は、小さな絵をデータ化して、プログラム領域に置いておき、描画したいものです。

そんな時、手頃な変換ツールがあれば便利なので、OpenGL(GLFW3)の一環として作ってみました。
※OpenGL はプレビューの描画にしか使っていないので、通常動作では、コマンドラインで行い、GUI は簡単なプレビューを助ける程度しかありません。

また、「漢字」を表示させたい用途もあります、自分のライブラリーは、FreeType2 を持っているので、TrueType フォントの描画も出来ますが、ライセンスの問題や、LCD のような低解像度で単色では、フリーで流通している BDF 形式を扱えたら便利です、そこで、BDF フォントファイルを読み込んで、変換する機能も入れてみました。
SJIS の漢字コードをおおよそいれると、12ピクセルのフォントでも156キロバイト必要です、通常は外部にEEPROM(2メガビット必要)を接続して、そこにデータを置く必要がありますが、SD-CARD のインターフェースがあれば、そこから必要なフォントをキャッシュすれば、かなり実用的な速度で描画する事も可能です。

※ビットマップの絵や自分フォントなどをデザインする場合には、自分は「edge」と言うフリーソフトを使っています、小回りが利いて、ドット絵をデザインするには便利なソフトです。

--

この変換ツールは、画像ファイルとして以下のフォーマットをサポートしています。
bmp,png,jpeg,jpeg2000,tga
※gif はあえてサポートしていません。

ファイル出力は、バイナリーか、C ソース用のテキスト出力を選択出来ます。
他に細かいオプションがあります。

出力は、ビットストリームで、行われます、展開する場合は、単純にLSBから1ビットづつアクセスして、順番に描画すればよく、サイズと速度で合理的と思えます。
出力はバイト単位で行われる為、余ったビットは0で埋められます。
※ AVR の描画サンプルが github にあります。

ソースコード、実行ファイルをgithubで公開しています。
※DLL は player/build からコピーして下さい。

コマンドを実行すると、以下のように簡単なヘルプが表示されます。

BitMap Converter
Copyright (C) 2013, Hiramatsu Kunihito
Version 0.50
usage:
    bmc.exe [options] in-file [out-file]
    -preview,-pre     preview image (OpenGL)
    -header size      output header
    -text             text base output
    -c-style symbol   C style table output
    -offset x,y       offset location
    -clip       x,y        clipping area
    -bdf              BDF file input
    -append           append file
    -inverse          inverse mono color
    -dither           ditherring
    -verbose          verbose

※英語表記がかなりいい加減です・・・

・「-preview」は、変換後に画像をプレビューします。
・「-no-header」は、通常画像のサイズ、又は切り出した場合、そのサイズを、横、縦2バイト出力しますが、それを抑止します、サイズが同じ物を連続して出力する場合などに使います。
※サイズが256バイトを超えると問題が起こります、ソースコードもありますから必要なら機能追加して下さい。

・「-header size」は、ヘッダー情報としてサイズを出力します、「size」は、ビット幅です。
・「-text」は、ソースコードに取り込めるようにした16進形式の羅列です。
・「-c-style symbol」は、uint8_t の配列として、シンボル名を含めて出力します。
・「offset x,y」はソース画像を切り出す場合のオフセットです。
・「clip x,y」は、ソース画像を切り出すサイズを指定します。
※これら、「x,y」のパラメーターは、一般的な数値計算を受け付けます。(Ex: 16*5,24*3)
・「-bdf」は、BDF 形式のファイルを入力する場合です。
※BDF 形式でのプレビューは限定的ですが、これは仕様です。
・「-append」は、出力ファイルに追加で出力する場合に指定します。
・「-inverse」はピクセルを反転します。
・「-dither」はディザリング処理を行います。
・「-verbose」は、内部情報を出力します。

※PNGファイルの変換とプレビュー

bmc -pre images/matrix_font.png out

bmc00

※BDFフォント(warabi12 フォント)の変換とプレビュー

/bmc -pre -bdf bdf/warabi12-0.19a/warabi12-1.bdf kfont12.bin

bmc_02

BDF の漢字ビットマップの出力では、SJIS並びとなっています、合理的に並べられ、SJISコードから漢字のビットマップをアドレスするのが容易な為ですが、文字コードはUTF8 を使う必要もあり、結局相互に変換を行う必要性は免れません・・・

また、SJIS をリニアアドレスに変換する場合は、以下のコードを参考にして下さい。

    // sjis コードをリニア表に変換するサンプル。
    // 上位バイト: 0x81 to 0x9f, 0xe0 to 0xef
    // 下位バイト: 0x40 to 0x7e, 0x80 to 0xfc
    static uint16_t sjis_to_liner_(uint16_t sjis)
    {
        uint16_t code;
        uint8_t up = sjis >> 8;
        uint8_t lo = sjis & 0xff;
        if(0x81 <= up && up <= 0x9f) {
            code = up - 0x81;
        } else if(0xe0 <= up && up <= 0xef) {
            code = (0x9f + 1 - 0x81) + up - 0xe0;
        } else {
            return 0xffff;
        }
        int loa = (0x7e + 1 - 0x40) + (0xfc + 1 - 0x80);
        if(0x40 <= lo && lo <= 0x7e) {
            code *= loa;
            code += lo - 0x40;
        } else if(0x80 <= lo && lo <= 0xfc) {
            code *= loa;
            code += 0x7e + 1 - 0x40;
            code += lo - 0x80;
        } else {
            return 0xffff;
        }
        return code;
    }

※ディザリング処理の場合(2013年11月21日機能追加)
DitherTest

※BDF フォントの読み込み時、プレビューを修正(2013年11月24日)
bmc_03

C 言語よりお得な C++ その4

(8)文字列の扱い(コンテナによるオブジェクトの受け渡し)
AVR の C++ には STL が無いので、std::string を使う事はできませんが、文字列を確保する簡単なコンテナを実装して、それを使う事は出来ると思います、またネットを探せば、std::string に似せた実装のいくつかを見つけられると思います。
※動的に確保が必要な文字列が必要無いのであれば、以下の項目はスルーして下さい、しかしながら、常に固定化された文字列だけ扱えば良い場合と言うのも稀と思います。
※std::string が使える環境で、あえて、同じような文字列のコンテナを使う意味は全くありません(ゲームの開発者などに多いようですが、std::string より優秀なクラスを設計して実装し、動作確認をするには、極めて莫大な時間と労力を消費すると思います)、std::string は極めて正しく実装されており、std::string より優れた実装を行う事は殆ど不可能に近いと思います、これは、STL を理解していない「一知半解」と言うべき愚かな行為です。
※メモリーの断片化を避けて、独自の記憶割り当てを行いたい為に、独自の string クラスを作る人がいますが、そんな事をしなくても、アロケーターを実装して、それを渡せば良いのです、std::string が仕様的に不適格で、別の実装を考えなければならない場合は通常殆どありません。
C 言語から C++ を始めて間もない頃は、文字列をやりとりする場合などに、std::string と char* をチャンポンで使った実装をする事が多いと思います、ですがー、コンテナを使った方法論に移行すべきタイミングです。
コンテナを使って返す事で、余分にコピーが発生するので、速度が落ちると考えるかもしれませんが、最適化以前に、より良い設計を心がけるべきで、最適化はその後です。
※通常、コンテナの受け渡しは参照によって行われるので、コピーが発生するのは、最小限となります。
※ AVR の gcc では std::string が使えないので、勉強と、STL 理解の目的で、string クラスを実装してみるのは有益だと思います、仕様は、std::string を元にすれば良いし、テンプレートの作り方などを習得できます、同じ物を作るのはハードルが高いので、自分に必要な機能だけ実装それば良いと思います。
実際には、AVR では、メモリー領域が物理的に分かれている為、ROM 上(コード領域)の場合と、RAM 上の場合で、別々の処理を行う必要があるので、簡単では無いのですが・・

class aaa {
  // 内部で扱うコンテナや変数は「private」にし、外部に公開する場合は、必ずアクセサーを使ってアクセスするように設計します。
  std::string  text_;
public:

  // const 参照で受けて・・
  void put(const std::string& text) {
    text_ = text;
  }

  // const 参照を返す・・
  const std::string& get() const { return text_; }

  // ポインターを返す仕様なら、条件が「偽」なら、0(NULL ポインター)を返せば、シンプルと思うかもしれませんが、ソースがコンテナであるのに、そのポインターを返すのでは二度手間になる場合があります、やはり、統一的にコンテナとして扱えた方が利便性が高いので、「偽」の場合に、空のコンテナを返すようにすれば、コンテナとして統一的に扱えます。
  const std::string& test(char ch) const {
    if(!text_.empty() && text_[0] == ch) {
      return text_;
    } else {
      static std::string empty;
      return empty;
    }
  }

  // 固定の文字列を返すのなら「const char*」で返すのは問題無いでしょう。
  const char* name() const { return "aaa"; }
};


int main() {
  aaa a;

  a.put("abcdefg");  ///< std::string は、「=」オペレーターで「char*」型も受け取れるように実装されている。

  std::string b("qwert");
  a.put(b);          ///< もちろん、std::string を受け取る事も出来る。

  {
    const std::string& t = a.get();
    if(!t.empty()) {
    
    }
  }

  {
    const std::string& t = a.test('a');
    if(t.empty()) {

    } else {

    }
  }
}

 
 
(9)avr-g++ の制限と回避
avr-g++ は、stdc++ ライブラリーが無い為、通常標準で備わっている事が出来ませんので、それら使う場合は個別に実装する必要があるようです。
・new、delete

void * operator new(size_t size)
{
    return malloc(size);
}

void operator delete(void * ptr)
{
    free(ptr);
}

 
・また場合によっては、スレッドセーフの機構を呼び出すコードが追加されますので、以下のコードも追加します。

__extension__ typedef int __guard __attribute__((mode (__DI__)));

extern "C" int __cxa_guard_acquire(__guard *);
extern "C" void __cxa_guard_release (__guard *);
extern "C" void __cxa_guard_abort (__guard *);

int __cxa_guard_acquire(__guard *g) {return !*(char *)(g);};
void __cxa_guard_release (__guard *g) {*(char *)g = 1;};
void __cxa_guard_abort (__guard *) {};

 
・最後に、仮想関数用のコードを追加します。

extern "C" void __cxa_pure_virtual(void);

void __cxa_pure_virtual(void) {};

 
これで、ほぼ遜色無く、C++ のコードをコンパイル出来るハズです。