「ソフトウェアー・エンジニアリング」カテゴリーアーカイブ

ソフトウェアー関係の話題など・・

最近は、github にプッシュしてます~

RXマイコン、ソフト遅延を改修

ソフト遅延ループを再度、再考

「attribute ((optimize(1)))」で、上手くいったと思ったら、ループの回数が8回以下だと、インライン展開されるようだ・・

色々、あーでもない、こーでもないと、色々やったが、どうも駄目で、最後はインラインアセンブラで書き直す事とした。
しかし、gcc におけるインラインアセンブラの書式がイマイチ判っていないので、google 先生に尋ねながら何とか、物になる実装を見つけた。
※以前、最適化でループが無くならないように「asm("nop");」を入れていたが、アセンブラならその必要は無いので1マシンサイクル短くなる。
※ラベルの付け方がよくわからない・・、なので、他と「かぶらない」ように冗長な名称にしてある。

        //-----------------------------------------------------------------//
        /*!
            @brief  マイクロ秒単位の待ち @n
                    あまり正確では無く、速度に関連する設定に依存します。 @n
                    ※キャッシュ有効、最高速度での物理計測によるパラメータ
            @param[in]  us  待ち時間(マイクロ秒)
        */
        //-----------------------------------------------------------------//
        static __attribute__((optimize(1))) void micro_second(uint32_t us) noexcept
        {
            asm("cmp #0, %[us]" : : [us] "r" (us) );
            asm("beq.b micro_second_loop0\n\t"
                "micro_second_loop1:");
                uint32_t lpn = device::clock_profile::DELAY_MS;
                asm("micro_second_loop2:\n\t"
                    "sub #1, %[lpn]" : : [lpn] "r" (lpn));
                asm("bne.b micro_second_loop2"); 
            asm("sub #1, %[us]" : : [us] "r" (us));
            asm("bne.b micro_second_loop1\n\t"
                "micro_second_loop0:");
//          while(us > 0) {
//              for(uint32_t n = 0; n < device::clock_profile::DELAY_MS; ++n) {
//                  asm("nop");
//              }
//              if(device::clock_profile::DELAY_T1) { asm("nop"); }
//              --us;
//          }
        }

アセンブラコード(RX24T 80MHz):

fffc01a0 <__ZN5utils5delay12micro_secondEm>:
fffc01a0:   61 01                           cmp #0, r1
fffc01a2:   20 0d                           beq.b   fffc01af <__romdatastart+0xfffffefb>

fffc01a4 <micro_second_loop1>:
fffc01a4:   75 45 14                        mov.l   #20, r5

fffc01a7 <micro_second_loop2>:
fffc01a7:   60 15                           sub #1, r5
fffc01a9:   21 fe                           bne.b   fffc01a7 <__romdatastart+0xfffffef3>
fffc01ab:   60 11                           sub #1, r1
fffc01ad:   21 f7                           bne.b   fffc01a4 <__romdatastart+0xfffffef0>

fffc01af <micro_second_loop0>:
fffc01af:   02                              rts

rxprog を更新

ついでに、rxprog を更新して、RX231 をサポートした。
このプロトコルは、RX24T とほぼ同じもので、プログラム/イレースへ推移する為のコマンドが異なるだけだった。

ほぼ、同じコードの一部を修正して、コードをコピペするのは、悪手でしかないが、見直すには時間的余裕が必要なので、後回しとする。

RX24T もそうなのだが、ID コードのプロテクトが無い場合、プログラム/イレースへ推移すると、自動で、フラッシュをイレースする仕組みがあるようだ・・
なので、コードフラッシュのプログラムでは、「イレース動作」をしないで、いきなり書きに行くようにしている。


まとめ

「時間待ち」は、ソフトで行わなくても、RXマイコンなら、必ずある「TMR タイマー」とかを使って、やれば、正確で、今回のような問題は起きないとも思のだが・・・

TMR を使った時間待ちは、今後の課題としたい・・

又、動作クロックの違いで、プログラムの動作条件(高速動作の場合、フラッシュメモリの速度が足枷となりオペコードの読出しに遅延を設けている)が変わり、クロック数ファクターが異なるので、それに対応したい・・
constexpr を使えば、コンパイル時に計算して、定数を計算可能と思う。

RX231 動作確認と部分的最適化の抑制


RX231 の動作確認

RX231 は、RX26T と同じ時期に購入してあったけど、中々作業が出来ないでいた。
RX26T も、落ち着き、余裕が出来たので実験基板を配線した。
今回、Flash:256K、RAM:32K のデバイスで、64 ピンパッケージを購入した、RX200 系は比較的安価な製品が多い。

いつものように、変換基板を直にユニバーサル基板へハンダ付けしている。
64 ピンだと、実験基板を作成するのは楽(電源ピンが少ない)なのだが、色々な機能をアサインする場合にピンが足りなくなる・・
RX231 は動作周波数が低く、それ程興味が無かったのもあって、なるべく安いデバイスを購入。
でも、100ピン版を購入しておくべきだったかもしれない・・・
Flash も 256K は少し物足りない・・・


RX231 の特徴

  • RXv2 コア(最大 54MHz 動作)
  • USB インターフェース
  • SDHI インターフェース(SD カードアクセス)
  • SSI インターフェース(シリアルサウンドインタフェース)
  • 静電容量式タッチセンサ
  • CAN インターフェース(RSCAN なので、専用ドライバーを実装する必要があり、現在開発中)

など、意外と機能が豊富で、応用範囲が広い。

※USB のクロック用に専用 PLL 回路があり、USB を使う場合でも、CPU のクロック周波数に影響しない。
※USB を使う場合、専用 PLL の制限により、外部発振周波数は、6MHz、12MHz のいずれか。(48MHz を作る必要がある為)


gcc 関数の最適化を制御

一般に、gcc の最適化 -O3 では、インライン展開や、直接埋め込みなど、色々な手法を駆使して、最大限高速に動作するようなバイナリーを生成する。
ただ、それだと、意図と異なった動作になる場合がある。

この C++ フレームワークでは、ソフトウェアーによる時間待ちが、その影響を受ける。

以下のコードは、ソフトウェアーによる時間待ち関数になる。

        //-----------------------------------------------------------------//
        /*!
            @brief  マイクロ秒単位の待ち @n
                    あまり正確では無く、速度に関連する設定に依存します。 @n
                    ※キャッシュ有効、最高速度での物理計測によるパラメータ
            @param[in]  us  待ち時間(マイクロ秒)
        */
        //-----------------------------------------------------------------//
        static __attribute__((optimize(1))) void micro_second(uint32_t us) noexcept
        {
            while(us > 0) {
                for(uint32_t n = 0; n < device::clock_profile::DELAY_MS; ++n) {
                    asm("nop");
                }
                --us;
            }
            if(device::clock_profile::DELAY_T1) { asm("nop"); }
        }

この時間待ちは、大体1マイクロ秒になるように、ループ数を調整している。
CPU の速度が 80MHz とかだと問題無いが、54MHz だと、ループの回数が少なく、for ループが展開されて、NOP 命令のみとなり高速に動作してしまう。
for ループを最適化で展開させないようにする為「attribute((optimize(1)))」を宣言してある。
※この方法を見つける為に、色々試行錯誤した。

「((optimize(1)))」が無い場合のアセンブルリスト(for ループが展開されている):

fffc0278:   fb 5a e8 03                     mov.l   #0x3e8, r5
fffc027c:   03                              nop
fffc027d:   03                              nop
fffc027e:   03                              nop
fffc027f:   03                              nop
fffc0280:   03                              nop
fffc0281:   03                              nop
fffc0282:   03                              nop
fffc0283:   03                              nop
fffc0284:   03                              nop
fffc0285:   03                              nop
fffc0286:   03                              nop
fffc0287:   03                              nop
fffc0288:   60 15                           sub #1, r5
fffc028a:   21 f2                           bne.b   fffc027c <__romdatastart+0xffffffc4>
fffc028c:   03                              nop
fffc028d:   60 14                           sub #1, r4
fffc028f:   21 e9                           bne.b   fffc0278 <__romdatastart+0xffffffc0>

「((optimize(1)))」がある場合:

fffc01a0:   61 01                           cmp #0, r1
fffc01a2:   20 0d                           beq.b   fffc01af <__romdatastart+0xffffff07>
fffc01a4:   66 c5                           mov.l   #12, r5
fffc01a6:   03                              nop
fffc01a7:   60 15                           sub #1, r5
fffc01a9:   21 fd                           bne.b   fffc01a6 <__romdatastart+0xfffffefe>
fffc01ab:   60 11                           sub #1, r1
fffc01ad:   21 f7                           bne.b   fffc01a4 <__romdatastart+0xfffffefc>
fffc01af:   03                              nop

※「noinline」の場合、関数呼び出しを消して、インライン展開するのを除外する、関数内のループには通常の最適化が行われるので、適当ではない。


RX231 のオーバークロック耐性

とりあえず、72MHz は動作するようだ・・(この位が安全圏か!?)

2024-01-28 01:18:18 Sunday
RX231 には、「動作電力コントロールレジスタ(OPCCR)」があって、初期状態では「中速モード」になっている。
これを「高速モード」に切り替える事で、高いクロックでも安定して動作するようだ。
90MHz で駆動したが、ちゃんと動作するようだ。

PLL USB専用PLL HOCO LOCO IWDTCLK メインクロック発振器 サブクロック発振器
高速動作モード ○(注 1) ○(注 1)
中速動作モード ○(注 1) ○(注 1)
低速動作モード × × × × ×

RX231/clock_profile.hpp の定義:

#if 0
        static constexpr uint32_t   PLL_BASE    = 54'000'000;     ///< PLL ベースクロック(最大 54MHz)

        static constexpr uint32_t   ICLK        = 54'000'000;     ///< ICLK 周波数(最大 54MHz)
        static constexpr uint32_t   PCLKA       = 54'000'000;     ///< PCLKB 周波数(最大 54MHz)
        static constexpr uint32_t   PCLKB       = 27'000'000;     ///< PCLKB 周波数(最大 32MHz)
        static constexpr uint32_t   PCLKD       = 54'000'000;     ///< PCLKD 周波数(最大 54MHz)
        static constexpr uint32_t   FCLK        = 27'000'000;     ///< FCLK 周波数(最大 1 ~ 32MHz)
        static constexpr uint32_t   BCLK        = 27'000'000;     ///< BCLK 周波数(最大 32MHz)
#else
//      static constexpr uint32_t   PLL_BASE    = 72'000'000;     ///< PLL ベースクロック(最大 54MHz)

//      static constexpr uint32_t   ICLK        = 72'000'000;     ///< ICLK 周波数(最大 54MHz)
//      static constexpr uint32_t   PCLKA       = 72'000'000;     ///< PCLKB 周波数(最大 54MHz)
//      static constexpr uint32_t   PCLKB       = 36'000'000;     ///< PCLKB 周波数(最大 32MHz)
//      static constexpr uint32_t   PCLKD       = 72'000'000;     ///< PCLKD 周波数(最大 54MHz)
//      static constexpr uint32_t   FCLK        = 36'000'000;     ///< FCLK 周波数(最大 1 ~ 32MHz)
//      static constexpr uint32_t   BCLK        = 36'000'000;     ///< BCLK 周波数(最大 32MHz)

        static constexpr uint32_t   PLL_BASE    = 90'000'000;     ///< PLL ベースクロック(最大 54MHz)

        static constexpr uint32_t   ICLK        = 90'000'000;     ///< ICLK 周波数(最大 54MHz)
        static constexpr uint32_t   PCLKA       = 90'000'000;     ///< PCLKB 周波数(最大 54MHz)
        static constexpr uint32_t   PCLKB       = 45'000'000;     ///< PCLKB 周波数(最大 32MHz)
        static constexpr uint32_t   PCLKD       = 90'000'000;     ///< PCLKD 周波数(最大 54MHz)
        static constexpr uint32_t   FCLK        = 45'000'000;     ///< FCLK 周波数(最大 1 ~ 32MHz)
        static constexpr uint32_t   BCLK        = 45'000'000;     ///< BCLK 周波数(最大 32MHz)
#endif

まとめ

現状、RX231 向けに、以下のプロジェクトを動作確認して、コミットしてある。

  • FIRST_sample
  • SCI_sample
  • FLASH_sample
  • FreeRTOS
  • RAYTRACER_sample
  • PSG_sample

※RAYTRACER_sample では、RXv2 コアの優位性で、54MHz 動作の RX231 の方が、96MHz 動作の RX62N より高速だった。

マイコン core FPU fsqrt 命令 周波数 [MHz] 描画方式 時間 [ms]
RX231 RXv2 O O 54 8 bits, port-bus 1736
RX62N RXv1 O X 96 8 bits, port-bus 1860

RX26TのPLLベースクロック不具合

RX26T の PLL ベースクロック

は、ハードウェアーマニュアルでは、120 ~ 240MHz となっている。

しかし、240MHz を設定すると動作しない状況だった。

120MHz なら動作するので、まぁ問題無いかと思っていたが、やはり気になるので、ルネサス社のサポートに問い合わせていた。

色々、やりとりをした結果、スマートコンフィギュレーターでプログラムを生成して試して下さいとの事だった・・
まぁ、そうなるよなぁーと思っていたが、やはり原因は究明しておかないとと思った。

しかし、オレオレフレームワークがある程度動作する環境だと、結構億劫で、門外漢が強く後回しになっていたが、ようやく作業を始めた。
※E2Studio は起動が遅いし、プロジェクト全体の構成などが複雑で、出来れば触りたく無い。

スマートコンフィギュレーターが生成したプログラムを眺めていて、以下の部分に気がついた。

    /* b19 to b16 should be set the same value as the ICK bit. */
    tmp2_clock = tmp_clock;
    tmp2_clock &= 0x0F000000;
    tmp2_clock >>= 8;
    tmp_clock |= tmp2_clock;

あっ・・・

ハードウェアーマニュアルを確認すると、予約ビットの扱いがひっそり書かれていた・・・

これは、罠だよなぁー・・・、そういえば、RX631でも同じような事があったよなぁー・・・
まぁ、自分のミスではあるのだが・・・
何でそこに気がつかなかったのか悔やまれる。

今回は、PLL ベースクロックが、228MHz なら動作していたもので、微妙な不具合で、デバイスの不良なのかと、ハードウェアーの問題だと思ってしまい、ソフトの問題に気が回らなかった。

今後、予約ビットの扱いは、最新の注意が必要だと痛感した・・・
※予約ビットに特定の値を設定する必要があるなら、「----」にしないで、特定の名称を割り当てるべきと思う。


GNU-RX 8.3.0 202305 バージョンの不具合?

RX26T では、TFUv2 が採用されている。

このサポートには、新しいコンパイラが必要と思われ、GNU-RX ツールチェインを最新にしておいた。

しかし、以下のオプションを使って、「cmath」 をインクルードした状態でコンパイルすると、エラーが出る・・・

-mtfu-version=v2 -mtfu=intrinsic,mathlib

※「mathlib」の部分
※「-mtfu-version」が追加されている。

c:\programdata\gcc for renesas rx 8.3.0.202305-gnurx-elf\rx-elf\rx-elf\rx-elf\include\c++\8.3.0.202305-gnurx\cmath:1122:11: error: ‘::hypotf’ has not been declared

 using ::hypotf;

GNU-RX 8.3.0 202004 とか一つ前のバージョンだと発生しない。

これは、現在、問い合わせ中だ。

mathlib は、通常の数学関数が、TFU を利用した高速版に置き換えられるものと思う。


まとめ

今回の騒動、素直に「スマートコンフィギュレーターを使ったらどうですか?」と言われそうだが、本当にそれが素晴らしいものなら初めから使っている。
色々と、ハードルがあっても、自分の信念は貫きたい、より一層の注意力が必要と思う一件だった。

RX26Tの動作確認

RX26T が発表されている

RX26Tが発表されて、購入が可能な事が判り、いつもの chip1stop で購入した。
品番は、R5F526TFDGFPで、フラッシュ512K、RAM64K、データフラッシュ16K、100ピンのバージョンだ。

構成としては、かなり目新しい部分もあるが、基本的にはRX200シリーズなので、RX24Tの後継品だろうか・・

基本、家電製品や自動車などに使う事がメインと思うので、値段もまぁまぁ安い部類。


特徴

  • RXv3 コア(最大120MHz動作)
  • TFU2 内蔵(バージョンアップ版 TFU)
  • RSCI モジュール(新しいシリアル通信モジュール)
  • I3C バスインターフェース
  • RSPIA(シリアルペリフェラルインターフェース)
  • CANFD(FD 仕様の CAN モジュール)

こんな感じだろうか・・
他にも細かい部分で色々な機能がある。
残念なのは、USB インターフェースが無い点。
※CANFD のクロック分周器が新設されたが、これは以前は、USB クロックの分周器として利用していたものなので、そのせいで、USB を切ったのかもしれない。

TFU2 は、レジスタ退避機能があり、割り込み内で利用可能になったようだ。

RSCI、I3C、RSPIA,CANFD は、新たにドライバーを作り直す必要がある。
これらモジュールは、ARM 系モジュールでも使われて、今後、RX マイコンでも主流になるものと思う。
※ソフト資産を共有可能なのだろう~

内蔵メモリの容量違いで、機能が削られたりする仕様があり、デバイス選択に複雑な要因が入り込む。
このようなセールスはやめて欲しいと思う、何かメリットがあるのだろうか?
内蔵RAMが64K版と48K版がある・・、この仕様も、何かメリットがあるのだろうか?


難産・・・

早速、ユニバーサル基板に組み、初期実験を行った。
フレームワークは、RX24T、RX66T、などと共通点が多く、必要な部分を実装しておいた。
どちらかと言うと RX66T に近い構成。

しかし、ちゃんと動作しない・・・
簡単なLED点滅ですら、動かない・・・

調査調査調査・・・

少し調査した限りでは、クロックのブーストは動作しているようだが、その先でつまずくようだ・・・
こんな症状は初めてで、基板の配線、電源、色々と検査した。
VSS が1本接続忘れがあったり、細かい部分でミスを発見したが、直しても、駄目・・・

簡単なプログラムでも、リセットを繰り返すと、頻度は少ないが動作しない場合がある。
これは、クロックのブーストが失敗するのかも・・

基板の配線、主に電源ラインを確認したり、何か間違いが無いか確認したりと色々と行ったが成果無し。

色々な事をトライして、判ったのは、内部 PLL の最大周波数を 240MHz にすると、動作が不安定になる。
120MHz だと、安定して動作するようだ。
ハードウェアーマニュアルでは、120MHz~240MHz となっている。

VCL に接続したコンデンサは、 0.47uF が無かったので 1uF を付けた、それが原因なのかもしれない。
しかし、内部電源の平滑用なので、容量は PLL の動作に関係無いと思うのだが・・

昔に、チップワンストップで、チップコンデンサがファイルされた「コンデンサセレクション」を買った(高かった)、それを探したら、0.47uF があったので付け替えた。
※ここぞと言う場合に使うので、あまり使いたく無いのと、サイズが1608なのが・・
しかし、やはり 240MHz の動作は不安定でほぼ動作しない。

とりあえず、内部 PLL は 120MHz で動作させる事とした。


ソフト・ディレイループ定数

RX24T より、優秀で、単純なループでも、40%くらい速い感じがある、同じ RXv3 コアの RX66T、RX72T とほぼ同等の性能のようだ。

        static constexpr uint32_t   DELAY_MS    = ICLK / 3'065'133;   ///< ソフトウェアー遅延における定数(1マイクロ秒)

オーバークロック耐性

        static constexpr uint32_t   PLL_BASE    =  180'000'000;       ///< PLL ベースクロック(最大 120 to 240MHz)

        static constexpr uint32_t   ICLK        =  180'000'000;       ///< ICLK 周波数(最大 120MHz)
        static constexpr uint32_t   PCLKA       =   90'000'000;       ///< PCLKA 周波数(最大 120MHz)
        static constexpr uint32_t   PCLKB       =   45'000'000;       ///< PCLKB 周波数(最大 60MHz)
        static constexpr uint32_t   PCLKC       =   90'000'000;       ///< PCLKB 周波数(最大 120MHz)
        static constexpr uint32_t   PCLKD       =   45'000'000;       ///< PCLKD 周波数(最大 60MHz)
        static constexpr uint32_t   FCLK        =   45'000'000;       ///< FCLK 周波数(最大 60MHz)

オーバークロックによるSCIサンプルの動作:

Start SCI (UART) sample for 'RX26T DIY' 180[MHz]
SCI PCLK: 45000000 [Hz]
SCI Baud rate (set): 115200 [BPS]
  Baud rate (real): 115355 (0.13 [%])
  SEMR_BRME: true
  SEMR_BGDM: true
CMT Timer (set):  100 [Hz]
  Timer (real): 100 [Hz] (0.00 [%])
#

とりあえず、180MHz(1.5倍)の動作は確認した、PLL が 240MHz で不安定なので、240MHz(2倍)は動作しないだろうと思う。
それでも、十分な耐性だと思える。


サンプルアプリの動作

当然なのだが、C++ フレームワークの柔軟性により、FIRST_sample が動作したら、SCI 関係も、何の苦労無く普通に動作する。

  • FIRST_sample
  • SCI_sample
  • RAYTRACER_sample

RAYTRACER_sample:

マイコン core FPU fsqrt 命令 周波数 [MHz] 描画方式 時間 [ms]
RX62N RXv1 O X 96 8 bits, port-bus 1860
RX631 RXv1 O X 96 8 bits, port-bus 1868
RX24T RXv2 O O 80 8 bits, port-bus 1224
RX26T RXv3 O O 120 8 bits, port-bus 692
RX65N RXv2 O O 120 Frame Memory 784
RX64M RXv2 O O 120 16 bits, port-bus 751
RX66T RXv3 O O 160 8 bits, port-bus 602
RX72T RXv3 O O 192 8 bits, port-bus 464
RX71M RXv2 O O 240 16 bits, port-bus 439
RX72N RXv3 O O 240 Frame Memory 361
  • CALC_sample
Start CALC sample for 'RX26T DIY' 120[MHz]
# Rad
# tan(355/226)
 -7497258.185325587112905071831891248663417267943785263161571
# Deg
# sin(45)
 0.7071067811865475244008443621048490392848359376884740365883
# cos(60)
 0.5
# cos(45)
 0.7071067811865475244008443621048490392848359376884740365883
# sin(60)
 0.8660254037844386467637231707529361834714026269051903140279
#

まとめ

久しぶりにブログを書いた。
RXマイコン関係もかなり久しぶり。

RX26T は、CP が高めなのと、新しいペリフェラルの投入もあり、中々面白いCPUとなっている。
RX26T に新設されたペリフェラルの実装もまだなので、今後それらを中心にサポート、実験をしていきたい。

glfw3_app C++ Mupdf ライブラリ用テストアプリ

Mupdf ライブラリ

以前(数年は前)に、glfw3_app のさらに前に、一度、試していた。

その時は、Mupdf のソースコードを取ってきて、自分でコンパイルして実装した覚えがある。

最近、Mupdf のリソースは MSYS2 でもサポートされ、pacman で簡単にインストール出来るようになっていた。

そんな事もあり、glfw3_app でも Mupdf ライブラリを再び試してみた。

構成が大きく異なる・・

最近のリリースは、以前の構成とはかなり異なっている、昔のコードはそのままではコンパイル出来ない。
サンプルを観たり、ヘッダーのプロトタイプを眺めたりしながら、コンパイルが通るようになった。

しかし、リンクで必要なライブラリが足りなくて失敗する・・

色々調べて、何とかリンクまで成功。

動かすが、クラッシュする・・

簡単なコードにして、PDF ファイルを食わせてみるが、クラッシュする・・

色々調べて、呼ぶ API が足りない事に気がつく。

  fz_register_document_handlers(context_);

コンテキストを生成したら、この API を呼ぶ事がマストのようだった。

描画されない・・

だが、ちゃんと描画がされない・・

良く調べると、良かれと思って追加したインスタンスが余計だった。

  fz_separations *seps = fz_new_separations(context_, 0);
  fz_pixmap* pix = fz_new_pixmap_with_data(context_, fz_device_rgb(context_), w, h, seps, alpha, stride, rgba);

上記「seps」は、使わない場合、必要無いようだ・・

  fz_pixmap* pix = fz_new_pixmap_with_data(context_, fz_device_rgb(context_), w, h, NULL, alpha, stride, rgba);

これで、普通に描画されるようになった。

まとめ

PDF を扱う実装は、RX マイコンのハードウェアーマニュアルから、色々と必要な情報を自動で取得する事を目指していて、その前段階。
その情報を使い、デバイス関係のソースコードを自動生成したり、テストコードを生成する事を考えている。

今回の実装は、その一歩となる。

詳細は、Qiitaに投稿してある。

pdfv アプリは、PDF をプレビューするアプリで、GUI を操作するメイン部は、俺俺フレームワークの助けを借りて 500 行くらいで実装出来ている。

Mupdf ライブラリ、便利!

RXマイコン CAN_sample

記事を Qiita に投稿

CAN_sample は、RXマイコン C++ フレームワークを使った、対話形式の CAN 通信サンプルです。

その解説を Qiita に投稿しました。

RXマイコン C++ フレームワークによる CAN 通信


今回のキモはやはり C++

boost::unordered_map、boost::unordered_set を利用しています。

std ライブラリにも同等のクラスはあるけど、boost の方がサイズが若干小さくなるので、CAN_sample では、boost 版で説明しています。

CAN 通信を行う場合、C++ を利用するとメリットが多いです。


GR-CITRUS を使ってみた

GR-CITRUS は、プロダクトとしては及第点に達していないけれでも、入手性と、コストで選びました。

  • GR-CITRUS は秋月電子で、処分価格(1500円)で販売されています。
  • GR-CITRUS に載っているRXマイコンは、Flash:2M、RAM:256K と豊富なので、大きなプログラムも動かせます。
  • GR-CITRUS で数少ないピンでも CAN0 を利用可能。

RXマイコンの品不足はいつまで続くのだろうか・・・


通信実験の相方は RX72T

以前、RX72M を購入する際、同時に購入して実験した RX72T/144 ピンボードを利用しました。


今後

RX72N Envision Kit の PMOD 端子に出ているポートにも、CAN バスをアサイン可能なので、RX72N でも試してみたい・・
それと、LCD もあるので、OBD2 を使って、車の ECU 情報を表示をしてみたい・・

CAN は意外と簡単に扱え、ノイズに強い高品質な通信が出来るので、複数のマイコンを使った装置など作る際に、相互の通信インターフェースとして重宝すると思われます。

RX631/RX63N 割り込みレベル設定の不具合など・・

CAN の実験で気がついた

GR-CITRUS はピンの制限が非道なのだが、CAN0 で利用するピン CRX0(P33), CTX0(P32) はとりあえず使える。
そこで、CAN_sample を動かしてみようと思い、プロジェクトに RX631 を追加してコンパイル。
とりあえず、ターミナル表示を確認した。

Start CAN sample for 'RX631 GR-CITRUS' 96[MHz]
CAN command version: 0.89
CAN0: SPEED: 1000000 [bps], BRP: 2, TSEG1: 15, TSEG2: 8, SJW: 4
    RX Interrupt level: 1, TX Interrupt level: 0
#

何故か、TX の割り込みレベルが0と表示されている・・
can_io クラスは、ポーリングでの動作は出来ない、必ず、1以上の割り込みレベルが必須となっていて、何も指定しないと、レベル1割り込みを設定する。
おかしいな・・、色々調べて、icu.hpp の CAN 関係の割り込みベクターに間違いがあった・・・
それを直して、もう一度。

Start CAN sample for 'RX631 GR-CITRUS' 96[MHz]
CAN command version: 0.89
CAN0: SPEED: 1000000 [bps], BRP: 2, TSEG1: 15, TSEG2: 8, SJW: 4
    RX Interrupt level: 0, TX Interrupt level: 0
#

今度は、両方「0」に・・・
調べると、RX631/RX63N の割り込みレベル設定(IPR レジスタ)は、かなりの部分が共有されており、この部分のケアが抜けていた。
RX621、RX62N や、RX63T、RX24T などでも同じだが、そちらはケアされていた。
RX621/RX62N に比べると、規則性があるが、全般に渡ってかなりの分量だったが、何とか修正した。

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  IPR レジスタ・クラス @n
                    全て、下位4ビットが有効
            @param[in]  base    ベースアドレス
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        template <uint32_t base>
        struct ipr_t {

            //-------------------------------------------------------------//
            /*!
                @brief  []オペレータ
                @param[in]  vec     標準割り込みベクター型
                @return IPR レジスターの参照
            */
            //-------------------------------------------------------------//
            volatile uint8_t& operator [] (VECTOR vec) {
                uint32_t idx = 0;
                switch(vec) {
                case VECTOR::BUSERR: idx = 0; break;
                case VECTOR::FIFERR: idx = 1; break;
                case VECTOR::FRDYI:  idx = 2; break;
                case VECTOR::SWINT:  idx = 3; break;
                case VECTOR::CMI0:   idx = 4; break;
                case VECTOR::CMI1:   idx = 5; break;
                case VECTOR::CMI2:   idx = 6; break;
                case VECTOR::CMI3:   idx = 7; break;
                case VECTOR::SPRI0:
                case VECTOR::SPTI0:
                case VECTOR::SPII0:
                    idx = static_cast<uint32_t>(VECTOR::SPRI0);
                    break;
                case VECTOR::SPRI1:
                case VECTOR::SPTI1:
                case VECTOR::SPII1:
                    idx = static_cast<uint32_t>(VECTOR::SPRI1);
                    break;
                case VECTOR::SPRI2:
                case VECTOR::SPTI2:
                case VECTOR::SPII2:
                    idx = static_cast<uint32_t>(VECTOR::SPRI2);
                    break;
                case VECTOR::RXF0:
                case VECTOR::TXF0:
                case VECTOR::RXM0:
                case VECTOR::TXM0:
                    idx = static_cast<uint32_t>(VECTOR::RXF0);
                    break;
                case VECTOR::RXF1:
                case VECTOR::TXF1:
                case VECTOR::RXM1:
                case VECTOR::TXM1:
                    idx = static_cast<uint32_t>(VECTOR::RXF1);
                    break;

...

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

※最近の RX マイコンは、ほぼ、割り込み要因毎に割り込みレベルが設定出来る仕様になっているのでスルーしていた。
※古い、RxV1 コアなどのマイコンは注意しなければならない・・


未定義ビットの扱い

システムクロックコントロールレジスタ(SCKCR)の予約ビットの扱いを見直した。

このレジスタ、B0~B7 には書き込む場合に特定のビットを立てなくてはならない。
素のままだと、特定のビットだけを変更する場合に問題が発生する。

    device::system::SCKCR.ICK = 10;

このような実装では、最終的に SCKCR レジスタ、B0~B7 には、読み出した値(全て0)が上書きされる。

そこで、以下のように、書き込み時に、特定のビットフィールドを追加するように改修した。

        //-----------------------------------------------------------------//
        /*!
            @brief  システムクロックコントロールレジスタ(SCKCR)
            @param[in]  base    ベースアドレス
        */
        //-----------------------------------------------------------------//
        template <uint32_t base>
        struct sckcr_t : public rw32_t<base, 0b0001'0001> {
            typedef rw32_t<base, 0b0001'0001> io_;
            using io_::operator =;
            using io_::operator ();
            using io_::operator |=;
            using io_::operator &=;

            bits_rw_t<io_, bitpos::B8,  4> PCKB;
            bits_rw_t<io_, bitpos::B12, 4> PCKA;
            bits_rw_t<io_, bitpos::B16, 4> BCK;

            bit_rw_t <io_, bitpos::B22>    PSTOP0;
            bit_rw_t <io_, bitpos::B23>    PSTOP1;
            bits_rw_t<io_, bitpos::B24, 4> ICK;
            bits_rw_t<io_, bitpos::B28, 4> FCK;
        };
        typedef sckcr_t<0x0008'0020> SCKCR_;
        static SCKCR_ SCKCR;

rw32_t クラスは、以下のように変更。

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    /*!
        @brief  Read/Write 32 bits アクセス・テンプレート
        @param[in]  adr アドレス
        @param[in]  wov 書き込み OR 値(通常0)
    */
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    template <address_type adr, uint32_t wov = 0>
    struct rw32_t {

        typedef uint32_t value_type;

        static constexpr auto address = adr;    ///< アドレス定義
        static constexpr auto write_or_value = wov; ///< 書き込み OR 値
        static constexpr uint8_t BUS = 32;      ///< バス幅
        static constexpr bool RD = true;        ///< 読出し
        static constexpr bool WR = true;        ///< 書き込み

        //-----------------------------------------------------------------//
        /*!
            @brief  書き込み
            @param[in]  data    書き込み値
        */
        //-----------------------------------------------------------------//
        static void write(value_type data) noexcept { wr32_(adr, data | wov); }

...

    };

テンプレートパラメーター、wov は、何も指定しない場合、「0」なので、影響を与えない。
書き込み時に「0」を OR するのが余計だと思うかもしれないが、最適化されると、その操作は削除されてて無くなるので通常動作で余分なマシンサイクルを消費しない。
最適化しない場合、余分なコードが大量に生成されてしまうので、専用関数とするべきかもしれない・・


まとめ

実装時、かなり注意しているが、間違いを避ける事は難しい、集中している時は、それも少ないとは言え、何かの別作業が間に入ったりすると、凡ミスも多くなる。
少し前から、ペリフェラルの定義を検証する為の仕組みを入れ始めている。
重要な部分はユニットテストなどを行うしかないが、テストコードを生成する、良い方法を考えないと現実的では無いとも思う。
これは、すこし考えたい・・
ハードウェアーマニュアルから必要な部分を抽出して、テストコードを自動生成するような事が必要かもしれない・・

RXマイコン、RX631/RX63N をサポート開始

最終的にはRXマイコン全サポートしたい!

かなり昔、RX63N が出た時期、176ピン版を購入した。(今は販売していない)

その時、変換基板も同時に購入して、実験基板を作ろうとしていたが、ピンの配置間隔がハーフピッチずれたバグ基板を掴まされ、作る気力が萎えてしまった・・
※ハーフピッチずれている為、ユニバーサル基板に乗せる事が出来ない。

そうこうしているうちに RX64M が出て、それを購入、品質の高い変換基板も購入、実験基板を制作して動かすまでになった。
そんなこんなで、RX631/RX63N はサポートをしていなかった。

デバイスは持っているので、実験しないと肥やしになるだけだし、ペリフェラル定義も、共通部分が多い事から RX220 サポートの勢いで進めていた。
必要最低限のピンを接続して、とりあえず動作する状態にしたい。

RXマイコンも、未だ購入が出来ない状態が続いているので、シリーズを増やすにしても、何とも歯がゆい状態が続いている。


GR-CITRUS を買う

基板を作る準備をしていたが、面倒になり、停滞していた。

そんな時、秋月のRXマイコン関係で、GR-CITRUS が1500円(通常2200円)の処分価格で売られている事を発見し、他の部品と共に注文した。
※使っていないピンを出していないとか、かなり不満の残るボードだが、1500円ならまーいいかと思って購入。

GR-CITRUS は、標準では USB 接続でフラッシュを書き換えるようになっている。
※MD端子は出ているが、2.54mm ピッチでは無い、狭いフットプリントになっているのが駄目・・(何で?)

RX631/RX63N は、フラッシュプログラム「rxprog」もサポートしたいので、外部にSCI1、MD 端子などを出してシリアルブート出来るように改造した。

RX631/RX63N は RX63T に近いプログラミングフォーマットとなっており、簡単に実装出来た。
マイクロマウスでRX631を使っている人が、rxprog を改造して使っているようだが、最新バージョンではコンパイルするだけで利用出来ると思う。

プログラムの書き込みは何の問題もなく出来た。

GR-CITRUS でシリアル書き込みを行う場合、P30(RXD1)、P26(TXD1) を配線する必要がある。
又、PC7 を抵抗でプルダウンする必要がある。
P26 は J5 によって、P05/DA1 ピンと共有するのだが、そうすると P05 を使えなくなる。
なので、J5 の P26 側にだけ配線を行い、外部に出す。
折角 100 ピンの RX631 を実装しているのに、多くのピンが未接続だし、ピンを共有していたり(何かの互換の為?)、ハッキリ言って糞ボードだと思う。
以上の点を許容できる人のみ購入を検討してみるのが賢明と思う。


クロックのブーストでハマる

RX631/RX63N は RX63T に近いクロック機能なので、RX63T のコードをコピーして少し手直しした。

とりあえず、LED 点滅「FIRST_sample」を書き込んだが、点滅しない・・・

調べると、クロック速度が遅い状態で、先に進まないようだ。
RX63T では動いていたから、何か違いがあるのか、色々調べたり、クロック関係のレジスタに直で値を入れたり、色々やったが全く駄目・・・
高速内部発振器「HOCO」では、ちゃんと動作しているようで、何で???と、数時間悩んだ・・・

そして、ようやく謎が解けたー

システムクロックコントロールレジスタ(SCKCR)の予約ビットに「0b0001」を書き込む必要があり、それが「0b0000」だと、ICLK が正しい周波数にならない・・

読み出すと「0b0000」なので、何も対策しないとハマる・・・

            device::SYSTEM::SCKCR = device::SYSTEM::SCKCR.FCK.b(clock_div_(clock_profile::FCLK))
                                  | device::SYSTEM::SCKCR.ICK.b(clock_div_(clock_profile::ICLK))
                                  | device::SYSTEM::SCKCR.BCK.b(clock_div_(clock_profile::BCLK))
                                  | device::SYSTEM::SCKCR.PCKA.b(clock_div_(clock_profile::PCLKA))
                                  | device::SYSTEM::SCKCR.PCKB.b(clock_div_(clock_profile::PCLKB))
                                  | 0b0001'0001;

この修正を加えたら、システムクロックがブーストするようになった。

RX631 のソフトディレイを調整した。


現在のフレームワーク状態

現在、ペリフェラルの実装、動作確認、は以下のようになっている。

シリーズ コア FPU DFPU 動作確認 ペリフェラルクラス rx_prog サポート リンカーファイル
RX220 RXv1 No - R5F52206
RX631/RX63N RXv1 Yes - R5F5631F/NE
RX63T RXv1 Yes - R5F563T6
RX621/RX62N RXv1 Yes - R5F562N7/8
RX24T RXv2 Yes - R5F524T8/A
RX64M RXv2 Yes - R5F564MF/G/J/L
RX71M RXv2 Yes - R5F571MF/G/J/L
RX651/RX65N RXv2 Yes - R5F565NE
RX66T RXv3 Yes - R5F566TA/E/F/K
RX72T RXv3 Yes - R5F572TF/K
RX72N RXv3 Yes Yes R5F572ND/N
RX72M RXv3 Yes Yes R5F572MD/N

RX631/RX63N は、RX63T にフラッシュプログラムは近いので、rx_prog もサポートした。


まとめ

とりあえず、FIRST_sample、SCI_sample、RAYTRACER_sample などで動作確認を行った。

SCI_sample:

Start SCI (UART) sample for 'RX631 GR-CITRUS' 96[MHz]
SCI PCLK: 48000000 [Hz]
SCI Baud rate (set): 115200 [BPS]
  Baud rate (real): 115384 (0.16 [%])
  SEMR_BRME: false
  SEMR_BGDM: false
CMT Timer (set):  100 [Hz]
  Timer (real): 100 [Hz] (0.00 [%])
#

RAYTRACER_sample:

RX631 GR-CITRUS Start for Ray Trace: 320, 240
# Render time: 1886ms (1)

CALC_sample:(関数電卓サンプル)

# Rad
# tan(355/226)
 -7497258.185325587112905071831891248663417267943785263161571

CAN_sample:

  • コンパイルのみ、動作確認はしていない。

I2C_sample:

  • コンパイルのみ、動作確認はしていない。

PSG_sample:

  • DA1 出力をアンプに接続して、動作確認。

「rxprog」は、RX631/RX63N を追加して、プログレス表示で、%の表示を追加した。

GR-CITRUS は、メモリもそれなりに多く、今後、他プロジェクトでも、RX631 を追加して、動作確認をおこなっていく。
惜しいのは、使っていないピンをランドに出していない点、そして、一部のポート同士を接続してある点、これが無ければ、色々応用が出来るのだが・・

現状のコードは、Github にコミットしてあり、マスターブランチにマージ済。

RXマイコンC++フレームワーク、RX220をサポート開始

RX220 マイコンをサポートする

半導体不足は未だ解消の気配は無く、RXマイコンを買うにしても、以前のような価格では流通していないし、バックオーダーで、買う事も出来ない。

RX220は、機能と価格で、CPはそんなに良くないが、秋月さんで売っているので、入手はしやすいだろうと思う。

  • DIPスイッチを逆に取り付けてしまったバージョンの基板なら比較的安価に購入できる。(1250円)
  • 以前RX72T(200MHz、RXv3コア)が10個で@600円くらいだった事を考えると、最大32MHzでRXv1コアはあまり触手が伸びない。
  • RX220はFPUも内蔵していない・・
  • 既に購入済で、積みボードになっている人もいるかもしれない。

とりあえず、RXマイコン全制覇を目論んでいるし、200番台をサポートするのは、それなりに価値がある。


秋月RX220ボードの改造

秋月さんのRX220ボードは、そのままでは多少使いづらい・・
このボードでは、内蔵フラッシュを書き換えるには、シリアル書き込みがほぼ標準的となる。
その場合、SCI1を使う事になるが、そのままだと、RSー232Cのレベル変換ICに接続されている。
今や、PCとの通信でRSー232Cを使う事は「稀」で、FT232やCP2102NなどのUSB/シリアル変換モジュールを使って、ロジックレベルで行うのが標準的となっている。
そうすると、RXマイコンのTxDは出力なので問題無いとしても、Rxdは入力となっていて、レベル変換ICの出力が接続されているので、モジュールのロジック端子を繋ぐ事ができない。
そこで、ボードのパターンをカットして、接続できるように改造した。
※何かの記事で読んだが、この「カットをする意味が判らない」と綴っている人がいた・・、これは、本当にビックリした、その程度の知識でマイコンをやってる人がいる現実に驚くばかり。
まぁ、でもマイコン掲示板で、自分で何も調べないで、いきなり質問する人や、初歩的と思えるような質問をする人、様々な人がいる現実を考えると、まぁそんなものかもしれない。


ソフトディレイの調整

RX220は最大32MHz(内蔵発振器動作)で動作する。

  • クロックジェネレーターにはPLL回路が無い為、外部にクリスタルを接続して利用する場合は、最大20MHzとなる。
  • クリスタルの発信器は、20MHzを超えると、ちょっと特殊な回路にしないと発振が安定しない(発振しない)ので、そうなっていると思う。
  • ただ、外部クロック入力でも最大20MHzなので、その他の物理的な要因が関係しているのかもしれない。
  • 内部発信器だと、正確性やジッタの問題があるとも思うが、意外と安定していると思う。
  • 内部発信器は最大50MHzで発振する事も出来るので、50MHzオーバークロックで動かしてみたが、普通に動作はするようだ。

最初、1マイクロ秒のソフト遅延を行う為、NOP命令のループ回数を調整していたが、思うように調整出来ない・・・
ある値を境に、遅くなったり、速くなったり、不思議な現象が発生した。

                for(uint32_t n = 0; n < device::clock_profile::DELAY_MS; ++n) {
                    asm("nop");
                }

・・・

少し考えたら、理由は判った、今まで、最低速度は、RX24Tの80MHzだったので、ループ回数はそれなりの値で問題無かった。
しかし、32MHzだと、ループ回数は少なく、ある値を境に、「asm("nop");」が、インライン展開されてしまう。

そこで、RX220の場合は、専用のAPIにした。

        static void delay_us() noexcept
        {
            // 定数(DELAY_MS)で指定すると、ループ数が少なくインライン展開され、予想した時間にならない為、直で’NOP’数で調整する。
            if(ICLK == 32'000'000) {  // 32MHz 用
                asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
                asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
                asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
                asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
            } else if(ICLK == 20'000'000) { // 20MHz 用
                asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
                asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop"); asm("nop");
                asm("nop"); asm("nop"); asm("nop");
            } else {
                for(volatile uint8_t i = 0; i < (ICLK / 1'000'000) - 10; ++i) {
                    asm("nop");
                }
            }
        }

rx_prog でも、RX220をサポート

最初、とりあえずルネサスフラッシュプログラマーで書き込みを行ったが、非常に不安定で、書き込みに失敗、そもそも接続も出来ないなどのトラブルが多発した。
この原因は追究していないが、USBシリアルデバイスがCP2102Nなのが問題なのかと思った。

  • CP2102Nは、FTDIより高速で、同じボーレートでも、転送にかかる遅延が非常に少ない為と思った。
  • RX200系は、RXマイコン初期の物なので、あまり枯れていないのかもと思った。
  • ルネサスフラッシュプログラマーの無料版は、ノークレームなので、改修を要求する事さえ出来ない。

そこで、まず、rx_prog にRX220のプロトコルを追加する事にした。
ブートモードの説明を斜め読みした感じでは、RX63Tとほぼ同じ感じで、そのまま行けると感じた。

「rx_prog.conf」に、RX220 デバイスのプロファイルを追加して、「rx220_protocol.hpp」を追加、説明と照らし合わせた。

R5F52206 {
    group = "RX220"
    clock = 3200
    divide_sys = 1
    divide_ext = 1
    rom = 256K
    ram = 16K
    data = 8K
    comment = "; RX220 Flash:256K DataFlash:8K RAM:16K"
    rom-area  = FFFC0000,FFFFFFFF
    data-area = 00100000,00101FFF
    ram-area  = 00000000,00003FFF
}

「rx63t_protocol.hpp」ほぼ同じでOKだったが、ファイルは別けておいた。

まず、コネクションを試してみた。

 % rx_prog -d RX220 --verbose
# Platform: 'Cygwin(MSYS2)'
# Configuration file path: '/usr/local/bin/rx_prog.conf'
# Group: 'RX220'
# Serial port path: 'COM7'
# Serial port speed: 230400
# Erase Page Wait: 2000 [uS]
# Write Page Wait: 5000 [uS]
# Serial port alias: COM7 ---> /dev/ttyS6
# Serial port path: '/dev/ttyS6'
# Device: R5F52206 (first find)
#   Master clock: 32.00 MHz
#   ICLK multiplier: 1 (32.00 MHz)
#   PCLK multiplier: 1 (32.00 MHz)
Connection OK. (RX62x)
#01/01: Device: RX200 Series (LittleEndian)
#01/01: Device ID: 0x33306A37
#01/01: Clock Mode: 0x00
#01/02: Multiplier: 1
#02/02: Multiplier: 1
#01/02: Frequency Min: 32 MHz, Max: 32 MHz
#02/02: Frequency Min: 32 MHz, Max: 32 MHz
#01/01: Change baud rate: 115200
#01/01: Boot Area: FF7FC000, FF7FFFFF
#01/01: Area: FFFC0000, FFFFFFFF
#01/26: Area: FFFFF000, FFFFFFFF
#02/26: Area: FFFFE000, FFFFEFFF
#03/26: Area: FFFFD000, FFFFDFFF
#04/26: Area: FFFFC000, FFFFCFFF
#05/26: Area: FFFFB000, FFFFBFFF
#06/26: Area: FFFFA000, FFFFAFFF
#07/26: Area: FFFF9000, FFFF9FFF
#08/26: Area: FFFF8000, FFFF8FFF
#09/26: Area: FFFF4000, FFFF7FFF
#10/26: Area: FFFF0000, FFFF3FFF
#11/26: Area: FFFEC000, FFFEFFFF
#12/26: Area: FFFE8000, FFFEBFFF
#13/26: Area: FFFE4000, FFFE7FFF
#14/26: Area: FFFE0000, FFFE3FFF
#15/26: Area: FFFDC000, FFFDFFFF
#16/26: Area: FFFD8000, FFFDBFFF
#17/26: Area: FFFD4000, FFFD7FFF
#18/26: Area: FFFD0000, FFFD3FFF
#19/26: Area: FFFCC000, FFFCFFFF
#20/26: Area: FFFC8000, FFFCBFFF
#21/26: Area: FFFC4000, FFFC7FFF
#22/26: Area: FFFC0000, FFFC3FFF
#23/26: Area: 00101800, 00101FFF
#24/26: Area: 00101000, 001017FF
#25/26: Area: 00100800, 00100FFF
#26/26: Area: 00100000, 001007FF
#01/01: Program size: 256 Bytes
#01/01: ID Protect: false

問題無い。

次に、書き込み、ベリファイも試して、問題無かった。
rx_prog は、多少書き込み速度が遅いものの、安定して書き込めるし、OS-X、Linux でも同じように動作する実績がある。
※RX220 は、OS-X、Linux では試していないけど・・

rx_prog -d RX220 --progress  --write --verify sci_sample.mot
Write:  #################################################
Verify: #################################################

これで、RX220を正式サポートした事になると思う。

Makefile は、以下の部分のみ異なるだけで良く、RX220 に移る事は簡単だ。

DEVICE      =   R5F52206

RX_DEF      =   SIG_RX220

まとめ

現状、サンプルプログラムは、「FIRST_sample」、「SCI_sample」のみだが、今後拡充してゆく予定。
※基本、各プロジェクトで、Makefileを用意すればOKだが、まだ、RSPIや、RIIC(I2C)ペリフェラルの追加などをしていない。

RX220 は浮動小数点を扱うとエミュレーションになってしまうが、「utils::format」の表示関係は、内部はほぼ整数を使っているし、速度以外はそれなりに使えると思う。
もう少し安価に入手できれば・・・
それでも、32ビットなので、RL78などの8ビット系に比べて大きなアドバンテージがあるのは言うまでも無い。

Start SCI (UART) sample for 'AE-RX220' 32[MHz]
SCI PCLK: 32000000 [Hz]
SCI Baud rate (set): 115200 [BPS]
  Baud rate (real): 117647 (2.08 [%])
  SEMR_BRME: false
  SEMR_BGDM: false
CMT Timer (set):  100 [Hz]
  Timer (real): 100 [Hz] (0.00 [%])
#

現在、RX631/RX63N もサポート中で、秋月ユーザーでも、C++ フレームワークを使ってみる機会がそれなりに増えると思う。

C++ に対する嫌悪感

主にC言語しか使わない人は、C++ に対する嫌悪感が半端ない事が多い。
また、C++ は判らないし、難しそうだから近寄らない人も多い。

しかしながら、マイコン初心者(プログラムの経験が全く無い人)に教える場合、C++ から始めても、そんなに敬遠したりしない事が判っている。
※もちろん、最初はあまり多くを示さないで、簡単な事、理解しやすい事、シンプルな事から始める。
むしろ、半端にプログラム出来る人より、飲み込みが速く、応用が利く人が多いように思う、これは、教え方次第のように思う。
年齢は関係無い、若い人は頭が柔らかく、吸収する力が大きいように感じる。

C++だと、コンパイラが教えてくれる事が多く、小さなミスは、ほぼ起きなくなるし、柔軟で応用範囲が広いライブラリなど、利便性が高い。

今回 RX220 をサポートしたが、アプリケーションレベルでは変更が全くなく、同じように動作する。

今後、勉強会や、講習会、YouTubeビデオなど、学べる機会や媒体を増やしていく予定でいる。

RXマイコン、C++、sci_io クラスの機能追加(RS-485)

RS-485

RS-485 は、非同期シリアルに RS-485 用ドライバを付け、若干のソフトを追加するだけで、利用出来る。
ドライバーは、差動信号を使い、ノイズに強く、長い通信路でも、信号の品質を維持する事が出来る。
ネットワークのトポロジーとして、CAN のような構成にできて、複数の機器を相互に接続出来る。
詳しくは、RS-485 の解説を参照してもらいたい。

但し、半二重通信なので、送信を行うには、送信ゲートを適切にコントロールする必要がある。
また、プロトコルを実装して、クライアントと通信するので、全体の構成を考える必要がある。
受信だけなら、流れてくるパケットをデコードするだけなので簡単だが、送信するには、受信データを監視して、タイミングを守る必要がある。
プロトコルは規定されていないが、PLC、インバーターなどで良く使われるものがあるようだ。

今回は、三相モーターのインバーターと通信するのがとりあえずのゴール。


sci_io クラスに機能を追加

sci_io クラスは、SCI を使った非同期シリアル通信を扱うクラスとなっている。

今回、RS-485 の送信ゲート制御を追加した。

一つのクラスに、あまり多くの機能を盛り込むのは、設計上別の問題もあるが、色々検討した結果、少しの機能追加で行える事が判ったので、機能追加とした。

RS-485 のゲート制御は、テンプレートパラメータで決定する為、通常の SCI 通信では、余分なコードが残らないように配慮してある。


機能を切り替える仕組み

定義は以下のようなもので、通常の SCI 定義を拡張したものとなっている。
ハードフロー制御(RTS)と共用となっているので、enum class の FLOW_CTRL 型に、RS485 を含めてあり、テンプレートパラメータで指示する。

    typedef utils::fixed_fifo<char, 1024> RS485_RXB;  // RS-485/RX (受信) バッファの定義
    typedef utils::fixed_fifo<char, 512>  RS485_TXB;  // RS-485/TX (送信) バッファの定義

    typedef device::PORT<device::PORT3, device::bitpos::B3> RS485_DE;   // for MAX3485 DE
    typedef device::sci_io<RS485_CH, RS485_RXB, RS485_TXB, device::port_map::ORDER::SECOND, device::sci_io_base::FLOW_CTRL::RS485, RS485_DE> RS485;
    RS485   rs485_;
  • RS-485 ドライバーの DE(送信ゲート)を制御する為、ポート定義を同時に行う。
  • RS-485 ドライバーの受信ゲートは、常に有効にしてある。
  • RS-485 ドライバは、ループバックしているので、送信した文字列をそのまま受信する。
  • 送信した文字列と受信した文字列が等しければ、衝突は発生せず、正しく送信出来た事を保障する事が出来る。
  • RS-485 ドライバーは、MAX3485 を使っている。(3.3V 動作品)

DE ポートの制御

  • RS-485 の送信ゲート制御は、送信データ列が出ている間だけ、送信ゲートを有効にする必要がある。
  • RX マイコンの SCI では、シリアル送信中のデータ列が出ている期間を知る手法はいくつかあるのだが、送信データがバッファリングされている為、単純な方法では、うまくいかない。
  • 送信割り込みは、送信が可能になったタイミングで発生する。
  • ストップビットを含めたシリアルデータが全て送られたタイミングで「送信終了割り込み」が発生する。
  • 色々検討して実験した結果、送信開始前に、ゲートを有効にして、送信 FIFO が「空」になったら、送信終了割り込みを発生させ、そのタイミングでゲートを閉じるようにした。


9600bps


送信終了割り込み(TEI)のハンドリング

送信終了割り込み(TEI)は多少厄介で、一工夫が必要となっている。
それは、マイコンによって、通常割り込みの場合と、グループ割り込みの場合がある為で、それを正しく管理する必要がある。

  • RX621/RX62N/RX63T/RX24T では通常割り込み。
  • RX64M/RX71M/RX65N/RX72N/RX66T/RX72T ではグループ割り込み。
        static inline void tei_task_()
        {
            if(send_.length() == 0) {
                RTS::P = 0;
            }
            SCI::SCR.TEIE = 0;
        }

        static INTERRUPT_FUNC void tei_itask_()
        {
            tei_task_();
        }

        void set_intr_(uint8_t level) noexcept
        {
            if(level > 0) {
                icu_mgr::set_interrupt(SCI::RXI, rxi_task_, level);
                icu_mgr::set_interrupt(SCI::TXI, txi_task_, level);
                if(FLCT == FLOW_CTRL::RS485) {
                    auto gv = icu_mgr::get_group_vector(SCI::TEI);
                    if(gv == ICU::VECTOR::NONE) {
                        icu_mgr::set_interrupt(SCI::TEI, tei_itask_, level);
                    } else {
                        icu_mgr::set_interrupt(SCI::TEI, tei_task_, level);
                    }
                }
            } else {
                icu_mgr::set_interrupt(SCI::RXI, nullptr, level);
                icu_mgr::set_interrupt(SCI::TXI, nullptr, level);
                if(FLCT == FLOW_CTRL::RS485) {
                    icu_mgr::set_interrupt(SCI::TEI, nullptr, level);
                }
            }
        }

現在、RX マイコン C++ フレームワークでは、グループ割り込みは、標準のディスパッチルーチンから呼ばれるので、割り込みタスクの属性を適切に設定する必要がある。

SCI::TEI(送信終了割り込み)がグループベクターの場合は、通常のタスクを登録し、グループベクターでは無い場合には、「INTERRUPT_FUNC」属性を付与した関数を登録する。
「INTERRUPT_FUNC」属性では、「RTE」オペコードで終了する。(通常関数は「RTS」オペコード)

RX24T の場合、「TEI」は通常ベクターで、グループでは無いので、「VECTOR::NONE」が返る。

        static ICU::VECTOR get_group_vector(ICU::VECTOR vec) noexcept {
            return ICU::VECTOR::NONE;
        }

RX72T の場合、「TEI」は「VECTOR_AL0」なので、「VECTOR::GROUPAL0」が返る。

        static ICU::VECTOR get_group_vector(ICU::VECTOR_AL0 vec) noexcept {
            return ICU::VECTOR::GROUPAL0;
        }

「icu_mgr::get_group_vector(SCI::TEI);」で、「SCI::TEI」がグループベクターなのか、そうでないのか判定出来る。
C++ では「型」の違いで、コンパイラが自動で関数を呼び分ける事ができ、処理を切り替える事が可能なので、柔軟性が大きい。
この判定は、定数が返るだけなので、最適化がされると、分岐や判定が綺麗に消えて、無駄なコードが残らない為、余分なメモリとマシンサイクルを節約する。
このような精妙な仕組みがスマートに実装出来るのは C++ の特徴となっている。
このような仕組みはC言語では難しい。


グループベクターのハンドリング

自分のフレームワークでは、割り込みなどは登録制にしてあり、グループベクターも登録制となっている。

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  GROUPAL0・ベクター型
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        enum class VECTOR_AL0 : uint8_t {
            TEI11 = 12,     ///< SCI11 / TEI11
            ERI11,          ///< SCI11 / ERI11

            SPII0 = 16,     ///< RSPI0 / SPII0
            SPEI0,          ///< RSPI0 / SPEI0
            NUM_ = 4
        };

SCI11 の送信終了割り込み「TEI11」は「グループベクター AL0」のグループで、上記番号は、レジスターのビット位置となっている。
グループ割り込みが発生した場合に該当する処理を呼び分けるディスパッチルーチンはなるべく高速に実行したいので、割り込みタスク登録時に必要なデータを登録してある。

        typedef icu_utils::dispatch<ICU::VECTOR_AL0> GROUPAL0_dispatch_t;
        static GROUPAL0_dispatch_t GROUPAL0_dispatch_;

        template<typename GRPV>
        class dispatch {
...
            void set_task(GRPV grpv, GTASK task) noexcept
            {
                uint32_t bits = 1 << static_cast<uint32_t>(grpv);
                for(uint32_t i = 0; i < NUM; ++i) {
                    if(bits_[i] == bits) {
                        bits_[i] = 0;
                        task_[i] = nullptr;
                        break;
                    }
                }
                for(uint32_t i = 0; i < NUM; ++i) {
                    if(bits_[i] == 0) {
                        bits_[i] = bits;
                        task_[i] = task;
                        break;
                    }
                }
            }

            void run(uint32_t togo) const noexcept
            {
                for(uint32_t i = 0; i < NUM; ++i) {
                    if(bits_[i] == 0) break;
                    if((bits_[i] & togo) != 0 && task_[i] != nullptr) {
                        (*task_[i])();
                    }
                }
            }
        };

        static INTERRUPT_FUNC void group_al0_handler_() noexcept
        {
            GROUPAL0_dispatch_.run(ICU::GRPAL0());
        }

        static void install_group_task(ICU::VECTOR_AL0 grpv, icu_utils::GTASK task) noexcept
        {
            ICU::GENAL0.set(grpv, false);
            set_task(get_group_vector(grpv), group_al0_handler_);
            GROUPAL0_dispatch_.set_task(grpv, task);
            if(task != nullptr) {
                ICU::GENAL0.set(grpv);
            }
        }
  • dispatch::run(togo) は割り込み要因 GRPAL0 レジスタを引数に呼ばれる。
  • 登録されたデータに従い、タスクを呼び出す。
  • この辺り、部分的な実装を抜き出しているので雰囲気だけなので、実際は、github のコードを観て下さい。

RX72T 関係
icu_utils 関係


まとめ

  • 現在サポートしているRXマイコンは10種程度に及ぶので、良く考えて実装しないと、修正が大掛かりになる場合もある。
  • 同じような機能を提供するクラスや関数をなるべく共有できるようにしないと、全ての品種で恩恵が薄くなる。

C++ だと、テンプレートや、C++11、C++14、C++17 で備わった機能など色々使って、かなりシンプルに実装が出来る、速度も速い!
テンプレートの場合、コンパイルがエラー無く通ると、大体ちゃんと動く場合が多い。
そして、ほとんどが、ヘッダーのみに全て実装出来る、なんて便利なんだろうといつも思う、利用する人が増えれば良いのにと思う。