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); }