はじめに
このページでは、「マイコンなどでC言語を使っていたがWindowsプログラムは初めて」という方向けに、Windowsの開発の様子を簡単に説明しつつ、Remoでの画像出しを行ってみます。
※このページで紹介する内容はあくまでも一例です。個別の作成のご相談ご質問はお答えできませんのでご了承下さい。
※以下の情報は2009年11月現在のものです。ご注意ください。
Remoについてはこちらを参照願います。
Windowsのプログラムついて
マイコンなどでも開発環境がありますが、PCの場合もプログラムを作るために開発環境が必要です。開発環境は条件付きでMicrosoftから無償でダウンロードできます。
Microsoft製のWindows開発環境は「Visual Studio」という名前です。これは複数の人間が、複数の言語を使って開発できるという比較的大きな企業向けのものです。この中で、言語を固定したものであれば無償で使うことができます。
現在、言語にはVisual Basic、VisualC++、VisualC#があります。VisualBasicは古いバージョンは比較的ビギナーにもわかりやすいという話でした(私はそうは思わなかった)が最近のバージョンは他の言語とあまりかわらないと思います。C++は昔からある言語で、非常に細かいことまでできますので高度なプログラムの場合にはC++でないと出来ないということがあるようです。C#は最近できた言語で、プログラムをするときの文字数が少なくて済む感じです。このページではC#を使います。
C#の無償版は「Visual C# Expressedition」になります。
Visual C#2008 Express Editionをダウンロードする
Visual??2008ExpressEditionのダウンロードページ
2009年12月3日現在で、ExpressEditionのダウンロードページはこのようになっています。C#2008の「Webインストール(ダウンロード)」というところを押すとダウンロードが開始されます。ダウンロード及びインストールにはかなり時間がかかりますので、注意してください。インストール時には同時に.NetFreamWork3.5というソフトもインストールされます(これが無いとVSが動かない)。
※このページはMicrosoftの都合で変わったりしますので、適時、ホームページを探して入手してください。現時点ですでにVS2010が発表されています。
C#の勉強方法、MSDNの活用
プログラム未経験者がプログラムを覚えようとする場合、本やインターネットを見るかと思いますが、どうしても人に聞きたくなる場合があります。このような場合のために、MicrosoftではMSDNというプログラマー向けのページがあります。ここのフォーラムで質問をすると、教えてもらえます。
書籍で勉強する場合ですが、難易度が違う複数の本で勉強することをお勧めします。私の場合、初心者向け1冊、中級向け4冊、逆引き大全1冊で勉強しました。簡単なことならすぐにできるようになりますが、実用的なものを作ろうとするとそれなりに勉強に時間がかかりますので余裕をもって取り組んでください。
VC#の起動
ここまでで、Visual C# Expressedition(以下VC#と略す)のインストールが終わっているとします。
Windowsで「スタート」−>「全てのプログラム」−>「Microsoft Visual C# Express Edition」で、VC#を起動します。
すると、このようなウィンドウが開きます。これが初期画面になります。尚、当ページで説明する画面は有償版のVisualStudioのものです。(記事を書いているPCに有償版が入ってたので。)中身は同じですが、画面の色などがちょっと違うかもしれません。
新規プロジェクトの作成
1つのアプリケーションは複数のファイルからなります。自分で書くファイルもあれば、開発環境が勝手に作るものもあります。それらのファイルを管理するのがプロジェクトです。
「ファイル」−>「新規作成」−>「プロジェクト」
新しいプロジェクトウィンドウが開きます。Windowsプログラムは全部書こうとすると、Windowを開くだけでもものすごい量を書かなくてはならないので、テンプレートを使います。上のテンプレートの中を見てください。
Windowsでウィンドウが開くようなプログラムは「Windowsフォームアプリケーション」になります。「フォーム」が一般的に言われているウィンドウにあたります。プログラム的にはフォームと言いましょう。
そのほかに、DOS窓で実行される「コンソールアプリケーション」や、クラスを作るだけの「クラスライブラリ」などがあります。一旦作ったアプリを流用したい場合はテンプレートとして登録すると、「マイテンプレート」のところに表示されるようになります。
自分でプログラムを書くようになってくると、MSDNのフォーラムとかで質問するケースが出てきますが、質問するときにかならず何のアプリを作っているかも記載した方がいいです。質問すると「その前に、それはフォーム?コンソール?Webアプリケーション?」とツッこまれます。
プロジェクト名を適当な名前に変え、参照ボタンを押してワークフォルダを指定してから「OK」ボタンを押すと、フォームを作る画面になります。ここでは「RemoImage」という名称にしました。
ちなみに、Windowsプログラムでは、変数名や関数名をあまり略さないのがよいとされています。たとえば、画面幅を表す変数に名前をつけるときに「pw」とかにしないで、「pictureWidth」とか意味がわかるようにするほうが良いです。長くなってキーを押す回数が増える心配をするかもしれませんが、C#を含むVisualStusioではインテリセンスという機能があり、先頭の数文字をキーするだけで候補がずらっと出てきますので全部打たなくてすみます。かつ意味がわかりやすいので間違いが減るという寸法です。ということで、マイコンのプログラム開発環境よりも、非常に長いプログラムを書くのが楽です。
フォームにコンポーネントを貼り付ける
Windowsフォームアプリケーションのプロジェクトを作ると、初期画面は下のようになります。
画面の説明をします。真ん中が作業領域です。テンプレートで作られたフォームのイメージが出ているかと思います。画面少し上のタブを見ると「Form1.cs」となっており、これがC#のフォームアプリのファイルになります。
画面右が上下に2分割されていると思います(設定によってはこれが出ない場合もある)。上が「ソリューションエクスプローラ」となっています。ソリューション=プロジェクトのような感じです。エクスプローラという名前の通り、必要なファイル管理をします。
右下がプロパティです。現在ハイライトしているコンポーネントのプロパティの表示や設定をします。(順を追って詳細を説明します。)
下にエラー一覧が表示されます。
コンポーネントやらプロパティやら耳慣れない用語が出てきましたが、順を追って説明しますのでお待ち下さい。
コンポーネントは、文字通りプログラムの部品です。たとえばボタンを構成する「Buttonコンポーネント」や、シリアル通信をする「SerialPortコンポーネント」などがあります。画面左のへりの部分に「ツールボックス」というのがあり、ここからコンポーネントを拾って貼り付けることができます。では実際に、
ツールボックスを開いて見ます。貼り付けはドラック&ドロップです。とりあえずButtonコンポーネントを貼り付けてみます。
button1という名称のボタンが配置されました。画面右下のプロパティを見てください。今、貼り付けたばかりなのでこのボタンがハイライトされ、そのボタンのプロパティが表示されています。プロパティとはそのコンポーネントの設定値です。Textという項目がボタン上に表示されるテキストです。試しに、プロパティのところで違う名前に変えると、その表示もかわります。
このようにして、フォームをつくっていきます。
各プロパティの意味ですが、ボタンだけでもたくさんのプロパティがあり、ビギナーはなにがなにやらわからないと思います。ヘルプを見て勉強するという手もありますが、PCプログラマ以外の方でしたらヘルプも意味不明だと思います。はじめのうちは「C#逆引き大全」というコンポーネントの辞書のような本がありますのでそれを使って徐々に覚えてください。慣れてくるとヘルプでだいぶわかるようになります。
おなじようにして、以下のコンポーネントを貼り付けて、プロパティを変更して置いてください。
<Buttonコンポーネント>
Text:画像取得
<PictureBoxコンポーネント>
Size:160,56
<SerialPortコンポーネント>
※まだなにも変更しない
SerialPortが見つからない方がいたら、ツールボックス内のコンポーネントのところを開くと中に入っていると思います。
SerialPortコンポーネントは、フォームの上にドロップすると下の方に表示されます。このように実際にはフォーム内に表示されないコンポーネントはフォーム欄外の下に表示されます。
コーディング
現状では画面イメージしかでていないので、どうやってプログラムを書くのだろうと疑問に思うでしょう。 フォームの適当なところで右クリックをするとポップアップが出て、その中に「コードの表示」というのがあるのでそれを選択するとコードが表示されます。左クリックではなく右クリックなので注意して下さい。
表示されたコードがこちらです。
タブが増えて、「Form1.cs」というタブになっています。先ほどのイメージは「Form1.cs[デザイン]」というタブですので、ボタンを追加するなどの場合はそちらで行います。
usingxxというところがマイコンの環境で言うINCLUDEのようなものです。namespaceはその中の変数名などの名前空間が同じになることを示します。
C#を含む、VisualStudioの言語はオブジェクト指向なので、全部クラスで表します。オブジェクト指向やクラスについては専門書を参照願います。
マイコンのC言語風に解釈すると
<オブジェクト指向>
過去に作ったソースを活用テクニックや考え方で、PCのように大量のコードを書くときにこの指向で書いてると非常に生産性がよいもの。但し、人によっては理解が難しい。オブジェクト指向言語(C#)を使ってオブジェクト指向で書かなくても(マイコンCのように書いても)プログラムは動くので、とりあえずマイコンC系の方は深入りしなくてもよいかも。
<クラス>
関数と変数と構造体が合体したようなもの。
という感じです。(正確な表現でないが、マイコンC系の方には理解してもらえるかも)
ということで、行12〜18を見ると class 型の From1 がこのフォームになります。
コンストラクタ
Cと大きく違うもう一つの点があります。コードはクラスの構造を記述し、その構造を実体化させないと使えません。クラスの挙動を記述しただけでは使えず、そのクラスを実体化した後に使えるようになります。上の例だと、行12〜18でForm1の挙動を記述してますが、その中で行14で自分自身を呼び出して実体化してます。この、クラスが自分自身を実体化するところを「コンストラクタ」と呼びます。コンストラクタがフォームを起動したときに実行される関数だと考えて下さい。
Windowではフォームを出すだけでも本当は沢山のプログラムを書かなくてはならないのですが、上を見るとコンストラクタ内にInitializeComponentというのがあります。実はここで必要なことが実行されています。
さきほどはForm1のデザイン画面で各プロパティを変えましたが、ここではコンストラクタ内でプロパティを変えてみましょう。一番初めにコンストラクタが動くので、起動直後すぐに設定されることになります。
serialPort1をまったく設定していませんが、今確定しているのは、Remoの通信速度が460800bps固定なのでこれを設定してみます。
コンストラクタ内で、InitializaComponent()が終わった後にserialPort1と書こうとしますと、seだけ入れた時点ですでにSerialPort1が候補に挙がってきます。この状態でEnterキーを押すと勝手にその名称を入れてくれます。便利。
その後、ドットを入れると、SerialPortの各要素の候補が出てきます。通信速度のプロパティはBaudRateですのでそれを選択(もしくは手打ち)します。
プロパティを変えるときは=で代入します。行の終わりはCと同じように;で終わります。これで起動直後にserialPort1の通信設定がされるようになります。同じようにCOMポート番号も設定してみましょう。
メソッド
Cの関数にあたるものを”メソッド”と言います。書き方や使い方はCの関数とほぼ同じです。
SerialPortコンポーネントを含むほとんどのコンポーネントにはあらかじめメソッドが入っています。上で設定したCOMポートを有効にするにはOpen()メソッドを使います。とりあえず、フォームを開いたときにCOMもオープンするようにしてみます。よって、コンストラクタ内に記述してみます。
実際にはエラー処理などを入れないといけないのですが、とりあえず簡単に書きました。RemoMortionEditorの方はエラー処理など、まあまあ入れてますので興味があればそちらも参照願います。
COM47は私のパソコンがRemoとペアリングしたときに割り当てられた番号なので、各自値は違うと思います。
デバック実行
とりあえずここまでを実行してみましょう。実行中にエラーが出た場合はコード編集に戻るモードの「デバック実行」モードで実行してみます。「F5」キーを押してください。
無事、COM47がオープンされれば、フォームが表示されると思います。このとき、RemoのLED_Tが点灯してCOMが開いて通信可能状態になった(接続された)のがわかります。フォームを閉じると接続が解除され、LED_Tが消えます。
では、ここでわざとエラーを出してみましょう。ありえないCOM番号を設定してF5キーでデバック実行してみます。
このようにコード編集画面で、間違っているところが指摘され、実行が停止します。
ビデオの停止ボタンのようなボタンを押すと、実行が中断されます。
イベント
マイコンのプログラムはmain関数内が順番に実行されます。Windowの場合はすでにWindows上で沢山のプログラムが走っていますので、何かを動かすきっかけは、”イベント”という単位で行います。
本ページではRemoの画面を表示させるのが目的ですが、画面データを取得するにはまずWindows側から命令を送信する必要があります。よってそのきっかけのイベントが必要です。
今、Form1にはボタンが配置されていますので”ボタンを押した”というイベントを使ってみます。
今度はデザインのタブにもどってみます。ボタンをクリックしてボタンがハイライトされている状態にするとプロパティのところにボタンの情報が出ます。
プロパティのところでイナズママークのようなものがあります。ここを押すとイベント一覧が表示されます。よく使われるイベントがすでにハイライトされていますが、ボタンの場合は「Click」イベントがハイライトされています。
ここで、Clickの右のセルをダブルクリックします。
すると、いきなりコード画面になり、ボタンを押したイベントが自動的に記述されます。この中にプログラムを書くことができます。
usingの追加
usingはINCLUDEのような感じでライブラリの追加をします。画面を出す場合にはフォームアプリケーションのテンプレートで勝手に追加されただけでは足りない機能も使うので、ライブラリを追加します。
ハイライトした1行を追加してください。
どのクラスやメソッドが、どのライブラリを必要とするか?というのははじめのうちはよくわからないと思いますが、ヘルプで調べるとなにが必要か書いてあります。
マイコンでタイマなどでウェイトをかけたりしますが、似たようなものにスレッドを停止させるというメソッドがあり、それを使うためにこれが必要です。
シリアルポートでのデータ送信
ボタンを押したイベントに対して、データをRemoへ送信しますので、ボタンイベントの中に送信命令を書きます。
行26:送信データは配列に格納してから送信します。配列の宣言の仕方はCとちょっと違うので注意。Cだと変数を宣言しただけですぐに使えますが、C#(VS)の場合は宣言後に実体化させます。
= new byte[4] というところで、宣言した配列を4バイトの長さで実体化しています。
行27〜30:送信データをセットしています。Cと同じ。命令の内容についてはRemoのページを参照して下さい。
行31:DiscardInBufferメソッドを実行すると、COMポートの受信バッファがクリアされます。ここ、必須ではないですが念のため入れてます。
行32:同じく送信バッファがクリアされます。
行33:Writeメソッドでデータを送信します。第一引数に配列名、次に配列の先頭、最後に長さを渡します。ここで注意点。マイコンのUARTはデータをセットするとほとんど即座に送信されますが、Windowsの場合は他のプログラムも常に走っているのですぐには送信されません。私の環境での実測値ですが、大体10〜30mS間があきます。
行34:画面を表示させる関数(メソッド)を作りました。次で説明します。
画像を表示するメソッド
とりあえず、全体を表示します。
流れは
1)画像の大きさを決め、ビットマップを用意
2)画像が送られてくるまで待つ
3)画像を受け取って配列に格納
4)YUVデータをRGBに変換してビットマップに展開
5)ピクチャーボックスに画像を渡して表示
となっています。順を追って詳細を説明します。
regionディレクティブ
プログラムの説明の前に、一番初めの行について説明します。プログラムではなく、コンパイラになにかを指示するものをディレクティブと言います。regionはソースコードをミニマイズするだけのもので、プログラムを単に見やすくするために使われ、プログラム的には意味はありません。が、かなり重宝します。
マイコンでCだと、プログラムが長くなってくるとファイル分割をしますが、これでもファイルが増えてくるとわけがわからなくなってきます。regionで隠す方法だと、1画面にものすごい長い量のコードを書いてもどんどん折りたためるので非常に便利です。
この行番号のところの−を押すと
このように折りたたまれます。行番号が38から87に飛んでいるのがわかるでしょう。ネストもできるので全体構造の把握とかもしやすいです。
ビットマップデータとSetPixcelメソッド
さて、ここからが本題です。
先にPictureBoxというコンポーネントを貼り付けました。これは画像を表示する領域を確保するコンポーネントで、画像そのものではありません。画像そのものはBitmapクラスというのがあり、これに画像を展開してPictureBoxに渡す、というやり方でモニタに表示します。
ここではビットマップを宣言して、Remoの画像サイズで実体化しています。このビットマップに受け取った画像データを展開します。
変数のサイズですが、intが32ビットになります。Byteは8ビットです。符号なしは先頭にuがつきますのでintの場合はuintになります。unsigned
のuですね。
スレッドスリープ
Windows上で沢山動いているプログラムはスレッドという単位で管理されています。詳しくは説明しませんが、今作っているプログラムはForm1のスレッド1本が走るものです。このスレッドを停止させるのがSleepメソッドです。これを使うには冒頭にあらかじめ
using System.Threading;
のように宣言しておく必要があります。
単位はmSになり、上記だと1秒停止することになります。尚、Form1が1秒停止するだけで、Windows上の他のプログラムは停止しません。Remoの画像取得命令は、私の環境だと実測で750mSだったので1秒あれば十分に返信が終わる時間です。
データ受信
返信されたデータは一旦、シリアルポートのバッファに格納されます。今何バイトバッファに入っているかはByteToReadプロパティで確認できます。
送られてくるデータは17942バイトなのがあらかじめ分かっています(Remoのページ参照)が、無線通信なので、途中でデータが途切れたり、またはタイミングがずれて予定よりも沢山受信する、という可能性もあります。
行49−52:データが多すぎたら、予定データ量(17942バイト)を受信します。
行53−56:データが少なすぎたら、来ている分だけ受信します。
YUV−>RGB変換、ビットマップ展開
PCのモニタはRGB値で画面を表示しています。ビットマップもRGBをセットするようになっています。
ビットマップに画像データを展開する簡単な方法としてSetPixcelメソッドがあります。座標とRGB値を1ピクセルづつセットします。行71と行79のところです。色はColorというクラスがあるのでそれを使います。
その他の行はYUVをRGBに変換しているところです。YUVについてはRemoのページを参照願います。
表示
ここまでで画像はbmpという名称のビットマップに展開されています。ピクチャーボックスのImageプロパティにビットマップを渡すとピクチャーボックスに表示されます。渡された画像はいつ更新されるかわからないので一応Refreshメソッドで表示を更新してます、が、静止画を取るぐらいならこの行は不要かも。
その他、YUV−>RGB変換中に小数点演算結果を8ビットに直したりしますので、結果が8ビットにおさまらなかった場合に値を修正するメソッドがこちら。
実行結果
では早速実行してみましょう。F5キーを押します。
ん、下の方が表示されません。YUV値の場合、データが全部0だとこのように緑色一色になります。
少し考えたら、どうやらシリアルポートのバッファ不足だったようです。
このように通信系のプログラムだと、単純にプログラムしたようにはなかなか動かないのでいろいろな原因を推測する力が必要です。
受信バッファの調整
SerialPortコンポーネントの設定はコンストラクタ内で行ってましたので、そこにバッファサイズを設定する行を追加します。
バッファがちょっと多いような気もしますが、PCの場合はメモリが豊富なのと、通信がいつ送られるかわからないので、普通はバッファを非常に多目にとるのが普通です。
ではF5で再度実行してみます。
ということで全データが取れました。
使えるプログラムにするには
このままではエラー処理など入れてないので実用的では有りません。また、画像表示に使っているSetPixcelメソッドは動作が非常に遅いことで有名です。これらについては書籍やMSDNで勉強してレベルアップを図ってください。尚、サンプルソフトのRemoMortionEditorではこれらの対策が(完全ではないが)されています。そちらを参照するのもよいかもしれません。
2009年12月4日
|