C言語によるシリアル通信を行うプログラムを作成します。開発環境はEclipse(Juno)でツールチェーン「MinGW GCC」を使ってC言語のプログラムを作成します。パソコン上でシリアルポートを使ったプログラムを作成するには、シリアル通信を行う場合は、Win32APIを使って通信を行います。Win32APIではシリアル転送をファイルとして取り扱い、CreateFile関数、WriteFile関数、ReadFile関数などを使用します。

Eclipse上でCプロジェクトの作成

まず、Eclipse上でCプロジェクトを作ります。メニューから「ファイル」→「新規」→「Cプロジェクト」を選択し、表示されたCプロジェクト作成ダイアログで、プロジェクトタイプを「空のプロジェクト」、ツールチェーンを「MinGW GCC」に設定して、Cプロジェクトを作成します。

Cプロジェクト選択メニュー

Cプロジェクト作成ダイアログ

WinAPIを用いたシリアル通信関数群

WinAPIでは、シリアルポートもファイルと同様に、まず、WindowsではHANDLE型の変数でファイルなどのオブジェクトを区別して、CreateFile()関数などを用いて、アクセスや操作を行います。この場合も、開いたシリアルポートを特定するためにHANDLE型の変数が必要です。シリアルポートの場合、これに加えて通信状況の設定をするために、SetCommState関数で設定をします。全体的な流れを次に示します。

  1. CreateFile関数で ファイルをオープンする。(COMポートのハンドルを取得する)
  2. SetupComm、 PurgeComm関数で送受信バッファーの設定及び初期化を行う
  3. DCB構造体、SetCommState関数を用いて初期設定、通信条件設定、ポート設定などを行う
  4. COMMTIMEOUTS構造体、SetCommTimeouts関数を用いてタイムアウトに関する通信設定を行う
  5. WriteFile関数で データを送信する
  6. ReadFile関数で データを受信する
  7. 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);
}


Teratermへのシリアル転送結果

サンプルプログラムを実行したときのTeratermへの表示を次に示します。Teratermから入力した文字が、サンプルプログラムによりそのままエコーバックして画面に表示される。