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

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

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

俺俺 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 を使わなかったけど、まぁ、なかなか良い。

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

MinGW でビルドする RX マイコン用 gcc

開発環境を MinGW に移行して、懸案だった RX マイコン用 gcc の構築を行った。

以前の cygwin 環境では、途中で、gcc が止まったり、妙なエラーが出て、上手くコンパイル出来ない状態だった・・
これは、適切なオプションを選択する事で回避出来るようだが、情報が無いし、試行錯誤に疲れて棚上げ状態だった。

MinGW 環境では、何とも普通にコンパイル出来るので、逆に不思議でさえ思ったが、これが普通なんだろうね・・・

コンパイルの詳細は、「Interface 2014年2月号」に詳しく載っているようだが、ネットにあるクロスコンパイラの構築などを参考にしても良いだろう。

手順が複雑で、扱うパッケージのバージョンとの相性などがある為、上手くいかない場合があると思う。

-----

RX マイコン用では、gcc-4.7.3 が良いようだ、C++11 を本格利用は出来ないが、C++0x は使えるので、問題無いと思う、gcc-4.8.x は失敗するようだ。

RX-gcc-4.7.3-ELF パッケージ
※コンパイル済みバイナリーを置いておく。(107MB)
※gcc-4.8.1 で構築した。

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

以前に、文字列を渡す場合に、「const char*」と、「const std::string&」などの参照コンテナで渡すのと、どちらを選択すべきか書いた。

C++ はオブジェクト指向言語であり、ポインターを渡すより、コンテナを参照渡しする方が殆どの場合有利なのは明白なのだが、C から移って来た場合、オブジェクト指向プログラムに不慣れな場合もあり、中途半端な設計(ポインターだったり、コンテナだったり)になってしまう事がある。
※思い返してみると、これは自分もそうだった・・・

今回、同じような事例として、「x、y軸」の位置を渡す方法を考えてみたい。

たとえば・・・

void set(int x, int y) { }

のような関数がある場合・・・

struct xy {
    int x;
    int y;
};
void set(const xy& p) { }

このように、「x、y軸」をコンテナに入れて、参照渡しにする方が何かと都合が良い。

しかし、多くの場合、直で値を入れたい場合は、冗長では?
みたいな意見もあるのだが、通常、直で値を入れて呼ぶ事は「稀」と思うけど、それでもそのようなケースが多いなら、以下のようにすべきだろう。

struct xy {
    int x;
    int y;
    xy() { }
    xy(int x_, int y_) : x(x_), y(y_) { }   ///< コンストラクターを定義
};
void set(const xy& p) { }
void set(int x, int y) { set(xy(x, y)); }   ///< 直で呼べるように定義を追加

    set(100, 200);        ///< 直接指定
    set(xy(100, 200));    ///< 直で呼びたい場合、このように書ける。

ここで、構造体 xy だが、メンバーは、public になっていて、アクセサーを使わずにダイレクトにアクセスしている点を検討すべき問題として付け加えておく。
※この例では、自分の流儀でそのような仕様にしているだけで、本来は、このような単純なクラスでも、アクセサーを用意してアクセスする事が望ましいと思う、自分の裁量で、メンバーに与える影響などが少ない場合は、ダイレクトでも良いと思っている為で、このようなダイレクトなアクセス方法に注意して欲しい。
内部の変数にアクセサーでアクセスするようにする事で、安全性や最適化など得られるメリットは他に色々あるのですが、範囲が大きくなり過ぎると思うので、これは、又、別の機会に論じる事とします。

上の例では、メンバーの型は「int」でしたが、構造体 xy をテンプレートにする事で、他の型も簡単に定義できる。

typedef xy<short> s_xy;
typedef xy<int>   i_xy;
typedef xy<float> f_xy;

template<typename T>
struct xy {
    typedef T value_type;
    T x;
    T y;
    void set(T x_, T y_) { x = x_; y = y_; }
    xy() { }
    xy(T v) : x(v), y(v) { }     ///< カスタムコンストラクター(同じ値で初期化)
    xy(T x_, T y_) : x(x_), y(y_) { }    ///< カスタムコンストラクター(個別に初期化)
    xy& operator = (const s_xy& p) { set(static_cast(p.x), static_cast(p.y)); return *this; }
    xy& operator = (const i_xy& p) { set(static_cast(p.x), static_cast(p.y)); return *this; }
    xy& operator = (const f_xy& p) { set(static_cast(p.x), static_cast(p.y)); return *this; }
};

「operator = 」を使い、複数の型に対応する事で、型が違う代入をスムーズに行えるようになるが、「float -> short」や、「float -> int」は、変換出来ない場合がある為注意する必要がある。
※そのような変換が起こった場合は、例外を出すなどが必要かもしれない。

typedef T value_type;

は、テンプレートの実装では、よく使われるやり方で、元の「型」を再定義する事で、そのクラスで使われている「型」にアクセスする方法を提供する。

    s_xy pos(0);    ///< カスタムコンストラクターが定義されている為、x、y、を同じ値で初期化する事が出来る。
    for(s_xy::value_type i = pos.x; i < (pos.x + 10); ++i) {
        ...
    }

※これは、例題なので、名前空間に入れていないが、実際に使う場合には、必ず、何らかの名前空間に入れて運用する必要がある、そうしないと、クラス名が既存のクラスとぶつかってしまう事になる。

「代数」のクラスは、自分で実装しなくても、ネットを探すと、色々なソースを見つける事が出来る、自分で実験的に実装する事で、より広範囲な理解とスキルを身につける事が出来る、そうしてから、より洗練された実装を利用しても良い。
※ある程度自分で作って、利用していると、愛着も沸くし、他の同じような実装に移るのが難しくなる、しかしながら、自分で作ったクラスはメンテナンスし易く、機能を追加したり改良するのが、楽であるメリットがある。

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

「printf」の功罪と「iostream」

C++ 初心者の頃 C 言語から C++ に移行して、大きな驚きを感じたのは、文字の入出力関係でしょうか?

現在 C++ でプログラムを作成するのが「常」となり、結論から言わせてもらうと、C++ では、もはや「printf」を使う理由は全く無い事です。

今「え?」と言った人は、C++ にまだ移行できていない中途半端な状態だと思われます。

    printf("Hello !!!\n");

では無くて、

    std::cout << "Hello !!!" << std::endl;

です!

C++ に馴染みの無い人には、今まで観た事の無いような、記述で、凄く奇妙に写ると思います。
C++ ではオペレーターをオーバーロード出来る為、「<<」を「シフト」では無く、全く別の意味で使う事が出来ます。
「iostream」クラスでは、cout(stdout)オブジェクトに対して、文字列を流し込む事で、文字の表示を行う事が出来るような設計になっています。
※入力は、std::cin オブジェクトから「>>」で行えます。

以前にとあるプログラマーが「iostream はわかりずらいので使う価値が無い」的な事を言っていましたが、甚だしい勘違いです。
「使い易い」、「使いずらい」と言う感覚は、単純に個人の「慣れ」の問題であって、その感覚だけで「扱いづらい」と結論してしまう事に危機的な危うさを感じます。
多分、iostream を設計した人々は、printf に関連する諸問題にとっくの昔に気がついていて、それを避け、尚且つ簡単に扱えるようにするにはどうしたら良いかを長い時間考えたり、ディスカッションして、現在の実装になったと思います。
iostream は、printf に比べると速度が遅い(負荷が大きい)と言うのはあるかもしれませんが、printf に比べると、安全で、プログラマーが受けられる恩恵が大きい優れた物です、それを良く理解しないままにわざわざ禁止する必要は無いのです。

「どんなに注意しても、人間は間違いを犯す」と言う基本的な事実があります。
printf 内のフォーマットと、可変長の引数の「型」の整合性は、コンパイラではチェックに限界があり、極めて深刻な問題をそのままエラー無くコンパイルする事が可能で、その場合、スタックを破壊する事で、微妙で見つけにくいバグをアプリケーションの動作に取り込んでしまう事があります。

iostream であれば、うっかりミスは、コンパイラがエラーを出しますし、スタックを壊すような危険な事もありません。

それでも、尚、printf の使い易さが忘れられない人には、「boost/format.hpp」を使って下さい。
これなら、ほとんど printf のような感覚で、しかも、安全に運用する事が出来ます。
※フォーマットと、引数の型の誤りには「例外」がスローされます。

文字列から数値、数値から文字列の変換には、「boost/lexical_cast.hpp」
と言うのがあり、非常に便利です、これがあれば、scanf を使う必要もありません。
※変換の失敗には「bad_lexical_cast 例外」がスローされるので、それをキャッチして、変換の失敗に備える必要がある。


これはゲームの開発での話ですが、ほとんど何処の会社でも printf を使う事を禁止しています、それは、どんなに注意しても、ミスを無くす事は出来ない為、見つける事が困難な問題をシステムに混入させてしまう危険を避ける為です。
※通常、リリースビルドでは #define で printf、sprintf などをオーバーロードして、命令を無効にするようなマクロが組まれています。

ただ、組み込みでは、残念な事があります、gcc の stdc++ ライブラリーでは、iostream クラスは他との依存が大きく巨大で、リンクすると、通常数百キロバイトのプログラムメモリーと、数十キロバイトのワークエリアを消費してしまいます、これでは、少ないリソースのマイコンでは物理的に使えません。
そこで、もし必要なら、小規模な iostream クラスを作成する必要があります(これは車輪の再発明ではありません)、ホビーで使うのであっても、あくまでも、安全性と利便性から、printf を使わない決断をすべき事項だと思います。
※以前、C 言語の時代でも、printf が巨大な為、多くの人が、tiny printf のような物を作って使っていました。
※オペレーターや、オブジェクト指向の勉強にもなるので、自分で作ってみると楽しいかもしれません。

一応、私が実装した、クラスを紹介します。
※これは、出力先として、「void sci_put(char);」、「void sci_puts(const char*);」などのシリアルインターフェースを使っています。

namespace utils {
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    /*!
        @brief  chout クラス
    */
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    struct chout {
    static const char endl = '\n';

    private:
    char        sup_ch_;
    char        hex_ch_;
    uint8_t     len_;

    public:
        //-----------------------------------------------------------------//
        /*!
            @brief  コンストラクター
        @param[in]  out 文字出力関数
        */
        //-----------------------------------------------------------------//
     chout() : sup_ch_('0'), hex_ch_('a'), len_(0) { }

        //-----------------------------------------------------------------//
        /*!
            @brief  16 進表示の英数字を大文字にする
        @param[in]  cap 「false」を指定すると小文字
        */
        //-----------------------------------------------------------------//
    void hexa_decimal_capital(bool cap = true) {
        if(cap) hex_ch_ = 'A'; else hex_ch_ = 'a';
    }

        //-----------------------------------------------------------------//
        /*!
            @brief  表示文字数を指定
        @param[in]  len 表示文字数
        */
        //-----------------------------------------------------------------//
    void set_length(uint8_t len) { len_ = len; }

        //-----------------------------------------------------------------//
        /*!
            @brief  ゼロサプレス時の文字を指定
        @param[in]  ch  文字
        */
        //-----------------------------------------------------------------//
    void suppress_char(char ch) { sup_ch_ = ch; }

        //-----------------------------------------------------------------//
        /*!
            @brief  文字表示
        @param[in]  ch  文字
        */
        //-----------------------------------------------------------------//
    void put(char ch) const { sci_putch(ch); }

        //-----------------------------------------------------------------//
        /*!
            @brief  文字列の表示
        @param[in]  str 文字列
        */
        //-----------------------------------------------------------------//
    void string(const char* str) const { sci_puts(str); }

        //-----------------------------------------------------------------//
        /*!
            @brief  長さ指定文字列表示
        @param[in]  str 文字列
        @param[in]  len 長さ
        */
        //-----------------------------------------------------------------//
    void len_string(const char* str, uint8_t len) const {
        while(len_ > len) {
            put(sup_ch_);
        ++len;
        }
        string(str);
    }

        //-----------------------------------------------------------------//
        /*!
            @brief  16 進数の表示
        @param[in]  val 値
        */
        //-----------------------------------------------------------------//
    uint16_t hexa_decimal(uint32_t val) const {
        char tmp[8 + 1];
        uint16_t pos = sizeof(tmp);
        --pos;
        tmp[pos] = 0;
        do {
        --pos;
        char n = val & 15;
        if(n > 9) tmp[pos] = hex_ch_ - 10 + n;
        else tmp[pos] = '0' + n;
        val >>= 4;
        } while(val != 0) ;

        len_string(&tmp[pos], sizeof(tmp) - pos - 1);
        return sizeof(tmp) - pos - 1;
    }

        //-----------------------------------------------------------------//
        /*!
            @brief  10 進数の表示
        @param[in]  val 値
        @param[in]  minus   マイナス符号を表示する場合「true」
        @return 表示文字数
        */
        //-----------------------------------------------------------------//
    uint16_t decimal(uint32_t val, bool minus = false) const {
        char tmp[11 + 1];
        uint16_t pos = sizeof(tmp);
        --pos;
        tmp[pos] = 0;
        do {
            --pos;
        tmp[pos] = '0' + (val % 10);
        val /= 10;
        } while(val != 0) ;

        if(minus) {
        --pos;
        tmp[pos] = '-';
        }

        len_string(&tmp[pos], sizeof(tmp) - pos - 1);
        return sizeof(tmp) - pos - 1;
    }

        //-----------------------------------------------------------------//
        /*!
            @brief  符号付き 10 進数の表示
        @param[in]  val 値
        @return 表示文字数
        */
        //-----------------------------------------------------------------//
    uint16_t decimal(int32_t val) const {
        bool minus = false;
        if(val < 0) {
            minus = true;
        val = -val;
        }
        return decimal(static_cast(val), minus);
    }

    chout& operator << (const uint32_t val) {
        decimal(val);
        return *this;
    }

    chout& operator << (const int32_t val) {
        decimal(val);
        return *this;
    }

    chout& operator << (const char* str) {
        string(str);
        return *this;
    }

    chout& operator << (const char ch) {
        put(ch);
        return *this;
    }
    };
}

※ただ、このクラスでは、浮動小数点や、2進数、8進数の表示をサポートしていません、浮動小数点表示が出来ないのは痛いので、改修する予定です。

現在、独自の format クラス一式を実装してあります、GitHUB 参照下さい。

MinGW による GLFW3、FreeType2、Bullet 環境の構築

今まで、Windows では主に cygwin64 を使って開発環境を整えていた。
しかし、ここにきて、色々な不具合に遭遇する・・・

たとえば、組み込みマイコンのクロス開発環境として、gcc をビルドするのだけど、cygwin では途中で必ず失敗する。
※cygwin では、何か特別なオプションを追加するのかもしれないが、情報が無いし、原因を追って、試すのに時間が掛かり過ぎる。
※大抵は、gcc がコンパイルエラーで止まる。
また、少し大きなライブラリー(bullet physics)をコンパイルしようとした場合にスタック不足で gcc-w64-mingw32-gcc がクラッシュしたり、リンク中に止まったりと、非常に辛い状況が連発していた。
状況を改善する為に、ソースを追ったり、オプションを追加したり、情報を求めて時間を浪費したものの、良い改善策が見つからない、これは、cygwin のバージョンアップにより解消するのかも知れないと思ったりもしたが、もう疲れた・・・
そこで、別の解決策として、MinGW 環境を試してみる事にした。

MinGW は、よりコンパクトに必要最小限のコマンドを集約した、gcc を使った Windows の開発環境と思えば良いと思う。
MinGW には MinGW の流儀(常識)があり、それを学ぶ必要がある為、時間がかかるので、躊躇していたが、使ってみない事には判らない。

それで、とりあえず、インストーラーを使ってインストールしてみた。

しかし、入れたハズの gcc すら動かない!?
何で?、と思って数日、profile のパスにバグがあり、それが原因だったようだ。

/c/MinGW/msys/1.0/etc/profile
の19、21行目
/mingw/bin:/bin ---> /c/mingw/bin:/bin
ドライブレターが抜けているようだが、正規のインストーラーでインストールしてこの完成度は「痛い」としか言い様が無い、ホント大丈夫なの?

気を取り直して、コマンド窓を、評判の mintty にしてみた、これは簡単、情報も多く直ぐに導入出来た。

自分は emacs 使いなので、コンソールで使える emacs を使いたかったが、MinGW では標準では無いので、仕方なく、Windows の emacs を入れて、パスを通した。

ここまで出来て、判った事。

・MinGW では、コンソールで emacs を動かすのは難しいようだ。(vim は標準であるが、キーバインドの慣れもあるので自分には使いづらい)
・Windows の日本語ファイル名などをスマートに解決出来ないようだ。(色々な理由で UTF-8 を使いたいが、それだと色々破綻する、とはいえ、CP932(Shift-JIS)も駄目で、日本語ファイル名の文字化けは我慢するしか無いようだ。
・cygwin では、大抵の unix コマンドは揃っていて、インストーラーで簡単にインストール出来るが、MinGW では、標準に無い物は自分でコンパイルして導入する必要があり、簡単に導入出来るものと、パッチを当てたり、改修する必要がある物など、簡単では無い場合があるようだ。

まず、自分のフレームワークで使っているライブラリーをコンパイルする必要がある。
「configure、make」で解決できるものと、「cmake」を使う物があるみたいなので、cmake の windows 版をインストールして、パスを通した。
※cmake はMinGW で動作する実行ファイルを作るのでは無く、windows 専用の物を使うようで、GUI 環境で使える版も付いてくる。

(1)標準的な各種ライブラリーのインストール

Windows 関係ライブラリー:
    mingw-get install mingw32-w32api

pthread ライブラリー:
    mingw-get install mingw32-pthreads-w32

zlib ライブラリー:(標準では DLL しか入らないようだ)
    mingw-get install mingw32-libz

※「mingw-get」で、GUI が起動するので、それで、必要そうな物はインストール出来る。

(2)GLFW-3.0.4 のコンパイル
MinGW の常識が判っていなかった為、コンパイル出来るまで試行錯誤が続いた。
glfw3 のアーカイブを解凍したら、ディレクトリーを移って。

    cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=/usr/local

として作成する。
※cmake は途中で失敗したら、キャッシュが残るようで、後何をやっても駄目ななので、アーカイブからやり直した(他にもっと良い方法があると思う)

後はお決まりの・・・

    make
    make install

とりあえず、GLFW3 のコンパイルは出来た。

ここまでの感触で、自分フレームワークを MinGW に移っても問題無いように感じた。

追記:2014/1/21
(3)freetype-2.5.2 ライブラリーの構築
freetype は、TrueType のレンダリングを行うオープンソースライブラリーで、自分のフレームワークでも日本語フォントの描画で使っている。
このライブラリーのコンパイルは、少し工夫が必要だった・・
インクルードファイルの相互依存の影響で、定義がすっぽ抜ける現象が起こる、本来はもっとスマートに解決したいが、無理やりだが以下のように解決した。

「ftgip.c」に、typedef を二行追加。
    typedef long off_t;
    typedef long long off64_t;

    ./configure --without-png
※ PNG が必要なのは、TrueType ファイル内に PNG でエンコードされたフォントのビットマップがあるのだろうけど、とりあえず、無視するようにした。

    make
    make install

(4)physics bullet-2.82 のコンパイルとデモプログラムの生成
bullet は物理法則をシュミレーションする演算ライブラリーで、色々な場面で使われている。
Visual Studio でコンパイルするのが一般的なようだが、MinGW でもライブラリーを作成する事が可能なようだ。
※現状の gcc-4.8.1 では、コンパイル中にクラッシュする為、ライブラリーを作れないが、クラッシュするソースを微妙に修正するとコンパイルが正常終了する事が判った。

修正1: src/BulletCollision/CollisionDispatch % emacs btInternalEdgeUtility.cpp
内「struct btConnectivityProcessor : public btTriangleCallback」クラスで、

btVector3 calculatedEdge = edgeCrossA.cross(edgeCrossB);
を、以下のように修正
calculatedEdge = edgeCrossA.cross(edgeCrossB);

btVector3 calculatedNormalB = normalA;
を、以下のように修正
calculatedNormalB = normalA;

変数として以下の二行を追加する。
	btVector3 calculatedEdge;
	btVector3 calculatedNormalB;
※この修正、厳密には正しく無いが、多分動作するだろうと思う、正しく動作しているかの検証はしていない、bullet は発展途上で、実装があっても使われていない事もあるので、どのデモプログラムを動作した場合にこのクラスが動くか見極めていない、コンパイルが通るだけのワークアラウンドと思って欲しい。

修正2: src/BulletDynamics/Character % emacs btKinematicCharacterController.cpp
383、384 行目をコメントアウト(単純に使われていないが、あるだけで gcc がクラッシュする・・)
///     btScalar hitDistance;
///     hitDistance = (callback.m_hitPointWorld - m_currentPosition).length();

GLUT の対応:
GLUT は、MinGW 用にコンパイルすれば良いのだが、GLFW3 を使うようになって OpenGL では使わなくなった為、bullet のデモを動かす為の対応として、
Glut/GL/glut.h ---> /c/MinGW/mingw32/include/GL にコピーする。
又、ライブラリー glut32.lib を、名前を変更して、コピーする ---> /c/MinGW/mingw32/lib/libglut32.a
※VisualStudio で C 言語ソースだけでコンパイルされたライブラリーは gcc のライブラリーと互換性がある。

    cmake -G "MSYS Makefiles" -DCMAKE_INSTALL_PREFIX=/usr/local
    make
    make install

(5)libpng-1.6.8 ライブラリーの構築
これは、普通に問題なく構築できる

    ./configure
    make
    make install

(6)openjpeg-2.0.0 ライブラリーの構築
標準的には、シェアードライブラリー形式(DLL)で生成される為、それをOFFにしてスタティックライブラリーにする

    cmake -G "MSYS Makefiles" -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_INSTALL_PREFIX=/usr/local
    make
    make install  

(7)JPEG ライブラリーの構築
JPEG ライブラリーは、x86 CPU の場合、マルチメディア命令で最適化されたバージョンがあるので、それを使う。
Independent JPEG Group's JPEG software release 6b
with x86 SIMD extension for IJG JPEG library version 1.02

※VisualStudio の C コンパイラで作成されたライブラリーファイルは、gcc と互換性があるので、「xxx.lib」を「libxxx.a」などにリネームすれば良い。
このライブラリーは非常に高速なので、画面をキャプチャーしてそれをリアルタイムに JPEG 圧縮して出力すれば、動画にも対応できる。(モーション JPEG 的な・・)
※生の画像を出力しない方が、ファイルサイズが小さくなり、高速化する。

(8)mupdf-1.3 ライブラリーの構築
mupdf ライブラリーはオープンソースの PDF ファイルを展開してビットマップを生成するライブラリーで、PDF を簡単に開けるので、便利に使っている。
このライブラリーは、freetype、openjpeg、jpeg、jbig2dec などを利用するのだけど、各ライブラリーのバージョンの違いを吸収する為、全てのソースコードを同梱している、構築には無駄にならないように、自前のライブラリーをリンクさせる。

まず Makefile を修正

3 行目「build ?= debug」を「build ?= release」としとく。

「include Makethird」をコメントアウトしておく「# include Makethird」

LIBS に追加「LIBS += -L/usr/local/lib -lpng -ljpeg_x86 -lopenjp2 -ljbig2dec -lfreetype -lz」

CFLAGS にも追加「CFLAGS += -I/usr/local/include -I/usr/local/include/openjpeg-2.0 -I/usr/local/include/libjpeg_x86 -I/usr/local/include/freetype2」
※JPEG ライブラリーは x86 CPU 用に最適化されたバージョンを使っている。

    make
    make install

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

前回、コンペアマッチタイマーの制御をテンプレート化してみました。

今回は、少し複雑ですが、シリアルコミュニケーションインターフェースをテンプレート化してみます。

実用的なシリアルコミュニケーションでは、通常、受信、送信は割り込みによって行い、メインとは FIFO などでやりとりします。
さらに RX マイコンでは、DMA も使う事が出来ますが、やりとりするデータ量と、出し入れに係わる細かい操作を考えると、DMA を使う事にあまりメリットが無いので、通常の割り込みで行う設計とします。

FIFO のバッファサイズは、アプリケーションの構造、送受信のボーレート、などにより最適なサイズがあると思われますので、可変に出来るようにします。
※以前のコンペアマッチタイマーより、バッファサイズをパラメーターとしている為、少し複雑です。

    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    /*!
        @brief  SCI I/O 制御クラス
        @param[in]  SCI SCIx 定義クラス
        @param[in]  recv_size   受信バッファサイズ
        @param[in]  send+size   送信バッファサイズ
    */
    //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
    template <class SCIx, uint32_t recv_size, uint32_t send_size>
    class sci_io {

        static utils::fifo<recv_size>   recv_;
        static utils::fifo<send_size>   send_;

        static INTERRUPT_FUNC void recv_task_()
        {
            bool err = false;
            if(SCIx::SSR.ORER()) {  ///< 受信オーバランエラー状態確認
                SCIx::SSR = 0x00;   ///< 受信オーバランエラークリア
                err = true;
            }
            ///< フレーミングエラー/パリティエラー状態確認
            if(SCIx::SSR() & (SCIx::SSR.FER.b() | SCIx::SSR.PER.b())) {
                err = true;
            }
            if(!err) recv_.put(SCIx::RDR());
        }

        static INTERRUPT_FUNC void send_task_()
        {
            SCIx::TDR = send_.get();
            if(send_.length() == 0) {
                SCIx::SCR.TEIE = 0;
            }
        }
...

重要な部分は、fifo の定義です、割り込み関数とクラスとで、送受信データをやりとりする必要がある為、「static」としています。
テンプレートのパラメーターから、受信サイズ、送信サイズを受け取って、静的に宣言されます。
割り込み関数も static 宣言します、この関数アドレスは、初期化時、割り込みベクターに渡されるようにしています。

    sci_io<device::SCI0, 128, 128> sci0_;
    sci_io<device::SCI1,  64, 256> sci1_;

↑のように、SCI0、SCI1 を宣言すると、テンプレートパラメーター SCIx が異なる為、static に宣言された fifo の領域は SCI0、SCI1 で別々に確保されます。

クラス内の static 宣言では実態を別に宣言しておく必要があります。

    template<class SCIx, uint32_t recv_size, uint32_t send_size>
        utils::fifo<recv_size> sci_io<SCIx, recv_size, send_size>::recv_;
    template<class SCIx, uint32_t recv_size, uint32_t send_size>
        utils::fifo<send_size> sci_io<SCIx, recv_size, send_size>::send_;

どうでしょうか、これで、チャネル毎バッファサイズを変更して静的に使う事が出来ます。

全ソースコードは github にあります。