Программирование PC Visual Studio C#: работа с последовательным портом Tue, February 21 2017  

Поделиться

нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Visual Studio C#: работа с последовательным портом Печать
Добавил(а) microsin   

Эта статья показывает, как записывать и читать данные от устройства, подключенного к последовательному порту (COM-порт) из приложения на языке C# в среде .NET. Мы будем читать и записывать данные через TextBox на форме, и будем работать с потоками.

serial-port-pinout

В недалеком прошлом для работы с Serial Port в среде .Net 1.1, мы должны были использовать либо Windows API, либо использовать управление из сторонних библиотек. В среде .Net 2.0 (и в более поздних версиях .NET) компания Microsoft добавила поддержку последовательного порта включением класса SerialPort как части пространства имен System.IO.Ports. Реализация класса SerialPort сделана очень прямо и очевидно. Чтобы создать экземпляр класса SerialPort class, просто передайте опции SerialPort конструктору класса:

// Все опции для последовательного устройства
// ---- могут быть отправлены через конструктор класса SerialPort
// ---- PortName = "COM1", Baud Rate = 19200, Parity = None,
// ---- Data Bits = 8, Stop Bits = One, Handshake = None
SerialPort _serialPort = new SerialPort("COM1", 
                                        19200, 
                                        Parity.None,
                                        8,
                                        StopBits.One);
_serialPort.Handshake = Handshake.None;

Для приема данных нам нужно создать обработчик события EventHandler для "SerialDataReceivedEventHandler":

// "sp_DataReceived" является вручную созданным методом (подпрограммой)
_serialPort.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);

Вы можете также установить другие опции, такие как ReadTimeout и WriteTimeout (таймауты чтения и записи):

// milliseconds _serialPort.ReadTimeout = 500;
_serialPort.WriteTimeout = 500;

Как только Вы готовы использовать последовательный порт, Вам нужно открыть его:

// Открытие последовательного порта
_serialPort.Open();

Сейчас мы готовы принять данные. Однако чтобы записать эти данные в область ввода TextBox на форме, нам нужно создать так называемого делегата (delegate). Библиотеки .Net не позволяют межпотоковое взаимодействие (cross-thread action), так что нам нужно использовать делегат. Делегат используется для записи в поток пользовательского интерфейса (User Interface, UI) из другого потока (не UI).

// Делегат используется для записи в UI control из потока не-UI
private delegate void SetTextDeleg(string text);

Мы создадим теперь метод "sp_DataReceived", который будет выполнен при поступлении данных в последовательный порт:

void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
   Thread.Sleep(500);
   string data = _serialPort.ReadLine();
   // Привлечение делегата на потоке UI, и отправка данных, которые
   // были приняты привлеченным методом.
   // ---- Метод "si_DataReceived" будет выполнен в потоке UI,
   // который позволит заполнить текстовое поле TextBox.
   this.BeginInvoke(new SetTextDeleg(si_DataReceived), 
                    new object[] { data });
}

Теперь создадим наш метод "si_DataReceived":

private void si_DataReceived(string data)
{
   textBox1.Text = data.Trim();
}

Мы можем теперь принять данные из последовательного порта от устройства и отобразить их на форме. Некоторые устройства отправляют данные сами, без запроса. Однако некоторым устройствам нужно отправить определенные команды, чтобы они ответили на них какими-то своими данными. Для этих устройств Вы будете записывать данные в последовательный порт, и будете использовать предыдущий код, чтобы получить данные обратно. В этом примере будет происходить обмен со шкалой. Для отдельной шкалы отправка команды "SI\r\n" приведет к возврату веса, который имеется на шкале. Эта команда является специфической именно для этого устройства, в Вашем же случае нужно читать документацию по протоколу устройства, чтобы найти команды, принимаемые устройством. Для записи в последовательный порт создайте кнопку "Start" на форме, и добавьте код в событие клика на ней Click_Event:

private void btnStart_Click(object sender, EventArgs e)
{
   // Makes sure serial port is open before trying to write
   try
   {
      if(!(_serialPort.IsOpen))
      _serialPort.Open();
      _serialPort.Write("SIrn");
   }
   catch (Exception ex)
   {
      MessageBox.Show("Error opening/writing to serial port :: "
                      + ex.Message, "Error!");
   }
}

Это все, что нужно Вам сделать. См. ссылку [1] для загрузки готового проекта Microsoft Visual C# 2010.

SerialPortCommunication-C-sharp-example

using System;
using System.IO.Ports;
using System.Threading;
 
public class PortChat
{
   static bool _continue;
   static SerialPort _serialPort;
 
   public static void Main()
   {
      string name;
      string message;
      StringComparer stringComparer = StringComparer.OrdinalIgnoreCase;
      Thread readThread = new Thread(Read);
 
      // Создание нового объекта SerialPort с установками по умолчанию.
      _serialPort = new SerialPort();
 
      // Позволяем пользователю установить подходящие свойства.
      _serialPort.PortName = SetPortName(_serialPort.PortName);
      _serialPort.BaudRate = SetPortBaudRate(_serialPort.BaudRate);
      _serialPort.Parity = SetPortParity(_serialPort.Parity);
      _serialPort.DataBits = SetPortDataBits(_serialPort.DataBits);
      _serialPort.StopBits = SetPortStopBits(_serialPort.StopBits);
      _serialPort.Handshake = SetPortHandshake(_serialPort.Handshake);
 
      // Установка таймаутов чтения/записи (read/write timeouts)
      _serialPort.ReadTimeout = 500;
      _serialPort.WriteTimeout = 500;
      _serialPort.Open();
      _continue = true;
      readThread.Start();
 
      Console.Write("Name: ");
      name = Console.ReadLine();
 
      Console.WriteLine("Type QUIT to exit");
 
      while (_continue)
      {
         message = Console.ReadLine();
 
         if (stringComparer.Equals("quit", message))
         {
            _continue = false;
         }
         else
         {
            _serialPort.WriteLine(
            String.Format("< {0} >: {1}", name, message) );
         }
      }
 
      readThread.Join();
      _serialPort.Close();
   }
 
   public static void Read()
   {
      while (_continue)
      {
         try
         {
            string message = _serialPort.ReadLine();
            Console.WriteLine(message);
         }
         catch (TimeoutException) { }
      }
   }
 
   public static string SetPortName(string defaultPortName)
   {
      string portName;
 
      Console.WriteLine("Available Ports:");
      foreach (string s in SerialPort.GetPortNames())
      {
         Console.WriteLine(" {0}", s);
      }
 
      Console.Write("COM port({0}): ", defaultPortName);
      portName = Console.ReadLine();
 
      if (portName == "")
      {
         portName = defaultPortName;
      }
      return portName;
   }
 
   public static int SetPortBaudRate(int defaultPortBaudRate)
   {
      string baudRate;
 
      Console.Write("Baud Rate({0}): ", defaultPortBaudRate);
      baudRate = Console.ReadLine();
 
      if (baudRate == "")
      {
         baudRate = defaultPortBaudRate.ToString();
      }
 
      return int.Parse(baudRate);
   }
 
   public static Parity SetPortParity(Parity defaultPortParity)
   {
      string parity;
 
      Console.WriteLine("Available Parity options:");
      foreach (string s in Enum.GetNames(typeof(Parity)))
      {
         Console.WriteLine(" {0}", s);
      }
 
      Console.Write("Parity({0}):", defaultPortParity.ToString());
      parity = Console.ReadLine();
 
      if (parity == "")
      {
         parity = defaultPortParity.ToString();
      }
 
      return (Parity)Enum.Parse(typeof(Parity), parity);
   }
 
   public static int SetPortDataBits(int defaultPortDataBits)
   {
      string dataBits;
 
      Console.Write("Data Bits({0}): ", defaultPortDataBits);
      dataBits = Console.ReadLine();
 
      if (dataBits == "")
      {
         dataBits = defaultPortDataBits.ToString();
      }
 
      return int.Parse(dataBits);
   }
 
   public static StopBits SetPortStopBits(StopBits defaultPortStopBits)
   {
      string stopBits;
 
      Console.WriteLine("Available Stop Bits options:");
      foreach (string s in Enum.GetNames(typeof(StopBits)))
      {
         Console.WriteLine(" {0}", s);
      }
 
      Console.Write("Stop Bits({0}):", defaultPortStopBits.ToString());
      stopBits = Console.ReadLine();
 
      if (stopBits == "")
      {
         stopBits = defaultPortStopBits.ToString();
      }
 
      return (StopBits)Enum.Parse(typeof(StopBits), stopBits);
   }
 
   public static Handshake SetPortHandshake(Handshake defaultPortHandshake)
   {
      string handshake;
 
      Console.WriteLine("Available Handshake options:");
      foreach (string s in Enum.GetNames(typeof(Handshake)))
      {
         Console.WriteLine(" {0}", s);
      }
 
      Console.Write("Handshake({0}):", defaultPortHandshake.ToString());
      handshake = Console.ReadLine();
 
      if (handshake == "")
      {
         handshake = defaultPortHandshake.ToString();
      }
 
      return (Handshake)Enum.Parse(typeof(Handshake), handshake);
   }
}

[Ссылки]

1. Проекты Visual Studio на языке C# и Visual Basic, работающие с COM-портом.
2. Алгоритм работы CTS и RTS в RS-232.

 

Комментарии  

 
0 #10 Владимир 29.03.2016 16:45
Не очень шарю в С/С++/С#. При запуске этого приложения выдает ошибку в textbox "Порт 'COM1' не существует." Всё подключено в порт COM4. В Вашем приложении не предлагается выбор портов в ComboBox1, список пуст. Подскажите в чем может быть проблема. Спасибо заранее!

microsin: чтобы выпадающий список выбора COM-портов был непуст, заполните его во время выполнения приложения путем сканирования доступных в системе имен COM-портов (определить наличие порта в системе можно при попытке его открыть), либо заполните список вручную. Пункты в выпадающий список добавляются методом CommBox->Items->Add, см. документацию по API стандартных компонентов Microsoft.
Цитировать
 
 
+2 #9 Виталий 08.12.2015 22:32
Для чего в методе sp_DataReceived остановка потока Thread.Sleep(500);?
Я правильно понимаю, что 500 мс не будут поступать данные из порта? Если да, то что нужно сделать, что бы не пропустить эти данные?

microsin: задержка с помощью Sleep делается для того, чтобы уступить время выполнения другим потокам. Это стандартное действие, без которого остальные приложения будут работать медленно. В течение этой задержки данные будут поступать в буфер драйвера, и задержка подбирается под отсутствие переполнения буфера - чтобы программа успела выбрать все пришедшие данные.
Цитировать
 
 
+1 #8 ale 13.05.2015 14:48
Communicating with Serial Port in C# site:c-sharpcorner.com
Цитировать
 
 
+1 #7 Brig 25.03.2015 07:17
Как организовать работу порта в 9-ти битном режиме?
Цитировать
 
 
+1 #6 Valery 10.01.2015 23:10
Спасибо, разобрался.
Цитировать
 
 
+1 #5 Valery 10.01.2015 20:56
Спасибо, отличная статья. А можно ли управлять сигналами RTS и DTR, и проверять состояние CTS и DSR?

microsin: да, конечно, все это можно. Прогуглите C# SerialPort CTS RTS, информация доступна даже на русском языке.
Цитировать
 
 
+1 #4 G-ray 09.10.2014 22:05
Спасибо!
Цитировать
 
 
0 #3 Владан 27.09.2013 18:25
Спасибо, братуха!!! Отличная статья!
Цитировать
 
 
+2 #2 DmitriyKornet 20.03.2013 18:24
Скажите а чем ограничена вообще скорость такого COM порта?

microsin: для обычного COM-порта максимальная скорость обычно ограничена аппаратно на 115200 бод. Для класса USB CDC (виртуальный COM-порт) скорость ограничена возможностями драйвера.
Цитировать
 
 
0 #1 Alex 03.03.2013 16:21
Отличная статья !!! Спасибо. ;-)
Цитировать
 

Добавить комментарий


Защитный код
Обновить

Top of Page