Top > WINDOWS > 非同期シリアル通信クラス >内部プログラム

内部プログラム

以下に非同期シリアル通信の概要を示します。 メインプログラムとは別にシリアル通信用にスレッドを用意して、 内部で通信イベントを待たせます。イベントの通知は、 Windowsが提供するイベントオブジェクトを介して行われます。 イベントはWaitForMultipleObjects()関数で待ち合わせができます。 具体的には、必要な数イベントハンドルを用意して、 WaitForMultipleObjects関数を呼び出すことで待ち状態に入ります。 いずれかのイベントハンドルがセットされると、WaitForMultipleObjects関数は、 セットされたイベントハンドルの番号を返して、待ち状態から戻ってきます。 イベントハンドルは手動でセットすることもできますが、 送信完了時や、データ受信時にOSにセットしてもらうこともできます。

受信データは、あらかじめシリアル通信クラスに呼び出される関数を登録しておき、 イベント発生時に登録した関数に引数としてわたします。 メインスレッドとの同期は、TThreadに用意されているSynchronizeメソッドを介して 関数を呼び出すことによって行われます。

ポートを開く

Windows上でシリアルポートを扱う場合、 ファイルと同様にCreateFile()でポートを開いて、WriteFile()、ReadFile()関数で読み書きを行います。 また、CreateFile()関数にFILE_FLAG_OVERLAPPEDを指定することで非同期通信を実現することができます。 CreateFileで開くパスはポート番号が9以下ならCOMn、10以上なら\\.\COMnとなります。

	//パスの作成
	stringstream path;
	if ( FChPort>9 ) {
		path << "\\\\.\\COM" << FChPort;
	}
	else {
		path << "COM" << FChPort;
	}


	//ポートのオープン
	hCom = CreateFile(
		path.str().c_str(),		// ファイル名
		GENERIC_READ | GENERIC_WRITE,	// アクセスモード
		0,				// 共有モード
		NULL,			// セキュリティ記述子
		OPEN_EXISTING,		// 作成方法
		FILE_FLAG_OVERLAPPED,	// ファイル属性
		NULL			// テンプレートファイルのハンドル
	);
	if ( hCom==INVALID_HANDLE_VALUE ) return false;

通信イベントを受ける

CreateFile関数でポートを開くときにFILE_FLAG_OVERLAPPEDを指定しているので、 ReadFile関数とWriteFile関数によるデータの読み書きは自動的に非同期で実行されます。 今回は、データ受信時と送信バッファ空の時にOSにイベントハンドラをセットしてもらいたいので、 OVERLAPPED構造体というものにイベントハンドラを格納して、 WaitCommEvent()関数でデータ送受信イベントにイベントハンドラを登録します。 WaitCommEvent関数は通信デバイス用に用意されたイベントを監視する関数で、 あらかじめSetCommMask()関数で指定されているイベントを監視します。

ハンドルの初期化と定数

WaitForMultipleObjects関数はイベント検出時、戻り値としてイベントを識別する番号を返します。 n番目のイベントハンドルの識別番号は、WAIT_OBJECT_0 + nとなります。 イベントハンドルに関しては、CreateEvent()関数でハンドルを作成した後、 OVERLAPPED構造体にハンドルを格納して、WaitCommEvent関数でイベント監視を開始します。 また、SetCommMask関数にて、送信バッファ空イベント(EV_TXEMPTY)、 受信イベント(EV_RXCHAR)およびその他イベントを有効にしています。
//定数の定義
#define WAIT_COMM		WAIT_OBJECT_0
#define WAIT_ESC		(WAIT_OBJECT_0+1)

const int COMM_EVENT = 0;
const int ESC_FUNC = 1;
	//イベントオブジェクトの生成
	hEventObjects[COMM_EVENT] = CreateEvent(NULL, TRUE, FALSE, NULL);
	hEventObjects[ESC_FUNC] = CreateEvent(NULL, TRUE, FALSE, NULL);
	
	
	//OVERLAPPED構造体の初期化
	//OVERLAPPED fOverLapped;
	ZeroMemory(&fOverLapped, sizeof(OVERLAPPED));
	fOverLapped.Offset = 0;
	fOverLapped.OffsetHigh = 0;
	fOverLapped.hEvent = hEventObject[COMM_EVENT];
	
	
	//イベントマスクを設定して通信イベントを待ちます。
	SetCommMask(hCom, EV_TXEMPTY | EV_RXCHAR | fEventMask);
	WaitCommEvent(hCom, &fEvent, &fOverLapped);

イベントスレッド

登録したイベントハンドルをWaitMultipleObjects関数を使って、 500msのタイムアウト付きで監視しつづけます。 イベント発生時は、関数の戻り値で発生元を判定して処理を行います。 イベントハンドルは、ResetEvent()関数を使用して手動でリセットします。
//えくすきゅーと
void __fastcall TCommPort::Execute()
{
	DWORD Result;

	// TODO : スレッドとして実行したいコードを以下に記述 */
	while ( !Terminated ) {

		//ここでイベント待ちブロック
		Result = WaitForMultipleObjects(2, hEventObjects, FALSE, 500);


		//イベント分岐
		switch ( Result ) {

			//通信イベント
			case WAIT_COMM:
			Synchronize((TThreadMethod)&comm_event);
			break;

			//RTSリクエスト
			case WAIT_ESC:
			Synchronize((TThreadMethod)&esc_func);
			break;
		}
	}
}
//---------------------------------------------------------------------------