Последовательная связь с использованием WPF, RS232 и микроконтроллера
Петряев Вячеслав Евгеньевич,
студент кафедры автоматизации производственных процессов Уральского государственного лесотехнического университета.
Эта статья покажет, как использовать последовательную передачу данных через RS232, используя технологию Microsoft WPF. Я не хочу останавливаться на внешнем виде программы, а уделить большее внимание коду. В частности есть ключевые пункты, такие как Dispatcher.Invoke метод и преобразование символов их из ASCII в 16 систему счисления, что является камнями преткновения в последовательной связи с микроконтроллером.
Ключевые слова: WPF, COM порт, RS232, C#.
Keywords: WPF, Serial Port, RS232, C#.
Следует начать с того, что нужно показать, как создать приложение. Чтобы создать проект для приложения:
1. Запустите Visual Studio 2010.
2. В меню File («Файл») выберите команду New («Создать»), а затем команду New Project («Проект»).
Рис. 1. Выбор меню создания проекта.
3. Откроется диалоговое окно New Project («Создание проекта»).
Рис. 2. Диалоговое окно создания проекта
Примечание. В поле со списком Target Framework Version («Целевая версия платформы») по умолчанию выбрано последнее использованное значение. При создании проекта убедитесь в том, что выбрана нужная версия платформы.
Шаг 1. Выберите язык в нашем случае C#, а затем выберите пункт Windows.
Шаг 2. Выберите пункт WPF Application («Приложение WPF»).
Шаг 3. Введите имя проекта.
Совет. Хотя в имени проекта можно использовать пробелы, делать это не рекомендуется.
Шаг 4. Нажмите кнопку ОК, чтобы создать новое приложение WPF.
Присвоим приложению заголовок. Свойство Title находится в верхней левой части хрома окна. [1]
Теперь можно начинать создавать приложение. Код написан просто, с небольшим вниманием на обозначения. Во-первых, начнем с интерфейса. Сперва сделаем 2 ComboBox для выбора номера COM порта и скорости передачи, а так же разместим TextBox для данных, которые нужно отправить, и Richtextbox для отображения полученной информации из COM порта, и пару кнопок для действий.
Первый важный фактор – то, откуда будут получены номера COM портов и скорость. Есть способы, с помощью которых можно получить эти данные от компьютера, но для большего контроля поместим все названия в файл XML по имени CommsData.xml. Эти данные обеспечены “Источником данных” и размещены в предалах Windows.Resource элемента. XPath показывает какой элемент выбран, а x:Key – то, на что я ссылаюсь в коде.
<Window.Resources>
<XmlDataProvider x:Key="ComPorts" Source="CommsData.xml" XPath="/Comms/Ports" />
<XmlDataProvider x:Key="ComSpeed" Source="CommsData.xml" XPath="/Comms/Baud" />
</Window.Resources>
Приведем содержание CommsData.xml.
<Comms>
<Ports>COM1</Ports>
<Baud>921600</Baud>
</Comms>
Так можно дописать сколько нужно строк с нужными названиями COM портов и скоростей.
В каждом элементе ComboBox нужно указать место, откуда он будет брать информацию. В нашем случае это xml файл. Для выбора порта:
<ComboBox Name="Comm_Port_Names” ItemsSource="{Binding Source={StaticResource ComPorts}}"/>
Для выбора скорости:
<ComboBox Name="Baud_Rates" ItemsSource="{Binding Source={StaticResource ComSpeed}}"/>
С интерфейсом закончили и можно приступать к добавлении функциональности.
Во-первых, мы должны добавить ссылку на библиотеку IO.PORTS, которая содержит команды для управления COM портом.
using System.IO.Ports;
Теперь мы можем создать последовательный порт. Для удобства я назвал его serial, но если вам требуется рабоать с несколькими портами, то их названия должны отличаться.
SerialPort serial = new SerialPort();
Есть несколько свойств последовательного порта, которые можно редактировать. Это происходит в рамках исходного кода перед нажатием кнопки Connect. Главное их установить до открытия порта.
serial.PortName = Comm_Port_Names.Text; //Com Port Name
serial.BaudRate = Convert.ToInt32(Baud_Rates.Text);
serial.Handshake = System.IO.Ports.Handshake.None;
serial.Parity = Parity.None;
serial.DataBits = 8;
serial.StopBits = StopBits.One;
serial.ReadTimeout = 200;
serial.WriteTimeout = 50;
Информацию об этих свойствах смотрите в RS232 протоколе. Эти параметры удовлетворяют в большинстве случаев. Есть два важных свойства. ReadTimeout – это свойство устанавливает максимальное времяЮ которое программа читает данные с порта от начала приема. Если сделать его маленьким, то могут возникать ошибки и сообщение может быть укорочено. WriteTimeout – время, отведенное для отправки данных через порт.
Теперь у нас есть последовательный порт. Важно создавать вызов функции каждый раз, когда порт имеет данные для чтения. Для этого создадим события и добавим его к событиям получения данных:
serial.DataReceived+=new SerialDataReceivedEventHandler(Recieve);
Будем записывать данные в строку
String recieved_data;
А затем вызовем функцию этих данных в нашу форму. Что бы это сделать вначале нужно подключить еще одну библиотеку:
using System.Windows.Threading;
private void Recieve(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
{
recieved_data = serial.ReadExisting();
Dispatcher.Invoke(DispatcherPriority.Send,
new UpdateUiTextDelegate(WriteData), recieved_data);
}
Сейчас мы должны использовать Dispatcher.Invoke метод. Его цель проста: главная форма находится под контролем своего потока; когда последовательный порт объявляет о получении данных, создается новый поток, чтобы прочитать эти данные. Этот метод позволяет передать нашу строку из одного потока в другой без ошибок.
Данные могут быть отображены в любом элементе, способном отображать текст. В нашем случае это RichTextBox. Сперва нужно создать абзац.
FlowDocument mcFlowDoc = new FlowDocument();
Paragraph para = new Paragraph();
Вот так выглядит функция для отображения данных:
private void WriteData(string text)
{
para.Inlines.Add(text);
mcFlowDoc.Blocks.Add(para);
Commdata.Document = mcFlowDoc;
}
Отправка данных. Для общения с микроконтроллером данные необходимо преобразовать в 16 систему. Текст, набранный в SerialData TextBox запускает функцию SerialCmdSend.Во-первых нужно проверить открыт ли порт. Во-вторых преобразовать данные в 16 систему.
private void Send_Data(object sender, RoutedEventArgs e)
{
SerialCmdSend(SerialData.Text);
SerialData.Text = "";
}
public void SerialCmdSend(string data)
{
if (serial.IsOpen)
{
try
{
byte[] hexstring = Encoding.ASCII.GetBytes(data);
foreach (byte hexval in hexstring)
{
byte[] _hexval = new byte[] { hexval };
serial.Write(_hexval, 0, 1);
Thread.Sleep(10);
}
}
catch (Exception ex)
{
para.Inlines.Add("Ошибка отправки" + data + "\n" + ex + "\n");
mcFlowDoc.Blocks.Add(para);
Commdata.Document = mcFlowDoc;
}
}
else
{
}
}
Вот что получается в итоге:
Если вы студент и у вас нет Visual studio, то вы можете получить его бесплатно на сайте dreamspark.ru.
Все. Сейчас вы имеете рабочее WPF приложение, способное работать с портом последовательной связи. Имея эти знания, вы можете начать разрабатывать, например, свою SCADA систему.
Литература
1. «Создание первого приложения WPF». - http://msdn.microsoft.com/ru-ru/netframework/ff934691.
2. Библиотека MSDN. - http://msdn.microsoft.com/library/ms123401.
Поступила в редакцию 12.09.2011 г.