C Sharpの基礎 - シリアル通信

提供:MochiuWiki : SUSE, EC, PCB
2024年9月17日 (火) 02:45時点におけるWiki (トーク | 投稿記録)による版 (→‎受信)
ナビゲーションに移動 検索に移動

概要

シリアル通信は、データを1ビットずつ順番に送受信する通信方式である。
C#では、System.IO.Ports名前空間のSerialPortクラスを使用してシリアル通信を実装することができる。

まず、SerialPortクラスのインスタンスを生成して、ポート名、ボーレート、データビット、ストップビット、パリティ等の通信パラメータを設定する。
これらのパラメータは、通信相手のデバイスと一致させる必要がある。

通信を開始するには、SerialPort.Openメソッドを実行する。

データを送信する場合は、WriteメソッドやWriteLineメソッドを実行する。

データを受信する場合は、同期的な方法と非同期的な方法がある。

  • 同期的な方法
    ReadLineメソッドやReadメソッドを使用してデータを受信する。
    これらのメソッドは、データが到着するまでプログラムの実行をブロックすることに注意する。

  • 非同期的な方法
    DataReceivedイベントを使用する。
    このイベントは、データが受信された時に発生し、イベントハンドラ内でデータを処理する。
    これにより、UIの応答性を維持しながらデータを受信することができる。


通信が完了した後は、必ずCloseメソッドを実行してポートを閉じる。

エラーハンドリングでは、タイムアウトの設定、例外処理、リソースの適切な解放等を考慮する必要がある。

シリアル通信は、組み込みシステム、産業用機器、DAQ等の様々な分野で利用されている。
C#の豊富なライブラリとイベント駆動型のプログラミングモデルにより、効率的なシリアル通信アプリケーションの開発が可能である。

実務では、デバッグツールやシリアルモニターを活用して、通信の動作を確認することも重要である。


同期通信

送信

以下に示すパラメータは、通信相手のデバイスの設定と一致している必要がある。
使用時では、接続するデバイスの仕様に合わせてこれらの値を適切に調整すること。

  • パリティ
    データの整合性チェックのために使用する。
    以下の例では、Parity.Noneに設定している。
    必要に応じて、Parity.EvenParity.OddParity.MarkParity.Spaceを指定する。
  • データビット
    各バイトのビット数を指定する。
    一般的に8ビットが使用されるが、7ビットや5ビット等も可能である。
  • ストップビット
    各バイトの終わりを示すビットを指定する。
    必要に応じて、StopBits.OneStopBits.OnePointFiveStopBits.Twoを指定する。
  • ハンドシェイク
    フロー制御の方法を指定する。
    ハードウェアフロー制御 (RTS / CTS)
    ソフトウェアフロー制御 (XON / XOFF)

    以下の例では、Handshake.Noneに設定している。
    必要に応じて、Handshake.RequestToSendHandshake.XOnXOff等を指定する。
  • タイムアウト
    読み取りと書き込みのタイムアウトを設定している。
    これにより、操作が無限に待機することを防ぐことができる。


 using System;
 using System.IO.Ports;
 
 class Sender
 {
    static void Main(string[] args)
    {
       string portName = "/dev/ttyS0";    // ポート名を適切に設定
       int baudRate    = 9600;            // ボーレート 9600[bps]
       Parity parity   = Parity.None;     // パリティ無し
       int dataBits    = 8;               // データ長は8ビット
       StopBits stopBits = StopBits.One;  // ストップビットは1ビット
 
       try
       {
          using (SerialPort serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits))
          {
             serialPort.Handshake    = Handshake.None;  // フロー制御の設定
             serialPort.ReadTimeout  = 500;             // 読み取りタイムアウトの設定 (ミリ秒)
             serialPort.WriteTimeout = 500;             // 書き込みタイムアウトの設定 (ミリ秒)
 
             serialPort.Open();
             Console.WriteLine("シリアルポートをオープン")
             Console.WriteLine("終了するには 'exit' と入力");
 
             while (true)
             {
                Console.Write("送信するメッセージを入力: ");
                string message = Console.ReadLine();
 
                if (message.ToLower() == "exit") break;
 
                serialPort.WriteLine(message);
                Console.WriteLine("メッセージが送信された");
             }
 
             serialPort.Close();
          }
       }
       catch (TimeoutException)
       {
          Console.WriteLine("送信操作がタイムアウト");
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 }


受信

以下の例では、シリアル通信を同期処理で受信している。

 using System;
 using System.IO.Ports;
 
 class Receiver
 {
    static void Main(string[] args)
    {
       string portName = "/dev/ttyS0";    // ポート名を適切に設定
       int baudRate    = 9600;            // ボーレート 9600[bps]
       Parity parity   = Parity.None;     // パリティ無し
       int dataBits    = 8;               // データ長は8ビット
       StopBits stopBits = StopBits.One;  // ストップビットは1ビット
 
       try
       {
          using (SerialPort serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits))
          {
             serialPort.Handshake    = Handshake.None;  // フロー制御の設定
             serialPort.ReadTimeout  = 500;             // 読み取りタイムアウトの設定 (ミリ秒)
             serialPort.WriteTimeout = 500;             // 書き込みタイムアウトの設定 (ミリ秒)
 
             serialPort.Open();
             Console.WriteLine("シリアルポートをオープン")
             Console.WriteLine("終了するには [Ctrl] + [C]キーを押下");
 
             serialPort.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
 
             // プログラムを実行し続けるためのループ
             while (true)
             {
                System.Threading.Thread.Sleep(100);
             }
          }
       }
       catch (TimeoutException)
       {
          Console.WriteLine("受信操作がタイムアウト");
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 
    private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    {
       SerialPort sp = (SerialPort)sender;
 
       try
       {
          string indata = sp.ReadExisting();
          Console.WriteLine("受信したデータ: " + indata);
       }
       catch (TimeoutException)
       {
          Console.WriteLine("データの読み取り中にタイムアウトが発生");
       }
    }
 }



非同期通信

送信

以下の例では、シリアル通信を非同期処理で送信している。

 using System;
 using System.IO.Ports;
 using System.Threading.Tasks;
 
 class AsyncSender
 {
    static async Task Main(string[] args)
    {
       string portName = "/dev/ttyS0";    // ポート名を適切に設定
       int baudRate    = 9600;            // ボーレート 9600[bps]
       Parity parity   = Parity.None;     // パリティ無し
       int dataBits    = 8;               // データ長は8ビット
       StopBits stopBits = StopBits.One;  // ストップビットは1ビット
 
       try
       {
          using (SerialPort serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits))
          {
             serialPort.Handshake    = Handshake.None;  // フロー制御の設定
             serialPort.ReadTimeout  = 500;             // 読み取りタイムアウトの設定 (ミリ秒)
             serialPort.WriteTimeout = 500;             // 書き込みタイムアウトの設定 (ミリ秒)
 
             serialPort.Open();
             Console.WriteLine("シリアルポートをオープン");
             Console.WriteLine("終了するには 'exit' と入力");
 
             while (true)
             {
                Console.Write("送信するメッセージを入力: ");
                string message = Console.ReadLine();
 
                if (message.ToLower() == "exit") break;
 
                byte[] buffer = System.Text.Encoding.UTF8.GetBytes(message + Environment.NewLine);
                await serialPort.BaseStream.WriteAsync(buffer, 0, buffer.Length);
 
                Console.WriteLine("メッセージが非同期で送信完了");
             }
 
             serialPort.Close();
          }
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 }


受信

以下の例では、シリアル通信を非同期処理で受信している。

 using System;
 using System.IO.Ports;
 using System.Threading;
 using System.Threading.Tasks;
 
 class AsyncReceiver
 {
    static async Task Main(string[] args)
    {
       string portName = "/dev/ttyS0";    // ポート名を適切に設定
       int baudRate    = 9600;            // ボーレート 9600[bps]
       Parity parity   = Parity.None;     // パリティ無し
       int dataBits    = 8;               // データ長は8ビット
       StopBits stopBits = StopBits.One;  // ストップビットは1ビット
 
       try
       {
          using (SerialPort serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits))
          {
             serialPort.Handshake    = Handshake.None;  // フロー制御の設定
             serialPort.ReadTimeout  = 500;             // 読み取りタイムアウトの設定 (ミリ秒)
             serialPort.WriteTimeout = 500;             // 書き込みタイムアウトの設定 (ミリ秒)
 
             serialPort.Open();
             Console.WriteLine("シリアルポートをオープン");
             Console.WriteLine("終了するには [Ctrl] + [C]キーを押下");
 
             using (var cts = new CancellationTokenSource())
             {
                Console.CancelKeyPress += (s, e) =>
                {
                   e.Cancel = true;
                   cts.Cancel();
                };
 
                await ReceiveDataAsync(serialPort, cts.Token);
             }
 
             serialPort.Close();
          }
       }
       catch (OperationCanceledException)
       {
          Console.WriteLine("エラー: 受信が中断");
       }
       catch (Exception ex)
       {
          Console.WriteLine($"エラーが発生: {ex.Message}");
       }
    }
 
    private static async Task ReceiveDataAsync(SerialPort serialPort, CancellationToken cancellationToken)
    {
       byte[] buffer = new byte[1024];
       while (!cancellationToken.IsCancellationRequested)
       {
          try
          {
             int bytesRead = await serialPort.BaseStream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
             if (bytesRead > 0)
             {
                string receivedData = System.Text.Encoding.UTF8.GetString(buffer, 0, bytesRead);
                Console.WriteLine($"受信したデータ: {receivedData.Trim()}");
             }
          }
          catch (OperationCanceledException)
          {
             throw;
          }
          catch (Exception ex)
          {
             Console.WriteLine($"受信中にエラーが発生: {ex.Message}");
          }
       }
    }
 }