緑の中に

緑の中に
国立天文台

3/01/2021

RS232C この先がわかんない!

 //+++++++ 各文の分かりやすい注釈文(コメント)が欲しいです +++++++

// +++++ コンパイルができたので、とにかく何とかなりそうな予感がします +++

// +++  minGW でコンパイルができました。ワーニングは出ました。ですが、+++

//++++ コンパイルエラーではなかったです +++++++++++++

//Good.

//東京工業大学ロッボト技術研究会さんありがとうございました。私、何とか自力でCompileできました。

//■C:\MinGW\RS232C> mingw32-g++  RS232C~1.CPP   私の現在のマシンはデヴァイスマネージャーでCOM1・COM6を表示していたのでCOM1を記述しました。

//私のマシンのCOM6は現在DataLoggerBで稼働使用中です。

// Compileエラー無し。ワーニングはあり。

/* RS232Cプログラムを書く方法ですが、

・C#やVBで.NETFrameworkを利用する

・C++でWin32APIを利用する

の二つの方法があるようですが、私がCしか触れたことがないのでWin32APIをつかって作ります。

またWindowsアプリケーションはよくわからないのでコンソールアプリケーションを想定しています。


全体の流れは

1.ファイルとしてCOMポートを開く

2.送信バッファの設定

3.送受信バッファの初期化

4.COMポート構成情報の初期化

5.タイムアウトの設定

でRS232C通信が使えるようになります。

今回の通信の仕様は

RTS,CTSフロー制御なし

9600bps

データサイズ8bit

パリティビットなし

ストップビット1bit

main()関数内での受信処理

です。


まずはいくつかのヘッダファイルを読み込んでおきましょう。*/

#include <string>//良く知らん

#include <stdio.h>//printfなどの標準入出力関数を使うためのヘッダファイル

#include <windows.h>//Wi32APIを使うためのヘッダファイル

#include <tchar.h>//_T()使うのに要る


int main(void){

//1.COMポートを開く

// HANDLE hComPort;//COMポートのハンドルいろいろ使うのでグローバル変数にしておくとよい

//  hComPort = CreateFile(    //ファイルとしてポートを開く

//      _T("COM4"),// ポート名を指すバッファへのポインタ:COM4を開く(デバイスマネージャでどのポートが使えるか確認)

//      GENERIC_READ | GENERIC_WRITE, // アクセスモード:読み書き両方する

//      0,        //ポートの共有方法を指定:オブジェクトは共有しない

//      NULL,       //セキュリティ属性:ハンドルを子プロセスへ継承しない

//     OPEN_EXISTING,     //ポートを開き方を指定:既存のポートを開く

//      0,   //ポートの属性を指定:同期 非同期にしたいときはFILE_FLAG_OVERLAPPED

//      NULL       // テンプレートファイルへのハンドル:NULLって書け

//  );

HANDLE hComPort;


hComPort = CreateFile(

_T("COM1"), //@@@@@@@@@@@@@@@@@@@@@@@@@ ここ

GENERIC_READ | GENERIC_WRITE,

0,

NULL,

OPEN_EXISTING,

0,

NULL

);

 if (hComPort == INVALID_HANDLE_VALUE){//ポートの取得に失敗

  printf("指定COMポートが開けません.\n\r");

  CloseHandle(hComPort);//ポートを閉じる

return 0;

 }

 else{

  printf("COMポートは正常に開けました.\n\r");

 }

/*ポートをファイルとみなしCreateFile()関数を用いて開きます。失敗するとINVALID_HANDLE_VALUEを返します。

ポートの属性は非同期(FILE_FLAG_OVERLAPPED)にした方がいいらしいですが上手くいかなかったので同期通信にしてあります。


2.送受信バッファの設定 */

 int check;//エラーチェック用の変数

 check = SetupComm(

       hComPort,//COMポートのハンドラ

       1024,//受信バッファサイズ:1024byte

       1024//送信バッファ:1024byte

  );

 if (check == FALSE){

     printf("送受信バッファの設定ができません.\r\n");

CloseHandle(hComPort);

     return 0;

 }

 else{

      printf("送受信バッファの設定が完了しました.\r\n");

 }

/*SetupComm()関数を用いて送受信バッファの設定をします。


3.送受信バッファの初期化 */

 check = PurgeComm(

       hComPort,//COMポートのハンドラ

       PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR//出入力バッファをすべてクリア

 );

 if (check == FALSE){

     printf("送受信バッファの初期化ができません.\r\n");

     CloseHandle(hComPort);

     return 0;

 }

 else{

      printf("送受信バッファの初期化が完了しました.\r\n");

 }

/* PurgeComm()関数を用いて出入力すべてのバッファをクリアします。


4.COMポート構成情報の初期化 */

 DCB dcb;//構成情報を記録する構造体の生成

 GetCommState(hComPort, &dcb);//現在の設定値を読み込み

 dcb.DCBlength = sizeof(DCB);//DCBのサイズ

 dcb.BaudRate = 9600;//ボーレート:9600bps

 dcb.ByteSize = 8;//データサイズ:8bit

 dcb.fBinary = TRUE;//バイナリモード:通常TRUE

 dcb.fParity = NOPARITY;//パリティビット:パリティビットなし

 dcb.StopBits = ONESTOPBIT;//ストップビット:1bit

 dcb.fOutxCtsFlow = FALSE;//CTSフロー制御:フロー制御なし

 dcb.fOutxDsrFlow = FALSE;//DSRハードウェアフロー制御:使用しない

 dcb.fDtrControl = DTR_CONTROL_DISABLE;//DTR有効/無効:DTR無効

 dcb.fRtsControl = RTS_CONTROL_DISABLE;//RTSフロー制御:RTS制御なし


 dcb.fOutX = FALSE;//送信時XON/XOFF制御の有無:なし

 dcb.fInX = FALSE;//受信時XON/XOFF制御の有無:なし

 dcb.fTXContinueOnXoff = TRUE;// 受信バッファー満杯&XOFF受信後の継続送信可否:送信可

 dcb.XonLim = 512;//XONが送られるまでに格納できる最小バイト数:512

 dcb.XoffLim = 512;//XOFFが送られるまでに格納できる最小バイト数:512

 dcb.XonChar = 0x11;//送信時XON文字 ( 送信可:ビジィ解除 ) の指定:XON文字として11H ( デバイス制御1:DC1 )

 dcb.XoffChar = 0x13;//XOFF文字(送信不可:ビジー通告)の指定:XOFF文字として13H ( デバイス制御3:DC3 )


 dcb.fNull = TRUE;// NULLバイトの破棄:破棄する

 dcb.fAbortOnError = TRUE;//エラー時の読み書き操作終了:終了する

 dcb.fErrorChar = FALSE;// パリティエラー発生時のキャラクタ(ErrorChar)置換:なし

 dcb.ErrorChar = 0x00;// パリティエラー発生時の置換キャラクタ

 dcb.EofChar = 0x03;// データ終了通知キャラクタ:一般に0x03(ETX)がよく使われます。

 dcb.EvtChar = 0x02;// イベント通知キャラクタ:一般に0x02(STX)がよく使われます


 check = SetCommState(hComPort, &dcb);  //設定値の書き込み

 if (check == FALSE){//エラーチェック

     printf("COMポート構成情報の変更に失敗しました.\r\n");

     CloseHandle(hComPort);

     return 0;

 }

 else{

      printf("COMポート構成情報を変更しました.\r\n");

 }

/* DCB構造体で構成情報を設定します。今回すべての変数について書き込んでいますが、GetCommState()関数で基本情報を読み込んでいるため変更する部分だけ書き換えればいいです。

ボーレートやデータサイズ、パリティビット、ストップビットなどはマイコン側とあわせてください。

書き換えた後にSetCommState()関数で再設定を行います。*/


//5.タイムアウト時間の設定

 COMMTIMEOUTS TimeOut; // COMMTIMEOUTS構造体の変数を宣言

 GetCommTimeouts(hComPort, &TimeOut); // タイムアウトの設定状態を取得


 TimeOut.ReadTotalTimeoutMultiplier = 0;//読込の1文字あたりの時間:タイムアウトなし

 TimeOut.ReadTotalTimeoutConstant = 1000;//読込エラー検出用のタイムアウト時間

 //(受信トータルタイムアウト) = ReadTotalTimeoutMultiplier × (受信予定バイト数) + ReadTotalTimeoutConstant

 TimeOut.WriteTotalTimeoutMultiplier = 0;//書き込み1文字あたりの待ち時間:タイムアウトなし

 TimeOut.WriteTotalTimeoutConstant = 1000;//書き込みエラー検出用のタイムアウト時間

 //(送信トータルタイムアウト) = WriteTotalTimeoutMultiplier ×(送信予定バイト数) + WriteTotalTimeoutConstant


 check = SetCommTimeouts(hComPort, &TimeOut);//タイムアウト設定の書き換え

 if (check == FALSE){//エラーチェック

     printf("タイムアウトの設定に失敗しました.\r\n");

     CloseHandle(hComPort);

     return 0;

 }

 else{

      printf("タイムアウトの設定に成功しました.\r\n");

 }

/*構成情報の設定と似た手順です。COMMTIMEOUTS構造体を使い、GetCommTimeouts()関数で基本情報を取得し、変更したのちSetCommTimeouts()関数で書き換えています。各値はよく知りませんがこんな感じが多いです。

ここまででCOMポートの初期設定ができました次に送信してみましょう。


6.送信 */

 char SendData[] = "17";//送信データの用意

 int SendSize = strlen(SendData)+1;//送信データサイズを取得

 DWORD writeSize;//実際に送信したデータサイズ

 WriteFile(hComPort,SendData,SendSize,&writeSize,NULL);

/* WriteFile()関数を用いて送信。送信するデータサイズはNULL文字の分+1するのを忘れないように。


7.受信 */

 int i = 0;

 char RecieveChar[1];//1文字受信のための変数

 char RecieveData[100];//受信文字列

 unsigned long nn;

 while (1) {

       ReadFile(hComPort, RecieveChar, 1, &nn, 0); // シリアルポートに対する読み込み

       if (nn == 1) {

           if (RecieveChar[0] == 10 || RecieveChar[0] == 13) { // '\r'や'\n'を受信すると文字列を閉じる

               if (i != 0) {

                   RecieveData[i] = '\0';//最後にNULL文字を付加して

                   i = 0;//文字列を先頭に戻す

                   printf("%s\n", RecieveData);//受信した文字列を表示

               }

}

           else {

                RecieveData[i] = RecieveChar[0];//受信した文字を文字列にする

                i++;

           }

       }

}

}

/*ReadFil()関数で1文字ずつ受信して'\r'や'\n'で文字列の切り分けを行っています。

受信のタイミングは分からないのでスレッド受信にすべきなのですが、スレッドに関して未だ理化していないことが多いので今回はwhileで回し続けています。


VisualStudio2013で作り、PIC32MX440FとRS23C通信を行いました。

数字を文字データとして送信しPICが素数かどうか判定して素数だと「~ is a prime! 」と返すプログラムを書いて実行したところ。*/



//--------------------------------------------------------------

//COM1を指定して、Compileし、実行しました

//Compilerは ■C:\MinGW\RS232C> mingw32-g++  RS232C~1.CPP

//C:\MinGW\RS232C>a.exe

//---------------------------------------------------------------

//COMポートは正常に開けました.

//送受信バッファの設定が完了しました.

//送受信バッファの初期化が完了しました.

//COMポート告ャ|情報を変更しました.

//タイムアウトの設定に成功しました.

//------------------------------------------------------------------------------------------------------------------------------

//なるメッセージを表示しました

//c:/mingw/bin/../lib/gcc/mingw32/6.3.0/../../../../mingw32/bin/ld.exe: cannot open output file a.exe: Permission denied

//はa.exegaがすでに実行中においてCompile実行をしたからです。

//--------------------------------------------------------------------------------------------------------------------------------