2012年3月25日日曜日
私が好んで使用しているクロス・プラットフォームの実装方法(シリアル・ポート・ライブラリの実例)
クロス・プラットフォーム!
プロフェッショナル・ソフトウェア・エンジニアの場合、自分が書いたコードが一体何のプラットフォームに依存しているのかを意識して書くのが通例です。この意識の下で設計実装する事で、異なるプラットフォームへの移植要求に対して柔軟に対応できます。結果的に機能実現に投資した労力に対する効果を最大にする事ができます。まぁ、簡単に言うと喜んで頂ける方が増えるわけです。
クロス・プラットフォームの実装方法にはいくつも方法があります。
今回は私が好んで使用している方法について述べたいと思います。
ソースコードのダウンロード
今回の説明に用いたソースコードは以下からダウンロードできます。
ダウンロードはこちらから。
クロス・プラットフォームの実装方法の中で、実利的でありながらあまり好ましくない例があります。
例えば、シリアルポートに対する制御を行なうライブラリをWindowsやLinuxで提供する事を考えてみましょう。
好ましいとは言えない例の場合、例えば以下のような感じの実装だったりします。
<シリアルポートをオープンする関数>
#ifdef WIN32
// Windows用のシリアルポートをオープンする処理
#else
// Linux用のシリアルポートをオープンする処理
#endif
<シリアルポートに書き込む関数>
#ifdef WIN32
// Windows用のシリアルポートに書き込む処理
#else
// Linux用のシリアルポートに書き込む処理
#endif
<シリアルポートから読み込む関数>
#ifdef WIN32
// Windows用のシリアルポートから読み込む処理
#else
// Linux用のシリアルポートから読み込む処理
#endif
<シリアルポートをクローズする関数>
#ifdef WIN32
// Windows用のシリアルポートをクローズする処理
#else
// Linux用のシリアルポートをクローズする処理
#endif
どうですか?
見やすいでしょうか?
Windows向けの実装に目を通している時でも、Linux向けのコードも一緒に目に入ってきます。
万が一Linux向けのコードを知らずにいじってしまったらどうなるでしょう?
Windows向けの機能修正後に知らずにコミットした場合には目も当てられません。
Windows向けの機能修正後にLinux側のテストもしてくれるでしょうか?
本来は「テストして当たり前」の事なのですが、実際に期待通りテストしてくれるとは限りません。
フローが曖昧な現場やプロジェクトもあれば、テストしない乱暴な人もいるからです。
プロジェクトにおいて緊急対応などになればなおさらです。
そんな事はあってはならないのですが、実際にはよくある話です。
残念な事に、実際の開発現場では上記のようなコードが多く存在します。
業務上の開発において、あまりこの手のコードを信用する気になれません。
ペアになって存在すべきテスト用コードが無ければなおさらです。
プラットフォームの切り替えがコードに記述されているという事は、依存する定義ファイルへのincludeも#ifdefで切り替えている事になります。対象プラットフォームのビルド環境に依存して、依存する定義ファイルが異なる場合、上記のプラットフォーム切り替え以外の#ifdefも混在します。
プラットフォーム依存コードだけの問題でない事になります。
また、上記の実装はソースコードがビルド・スクリプトにも依存している事を暗に示しています。
これも私が上記のような実装を好まない理由の1つです。
実装詳細を知らないとビルド・スクリプトが書けない事を意味し、更にこの手のコードが数十個、数百個存在して、なおかつそれらが相反する要求になっていた場合どうなるのだろう?と考えただけで嫌になります。
例えばWIN32なの?それともWINDOWSなの?えーとWINなの?とかビルド・スクリプトを書く時に考えたくないですよね?
そして#ifdefの中身が実際にコードとしてコンパイルされているのか?などと余計な事を考えなくてはなりません。これは本来考えなくて良い内容です。
私が好んで使用している方法
私は好ましいとは言えない例に挙げたようなコードを実際の開発現場で多く見てきました。
更にこういった実装をしている下層ライブラリを使う事になった場合、本当に不安になるわけです。
不安の背景には、先に挙げた小さな要素が複数存在します。
私の場合、以下のようなアプローチで先に挙げた不安をできるだけ解消するようにしています。
<シリアルポートをオープンする関数>
// Linux用のシリアルポートをオープンする処理
<シリアルポートに書き込む関数>
// Linux用のシリアルポートに書き込む処理
<シリアルポートから読み込む関数>
// Linux用のシリアルポートから読み込む処理
<シリアルポートをクローズする関数>
// Linux用のシリアルポートをクローズする処理
先のプラットフォーム混在時の実装モデルと見比べて分かるとおり、シンプルな実装が2つのプラットフォームに対して提供されている事がわかります。そして、互いの実装は異なるファイルに実装されています。
これが「インターフェースはヘッダで、実装の詳細はソースで、を厳密に扱う。(当たり前) 」と「プラットフォーム毎に実装を分離する。プラットフォーム切り替えのために#ifdefは使わない。」の実例です。
当然ですがヘッダにはプラットフォーム依存の内容は一切書けません。
例えば、プラットフォーム依存の要素はどのように実装するのでしょうか?
例えば、ハンドラひとつとってみてもWindowsとLinuxで事情が異なるかもしれません。
Windowsの場合HANDLEが使用され、Linuxの場合intが使用されます。
これから示すのが「ハンドラを賢く実装する。」の答えです。
ヘッダでは「ハンドラの構造体をSERIALという名前の型として定義するだけ」の実装です。
ヘッダでは実装の詳細について触れず、「とにかくstruct serialをSERAILとして扱う事だけ」を宣言します。
では、実装側はどうなっているのか?を見てみます。
まずはWindows用の実装です。
そしてLinux用の実装です。
(Linux用の実装はWindowsと異なり端末状態の復旧やミューテックスが含まれています。)
先の例ではプラットフォームはWindowsとLinuxの2つでした。
プロフェッショナル・ソフトウェア・エンジニアの場合、自分が書いたコードが一体何のプラットフォームに依存しているのかを意識して書くのが通例です。この意識の下で設計実装する事で、異なるプラットフォームへの移植要求に対して柔軟に対応できます。結果的に機能実現に投資した労力に対する効果を最大にする事ができます。まぁ、簡単に言うと喜んで頂ける方が増えるわけです。
クロス・プラットフォームの実装方法にはいくつも方法があります。
今回は私が好んで使用している方法について述べたいと思います。
ソースコードのダウンロード
今回の説明に用いたソースコードは以下からダウンロードできます。
ダウンロードはこちらから。
- ライセンスはMITです。
- 実際にkz_h8writeの下層で使用しているシリアル・ライブラリです。
- 好ましいとは言えない例は、ここでは掲載しませんので実例を探してみて下さい。
クロス・プラットフォームの実装方法の中で、実利的でありながらあまり好ましくない例があります。
例えば、シリアルポートに対する制御を行なうライブラリをWindowsやLinuxで提供する事を考えてみましょう。
好ましいとは言えない例の場合、例えば以下のような感じの実装だったりします。
<シリアルポートをオープンする関数>
#ifdef WIN32
// Windows用のシリアルポートをオープンする処理
#else
// Linux用のシリアルポートをオープンする処理
#endif
<シリアルポートに書き込む関数>
#ifdef WIN32
// Windows用のシリアルポートに書き込む処理
#else
// Linux用のシリアルポートに書き込む処理
#endif
<シリアルポートから読み込む関数>
#ifdef WIN32
// Windows用のシリアルポートから読み込む処理
#else
// Linux用のシリアルポートから読み込む処理
#endif
<シリアルポートをクローズする関数>
#ifdef WIN32
// Windows用のシリアルポートをクローズする処理
#else
// Linux用のシリアルポートをクローズする処理
#endif
どうですか?
見やすいでしょうか?
Windows向けの実装に目を通している時でも、Linux向けのコードも一緒に目に入ってきます。
万が一Linux向けのコードを知らずにいじってしまったらどうなるでしょう?
Windows向けの機能修正後に知らずにコミットした場合には目も当てられません。
Windows向けの機能修正後にLinux側のテストもしてくれるでしょうか?
本来は「テストして当たり前」の事なのですが、実際に期待通りテストしてくれるとは限りません。
フローが曖昧な現場やプロジェクトもあれば、テストしない乱暴な人もいるからです。
プロジェクトにおいて緊急対応などになればなおさらです。
そんな事はあってはならないのですが、実際にはよくある話です。
残念な事に、実際の開発現場では上記のようなコードが多く存在します。
業務上の開発において、あまりこの手のコードを信用する気になれません。
ペアになって存在すべきテスト用コードが無ければなおさらです。
プラットフォームの切り替えがコードに記述されているという事は、依存する定義ファイルへのincludeも#ifdefで切り替えている事になります。対象プラットフォームのビルド環境に依存して、依存する定義ファイルが異なる場合、上記のプラットフォーム切り替え以外の#ifdefも混在します。
プラットフォーム依存コードだけの問題でない事になります。
また、上記の実装はソースコードがビルド・スクリプトにも依存している事を暗に示しています。
これも私が上記のような実装を好まない理由の1つです。
実装詳細を知らないとビルド・スクリプトが書けない事を意味し、更にこの手のコードが数十個、数百個存在して、なおかつそれらが相反する要求になっていた場合どうなるのだろう?と考えただけで嫌になります。
例えばWIN32なの?それともWINDOWSなの?えーとWINなの?とかビルド・スクリプトを書く時に考えたくないですよね?
そして#ifdefの中身が実際にコードとしてコンパイルされているのか?などと余計な事を考えなくてはなりません。これは本来考えなくて良い内容です。
私が好んで使用している方法
私は好ましいとは言えない例に挙げたようなコードを実際の開発現場で多く見てきました。
更にこういった実装をしている下層ライブラリを使う事になった場合、本当に不安になるわけです。
不安の背景には、先に挙げた小さな要素が複数存在します。
私の場合、以下のようなアプローチで先に挙げた不安をできるだけ解消するようにしています。
- インターフェースはヘッダで、実装の詳細はソースで、を厳密に扱う。(当たり前)
- プラットフォーム毎に実装を分離する。プラットフォーム切り替えのために#ifdefは使わない。
- ハンドラを賢く実装する。
Cの実装においてインターフェースはヘッダで、実装の詳細はソースでというのは当たり前の話でが、実際に実装されているコードを見ているとあまり厳密に扱っていない例を多く見かけます。
ヘッダで定義する内容や外部依存定義を最小限にする事で、外部依存が最小で移植性の高いソフトウェア・コンポーネントが実現できます。厳密なインターフェースの定義に対して、それぞれの実装の詳細を提供する事で機能を実現するのが基本です。
私の場合、ヘッダに対する実装という形で「ファイル名を見ただけで想像できる」ように名称をつけるようにしています。
Windows向けのライブラリの実装は、概ね以下のとおりです。
<シリアルポートをオープンする関数>
// Windows用のシリアルポートをオープンする処理
<シリアルポートに書き込む関数>
// Windows用のシリアルポートに書き込む処理
<シリアルポートから読み込む関数>
// Windows用のシリアルポートから読み込む処理
<シリアルポートをクローズする関数>
// Windows用のシリアルポートをクローズする処理
// Windows用のシリアルポートをオープンする処理
<シリアルポートに書き込む関数>
// Windows用のシリアルポートに書き込む処理
<シリアルポートから読み込む関数>
// Windows用のシリアルポートから読み込む処理
<シリアルポートをクローズする関数>
// Windows用のシリアルポートをクローズする処理
Linux向けのライブラリの実装は以下です。
<シリアルポートをオープンする関数>
// Linux用のシリアルポートをオープンする処理
<シリアルポートに書き込む関数>
// Linux用のシリアルポートに書き込む処理
<シリアルポートから読み込む関数>
// Linux用のシリアルポートから読み込む処理
<シリアルポートをクローズする関数>
// Linux用のシリアルポートをクローズする処理
先のプラットフォーム混在時の実装モデルと見比べて分かるとおり、シンプルな実装が2つのプラットフォームに対して提供されている事がわかります。そして、互いの実装は異なるファイルに実装されています。
これが「インターフェースはヘッダで、実装の詳細はソースで、を厳密に扱う。(当たり前) 」と「プラットフォーム毎に実装を分離する。プラットフォーム切り替えのために#ifdefは使わない。」の実例です。
当然ですがヘッダにはプラットフォーム依存の内容は一切書けません。
例えば、プラットフォーム依存の要素はどのように実装するのでしょうか?
例えば、ハンドラひとつとってみてもWindowsとLinuxで事情が異なるかもしれません。
Windowsの場合HANDLEが使用され、Linuxの場合intが使用されます。
これから示すのが「ハンドラを賢く実装する。」の答えです。
ヘッダでは「ハンドラの構造体をSERIALという名前の型として定義するだけ」の実装です。
ヘッダでは実装の詳細について触れず、「とにかくstruct serialをSERAILとして扱う事だけ」を宣言します。
では、実装側はどうなっているのか?を見てみます。
まずはWindows用の実装です。
そしてLinux用の実装です。
(Linux用の実装はWindowsと異なり端末状態の復旧やミューテックスが含まれています。)
こんな感じで2つのプラットフォームに対する実装は、それぞれ異なる物を提供していますが、上位からは2つの差異を気にすることなく使用できます。(Windows側はちょっと手抜き感がありますが・・・。)
設計実装の判断基準
私の設計実装の判断基準の1つは「その設計や実装がN個になったらどうなるのか?」です。
先の例ではプラットフォームはWindowsとLinuxの2つでした。
では、3つ目のプラットフォームが出てきた場合どうなるのでしょうか?
あまり好ましい実装になりそうにありませんよね?
私のアプローチの場合、3つ目のプラットフォームが現れた場合、新たにファイルを1つ追加するだけで済みます。もちろん他のプラットフォームの実装には一切影響を与えません。
こんな感じで設計実装の判断を行なっています。
様々なアプローチに対して設計実装の判断基準を明確にしておくと、設計や実装の度に迷う必要がなくなります。
まとめ
今回はシリアル・ポート・ライブラリを実例に挙げ、異なるプラットフォームに対してどのように実装を提供するとシンプルにまとめる事ができるのか?について示しました。
2012年2月22日水曜日
小規模組み込みシステムでも使えるBMPライブラリ(Tiny BMP I/O)
はじめに
これは何?
Tiny BMP I/Oは、BMP Data StreamからBMPイメージを読み込んでPixel Storageにピクセル値を格納したり、Pixel Storageからピクセル値を読み込んで、BMP Data StreamにBMPイメージを書き込んだりすることのできるライブラリです。
一般に書かれたライブラリでは、「BMPデータがどこに格納されているのか=ファイルに格納されている」、「ピクセルデータがどこに格納されているのか=メモリ」という暗黙の前提が使われている事が多いのですが、Tiny BMP I/Oはそれらをシステム依存として捉えて設計してあります。
システムに依存する部分をきちんと切り出す事で「1つのライブラリでSDからも、フラッシュからもSRAMからだって読める!」、「カメラから読んだ画像を簡単にBMPファイルにできる!」など、色々な用途で使用する事が可能になります。
例えば、「SDカードにあるファイルを読み込んで液晶に表示したい」とか「カメラから取り出した映像をファイルに書き込みたい」などはすぐに考える事のできる応用例の1つです。
また、それだけではつまらないのでbmpimg_tというキャンバスに対して描画可能なプチユティリティも装備しました。描きたいパターンをさっと実現してファイル化する事が可能です。
int bmpimg_draw_box(bmpimg_t *p, const int x1, const int y1, const int x2, const int y2, bmpcol_t *color);
int bmpimg_fill_box(bmpimg_t *p, const int x1, const int y1, const int x2, const int y2, bmpcol_t *color);
int bmpimg_draw_string(bmpimg_t *p, const int x, const int y, const int size, const char *text, bmpcol_t *color);
int bmpimg_draw_line(bmpimg_t *p, const int x1, const int y1, const int x2, const int y2, bmpcol_t *color);
int bmpimg_draw_pixel(bmpimg_t *p, const int x, const int y, bmpcol_t *color);
- 24ビットBMPファイルの入出力に対応。
- 特定プラットフォームに非依存。
- 便利な描画用ユティリティ付属。
- その他。
簡単に使えます
Tiny BMP I/Oはシステムに依存する以下の機能を実装するだけで簡単に使えます。
- データのリード、ライト関数
- ピクセルのリード、ライト関数
データのリード、ライト関数は、BMPのイメージが直列化されて格納されている領域を、ストリームとして入出力するための機能を提供するものです。
ピクセルのリードライト関数は、画像を実際に表示したりするための入出力機能を提供するものです。
ポイント
この手のライブラリは世の中に溢れています。
「また車輪の再開発か」と言われそうですが、必ずしもそうではありません。
ここではTiny BMP I/Oで考慮されている点を挙げてみます。
- データがどこに格納されているのか?に依存しない。
- 画像ピクセルがどこに、どのように格納されているのか?に依存しない。
データがどこに格納されているのか?に依存しない。
小規模組み込みシステムでは、データが必ずファイルシステム上に存在するとは限りません。
一般に流通しているライブラリの多くは「データはファイルシステム上のファイルとして存在する事」を前提に設計実装されています。これらのライブラリを使用する場合、小規模組み込みシステムにファイルシステムとlibcをポートする事になります。
Tiny BMP I/Oは、データがどこに格納されいるのか?についての前提を持ちません。
それがファイルシステム上であろうが、フラッシュメモリ上であろうが、SRAM上であろうが、何の問題もありません。どこに格納されていても
画像ピクセルがどこに、どのように格納されているのか?に依存しない。
ここで言う「画像ピクセル」とは、データを読み込んで解釈した映像の1つ1つのピクセルを指します。
「画像ピクセルがどこに格納されているのか?」ですが、普通に考えるとメモリ上に格納するわけですが、ちょっとした画像ファイルでも結構な容量になってしまいます。例えば、1920x1080ピクセルでR, G, Bの各レイヤーが8ビットだったとすると、1920x1080x1x3=6,220,800[Bytes] (約6[MB])になってしまいます。潤沢なメモリが対象システムに存在するかどうかはシステム次第です。
読み込んだデータをそのままディスプレイに表示させたいだけの場合もあります。
この場合、わざわざ読み込んだデータをメモリ上に展開する必要はありません。
そのまま表示してしまえば良いのです。
巷に出回っているBMPライブラリの多くは、画像ピクセルデータをメモリに展開する事を前提で書かれているため、小規模組み込みシステムでそのまま使用することは難しい事があります。
Tiny BMP I/Oはシステムに対するインターフェースを規定して設計してあります。
このインターフェースさえ守れば、画像ピクセルがどこにどのように格納されていても問題ありません。
サンプルプログラムでは、インターフェースの動作を的確に示すために、汎用プラットフォームで動作するコードを示しています。このコードを見れば、Tiny BMP I/Oが規定している入出力インターフェースを簡単に理解することができます。
実際にTiny BMP I/Oを使って出力したサンプル画像を示します。
サンプルプログラムでは、汎用プラットフォーム向けの実装を提供していますが、Tiny BMP I/Oのインターフェースに従った実装をプラットーフォーム向けに提供しているだけです。
プラットフォームに依存した部分を書き換えれば色々なプラットフォームで使うことができます。
/**
* @file sample.c
* @author Shinichiro Nakamura
* @brief 小規模組み込みシステム向けBMP I/Oのサンプル実装。
*/
/*
* ===============================================================
* Tiny BMP I/O Module
* Version 0.0.1
* ===============================================================
* Copyright (c) 2010-2011 Shinichiro Nakamura
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or
* sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
* ===============================================================
*/
#include
#include
#include "pattern.h"
int main(int argc, char **argv);
/**
* @brief 独自に規定したキャンバス構造。
* @details
* このサンプルでは、汎用プラットフォーム向けなのでメモリ上に画像を格納する。
*/
typedef struct {
int w; /**< 横方向サイズ。 */
int h; /**< 縦方向サイズ。 */
bmpcol_t *buffer; /**< バッファへのポインタ。 */
} canvas_t;
/**
* @brief ストリームからデータを読み込む。
* @details
* インターフェースでは、何からどのように読み込むかについて一切感知していない。
* この関数では、何からどのように読み込むかについて解決する。
*
* @param buf バッファへのポインタ。
* @param size 読み込みバイトサイズ。
* @param extobj ユーザが指定した拡張オブジェクト。
*/
int func_fread(void *buf, const unsigned int size, void *extobj)
{
FILE *fp = (FILE *)extobj;
return fread(buf, size, 1, fp);
}
/**
* @brief ストリームへデータを書き込む。
* @details
* インターフェースでは、何にどのように書き込むかについて一切感知していない。
* この関数では、何にどのように書き込むかについて解決する。
*
* @param buf バッファへのポインタ。
* @param size 書き込みバイトサイズ。
* @param extobj ユーザが指定した拡張オブジェクト。
*/
int func_fwrite(const void *buf, const unsigned int size, void *extobj)
{
FILE *fp = (FILE *)extobj;
return fwrite(buf, size, 1, fp);
}
/**
* @brief ピクセル値を書き込む。
* @details
* インターフェースでは、何にどのように書き込むかについて一切感知していない。
* この関数では、何にどのように書き込むかについて解決する。
*
* @param x X座標。
* @param y Y座標。
* @param r 赤。
* @param g 緑。
* @param b 青。
* @param extobj ユーザが指定した拡張オブジェクト。
*/
void func_pixel_write(const int x, const int y, const uint8_t r, const uint8_t g, const uint8_t b, void *extobj)
{
canvas_t *canvas = (canvas_t *)extobj;
bmpcol_t *buffer = canvas->buffer + (canvas->w * y) + x;
buffer->r = r;
buffer->g = g;
buffer->b = b;
}
/**
* @brief ピクセル値を読み込む。
* @details
* インターフェースでは、何からどのように読み込むかについて一切感知していない。
* この関数では、何からどのように読み込むかについて解決する。
*
* @param x X座標。
* @param y Y座標。
* @param r 赤。
* @param g 緑。
* @param b 青。
* @param extobj ユーザが指定した拡張オブジェクト。
*/
void func_pixel_read(const int x, const int y, uint8_t *r, uint8_t *g, uint8_t *b, void *extobj)
{
canvas_t *canvas = (canvas_t *)extobj;
bmpcol_t *buffer = canvas->buffer + (canvas->w * y) + x;
*r = buffer->r;
*g = buffer->g;
*b = buffer->b;
}
int main(int argc, char **argv)
{
const int imgw = 1280;
const int imgh = 720;
canvas_t canvas;
bmpimg_t bmpimg;
FILE *fp;
/*
* 画像ピクセルを格納する領域を確保する。
*/
canvas.w = imgw;
canvas.h = imgh;
canvas.buffer = (bmpcol_t *)malloc(sizeof(bmpcol_t) * imgw * imgh);
/*
* 開始処理。
*
* ピクセル入出力関数を渡して初期化する。
* ユーザが指定可能な拡張オブジェクトに、独自に規定したキャンバスを渡しておく。
*/
bmpimg_open(&bmpimg, imgw, imgh, func_pixel_write, &canvas, func_pixel_read, &canvas);
/*
* サンプルの実装では、ファイルから読み込む。
*/
fp = fopen("input.bmp", "rb");
if (fp != NULL) {
bmpimg_bmp_read(&bmpimg, func_fread, fp);
fclose(fp);
}
{
/*
* パターン1を書き込む。
*/
pattern_sample1(&bmpimg);
/*
* サンプルの実装では、ファイルに書き込む。
*/
fp = fopen("sample1.bmp", "wb");
if (fp != NULL) {
bmpimg_bmp_write(&bmpimg, func_fwrite, fp);
fclose(fp);
}
}
{
/*
* パターン2を書き込む。
*/
pattern_sample2(&bmpimg);
/*
* サンプルの実装では、ファイルに書き込む。
*/
fp = fopen("sample2.bmp", "wb");
if (fp != NULL) {
bmpimg_bmp_write(&bmpimg, func_fwrite, fp);
fclose(fp);
}
}
/*
* 終了処理。
*/
bmpimg_close(&bmpimg);
/*
* 画像ピクセルを格納する領域を破棄する。
*/
free(canvas.buffer);
return 0;
}
2012年1月29日日曜日
Blackfin ADSP-BF592搭載基板ACB-BF592の下ごしらえ
先日入手したBlackfin ADSP-BF592搭載基板ACB-BF592の下ごしらえです。
この基板は動かすためのコンポーネントが全て入ったいわゆる「全部入り」なので、ピンヘッダをくっつけておけば準備完了です。
ということでピンヘッダを準備します。
デバッガを接続する箇所にはキーに合わせてピンを1ピン抜いておきます。
実装するとこんな具合。
この基板は動かすためのコンポーネントが全て入ったいわゆる「全部入り」なので、ピンヘッダをくっつけておけば準備完了です。
ということでピンヘッダを準備します。
デバッガを接続する箇所にはキーに合わせてピンを1ピン抜いておきます。
実装するとこんな具合。
秋月電子通商さんのAE-FT2232と並べてみましょう。
このサイズでDSPが楽しめるなんて幸せです。
こんな感じで準備ができました。
DSP空挺団さんのページでは、ブレッドボードを使って使用する様子が書かれています。
うーん。
私も早くやってみたい!
ラベル:
ACB-BF592,
ADSP-BF592
2012年1月22日日曜日
これは新機軸!金子システム株式会社さんのDSP基板が登場(予定)!
このブログを読んでいらっしゃる方の中にも「DSP触ってみたいなぁ」という方が結構いるのではないでしょうか?そんな私も例に漏れず以前から「Blackfinを触ってみたい触ってみたい」と念仏のように唱えていました。
色んなきっかけによって徐々に環境を整えつつあるのですが、先日「私好みのキュッと引き締まったDSP基板」を入手する事ができましたので御紹介します。
私がすっかり気に入ってしまったDSP基板の写真はこちら。
この基板は金子システム株式会社さんの設計によるもの。
基板フェチの私は美しく引かれたパターンとティアドロップにうっとりです。
プロの仕事はやはり違います。
コンパクトな基板の上にBlackfinとクロックとSPIフラッシュとJTAG用ヘッダとブートモード選択用DIPスイッチと1.2V降圧レギュレータが搭載されています。要するにこの基板に+3.3Vを供給するだけで、直ぐにでもDSPを楽しむ事ができるのです。
搭載されているDSPはAnalog Devices社のADSP-BF592 Blackfinです。
なるほど~って、全然まだ意味がわかっていません。
なんだこのブロック・ダイアグラムにある「BLACKfin」ってのは!と突っ込みたくなります。
これがDSPの心臓部分か。
そんな感じでヨチヨチ状態ですが、NXPセミコンダクターズのLPCシリーズと合わせて、今年はこちらも楽しみたい!それにはまずデバッガの入手かな?
ちなみに、このDSP基板は今後販売予定があるそうです。
興味のある方は金子システム株式会社さんまで問い合わせてみて下さい。
「CuBeatSystemsのウェブを見た」と言うと・・・おそらく特典は無いと思います。
色んなきっかけによって徐々に環境を整えつつあるのですが、先日「私好みのキュッと引き締まったDSP基板」を入手する事ができましたので御紹介します。
私がすっかり気に入ってしまったDSP基板の写真はこちら。
この基板は金子システム株式会社さんの設計によるもの。
基板フェチの私は美しく引かれたパターンとティアドロップにうっとりです。
プロの仕事はやはり違います。
コンパクトな基板の上にBlackfinとクロックとSPIフラッシュとJTAG用ヘッダとブートモード選択用DIPスイッチと1.2V降圧レギュレータが搭載されています。要するにこの基板に+3.3Vを供給するだけで、直ぐにでもDSPを楽しむ事ができるのです。
搭載されているDSPはAnalog Devices社のADSP-BF592 Blackfinです。
なるほど~って、全然まだ意味がわかっていません。
なんだこのブロック・ダイアグラムにある「BLACKfin」ってのは!と突っ込みたくなります。
これがDSPの心臓部分か。
そんな感じでヨチヨチ状態ですが、NXPセミコンダクターズのLPCシリーズと合わせて、今年はこちらも楽しみたい!それにはまずデバッガの入手かな?
ちなみに、このDSP基板は今後販売予定があるそうです。
興味のある方は金子システム株式会社さんまで問い合わせてみて下さい。
「CuBeatSystemsのウェブを見た」と言うと・・・おそらく特典は無いと思います。
ラベル:
ACB-BF592,
ADSP-BF592,
Blackfin
プチ報告「小規模組み込みシステムプラットフォーム研究部会」
遅ればせながら、先週行なった「小規模組み込みシステムプラットフォーム研究部会」~近代文学施設でモクモクと組み込みシステムプラットフォームを研究しよう~のプチ報告です。
場所は神奈川県横浜市の港の見える丘公園の中にある神奈川近代文学館でした。
プチ参加賞は夏目漱石が自筆で作った原稿用紙の復刻版・・・を1枚。
これは施設で販売されていたので面白くて購入しました。
朝9時30分から18時00分までの開催。
途中、公園内の散策を楽しむ余裕もありました。
和室の風景はこちら。
温泉旅館に宿泊しているかの雰囲気が個人的に好みです。
相変わらずの絵で皆様をお出迎えしました。
夜は当然ながら親睦を深める会です。
と言っても、コアなメンバーが集うので結局のところ熱く組み込み談義が繰り広げられました。
楽しかった!
前回の「LPCXpresso横浜お楽しみ部会」では、参加者の開発の様子も御紹介しました。
本当は今回も御紹介しようと思ったのですが、今後は参加された方が見れるお互いの様子という形にしようと思います。
その方が、公開したくない人も参加しやすいかな?と思いました。
継続的に開催する方向で検討中ですので、皆様も機会があえば是非!
2012年1月9日月曜日
Red Probe+を購入しました
本日やっとこさの開梱です。
本体には20ピンのJTAGケーブルが接続されています。
付属品やUSBケーブルとウェブへのリンクを示すシートです。
こちらは一緒に購入した10ピン-20ピン JTAGアダプタです。
SWDをインターフェースとして使用する時に使います。
実は、Red Probe+内部にもSWD用ピンヘッダがあります。
上記のアダプタの10ピンケーブルを、内部SWD用ピンヘッダに直接接続して使用する事も可能。
その場合、20ピンJTAGケーブルは取り外しておいた方が良いでしょう。
それらの情報はcode_red社のウェブから見る事ができます。
http://www.code-red-tech.com/red-probe-plus.php
http://support.code-red-tech.com/CodeRedWiki/HardwareDebugConnections
私はBlackTank(20ピン JTAGが出ている)でもこいつを使用する予定なのでアダプタを使用します。
ちょっとケーブル部分が長いです。
かかった費用はこちら。
なかなか大きな出費です。
気になる使用感やLPC-LINKとの違いなどは後日レポートしたいと思います。
有償のIDEであるRed Suiteを使った時に最大限メリットが出るような製品設計です。
LPCXpressoと組み合わせた場合、どの程度メリットを享受する事ができるのでしょうか。
その点を知りたいがためだけに購入したという人柱的企画です。
LPC-LINKと同じだったらどうしよう・・・。
FusionPCBの基板発注から到着までの日数(DHL発送選択時)
工程を振り返ってみる
昨年末の12/29にFusionPCBへ発注した基板が手元に届きました。
今回はできるだけ早く基板が欲しかったのでDHLによる発送を指定。
ちょっと工程を振り返ってみましょう。
12/29:発注
12/30:オーダー受付 (1日目)
12/31:土曜
01/01:日曜
01/02:月曜 (2日目)
01/03:火曜 (3日目)
01/04:発送 (4日目)
01/05:運送
01/06:運送
01/07:到着
発注してから4営業日で発送された事になります。
なかなか早いですね。
中国は旧正月がお休みです。
ということで日本のお正月とは関係なくお仕事をされているわけです。
年末からお正月にかけての発注は、仕事量も減っていて例年素早く対応可能なのかもしれません。
お正月にこたつでぬくぬくしながら研究したい場合、この時期の発注は検討の価値がありそうです。
費用
ちなみに費用は5cmx10cm MaxのコースでDHL送料込みで5千円前後でした。
気長に基板の到着を待つ事を前提にDHLを選択しなければ半分くらいの費用で済みます。
おまけ
一緒に入っていたシートがこれ。
一昔前、「中国」と「イノベーション」が一緒になるなんて誰も考えもしませんでした。
「Innovate with China」がなんとなく真実味を帯びてきているような気もします。
がんばれニッポン!
(と言いながら中国の企業に基板を発注しているので、何とも言えません。)
昨年末の12/29にFusionPCBへ発注した基板が手元に届きました。
今回はできるだけ早く基板が欲しかったのでDHLによる発送を指定。
ちょっと工程を振り返ってみましょう。
12/29:発注
12/30:オーダー受付 (1日目)
12/31:土曜
01/01:日曜
01/02:月曜 (2日目)
01/03:火曜 (3日目)
01/04:発送 (4日目)
01/05:運送
01/06:運送
01/07:到着
発注してから4営業日で発送された事になります。
なかなか早いですね。
中国は旧正月がお休みです。
ということで日本のお正月とは関係なくお仕事をされているわけです。
年末からお正月にかけての発注は、仕事量も減っていて例年素早く対応可能なのかもしれません。
お正月にこたつでぬくぬくしながら研究したい場合、この時期の発注は検討の価値がありそうです。
費用
ちなみに費用は5cmx10cm MaxのコースでDHL送料込みで5千円前後でした。
気長に基板の到着を待つ事を前提にDHLを選択しなければ半分くらいの費用で済みます。
おまけ
一緒に入っていたシートがこれ。
一昔前、「中国」と「イノベーション」が一緒になるなんて誰も考えもしませんでした。
「Innovate with China」がなんとなく真実味を帯びてきているような気もします。
がんばれニッポン!
(と言いながら中国の企業に基板を発注しているので、何とも言えません。)
登録:
コメント (Atom)