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

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

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

R8C(M110AN)を使ったRCサーボテスター

100円マイコンR8Cには、14ピンの小型パッケージ(M110AN)が
ある。

今まで、ほぼ、20ピンの120ANを多用してきた為、あまり使う理由が無
かったので、今回、RCサーボテスターを作成し、小さいケースに収める事で、
14ピンパッケージの利便性を役立てた。

以前に、サンプルとして、2チャネルのRCサーボを制御する為のコードを実
装したが、これは、それを少しだけ修正して、シングルチャネルで動作させる
だけのものだ。
※ボリュームの角度により点滅速度を変化させるLEDインジケーターを追加
した。

タカチのケースに収める為、余分なスペースが無いので、6ピンのフラッシュ・
プログラム用コネクターを設けている。

インジケーターは、ケースに穴を開けて、グルーガンで塞ぎ、硬化したら、表
面の凸部分をカッターで削って平面にする。
最近良く使う手法で、こうする事で、かなり自由にLEDを基板に取り付けら
れる。
また、グルーガンのスティックが半透明なので、淡い発光で見栄えも良い。

RCサーボテスター回路図:

現状では、サーボのPWMパルスの仕様は、JRサーボ用にしてある。
※ソースコードの先頭で、フタバ用に切り替える事ができる。

// どちらか片方を有効にする
#define JR_TYPE_SERVO
// #define FUTABA_TYPE_SERVO

ソースコードと、KiCADのプロジェクトは、GitHub にプッシュ済み。

NESエミュレーターのテスト

最近、ESP32の情報を集めていて、ESP32のプロジェクトにNESのエミュ
レーターがある事に気がついた。
※ESP32の方は、開発環境が、少し特殊なので、少し様子見ではあるのだが・・

NESのエミュレーションは前から興味があり、最終的にはRXマイコンで動かそう
と思っているのだが、その前に、十分コードを検証しておく必要があると考えていた。

そこで、glfwフレームワーク上で十分コードを精査しておこうと思い、必要そう
なコードをプロジェクトに入れてコンパイルしてみた。

GLFW、NESエミューレーター

NESエンジン部分のコンパイルが通り、OpenGLの描画部分、OpenALの
ストリーム部分、キー入力部分など繋げてみたら、とりあえず動いた!
※現状で、OS-X、Windowsで動作するが、動かないでアプリがクラッシ
ュする、ROMファイル(.NES)もかなりある・・・
音は、ストリームのキューイングがうまくいっていないが、とりあえず鳴る。
音は、Windowsは、普通に鳴るように修正をしたが、OS-Xでは、OpenALの実装が違う
ためか、途切れる・・・
バッファーを多くとれば解消するかもしれないが、遅延が多いのは問題なので、対応
を考えなければならない。

エミュレーター部分はCで書かれているのだが、かなり怪しいコード満載で、かな
り「痛い」コードと言える部類だが、力作ではある、とりあえず、明らかにおかしな
部分は修正を行なって、動作確認などしてあるが、まだまだ動作が怪しい。
※メモリーリークがあり、クラッシュしたり、アサーションを起こす・・・
奇妙な作りの部分が沢山あり、適宜修正して、なるべく多くのROMファイルを動
かそうと思う。


GALAXIAN.NES


Zombie.nes


GRADIUS.NES


Solstice_J.nes
-----
使い方:
F1:ファイラーの表示 On/Off(NESファイルを選択)
F2:制御パネル On/Off(ステートのロード/セーブ、マスターボリューム)
1:セレクト
2:スタート
Z:Aボタン
X:Bボタン
方向:十字ボタン
F4:ログ表示ターミナル(逆アセンブル、読み出し、書き込みなど)

scanfに対応するC++のクラスを作ってみる~

可変引数は「危険」なので、C++では「printf」を使わない文化がある。
それは、以前に説明した、しかしながら、printf のコンビニエンス性は捨てがたい、
そこで、boost::format に習って、組み込み向け utils::format クラスを提供した。


それでは、入力はどうしたものか・・・
一般的には、scanf を使って文字列中の数字を変換する。
しかし、scanf は C の API で、可変引数も使う、なので、format クラスに対応する
入力クラスも実装する必要性が大きい、そこで、input クラスを実装してみた。
※良くある、数字の変換に付き物の機能も追加した。

以下のように使う。

    int a;
    uint32_t b;

    int n = (utils::input("%d[, ]%x", "-99 100") % a % b).num();
    std::cout << a << ", " << b << std::endl;
    std::cout << "N: " << n << std::endl;

結果:

-99, 256
N: 2

上のサンプルでは、「%d」、「%x」の仕切りとして、「,」又は「 」(スペース)を
使う事ができる。
これは、正規表現に似た書式を簡略化したものだが、機能的には最低限の物しか無い。
※豊富な機能を盛り込むとコードが肥大化するし、たぶん実際には、それ程高機能で
無くても困らないと思えるからだ。

また、取り込んだ数は、「num()」を使って取得する事ができる。

input クラスの書式は以下のようになっている。

//-----------------------------------------------------------------//
/*!
    @brief  コンストラクター
    @param[in]  form    入力形式
    @param[in]  inp     変換文字列(nullptrの場合、sci_getch で取得)
*/
//-----------------------------------------------------------------//
basic_input(const char* form, const char* inp = nullptr);

basic_input クラス・テンプレートは、以下のように typedef されている。

typedef basic_input<def_chainp> input

ここで、「def_chainp」クラスは以下のようになっている。
自前の特殊クラスを作って食わせる事ができる。

//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
/*!
    @brief  標準入力ファンクタ
*/
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
class def_chainp {
    const char* str_;
    char        last_;
    bool        unget_;
public:
    def_chainp(const char* str = nullptr) : str_(str), last_(0), unget_(false) { }

    void unget() {
        unget_ = true;
    }

    char operator() () {
        if(unget_) {
            unget_ = false;
        } else {
            if(str_ == nullptr) {
                last_ = sci_getch();
            } else {
                last_ = *str_;
                if(last_ != 0) { ++str_; }
            }
        }
        return last_;
    }
};

※「unget()」は、一文字を書き戻す。

input.hpp のプロジェクト

RXマイコン用、FatFsとシステムコール

データロガーの実装を進めるに中って、boost を使うようになった。
そうすると、どうしても「システムコール」を実装しなくてはならない状況にな
った。
これは、boost の動作で、ファイルを操作したり、標準出力を使っている為なの
だと思うが、リンク時一連のシステムコールがリンクできずににエラーを起こす。
※boost の内部設定を変更して、それらを使わないように「魔改造」する事も考
えられるが、それはそれで、大変と思うので、とりあえず、標準的な設定で何と
かなるよにしたい。

以前にSH2Aで、LCDの描画などを行った時にも、同じような事があり、実
装したので、どうすれば良いか、簡単に説明しておこうと思う。

まず、最低限必要なのが、標準入出力のしくみなどだ。

以下の関数を最低限実装する必要があるようだ、何か足りなければ、リンクエラ
ーが出るので、その都度追加すればよい。

int open(const char *path, int flags, ...);
int fstat(int file, struct stat *st);
int read(int file, void *ptr, int len);
int write(int file, const void *ptr, int len);
int lseek(int file, int offset, int dir);
int close(int file);
int isatty(int file);
void kill(int n, int m);
int getpid(int n);
void exit(int n);

また、固定のファイルディスクリプタとして以下がある:

stdin  : 0
stdout : 1
stderr : 2

これらは、オープンもクローズもできない、また、シークも必要ない。

「write」、「read」の中で、「file」が0、1、2の場合に、シリアル入出力へ
繋げば良いと思う。
「isatty」は、ファイルディスクリプターが端末を参照しているかをチェックす
る機能なので、これも実装しておく。
※そうすれば、「printf」は使えるようになる。(ただ、C++ ではあくまでも、
iostream が基本なのと、自分の実装では、format があるので必要無い、又、
iostream を使うようなコードを書くと、単純にメモリーが足りなくて、リンク
時にエラーを起こすだけではある)
※とりあえず、この程度なら、RL78やR8Cでも利用可能と思う。

//-----------------------------------------------------------------//
/*!
    @brief      ファイル記述子で指定されたファイルから指定バイトを読む
    @param[in]  file    ファイル記述子
    @param[in]  ptr     読み込み先
    @param[in]  len     読み込みバイト数
    @return     読み込みバイト数
*/
//-----------------------------------------------------------------//
int read(int file, void *ptr, int len)
{
    int l = 0;
    if(file >= 0 && file <= 2) {
        // stdin
        if(file == 0) {
            char *p = ptr;
            for(int i = 0; i < len; ++i) {
                *p++ = sci_getch();
            }
            errno = 0;
            l = len;
        }
    }
    return l;
}

//-----------------------------------------------------------------//
/*!
    @brief      ファイル記述子で指定されたファイルから指定バイトを書く
    @param[in]  file    ファイル記述子
    @param[in]  ptr     書き込み元
    @param[in]  len     書き込みバイト数
    @return     書き込みバイト数
*/
//-----------------------------------------------------------------//
int write(int file, const void *ptr, int len)
{
    int l = -1;
    if(file >= 0 && file <= 2) {
        if(file == 1 || file == 2) {
            const char *p = ptr;
            for(int i = 0; i < len; ++i) {
                char ch = *p++;
                sci_putch(ch);
            }
            l = len;
            errno = 0;
        }
    }
    return l;
}

//-----------------------------------------------------------------//
/*!
    @brief      ファイルディスクリプタが端末を参照しているかをチェックする
    @param[in]  file    ファイル記述子
    @return     端末を参照するオープンされたファイルディスクリプタ @n
                であれば 1 を返す。@n
                そうでなければ 0 を返し、 errno にエラーを示す値を設定する。
*/
//-----------------------------------------------------------------//
int isatty(int file)
{
    if(file >= 0 && file <= 2) {
        errno = 0;
        return 1;
    } else if(file < OPEN_MAX_) {
        errno = ENOTTY;
        return 0;
    }
    errno = EINVAL;
    return 0;
}

--------

FatFs を使って、SDカードにアクセスする場合は、GitHub の syscalls.cを参照してほしい。
RX24Tの場合、RAMは16Kしか無いので、記憶割り当てを実装するのは、
少しキビシイ、そこで、FatFsの設定では、「記憶割り当て」を使わない設
定にしている、そうすると、1個のファイルを開くのに必要なメモリーは、そこ
そこ大きく、最大オープン数を多く取るとスタテックにメモリーを消費する。
ファイルの先頭で、「OPEN_MAX_」を「4」と定義してある。
最大4つまでファイルを開く事ができる、通常これくらいあれば十分と思うが、
アプリにより最適な値を調整する必要があるかもしれない。
又、FatFSを使わない場合には、「FAT_FS」をコメントアウトして、
無効にする。

ファイルをオープンするパスは、UTF-8を使う、内部でShift_JIS
に変換されて、FatFSに渡される。

FatFsを使う場合、内部で「sdc_io」クラスと連携しているので、SDカード
の自動マウントなどを見守る必要がある。

RX24Tを使ったデータロガー(その1)

以前にATMegaを使ってデータロガーを作成したが、需要が無かったのと、
大きくて、使いかってが悪いなどで使わなくなっていた。
IMG_0904sIMG_0905sIMG_0906s

最近、需要が、少しではあるがありそうなので、最新のRX24Tで作り直す
事にした。
ソースは、ATMegaの物をなるべく利用するが、元はCで書かれているの
と、AVR用に特化している部分もあるので、C++で作り直す。

今度は利便性を第一に考えて、小型に作る。
とりあえず、タカチのケース(CS115N)に入るようにして、基板を起こす
予定でいる。
※最近は、基板を小ロット作成するのにかかるコストは凄く少なくなっている。
IMG_0907s

スペックや仕様は以下の通り:
・100Hzサンプリング
・ラップタイム入力
・G.P.S.入力
・加速度、ジャイロセンサー(MPU6050)
・速度(パルス)入力、2チャンネル
・回転(パルス)入力、1チャネル(エンジン回転用)
・アナログ4チャンネル
・SDカード対応
・128×64、液晶表示
・720mAリチウムポリマー電池と充電、3.3VDC/DC
・USB充電

RX24Tデータロガーリソース

RX24Tで内臓データフラッシュの読み書き

RX24Tの内臓データフラッシュの読み書きが出来たので、関係クラスと
サンプルを追加した。

RX24Tでは、内臓データフラッシュは8Kバイトあり、1Kバイト毎の
ブロックに分かれている。
※消去は、1Kバイト単位で行う。

以前、RL78では、内臓データフラッシュへのアクセスは、情報が公開さ
れておらず、使う事が出来なかった。
※操作ラブラリーだけが公開されており(gcc 版は無い)ソースコードや、
関係レジスターの説明等は未公開となっている為、実装をあきらめた。
※「公開できない」理由について、色々考えたりもしたが、それが、トリガー
となって、RL78からは疎遠になってしまった。

RXマイコンでは、アクセスが多少面倒ではあるものの、内蔵データフラッ
シュを使う事が出来る。(常識でもある)
また、ROMフラッシュ領域も、書き込みなどが行えるものの、操作が面倒
なのと、当面必要では無いので、実装していない。

データフラッシュは、消去が1バイト単位で出来ない(1Kバイト毎)ので、
汎用的なメモリーとして扱うようにするには、巧妙なマネージメントを必要
とする、これは、フラッシュの書き換え可能回数制限や、ブロック・イレー
スに、時間が多少必要など、フラッシュメモリーの特性が起因している。

データフラッシュの扱いは、RXマイコンで共通なら、問題は無いが、デバ
イスによって異なるようだ・・
今回、RX24T専用となったので、

RX24T/flash.hpp
RX24T/flash_io.hpp

専用のクラスを用意した。

ただ、データフラッシュに対する操作は同じ仕様とする。

//-----------------------------------------------------------------//
/*!
    @brief    開始
*/
//-----------------------------------------------------------------//
void start() const;

//-----------------------------------------------------------------//
/*!
    @brief  読み出し
    @param[in]   org   開始アドレス
    @return データ
*/
//-----------------------------------------------------------------//
uint8_t read(uint16_t org) const;

//-----------------------------------------------------------------//
/*!
    @brief  読み出し
    @param[in]  org     開始アドレス
    @param[in]  len     バイト数
    @param[out] dst     先
*/
//-----------------------------------------------------------------//
void read(uint16_t org, uint16_t len, void* dst) const;

//-----------------------------------------------------------------//
/*!
    @brief  消去チェック
    @param[in]  bank    バンク
    @return エラーがあれば「false」
*/
//-----------------------------------------------------------------//
bool erase_check(data_area bank) const;

//-----------------------------------------------------------------//
/*!
    @brief  消去
    @param[in]  bank     バンク
    @return エラーがあれば「false」
*/
//-----------------------------------------------------------------//
bool erase(data_area bank) const;

//-----------------------------------------------------------------//
/*!
    @brief  書き込み
    @param[in]  src     ソース
    @param[in]  org     開始オフセット
    @param[in]  len     バイト数
    @return エラーがあれば「false」
*/
//-----------------------------------------------------------------//
bool write(const void* src, uint16_t org, uint16_t len) const;

//-----------------------------------------------------------------//
/*!
    @brief  書き込み
    @param[in]	org	開始オフセット
    @param[in]	data	書き込みデータ
    @return エラーがあれば「false」
*/
//-----------------------------------------------------------------//
bool write(uint16_t org, uint8_t data) const;

データ・フラッシュ・サンプル

RX64M内臓RTCを使う

正月休みは風邪引いて本物の寝正月で、趣味の工作など、全く出来なかった。

去年の11月終わりに請けた仕事も、ようやく何とかなりそうで、やっとRXマイコン
を再開した。

RX64M、176ピンは、内臓RTCを持っており、バックアップ用別電源端子が用
意されている。
※ VBATT(15)
※バッテリーバックアップできないようなRTCは、ほとんど利用価値が無い為、外部
にさらにRTCを繋ぐ必要がある。

RX64M、RTCのもう一つのメリットは、バイナリーカウンターモードがある点で、
これは、やっとまともな仕様になったとも言える。
時間管理を普通にやる場合、シリアライズがどうしても必要だが、一般的なRTCのレ
ジスター構成は、二度手間になるだけで何のメリットも無いものになっている。
※レジスターは、秒、時間、日、月、年、曜日などに分かれており、扱いが容易では無
い、これは、30年前以前の処理能力が無かった時代の腐った仕様が現役になっている。
時間の設定や、時間管理を考えた場合、「うるう年」や「曜日」の管理を行うのが普通
なので、基点からの経過時間(秒)で管理するのが確実だ、従って、RTCは、単純な
秒単位のカウンターが一番適している。
ハードウェアーデバイスに着目すると、単なるバイナリーカウンターのRTCも流通し
てはいるが、腐った仕様のRTCよりコストが高い事に驚く。
DS1371などがバイナリー仕様のRTC
バイナリーカウンタRTC:
DS1371(単価341円)

年、月、日、時間RTC:
MCP79410(単価122円)
DS3231モジュール(単価110円)
※何でこんな値段でやれるのか、不思議でならない・・・

IMG_0902s
リチウムイオン電池には直列に逆流防止ダイオードを入れる。
ただ注意しなければならないのは、順方向電圧降下が少ないタイプ(ショットキー)は
逆方向電流が多い物が多い、今回は、RB751S-40を使った。

RX64M内臓のRTCは、バイナリーカウンターなので、非常に簡単に実装する事が
できた。

RTCの読み出しでは、注意する必要がある。

複数の非同期カウンターをどのように読むべきか?

何も考えずに読み出すと、カウントアップ時の非同期性の為、間違った値を読んでしま
う事がある。
全てのカウンターレジスター更新には、多少の時間的ズレがある為、確率的に全てのカ
ウント更新が終了していない中途半端な状態を読み出してしまう為で、以前に観た実装
では、RTCから1Hz出力を出して、それを割り込みを使って、そのタイミングで読
み出すとかしていた、しかもこの方法でも、正しい値を読み出せる保障は無く、多分、
「たまたま」うまく読み出せているだけで、RTCが違う場合には、うまくいかない場
合もあるかもしれない。

非同期で動作する複数のカウンターがある場合、以下のように、二度読み出して、両方
同じなら、「値を信用」し、違えば、二度同じ値になるように読み出すループを作れば
良い。
腐った仕様のRTCでは、利用するレジスター全てを比較する。

bool get_time(time_t& tp) const {
    auto a = get_();
    for(int i = 0; i < 5; ++i) {
        auto b = get_();
        if(a == b) {
            tp = static_cast(a);
            return true;
        }
        a = b;
    }
    return false;
}

そして、
以前に、R8C、RL78のRTC用に実装した、マネージメントを組み込んだ。

時間関数は、libc に含まれているが、巨大なので、必要最低限の実装を行った独自の
「common/time.c」を使っている。
※「うるう秒」の補正は行っていない。

# RX64M RTC sample
# help
date
date yyyy/mm/dd hh:mm[:ss]
# date
Sat Jan 14 23:13:56  2017

コマンドラインから、時間設定と、表示を行う事ができる。
時間管理は、「time_t」(基点からの経過秒数)で行っており、内部はグリニッジ標準
時間を使い、タイムゾーン(東京+9時間)はハードコートしてある。

RX64Mのバックアップ機能も問題無く動作しているようだ~

RX64M_RTC_sample

整数計算でサインテーブルを生成する

これは、アセンブラ時代に、サインテーブルを生成する方法を色々試して、ある程度
成果のあった方法です。
※浮動小数点の sin で求めた値と微妙に違うところもありますが、実用性は十分あり
ます。

現在の C++11 以降では、constexpr でコンパイル時に計算して、定数としてソースコ
ードにテーブルを埋め込む事ができるので、必要性は薄いのですが、知っていれば、
応用範囲は広いでしょう。
ハードウェアーに実装して、周波数シンセサイザや音源の波形ベースに使う事も考え
られます。

x' = x・cos(s) - y・sin(s)
y' = x・sin(s) + y・cos(s)

s が十分小さい場合、以下のように近似できる。
cos(s) ≒ 1
sin(s) ≒ s

x' = x - y・s
y' = x'・s + y

テーブル生成に使っていますが、連続性がある場合は、リアルタイムに計算する事も
できます。(こちらの方が応用性があります)

非常に古いですが、1979年に発売された、ナムコのギャラクシアンで、編隊が、
実機の手前で、回転したり、サインカーブ的な軌跡で攻撃してくる場面がありますが、
この原理が使われていました。
※この当時、安易にテーブルにする人が多い中で、真のエンジニアでした。

また、単振動を使って、サインテーブルを計算する方法もあるかと思いますが、周期
を決定する定数を求めるのは、整数計算だけでは、簡単では無いと思えます。

テーブル生成では、テーブルのサイズは、2のN乗の方が、扱いやすいので、sの定
数を求める場合は、全円周(2・π)を、テーブル長で割る必要があります。

template <uint16_t shi, uint16_t len>
class sin_cos {

    static const uint32_t pai_ = 0xC90FDAA2;    ///< 円周率(3.141592654 * 2^30)
    static const uint32_t pai_shift_ = 30;      ///< 円周率、小数点位置

    uint16_t tbl_[(1 << shi) + 1];

    void build_()
    {
        int16_t gain = 16;  // 精度を確保する為の下駄
        int64_t x = len << gain;  // cos(0) の値
        int64_t y = 0;    // sin(0) の値
        for(uint16_t i = 0; i < ((1 << shi) + 1); ++i) {
            tbl_[i] = y >> gain;   // pai_ のビット位置補正
	    // 全周は、2・πなので+1
	    x -= (static_cast(pai_) * y) >> (pai_shift_ + shi + 1);
	    y += (static_cast(pai_) * x) >> (pai_shift_ + shi + 1);
        }
    }
    public:
        sin_cos() { build_(); }
};

整数演算で平方根

最近、整数演算だけで色々する話があったので、過去に作成したものを見直してみた。

・平方根
これは、ファミコン時代から使っていた方法をC++(C)で表現したもので。
※アルゴリズムは、コンピューターが出現する以前から知られていた数学的手法を元に
しているようで、初期のデジタルコンピューターが出現する、数十年前に既に知られて
いた方法のようである。

過去には、色々なマイコン用(6502、6809、68000、Z80)アセンブラ
で書いた記憶がある。
Cなどの高級言語では、キャーリーの扱いが無い為、シフトや、ビット操作を行う場合
無駄が多い。
アセンブラで書けば、より一層の高速化ができるけど、互換性を考えて、Cで実装して
ある。
ハードウェアーにも実装しやすく、割り算よりリソースを使わないと思われる。
※整数計算なので、「余り」がある。

ゲームにおいて平方根は、必ず必要な「道具」で、シューティングゲームの誘導ミサイ
ル、正規化、ほぼありとあらゆる場面で使うと思われる。

template <typename T>
struct sqrt_t {
    typedef T value_type;

    T   val;    ///< 答え
    T   mod;	///< 余り

    sqrt_t(T v = 0, T m = 0) : val(v), mod(m) { }
};

static sqrt_t<uint16_t> sqrt16(uint16_t in)
{
    uint32_t a = in;
    uint32_t b = 0x4000;
    for(int i = 0; i < 8; ++i) {
        if(a >= b) {
            a -= b;
            b = ((b + b) & 0xfffe0000) + 0x10000 + (b & 0xffff);
        } else {
            b = ((b + b) & 0xfffe0000) + (b & 0xffff);
        }
        a <<= 2;
    }
    sqrt_t<uint16_t> ans(b >> 16, a >> 16);
    return ans;
}

・32ビット版

static sqrt_t<uint32_t> sqrt32(uint32_t al)
{
    uint32_t ah, bh, bl;

    ah = bh = 0;
    bl = 0x40000000;
    for(int i = 0; i < 16; ++i) {
        if(al >= bl) {
            if(ah >= bh) {
                ah -= bh;
                al -= bl;
                bh += bh + 1;
            } else {
                bh += bh;
            }
        } else {
            if(ah >= (bh + 1)) {
                ah -= bh + 1;
                al -= bl;
                bh += bh + 1;
            } else {
                bh += bh;
            }
        }
        ah <<= 2;
        ah += al >> 30;
        al <<= 2;
    }

    sqrt_t ans(bh, ah);
    return ans;
}

intmath.hpp

マイクロチップ用開発環境を構築

マイクロチップ用のソースを修正する作業が生じた為、Windows で、環境構築をしてみた。

遠い昔に、マイクロチップが出始めた時に、自分でアセンブラを実装して開発した覚えが
あるが、最近は、AVRやルネサスがメインとなり、使わなくなった。

開発環境が有料な事や、プログラマーが高いのも敬遠する理由ではある。
・プログラマーを自分で作る事もできそうだが、種類が多いので大変そうだ・・・
・60日を過ぎると、無料版は、最適化が「-O1」のみとなる。
※一応ソースコードが入手できるので、パッチを当てて、制限を回避する事も出来るよう
だが・・・
・現状で、C++14 のコードをコンパイル出来ないのは、まともなコンパイラとは思えない。

PICの良い面は、デバイスの入手性、バリエーションの多さ、「コスパ」なのだと思うが
自分はメリットを感じない。

今回使うチップは、「PIC24EP256GU810」で、「MPLAB C30」をインストールした。
※コンパイラは魔改造された gcc のようだ。

例によって、IDEを使わず、コマンドラインで開発したいので、Windows、MSYS2 で動作
させてみた。
以前から少しづつアップデートしている独自の「Makefile」を PIC30 用に修正しておこなった。
※この「Makefile」は、非常に柔軟性が高い!
従属規則の生成も全く問題無く、正常に生成された。
そして、暫く格闘したが、コンパイルしてリンクできるようになった。

・MPLAB C30 にパスを通す。(PATH=$PATH:/c/'Program Files (x86)'/Microchip/'MPLAB C30'/bin)
・デバイスを指定する。(-mpu=24EP256GU810)
・適切なコンパイルオプションを選ぶ。(-mlarge-code -mlarge-data -mlarge-scalar -mconst-in-data)
・適切なリンクオプションを選ぶ。(-T p24EP256GU810.gld --heap=1024 --stack=1024)

これだけ行って、コンパイル、リンクが通った。

#=======================================================#
#                                                       #
#    PIC C30 Makfile                                    #
#                                                       #
#=======================================================#
TARGET  =   farm

DEVICE  =   24EP256GU810

BUILD   =   release

VPATH   =

ASOURCES =

CSOURCES =   ここにソースファイルを列挙する~

PSOURCES =   

USER_LIBS =

LDSCRIPT  =

USER_DEFS =

INC_SYS   =

INC_APP   =

OPTIMIZE  =   -O1 -mlarge-code -mlarge-data -mlarge-scalar -mconst-in-data

MCU_TARGET =   -mcpu=$(DEVICE)

CP_OPT   =   -Wall -Werror \
             -Wno-unused-variable \
             -Wno-unused-function \
             -fno-exceptions

#CC_OPT  =   -Wall -Werror \
#            -Wno-unused-variable \
#            -fno-exceptions
#CC_OPT  =   -x c
CC_OPT   =

SYSINCS  =   $(addprefix -I, $(INC_SYS))
APPINCS  =   $(addprefix -I, $(INC_APP))
AINCS    =   $(SYSINCS) $(APPINCS)
CINCS    =   $(SYSINCS) $(APPINCS)
PINCS    =   $(SYSINCS) $(APPINCS)
LIBINCS  =   $(addprefix -L, $(LIB_ROOT))
DEFS     =   $(addprefix -D, $(USER_DEFS))
LIBS     =   $(addprefix -l, $(USER_LIBS))

# You should not have to change anything below here.
AS       =   pic30-as
CC       =   pic30-gcc
CP       =
AR       =   pic30-ar
LD       =   pic30-ld
OBJCOPY  =
OBJDUMP  =   pic30-objdump
SIZE     =
HEX      =   pic30-bin2hex

# AFLAGS =   -Wa,-adhlns=$(<:.s=.lst),-gstabs
# AFLAGS =   -Wa,-adhlns=$(<:.s=.lst)
# ALL_ASFLAGS = -x assembler-with-cpp $(ASFLAGS) $(DEFS)
ALL_ASFLAGS  =   $(AFLAGS) $(MCU_TARGET) $(DEFS)

# Override is only needed by avr-lib build system.

CFLAGS  =   $(CC_OPT) $(OPTIMIZE) $(MCU_TARGET) $(DEFS)
PFLAGS  =   -std=c++14 $(CP_OPT) $(OPTIMIZE) $(MCU_TARGET) $(DEFS)

# override LDFLAGS = $(MCU_TARGET) -nostartfiles -Wl,-Map,$(TARGET).map -T $(LDSCRIPT)
# override LDFLAGS = $(MCU_TARGET) -T$(DEVICE).gld,--defsym=__MPLAB_BUILD=1,--defsym=__MPLAB_DEBUG=1,--heap=1024,--stack=1024,-Map="VTS-10a.map"
override LDFLAGS = $(MCU_TARGET) -T p$(DEVICE).gld --heap=1024 --stack=1024

OBJCOPY_OPT  =   --srec-forceS3 --srec-len 32

OBJECTS =   $(addprefix $(BUILD)/,$(patsubst %.s,%.o,$(ASOURCES))) \
            $(addprefix $(BUILD)/,$(patsubst %.c,%.o,$(CSOURCES))) \
            $(addprefix $(BUILD)/,$(patsubst %.cpp,%.o,$(PSOURCES)))

DOBJECTS =  $(addprefix $(BUILD)/,$(patsubst %.c,%.o,$(CSOURCES))) \
            $(addprefix $(BUILD)/,$(patsubst %.cpp,%.o,$(PSOURCES)))

DEPENDS =   $(patsubst %.o,%.d, $(DOBJECTS))

.PHONY: all clean
.SUFFIXES :
.SUFFIXES : .rc .hpp .s .h .c .cpp .d .o

all: $(BUILD) $(TARGET).elf

$(TARGET).elf: $(OBJECTS) Makefile
    $(CC) $(LDFLAGS) $(LIBINCS) -o $@ $(OBJECTS) $(LIBS)
    $(HEX) $@ $(TARGET).hex

$(BUILD)/%.o: %.s
    mkdir -p $(dir $@); \
    $(AS) -c $(AOPT) $(AFLAGS) $(AINCS) -o $@ $<

$(BUILD)/%.o : %.c
    mkdir -p $(dir $@); \
    $(CC) -c $(COPT) $(CFLAGS) $(CINCS) $(CCWARN) -o $@ $<

$(BUILD)/%.o : %.cpp
    mkdir -p $(dir $@); \
    $(CP) -c $(POPT) $(PFLAGS) $(PINCS) $(CPWARN) -o $@ $<

$(BUILD)/%.d: %.c
    mkdir -p $(dir $@); \
    $(CC) -MM -DDEPEND_ESCAPE $(COPT) $(CFLAGS) $(APPINCS) $< \
        | sed 's/$(notdir $*)\.o:/$(subst /,\/,$(patsubst %.d,%.o,$@) $@):/' > $@ ; \
        [ -s $@ ] || rm -f $@

$(BUILD)/%.d: %.cpp
    mkdir -p $(dir $@); \
    $(CP) -MM -DDEPEND_ESCAPE $(POPT) $(PFLAGS) $(APPINCS) $< \
        | sed 's/$(notdir $*)\.o:/$(subst /,\/,$(patsubst %.d,%.o,$@) $@):/' > $@ ; \
        [ -s $@ ] || rm -f $@

clean:
    rm -rf $(BUILD) $(TARGET).elf $(TARGET).mot $(TARGET).lst $(TARGET).map

clean_depend:
    rm -f $(DEPENDS)

後は、書き込んでテストとなるが、ライターは自作する時間も惜しいので、仕方なく
「PICkit3」を買う(5700円)事にする。(自分のお金じゃ無いけど・・)
IMG_0898s
※自分は、デバッグ機能(トレース、実行など)が必要になった事が少ないので、
フラッシュ書き込みさえ出来れば、満足なのだけど・・・

やはり、コンパイラが有償(無償版は60日過ぎると、最適化できなくなる)なのは、最
悪だと思う、自分的には、仕事以外では絶対に使わないデバイスだと思う。

追記:
Windows10 のノートにもPICの開発環境をインストールしたが、コンパイラを走らせる
と「ライセンスがどうの」と言ってきてエラーになる・・・
調べると、「mplabc30-v3_31-windows-installer.exe」のバージョンに起因する、
license-lm 関係のバグのようだ、そこで、「v3_30」を取ってきてインストールした。

-----
やっぱり、ルネサス系、R8C、RL78、RXマイコンが最高だと、再認識する。
※コストでは、PICに武がある事は認めるのだけど・・・