WR250Xカスタム(その1)

最初はノーマルで乗ろうと思ったけど、やっぱし改造してしまう・・・

先日オークションを観てたら、ヨシムラのスリップオンマフラーがそこそこの値段で出ていたー
見た目、大きな傷も無いし、即決価格がそんなに高くなかったので、ポチってしまった・・・

WR250用のサイレンサーは色々出ているけど、各社物色したり、色々な情報を総括して、「RS-4J」はベストな選択だと思う。
チタンカバーで、カーボンエンド、これがやっぱり良さそうで、創りは流石に「ヨシムラ」隅々まで非常に良く出来ている。

中古なので、まぁ色々あるのが普通だけど、「写真の部品が全て」と書いてあったがあまり気にせず、落札したが、「物」が届いてみると、エキパイのクランプバンドが無い・・。
出品者に連絡したけど、「無い」との事、「何で???」多少怪しさを感じながらも、ヨシムラジャパンに電話した。
そして、部品をオーダーしようとしたのだが、衝撃的な事実が・・
「部品は今欠品中で最短でも来年の2月くらい」との返事・・・、えーーー、そしたら、「43-47」のクランプバンドなら、付きますよ!
との事で、探してみると・・あった!、これだ!「キジマ(Kijima) マフラーバンド 43-47mm」早速アマゾンで注文した。
※毎度の事だけど、アマゾンさん、何で、コレだけ送るのにあんな大きなボックスで送るかなーーwww(一番小さいらしいけど・・)

マフラーのガスケットも基本新品が良いので、近くのYSPで注文した、特殊な形状のガスケットで、1166円だった(高い!)・・・

そして、早速組んでみたー、多少、組み付けで悩んだが、何とか取り付けできた~
アイドリング中は、音は爆音でもなく(ノーマルより若干大きい程度)、回すとけっこう気持ちよい音がするー。
※12月に入って急に寒くなった、でも早速テスト走行と思ったけど、雨が降ってきたので、今日は試せなかった、残念・・・

で、次の日、ちょっと奥多摩駐車場まで・・前日は、イベントやってたらしい、この時期寒くて「5度」・・どんだけー

IMG_0701

排気デバイスが無いので、低速トルクが無くなったけど、アクセル開けると凄く気持ちよく回るー、これはタコが欲しい~
※流石に回すとそれなりの音がするー

—–
モタードやモトクロスの倒立では、インナーパイプを保護する為カバーが付いている、なので、インナーパイプを掃除出来ない。
なので、外して掃除。
フロントフォークのカバーを止めているボルトはスチールの安っすいボルト・・
多分錆びると思い、ステンレスのキャップボルトを買ってきた、このまま付けるのもイマイチなので、旋盤でテーパー加工してみた。

まず、ボルトをチャックに銜える為に、アルミカラーを作り三等分、そしてひたすら加工。

IMG_0694

IMG_0695

WR250Xは、峠楽しい!

組み込みでもC++

この記事はC++ Advent Calender 2014の参加記事です。

感覚的には、組み込み関係の業界では、C++の取り組みはかなりお寒いものがあります。

現代の組み込みでは、32ビットCPU、FPU内蔵、100MHz動作、そんなデバイスが539円、gcc で開発でき、もちろんC++を使えます、STLやboostは限定的に使えます。
※メモリーが少ないので、メモリーを多く必要なライブラリーに起因したクラスは使えません(たとえば、iostream 関係は、非常に多くメモリーを消費する為、デバイスによっては使えません)

去年は「WinAVR C++ の実力とは!?」を書きました。
AVRマイコンは8ビットのRISCマイコンで、扱えるリソースが少ない為もあり、STLが使えないなど制限がありました。
今回は、ルネサス エレクトロニクスの32ビットマイコンであるRX600シリーズで、C++の開発環境や、C++で実装するI/O定義クラスの紹介をしたいと思います。

RX621マイコンボード
IMG_0448s
・搭載マイコン:R5F56218BDFP(32ビットRXコア)
・プログラム用フラッシュメモリ:512Kバイト
・データ用フラッシュメモリ:32Kバイト
・RAM:96Kバイト(ノーウェイト動作)
・動作周波数:96MHz(12MHzをPLLにより8逓倍動作)

RXマイコンは、CISC型の32ビットマイコンで、低消費電力、安価、高機能、高速など、色々なメリットがある国産のマイコンです。
組み込み系マイコンと言えば、ARMの人気がもの凄く高く、色々なメーカーが採用している為、バリエーションも多く価格も安く、開発環境も整っていると言えるでしょう。
しかしながら、RXマイコンは、自分が評価した価格ゾーンでは、ARMより多くのメリットがあると感じます。
日本のメーカーにエールを送りたいとゆーのもありますが、コストが同じ程度なら、性能、入手性、日本語マニュアルなど、より扱いやすい物を使う方が効率が良いと感じます。
それでも、本当はARMの方が優位だとは思いますが、天邪鬼なんでRX押しです!www

開発環境:

ルネサスでは、開発ツールとして、自社開発の統合開発環境を用意しており、無料版(64キロバイト以上のバイナリーを作れない)もあります。
又、GNUを使った無料の統合開発環境「KPIT」も使う事が出来ますが、ルネサス提供のサンプルプログラムをコンパイル出来ないなど、冷遇されており、自分のようにコマンドラインで、コンパイラ、リンカー、Makefile、emacs で開発を進めるスタイルにも合いません。
・統合開発環境では多くの場合、裏で、目に見えない「何か」をやっており、「つぶし」が利かない。
・サターンの時代(SH2)に、自社開発のコンパイラを使った経験から、あまり良い印象が無いので、最初から使う気にもならず評価もしていません。
・新しい統合開発環境の操作を学ぶのに時間がかかる。
・少し大きなプログラムを作ったら、簡単に64キロバイトを超えてしまい、有料版ツールが必須になるが、数十万円の開発ツールは買う気にはなりません。
・ gcc ならソースコードを取ってきて、自分でコンパイルすれば、RX用の gcc を使う事が出来ます。
・自分で開発環境を用意する場合、デバッグ環境も自分で整えなければなりませんが、そこは、経験とアイディアで乗り切りますwww
・マイコンのフラッシュメモリーにプログラムを書き込むツールは、無料版があり(Windows環境のみ)こちらは、大きな制限はありません。
・メーカー専用のコンパイラは、gcc より、進んだ最適化が出来るとか言いますが、非常に限られた場合の評価しか宣伝していない為、懐疑的です。
・メーカー製IDEの良い部分としては、J−TAGプローブなどで行うリアルタイムのデバッグ環境などです。

マイコン用クロス開発用 gcc のビルドは、以下の3つのバージョンがマッチしないと、コンパイルに失敗する場合があります。
RXマイコンの場合、最新版は、以下の3つの組み合わせになるようです。
GCC 4.8.3
Binutils 2.24
Newlib 2.1.0

※まだ、自分は4.7.3を使っているので、試していません。

Windows の cygwin 環境では、コンパイルを通す為には、色々なオプションを付ける必要があるようで、大抵標準のオプションだけではコンパイルに失敗します。
又、コンパイルに必要な、ツールのバージョンにも影響するようで、再現性に乏しく困難なので、最近は MSYS(MinGW) でコンパイルしています、こちらは、比較的普通にコンパイルに成功します。
MacOSやLinuxでは、何の苦労も無くコンパイル出来ます、ただし、デバイス内臓のフラッシュROMに書き込むツールが対応していないので、現実的には Windows を使う事になります、現在、コマンドラインから、フラッシュへ書き込むプログラムを独自開発している最中です。

組み込みマイコン用の gcc が、PCの gcc と違う点は、スタートアップルーチンをデバイス毎に用意する必要があり(デバイスによって、持っているRAM、ROMのサイズが違い、初期化を行う方法が異なっているのが普通です)、多少複雑です、マイコンの場合、コールドスタートした状態では、割り込みは禁止され、最低限の設定状態で起動しています。
なので、最初に行うべきは、デバイスの必要な部分を初期化して、スタックや、記憶割り当てが使うメモリーの領域設定、C++の場合は、静的なクラスのコンストラクターを呼び出して初期化するなど、行う必要があります。

rx-elf/lib/rx.ld
RX用 gcc をビルドしたら、上記パスに、スタートアップに必要なファイルが用意されています。
「rx.ld」は、リンカースクリプトで、データセクション、プログラムセクション、など、初期化が必要な領域、初期化を必要とするクラスのコンストラクターやデストラクターのエントリーテーブルなど必要なリンク情報が入っています。
また、リンカースクリプトで、デバイスに適合するRAMやROM領域のアドレスを指定します。

以下は、リンカースクリプトの一部です。

OUTPUT_ARCH(rx)
ENTRY(_start)
MEMORY {
RAM(rwx) : org = 0x00000000, len = 0x00002000
ROM(rx) : org = 0xFFFF0000, len = 0x00010000
}
SECTIONS {
    _usp_init = 0x00001f00;
    _isp_init = 0x00002000;
    .fvectors 0xFFFFFFD0 : {
        vect.o(.fvectors)
    }

...

※ハードウェアーベクターテーブルの指定もあります。

マイコンは電源が入るとリセット信号がアサートされ、ハードウェアーベクターにあるリセットベクター先に飛び、プログラムを開始します。
以下はスタートルーチンで、マイコン独自の命令を実行する為、アセンブラで記述してあります。
※この部分だけはアセンブラを使う必要があります。

	.global _power_on_reset
_power_on_reset:
.global _start
_start:
# スタックの設定
    .extern _usp_init
    mvtc #_usp_init, usp
    .extern _isp_init
    mvtc #_isp_init, isp
# 割り込みベクタの設定
    .extern _interrupt_vectors
    mov.l #_interrupt_vectors, r5
    mvtc r5,intb
    mov.l #0x100, r5
    mvtc r5,fpsw
# Iレジスタを設定し、割り込みを許可する
    mov.l #0x00010000, r5
    mvtc r5,psw
# PMレジスタを設定し、ユーザモードに移行する
    mvfc psw,r1
    or	#0x100000, r1
    push.l r1
# UレジスタをセットするためにRTE命令を実行する
    mvfc pc,r1
    add	#0x0a,r1
    push.l r1
    rte
    nop
    nop
# init() 関数から開始
    .extern _init
    bsr	_init

    .global _set_intr_level
_set_intr_level:
    and	#15,r1
    shll	#24,r1
    or	#0x100000,r1
    mvtc r1,psw
    rts
    nop

    .global _exit
_exit:
    wait
    bra _exit

これは、「init.c」で、main に飛ぶ前に行う初期化を含んだものです。
※RXマイコンでは割り込みベクターのベースアドレスを変更できるので、自分のシステムでは、利便性を考慮して、RAM上に置いてあります。

int main(int argc, char**argv);
extern int _datainternal;
extern int _datastart;
extern int _dataend;
extern int _bssstart;
extern int _bssend;
extern int _preinit_array_start;
extern int _preinit_array_end;
extern int _init_array_start;
extern int _init_array_end;
extern void (*interrupt_vectors[256])(void);
extern void null_task_(void);
int init(void)
{
    // 割り込みベクターテーブルの初期化
    for(int i = 0; i < 256; ++i) {
        interrupt_vectors[i] = null_task_;
    }

    // R/W-data セクションのコピー
    {
        int *src = &_datainternal;
        int *dst = &_datastart;
        while(dst < &_dataend) {
            *dst++ = *src++;
        }
    }

    // bss セクションのクリア
    {
        int *dst = &_bssstart;
        while(dst < &_bssend) {
            *dst++ = 0;
        }
    }

    // 静的コンストラクターの実行(C++ )
    {
        int *p = &_preinit_array_start;
        while(p < &_preinit_array_end) {
            void (*prog)(void) = (void *)*p++;
            (*prog)();
        }
    }

    {
        int *p = &_init_array_start;
        while(p < &_init_array_end) {
            void (*prog)(void) = (void *)*p++;
            (*prog)();
        }
    }

    // main の起動
    static int argc = 0;
    static char **argv = 0;
    int ret = main(argc, argv);
    return ret;
}

これで、main から実行でき、後は、通常のアプリケーションとほぼ同じとなります、最低限これだけあれば、後は、工夫次第で何とでもなります。
※リンカーで、独自のリンカースクリプトを使い、標準のスタートアップを行わないので、「-nostartfiles」を指定します。

最低限のデバッグとして、シリアルインターフェースを繋いで、ターミナルで文字の入出力が必要な場合、stdin、stdout に相当する部分を実装する必要があります。
RX63Tでは無理ですが、RX621では、それらの実装で、iostreamクラスを使え、通常のPCアプリとほぼ遜色無く動作します。
※PCでは、メモリーの消費はあまり気にしませんが、組み込みでは、注意する必要があります。

I/O定義:

組み込みマイコンでのデバイスドライバーに相当する部分は、通常は、Linux のようなシステムとは違い、カーネルによるしばりは無く、自由に直接プログラム出来ます。
であるにも係わらず、未だに、「C」でプログラムする事が当たり前のような状況になっています。
デバイスメーカーが提供するサンプルプログラムも、C++で作られたのは見た記憶がありません。(ARMではあるようです)
※それ以前に、サンプルプログラムの品質が低いと感じます。
趣味で、組み込みマイコンのプログラムをする場合でも、C++は敬遠されている感じがします。

さて、組み込みマイコンでは、I/O(シリアルコミュニケーション、A/D 変換、ポートなど)の操作を直接行いますが、最近のI/Oは高機能で、非常に多くの機能があります。
定義は複雑で肥大化しており、マイコンのバリエーションにより、機能が微妙に異なったりする場合もあります、もはや、一つのヘッダーでは、管理できないと思われます。

たとえば、ルネサスは、マイコン内のI/Oの機能を定義したヘッダーを提供していますが、これは、処理系依存で実装されている為、gcc でそのまま使う事は出来ません。
また、基本的に「C」言語を想定した定義なので、C++でプログラミングしたい場合にあまりメリットがありません。

これは、RX62Nの定義ヘッダーの一部です(A/D変換の部分です)

struct st_ad {
	unsigned short ADDRA;
	unsigned short ADDRB;
	unsigned short ADDRC;
	unsigned short ADDRD;
	char           wk0[8];
	union {
		unsigned char BYTE;
		struct {
			unsigned char :1;
			unsigned char ADIE:1;
			unsigned char ADST:1;
			unsigned char :1;
			unsigned char CH:4;
		} BIT;
	} ADCSR;
	union {
		unsigned char BYTE;
		struct {
			unsigned char TRGS:3;
			unsigned char :1;
			unsigned char CKS:2;
			unsigned char MODE:2;
		} BIT;
	} ADCR;
	union {
		unsigned char BYTE;
		struct {
			unsigned char DPSEL:1;
		} BIT;
	} ADDPR;
	unsigned char  ADSSTR;
	char           wk1[11];
	union {
		unsigned char BYTE;
		struct {
			unsigned char :6;
			unsigned char DIAG:2;
		} BIT;
	} ADDIAGR;
};

ビットフィールドが使われています、これは、処理系依存で、gcc では、LSB、MSBが逆になります。
また、最適化した場合に、適切なビット幅のアクセス(32ビットのI/Oに8ビットのアクセスなど)が行われない場合があり、RO(リードのみ)、WO(ライトのみ)などの表現も無く、読みにくく、使いやすいとは言えないと思います。

そこで、C++をもっと積極的に使って定義ファイルを作ったらどうかと考えます。
下の実装は、上のビットフィールドから、テンプレートを使った実装です。(少し冗長な部分があります)

//=====================================================================//
/*!	@file
	@brief	RX62N, RX621 グループ・AD 定義 @n
			Copyright 2013 Kunihito Hiramatsu
	@author	平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include "io_utils.hpp"

namespace device {

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    /*!
        @brief  AD 定義
        @param[in]  base  ベース・アドレス
    */
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    template <uint32_t base>
    struct ad_t {

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDRA レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        io16<base + 0x00> ADDRA;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDRB レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        io16<base + 0x02> ADDRB;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDRC レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        io16<base + 0x04>	ADDRC;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDRD レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        io16<base + 0x06>	ADDRD;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADCSR レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        typedef io8<base + 0x10> adcsr_io;
        struct adcsr_t : public adcsr_io {
            using adcsr_io::operator =;
            using adcsr_io::operator ();
            using adcsr_io::operator |=;
            using adcsr_io::operator &=;

            bits_t<adcsr_io, 0, 4>	CH;
            bit_t<adcsr_io, 5>	ADST;
            bit_t<adcsr_io, 6>	ADIE;
        };
        static adcsr_t	ADCSR;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADCR レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        typedef io8<base + 0x11> adcr_io;
        struct adcr_t : public adcr_io {
            using adcr_io::operator =;
            using adcr_io::operator ();
            using adcr_io::operator |=;
            using adcr_io::operator &=;

            bits_t<adcr_io, 0, 2>	MODE;
           bits_t<adcr_io, 2, 2>	CKS;
            bits_t<adcr_io, 5, 3>	TRGS;
        };
        static adcr_t	ADCR;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDPRA レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        typedef io8<base + 0x12> addpra_io;
        struct addpra_t : public addpra_io {
            using addpra_io::operator =;
            using addpra_io::operator ();
            using addpra_io::operator |=;
            using addpra_io::operator &=;

            bit_t<addpra_io, 7>	DPSEL;
        };
        static addpra_t	ADDPRA;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADSSTR レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        static io8<base + 0x13> ADSSTR;

        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        /*!
            @brief  ADDIAGR レジスタ
        */
        //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
        typedef io8<base + 0x15> addiagr_io;
        struct addiagr_t : public addiagr_io {
            using addiagr_io::operator =;
            using addiagr_io::operator ();
            using addiagr_io::operator |=;
            using addiagr_io::operator &=;

            bits_t<addiagr_io, 0, 2>	DIAG;
        };
        static addiagr_t	ADDIAGR;

    };
    typedef ad_t<0x00088040>	AD0;
    typedef ad_t<0x00088060>	AD1;
}

io_utils.hppは、git にありますので参照の事。
今までにこのような取り組みは多少あり、同じような考えによる実装はいくつか参考にした事がありますが、不満な部分などがあり、結局自分で考えて実装したものです。
・ビットと機能の対比がわかりやすい。
・8、16、32ビットアクセス、RO、WO、RWを表現できる。
・最適化レベルに関係無く、厳密なアクセスを行える。
・エンディアンに依存しない。
・処理系に依存しない。(実際は gcc 以外で試していませんが、x86 の clang でシュミレーターで実験しています)
・最適化されたアセンブラソースを確認して、これなら満足できると思う。
※但し、レビューもされていませんし、もっと良い方法、適切な実装があるかもしれません。
※参照RX、I/O関係
※RO、WO、RWを分ける必要があるので、読み出しの場合は、()オペレーターを使っています。

クラス化した事で色々な恩恵を受けられます。
たとえば、シリアルコミュニケーションは、通常複数チャネルがありますが、I/O部分のアクセスをクラス化した事で、かなりスッキリ書けるようになり、異なったチャネルの操作もテンプレートで簡単に記述できるようになりました。

I/O操作をC++で書けると何がありがたいかと言うと、テンプレートが使える事で、微妙な違いを、隠蔽して、判りやすくて使いやすい入出力クラスをシンプルに書けます。

組み込みでC++を使うべき理由の一つです。

最後に:

組み込みの世界では、何故か、C++が敬遠される傾向にあると感じがしますが、C++こそ、組み込みに適した言語と思えてなりません。

これらRXマイコン関連の成果は、以下のリンクで公開していますので、興味ある人はどうぞ。

RX関係ソースコード

ウィンカーの LED 化(その1)

前から改造してみたかった、ウインカーのLED化を行っている。

まず、電球をLED化する、球切れの心配が無くなり、消費電力が減る。
それとLEDは、点灯、消灯がハッキリしていて視認性も良くなるのではと思う。

改造したバイクはヤマハのWR250Xモタードで、標準的には10Wの電球が付いていた。
IMG_0679s
ポジションは無く、1灯式バルブ。
※LED化する事でウィンカーリレーも自作するので、ポジションランプ機能も加えようと思う。

口金は「BA15s」と言うタイプで、ピンが水平に出ている。
※千石電商で購入

自作する場合に考えなくてはならない事は、以下のような点・・
・頑丈で、振動などで、点灯不良にならない構造。(接着剤での接着などを極力避ける)
・最低でも4個は必要なので、作りやすさ、同じ物が作れる(再現性の確保)事。
・売っているLEDバルブには無い機能と性能など。

以上のような点だろうか・・

最初に問題となるのは、口金にLEDをどのように固定するか?
そこで、以前に自動車の車内を照らすシーリングライトをLED化した時の手法を応用した。
まず、口金にユニバーサル基板をピッタリサイズで切り出し差し込む、この時、スルホールが、口金の金属と近くなるようにする。
基板には、垂直に基板を固定する為のボスを作っておく。
IMG_0676s

基板を中に入れて、スルホールにハンダを流し、口金の金属部分にもハンダを流して固定する。
※それなりにハンダを流し込めば、ある程度の強度は確保出来ると思う。
IMG_0678s

蓋をするように基板を切り出す、大体円になるように基板を削る、中心も、先ほどの基板のボスが入るようにリューターで削る。
※10Wの電球は外形19mmくらいなので、それ以下の大きさにする。
IMG_0680s

IMG_0681s

さて、LEDはどのような物を選ぶのが良いか?、何個くらいを並列にするか?

LEDの性能は、日進月歩で、日増しに効率が高くなっている為、「使いたい時にまとめて買う!」のが良いと思う。
今回選択したのは、このLED「OSWX4EZ4E1P」リンクは秋月のものだが、適当なLED屋さんでまとめて買った方が安いと思う(自分は千石の手前のLED専門ショップで50個1000円で買った)

このLEDは、3個の白色LEDが直列になっていて、90mAまで電流が流せる、その場合30ルーメンと言う明るさ。
LEDは、 全ての範囲で電流と明るさが比例するわけではなく、効率の良い電流値があるものだ、その電流なら、発熱も少ない。
今回は実験して、10Wの電球との明るさの違いなどを調べ、1個辺り、約20mA~30mA程度にし、全部で9個を使う(大体全体で200ルーメン程度だと思う)のが丁度良いと思った。
※LEDを並列にして使うので、特性が揃っている必要がある為、同じロットの物を選ぶ必要がある。
※それでも10Wの電球よりかなり明るい。
※ある程度数を多くして分散させた方が、光が集中しないで電球と同じように拡散させる事が出来る。

IMG_0683s
実験用として作ったLEDアレイ

LEDに流れる電流の制限抵抗は、以下のように計算した。

・電源の電圧を13Vとする。
※LED保護と汎用性を考えて、ショットキーダイオードによるブリッジを組む、こうすると、極性が事実上無くなるので、扱いが楽になる。
・ショットキーダイオードの順方向電圧降下を0.4Vとする。
・LEDのVFを3.1Vとする。

25mAの電流を流すには?

13-0.4×2-3.1×3 / (25mA×9) = 13オーム

※適当な抵抗が無かったので47オームを4本並列とした。

まず、下の列、5個を直線で繋ぎ~
IMG_0684s

適当に曲げて五角形にする。
IMG_0685s

それを、口金のベースに並べて端子をハンダ付け。
IMG_0686s

最後に正面用アレイ:
まず、2個のLED、サイドを斜めに削って、斜めに密着させる。
IMG_0687s

それを2組作り、やはりサイドを斜めに削り、密着させて4個のアレイにする。

それを、上に載せて隙間からハンダを流して接続、出来上がり~
IMG_0689s
※順番を考えながら、ショートに注意して組み立てる、失敗すると修正が大変なので注意。
※4隅の角を少し削って19mmの直径に収まるようにする。

やっと1個出来た・・・、あと3個、とりあえず今日はここまで。

俺俺フレームワークのWindowsへの対応

OS-X 対応で、Windows はまぁとりあえずスルーしても良いかなぁーって思ったけど、やっぱり折角動くもんなら、
少しの労力でマルチプラットホームになる事だしサポートしようと思い、少しばかり修正した。

MSYS の Mingw 環境では、C++11 の thread 関係 API をサポートしていない。
そこで、それら API を使わないように修正をするだけの事で、Mac、Windows 両対応となった。

内部ソースには、機種依存の条件分岐が色々あるのだが、それらは、今後少しづつ無くしていき、クリーンな状態
へ移行する。

以前のブログで、Mingw 環境の構築を少しだけ載せたけど、新しいフレームワークでは、Mac 対応で、少しばかり
環境を更新したので、その辺りを少し解説しておく。

(1) MP3 タグ情報ライブラリー
以前のシステムでは、tag 情報を取得するライブラリーとして、id3tag ライブラリーを使っていて、機能的にどう
しても不足していた部分やバグを修正して使っていたのだけど、色々探したけど、修正したソースが無い事が判った・・
そこで、少し探して、簡単に使えそうなライブラリーとして、taglib を検討してみた、ソースは C++ なんだけど、
かなり、昔の C++ って感じで、イマイチ感があったけど、他の候補を色々探して、検討するのも時間がかかりそう
なので、とりあえず、このライブラリーを使わせてもらう事にして、実装した。
なので、Windows でも、このライブラリーをコンパイルしてインストールする必要がある。
※taglib-1.9.1 を使った。

(2) OpenJPEG ライブラリー
macport でインストールした OpenJPEG ライブラリーは 2.1 で、API も少し違う為、Windows も 2.1 にアップ
デートした。

(3) JPEG ライブラリー
macport では、MMX で高速化した JPEG ライブラリーとして、libjpeg-turbo がある、またこのライブラリーは
若干の拡張がしてある。(Alpha チャネルを扱える)
そこで、Windows でもこのライブラリーを使いたいのだけど、コンパイルする為に、nasm が必要で、インストール
が複雑になるので、とりあえず、Windows では以前のライブラリーを使う事にした。
※アルファ付き画像をセーブした場合、アルファチャネルは無くなる。

(4) Makefile の改修
Windows では、Makefile を共有する事が難しい為、Makefile.win と別にした。
※この辺りも、cmake を使うとか、今後工夫が必要と思う。

以上の対応で、音楽プレイヤーは同等の動作をするようになった。

※最新版ソースコードは GitHub に push してある。

俺俺 RX マイコンボードを作ろう!(その1)

最近は、フルタイムで仕事をしている関係で、趣味の時間が凄く制限されていたのだが、仕事もだいぶ安定してきて、趣味の時間を持てるようになってきた。
それと、「この暑さ」、休みの多くは、ファミレスにノートを持ち込んで、主にソフトの制作などを行っていた。
最近、少し暑さも和らいだので、ハードの作業もしようかと思案していたら、秋月で良い物を見つけた。

128Mbits SDRAM 133MHz
まぁ、別にそんな珍しい物でも無いけど、デバイスを単体で扱っているのは珍しい、数個単位で買えるのはありがたい。
※多分、秋月で、SDRAMが乗ったマイコンボードでも出るのだと思う。

そして、マルツパーツで 176ピン、フラットパッケージ版 RX63N が単体で買える事が判ったのが最後の一押しとなった。

「176ピン、フラットパッケージ」は重要。
・0.5mm ピッチなので、ギリギリ、手ハンダができる。
・32ビットバスでSDRAMを接続出来る。(フルスピードで動かしても、性能があまり落ちない)
・内蔵 RTC のバックアップが出来る。
・外部バスを使っても、残りの空いたピンはかなり潤沢で、色々活用出来る。

そして、前から、RX マイコンの大容量メモリーを積んだボードが欲しかった。
・開発の効率などを考えると、プログラムをRAMに転送して実行したい。
・RX マイコン用の LLVM を作ってみたい、それには、ある程度メモリーが潤沢に使えるボードが必要。
・組み込み機器でも、メモリーが潤沢に使える環境が欲しい。
※大容量メモリーが無いと、画像ファイルなどを扱うのが難しい。
・PC 用フレームワークのソースコードをそのまま利用したい。
・安いボードじゃないと、色々な物に使う気になれない。
※「なひたふ」さんとこで、究極の RX62N ボードを扱っているのだけど、値段が高くて手が出せない。
※構成的には、同じようなものになってしまうのだけど・・・

他にも色々な動機がある〜

もうそろそろ、RX64M シリーズが流通しそうな感じなのだが、KiCAD を使ってボードも作ってみたかったので、汎用的で、応用が効く俺俺ボードを自分で作ってみる事にした。

とりあえず、構成はこんな感じにする予定〜
・RX63N(R5F563NEDDFC#V0)176 ピンパッケージ(内蔵、256K RAM、2048K Flash)
・内蔵 RTC 用外部バッテリーバックアップ
・128M ビット SDRAM x 2、32ビットバス接続(32Mバイト)
・10/100 インサーネット(PHY層: LAN8720AI)※別モジュールにするかも・・
・マイクロSDカードインターフェース
・リチウムイオン/ポリマー、充電コントローラーと DC/DC コンバーター
・A/D 入力用バッファアンプと電圧リファレンスなど
・オーディオインターフェース(VS1053B)
・USB インターフェース(2チャンネル分)
・未使用ポート用ピンヘッダー
・J-TAGコネクター
ちょっと盛り過ぎな感じもするのだが、使わない物は載せなければ良いと思うので・・・
※完成したら、ボード単体で販売する予定(時価)だが、欲しい人は少ないかもしれないねwww
※SDRAM は、@300なので、2個載せても600円、しかも32Mバイトの空間を使えるのは便利

そもそも、R-PiやBBBが5千円くらいで買える現実を考えると、0.1GHz程度のマイコンなんてと思うのだが、OSが無く、電源投入で、いきなりアプリを動かせ、電源をいきなり切れるような、組み込みの用途は必要だと感じているし、0.1GHzで十分な用途はけっこう多い。
またLinuxベースで、ハードウェアーに依存した細かい制御を書くのは、面倒でもあるし、リアルタイム的な制御が難しい場合もある。
※内蔵カウンターを使ったタイマーや、PWM 出力、割り込み制御など。

別の動機として、販売されている組み込み用ワンボードマイコンが高価過ぎるのも問題と思う。
趣味では無く、会社の形態として販売する場合、あのような値段になる事は理解出来るので、自分で作るしか無い。
イニシャルコストはかかるのだが、3枚、同じボードを使う事を考えたら、作った方が安い事になる。
※この場合、制作に関わる人件費を計上してないのだけどwww

まず、マルツパーツで、R5F563NEDDFC#V0(RX63N, Flash: 2MB, RAM: 128K) を注文した、1912円もしたけど、数個単位で買えるのはありがたい。
数日で、物は届いたのだが、梱包が微妙、足が曲がっている感じがする・・・
とりあえず、交換の手配をメールで問い合わせたのだが、写真を送って欲しいとある。
0.5mm ピッチだと、ルーペで拡大しても、何とも言えないのだが、梱包のやり方に難があるように思う。

IMG_0658s

導電スポンジにチップを載せた状態で、ラップして内部の空気を抜いてあるのだが、空気を抜く事でスポンジが縮み、ピンにストレスがかかっていて、反っているじゃないかと思う。
ラップを破れば、空気が入って、反りは戻るかもしれないが、微妙に反った状態になるかもしれず、非常に微妙・・・
普通は、もっと安全確実な方法で梱包すると思うのだが、デリケートな品物だけに、強引すぎると思う。
※以前に RX63T を注文した時は、こんな強引な梱包をしてこなかった。

「ピンが曲がっていたら交換してくれる」と言う事なので、意を決して、パッケージを開けてみたー
ピンが曲がっていたと思えたのは、ピンが導電スポンジにめり込んでいる為だったようで、購入した3個、全て問題無かった。
でも、もっと「やんわり」梱包する方法はあるハズで、そこは改善して欲しいとこ。

早速、変換基板にハンダ付けしてみた。
※変換基板は、今まではダイセン製を好んで使っていたが、秋月で良さそうな物を売っていたので購入してみた、値段は安いけど、品質は満足のいく代物。
やっぱ、0.5mmピッチは厳しいなぁー、176ピンもあるので大変、それと、ハンダコテの温度設定が低くなってて、難儀した。
ハンダ吸い取り線による、ブリッジの除去も、温度が低かったせいか、難儀した・・・

IMG_0664s

ルーペで拡大して、念入りに確認したので、まぁ、大体大丈夫と思う、多少怪しい部分もあるのだけど・・・

追記:(2014年9月21日)
この変換基板、重大な問題がある事が判りましたー、まぁ176ピンの変換基板を使う人は限られると思いますが・・

IMG_0665s

↑写真で判るように、縦と横のピンヘッダー取り付け部分のグリッドが 2.54mm ピッチでは無く、そのままでは、一般的な
蛇の目基板に取り付け出来ません・・・

※twitterでツイートしたら沢山の方にリツイートしてもらいました・・・
※秋月には、報告しません、どうせしても無駄だから・・・、以前にSDカードモジュールの結線ミスに気がついて、報告しましたが、
何の返信もありませんし、商品のQ&Aに情報が共有される事もなく無視されました、なので、今回は何もしません。
秋月は、商品の価格が安く、それゆえ、自作をするものにはメリットが大きいし、利用している人は多いのですが、
アフターケアに関しては、全く駄目なように思います、それを判った上で最大限利用するしかありません。
※現在は、ピッチがズレている事が明記されているようです。

※以前に秋月にメールした全文

お疲れ様です。

K-05818「SDカードスロットDIP化モジュール」について
参考資料で、CD(Card Detect)となっている信号は、ライトプロテクト信号のようです。
この商品に使われているソケットは、「Card Detect」用の端子がありますが、(4番)、GND(5番)とショートされており、使う事が出来ない仕様となっています。
カッターなどで、パターンを切って、(4番)を引き出すと、「Card Detect」として使う事ができました。
Q&A等に、情報として記入する事をお勧めしたいです。

それでは宜しくお願いします。

—–
普通は、いきなり基板を設計するかもしれないが、個人制作なので、とりあえず、ユニバーサル基板で、試作してみる事にする。
まぁ時間はかかるけど、作って動かしてみないと判らない事も多くあるのではと思う。

C++11 future の実装依存?

C++11 では標準ライブラリーでスレッド関係の API が用意されている。

future テンプレートクラスは、お手軽に、平行処理で結果を受け取る方法としてコンビニエンスなやり方だ。

std::string task_(const std::string in)
{
    std::string text;

    // 何かの処理・・・

    return text;
}

std::string in;

std::future<std::string> f = std::async(std::launch::async, task_, in);

こんな感じで、簡単に実行委譲が出来る。

——
最近、俺俺フレームワーク内で、音楽ファイルのタグ情報を future を使って取得する実装を書いた。
しかしながら、思ったようには動作せず、色々調べたり、実験を行った。

結論を言うと、これは、OS-X、clang の C++11 ライブラリーに起因した問題のように思う。

症状としては:
・メインプログラムから、future を使って、タスクを起動しても、メインプログラムが全然軽くならない・・・
※ future で起動したタスクは、メインプログラムの負荷として加算される感じ。
・フラグがあって、それで「別スレッド」を指定してもだ・・( std::async::lanuch )
※ Android でも試したが、そもそも「別スレッド」を指定出来ない(コンパイルエラーとなる)ので、これは正しい動作なのかもしれない。
※これは clang3.4 で使っているライブラリーの問題なのかもしれないが、情報は探せなかった。
※ Windows の gcc や clang では、C++11 thread API 関係が実装されていない事と何か関係がありそうだー

結局、pthread を使って、やりたい事は出来たのだが、「future 便利だー」って盛り上がってたのに・・
何ともかんとも・・・
※ C++11 にも thread 関係があるのだけど、future で懲りたので、あえて、pthread を使った、pthread でもそんなに複雑化せずに平行処理を書ける。
ただし、future を使った場合と比べると実装量は増えるが、厳密な処理負荷は、少ないだろう。
もうしばらくしたら、C++11 の thread を本格運用して試す事とする・・
※石橋を叩いて渡る〜

OS-X でC++アプリケーション(GLFW3)

BqNTjU-CEAArsW9

今まで、自分で細々と作っているフレームワークはGLFW3をベースにしているものの、Windows 環境しかテストしていなかった。
MacBook Pro を買ったので、Mac に対応させるべく作業をした。
最近はフルタイムで仕事をしているので、自分のプログラミングは時間が限られる、最近ようやく、Windows 版と遜色なく動作するようになったので、要点をまとめてみたい、同じような事をしている人に参考になればと思う。

・カレントディレクトリーの扱いが違う。
アプリケーションが動作しているカレントディレクトリーの、挙動が異なるようだ。
Mac(unix) では基本的に、コンソールから起動した場合と、finder などから起動した場合で、main 関数のパラメーター「argv[0]」に入ってくるパスが異なる。
unix では、アプリケーションのカレントディレクトリーの管理は、シェルが行っており、シェルから起動しない場合を想定しておく必要がある。

Windows では、アプリを起動させたパスをカレントパスとして設定している。

良く、アプリを置いたパスに初期に読み込むファイルなどを置いておく事があるけど、何らかの方法で、相対パスから絶対パスを生成する必要がある。
自分は、「argv[0]」にアプリケーションの起動パスがあるので、それを使って何とかした。
※また、シェルから起動した場合は、argv[0]には、「./xxxx」など、シェルのコマンドラインがそのまま反映されるだけなので、「getcwd」APIなどで、カレントディレクトリーを取得する必要がある。
しかしながら、「getcwd」は、シェルから起動しないと正しいパスを返さないようだ。

・ランタイムライブラリー(要するに DLL、unix 的にはシェアードライブラリー?)
OS-X では、コンパイル時のライブラリーのリンクが固定していて、実行バイナリーを他の環境に持って行く場合には、ライブラリーを期待した場所に置いておく必要がある。(これは設定ファイルで変えられるかもしれないが調べていない)
俺俺フレームワークでは、色々なオープンソースを使っているのだけど、OS-X ではコンソールからコンパイルしてインストールすのが、比較的楽なので、問題は少ないかもしれない。

・システムフォントのパス
当然だけど、OS-X では、フォントパスが違うので、それを対応する必要がある。
ちなみに

/System/Library/Fonts

となる、標準的日本語フォントは

ヒラギノ角ゴ ProN W3.otf

を使っている。

・コンソールの扱い
Windows(mingw)では、リンカーのオプションに「-m window」があり、コマンド起動後にコンソールを表示して、stdout や stderr の表示をするなどに対応出来るけど、OS-X では、必ずコンソールも起動してしまう。
コンソールが必要無い場合はどうするのか?、謎だったけど、色々調べたら、フォルダーで全体を管理するようだ、その際、アプリケーションのアイコンやら、何やら、色々な設定を「info.plist」の XML ファイルに記述する方法のようで、Windows のアプリケーションとは全く考えが違う。

・解像度に対しての対応
MacBook Pro riteina では、高解像度の液晶を使っている、解像度が高い為、見た目の大きさを揃える為には、Window の仮想サイズと、実際のサイズ(フレームバッファのサイズ)が異なり、それ相応の対処をしなければならない。
OpenGLでは、「glViewport」で指定するサイズは、実際の解像度ベースで指定して、プロジェクションマトリックスで指定する大きさは、仮想サイズで指定することになる。
仮想サイズは、実際のサイズの丁度半分となるようだけど、自由なサイズに対応しておく必要がありそうだ。

・スレッド
OS-X は pthread に対応との事だが、pthread の仕様を全て満足している訳では無く、使えるのは一部の API だけなので注意が必要だ、特に悲惨なのは、コンパイルは通るけど、API は何もしないで、エラーコードと共に素通りする。
※名前無しセマフォなど
※現状では、POSIX の API に完全に全て対応しているのは Linux カーネルのみなのかもしれない。
OS-X で pthread のプログラムを作成する場合には、注意して設計する必要がある。
boost や、C++11 の API を使うのが賢いかもしれない。
ただ、現状では、Mingw の環境では、C++11 でも、thread をサポートしていない為、マルチプラットホームでは、コードを共有出来ない。
※一方 Windows には、pthread_win32 ライブラリーがある為、pthread のプログラムを作成するのに、問題は少ない。

・OpenGL プログラムのスワップフレーム
OS-X では、OpenGL のプログラムを待機状態にしたり、他の Window で全て隠すと、スワップフレームが画面のリフレッシュと同期しなくなり素通りになる為、実質的に、描画ループが最高速で回り始める、そうすると、CPU の空き時間を全て食いつぶして負荷が100%に近くなる。
これは、色々調べたけど、「仕様」のようだ、そこで、この問題を回避する対策が必要だ。
自分のアプリケーションは、GLFW3 を介して、システムと繋がっている為、「待機状態」を検出する方法が無い為、別の方法で、この問題を回避した。
(1)16ミリ秒のタイマータスクを用意する。(usleep、nanosleep など)
※ OS-X の sleep 系は意外と正確なようだ。(Windows のタイマーが不正確すぎなのかもしれない)
(2)フレームの先頭で、先に用意したタスクをスレッドで起動する。(C++11 の future を利用した)
(3)スワップフレームの手前で、先のスレッドの終了を待つ。
この戦略は、フレームレートが 60Hz(16.677ミリ秒) の場合、タイマーが正確な場合を前提としている。
※オーバーヘッドが、0.677 ミリ秒より大きい場合は、待機時間を調整する必要がある。
※スレッドの追加で、システムの負荷が1〜2%増加する。(意外と重い)
※プログラムは、common/glcore.hpp, common/glcore.cpp を参照
※ Windows では、プログラムが待機状態になると、メインループは停止するので、回り続ける仕様自体は、歓迎されるべきなのかもしれない。
なので、Windows では、動かし続ける必要のあるタスクは、メインループに依存しないように設計する必要がある。

——————–

OS-X は、実質 unix マシンなので、X-code を利用しなくても、コンソールとテキストエディターだけで、遜色無くアプリケーションを開発できて、非常に快適な事が判った。
コンソールは多国語対応で(当然日本語にも)、複雑な設定をする事なく、日本語をスマートに入力、表示出来る。
emacs もコンソールで起動でき、日本語入力にも対応している。
今まで食わず嫌いで、OS-X を使わなかったけど、まぁ、なかなか良い。

MacBook Pro を買う

以前に、Mac Mini を購入して、iPhone のソフトでも作ろうと思い色々やっていたけど、ある程度「俺俺フレームワーク」が動くようになってからは、Windows 環境に移って、放置していた。

ノートPCを新規に購入する時も、MacAir13 とかで悩んだけど、画面の解像度が低いのと、ビデオデバイスが、インテル内蔵でショボイので、敬遠して、NVIDIA のビデオデバイス内蔵で、薄くて軽い、ASUSのUltraBookを購入した。

知り合いに、「今時、Windows」などと言われて、「Mac が一番だと思ってるやつ」の心の狭さに、引いたー。

そんで、最近、仕事で Android のソフト開発をするようになったんだけど、Windows では、開発出来ない状況を経験だった、(みんな Mac を使っていた)。
これは、NDKで開発しているのだけど、C++ のソース数が異常に多くて(350ファイルくらいある)、Windows(cygwin) では、コマンドラインで受け付けるバッファがオーバーフローしてしまい、リンク出来ない。
※情けないぞ!

仕方無いので会社では Mac を使っていたが、たまたま、Mac のホームページを観にいったら、最近出た、インテルの新しい CPU を使い、Retina ディスプレイで、光学ドライブが無く、薄型で、軽い(1.57キロ)「MacBook Pro」が目に止まり、興味をそそられた。
会社で使っているのも Mac なので、ビデオデバイスが、インテルでショボイけど、ここらで、MacBook も欲しいなぁーと思い、ポチってしまった!
メモリーは、折角だから 16GB とかにしておいたが、かなりの高額な物になってしまったが仕方無い・・・

買って使ってみると、思いの他快適な事に少し驚いたが、何もかも Mac で完結する訳でも無いので、悪くは無いよねーって、少し強がってみる〜www

そんで良かったと思う事などー

・タッチパッドの操作性が凄く良い!
※UltraBook も少し見習うべき!
※このくらい操作性が良いと、普通の操作ではマウスは必要無いなぁー

・電池が凄く持つ!
※これは新しいインテルの CPU も貢献しているのかも。

・電池で動作させていても、電源を繋いでいる時とあまり変わらない操作性。
※よく出来ている〜

・基本 unix なんで、MinGW とかから違和感無く使え、ターミナル上で、emacs も普通に動く。

駄目なとこは・・・

・Windows にあって Mac に無いフリーソフトが結構あって、多少辛いとこもある。

・やはりシングルボタン系では厳しい場面がある。

・値段が少し高い。
※確かに、細かい部分の作り込みなど、非常に良く考えられていて、素晴らしい完成度なのだが、UltraBook とかの同列機から考えると、2割くらい割高だから、当たり前とも言えるか・・・

・ビデオデバイスが選べず、インテル内蔵のショボイまんま
※これは全体の構成を考えると、仕方無い部分もある、セカンダリーのデバイスを乗せると、電気も食うし、色々と考えなければならない事が多くある、セカンダリーのビデオデバイスを必要とする人も凄く少ないから、まぁしょうがないな・・・

そんなこんなで、結構快適に使ってますーwww

MacBook Pro 13 retina

次は、Windows で作っていた、「俺俺フレームワーク」を Mac に対応したので、次のブログで紹介したいと思います。

Makefile による別ディレクトリーへのオブジェクト、従属規則のセーブ

今まで、従属規則の生成には、X11 のコマンドであるところの「makedepend」を長らく使って来た、しかし、makedepend コマンドが無い場合(X11 関係アーカイブをインストールする必要がある)
や、コンパイラがインクルードファイルを検索する挙動とは多少違う場合もあったりしていた。
そこで、「gcc」の「-MM」オプションを使ってインクルードパスを収集する方法に切り替えた。
※このコンパイラーオプションは、コンパイラー自らのパスでインクルードファイルのフルパスを出力する、この時、「#if、#endif」などの制御文なども評価する。

通常、コンパイラーは、ソースコードと同じ場所にオブジェクトを生成する、ソースコードを共有して、複数のアプリケーションを作成すると、それでは、不具合が起こる場合もあり(コンパイラーオプションや、デバッグ、リリースビルドなど)、アプリケーション毎に、生成されたオブジェクトコードを管理したい。
そこで、生成オブジェクトを特定のディレクトリーに集中させるようにした。

また、複数のアプリケーションで Makefile の修正を最小限に出来るように、多少の工夫をしてみた。

Makefile の構文を学習しなおしたり、google 先生に教えてもらった make 関係のリンクを観たりで、自分の思ったような構成に出来たので紹介する。

ディレクトリーは以下のような構成にしている。

project:
    common:      ---> アプリケーション共通のソースコードを格納するディレクトリー
        ...:     ---> ソースモジュール毎のディレクトリー
            ...  ---> ソース郡
        ...:
            ...
        ...:
            ...
    xxxx:           ---> xxxx アプリケーションディレクトリー
        xxxx.cpp    ---> xxxx アプリケーション固有のソースコード
        Makefile    ---> xxxx アプリケーションの Makefile
    yyyy:           ---> yyyy アプリケーションディレクトリー
        yyyy.cpp
        Makefile

・各アプリケーションで共通するソースコード郡を「common」ディレクトリーに配置している。
・アプリケーションは、専用ディレクトリーを設けて、そこで、生成された二次ファイルを格納する。
※通常、「release」、「debug」のどちらか

一番のキモは、-MM コマンドの仕様で痛い部分を回避する為の小細工で、以下のリンクを参考にした。
※スイマセン、殆ど同じような物です、VPATH の指定が追加されたくらいです。

Makefile 別ディレクトリに中間ファイル & 自動依存関係設定

この問題は、オブジェクトファイルのパスがフルパスにならない点で、これを改善する為、sed による整形を使って、回避している。
リンクの sed スクリプトでは、「.o」のマッチングが不十分で、特定のファイル名の組み合わせなどで整形を失敗する、そこで、適切に修正した。
「\.o:」「.」を「\.」にする事で、正確に「.」(ピリオド)にマッチするようになる、また、一応、セパレーター「:」を追加して、厳密にマッチするようにしてある。
※詳しくは sed の仕様、-MM の説明を参照の事

「VPATH」を指定すると、make が、ファイルを検索するルートを指定出来るので、関係ソースコードを羅列する部分がスッキリする。

・以下の例は、アプリケーション「player」の Makefile です。
・「TARGET」アプリケーション名(Windows では .exe を追加する)
・「ICON_RC」アプリケーションアイコン指定がある場合、リソースの記述を行う
icon.rcの例

EXE_ICON ICON res/player.ico

・「BUILD」ビルドディレクトリー(debug、又は、release)
※従属規則 *.d 、とオブジェクトファイル *.o が格納される。
・「VPATH」共有ソースコードのルートディレクトリー
・「CSOURCES」C 言語ソースコードのリスト
・「PSOURCES」C++ 言語ソースコードのリスト
・「STDLIBS」標準的ライブラリーのリスト
・「OPTLIBS」オプションライブラリーのリスト
・「INC_SYS」システム系のインクルードパス
・「INC_LIB」ローカルライブラリーのインクルードパス
※従属規則に含まれない
・「PINC_APP」C++ 言語用、インクルードパス
・「CINC_APP」C 言語用、インクルードパス
※従属規則に含まれる
・「LIBDIR」ローカルライブラリーオブジェクトのリンクパス

# Makefile
TARGET    = player.exe

ICON_RC   = icon.rc

# 'debug' or 'release'
BUILD     = release

VPATH     = ../common

CSOURCES  = minizip/ioapi.c \
            minizip/unzip.c

PSOURCES  = main.cpp \
            player.cpp \
            core/glcore.cpp \
.....

STDLIBS    =
OPTLIBS    =    glfw3 glew32 opengl32 glu32 gdi32 imm32 \
                pthread \
                openal winmm dsound \
                png16 jpeg_x86 openjp2 \
                freetype \
                id3tag \
                z \
                mad \
                faad mp4ff

INC_SYS     =    /usr/local/boost_1_54_0

INC_LIB	    =    /usr/local/include \
                 /usr/local/include/libpng16 \
                 /usr/local/include/libjpeg_x86 \
                 /usr/local/include/openjpeg-2.0 \
                 /usr/local/include/freetype2

PINC_APP    =   . ../common
CINC_APP    =   . ../common
LIBDIR      =   /usr/local/lib

INC_S   =    $(addprefix -I, $(INC_SYS))
INC_L   =    $(addprefix -I, $(INC_LIB))
INC_P   =    $(addprefix -I, $(PINC_APP))
INC_C   =    $(addprefix -I, $(CINC_APP))
CINCS   =    $(INC_S) $(INC_L) $(INC_C)
PINCS   =    $(INC_S) $(INC_L) $(INC_P)
LIBS    =    $(addprefix -L, $(LIBDIR))
LIBN    =    $(addprefix -l, $(STDLIBS))
LIBN   +=    $(addprefix -l, $(OPTLIBS))

#
# Compiler, Linker Options, Resource_compiler
#
CP      =    g++
CC      =    gcc
LK      =    g++
RC      =    windres

POPT    =   -O2 -std=gnu++11
COPT    =   -O2
LOPT    =

PFLAGS  =   -DWIN32 -DHAVE_STDINT_H
CFLAGS  =   -DWIN32

ifeq ($(BUILD),debug)
    POPT += -g
    COPT += -g
    PFLAGS += -DDEBUG
    CFLAGS += -DDEBUG
endif

ifeq ($(BUILD),release)
    PFLAGS += -DNDEBUG
    CFLAGS += -DNDEBUG
endif

# 	-static-libgcc -static-libstdc++
LFLAGS	=

# -Wuninitialized -Wunused -Werror -Wshadow
CCWARN	= -Wimplicit -Wreturn-type -Wswitch \
          -Wformat
CPPWARN	= -Wall

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

ifdef ICON_RC
    ICON_OBJ = $(addprefix $(BUILD)/,$(patsubst %.rc,%.o,$(ICON_RC)))
endif

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

all: $(BUILD) $(TARGET)

$(TARGET): $(OBJECTS) $(ICON_OBJ) Makefile
    $(LK) $(LFLAGS) $(LIBS) $(OBJECTS) $(ICON_OBJ) $(LIBN) -o $(TARGET)

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

lt;

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


lt;

$(ICON_OBJ): $(ICON_RC)
$(RC) -i


lt; -o $@

$(BUILD)/%.d : %.c
mkdir -p $(dir $@); \
$(CC) -MM -DDEPEND_ESCAPE $(COPT) $(CFLAGS) $(INC_C)


lt; \
| sed 's/$(notdir $*)\.o:/$(subst /,\/,$(patsubst %.d,%.o,$@) $@):/' > $@ ; \
[ -s $@ ] || rm -f $@

$(BUILD)/%.d : %.cpp
mkdir -p $(dir $@); \
$(CP) -MM -DDEPEND_ESCAPE $(POPT) $(PFLAGS) $(INC_P)


lt; \
| sed 's/$(notdir $*)\.o:/$(subst /,\/,$(patsubst %.d,%.o,$@) $@):/' > $@ ; \
[ -s $@ ] || rm -f $@

run:
./$(TARGET)

clean:
rm -rf $(BUILD) $(TARGET)

clean_depend:
rm -f $(DEPENDS)

dllname:
objdump -p $(TARGET) | grep "DLL Name"

tarball:
tar cfvz $(subst .exe,,$(TARGET))_$(shell date +%Y%m%d%H).tgz \
*.[hc]pp Makefile ../common/*/*.[hc]pp ../common/*/*.[hc]

bin_zip:
$(LK) $(LFLAGS) $(LIBS) $(OBJECTS) icon.o $(LIBN) -mwindows -o $(TARGET)
rm -f $(subst .exe,,$(TARGET))_$(shell date +%Y%m%d%H)_bin.zip
zip $(subst .exe,,$(TARGET))_$(shell date +%Y%m%d%H)_bin.zip *.exe *.dll res/*.*

-include $(DEPENDS)

完全な Makefile は Github にプシュした!

C 言語よりお得な C++ その10

以前に、std::iostream に代わる小規模なクラスの紹介をしました。
その中で、std::iostream に馴染めなくて、printf 形式が忘れられない人の為に、「boost::format.hpp」を紹介しました。
しかしながら、「boost::format.hpp」は、std::iostream に依存している為、そのままでは、結局リソースを大量に消費してしまい、小規模な組み込みマイコンでは使えません。
そこで、機能を絞った簡易的な format クラスに相当する物を実装してみましたので紹介します。
※機能が足りなければ、自分で拡張する事も出来ると思います。
※本家では、エラーの場合は、例外がスローされますが、それでは使いにくいと思い、エラー関数クラスでハンドリングするようにしています。
※「例外」をスローさせたい場合は、エラー関数から、例外を投げれば良いと思います。
※組み込みマイコン向けに、A/D 変換などの値(整数)を、10進表示する場合に小数点位置を指定して、それを簡単に表示できるようなフォーマットも用意しました。

このように使います。

    int x = 1095;
    int y = 123;
    utils::format<output>("Pos: %d, %d\n") % x % y;

Pos: 1095, 123

    int adv = 257;
    utils::format<output>("A/D Ch0: %2.4:8y\n") % adv;

/// 2.4 ---> 実数2桁、小数4桁。
/// :8 ---> 小数点以下8ビットとして扱う。

A/D Ch0:  1.0039

ここで「output」は、文字の出力クラスで、以下のような定義を行います。
struct output {
    void operator() (char ch) {
        serial_out_(ch);  ///< ターミナルへ文字出力
    }
};
「operator()」を定義する事で、以下のように関数オブジェクトとして使えます。
    output o;
    o('a');    ///< 'a' を出力
    o('\n');   ///< 改行を出力
※「operator()」を「public」にする為、あえて、「struct」としています。

さて、実際の実装ですが、まず format の設計方針を決めます。
・名前空間を「utils」とします。
・float の表示は、基本的に行わない事とします。(今後コンパイルオプションで切り替える)
・整数計算のみを使い、巨大にならないよう配慮する。
・クラッシュは論外としても、きめ細かいエラーのハンドリングは省略する。(必要なら追加する事も可能)
・printf のフォーマットに近い仕様を網羅する。
・2進、8進、16進表示を行う。
・ゼロサプレスの制御
・有効表示数の制御
・オートフォーマットは未サポートとする。

format の中

・フォーマット文字列をスキャンして「%」以下の書式を読み取る。
    void next_() {
        if(form_ == 0) {
            err_(error_case::NULL_PTR);
            return;
        }
        char ch;
        bool fm = false;
        bool point = false;
        bool ppos = false;
        uint8_t n = 0;
        while((ch = *form_++) != 0) {
            if(fm) {
                if(ch == '+') {
                    sign_ = true;  // 符号付きの場合
                } else if(ch >= '0' && ch <= '9') {
                    if(n == 0 && ch == '0') {
                        zerosupp_ = true;  // 最初の数字が「0」なら、0サプレスしない。
                    } else if(point || ppos) {
                        if(point) {
                            decimal_ *= 10;
                            decimal_ += static_cast(ch - '0');
                        } else {
                            ppos_ *= 10;
                            ppos_ += static_cast(ch - '0');
                        }
                    } else {
                        real_ *= 10;
                        real_ += static_cast(ch - '0');
                    }
                    ++n;
                } else if(ch == '.') {
                    ppos = false;
                    point = true;
                } else if(ch == ':') {
                    ppos = true;
                    point = false;
                } else if(ch == 's') {
                    mode_ = mode::STR;
                    return;
                } else if(ch == 'c') {
                    mode_ = mode::CHA;
                    return;
                } else if(ch == 'b') {
                    mode_ = mode::BINARY;
                    return;
#ifdef WITH_OCTAL_FORMAT
                } else if(ch == 'o') {
                    mode_ = mode::OCTAL;
                    return;
#endif
                } else if(ch == 'd') {
                    mode_ = mode::DECIMAL;
                    return;
                } else if(ch == 'u') {
                    mode_ = mode::U_DECIMAL;
                    return;
                } else if(ch == 'x') {
                    mode_ = mode::HEX;
                    return;
                } else if(ch == 'X') {
                    mode_ = mode::HEX_CAPS;
                    return;
                } else if(ch == 'y') {
                    mode_ = mode::FIXED_REAL;
                    return;
#if defined(WITH_FLOAT_FORMAT) | defined(WITH_DOUBLE_FORMAT)
                } else if(ch == 'f' || ch == 'F') {
                    mode_ = mode::REAL;
                    return;
                } else if(ch == 'e' || ch == 'E') {
                    mode_ = mode::EXPONENT;
                    return;
                } else if(ch == 'g' || ch == 'G') {
                    mode_ = mode::REAL_AUTO;
                    return;
#endif
                } else if(ch == '%') {
                    out_(ch);
                    fm = false;
                } else {
                    err_(error_case::UNKNOWN_TYPE);
                    return;
                }
            } else if(ch == '%') {
                fm = true;  // フォーマットの開始を検出!
            } else {
                out_(ch);  // フォーマットに関係しない文字は、そのまま出力
            }
        }
    }
・オペレーター「%」を定義する
//  この定義では、int 型の値が代入された場合の挙動を記述します。
//  事前に format 文字列の中をスキャン(next_() 関数)して、「%」を見つけ、それに続く「型」を「mode_」に格納しておきます。
    format& operator % (int val) {
        if(mode_ == mode::BINARY) {
            out_bin_(val);
        } else if(mode_ == mode::OCTAL) {
            out_oct_(val);
        } else if(mode_ == mode::DECIMAL) {
            out_dec_(val);
        } else if(mode_ == mode::HEX) {
            out_hex_(static_cast(val), 'a');
        } else if(mode_ == mode::HEX_CAPS) {
            out_hex_(static_cast(val), 'A');
        } else if(mode_ == mode::FIXED_REAL) {
            if(decimal_ == 0) decimal_ = 3;
            out_fixed_point_(val, ppos_);
        } else {
            err_(error_case::DIFFERENT_TYPE);
        }
        reset_();  // 変数をリセット
        next_();  // 「%」のスキャンを再始動
        return *this;
    }

※これらはソースの一部です。
「%」オペレーターでは、「int」型、「unsigned int」型、「const char*」型など、色々な型を定義してあり、コンパイラが適合する型を選択して呼び出してくれます。

組み込みマイコンでは、A/D 変換した値(大抵、電圧や電流値)を、小数点以下まで表示させたい場合があります、そこで、「y」フォーマットを用意しておきました、これは、整数値を固定小数として扱い、小数点以下も変換して表示します、小数点の位置は「:x」として自由に設定できます。(最大28ビット)
※浮動小数点が扱えない場合などに重宝します。
たとえば、12ビットのA/Dコンバーターで、基準電圧を2.5V(4096)の場合で、A/D入力に1/5の電圧が分圧される場合は、以下のようになります。


    uint32_t adv = get_adc();
    utils::format<output>("A/D Chanel: %2.3:13y\n") % (adv * 25);  // 2.5 * 5 * 2 
//  2.5 * 5 ---> 12.5 なので、さらに倍にして、小数点以下を12に1を加えて13ビットとする。

※8進数は、あまり使わないと思うので、コンパイルオプションとしました。(リソースの節約)
※逆に、2進数表示は大抵必要なので、「%b」フォーマットを追加してあります。
※浮動小数点は、実装中です、仕様が複雑なので、今後の対応、課題とします。

最終的なソースコードは、format.hpp ここにあります。
※いつもの github