Windows環境でのシリアル通信

シリアル通信を使うことで手軽にパソコンとマイコン間で通信が行えます.
パソコン側の環境ではTeraTermなどのターミナルソフトウェアを利用するのも手軽で良いですが,
通信データがテキストデータとなってしまうため,処理速度やメモリー節約の観点からは不利です.
そのような時は専用のモニタリングソフトウェアなどの開発も検討するべきでしょう.
ということで,WindowsRS232C通信ソフトウェアを書いてみました.
このソフトウェアは通信にスレッドを用いることによって受信待ちブロッキングを回避しています.

serial.h

#ifndef _SERIAL_H_
#define _SERIAL_H_

typedef struct _TAG_SERIAL* serial_t;

/* 関数プロトタイプ */
serial_t serial_create(char *pname, unsigned int baud);
void serial_delete(serial_t obj);
unsigned int serial_send(serial_t obj, unsigned char *buf, unsigned int size);
unsigned int serial_recv(serial_t obj, unsigned char *buf, unsigned int size);
unsigned int serial_recv_length(serial_t obj);

#endif /* _SERIAL_H_ */

serial.c

#include <windows.h>
#include <stdlib.h>
#include "fifo.h"
#include "serial.h"

/* 1つのシリアル通信に関するデータ構造 */
struct _TAG_SERIAL {
	// 通信関係
	HANDLE handle;
	DCB dcb;
	
	// スレッドに関して
	HANDLE thread_handle;
	DWORD thread_id;
	BOOL thread_active;
	CRITICAL_SECTION cs_send;
	CRITICAL_SECTION cs_recv;
	
	// FIFO
	fifo_t *q_recv;
	fifo_t *q_send;
	
	// その他
	char *pname;
	char *msg;
};

/* プロトタイプ */
DWORD WINAPI serial_thread(LPVOID param);

/* 1次バッファ */
#define SERIAL_TMP_BUFSIZE	128

/* シリアル通信を開始 */
serial_t serial_create(char *pname, unsigned int baud)
{
	serial_t obj;
	COMMTIMEOUTS timeout;
	
	// インスタンスメモリ確保
	obj = (serial_t) malloc(sizeof(struct _TAG_SERIAL));
	if ( obj == NULL ) return NULL;
	ZeroMemory(obj,sizeof(struct _TAG_SERIAL));
	obj->pname = pname;
	
	// COMポートのハンドルを取得
	obj->handle = CreateFile(pname,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL/*|FILE_FLAG_OVERLAPPED*/,NULL);
	if ( obj->handle == INVALID_HANDLE_VALUE ) {
		free(obj);
		return NULL;
	}
	
	// COMポートの通信設定
	GetCommState(obj->handle, &obj->dcb);
	obj->dcb.BaudRate = baud;
	if ( SetCommState(obj->handle, &obj->dcb) == FALSE ) {
		free(obj);
		return NULL;
	}
	
	
	// COMポートのタイムアウト設定
	ZeroMemory(&timeout,sizeof(COMMTIMEOUTS));
	timeout.ReadIntervalTimeout = MAXDWORD;
	if ( SetCommTimeouts(obj->handle, &timeout) == FALSE ) {
		free(obj);
		return NULL;
	}
	
	// FIFOメモリ確保
	obj->q_send = fifo_create();
	obj->q_recv = fifo_create();
	if ( obj->q_send == NULL || obj->q_recv == NULL ) {
		fifo_delete(obj->q_send);
		fifo_delete(obj->q_recv);
		free(obj);
		return NULL;
	}
	
	// スレッド開始
	InitializeCriticalSection(&obj->cs_recv);
	InitializeCriticalSection(&obj->cs_send);
	obj->thread_active = TRUE;
	obj->thread_handle = CreateThread(NULL,0,serial_thread,(LPVOID *)obj,0,&obj->thread_id);
	if ( obj->thread_handle == NULL ) {
                DeleteCriticalSection(&obj->cs_recv);
                DeleteCriticalSection(&obj->cs_send);
		fifo_delete(obj->q_send);
		fifo_delete(obj->q_recv);
		free(obj);
		return NULL;
	}
	
	return obj;
}

/* シリアル通信を停止 */
void serial_delete(serial_t obj)
{
	DWORD thread_state;
	
	// スレッドを停止
	obj->thread_active = FALSE;
	do {
		Sleep(1);
		GetExitCodeThread(obj->thread_handle,&thread_state);
	} while (thread_state == STILL_ACTIVE);
	DeleteCriticalSection(&obj->cs_send);
	DeleteCriticalSection(&obj->cs_recv);
	
	// 通信ポートを閉じる
	CloseHandle(obj->handle);
	
	// メモリー領域の解放
	fifo_delete(obj->q_send);
	fifo_delete(obj->q_recv);
	free(obj);
}

/* シリアル通信スレッド */
DWORD WINAPI serial_thread(LPVOID param)
{
	serial_t obj = (serial_t) param;
	BYTE recv_buf[SERIAL_TMP_BUFSIZE];
	BYTE send_buf[SERIAL_TMP_BUFSIZE];
	DWORD recv_len;
	DWORD send_len,send_size;
	BOOL ret;
	BOOL recv_hold = FALSE;
	BOOL send_hold = FALSE;
	
	while ( obj->thread_active ) {
		// 受信
		if ( recv_hold == FALSE ) {
			ret = ReadFile(obj->handle, recv_buf, sizeof(recv_buf), &recv_len, NULL);
			if ( ret == FALSE ) {
				obj->msg = "ReadFile failed.";
				break;
			}
			if ( recv_len )	recv_hold = TRUE;
		} else if ( TryEnterCriticalSection(&obj->cs_recv) ) {
			recv_len -= fifo_write(obj->q_recv, recv_buf, recv_len);
			LeaveCriticalSection(&obj->cs_recv);
			if ( recv_len != 0 )	obj->msg = "q_recv is fully filled. (>_<)";
			recv_hold = FALSE;
		}
		
		// 送信
		if ( send_hold ) {
			ret = WriteFile(obj->handle, send_buf, send_size, &send_len, NULL);
			if ( ret == FALSE ) {
				obj->msg = "WriteFile failed.";
				break;
			}
			if ( send_size != send_len )	obj->msg = "WriteFile spilled some of q_send.";
			send_hold = FALSE;
		} else if ( TryEnterCriticalSection(&obj->cs_send) ) {
			send_size = fifo_read(obj->q_send, send_buf, sizeof(send_buf));
			LeaveCriticalSection(&obj->cs_send);
			send_hold = TRUE;
		}
		
		Sleep(1);
	}
	
	obj->thread_active = FALSE;
	ExitThread(TRUE);
	return 0;
}

/* 送信する */
unsigned int serial_send(serial_t obj, unsigned char *buf, unsigned int size)
{
	unsigned int ret;
	EnterCriticalSection(&obj->cs_send);
	ret = fifo_write(obj->q_send, buf, size);
	LeaveCriticalSection(&obj->cs_send);
	return ret;
}

/* 受信する */
unsigned int serial_recv(serial_t obj, unsigned char *buf, unsigned int size)
{
	unsigned int ret;
	EnterCriticalSection(&obj->cs_recv);
	ret = fifo_read(obj->q_recv, buf, size);
	LeaveCriticalSection(&obj->cs_recv);
	return ret;
}

/* 受信したバイト数を取得 */
unsigned int serial_recv_length(serial_t obj)
{
	return fifo_length(obj->q_recv);
}

以上が通信部です.
バッファリングのためにFIFOを実現するコードも用います.
fifo.h

#ifndef _FIFO_H_
#define _FIFO_H_

#define FIFO_BUFSIZE	1024

/* FIFOデータ構造 */
typedef struct _TAG_FIFO {
	unsigned char buf[FIFO_BUFSIZE];
	unsigned int size;
	unsigned int read;
	unsigned int write;
}fifo_t;

/* プロトタイプ */
fifo_t *fifo_create(void);
void fifo_delete(fifo_t *obj);
unsigned int fifo_write(fifo_t *obj, unsigned char *buf, unsigned int size);
unsigned int fifo_read(fifo_t *obj, unsigned char *buf, unsigned int size);
unsigned int fifo_length(fifo_t *obj);

#endif /* _FIFO_H_ */

fifo.c

#include <stdlib.h>
#include "fifo.h"

/* インスタンス生成 */
fifo_t *fifo_create(void)
{
	fifo_t *obj = (fifo_t *) malloc(sizeof(fifo_t));
	if ( obj == NULL )	return NULL;
	
	obj->read = obj->write = 0;
	obj->size = FIFO_BUFSIZE;
	
	return obj;
}

/* インスタンス消去 */
void fifo_delete(fifo_t *obj)
{
	free(obj);
}

/* データを書き込む. 戻り値:書き込めたバイト数 */
unsigned int fifo_write(fifo_t *obj, unsigned char *buf, unsigned int size)
{
	unsigned int ret = 0;
	unsigned int next = (obj->write + 1) % obj->size;
	
	while ( next != obj->read && ret < size ) {
		obj->buf[obj->write] = buf[ret];
		obj->write = next;
		next = (obj->write + 1) % obj->size;
		ret++;
	}
	
	return ret;
}

/* データを読み込む.戻り値:読み込めたバイト数 */
unsigned int fifo_read(fifo_t *obj, unsigned char *buf, unsigned int size)
{
	unsigned int ret = 0;
	
	while ( obj->read != obj->write && ret < size ) {
		buf[ret] = obj->buf[obj->read];
		obj->read = (obj->read + 1) % obj->size;
		ret++;
	}
	
	return ret;
}

/* 格納されているデータ数を取得する */
unsigned int fifo_length(fifo_t *obj)
{
	return (obj->size + obj->write - obj->read ) % obj->size;
}

以上のコードでシリアル通信を実現できます.
PC側でエコーを実現するのであれば,以下のようなコードで実現できると思います.

#include <stdio.h>
#include <windows.h>
#include <conio.h>
#include <stdlib.h>
#include "serial.h"
int main (void)
{
  serial_t obj = serial_init("COM1",9600);
  unsigned char buf[128], len;

  if ( obj == NULL ) {
    fprintf(stderr,"オブジェクト生成に失敗");
    return EXIT_FAILURE;
  }

  while (1) {
    len = serial_recv(obj,buf,sizeof(buf));
    if (len) serial_send(obj,buf,len);
    Sleep(1);
    if ( kbhit() )  break;
  }

  serial_delete(obj);

  return EXIT_SUCCESS;
}