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

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

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

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

(4)クラス
「クラス」は、C++では、大きなトピックです、ここでは、クラスに関連するいくつかの事を紹介します。

・初期化リスト
「クラス」では、「コンストラクター」と呼ばれる特別のメソッドがあります。

class bitmap
{
public:
// コンストラクター
 bitmap() { }
};

これは、ご承知の通りです。
普通、コンストラクター内では、変数の初期化を行います。

class bitmap
{
  int counter_;
public:
// コンストラクター
 bitmap() { counter_ = 0; }
};

「= 0」と値を代入しています。
これは、間違いでは無いのですが、「=」(イコール)で代入するのでは無く、コンストラクターでは、「初期化リスト」を使います。
違いは、初期化リストでは、各オブジェクトのコンストラクターを呼んでいるのに対して、代入では、=オペレーターを呼んでいる事になります、最適化された場合は、殆ど同じになりますが、コンストラクター内では、初期化リストで初期化するようにして下さい。
※詳細な理由については、記しませんので、ご自分で調べて下さい。

class bitmap
{
  int counter_;
public:
  bitmap() : counter_(0) { }
}

・メンバー変数に「_」アンダースコアーを付ける
クラス内のメンバー変数は、引数の変数名などと被らないようにします。
典型的には、「m_counter」などとする事もありますが、これは、ハンガリアンスタイルと言えます、なので、シンプルに後ろに付けるのが好ましいと思えます。
※高橋晶さんから指摘してもらいましたので修正します。
先頭アンダースコアの次に大文字が来る場合は規約違反となります。

※インクルードガードにアンダースコアー
多くの人(C のプログラマーに多い)が、インクルードガードで使うキーワードに、未だにアンダースコアーを使っている人がいます。

func.h の場合
#ifndef __FUNC_H__
#define __FUNC_H__

...

#endif

しかしこれは、規約違反である事を念のため確認しておきます。
※私は、「#pragma once」をお勧めしますが・・

func.h の場合
#ifndef FUNC_H
#define FUNC_H

...

#endif

・引数の void
受け取るパラメーターが無い場合、C では void を使いました。

void init(void)
{
}

C++ では、何も書く必要は無くなりましたので、引数が無ければ何も書きません。

void init()
{
}

※しかし、書いてもエラーにはなりません、問題なのは、書く必要が無いのにあえて書く人と、その心理でしょうか・・・

・引数に標準的な値を代入できる
受け取るパラメーターがあったとして、標準的な値を代入しておく事が出来ます。

void set(int value = 1);

...

  set();     //「1」が引数として使われる。
  set(100);  //「100」が引数として使われる。

これらを利用して、色々と便利な事が行えます、応用してみて下さい。
私がお勧めしたい応用として、ブーリアンを使った、フラグの設定を紹介します。
よく、状態として、「許可」と「不許可」を設定したい場合があります。
そんな時・・・

void enable();
void disable();


のようにしますか?
ですが、これだと、何かの状態を評価してから、状態を設定する場合、関数を呼び分けなければなりません。

if(flag) enable();
else disable();

そこで・・

void enable(bool f = true)
{
}

とすればー

  enable();  // 許可したい場合

...

  enable(false);  // 不許可したい場合

...

  enable(flag);  // flag が「true」か「false」で、「許可」、「不許可」を設定できる。

※よく、二つの状態を受け渡しするのに、「int」とかを使う人がいますが、それは間違いです「bool」を使って下さい。
※又、3つなら「int」が便利(1, 0, -1)と言う人がいますが、それも間違いで、3つの状態があるなら、enum などで、3つの状態を定義して、それを使います。

・enum の便利な使い方

C++ では、define を使わなくなりますし、あえて使う理由もありません、そこで定数を定義するのに便利な enum を C++ で便利に使う為の方法を紹介します。

enum は意味のある値を定義する上で便利な機能ですが、不都合な事が起こります。

たとえば、以下のような、enum の定義では、enum 内に同じ名前のキーワードを定義できません。

enum holizontal {
  LEFT,
  H_CENTER,   // これは少し冗長
  RIGHT
};

enum vertical {
  TOP,
  V_CENTER,   // これは少し冗長
  BOTTOM
};

しかし、C++では、こう書けば・・

struct holizontal {
  enum type {
    LEFT,
    CENTER,
    RIGHT
  };
};

struct vertical {
  enum type {
    TOP,
    CENTER,
    BOTTOM
  };
};

型名をクラス名にして、その中で enum を定義する事で、別々に定義出来ます。
※名前空間で分離する事も出来ますが、私は、クラスで括る方が好みです。
※私は、このような場合に enum の型として type と言うキーワードを使うのが好みです。

int main()
{
  holizontal::type h = holizontal::CENTER;
  vertical::type v = vertical::CENTER;

...

}

※structとclassの違いについて
C++ では、struct と class の違いは、殆どありません、private か、public の違いくらいです。
※ main 関数の戻り値
↑のサンプルでは、main 関数の戻り値(return 0;)が記述されていません、C++ では、main 関数の戻り値が無い場合、0が戻る事が保障されています、これは言語規約に定められています、言いたいのは、C++ では、C 以上に言語規約を良く理解して正確に従う必要がある点です。

・new、delete をなるべく使わない
クラスを定義して、それを使う場合、以前に良く見たサンプルは、以下のような物です。

  func* f = new func;

  f->xxx();

  delete f;

しかし、このように書けば、new、delete を省略出来ます。

  {
    func f;

    f.xxx();

  }

↑のように書けば、delete を忘れて、メモリーリークする事を防げますし、func クラスが生成されるタイミングもスコープで制御できます。
雑多な事はコンパイラに任せ、コンパイラに出来ない事に集中します、最適化にも貢献出来ます。

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

C++を常用的に使うようになってから随分時間もたち、C++も少しだけ上達してきました。
最近では殆どのプログラムをC++で行うようになり、組み込みのような小規模なシステムでも、開発環境が提供される限りC++を使っています。

話は変わって、AVRマイコンの開発環境で「WinAVR」と言うのがあります、gcc が使えるフリーのツールキットなのですが、8ビットマイコンであるにも関わらず、C、C++でプログラミングできるフリーの開発ツールです、但し、C++のプログラミングでは、STLは使えないのですが、まぁ、数百バイトのRAMや、数キロバイトのROMでは、リソースの関係で困難と思えます、まして、データ領域とプログラム領域が分離しているアーキテクチャーでは、物理的に同じような実装をする事は難しいのかもしれません。
ただ、string、vector、map のようなコンテナは仕様に制限を設定すれば、実装する事は可能でしょう。

AVRで使える g++ は非常に良く出来ていて、最近では、AVRのプログラミングでもC++でプログラムするようになりました、何か特別な理由が無い限り、Cでプログラムを書く理由は無いと思えます。

しかしながら、典型的なCのプログラマーは、多くの勘違いをしている事が多いと思います、これはC++を本格的に学ぶ前の自分もそうでした。

(1)C++はリソースを多く消費し、プログラムサイズが大きくなる。
(2)C++は余分な処理が多く、余分なマシンサイクルを消費するので、遅い。
(3)Cのプログラミングスキルは、C++にそのまま生かせる。

まず(1)ですが、幾つかのプログラムをつくり、最適化されたアセンブラコードを観てみましたが、適正に書かれたC++コードは、Cのコードと遜色は無く、余分な命令は殆ど追加されていませんでした。
※これは処理系によっては、違う結果となる場合もありますが、WinAVR g++ ではそんな事は無く、洗練されたアセンブリコードが出るようです。

次に(2)ですが、(1)で示したように、余分な命令は殆ど無いので、余分なマシンサイクルも消費しません、場合によっては、より最適化されたコードが出る可能性も含まれている為、C言語より高速なコードが出る可能性もあります。
但し、「適正に書かれた」と言う前提が付きます、適正に書くには「学ぶ」必要がある為、C++に不慣れなプログラマーが、少しのC++のスキルを使って検証したら、別の結論に行き着く可能性があります。

そして(3)です、確かに、何かの処理をどのように細分化して、実際に動くコードを作成する能力(基本的なプログラミングのスキル)は、生かされるのですが、C++には、Cに無い様々な用法があります、それを知らないと、適切なコーディングは出来ないと思うのです、しかしながら典型的なCのプログラマーは、C++をある程度理解しているし、実際に動くコードを実装出来るので、使うつもりなら、いつでもC++でプログラミング出来る(C++は大体理解している)と思っています、しかし、C++はCに似ていますが、全く違う言語である事を認識すべきでは無いでしょうか、C++で適正なプログラミングをする為には、多くの時間を使って学ぶ必要があると思います、もしC++を理解していれば、Cより便利で安全なC++が使える環境でC++でプログラミングしない理由は無いハズなのです、C++を使わないのはそれ相応の理由が存在するのでしょう。
※幾つかの場面で、C++でプログラムしない理由は存在しますが、本当にそうなのかは検証しなければ判らないし、ケースバイケースです、Cを選択するのが最適な場合はあるででしょうが・・

これから、不定期に何回かに分けて、C++でプログラムするのに便利なテクニックや豆知識を紹介していこうと思います。
主に、昔に感じた事や重要と思われるトピックを中心に書いていこうと思います。
この文書は、正確でなかったり、実際と違う場合などがあると思います、気が付いたり指摘されれば修正していこうと思います。

良く、初心者が読むのに適したC++の参考書は?と聞かれますので、とりあえず、この本をあげておきます。
C++ Coding Standards―101のルール、ガイドライン、ベストプラクティス

・名前空間

Cには無い便利な機能として「名前空間」があります、名前空間を活用する事で、プログラムを判りやすく、構造的にする事が出来ます。
※悪名高い Embedded C++ には名前空間がありませんが、C++を理解出来なかった人たち(日本人です)によって策定された仕様です、恥ずかしい事です。

・参照

「参照」は、C++でも最も利益のある機能の一つだと考えます、参照はポインターに似ている為、Cのプログラマーは軽視しがちですが、コンパイラーにとっては、最適化を進める上で、非常に強力な武器です、関数に渡すのがポインターでは無く、参照であれば、より進んだ最適化を行う可能性が生まれます。
また、参照では、ポインターのような、NULLチェックを行う必要は無く、構造的に参照が適用出来ない場合は、コンパイラが教えてくれます。
ポインターより少しだけ制限のある参照は、より洗練された構造をプログラムに提供し、それと同時に安全性も提供します。
参照では、const をより明確に使え、明確な意図をもって伝播させる事が出来ます、これを最初は「ウザイ」と思う人もいますが、そうでは無い事は直に理解出来ると思います。
一つの典型的な方法論として、まず参照で解決出来るか考えて、なるべく参照を使うように全体を設計し、どうしても参照に出来ない場合だけ、ポインターを使うようにします。

ポイント:
NULL について:
「NULL」はC言語のマクロであり、C++でも使えますが使うべきでは無いと考えていますし、使う理由もありません。
C++では「0」を使えば良いのです、NULLを使う理由として、数値に代入する0と、ポインターに代入する0を明確に分けたいとの理由を挙げる人もいますが、それが、わざわざ、C言語のマクロを使う理由としてメリットがあるとは考えられません、また、NULLマクロを使うには stdlib.h(cstdlib)をインクルードする必要があります。

・基本的な事

(1)標準ライブラリーのインクルード
C言語のヘッダーをインクルードする際に
C++では、C言語で使える関数も当然使えます、その際ヘッダーをインクルードしますが、C++用に専用ヘッダーが用意されています。

たとえば、「stdio.h」なら「cstdio」、「stdlib.h」なら「cstdlib」、「string.h」なら「cstring」です。
※規則は察しがつくと思います。
C++の標準的ヘッダーは、「.h」などの拡張子が無いので、それに習っているのと、C++から使う際の「おまじない」がしてあります。
※AVR の g++ では、C++ 専用のヘッダーが用意されていない為、普通のCヘッダーをインクルードします。

(2)型について
C++では、「型」を厳密に評価します。

Cの場合、たとえばポインターは典型的に以下のように書きます。

void func(char *ptr)
{
  char *tmp = NULL;

...


}

しかしC++では・・

void func(char* ptr)
{
  char* tmp = 0;

...


}

ポインターを示す「*」が、変数名に付いていたのが、型に付くようになっています。
Cでは、「ptr」の「ポインター」、「tmp」の「ポインター」だったのが、
C++では、「ptr」や「tmp」は「char」の「ポインター型」と言う考えによるものです。
しかし、多くの人が、自分流の定型記述セオリーを持っており、少しでも異なると、「気持ち悪い」と感じる為、それだけでも、テンションが下がる要因になる場合も少なくありません。
これは、慣れの問題で、もちろんコンパイラーは、「char *ptr」でも「char* ptr」でもエラー無くコンパイル出来ますが、しばらくは、自己流の狭い考えを捨てて、流れに身を任す事が寛容と考えます。

ただ、ここで問題が起こります。

  char* tmp, ptr;

このように書くと、「tmp」は「ポインター型」ですが、「ptr」は「char型」です、これは、C言語との互換を考慮して、このような不都合な事が起こります。
なので、一つの方法として、コンマで区切って、複数の変数を宣言しない事で避ける事ができます。

(3)スコープを利用した宣言

以前、Cの典型的関数では、関数内で使う変数を、頭の方で、集中的に宣言していました。

void func(void)
{
  int i, j, k;
  char c;

...


}

しかし、C++では、変数を使いたい時に、初めて変数を宣言できます。

void func()
{
  int i;
  int j;

...

  char c;
  int k;

...


}

また、スコープを使って、分離する事で、同じ変数名を何度でも宣言できます。

void func()
{
  int j;
  {
    int i;

...

  }

  {
    int i;
    int j;

...

  }
}

これは、コードがより観やすくなるだけでは無く、関数内であってもモジュール化でき、最適化に貢献できます。
ただ、↑の例で、スコープで囲まれた変数「j」を、大域の「j」と混同してしまう場合があり、注意する必要があります。※警告により回避出来ます
その都度宣言する事で、不必要なコードが生成されると思っている人がいますが、最適化されたコードは、そのような事はありません。(コンパイラーの常識、プログラマーの非常識)

OpenGL(with GLFW3)オーディオプレイヤー(ベータ)

OpenGL GLFW3 を使ったオーディオプレイヤーを作ってみました。

まだ、最低限の機能しかありません。

これは、OpenGL で GUI のフレームワークを実現する為のテスト的アプリケーションです。

GUI のフレームワークを作るにしても、何か、アプリケーションを作ってみない事には、必要な要件や仕様などが判らない為です。
GLFW3 を使っているので、Linux や、OS-X でも動作可能と思いますが、現状では Windows 依存のコードが含まれている為、コンパイル出来ません・・

player

・mp3, wav 形式のファイルを再生するアプリケーションです。
・mp3 タグ情報の表示などを行います。
・描画は OpenGL で行っています。
・フォントの描画では、FreeType2 を使っています。
・cygwin の i686-w64-mingw32 クロスコンパイラを使っています。
・左下隅のボタンを押すと、ファイル選択ダイアログが開きます、音楽ファイルを選択して下さい。
・まだベータ版で予期しない動作で落ちる場合もあります(表面的なテストしかされていません)。
・ソースコードは、少し整理してから公開する予定ですが、「今欲しい」方はリクエストを」下さい、方法を考えます。

オーディオプレイヤー

※9月8日15時23分
・大きなメモリーリークを発見したので修正、アーカイブのリンクを修正

※9月9日22時47分
・aac (mp4a) のデコードを追加、タグ情報の表示
・FAAD2 ライブラリーの libmp4ff には、カバー情報(アルバムのアートワーク)を正しく扱えないバグがあり、修正しました。
・その他、微妙に修正

※9月10日22時
GitHub にソースコード一式を公開しました。

AKI-RX62で「C++」を使わない理由が無い!

RXマイコン用、C++デバイス制御クラスを実装していて、色々と少しづつアップデートしている。

今日の標語:

  「C++」を使わない理由が無い!

RX621に内臓されているハードウェアーリソースは豊富なので、まだ、全ては実装出来ていないけど、とりあえず、必要そうな定義は実装してみた。
※十分なテストとかしていないので、バグがあるかもしれないけど・・・
※一番ありがちなのは、アドレス定義ミス。

各ソースコードと機能の対応:
※ルネサスの「RX62Nグループ、RX621グループユーザーズマニュアル ハードウェア編」を元にしている。

rx62x_system.hpp    システム制御関係の定義(リセット、電圧検出回路、クロック発生、消費電力削減)

rx62x_icu.hpp       ICU(割り込みコントローラー)の定義

rx62x_port.hpp      I/Oポートの定義

rx62x_dmaca.hpp     DMACA(DMAコントローラー)の定義
rx62x_exdmac.hpp    EXDMAC(EXDMAコントローラー)の定義
rx62x_dtc.hpp       DTC(データトランスファーコントローラー)の定義
rx62x_crc.hpp       CRC の定義

rx62x_mtu.hpp       マルチファンクションタイマユニットの定義

rx62x_i2c.hpp       I2C(RIIC) バスインターフェースの定義
rx62x_i2c_io.hpp    I2C の制御
rx62x_i2c_io.cpp

rx62x_rspi.hpp      RSPI の定義
rx62x_rspi_io.hpp   RSPI の制御
rx62x_rspi_io.cpp

rx62x_cmt.hpp       CMT(コンペアマッチタイマー)の定義
rx62x_cmt_io.hpp    CMT の制御
rx62x_cmt_io.cpp

rx62x_sci.hpp       SCI(シリアルインターフェース)の定義
rx62x_sci_io.hpp    SCI の制御
rx62x_sci_io.cpp

rx62x_rtc.hpp       RTC の定義
rx62x_rtc_io.hpp    RTC の制御

rx62x_s12ad.hpp     S12AD(12ビットA/Dコンバーター)の定義
rx62x_ad.hpp        AD(10ビットA/Dコンバーター)の定義
rx62x_da.hpp        DA(10ビットD/Aコンバーター)の定義

全ソースコード

☆テンプレート化してあるので、複数チャネルがあるデバイスは全て同じように扱える。
☆デバイスのテンプレートは、多少工夫してあるので、判りやすく、間違えば大抵エラーとなる。

DMACA、4チャネル
EXDMACA、2チャンネル
I2C、2チャンネル
RSPI、2チャンネル
CMT、4チャネル
SCI、6チャネル、※何故か4チャンネルは無く(0、1、2、3、5、6)
※SCIは、シリアル入力(RxD)に対応するポートを入力に指定する必要がある。

ルネサスがサンプルなどで提供しているデバイスの定義ファイルを捨てて、とりあえず、現状、定義してあるデバイスヘッダーでコンパイル出来るように変更した。
この修正で、必要な部分は、全て C++ で実装されているので、それを使う事が出来る。

fatfs は、RXマイコン依存の部分を新しいデバイステンプレートを使って、C++でコンパイル出来るように修正した。
※基本的に、えるむさんのコードはそのままになっている。
※サンプルにあったRXマイコン用のコードは、C++デバイス定義用「mmc_rspi.hpp、mmc_rspi.cpp」に修正されている。

プログラムの動作について:
・RxD0、TxD0 にターミナルを接続すると、簡単なシェルが起動する。(115200、8ビット、1ストップビット)
・RTC は外部の I2C に DS1371 を接続する。(ds1371_io.cpp でどのポートを使っているか判る)
※RX621 内臓の RTC は、バッテリーバックアップ出来ないので仕方なく接続・・・
※RX621 内臓の RTC を使う事も出来る、コードは一応用意した。(rx62x_rtc_io.hpp)
※「sd_monitor.cpp」で、どちらを使うか選択出来る。
・VS1063a の接続する場合、ソースコードを確認の事。
・「syscalls.c」で、POSIX のファイル操作(SDメモリーに対して行う)を実装してあり、fopen 系、printf など普通に使う事が出来る。
・「init.c」で静的に宣言しているクラスなどのコンストラクターが呼ばれる。
・「stdc++」ライブラリーをリンクしているので、STL も使える。
・コマンドは、以下

% ls         SD カードのディレクトリーを表示
% cd xxx     カレントディレクトリーを移動
% date       日付、時間を表示、設定も可能
% play xxx   オーディオファイルを再生(現在はポーリングとなっている)
% volume     ボリュームの呼び出しと設定

このフレームワーク?では、C++ で普通にプログラム可能なのだが、iostream は使わない事、メモリを非常に食うので、フラッシュに入らないと思う。
※ C++ だけど、とりあえず、printf を使って欲しい。
※当然ながら、iostream と関連性が強く、インクルードしてあるソースも避ける事。
STL は普通に使えるし、boost も全てでは無いけど使える~(iostream に関連する制限がある点に注意)

リンクに、変な警告が出ているけど、とりあえず無視するしかないが、問題は無さそう~
※このエラーが何故出るのか不明・・・、出ないようにする方法が不明なので、そのままになってる。

------
I2C や RSPI のデータ転送を DMA ベースにしたいとか、色々やる事があるけど、その前にお約束の LCD を繋ぎたい~

※バグを発見したり、質問があれば書きこんで~

C++ でI/Oデバイスの操作を考える

組み込みでもC++と言う事で、最近では、RXマイコンでC++0Xを使ってソフト開発をしていますが・・

ルネサスなどのサンプルでは、I/Oデバイスの操作は、C言語で使えるビットフィールドを多用しています。
サンプルに付属のハードウェアーデバイスを定義してあるヘッダー「iodefine_renesas.h」がその典型例です、しかしながら、規約では、16ビットや32ビットの定義は処理系依存であったと思います。
又、エンディアンが変わった場合も少なくと gcc では問題あります。
それなのに、このやり方が使われているのは大きな問題だと感じています。

ルネサスが試用版としてダウンロード出来るコンパイラでは問題無いようですが、gcc では問題となります。
※やった事は無いのですが、ルネサスのCコンパイラで boost が正しくコンパイルできるか不明ですし、C++11を使いたいし、IDEは嫌いだしで、積極的に使いたいとは思えません。
※時間が出来たら、RX用のLLVMをポートしたいです。
特に gcc では、最適化が施された場合に、32ビットでしかアクセス出来ないI/O空間に、バイトでアクセスするようなコードが出てくる場合もあります。
※これはRX用 gcc の問題なのかもしれません。

又、C++の強みを生かしたコーディングをしたいのもあります、今回RXマイコン用のコードを作る過程で、C++でI/Oデバイスを操作するライブラリーを実験、考えてみました。
自分のC++スキル以内の事しか出来ませんが、とりあえず、我慢できる程度の物が出来たので紹介します。

まず、どんな感じで書きたいか考えます。
RXマイコンに備わっているI/Oポートを例に考えてみます。

    device::PORT2::DDR.B3 = 1; // PORT23 output (/xCS)
    device::PORT2::DDR.B2 = 1; // PORT22 output (/xDCS/BSYNC)
    device::PORT4::DDR.B7 = 0; // PORT47 input (DREQ)
    device::PORT4::ICR.B7 = 1;
    device::PORT2::DR.B3 = 1; // xCS=H
    device::PORT2::DR.B2 = 1; // xDCS=H

とりあえず、こんな感じでしょうか?
※名前空間は「device」としています。

template <uint32_t base>
struct portx_t {
    typedef io8<base + 0x00> ddr_io;
    struct ddr_t : public ddr_io {
        using ddr_io::operator =;
        using ddr_io::operator ();
        using ddr_io::operator |=;
        using ddr_io::operator &=;

        bits_t<ddr_io, 0, 1>  B0;
        bits_t<ddr_io, 1, 1>  B1;
        bits_t<ddr_io, 2, 1>  B2;
        bits_t<ddr_io, 3, 1>  B3;
        bits_t<ddr_io, 4, 1>  B4;
        bits_t<ddr_io, 5, 1>  B5;
        bits_t<ddr_io, 6, 1>  B6;
        bits_t<ddr_io, 7, 1>  B7;
    };
    static ddr_t DDR;
};
typedef portx_t<0x0008c000> PORT0;
typedef portx_t<0x0008c001> PORT1;

オペレーターの定義として、「=」、「()」、「&=」、「|=」を定義してあります。

「=」オペレーターの戻り値をvoidとして値を返さないようにしてあります。
PORT0::DDR = 0xf0;
と代入は出来ますが・・・
uint8_t data = PORT0::DDR;
とは出来ません、代わりに、
uint8_t data = PORT0::DDR();
とする事で値を取得できます。
これには、幾つかの側面があって、I/Oに対する「読み出し」と「書き込み」は同じアドレスであっても全く別の操作だからです。
又、最適化によって起こる問題を回避できると考えています。
※又、「=」オペレーターを無効にする事により、リードオンリーのデバイスを扱えます。(書き込もうとするとコンパイルエラーになります)

さて、バイト操作が出来たので、ビット操作を考えてみます。
※ bits_t の定義がそれに辺り、アクセス開始ビットと、アクセスするビット幅を定義してあります。

template <class T, uint8_t pos, uint8_t len>
struct bits_t {
    static typename T::value_type get() {
        return (T::read() >> pos) & ((1 << len) - 1);
    }
    static void set(typename T::value_type v) {
        typename T::value_type m = ((1 << static_cast(len)) - 1) << pos;
        T::write((T::read() & ~m) | (v << pos));
    }

    void operator = (typename T::value_type v) { set(v); }
    typename T::value_type operator () () { return get(); }
};

テンプレートに渡すT型のクラスは、8ビット、16ビット、32ビットのアクセス幅により決定します。

    typedef io8<base + 0x00> ddr_io;
    bits_t<ddr_io, 0, 1> B0;

※上の例では、io8 クラスを使っています。

io8 はこんな感じです。

template <uint32_t adr>
struct io8 {
    typedef uint8_t value_type;
    static void write(uint8_t data) { wr8_(adr, data); }
    static uint8_t read() { return rd8_(adr); }void operator = (value_type data) const { write(data); }

    value_type operator () () const { return read(); }
    void operator |= (value_type data) const { write(read() | data); }
    void operator &= (value_type data) const { write(read() & data); }
};

実際にハードウェアー上のアドレスにアクセスするのは、以下です。

static inline void wr8_(uint32_t adr, uint8_t data) {
    *reinterpret_cast<volatile uint8_t*>(adr) = data;
}
static inline uint8_t rd8_(uint32_t adr) {
    return *reinterpret_cast<volatile uint8_t*>(adr);
}

まだ冗長な部分がありますが、とりあえずやりたい事は出来ています。

※完全なソースコードは、AKI-RX62 でMP3再生(VS1063a)を参照して下さい。

※参考文献:
C++テンプレートテクニック

追記、修正: 2013,7,21
operator は継承出来ないので、冗長となっていた部分、using 文で、シンプルに書ける事が判った為修正。
※using ってこんな感じで使えるのか・・
※RX のソースアーカイブは、別の作業(RSPIの割り込み化)をしているので、それが終わってから更新します。

追記、修正: 2013,7,25
コンパイルした場合にどのようなアセンブリコードが出てくるか検証しました。

int main(int argc, char** argv)
{
    // ICLK=96MHz, BCLK=48MHz, PCLK=48MHz SDCLK出力,BCLK出力
    device::SYSTEM::SCKCR = 0x00010100;
    device::SYSTEM::MSTPCRA.MSTPA15 = 0;  // B15 (CMT)のストップ状態解除
    device::SYSTEM::MSTPCRB.MSTPB31 = 0;  // B31 (SCI0)のストップ状態解除
    device::PORT3::DDR.B0 = 1;            // PORT3:B0 output
    device::PORT2::ICR.B1 = 1;            // PORT2:B1 input (RXD0)
}

-O2 で最適化した場合のアセンブリコードです。

fff80a64 <_main>:
fff80a64: fb ee 20 00 08         mov.l    #0x80020, r14
fff80a69: f8 ee 00 01 01         mov.l    #0x10100, [r14]
fff80a6e: fb 4e 10 00 08         mov.l    #0x80010, r4
fff80a73: ec 4e                  mov.l    [r4], r14
fff80a75: fb 3e 14 00 08         mov.l    #0x80014, r3
fff80a7a: 77 2e ff 7f ff         and      #0xffff7fff, r14
fff80a7f: e3 4e                  mov.l    r14, [r4]
fff80a81: ec 3e                  mov.l    [r3], r14
fff80a83: fb 4e 03 c0 08         mov.l    #0x8c003, r4
fff80a88: 74 2e ff ff ff 7f      and      #0x7fffffff, r14
fff80a8e: e3 3e                  mov.l    r14, [r3]
fff80a90: cc 43                  mov.b    [r4], r3
fff80a92: fb ee 62 c0 08         mov.l    #0x8c062, r14
fff80a97: 65 13                  or       #1, r3
fff80a99: c3 43                  mov.b    r3, [r4]
fff80a9b: cc e4                  mov.b    [r14], r4
fff80a9d: 66 01                  mov.l    #0, r1
fff80a9f: 65 24                  or       #2, r4
fff80aa1: c3 e4                  mov.b    r4, [r14]
fff80aa3: 02                     rts

及第点でしょうか・・

MuPDF 1.2 ライブラリーの C++ ラッパー

MuPDF とは?

MuPDF は、PDF を描画する為のライブラリーで、C で実装されたオープンソースライブラリーである。
非常に軽量で、高速、色々なプラットフォームに対応している。

Adobe の PDF Reader は、はっきり言って「糞」だ、頻繁にバージョンアップを繰り返して、とにかく「ウザイ」し、重いし、良い所は無い。
MuPDF のエンジンを使ったアプリケーションとして、Sumatra PDF がある。
※自分は、AdobeReader はアンインストールして、コレを使っているが、得に不便を感じない。

以前から、自分のフレームワーク(以前から使っていた「glfw」って名称は、GLFW3 と混同しやすいので使えん、新しい名称を考えないと・・・)では MuPDF ライブラリーのラッパーを実装していたが、それはバージョンが 1.0 になる前で、最新は、1.2 となっている。
バージョンアップしたいと思っていたが、API 変わって、書き直す必要があった、最近ようやく、1.2ライブラリーをコンパイルしなおして、ラッパーを書き直した。

コードがかなり洗練され、とても使いやすくなったようだ、ワイド文字列の処理も UTF8 に統一され、レンダリングも簡単になった。

折角なので、簡単な描画サンプルを作ってみた、描画は GLFW3(OpenGL) で、レンダリングされた PDF はテクスチャーとして扱う。
操作方法:
起動したら、PDF ファイルをドラッグ&ドロップ
マウスボタンの右、左でページ移動(PageUp、PageDownキーでも可能)

自分で作成したアプリケーションに PDF を簡単に表示できるのは、色々と便利な事が多い!
このアプリは、サンプルなので、必要最低限の機能しかないのであしからず~

「img_io/pdf_in.hpp」を観てもらえば、大体の使い方は判ると思う。

ソースコード
実行バイナリー

コンパイルに必要なライブラリーパック
※注意 /usr/local/ 以下に展開
※このライブラリーパックに mupdf のライブラリー libfitz.a が同根されています。
※ MuPDF ライブラリーは、freetype2、JPEG、Z-Lib、jbig2dec、OpenJpeg(JPEG2000)などのライブラリーを必要とします。(それも同根してあります)
※JPEG ライブラリーは、x86 アセンブラで高速化してあるバージョンが入っています。

らぶひな PDF
PDF_test1

RX マイコンのPDF
PDF_test2

GLFW3と自分FWを使って昔懐かしい「Space Invaders」を作る

以前から、チクチクとゲーム開発の補助になる自分用フレームワークを作ってきましたがー
GLFW3 がリリースされたタイミングで、glut から GLFW3 に変更していましたが、とりあえずのベータが出来たので、アーカイブを公開します。

自分用フレームワークの完成度を上げるには、一通りの機能を使って、アプリケーションを作る必要があるものです。
また、C++ の熟練度が少しづつ向上して、ソースコードの品質も少しづつではあるけど洗練されてきつつあります。
そこで、何か良い題材は無い物かと考えてましたが、とりあえず、キーボード入力と、サウンド(効果音)が出る、そして簡潔に出来る。
などの理由で、昔懐かしい「Space Invaders」を作る事にしましたww

「作る」と言っても、「Space Invaders」は、mame32 由来のエミュレーターソースをそのまま使います。
※その為、このソースコードもそれらライセンスに従います。

スペースインベーダーは、8ビットのZ80(2MHz)を使い、256×240のモノクロビットマップグラフィクスを使ったゲームで、
当時は、もの凄く洗練されていました。(今でも洗練されていると思う)
エミュレーターは、8080のエミュレーションと、インベーダーのハードをエミュレーションします。
結果は、モノクロビットマップの出力として出てくるので、OpenGL のテクスチャーに貼って、描画します。
インベーダーはアナログシンセサイザーを持っていて、現在では、はサンプリングされたデータを入手出来るので、それを使いSEとして
発音します。

spain

プログラムの動作には、ROMイメージが必要なので、どこかで見つけて下さい。
※このプログラムでは、sounds.zip、invaders.zip の二つのアーカイブを必要とします。
ここが参考になると思います

ソースコード

コンパイルに必要なライブラリー郡、/usr/local 以下に展開

boost_1_53_0 が必要です。

コンパイル済みバイナリー

※操作方法
「1」1プレイヤーボタン
「2」2プレイヤーボタン
「3」コイン
「←」ビーム砲左
「SPACE」ビーム発射
「→」ビーム砲右

GLFW-3.0.1 の機能追加

GLFW ですが、既に 3.0.1 がリリースされてましたーー

Bugfix: The wrong name was used for the CMake variable for the Xxf86vm library
[Cocoa] Bugfix: glfwGetFramebufferSize return the size in screen coordinates
[Cocoa] Bugfix: Messages not supported on Mac OS X 10.6 were used without tests for precence
[Cocoa] Bugfix: Process transformation was not performed if menu bar creation was disabled
[Win32] Bugfix: Context creation was attempted even if no valid pixel formats had been found
[X11] Bugfix: Duplicate window position and window and framebuffer size events were reported

まぁ、基本的にBugFixのようですが・・・

さて、OpenGL のアプリケーションですが、作っていて不便な事があります、アプリケーション・ウィンドウに
ファイルをドラッグ・アンド・ドロップして、ファイル名を受け取る事が出来ない事です。

アプリケーションを作る上で不便なので、GLFW を改造して、この機能を付けましたー

glfw-3.0.1 のパッチ

glfw-3.0.1 のソースを展開して、ルートディレクトリーに異動後、以下のコマンドを入れればパッチが当たると思います。

patch -p0 < glfw_3_0_1-dropfiles.diff で、make すれば OK。 exsamples に dropfiles.c のサンプルがあるので、これを観ればどのようにファイル名を受け取るのか判ると思います。 もっと実用的なサンプルとして、ビューアー・プレイヤーを作りました。
※ソースコードが欲しい方は、連絡下さい、必要なフレームワークの準備に少し時間が必要です。

このアプリケーションは、起動すると、空のウィンドウが開きます、ここに画像ファイル(BMP、PNG、JPEG、JPEG2000)や音楽ファイル(WAV、MP3)を落とせば、それを開きます。
※音楽ファイルは再生します、MP3に画像が含まれている場合は、それを表示します。

※メイン部分だけアップしておきます~

GLFW3.0 を使う

自分のフレームワークとして、「gl_fw」と言うプロジェクトを実装していましたがー

最近(そうでもないかww)、GLFW と言うフレームワークがある事を知りました、現在のところ、OS-X、X11、Windows で使える、マルチプラットホームのフレームワークです。
C、Objective-C(OS-X)で実装されており、シンプルで、良く出来ています。
※「glut」があるのですが、これは、メンテナンスされていないし、色々と痛いところが多いのです。

最近、メジャーバージョンが「3.0」となり、「2.x」から、より洗練された実装になりましたー、まだ機能で欲しい部分は色々あるのですがー、これから追加されていくと思います。

自分のフレームワークも glut から GLFW に変更すべく色々修正中です。
GLFW 3.x で良い部分をいくつか上げると・・・
・マウスホイールのイベントを取得できる。
・クリップボードのやり取りが追加された。
・メインループから綺麗に抜けてくれる。(glutでは、スマートでは無い方法で実装しなければなりません・・)
・ゲームパッドの値を標準的に取得できる。
・キーボードの扱いがゲーム向き。(単純に押したか、押さないを取得できる)
・必要最小限の機能だけ用意してあり、わかり易い。
・ライブラリーをコンパイルするのが簡単。(cygwin の windows クロスコンパイラ i686-w64-mingw32 でコンパイルできるようになった)

現在の「3.0」には、ドロップファイルのイベントを受け取る機能が無いので、これは自分で足そうと思います。
※「2.x」で実装したので、「3.0」風に書き直す感じww

cygwin で glfw-3.0 をコンパイルする手順を書いておきます。

(0) cygwin に i686-w64-mingw32-gcc、cmake などをインストールしておく。
(1) glfw-3.0 のソースアーカイブを取得して展開する。
(2) glfw-3.0 に移動
(3) 「cmake -DCMAKE_TOOLCHAIN_FILE=CMake/i686-w64-mingw32.cmake」
(4) 「make」
※何も指定しないとスタティックリンクライブラリーが作られるようです。(DLL にする意味は無いと思う)

これでおしまい!(ああ、何て簡単なんだろ・・)

(5) 「make install」で、/usr/local/以下にインストールされます。

では動かしてみましょう!


#include <stdlib.h>
#include <GLFW/glfw3.h>

int main(int argc, char** argv);
int main(int argc, char** argv)
{
    GLFWwindow* window;

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (!window)
    {
        glfwTerminate();
        return -1;
    }

    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    {
        /* Render here */

        /* Swap front and back buffers */
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    }

    glfwTerminate();

    return 0;
}

※ cygwin のコンパイルで重要なのは、リンクライブラリーとして、glfw3、opengl32、glu32、gdi32 を加える事です。

さて、ゲームなどで必要なのは、サウンド、スレッド、画像の読み込み等ですが、GLFW のポリシーでは、「これをフレームワークに含め無い方が良い」との考えのようです。

自分もそう思います、自分は、以下のように解決しています。
・サウンド ---> OpenAL を使うのが良いでしょう~
・スレッド ---> POSIX のスレッド pthread を使う。(OS-X、Linux、mingw では標準です、VisualStudio を使う場合は、pthread-win32 ライブラリーを使います)
・画像 ---> libpng、などを使えば良いし、読み込みをサポートするライブラリーやソースは沢山あるので、自分に合った物を選べば良い。

「gl_fw」(紛らわしいけど、GLFW を知る前からなので・・・)の改修が終わったら、またブログを書きます。
※gl_fw は、C++ を使ったフレームワークで、サウンド(単音、ストリーム)を扱うクラス、画像ファイルを単一的に扱うクラス、フォント(漢字)、GUI、などそこそこ高機能な物です。
ソースコードは、テストが済み次第取得できるようにしますが、興味のある人は連絡下さい~
githubから取得出来ます。
※glfw3には、ドロップファイルを受け取る改修をしています(windows版のみ)、Libraries にlibファイルとヘッダーを用意しています。

クアッドコプター3

骨組みが出来たら、現実味が増した感じですけどー、まだまだ先は長いですねー
まず、基板に、ジャイロ。加速度センサーを中心に配置しました、残りのスペースにCPUなどを載せます。
今回は手持ちの関係で、SH7125を使う事にしましたー、現在では、100MHzのRXマイコンが手頃な価格で入手できるので、わざわざ、過去のCPUを使う理由も無いと思いますがー、タンスの肥やしにするのもアレなので、あえてこれを使います、機能的には十分なものと思います。
SH7125とRXマイコンの比較(参考)
・価格
SH7125F(R5F71253) ---> 900円(秋月の価格)
RXマイコン(R5F56218BDFP) ---> 950円(秋月の価格)
・内蔵メモリー
SH7125F ---> フラッシュ128キロバイト、RAM8キロバイト
RXマイコン ---> フラッシュ512キロバイト、RAM96キロバイト
・速度など
SH7125F ---> 50MHz、5V動作、各種タイマー、A/Dなど
RXマイコン ---> 100MHz、単精度浮動小数点演算器、2.7V~3.6V動作、A/D、USBインターフェース、リアルタムクロックなど

どうです、比べるまでも無い感じですね・・
※基板作る時にはRXでやってみようとおもいますねー

まあでも、SH7125のりソースは少しだけあるので、未知のCPUで、荒波に飛び込むより、無難ではあります、でもやっぱりメモリー(RAM)が少ないのは痛いです・・
プログラム変更の度にフラッシュに書き込むのは面倒だし効率が悪いので、簡単なモニターを用意して、実験段階ではRAMで開発をするのが一般的な方法です、自分も、Sフォーマットをロードしたり、メモリーのダンプを行うモニターを作成しましたが、RAMが狭く、色々考えながら進めないと駄目な感じです。

昨日も、インプットキャプチャー機能を試していると、1チャンネルだけなら動作するのに、複数のチャネルを同時に動かすとハングアップしたり、妙な動作をしていましたがー、よくよく調べてみると、スタックの領域と、ワークエリアが接近し過ぎていて、ワークエリアを壊していたようです・・・
※これ気がつくまでに凄く時間がかかりました・・・・・

・プロペラの話・・・
クアッドコプターでは、2組の正転、逆転の組みを作り、回転による反力を相殺します、最初、「あぁ、逆に回すんだから、プロペラを逆に組めばいいんだよねー」と簡単に考えてましたがー、実際に逆転にしたら、風向も逆になるだけで意味ありません、よーーく考えると、「ピッチが逆じゃないと駄目なんでは・・」と思いましたー、ネットを調べると、CW/CCWペアとかでプロペラ売ってました、「あーーあ、なるほどねー」って感じで、急遽、シンガポールで売っていた8インチ、ピッチ4.5のプロペラを購入しましたー(汗)

このクラスだと、飛ぶ事は出来るでしょうが、やはり推力は出ない感じなので、実験機が上手くいったら、RXマイコンで作り直します、その時は10インチのプロペラでやってみたいですねー

続く~