RXマイコン、C++、std::function クラスの謎

FTPサーバーの次は、HTTPサーバーも実装しているが、まだ機能が不足
している、順次機能追加していく。

登録タスクの実行としくみ:
組み込みマイコンで動かすサーバーでは、固定されたページを単に送るだけで
は無く、状況により動的に生成した構文を送る事ができる。
その際、リンクパスと、関連する実行関数を登録出来ると、非常に利便性が高
い。

C++11 から導入された、「std::function」テンプレートは、関数ポインタでも、
ラムダ式でも、クラスのメンバーでも何でも登録できる、便利な物だが、何らか
の要因により、コンパイル、リンク後の使用メモリーが巨大になる現象が発生し
ている・・・

#include <functional>

typedef std::function< void () > http_task_type;

http_task_type	task_;

void aaa()
{
    if(task_) {
        task_();
    }
}

※関数呼び出しが1箇所の場合

   text    data     bss     dec     hex
 115676   71804   79388  266868   41274
void aaa()
{
    if(task_) {
        task_();
    }
}

void bbb()
{
    if(task_) {
        task_();
    }
}

※関数呼び出しが2箇所になった場合

   text    data     bss     dec     hex
 515860  106844   84148  706852   ac924

実行バイナリーは500K増え、RAMも40K増えている・・
RX64M のメモリーは潤沢なので、それでも問題は無いが、納得は出来ない。

組み込みマイコンでC++を使う場合の難点は、巨大なクラスを使えない(使わ
ない)事で、あらかじめ判っている事として、「iostream」関係がある。
(1)「iostream」を使わない場合

   text    data     bss     dec     hex
  86708      48   20988  107744   1a4e0

(2)「iostream」を使った場合

   text    data     bss     dec     hex
 528228  108620   27936  664784   a24d0

※なので、それに代わる「format」、「input」クラスなどを実装している。

どうやら、「std::function」は、何らかの場合に、「iostream」の機能を使うら
しい・・・
※「例外」と「rtti」は無効にしている。
※ソースコードを詳細に追っていないので、明確には判らないが・・・
※コードは巨大で複雑なので、時間が出来た時にでも調査する事にするが、とりあ
えず、巨大にならないように工夫して実装するしか無い。

RXマイコン(組み込み向け)formatクラスの改修

前回、「fixed_string」テンプレートを紹介したが、本題の「format」に移る。

「format」クラスは、「boost::format」の組み込みマイコン向けに仕様を変更した俺俺クラスだ。
それでも、sprintf などの他ライブラリに依存していない為、「format.hpp」のみで簡潔するようになっている。
※インクルード部

#include <type_traits>
#include <unistd.h>

最終的な出力は、「iostream」では無く、ターミナルへの文字出力で、「syscalls」の「write」を使っている。(ディスクリプタは stdout)

また、出力クラスは、テンプレートの引数で与えており、出力ファンクタを実装する事で、色々な出力形態に対応できる。
以前は、固定文字列クラスを実装していなかった為、簡易固定文字列を作り、利用していたが、柔軟性が無く拡張しずらかった為文字列クラスを使える仕様に修正した。
※出来てみると、よりシンプルな構造になり、使いやすく、判りやすくなった。
クラスの型は、こんな感じだ。

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  簡易 format クラス
    @param[in]  CHAOUT  文字出力ファンクタ
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <class CHAOUT>
class basic_format {

※「CHAOUT」へは、基本1文字づつ追加する。

標準の文字出力クラスは以下のようになっている(stdout)

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  標準出力ファンクタ
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
class stdout_chaout {

    uint32_t     size_;

public:
    //-----------------------------------------------------------------//
    /*!
        @brief  コンストラクター
    */
    //-----------------------------------------------------------------//
    stdout_chaout() : size_(0) { } 

    void operator() (char ch) {
        char tmp = ch;
        write(1, &tmp, 1);  // FD by stdout
        ++size_;
    }

    void clear() { size_ = 0; };

    uint32_t size() const { return size_; }
};

「format」クラスの一番大きな変更は、「CHAOUT」をスタティックとした点で。

    static CHAOUT  chaout_;
・・・
};
// テンプレートクラス定義の外で、static の実態を宣言する。
template <class CHAOUT> CHAOUT basic_format<CHAOUT>::chaout_;

文字出力が、都度、リセットされないので、追加などの制御がやりやすくなった。
CHAOUT の暗黙的な仕様として、「operator(char)」、「size()」があれば良い。
※「clear()」は利便性の為用意してある。

標準的な文字列クラスを使った場合は以下のように、文字の終端を制御するクラスも与える事で、内部的制御を一般化できる。

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  文字列クラス、出力ファンクタ
    @param[in]  STR     文字列クラス
    @param[in]  TERM    ターミネーター・ファンクタ
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <class STR, class TERM>
class string_chaout {

・・・

    void operator () (char ch) {
        str_ += ch;
        if(str_.size() >= str_.capacity()) {
            term_(str_.c_str(), str_.size());
            str_.clear();
        }           
    }

・・・

};

「TERM」クラスは、「STR」クラスへの文字追加が出来なくなる前に、挙動を制御する。

    void flush() {
        if(str_.size() > 0) {
            term_(str_.c_str(), str_.size());
            str_.clear();
        }
    }

    STR& at_str() { return str_; }

    TERM& at_term() { return term_; }

「flush()」関数と、「STR」クラスへの参照、「TERM」クラスへの参照を追加してある。
※「format」クラスは、これらの関数を必要としない。

CHAOUT クラスをスタティックにすると、一時的に確保した領域(スタック)などで使いたい場合に、使いにくくなってしまう。
そこで、バッファのポインターとサイズを与えて処理を行えるように、コンストラクターを追加した。(これは以前に作ったものを部分的に作り直した。)
リソースはスタティックなので、利便性を考えて関数をリセットする為の仕組みも追加した。

//-----------------------------------------------------------------//
/*!
    @brief  コンストラクター
    @param[in]  form    フォーマット式
    @param[in]  buff    文字バッファ
    @param[in]  size    文字バッファサイズ
    @param[in]  append  文字バッファに追加する場合「true」
*/
//-----------------------------------------------------------------//
basic_format(const char* form, char* buff, uint32_t size,  bool append = false) noexcept :

また、この場合に合わせて、「memory_chaout」クラスを用意した。

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  メモリー文字列クラス
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
class memory_chaout {
    char*       dst_;
    uint32_t    size_;
    uint32_t    pos_;
public:
    //-----------------------------------------------------------------//
    /*!
        @brief  コンストラクター
        @param[in]  dst     出力先
        @param[in]  size    最大サイズ
    */
    //-----------------------------------------------------------------//
    memory_chaout(char* dst = nullptr, uint32_t size = 0) : dst_(dst), size_(size), pos_(0) { }

    void set(char* dst, uint32_t size)
    {
        if(dst_ != dst || size_ != size) {  // ポインター、サイズ、どちらか異なる場合は常にリセット
            pos_ = 0;
        }
        dst_ = dst;
        size_ = size;
    }

    void operator () (char ch) {
        if(pos_ < (size_ - 1)) {
            dst_[pos_] = ch;
            ++pos_;
            dst_[pos_] = 0;
        }
    }

    void clear() { pos_ = 0; }

    uint32_t size() const { return size_; }
};

これで、以下のように使える。

typedef utils::basic_format<utils::memory_chaout> sformat;
    char tmp[256];
    sformat(xxxxxxx, tmp, sizeof(tmp));
    sformat(xxxxxxx, tmp, sizeof(tmp), true);  // 追加する場合

format クラス、完全なソースコード

RXマイコン(組み込み向け)固定文字列クラス

RX マイコン向け http サーバーを実装する過程で、マイクロソフトのIEだ
け極めて遅い応答しかできない事が判った。

Firefox では、1秒以下で表示できるページなのに、IE(最新版)だと、
30秒、またはそれ以上の時間がかかる。

調べてみると、IEのパケット受け取りに、大きな制限があり、異常にインタ
ーバルが大きくなっているようだった、小さいパケットで、分割して送ると、
IEで許容しているサイズに満たない場合、タイムアウトするまで、html の
デコードが遅延され、結果次のパケットを受け付けない。
※何という腐った仕様だろうか・・・
※他の要因として、html のヘッダーにある「Content-Length」が無い、又は
正しくない場合に起こる挙動なのかもしれない。

当初、RXマイコン側での html 文の生成は、行単位で動的に行い、内部の行
生成のタイミングでパケットを送信していた。
※全体の長さは、最終文が来るまで判らないので、「Content-Length」は作成
していないかった。
この方法はメモリーの利用は最低限になるのだが、IEのように大きなパケット
を前提にした、腐ったブラウザでは表示速度に問題が起こる。

そこで、html 文を一旦メモリーに展開して貯めて、一気に転送する方法に変更
する事にした。
※完全に固定されたページでは無く、動的に生成している為
ここで問題なのは、「utils::format」クラスの挙動で、基本、行単位の動作を
前提にしている為、かなり大きく、柔軟に変更する必要がある事がわかり、仕様
を検討した。

そこで、固定サイズの配列で動作する文字列クラスを実装した。
メモリーが潤沢に無い場合には、「std::string」は事実上利用出来ないからで、
メモリーアロケーターが密接に関連したクラスは使う事が出来ない。
※今まで何故作らなかったのか不思議・・・
とりあえず、最低限の機能のみ実装してあり、必要になった機能を追加する。
※当然、std::string の仕様に準拠する。

以下に、ソースの一部を示す。

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  固定サイズ文字列クラス
    @param[in]  SIZE    文字列サイズ
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <uint32_t SIZE>
class fixed_string {
    char        text_[SIZE];
    uint32_t    pos_;

public:
    //-----------------------------------------------------------------//
    /*!
        @brief  コンストラクタ
        @param[in]    str     初期設定文字列
    */
    //-----------------------------------------------------------------//
    fixed_string(const char* str = nullptr) : pos_(0) {
        if(str != nullptr) {
            std::strcpy(text_, str);
            pos_ = std::strlen(text_);
        } else {
            text_[pos_] = 0;
        }
    }

    //-----------------------------------------------------------------//
    /*!
        @brief  格納可能な最大サイズを返す(終端の数を除外)
        @return 格納可能な最大サイズ
    */
    //-----------------------------------------------------------------//
    uint32_t capacity() const noexcept { return SIZE - 1; }
...

完全なソースコード

RXマイコン、C++、FTPサーバーの実装

RX マイコン C++ で、FTPサーバーを実装した。
※動作検査は、GR-KAEDE で行っている。

車輪の再発明的要素は高いが、学習的要素もあるしで、取り組んでみた。
※Arduino の FtpServer プログラムとネットの情報を参考とした。
Arduino 系の C++ ソースは、自分的には気に入らない部分が多い、なので応答メ
ッセージや、全体の流れなど参考とした。

・Arduino の構成は、最新の C++ からは乖離しており、あまり使う気にならない。
・必要の無い継承関係(継承は便利な機能ではあるが、結びつきが強すぎるので、
最近はあまり使わない、どうしても使う必要がある場合だけ使う)
・テンプレートを使わないスタイル(テンプレートを使う事で、柔軟性と拡張性
が導入できると思うので、最近は良く使うようになっている)
・ヘッダーとソースに分かれている。(C++ では、定義と実装を分離する必要が
無く、1つのソース(ヘッダー)のみで管理できる)
・他、細かい部分で色々・・

-----
ある程度実装して、クライアントを接続してみた。

最初、何とか、FFFTP のみ使える状態になった。
※データ転送は PASV モードで行った。

次に FileZilla を試したが、正常に動作しない・・・

MSYS2 上のコンソールから、ftp コマンドで接続してみたが、やはり駄目・・・

原因を調査すると、「SYST」コマンドのタイミングのようだった。
・FFFTP では、SYST は使っていないようだったのでサポートしていなかった。
・コンソールの ftp コマンドでは、SYST のリクエストが来るタイミングが、USER
認証の前だった。
FileZilla では、PASS 認証の後だった。
これを改修したら、接続は出来るようになった。

次に問題になったのは、データ転送のやり方だった。
・FFFTP では、PASV モードで転送を行っていたので、PORT コマンドに対応してい
なかった、そこで、PORT コマンドの対応も行った。
※PORT コマンドでは、FTP サーバーのデータ転送は、クライアント接続となる。
そこで、関連するデータ転送部分で、PASV モードと、PORT モードで、サーバー
動作か、クライアント動作を分けた。

これを行ってから、PORT モードでの転送も行えるようになり、ftp コマンド、
FileZilla での接続等、出来るようになった。

各種クライアントによる接続状況:

FFFTP:
・PASV 有効 ---> OK
・PASV 無効 ---> NG
※PASV 無効 では PORT モードで接続するが、ネットワークが複数(WiFiや
セカンダリーネットワーク)ある機種では、利用している IP アドレスを正しく
取得しない為、正常に接続できないようだ、これは、FFFTP のバグ(仕様)と
思える。

FileZilla:
・既定値(PORT モード)---> OK
・アクティブ(PASV モード) ---> NG
・パッシブ(PASV モード)---> OK
※「アクティブ」の仕様が不明

ftp:(MSYS2 上のコマンド、Windows の ftp コマンドでは無い)
たぶん PORT モード ---> OK

-----
まだ、現在、ネットスタックが調整中であり、中途な状態ではあるが・・

ftp_server.hpp

に、ftp_server のソースコードがある。

サポートコマンド一覧:(※一部、実装中)

    // RFC 959
    ABOR,   ///< ファイルの転送を中止する。
    ACCT,   ///< アカウント情報。引数はユーザアカウントを示す文字列。
    ALLO,   ///< ファイルを受け取るために十分なディスクスペースを割り当てる。引数は予約するサイズ。
    APPE,   ///< 引数に示したファイルに対して追記する。
    CDUP,   ///< 親ディレクトリに移動する。
    CWD,    ///< 作業ディレクトリの変更。引数は移動するディレクトリ。
    DELE,   ///< ファイルを削除する。引数は削除するファイル。
    HELP,   ///< コマンドの一覧。引数を指定するとより詳しいコマンド情報を返す。
    LIST,   ///< 引数に指定したファイルの情報やディレクトリの一覧。
            //   指定しない場合、現在のディレクトリの情報を一覧。
    MKD,    ///< 引数に指定した名前のディレクトリを作成する。
    NLST,   ///< 引数に指定したディレクトリのファイル一覧を返す。
    NOOP,   ///< 何もしない。接続維持のためダミーパケットとして使われることがほとんど。
    MODE,   ///< 転送モードの設定(ストリーム、ブロック、圧縮)。
    PASS,   ///< 認証パスワード。
    PASV,   ///< パッシブモードに移行する。
    PORT,   ///< サーバが接続すべきポートとアドレスを指定する。
    PWD,    ///< 作業ディレクトリを取得する。
    XPWD,   ///< 作業ディレクトリを取得する。(拡張)
    QUIT,   ///< 接続を終了する。
    REIN,   ///< 接続を再初期化する。
    REST,   ///< ファイルの転送を指定した箇所から再開する。
    RETR,   ///< リモートファイルをダウンロード(Retrieve)する。
    RMD,    ///< 引数に指定したディレクトリを削除する。
    RNFR,   ///< 引数に指定した名前のファイル(ディレクトリ)をリネームする。
    RNTO,   ///< 引数に指定した名前のファイル(ディレクトリ)にリネームする。
    SITE,   ///< RFCで定義されていないようなリモートサーバ特有のコマンドを送信する。
    SMNT,   ///< ファイル構造をマウントする
    STAT,   ///< 現在の状態を取得する。
    STOR,   ///< ファイルをアップロード(Stor)する。
    STOU,   ///< ファイル名が重複しないようにファイルをアップロードする。
    STRU,   ///< 転送するファイルの構造を設定する。
    SYST,   ///< システムの種別を返す。
    TYPE,   ///< 転送モードを設定する(アスキーモード、バイナリモード)。
    USER,   ///< 認証するユーザー名

    // RFC 2389
    FEAT,   ///< サーバに実装されている拡張コマンドのリストを取得する。 
    OPTS,   ///< 拡張機能の設定。 

    // RFC 3659
    MDTM,   ///< 引数に指定したファイルの最終更新時間の詳細を返す。
    MLSD,   ///< 引数に指定したディレクトリのファイル一覧を詳細な最終更新時間をつけて返す。
    MLST,   ///< 引数に指定したディレクトリの詳細な情報を返す。
    SIZE,   ///< ファイルサイズを返す

ftp_server クラスは、ファイルアクセスクラスを参照する為、以下のように
SDC(ファイルアクセスクラス「utils::sdc_io」)を typedef しておく。

typedef net::ftp_server<SDC> FTP;
    FTP     ftp_;

コンストラクターで、SDC の実態、及び、Ethernet クラス(net スタッククラス)
を渡す。

    ftp_(ethernet_, sdc_io_)

開始時、ユーザー名、パスワードを引数とする。

    ftp_.start("user", "pass");

実行時、1/100秒間隔でサービスを呼び出す。

    ftp_.service();

-----
ネットスタックは、GR-KAEDE の WEB コンパイラ用ソースを元にしているが、
かろうじて動いている状態なので、回収中。
※あまりにも、酷いソースコードなので、前面的に修正中・・・

サーミスタの温度計算テンプレート

サーミスタの温度計算テンプレートを実装してみた。

このような簡単なテンプレートクラスを書くのは、程よい難しさもあり、パズル的
要素があって意外に楽しい。

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

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
  @brief  NTCTH テンプレートクラス
  @param[in]  ADNUM A/D 変換値の量子化最大値
  @param[in]  THM   サーミスタの型
  @param[in]  REFR  分圧抵抗値
  @param[in]  thup  サーミスタが VCC 側の場合「true」、GND 側の場合「false」
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
template <uint32_t ADNUM, thermistor THM, uint32_t REFR, bool thup>
class NTCTH {

使い方:

#include "chip/NTCTH.hpp"

・・・

// A/D: 12 bits, NT103, 分圧抵抗: 10K オーム、サーミスタ: VCC側
typedef chip::NTCTH<4095, chip::thermistor::HX103_3380, 10000, true> THERMISTOR;
THERMISTOR thermistor_;

・「NTCTH.hpp」をインクルードする。
・テンプレートパラメーターを typedef しておく。
※A/D 変換の分解能(この場合は12ビット)
※NT103 型サーミスタ(B定数などの定義は、「NTCTH.hpp」にある)
※分圧抵抗の値(10000オーム)
※サーミスタが、VCC側か、GND側にあるのか定義

auto v = get_adc(6);  // CH6
utils::format("温度: %5.2f [度]") % thermistor_(v);

クラスに、A/D変換値を入れて、呼ぶと温度が返ってくる。
※サーミスタの抵抗値が「0」になる場合は抵抗計算が成立しなくなるが、実際には、
そのようねケースは起こらないので、「良し」としとく。

テンプレート化している事で、不必要な比較的要素(サーミスタがVCC側かGND側)
かで別れる計算過程などがコンパイル時に決定されるので、条件分岐は無くなり、最適化
も十分行われる。

※対数計算があるので、数学ライブラリ「libm」をインクルードする必要がある。

※サーミスタは種類が多く特性が違うので、新たなサーミスタを使う場合は、テンプレー
トに定義を追加する必要がある。
・「thermistor」型の定義

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  サーミスタ型
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
enum class thermistor {
    NT103_34G,  ///< THB:3435, TR25:10K
    NT103_41G,  ///< THB:4126, TR25:10K
    HX103_3380, ///< THB:3380, TR25:10K (25C to 50C)
};

・「パラメーターの取得関数」

static void get_para_(float& THB, float& TR25)
{
    switch(THM) {
    case thermistor::NT103_34G:
        THB  = 3435.0f;  ///< サーミスタB定数
        TR25 = 10e3;     ///< R25 サーミスタ25℃基準抵抗値
        break;
    case thermistor::NT103_41G:
        THB  = 4126.0f;  ///< サーミスタB定数
        TR25 = 10e3;     ///< R25 サーミスタ25℃基準抵抗値
        break;
    case thermistor::HX103_3380:
        THB  = 3380.0f;  ///< サーミスタB定数
        TR25 = 10e3;     ///< R25 サーミスタ25℃基準抵抗値
        break;
    default:
        break;
}

R8C/M120AN サーミスター・サンプル

-----
100円マイコン、R8Cでも、問題なく動作した。
・NTCサーミスタ(NCP18XH103F03RB)

※将来的にR8Cで温度制御に使う予定。

サーミスタの温度表示サンプル、R8Cでのバイナリーサイズ:
※log 計算や float の計算が含まれるので、確認

m32c-elf-size thm_sample.elf
   text    data     bss     dec     hex filename
  24580     296      76   24952    6178 thm_sample.elf

R8C/M120AN でのバイナリーサイズ

RX64M データフラッシュの操作

以前にRX24T用に、実装した事があるので、まぁ、同じようなものだろうと思った
が、全く違う仕様で、マニュアルを見ながら、チクチクと実装したので、それなりに時
間がかかった。
※「RX64Mグループ、RX71Mグループフラッシュメモリユーザーズマニュアル ハードウェア インタフェース編」
※リンクを開くには、ルネサスのアカウントが必要

RX64M、RX71Mは共通のようだが、基本的に、RX64Mは、フラッシュメモ
リーはEEPROM系では無い感じだ。

通常、EEPROMでは、イレースすると、データが「FF」になるが、RX64Mは
以前に書かれた値が維持されるようだ、しかしながら、イレースを発行したバンクには
新規に新しい値に変更ができるようになる。

なので、以前とは少し違った管理方法を考える必要がある。

それでも、R8C、RX24T、などと共通の仕様にしておく事は有益なので、そのよ
うな見た目を提供できるように実装を行った。
※単純にデータを保持する目的なら、I2Cで外部に接続したEEPROMの方が簡単
で汎用性が高いと思われる。
前から疑問なのだが、なぜ、こんなにも、面倒な操作が必要なのか理解に苦しむ。
※単純にメモリーが「主」のEEPROMをウェアハ上に作る場合と、一般的なロジッ
クが「主」のマイコンでは、製造過程におけるプロセスが随分違う為、マイコン内臓の
EEPROMの場合は、そう単純ではないのかもしれないが、単にデータフラッシュに
読み書きするだけなのに、毎回、仕様を見ながらシーケンスを作る手間は勘弁してほし
い、かと言って、「ル◎サ◎」が提供するソースコードの品質は極めて低いので使う気
にならないしで・・・

RX64Mのフラッシュが特殊な点は、フラッシュメモリーの操作は、内臓されたシー
ケンサーが行い、このシーケンサーにコマンドを送る事で二次的な操作で行う点だ。

初期化では、このシーケンサーにファームを転送して、起動する必要がある。
ファームは、RX64M内に置かれているので、単にデータの転送を行うだけなのだが、
特殊だと感じる。
※確かに、シーケンサーを動かすファームを書き換える事で、色々な仕様を網羅できる
のは後々便利なのだが・・

RX64Mのフラッシュで、とりあえず、駄目なところは、書き込みが4バイト単位で
しか行えない点だろう。
※当然書き込み先アドレスも4の倍数となる。
一度、データを書いたブロックは、イレースするまで更新ができない訳だから、バイト
単位でオーバラップするような操作は行えない事になる・・・
フラッシュの書き込みは、必ず4バイト(32ビット)単位で行うようにしなければな
らない。

とりあえず、「できた」レベルで、あまり詳細に検討していないがソースコードを、
Git に上げた。

flash_io.hpp

RX64M、GR-KAEDEのSPI、SDカードインターフェースの罠

GR-KAEDEにはRSPIを使った、SDカードインターフェースがある。

ところが、非常に不安定で、動作しない事が多かった。
実装はRX24Tでも実績のあるコードで、RX24Tでは何の問題も無く動作
しているものだった・・・

MMC/SDCの使い方
SDカードをSPIモードにするには、「DAT0」端子をプルアップしておく
必要があるのだが、回路図を観ると、GR-KAEDEには、プルアップが無い
ようだ、そこで、10Kでプルアップした。
しかし、全く効果は無かった。

電源ノイズによるものなのかとも思い、10uFをマイクロSDソケットの端子
に接続したが効果は無かった。

ソフト的に色々考えられる事を試したが、成果は無かった・・・

症状としては、初期化の段階で、SDカードをSPIモードに切り替える段階で
エラーになり、SPIモードにならないものだった。

SDカードの相性問題があるのかもしれず、試したが違うようだ(マイクロSD
を二種類しか持っていない)
※そもそも電気回路に「相性」のような不確定性があるハズも無い。
※ごく稀に動作する事もあるが、何かの拍子にすぐに不良になる。

約2日悩んだが、どうやら原因が判った。

RX64Mのデータシートを観ていて、気が付いた、何と、GR-KAEDEで
は、DAT0はPC7に接続している、しかも、この端子はリセット時にブート
モードを切り替えるMD端子でもある。
よくよく回路図を見ると、モード切替のDIP-SW付近で、4.7Kオームで
プルダウンされている。

あーーー、これだ・・・・・


そこで、1Kオーム位ででプルアップしたいが、それだと、ブート出来ない。
そこで、1KオームでPC3と接続しておき、SDカードの初期化時にPC3を
出力にして「H」を出力する事で、プルアップした状態を作る事にした。

この改修を行い、PC3の制御を足したら、当たり前だが、SDカードがSPI
接続出来るようになった。

-----
また、DAT0のプルアップは、必要無いカードもあり、状況を複雑にしている。

GR-KAEDEでSDカードを使っている人はどのように回避しているのだろ
うか?

追記:
ネットを調べたら、普通に情報があった(良く調べるべきだった・・・)
GR-KAEDEでSDカードにアクセスする場合について

RX64M、GR-KAEDEを試用してみた

GR-KAEDEを試用する機会があったので、少し内容に触れてみたい。

同時に、「RENESAS E1」も借りたので、それについても述べておく。

ルネサス関係のマイコンを集中して扱っているが、E1エミュレーターは、持って
いない。
理由として:
・値段が高い
・基本的に普段は詳細なデバッグを必要としない事が多い
※この理由が大きい、ターミナルデバッグで通常は十分
・ルネサスのIDEを使わない(購入するには高価すぎる)
・KPIT はフリーで使えるが、やはり、IDE が好きでは無いし、自分的には刺さらな

など、色々な理由がある、今回は、デバッガーとしてではなく、FlashROM
の書き換え時に、「Renesas Flash Programmer」でのみ利用した。
通常は、シリアルポートを使って書き込んでいるので、それに比較すると、書き込
み速度が速く、重宝した。

GR-KAEDEは、ハードとしては悪くは無いが、高すぎるので興味は無い。
※サポートや、色々考えると、まぁしょうがないのは理解できるが納得はできない。
※バッテリーバックアップ用の電池フォルダーくらいは付けて欲しかった。
・初期からSDRAMが載っているのは好印象。

GR-KAEDEに用意されているプロジェクトに関して:
とりあえず、WEBコンパイラで、簡単なコードを動かしてみて、動作確認し、自分
のシステムに、必要な部分を移植(主にイーサーネット関係のスタック)する為、
プロジェクト一式のソースコードを取得した。
※何故か、ビルドして出来た、mot ファイルをフラッシュに直接書いても動作しない。
スケッチとしてロードすると機能した、「まぁ、そういうものなのだろう」として
詳細な理由は調べなかった。

今まで、GR系のソースコードには触れていなかったのだが、開発期間が短いので、
色々調べたが、やはり既にある程度枯れているソースを再利用する事にした。

第一印象:
・CとC++のコードがあるが、どれも、品質がとても低い。
※ボードの価格に値しない、ソースコードの品質
・コーディング規約はほとんど守られていない。
※多分、会社内でコードレビューを行った事が無いのだろうな・・・
・関数名、クラス名、変数名のつけ方に一貫性がないのでわかり難い。
・C++のクラスがいくつかあるが、これはC++とは言えない(C+-だろうな)
※典型的な、C++を少しだけかじったCのプログラマーのコード
・色々なところに細かく罠とも思える実装依存が含まれている
※動作させるまでに、時間がかかった・・・
・設計が悪すぎで、典型的なスパゲッティーコードになっている。

そんなこんなで、動作させるまでに非常に時間がかかってしまったが、色々改修し
ながらコツコツを進めている・・・

R8C(M120AN)を使ったUSB、電流、電圧チェッカー(その2)

液晶のDPIが高い為、いつも使っている12×6ピクセルのフォントでは
小さすぎて見にくい(最近、視力が極端に落ちてる事も影響・・・)

とりあえず、24ピクセルのフォントを用意するまでの繋ぎで、縦横二倍
にして表示する事で、解像度を生かせないが、それなりの大きさになった。

※余ったスペースで、電力変化をグラフ表示した。

ケースに液晶の穴を開けたが、液晶への配線が邪魔で、蓋が閉まらないが、
何とか工夫すれば、閉まる感じではある。

単独で、電力のグラフ表示も追加した。

マイコンのアナログ入力はまだ余っているので、USBの差動信号電圧を測
定する機能も追加した。

まだ、ソフトを見直す余地はあるが、とりあえず完成とする。

-----
今後の改修予定:
・電力のグラフ表示のインターバル設定
・電圧、電流変化時のフィルター

などなど

R8C(M120AN)を使ったUSB、電流、電圧チェッカー(その1)

R8C(100円マイコン)では、多分始めてとなる実用的なサンプルだ。
※液晶の事故により、第二段となった・・

マイコンは100円だけど、ハイサイトの電流検出に210円のICを使っているのが
痛い・・・
まぁそれはそれとして、多少豪華な仕様(128×32グラフィックス液晶)にしてあ
る。

また見た目も重要なので、タカチ電機工業のケース(CS75N)に収まるように
製作した。

表示切替などを行う、スイッチも設けた。

問題となったのは、「クイックチャージ」をどうするかだ、この仕様では、最大20V
の電圧に対応する必要がある。
電源電圧が5~20Vも変化するとなると、部品の選定や回路設計が難しくなる。
自分は、そのようなデバイスを持っていないが、折角だから「対応」を視野に入れて製
作した。

・マイコンは3.3Vで動作させる。
※グラフィックス液晶が3.3V動作なのと、M120ANのA/D変換の基準電圧は
VCCと同等なので、電源電圧が正確である必要がある。
※5V~20Vから3.3Vを出力する事ができるレギュレーターを入れる。
・ハイサイト電流検出のIC、「LT6106CS5」は、都合の良い事に、最大36
VまでOKなので、これをそのまま使う。

-----

さて、製作してる過程で、配線を修正したり、オペアンプを追加したりしてたら、液晶
に余計な力がかかり、フレキが「ポロリ」ととれてしまいご臨終となった。

しばらくの間、その衝撃で、製作意欲が吹き飛んでいたのだが、秋月で、新たな液晶を
購入、入れ替えた・・・

この液晶、以前のより解像度が高いのだけど、表示面積は少し小さい、またバックライ
トも無いが、コントラストは良好なようだ。
解像度は、128×48ピクセルあり、フレームバッファを用意すると、768バイト
必要となる為、少ないRAMしかないM120ANでは、半分のフレームバッファを
用意して、二回に分けてレンダリングし、半分づつLCDに転送する事で、何とかやり
くりしている。

-----

ケースは、少し小さすぎで、色々削って、何とか入る余地を設けたが、もう少し大きい
方が良さそうだった・・・

基板に、フラッシュプログラムの切り替えや、シリアルコネクターを設ける余裕が無か
ったので、インライン6ピンの専用ラインを設けて、外部でフラッシュプログラム時の
切り替えやリセットなどを行うようにした。

※この段階では液晶の窓は、開けていない・・・

表示に使うフォントなど作り直す必要もあり、色々修正中ではあるけど、とりあえず、
電圧表示と、電流表示、経過時間表示などできたので、回路図を含めて、Gitに上げた。

USB チェッカー

Just another WordPress site