C言語によるシリアル通信を行うプログラムを作成します。開発環境はEclipse(Juno)でツールチェーン「MinGW GCC」を使ってC言語のプログラムを作成します。パソコン上でシリアルポートを使ったプログラムを作成するには、シリアル通信を行う場合は、Win32APIを使って通信を行います。Win32APIではシリアル転送をファイルとして取り扱い、CreateFile関数、WriteFile関数、ReadFile関数などを使用します。
Eclipse上でCプロジェクトの作成
まず、Eclipse上でCプロジェクトを作ります。メニューから「ファイル」→「新規」→「Cプロジェクト」を選択し、表示されたCプロジェクト作成ダイアログで、プロジェクトタイプを「空のプロジェクト」、ツールチェーンを「MinGW GCC」に設定して、Cプロジェクトを作成します。
WinAPIを用いたシリアル通信関数群
WinAPIでは、シリアルポートもファイルと同様に、まず、WindowsではHANDLE型の変数でファイルなどのオブジェクトを区別して、CreateFile()関数などを用いて、アクセスや操作を行います。この場合も、開いたシリアルポートを特定するためにHANDLE型の変数が必要です。シリアルポートの場合、これに加えて通信状況の設定をするために、SetCommState関数で設定をします。全体的な流れを次に示します。
- CreateFile関数で ファイルをオープンする。(COMポートのハンドルを取得する)
- SetupComm、 PurgeComm関数で送受信バッファーの設定及び初期化を行う
- DCB構造体、SetCommState関数を用いて初期設定、通信条件設定、ポート設定などを行う
- COMMTIMEOUTS構造体、SetCommTimeouts関数を用いてタイムアウトに関する通信設定を行う
- WriteFile関数で データを送信する
- ReadFile関数で データを受信する
- CloseHandle関数でファイルを閉じる
次に、実際の実装例を示します。ここでは、RS232_OpenComport関数は、上記にの1-4までの機能を実装しています。DCB( デバイス制御ブロック)構造体の変数を宣言して 各種通信条件を設定します。伝送速度、パリティ有無、ストップビット数、CTS/RTSハードウェアフロー制御及び、Xon/Xoffソフトウェアフロー制御の有無や諸定数等の通信条件設定を行います。
include <windows.h>
#include <stdlib.h>
#include <stdio.h>
HANDLE Cport[16];
char comports[16][10]={"\\\\.\\COM1", "\\\\.\\COM2", "\\\\.\\COM3", "\\\\.\\COM4",
"\\\\.\\COM5", "\\\\.\\COM6", "\\\\.\\COM7", "\\\\.\\COM8",
"\\\\.\\COM9", "\\\\.\\COM10", "\\\\.\\COM11", "\\\\.\\COM12",
"\\\\.\\COM13", "\\\\.\\COM14", "\\\\.\\COM15", "\\\\.\\COM16"};
char mode_str[128];
int RS232_OpenComport(int comport_number, int baudrate, const char *mode)
{
if((comport_number>15)||(comport_number<0))
{
printf("illegal comport number\n");
return(1);
}
switch(baudrate)
{
case 110 : strcpy(mode_str, "baud=110");
break;
case 300 : strcpy(mode_str, "baud=300");
break;
case 600 : strcpy(mode_str, "baud=600");
break;
case 1200 : strcpy(mode_str, "baud=1200");
break;
case 2400 : strcpy(mode_str, "baud=2400");
break;
case 4800 : strcpy(mode_str, "baud=4800");
break;
case 9600 : strcpy(mode_str, "baud=9600");
break;
case 19200 : strcpy(mode_str, "baud=19200");
break;
case 38400 : strcpy(mode_str, "baud=38400");
break;
case 57600 : strcpy(mode_str, "baud=57600");
break;
case 115200 : strcpy(mode_str, "baud=115200");
break;
case 128000 : strcpy(mode_str, "baud=128000");
break;
case 256000 : strcpy(mode_str, "baud=256000");
break;
case 500000 : strcpy(mode_str, "baud=500000");
break;
case 1000000 : strcpy(mode_str, "baud=1000000");
break;
default : printf("invalid baudrate\n");
return(1);
break;
}
if(strlen(mode) != 3)
{
printf("invalid mode \"%s\"\n", mode);
return(1);
}
switch(mode[0])
{
case '8': strcat(mode_str, " data=8");
break;
case '7': strcat(mode_str, " data=7");
break;
case '6': strcat(mode_str, " data=6");
break;
case '5': strcat(mode_str, " data=5");
break;
default : printf("invalid number of data-bits '%c'\n", mode[0]);
return(1);
break;
}
switch(mode[1])
{
case 'N':
case 'n': strcat(mode_str, " parity=n");
break;
case 'E':
case 'e': strcat(mode_str, " parity=e");
break;
case 'O':
case 'o': strcat(mode_str, " parity=o");
break;
default : printf("invalid parity '%c'\n", mode[1]);
return(1);
break;
}
switch(mode[2])
{
case '1': strcat(mode_str, " stop=1");
break;
case '2': strcat(mode_str, " stop=2");
break;
default : printf("invalid number of stop bits '%c'\n", mode[2]);
return(1);
break;
}
strcat(mode_str, " dtr=on rts=on");
Cport[comport_number] = CreateFileA(comports[comport_number],
GENERIC_READ|GENERIC_WRITE,
0, /* no share */
NULL, /* no security */
OPEN_EXISTING,
0, /* no threads */
NULL); /* no templates */
if(Cport[comport_number]==INVALID_HANDLE_VALUE)
{
printf("unable to open comport\n");
return(1);
}
DCB port_settings;
memset(&port_settings, 0, sizeof(port_settings)); /* clear the new struct */
port_settings.DCBlength = sizeof(port_settings);
if(!BuildCommDCBA(mode_str, &port_settings))
{
printf("unable to set comport dcb settings\n");
CloseHandle(Cport[comport_number]);
return(1);
}
if(!SetCommState(Cport[comport_number], &port_settings))
{
printf("unable to set comport cfg settings\n");
CloseHandle(Cport[comport_number]);
return(1);
}
COMMTIMEOUTS Cptimeouts;
Cptimeouts.ReadIntervalTimeout = MAXDWORD;
Cptimeouts.ReadTotalTimeoutMultiplier = 0;
Cptimeouts.ReadTotalTimeoutConstant = 0;
Cptimeouts.WriteTotalTimeoutMultiplier = 0;
Cptimeouts.WriteTotalTimeoutConstant = 0;
if(!SetCommTimeouts(Cport[comport_number], &Cptimeouts))
{
printf("unable to set comport time-out settings\n");
CloseHandle(Cport[comport_number]);
return(1);
}
return(0);
}
int RS232_PollComport(int comport_number, unsigned char *buf, int size)
{
int n;
/* added the void pointer cast, otherwise gcc will complain about */
/* "warning: dereferencing type-punned pointer will break strict aliasing rules" */
ReadFile(Cport[comport_number], buf, size, (LPDWORD)((void *)&n), NULL);
return(n);
}
int RS232_SendByte(int comport_number, unsigned char byte)
{
int n;
WriteFile(Cport[comport_number], &byte, 1, (LPDWORD)((void *)&n), NULL);
if(n<0) return(1);
return(0);
}
void RS232_cputs(int comport_number, const char *text) /* sends a string to serial port */
{
while(*text != 0) RS232_SendByte(comport_number, *(text++));
}
ちなみにCOMポートのCTSやDSRを確認したい時はGetCommModemStatusを使用します。
DWORD sts; GetCommModemStatus(hRs232c,&sts); if ((sts & MS_CTS_ON) != 0) // CTSがON if ((sts & MS_DSR_ON) != 0) // DSRがON
また、COMポートのDTRやRTSを設定したい時はEscapeCommFunctionを使用します。
EscapeCommFunction(hRs232c,SETDTR); // DTRをON EscapeCommFunction(hRs232c,CLRDTR); // DTRをOFF EscapeCommFunction(hRs232c,SETRTS); // RTSをON EscapeCommFunction(hRs232c,CLRRTS); // RTSをOFF
ターミナルソフト「Teraterm」とのシリアル通信のサンプルプログラム
次に、パソコンのCOMポートにターミナルソフト「Teraterm」を接続して文字列の送受信をおこなった例を紹介します。
#include <stdlib.h>
#include <stdio.h>
#include <Windows.h>
#include "rs232.h"
int main() {
int n;
unsigned char buf[4096];
int cport_nr = 10; /* COM11 */
int bdrate = 9600; /* 9600 baud */
char mode[] = { '8', 'N', '1', 0 };
if (RS232_OpenComport(cport_nr, bdrate, mode)) {
printf("Can not open comport\n");
return (0);
}
RS232_cputs(cport_nr, "\r\nStart.\r\n");
while (1) {
n = RS232_PollComport(cport_nr, buf, 4095);
if (n > 0) {
buf[n] = 0; /* nullコードの挿入 */
RS232_cputs(cport_nr, (char *) buf);
}
Sleep(1000);
}
return (0);
}


