Работа с COM портом на C в Windows | Blablacode

C# com-порт получение информации, обработка старт-бита, стоп-бита?

Добрый день!

Прошу помощи в понимании, а может и в решении интересующего меня вопроса.

Сначала обрисую суть поставленного передо мной задания, предупрежу сразу в программировании я, так сказать, очень молодой и это в какой-то степени мой первый проект, который я хочу реализовать на платформе visual studio.

Задача:
Есть некое устройство, которое соединяется с компьютером по RS-485 и передает данные 38400 бит/сек, устройство может передавать пакеты с дискретностью 25 Гц или 50Гц.

Командно-информационный пакет, передаваемый устройством, состоит из 6 блоков, в начале каждого пакета передается маркер начала пакета “0xFF, 0xFF”, затем идут 6 блоков данных в конце пакета передается 2-х байтовое поле .В конце идет бит четности (формируется при выполнении операции “Исключающее ИЛИ “четное число логических единиц в байте должно соответствовать биту чётности 0 ) и 2 стоповых бита.

— Длина одного пакета 52 байта.
— Время паузы между двумя пакетами не более 2-х бит.

Характеристики блока данных:
— Длина блока 8 байт.
— Далее 4 значения по 2 байта

Собственно вопрос, который меня интересует: как это все правильно организовать.

Первым делом я кинулся искать как работать с COM-портом, наткнулся на пространство имен:

using System.IO;
using System.IO.Ports;

Ну, и первым делом, как мудрый ВАСЯ, определяю доступные порты в системе и вывожу их в combobox.

private void Form1_Load(object sender, EventArgs e)
        {
            //чтение портов доступных в системе
            string[] ports = SerialPort.GetPortNames();
            //Очистка содержимого бокса
            comboBox1.Items.Clear();
            //Добавление найденных портов в бокс
            comboBox1.Items.AddRange(ports);
        }

Далее, по кнопке старт я запускаю само чтение данных приходящих с устройства. Вот тут для меня начинается дремучий лес.

private void button1_Click(object sender, EventArgs e)
        {
//выбранный порт
            Object selectedItem = comboBox1.SelectedItem;

//Создаю объект port1  с параметрами.
//selectedItem.ToString() - порт к которому подключено устройство.
//9600 скорость передачи данных в порт, как я понял 9600 бод - 38400 бит/сек.
//Parity.None бит четности установил в отсутствие так как пока не сильно понимаю что с ним делать об этом далее.
//416  бит данных т.е. 8*52 = 416.
//один стоп бит
            SerialPort port1 = new SerialPort(selectedItem.ToString(), 9600, Parity.None, 416, StopBits.One);
//Открываю порт.
            port1.Open();
//тут я писал разного рода код для вывода полученных данных в текстовое поле но получал один бред.
        }

Теперь вопрос:
Как я понимаю эту проблему — в порт записываются данные, переданные устройством, а мне их необходимо считать.
Как мне правильно дождаться начала пакета “0xFF,0xFF”, чтобы после это принимать оставшуюся часть пакета, или прием стоит начать после получения конца предыдущего пакета?

Если у кого-то есть подобные коды обработки ожидания начала или конца пакетов и нет желания объяснять, просьба скинуть участки кода буду разбираться сам.

После правильного начала приема пакетов мне их необходимо рассортировать, ничего лучше чем засунуть принятый пакет в переменную последовательности байт я не нашел, точнее это можно сказать единственное, что улеглось в моей голове.

private void button1_Click(object sender, EventArgs e)
        {
            Object selectedItem = comboBox1.SelectedItem;
            SerialPort port1 = new SerialPort(selectedItem.ToString(), 9600, Parity.None, 416, StopBits.One);
            port1.Open();
//создаю массив размером 52 т.е на запись 52 байт переданного с устройства пакетов
            byte[] data1 = new byte[52];
//Ну и запускаю команду чтения в массив data1
            port.Read(data1, 0, data1.Length);
            int databyte = port.ReadByte();
        }

Но приходит мне какая то ересь…
Правильно ли я понимаю этот процесс чтения из com-порта?

Просьба пнуть, ткнуть, дать леща или помочь в решении данного задания кому как будет угодно.

Кто знает что почитать конкретно по этому вопросу, где доступно написано или есть подобные решения просьба скинуть почитать что бы лучше разбираться.

Пока как то так. Буду признателен любой помощи, подсказке, куску кода.

Работа с com портом на c в windows | blablacode

Последовательные порты полюбились разработчикам за их простоту в обслуживании и использовании.

И конечно же писать в консоль терминальной программы это всё хорошо, но хочеться своё приложение, которое по нажатии клавиши на экране выполняет нужные вам действия 😉

В этой статье опишу как работать с com портом на языке Си .

Решение простое, но почемуто рабочий пример найден был не сразу. За сим сохраняю его тут.

Конечно вы можете использовать кроссплатформенные решения вроде QSerial – библиотеки в составе Qt, я наверное так и сделаю, но в будующем. Сейчас же речь о “чистом” виндовском C . Писать будем в Visual Studio. У меня 2021, хотя роли это никакой не играет…

Создаём новый консольный Win32 проект.

Инклудим header файлы:

#include <windows.h>
#include <iostream>
using namespace std;

Объявляем обработчик com порта:

HANDLE hSerial;

Я делаю это глобально, чтобы не заморачиваться с указателями при передаче его в функции.

Дальше начинаем формировать функцию main:

int _tmain(int argc, _TCHAR* argv[])
{

Терпеть не могу виндовский стиль программирования. Обозвали всё посвоему и сидят радуются…

Теперь магия объявления строки с именем порта. Дело в том, что char оно преобразовывать само не умеет.

LPCTSTR sPortName = L"COM1";  

Работа с последоавательными портами в Windows проходит как с файлом. Открываем первый ком порт для записи/чтения:

hSerial = ::CreateFile(sPortName,GENERIC_READ | GENERIC_WRITE,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);

Проверяем работоспособность:

if(hSerial==INVALID_HANDLE_VALUE)
{
		if(GetLastError()==ERROR_FILE_NOT_FOUND)
	{
		cout << "serial port does not exist.n";
	}
	cout << "some other error occurred.n";
}

Теперь нужно настроить параметры соединения:

DCB dcbSerialParams = {0};
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
if (!GetCommState(hSerial, &dcbSerialParams))
{
	cout << "getting state errorn";
}
dcbSerialParams.BaudRate=CBR_9600;
dcbSerialParams.ByteSize=8;
dcbSerialParams.StopBits=ONESTOPBIT;
dcbSerialParams.Parity=NOPARITY;
if(!SetCommState(hSerial, &dcbSerialParams))
{
	cout << "error setting serial port staten";
}

На msdn советуют сначала получить параметры, а затем менять их. Мы ещё только учимся, поэтому делаем как просят.

:/>  Изменение частоты повтора клавиатуры и задержки повтора - инструменты для устранения ошибок

Теперь объявим строку, которую будем передавать и переменные необходимые для этого:

char data[] = "Hello from C  ";  // строка для передачи
DWORD dwSize = sizeof(data);   // размер этой строки
DWORD dwBytesWritten;    // тут будет количество собственно переданных байт

Посылаем строку. Напомню, что пример простейший, поэтому никаких особо проверок я не делаю:

BOOL iRet = WriteFile (hSerial,data,dwSize,&dwBytesWritten,NULL);

Также я решил вывести для контроля размер строки и количество отосланных байт:

cout << dwSize << " Bytes in string. " << dwBytesWritten << " Bytes sended. " << endl;

В конце программы делаем бесконечный цикл чтения данных:

	while(1)
	{
		ReadCOM();
	}
	return 0;
}

Теперь функция чтения:

void ReadCOM()
{
      DWORD iSize;
      char sReceivedChar;
      while (true)
      {
            ReadFile(hSerial, &sReceivedChar, 1, &iSize, 0);  // получаем 1 байт
			if (iSize > 0)   // если что-то принято, выводим
				cout << sReceivedChar;
      }
}

Вот собственно и весь пример.

Я создал виртуальный com порт. И слал из COM1 в COM2:

Демонстрация работы c COM портом на С

Из нашей программы было отправлено “Hello from C “, а из терминала “hello how2.org.ua”.

Скачать пример.

Работа с последовательными портами – работа с com портами

В настоящее время существует множество устройств, которые обмениваются с компьютером информацией через последовательный порт (COM1, COM2) по протоколу RS-232. Причем такие устройства разрабатывают до сих пор и, я уверен, будут разрабатывать и в дальнейшем. Ведь несмотря на недостатки такой связи: медленная скорость обмена информацией, ограничение на длину соединительных линий — существует и немало достоинств: программная поддержка протокола RS-232 и ему подобных многими периферийными устройствами, специализированными микросхемами, низкая стоимость, минимальное количество соединительных проводов, простота.

Но, как это ни странно, информации по работе с последовательными портами в программах под Win32 очень мало. Материал этой статьи основан на статье Олега Титова Работа с коммуникационными портами (COM и LPT) в программах для Win32 — ознакомиться с ней можно по адресу. Автором очень подробно описаны функции для работы с коммуникационными портами, основное внимание уделено синхронному обмену информацией. Мы же рассмотрим вариант обмена между компьютером и периферийным устройством в асинхронном режиме (как правило, используемом наиболее часто) — причем для простейшего соединения по трем проводам, без использования управляющих сигналов. Таким же образом можно организовать связь между двумя компьютерами (хотя бы для проверки работы своей программы).

Соединение COM-COM по трем проводам

Начнем с главного: с последовательными портами в Win32 работают как с файлами. Причем используют только функции API Win32.

Начинается работа с открытия порта как файла, причем для
асинхронного режима ввода-вывода возможен только один вариант:

HANDLE handle = CreateFile(«COM1», GENERIC_READ |
GENERIC_WRITE, NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED,
NULL);

Других вариантов быть не может, поэтому не будем рассматривать
параметры этой функции подробно, единственное, что можно сделать —
это заменить “COM1” на “COM2”. Больше последовательных портов на
компьютере, как правило, нет. При успешном открытии порта функция
возвращает дескриптор handle, с которым и будем работать в
дальнейшем. При неудачном открытии порта функция вернет значение
INVALID_HANDLE_VALUE.

Настройка порта

Получив доступ к порту, необходимо его настроить — задать
скорость обмена, формат данных, параметры четности и т. д. Основные
параметры последовательного порта задают структуры DCB и
COMMTIMEOUTS. Структура DCB содержит основные параметры порта и,
кроме этого, довольно много специфических полей.

typedef struct _DCB {

DWORD DCBlength; // sizeof(DCB)

DWORD BaudRate; // current baud rate

DWORD fBinary:1; // binary mode, no EOF check

DWORD fParity:1; // enable parity checking

DWORD fOutxCtsFlow:1; // CTS output flow control

DWORD fOutxDsrFlow:1; // DSR output flow control

DWORD fDtrControl:2; // DTR flow control type

DWORD fDsrSensitivity:1; // DSR sensitivity

DWORD fTXContinueOnXoff:1; // XOFF continues Tx

DWORD fOutX:1; // XON/XOFF out flow control

DWORD fInX:1; // XON/XOFF in flow control

DWORD fErrorChar:1; // enable error replacement

DWORD fNull:1; // enable null stripping

DWORD fRtsControl:2; // RTS flow control

DWORD fAbortOnError:1; // abort reads/writes on error

DWORD fDummy2:17; // reserved

WORD wReserved; // not currently used

WORD XonLim; // transmit XON threshold

WORD XoffLim; // transmit XOFF threshold

BYTE ByteSize; // number of bits/byte, 4-8

BYTE Parity; // 0-4=no,odd,even,mark,space

BYTE StopBits; // 0,1,2 = 1, 1.5, 2

char XonChar; // Tx and Rx XON character

char XoffChar; // Tx and Rx XOFF character

char ErrorChar; // error replacement character

char EofChar; // end of input character

char EvtChar; // received event character

WORD wReserved1; // reserved; do not use

} DCB;

Мы рассмотрим назначение только некоторых основных полей этой
структуры, используемых для нашего случая ввода-вывода, так как
многие поля можно заполнить значениями “по умолчанию”, пользуясь
функцией GetCommState:

BOOL GetCommState(

HANDLE hFile,

LPDCB lpDCB

);

Таким образом, нет необходимости вникать во все тонкости
структуры. После этого некоторые поля DCB все же придется заполнить
вручную, а именно:

:/>  Вылетают игры на Рабочий стол Windows 10 без ошибок: 6 причин и что делать

BaudRate — скорость передачи данных. Возможно указание
следующих констант: CBR_110, CBR_300, CBR_600, CBR_1200, CBR_2400,
CBR_4800, CBR_9600, CBR_14400, CBR_19200, CBR_38400, CBR_56000,
CBR_57600, CBR_115200, CBR_128000, CBR_256000. Можно просто указать
соответствующее число, например 9600, но предпочтительнее все-таки
пользоваться символическими константами.

ByteSize — определяет число информационных бит в
передаваемых и принимаемых байтах. Может принимать значение 4, 5, 6,
7, 8.

Parity — определяет выбор схемы контроля четности. Данное
поле должно содержать одно из следующих значений:

  • EVENPARITY — дополнение до четности;
  • MARKPARITY — бит четности всегда равен 1;
  • NOPARITY — бит четности отсутствует;
  • ODDPARITY — дополнение до нечетности;
  • SPACEPARITY — Бит четности всегда 0.

StopBits — задает количество стоповых бит. Поле может
принимать следующие значения:

  • ONESTOPBIT — один стоповый бит;
  • ONE5STOPBIT — полтора стоповых бита (практически не
    используется);
  • TWOSTOPBIT — два стоповых бита.

После того как все поля структуры DCB заполнены, необходимо
произвести конфигурирование порта, вызвав функцию SetCommState:

BOOL SetCommState(

HANDLE hFile,

LPDCB lpDCB

);

В случае успешного завершения функция вернет отличное от нуля
значение, а в случае ошибки — нуль.

Второй обязательной структурой для настройки порта является
структура COMMTIMEOUTS. Она определяет параметры временных задержек
при приеме-передаче. Вот описание этой структуры:

typedef struct _COMMTIMEOUTS {

DWORD ReadIntervalTimeout;

DWORD ReadTotalTimeoutMultiplier;

DWORD ReadTotalTimeoutConstant;

DWORD WriteTotalTimeoutMultiplier;

DWORD WriteTotalTimeoutConstant;

} COMMTIMEOUTS,*LPCOMMTIMEOUTS;

Поля структуры COMMTIMEOUTS имеют следующие значения:

  • ReadIntervalTimeout — максимальное временной промежуток
    (в миллисекундах), допустимый между двумя считываемыми с
    коммуникационной линии последовательными символами. Во время
    операции чтения временной период начинает отсчитываться с момента
    приема первого символа. Если интервал между двумя
    последовательными символами превысит заданное значение, операция
    чтения завершается и все данные, накопленные в буфере, передаются
    в программу. Нулевое значение данного поля означает, что данный
    тайм-аут не используется.
  • ReadTotalTimeoutMultiplier — задает множитель (в
    миллисекундах), используемый для вычисления общего тайм-аута
    операции чтения. Для каждой операции чтения данное значение
    умножается на количество запрошенных для чтения символов.
  • ReadTotalTimeoutConstant — задает константу (в
    миллисекундах), используемую для вычисления общего тайм-аута
    операции чтения. Для каждой операции чтения данное значение
    плюсуется к результату умножения ReadTotalTimeoutMultiplier на
    количество запрошенных для чтения символов. Нулевое значение полей
    ReadTotalTimeoutMultiplier и ReadTotalTimeoutConstant означает,
    что общий тайм-аут для операции чтения не используется.
  • WriteTotalTimeoutMultiplier — задает множитель (в
    миллисекундах), используемый для вычисления общего тайм-аута
    операции записи. Для каждой операции записи данное значение
    умножается на количество записываемых символов.
  • WriteTotalTimeoutConstant — задает константу (в
    миллисекундах), используемую для вычисления общего тайм-аута
    операции записи. Для каждой операции записи данное значение
    прибавляется к результату умножения WriteTotalTimeoutMultiplier на
    количество записываемых символов. Нулевое значение полей
    WriteTotalTimeoutMultiplier и WriteTotalTimeoutConstant означает,
    что общий тайм-аут для операции записи не используется.

Немного поподробнее о тайм-аутах. Пусть мы считываем из порта 50
символов со скоростью 9 600 бит/с. Если при этом используется 8 бит
на символ, дополнение до четности и один стоповый бит, то на один
символ в физической линии приходится 11 бит (включая стартовый бит).
Значит, 50 символов на скорости 9 600 бит/с будут приниматься

50×11/9600=0,0572916 с

или примерно 57,3 миллисекунды, при условии нулевого интервала
между приемом последовательных символов. Если же интервал между
символами составляет примерно половину времени передачи одного
символа, т. е. 0,5 миллисекунд, то время приема будет

50×11/9600 49×0,0005=0,0817916 с

или примерно 82 миллисекунды. Если в процессе чтения прошло более
82 миллисекунд, то мы вправе предположить, что произошла ошибка в
работе внешнего устройства и можем прекратить считывание, тем самым
избежав зависания программы. Это и есть общий тайм-аут операции
чтения. Аналогично существует и общий тайм-аут операции записи.

Формула для вычисления общего тайм-аута операции, например,
чтения, выглядит так:

NumOfChar x ReadTotalTimeoutMultiplier
ReadTotalTimeoutConstant

где NumOfChar — число символов, запрошенных для операции чтения.

В нашем случае тайм-ауты записи можно не использовать и
установить их равными нулю.

После заполнения структуры COMMTIMEOUTS, необходимо вызвать
функцию установки тайм-аутов:

BOOL SetCommTimeouts(

HANDLE hFile,

LPCOMMTIMEOUTS lpCommTimeouts

);

Поскольку операции передачи-приема ведутся на малой скорости,
используется буферизация данных. Для задания размера буфера приема и
передачи необходимо воспользоваться функцией:

BOOL SetupComm(

HANDLE hFile,

DWORD dwInQueue,

DWORD dwOutQueue

);

Допустим, вы обмениваетесь с внешним устройством пакетами
информации размером 1024 байта, тогда разумным размером буферов
будет значение 1200. Функция SetupComm интересна тем, что она может
просто принять ваши размеры к сведению, внеся свои коррективы, либо
вообще отвергнуть предложенные вами размеры буферов — в таком случае
эта функция завершится ошибкой.

Приведу пример открытия и конфигурирования последовательного
порта COM1. Для краткости — без определения ошибок. В данном примере
порт открывается для работы со скоростью 9 600 бит/c, используется 1
стоповый бит, бит четности не используется:

#include

. . . . . . . . . .

HANDLE handle;

COMMTIMEOUTS CommTimeOuts;

DCB dcb;

handle = CreateFile(«COM1», GENERIC_READ | GENERIC_WRITE,
NULL, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);

SetupComm(handle, SizeBuffer, SizeBuffer);

GetCommState(handle, &dcb);

:/>  Как отрегулировать громкость на компьютере windows 7 и что делать и как исправить если пропал звук на компьютере windows 7

dcb.BaudRate = CBR_9600;

dcb.fBinary = TRUE;

dcb.fOutxCtsFlow = FALSE;

dcb.fOutxDsrFlow = FALSE;

dcb.fDtrControl = DTR_CONTROL_HANDSHAKE;

dcb.fDsrSensitivity = FALSE;

dcb.fNull = FALSE;

dcb.fRtsControl = RTS_CONTROL_DISABLE;

dcb.fAbortOnError = FALSE;

dcb.ByteSize = 8;

dcb.Parity = NOPARITY;

dcb.StopBits = 1;

SetCommState(handle, &dcb);

CommTimeOuts.ReadIntervalTimeout= 10;

CommTimeOuts.ReadTotalTimeoutMultiplier = 1;

// значений этих тайм – аутов вполне хватает для уверенного
приема

// даже на скорости 110 бод

CommTimeOuts.ReadTotalTimeoutConstant = 100;

// используется в данном случае как время ожидания
посылки

CommTimeOuts.WriteTotalTimeoutMultiplier = 0;

CommTimeOuts.WriteTotalTimeoutConstant = 0;

SetCommTimeouts(handle, &CommTimeOuts);

PurgeComm(handle, PURGE_RXCLEAR);

PurgeComm(handle, PURGE_TXCLEAR);

После открытия порта первым делом необходимо сбросить его, так
как в буферах приема и передачи может находиться “мусор”. Поэтому в
конце примера мы применили ранее не известную нам функцию
PurgeComm:

BOOL PurgeComm(

HANDLE hFile,

DWORD dwFlags

);

Эта функция может выполнять две задачи: очищать очереди
приема-передачи в драйвере или же завершать все операции
ввода-вывода. Какие именно действия выполнять, задается другим
параметром:

  • PURGE_TXABORT — немедленно прекращает все операции
    записи, даже если они не завершены;
  • PURGE_RXABORT — немедленно прекращает все операции
    чтения, даже если они не завершены;
  • PURGE_TXCLEAR — очищает очередь передачи в драйвере;
  • PURGE_RXCLEAR — очищает очередь приема в
    драйвере.
    Эти значения можно комбинировать с помощью побитовой
    операции OR. Очищать буферы рекомендуется также после ошибок
    приема-передачи и после завершения работы с портом.

Настало время для рассмотрения непосредственно операций
чтения-записи для порта. Как и для работы с файлами, используются
функции ReadFile и WriteFile. Вот их прототипы:

BOOL ReadFile(

HANDLE hFile,

LPVOID lpBuffer,

DWORD nNumOfBytesToRead,

LPDWORD lpNumOfBytesRead,

LPOVERLAPPED lpOverlapped

);

BOOL WriteFile(

HANDLE hFile,

LPVOID lpBuffer,

DWORD nNumOfBytesToWrite,

LPDWORD lpNumOfBytesWritten,

LPOVERLAPPED lpOverlapped

);

Рассмотрим назначение параметров этих функций:

  • hFile — описатель открытого файла коммуникационного
    порта;
  • lpBuffer — адрес буфера. Для операции записи данные из
    этого буфера будут передаваться в порт. Для операции чтения в этот
    буфер будут помещаться принятые из линии данные;
  • nNumOfBytesToRead, nNumOfBytesToWrite — число ожидаемых
    к приему или предназначенных для передачи байт;
  • nNumOfBytesRead, nNumOfBytesWritten — число фактически
    принятых или переданных байт. Если принято или передано меньше
    данных, чем запрошено, то для дискового файла это свидетельствует
    об ошибке, а для коммуникационного порта — совсем не обязательно.
    Причина в тайм-аутах.
  • LpOverlapped — адрес структуры OVERLAPPED, используемой
    для асинхронных операций.

В случае нормального завершения функции возвращают значение,
отличное от нуля, в случае ошибки — нуль.

Приведу пример операции чтения и записи:

#include

…………..

DWORD numbytes, numbytes_ok, temp;

COMSTAT ComState;

OVERLAPPED Overlap;

char buf_in[6] = «Hello!»;

numbytes = 6;

ClearCommError(handle, &temp, &ComState);

// если temp не равно нулю, значит — порт в состоянии
ошибки

if(!temp) WriteFile(handle, buf_in, numbytes,
&numbytes_ok, &Overlap);

ClearCommError(handle, &temp, &ComState);

if(!temp) ReadFile(handle, buf_in, numbytes, &numbytes_ok,
&Overlap);

// в переменной numbytes_ok содержится реальное число
переданных-

// принятых байт

В этом примере мы использовали две неизвестные нам ранее
структуры COMSTAT и OVERLAPPED, а также функцию ClearCommError. Для
нашего случая связи “по трем проводам” структуру OVERLAPPED можно не
рассматривать (просто использовать, как в примере). Прототип функции
ClearCommError имеет вид:

BOOL ClearCommError(

HANDLE hFile,

LPDWORD lpErrors,

LPCOMSTAT lpStat

);

Эта функция сбрасывает признак ошибки порта (если таковая имела
место) и возвращает информацию о состоянии порта в структуре
COMSTAT:

typedef struct _COMSTAT

DWORD fCtsHold:1;

DWORD fDsrHold:1;

DWORD fRlsdHold:1;

DWORD fXoffHold:1;

DWORD fXoffSent:1;

DWORD fEof:1;

DWORD fTxim:1;

DWORD fReserved:25;

DWORD cbInQue;

DWORD cbOutQue;

} COMSTAT, *LPCOMSTAT;

Нам могут пригодиться два поля этой структуры:

  • CbInQue — число символов в приемном буфере. Эти символы
    приняты из линии, но еще не считаны функцией ReadFile;
  • CbOutQue — число символов в передающем буфере. Эти
    символы еще не переданы в линию.

Остальные поля данной структуры содержат информацию об
ошибках.

И наконец, после завершения работы с портом его следует закрыть.
Закрытие объекта в Win32 выполняет функция CloseHandle:

BOOL CloseHandle(

HANDLE hObject

};

На нашем сайте вы можете найти полный текст класса для работы с
последовательным портом в асинхронном режиме “по трем проводам”, а
также пример программы с использованием этого класса. Все это
написано под Builder С , но, поскольку используются только функции
API Win32, текст программы легко изменить под любой компилятор С .
Возможно также, что класс написан не совсем “по правилам” — прошу
извинить, автор не является “правильным” программистом и пишет так,
как ему удобно J .

Если у вас возникли вопросы по поводу использования функций, рассмотренных выше, вы всегда сможете обратиться к справочной информации по Win32. А если возникнет необходимость более полно использовать последовательные порты (например, использовать различные управляющие сигналы) прочтите статью Олега Титова Работа с коммуникационными портами (COM и LPT) в программах для Win32

Оставьте комментарий