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 にコミットしてあり、マスターブランチにマージ済。