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」で定義されたシンボル名が優先されるので、割り込みエントリーが
空のままとなる、そして、コンパイル、リンクはエラー無く終了するので、こ
の手のミスが明るみになる可能性はかなり低い・・・
これは、課題とする。

R8C(M120AN)を使った、MAX6675のテスト

最近は、RXマイコンが多くて、R8Cはご無沙汰だった。

R8Cは、ちょっとしたデバイスの試験なら、気軽にできる。

以前に、オーブントースターを温度制御したくて、買っておいた、
k熱電対センサーと、モジュールを使って、温度を表示してみた。
MAX6675/Kタイプ 熱電対

MAX6675は、アンプとA/Dコンバーターが一体になった
ICで、出力はSPI、電源も3V~5Vまで使え、0.25℃
の分解能、0℃~1024℃まで計測でき、便利なICだ。

プログラムは、ICからデータを読み出して、表示するだけの物
だが、チップ・テンプレート・クラスに新たに、「MAX6675.hpp」
を追加した。

MAX6675 は、初期化の必要も無く、単にデータを読み出すだけの
ものだ。

サンプルでは、以下のように定義した。
※MAX6675 の SPI は、出力(MOSI)は使わないので「NULL_PORT」と
している。

// P1_0(20):
typedef device::PORT<device::PORT1, device::bitpos::B0> SPI_SCK;
// P1_1(19):
typedef device::PORT<device::PORT1, device::bitpos::B1> MAX_CS;
// P1_2(18):
typedef device::PORT<device::PORT1, device::bitpos::B2> SPI_SDI;

typedef device::spi_io<SPI_SCK, device::NULL_PORT, SPI_SDI> SPI;
SPI     spi_;

typedef chip::MAX6675<SPI, MAX_CS> MAX6675;
MAX6675	max6675_(spi_);

初期化は、以下のようにすれば良い。

    // SPI 開始
    spi_.start();

    // MAX6675 開始
    max6675_.start();

温度表示:

    auto v = max6675_.get_temp();
    utils::format("%6.3f\n") % v;

※当然ながら、RXマイコンでも同じように使える。

GitHub にサンプルプロジェクトを追加した。
MAX6675サンプル

RXマイコン、C++、割り込み対応の文字出力

ネットスタック作りは、未だ実装中、もうしばらくかかる・・・
意外と奥が深い、色々考えると、タイミングや応答の順番、イレギュラーな場合
など、色々考慮する必要があり、実験しながらなので、中々決まらない。
それでも、少しづつは進んでいる。

その過程で、イーサーネットの割り込みタスク(EDMAC)内から、文字出力
を行う事が多くなってきたが、文字出力は、排他制御されていないので、メイン
ループの出力と衝突すると、厄介な事になる(文字出力がハングアップする)そ
こで、どうにかできないか考えてみた。

void sci_putch(char ch)
{
    static volatile bool lock = false;
    static utils::fifo<uint8_t, 1024> tmp;
    if(lock) {
        if((tmp.size() - tmp.length()) >= 2) {
            tmp.put(ch);
        }
        return;
    }
    lock = true;
    while(tmp.length() > 0) {
        sci_.putch(tmp.get());
    }
    sci_.putch(ch);
    lock = false;
}

とりあえず、ロック機構を使ってリソースを包んで、ロック状態の場合は、一旦
バッファ(FIFOテンプレートクラス)に貯める。
ロックが外れたら、バッファに残っていた文字を取り出し、表示する。
※この取り出すタイミングは、多少問題で、文字出力が無いと、バッファに残っ
た文字が永遠に出力されない。
バッファに残った文字は、メイン部分で、タイマーの同期ループなどで出力する
ようにすればOKと思う。

RXマイコン、C++、新たなネットスタックの実装(UDP通信)

やっと、UDP通信が出来た。

まだ、不完全ではあると思うが、簡単な送受信が成功した。

作ってみて判るのは、やはり、ネットスタックの実装は、C++に良くマッチ
すると思う事。

・構造体やクラスは、処理系にとって都合の良いように、アライメントされる
が、パケットの中身は、処理系のアラインサイズとは無関係な場合もある為、
随所に「__attribute__((__packed__))」を使って、「余り」が出ないように
してある、これは、gcc 特有のキーワードなので、コンパイラーが違うと、エ
ラーになると思う。
・内部で扱うバッファは、固定サイズにしてあり、アロケーターを必要としない
ようにした。(テンプレートのパラメーターとしてある)
※これが、良いか悪いか、判断の分かれる部分ではあるけど、組み込みでは、
少メモリーに対応する必要と、システムのアロケーターが使えない場合も考慮
する必要性もあるので、そのような実装にしてある。
・UDP/TCP の経路を開始した時に必要なバッファを動的に確保しないと、スタ
テックなメモリーは設定した経路数の最大値によって、多く消費してしまうが、
この辺りの管理は、ユーザーの設定(テンプレートパラメーター)にまかせる
事としている。
・また、メモリー管理を独自に実装する事も考えられるが、現実として、独自
のアロケーターを実装しても、安定して動作するまでは、かなり時間のかかる
困難な作業ではあるし、「DL malloc」(通称)を超える事は、ほとんど不可能
に近い事を考えると、これは後回しにすべき問題と思える。
※殆どのシステムには、高性能な「DL malloc」か、又は、修正版が実装されて
いるにも拘らず、独自に実装している「例」を多く見かけるが、一度、システム
のアロケーターとの違い(勝負)を厳密に行うべきで、容易には、様々な面で、
超える事は出来ないと思われる。(記憶割り当ての効率、速度、断片化割合)

割り込み内での処理と、通常の処理を、分離してあり、コンテキストのオーバー
ラップを起こさないように工夫してある。

また、マルチタスクでは無く、シングルタスクをポリシーとして実装してあり、
処理が集中しないように工夫してある。
※大きなバッファの処理は、ある程度は仕方無いが・・・
※各クラスには、サービスルーチンが用意してあり、10ms単位で呼ぶよう
にするが、必ず、割り込み外から呼ぶようにデザインしてある。

—–
現状では、RX64Mや、GR-KAEDEで動作する。
※イーサーネットの物理層(PHY)ハードウェアーとの接続は、RMIIで
リンクアップはポーリングで行っている。

ソースコードは、GitHub net2で公開している。

UDP通信の簡易テストUDP

※MITライセンス