Форум программистов, компьютерный форум, киберфорум
C++ Builder
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.87/120: Рейтинг темы: голосов - 120, средняя оценка - 4.87
-11 / 0 / 1
Регистрация: 02.09.2015
Сообщений: 76

Работа с COM-портом в асинхронном режиме

02.09.2015, 08:40. Показов 23658. Ответов 19
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Пробую разобраться с работой COM порта. Получилось запустить в синхронном режиме. Стало ясно, что в этом режиме, вызывая функцию чтения данных ReadFile(), я не знаю заранее есть ли данные для чтения. Можно циклически проверять их наличие, но это приводит к дополнительным расходам времени ЦП. Поэтому на практике часто удобней использовать асинхронный режим.
Я нашел вот такую статью http://piclist.ru/S-COM-THREAD... US.html#22 и решил попробовать написать похожую программу в C++ Builder 6. Код такой:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
//---------------------------------------------------------------------------
 
 
 
#include <vcl.h>
#pragma hdrstop
 
#include <io.h>         // для работы с файлами
#include <fcntl.h>      // для работы с файлами
#include <sys\stat.h>   // для работы с файлами
 
 
#include "COMport.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
 
 
 
 
//-Объявления глобальных переменных-----------------------------------------------------------------------------------------
#define BUFSIZE 255     //ёмкость буфера
 
unsigned char bufrd[BUFSIZE], bufwr[BUFSIZE]; //приёмный и передающий буферы
 
HANDLE COMport;     //дескриптор порта
 
//структура OVERLAPPED необходима для асинхронных операций, при этом для операции чтения и записи нужно объявить разные структуры
//эти структуры необходимо объявить глобально, иначе программа не будет работать правильно
OVERLAPPED overlapped;      //будем использовать для операций чтения (см. поток ReadThread)
OVERLAPPED overlappedwr;        //будем использовать для операций записи (см. поток WriteThread)
 
int handle;                 //дескриптор для работы с файлом с помощью библиотеки <io.h>
 
bool fl=0;  //флаг, указывающий на успешность операций записи (1 - успешно, 0 - не успешно)
 
unsigned long counter;  //счётчик принятых байтов, обнуляется при каждом открытии порта
 
 
 
char *buf_out;// = "Test string";
char *buf_in;
DWORD bc;
DCB *dcb;
 
 
char NumOut = 0;
char NumIn = 0;
 
String portname;
 
//--------------------------------------------------------------------------------------------------------------------------------
 
 
 
 
//-Объявления потоков---------------------------------------------------------------------------------------------------------------
HANDLE reader;  //дескриптор потока чтения из порта
HANDLE writer;  //дескриптор потока записи в порт
 
DWORD WINAPI ReadThread(LPVOID);
DWORD WINAPI WriteThread(LPVOID);
//--------------------------------------------------------------------------------------------------------------------------------
 
 
 
 
 
//-Поток ReadThead--------------------------------------------------------------------------------------------------------------------
 
void ReadPrinting(void);
 
//---------------------------------------------------------------------------
 
//главная функция потока, реализует приём байтов из COM-порта
DWORD WINAPI ReadThread(LPVOID)
{
COMSTAT comstat;        // структура текущего состояния порта, в данной программе используется для определения количества принятых в порт байтов
DWORD btr, temp, mask, signal;  // переменная temp используется в качестве заглушки
 
overlapped.hEvent = CreateEvent(NULL, true, true, NULL); // создать сигнальный объект-событие для асинхронных операций
SetCommMask(COMport, EV_RXCHAR);                     // установить маску на срабатывание по событию приёма байта в порт
while(1)                         // пока поток не будет прерван, выполняем цикл
  {
  WaitCommEvent(COMport, &mask, &overlapped);                   //ожидать события приёма байта (это и есть перекрываемая операция)
  signal = WaitForSingleObject(overlapped.hEvent, INFINITE);    //приостановить поток до прихода байта
 
  if(signal == WAIT_OBJECT_0)                       //если событие прихода байта произошло
    {
    if(GetOverlappedResult(COMport, &overlapped, &temp, true)) //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent
      if((mask & EV_RXCHAR)!=0)                 //если произошло именно событие прихода байта
        {
        ClearCommError(COMport, &temp, &comstat); // нужно заполнить структуру COMSTAT
        btr = comstat.cbInQue;                    // и получить из неё количество принятых байтов
        if(btr)                                   // если действительно есть байты для чтения
          {
          ReadFile(COMport, bufrd, btr, &temp, &overlapped); // прочитать байты из порта в буфер программы
          counter+=btr;                                      // увеличиваем счётчик байтов
          ReadPrinting();                                    // вызываем функцию для вывода данных на экран и в файл
          }
        }
    }
  }
}
 
 
//---------------------------------------------------------------------------
 
//выводим принятые байты на экран
void ReadPrinting()
{
Form1->MemoOut->Lines->Add((char*)bufrd);    //выводим принятую строку в Memo
memset(bufrd, 0, BUFSIZE);          //очистить буфер (чтобы данные не накладывались друг на друга)
 
}
 
 
 
 
 
//-Поток WriteThead-----------------------------------------------------------------------------------------------------------------------
 
//главная функция потока, выполняет передачу байтов из буфера в COM-порт
DWORD WINAPI WriteThread(LPVOID)
{
 
DWORD temp, signal; //temp - переменная-заглушка
 
overlappedwr.hEvent = CreateEvent(NULL, true, true, NULL);        //создать событие
while(1)
  {
  WriteFile(COMport, bufwr, strlen(bufwr), &temp, &overlappedwr);  //записать байты в порт (перекрываемая операция!)
  signal = WaitForSingleObject(overlappedwr.hEvent, INFINITE);    //приостановить поток, пока не завершится перекрываемая операция WriteFile
 
  if((signal == WAIT_OBJECT_0) && (GetOverlappedResult(COMport, &overlappedwr, &temp, true))) //если операция завершилась успешно
    {
    Form1->StatusBar->Panels->Items[0]->Text  = "Передача прошла успешно"; //вывести сообщение об этом в строке состояния
    }
 
  else
    {
    Form1->StatusBar->Panels->Items[0]->Text  = "Ошибка передачи"; // иначе вывести в строке состояния сообщение об ошибке
    }
 
  SuspendThread(writer);
 
  }
}
//-----------------------------------------------------------------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
void __fastcall TForm1::ConnectBtnClick(TObject *Sender)
{
COMMTIMEOUTS timeouts;
 
 
//-Открытие порта----------------------------------------------------------------------------------------------------------------
portname = ChoiceCOM->Text;
COMport=CreateFile(portname.c_str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
 //здесь:
 // - portname.c_str() - имя порта в качестве имени файла,
 //   c_str() преобразует строку типа String в строку в виде массива типа char, иначе функция не примет
 // - GENERIC_READ | GENERIC_WRITE - доступ к порту на чтение/записть
 // - 0 - порт не может быть общедоступным (shared)
 // - NULL - дескриптор порта не наследуется, используется дескриптор безопасности по умолчанию
 // - OPEN_EXISTING - порт должен открываться как уже существующий файл
 // - FILE_FLAG_OVERLAPPED - этот флаг указывает на использование асинхронных операций
 // - NULL - указатель на файл шаблона не используется при работе с портами
 
if(COMport==INVALID_HANDLE_VALUE)
  {
  MemoOut->Lines-> Add("Не удалось открыть последовательный порт.");
  return;
  }
//-------------------------------------------------------------------------------------------------------------------------------------------
 
 
 
//-Инициализация порта----------------------------------------------------------------------------------------------------------------
dcb=(DCB*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(DCB)); // Выделение области памяти для DCB из кучи и заполнение этой области нулями.
 
if(!GetCommState(COMport, dcb)) // Эта функция заполняет DCB информацией о текущем состоянии устройства, точнее о его настройках.
  {// Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
  HeapFree(GetProcessHeap(),0,dcb); // Освобождение памяти из кучи.
  CloseHandle(COMport);
  MemoOut->Text  = "Не удалось считать DCB";
  return;
  }
 
//-Инициализация структуры DCB.
dcb->BaudRate = StrToInt(ChoiceSpeed->Text); // задаём скорость передачи 115200 бод
dcb->fBinary = TRUE;                         // включаем двоичный режим обмена
dcb->fOutxCtsFlow = FALSE;                   // выключаем режим слежения за сигналом CTS
dcb->fOutxDsrFlow = FALSE;                   // выключаем режим слежения за сигналом DSR
dcb->fDtrControl = DTR_CONTROL_DISABLE;      // отключаем использование линии DTR
dcb->fDsrSensitivity = FALSE;                // отключаем восприимчивость драйвера к состоянию линии DSR
dcb->fNull = FALSE;//TRUE                    // разрешить приём нулевых байтов
dcb->fRtsControl = RTS_CONTROL_DISABLE;      // отключаем использование линии RTS
dcb->fAbortOnError = FALSE;                  // отключаем остановку всех операций чтения/записи при ошибке
dcb->ByteSize = 8;                           // задаём 8 бит в байте
dcb->Parity = 0;                             // отключаем проверку чётности
dcb->StopBits = 0;                           // задаём один стоп-бит
//dcb->DCBlength=sizeof(DCB);
 
//-Загрузить структуру DCB в порт.
if(!SetCommState(COMport, dcb))
  {// Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
  HeapFree(GetProcessHeap(),0,dcb); // Освобождение памяти из кучи.
  CloseHandle(COMport);
  MemoOut->Text = "Не удалось установить DCB.";
  return;
  }
 
//-Установка таймаутов.
timeouts.ReadIntervalTimeout = 0;     // таймаут между двумя символами
timeouts.ReadTotalTimeoutMultiplier = 0;  // общий таймаут операции чтения
timeouts.ReadTotalTimeoutConstant = 0;    // константа для общего таймаута операции чтения
timeouts.WriteTotalTimeoutMultiplier = 0; // общий таймаут операции записи
timeouts.WriteTotalTimeoutConstant = 0;   // константа для общего таймаута операции записи
 
//-Загрузить структуру таймаутов в порт.
if(!SetCommTimeouts(COMport, &timeouts))
  {// Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
  CloseHandle(COMport);
  MemoOut->Text = "Не удалось установить тайм-ауты.";
  return;
  }
 
//-Установить размеры очередей приёма и передачи
//  SetupComm(COMport,2000,2000);
//---------------------------------------------------------------------------------------------------------------------------------------------
 
 
PurgeComm(COMport,PURGE_RXCLEAR); // Очистка буферов порта.
 
ConnectBtn->Enabled = false;
DisconnectBtn->Enabled = true;
 
 
 
//-Создание потоков на WINAPI:
 
// Cоздаём поток чтения, который сразу начнёт выполняться (предпоследний параметр = 0).
reader = CreateThread(NULL, 0, ReadThread, NULL, 0, NULL);
 
// Cоздаём поток записи в остановленном состоянии (предпоследний параметр = CREATE_SUSPENDED).
writer = CreateThread(NULL, 0, WriteThread, NULL, CREATE_SUSPENDED, NULL);
 
 
counter = 0;
 
//------------------------------------------------------------------------------------------------------------------------------------------------------
}
 
 
 
 
 
//--------------------------------------------------------------------------------------------------------------------------------------------
void __fastcall TForm1::DisconnectBtnClick(TObject *Sender)
{
 
//Примечание: так как при прерывании потоков, созданных с помощью функций WinAPI, функцией TerminateThread
//        поток может быть прерван жёстко, в любом месте своего выполнения, то освобождать дескриптор
//        сигнального объекта-события, находящегося в структуре типа OVERLAPPED, связанной с потоком,
//        следует не внутри кода потока, а отдельно, после вызова функции TerminateThread.
//        После чего нужно освободить и сам дескриптор потока.
 
if(writer)      //если поток записи работает, завершить его; проверка if(writer) обязательна, иначе возникают ошибки
  {
  TerminateThread(writer,0);
  CloseHandle(overlappedwr.hEvent); //нужно закрыть объект-событие
  CloseHandle(writer);
  }
 
if(reader)         //если поток чтения работает, завершить его; проверка if(reader) обязательна, иначе возникают ошибки
  {
  TerminateThread(reader,0);
  CloseHandle(overlapped.hEvent);   //нужно закрыть объект-событие
  CloseHandle(reader);
  }
 
CloseHandle(COMport);                  //закрыть порт
COMport=0;              //обнулить переменную для дескриптора порта
 
ConnectBtn->Enabled = true;
DisconnectBtn->Enabled = false;
 
NumIn=0;
NumOut=0;
}
//--------------------------------------------------------------------------------------------------------------------------------
 
 
 
 
//---------------------------------------------------------------------------
void __fastcall TForm1::SendBtnClick(TObject *Sender)
{
memset(bufwr,0,BUFSIZE);             // очистить программный передающий буфер, чтобы данные не накладывались друг на друга
PurgeComm(COMport, PURGE_TXCLEAR);   // очистить передающий буфер порта
strcpy(bufwr,MemoOut->Text.c_str()); // занести в программный передающий буфер строку из Edit1
ResumeThread(writer);                // активировать поток записи данных в порт
}
//---------------------------------------------------------------------------

При запуске программы, отправка вроде работает, но на входе ничего нет. Мне кажется, что ошибка где-то в функции DWORD WINAPI ReadThread(LPVOID), а именно не выполняется условие if(signal == WAIT_OBJECT_0). Подскажите, где я накосячил?
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
02.09.2015, 08:40
Ответы с готовыми решениями:

COM-порт в асинхронном режиме
Настраиваю COM-порт для работы в асинхронном режиме с использованием API. К порту подключаю RS-232 переходник и смотрю сигналы на выводах....

Не работает Com порт в асинхронном режиме
Крайне необходимо небольшое приложение для Com порта работающее в асинхронном режиме. Принял некоторое переменное количество байт и сразу...

работа с COM портом
здравствуйте всем,я в форумах в первие ,и просил бы не судить очень строго. помогите пожалуйста , хочу реализовать приложение работающую с...

19
19 / 19 / 4
Регистрация: 27.05.2013
Сообщений: 119
22.10.2015, 11:28
Прошелся бы отладчиком по коду и дал больше данных.
Насколько я помню FILE_FLAG_OVERLAPPED это как раз синхронный режим а не асинхронный.
И кажется не нужно тогда signal = WaitForSingleObject(overlapped.hEvent, INFINITE);
И написать не помешало как обмен происходит, через виртуальный порт или провода куда то идут.
0
-11 / 0 / 1
Регистрация: 02.09.2015
Сообщений: 76
27.10.2015, 08:11  [ТС]
Обмен происходит через замкнутые между собой выводы RX и TX в разъёме COM-порта на ПК.
0
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
27.10.2015, 10:34
Да и стоит сразу заменить обычный поток на TThread.
0
-11 / 0 / 1
Регистрация: 02.09.2015
Сообщений: 76
27.10.2015, 10:57  [ТС]
Я принципиально хочу решить задачу средствами WINAPI. Это невозможно?
0
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
27.10.2015, 11:20
Цитата Сообщение от Alexey9891 Посмотреть сообщение
Я принципиально хочу решить задачу средствами WINAPI.
Что я могу тут сказать, удачи ...

Добавлено через 2 минуты
Ибо так делать (отсутствует синхронизация с VCL) нельзя:
C++
1
2
3
4
5
void ReadPrinting()
{
Form1->MemoOut->Lines->Add((char*)bufrd);    //выводим принятую строку в Memo
memset(bufrd, 0, BUFSIZE);          //очистить буфер (чтобы данные не накладывались друг на друга)
}
0
-11 / 0 / 1
Регистрация: 02.09.2015
Сообщений: 76
20.01.2016, 14:48  [ТС]
Цитата Сообщение от Avazart Посмотреть сообщение
Что я могу тут сказать, удачи ...
Вобщем удача мне пригодилась, программа работает. Щас пишу всякие "обвесы" к ней для решения конкретных задач.
0
1 / 0 / 1
Регистрация: 21.01.2016
Сообщений: 7
22.01.2016, 15:50
Alexey9891, и где собака порылась? напиши своё решение плз..
0
-11 / 0 / 1
Регистрация: 02.09.2015
Сообщений: 76
22.01.2016, 23:38  [ТС]
С тех пор как я создал эту тему, прошло много времени. Я уже точно и не помню в чем там был косяк, помню что какой-то тривиальный. Щас уже лень искать его в старом коде. Если интересует моя программа обращайтесь в личку.
0
 Аватар для popal
0 / 0 / 2
Регистрация: 28.10.2015
Сообщений: 36
25.01.2016, 13:57
Alexey9891, здравствуйте,можно код программы?)
0
-11 / 0 / 1
Регистрация: 02.09.2015
Сообщений: 76
26.01.2016, 08:00  [ТС]
Цитата Сообщение от popal Посмотреть сообщение
здравствуйте,можно код программы?)
Боюсь весь код программы только запутает Вас. Может интересуют конкретные модули?
0
 Аватар для popal
0 / 0 / 2
Регистрация: 28.10.2015
Сообщений: 36
27.01.2016, 15:04
Alexey9891, мне нужно посмотреть код инициализации порта и принятия данных. У меня асинхронный режим, только принятие.
0
-11 / 0 / 1
Регистрация: 02.09.2015
Сообщений: 76
28.01.2016, 13:37  [ТС]
Цитата Сообщение от popal Посмотреть сообщение
мне нужно посмотреть код инициализации порта и принятия данных. У меня асинхронный режим, только принятие.
Код инициализации порта. Процесс начинается по нажатию кнопки:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
void __fastcall TForm1::ConnectBtnClick(TObject *Sender)
{
Form1->PanelStatus->Caption = "";
DCB dcb;
COMMTIMEOUTS timeouts;
 
//-Открытие порта---------------------------------------------------------------
portname = ChoiceCOM->Text;
COMport=CreateFile(portname.c_str(), GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
 //здесь:
 // - portname.c_str() - имя порта в качестве имени файла,
 //   c_str() преобразует строку типа String в строку в виде массива типа char, иначе функция не примет
 // - GENERIC_READ | GENERIC_WRITE - доступ к порту на чтение/записть
 // - 0 - порт не может быть общедоступным (shared)
 // - NULL - дескриптор порта не наследуется, используется дескриптор безопасности по умолчанию
 // - OPEN_EXISTING - порт должен открываться как уже существующий файл
 // - FILE_FLAG_OVERLAPPED - этот флаг указывает на использование асинхронных операций
 // - NULL - указатель на файл шаблона не используется при работе с портами
 
if(COMport==INVALID_HANDLE_VALUE)
  {
  PanelStatus->Font->Color = clRed;
  PanelStatus->Caption = " Не удалось открыть последовательный порт " + AnsiString(portname.c_str()) + ".";
  return;
  }
 
 
//------------------------------------------------------------------------------
 
//-Инициализация порта----------------------------------------------------------
//dcb=(DCB*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(DCB)); // Выделение области памяти для DCB из кучи и заполнение этой области нулями.
dcb.DCBlength=sizeof(DCB);
 
if(!GetCommState(COMport, &dcb)) // Эта функция заполняет DCB информацией о текущем состоянии устройства, точнее о его настройках.
  {
  // Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
//  HeapFree(GetProcessHeap(),0,dcb); // Освобождение памяти из кучи.
  COMClose();
  PanelStatus->Font->Color = clRed;
  PanelStatus->Caption = "Не удалось считать DCB";
  return;
  }
 
//-Инициализация структуры DCB.
dcb.BaudRate = StrToInt(ChoiceSpeed->Text); // задаём скорость передачи 115200 бод
dcb.fBinary = TRUE;                         // включаем двоичный режим обмена
dcb.fOutxCtsFlow = FALSE;                   // выключаем режим слежения за сигналом CTS
dcb.fOutxDsrFlow = FALSE;                   // выключаем режим слежения за сигналом DSR
dcb.fDtrControl = DTR_CONTROL_DISABLE;      // отключаем использование линии DTR
dcb.fDsrSensitivity = FALSE;                // отключаем восприимчивость драйвера к состоянию линии DSR
dcb.fNull = FALSE;//TRUE                    // разрешить приём нулевых байтов
dcb.fRtsControl = RTS_CONTROL_DISABLE;      // отключаем использование линии RTS
dcb.fAbortOnError = FALSE;                  // отключаем остановку всех операций чтения/записи при ошибке
dcb.ByteSize = 8;                           // задаём 8 бит в байте
dcb.Parity = 1;                             // включаем проверку чётности
dcb.StopBits = 0;                           // задаём один стоп-бит
 
//-Загрузить структуру DCB в порт.
if(!SetCommState(COMport, &dcb))
  {// Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
//  HeapFree(GetProcessHeap(),0,dcb); // Освобождение памяти из кучи.
  COMClose();
  PanelStatus->Font->Color = clRed;
  PanelStatus->Caption = "Не удалось установить DCB.";
  return;
  }
 
//-Установка таймаутов.
timeouts.ReadIntervalTimeout = 0;     // таймаут между двумя символами
timeouts.ReadTotalTimeoutMultiplier = 0;  // общий таймаут операции чтения
timeouts.ReadTotalTimeoutConstant = 0;    // константа для общего таймаута операции чтения
timeouts.WriteTotalTimeoutMultiplier = 0; // общий таймаут операции записи
timeouts.WriteTotalTimeoutConstant = 0;   // константа для общего таймаута операции записи
 
//-Загрузить структуру таймаутов в порт.
if(!SetCommTimeouts(COMport, &timeouts))
  {// Если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния.
  COMClose();
  PanelStatus->Font->Color = clRed;
  PanelStatus->Caption = "Не удалось установить тайм-ауты.";
  return;
  }
 
//-Установить размеры очередей приёма и передачи
//  SetupComm(COMport,2000,2000);
//------------------------------------------------------------------------------
 
PurgeComm(COMport,PURGE_RXCLEAR); // Очистка буферов порта.
 
ConnectBtn->Enabled = false;
DisconnectBtn->Enabled = true;
 
//-Создание потоков на WINAPI:
 
// Cоздаём поток чтения, который сразу начнёт выполняться (предпоследний параметр = 0).
reader = CreateThread(NULL, 0, ReadThread, NULL, 0, NULL);
 
// Cоздаём поток записи в остановленном состоянии (предпоследний параметр = CREATE_SUSPENDED).
writer = CreateThread(NULL, 0, WriteThread, NULL, CREATE_SUSPENDED, NULL);
 
counterRX = 0;
 
}
//******************************************************************************





Поток чтения данных:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
//-Объявления потоков-----------------------------------------------------------
HANDLE reader;  //дескриптор потока чтения из порта
HANDLE writer;  //дескриптор потока записи в порт
 
DWORD WINAPI ReadThread(LPVOID);
DWORD WINAPI WriteThread(LPVOID);
//******************************************************************************
 
 
//-Поток ReadThead--------------------------------------------------------------
void ReadPrinting(DWORD);
 
//главная функция потока, реализует приём байтов из COM-порта
DWORD WINAPI ReadThread(LPVOID)
{
COMSTAT comstat;        // структура текущего состояния порта, в данной программе используется для определения количества принятых в порт байтов
DWORD btr, temp, mask, signal;  // переменная temp используется в качестве заглушки
 
overlapped.hEvent = CreateEvent(NULL, true, true, NULL); // создать сигнальный объект-событие для асинхронных операций
SetCommMask(COMport, EV_RXCHAR);                     // установить маску на срабатывание по событию приёма байта в порт
while(1)
//while(!flag)                       // пока поток не будет прерван, выполняем цикл
  {
  WaitCommEvent(COMport, &mask, &overlapped);                   //ожидать события приёма байта (это и есть перекрываемая операция)
  signal = WaitForSingleObject(overlapped.hEvent, INFINITE);    //приостановить поток до прихода байта
  if(signal == WAIT_OBJECT_0)                       //если событие прихода байта произошло
    {
    if(GetOverlappedResult(COMport, &overlapped, &temp, true)) //проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent
      if((mask & EV_RXCHAR)!=0)                 //если произошло именно событие прихода байта
        {
        ClearCommError(COMport, &temp, &comstat); // нужно заполнить структуру COMSTAT
        btr = comstat.cbInQue;                    // и получить из неё количество принятых байтов
        if(btr)                                   // если действительно есть байты для чтения
          {
          ReadFile(COMport, bufrd, btr, &temp, &overlapped); // прочитать байты из порта в буфер программы
          counterRX+=btr;                                      // увеличиваем счётчик байтов
          ReadPrinting(btr);                                    // вызываем функцию для вывода данных на экран и в файл
          }
        }
    }
  }
//CloseHandle(overlapped.hEvent);
//flag2=1;
}
//******************************************************************************
 
 
 
//-Вывод принятых байтов на экран-----------------------------------------------
void ReadPrinting(DWORD btr)
{
String stroka, temp_str;
for(UINT32 i=0; i<btr ; i++)
  stroka = stroka + IntToHex(bufrd[i], 2) + ' '; // Перенос буфера в строку.
 
Form1->MemoRX->Lines->Add(stroka); // Вывод.
Form1->LabelRX->Caption = "Всего принято: " + IntToStr(counterRX);
memset(bufrd, 0, BUFSIZE); // Очистить буфер (чтобы данные не накладывались друг на друга).
}
//******************************************************************************
Хотя можно и не так заморочисто. Для меня принципиально было сделать на WinAPI и обязательно используя потоки чтения и записи.

Добавлено через 3 минуты
А классный форум, сам написал вопрос, а теперь сам на него овечаю
0
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
28.01.2016, 13:39
Такой код на мусорку, ибо без синхронизации... ибо он при определенных условиях будет подвешивать программу и программа будет падать.
0
-11 / 0 / 1
Регистрация: 02.09.2015
Сообщений: 76
28.01.2016, 13:52  [ТС]
Цитата Сообщение от Avazart Посмотреть сообщение
Такой код на мусорку, ибо без синхронизации... ибо он при определенных условиях будет подвешивать программу и программа будет падать.
Согласен, только что-то вот ваших предложений я не видел с 27.10.2015, 11:20.
0
Эксперт С++
 Аватар для Avazart
8488 / 6155 / 615
Регистрация: 10.12.2010
Сообщений: 28,683
Записей в блоге: 30
28.01.2016, 13:58
Цитата Сообщение от Alexey9891 Посмотреть сообщение
Согласен, только что-то вот ваших предложений я не видел с 27.10.2015, 11:20.
Цитата Сообщение от Avazart Посмотреть сообщение
Да и стоит сразу заменить обычный поток на TThread.
И добавить синхронизацию соответственно.
0
-11 / 0 / 1
Регистрация: 02.09.2015
Сообщений: 76
01.02.2016, 10:06  [ТС]
Цитата Сообщение от Avazart Посмотреть сообщение
И добавить синхронизацию соответственно.
Сравнение использования класса TThread и средств WINAPI

Одно из отличий заключается в том, что в потоках WINAPI можно использовать обращение к
графическим компонентам и файлам, так как сообщения потоков WINAPI ставятся в очередь сообщений
главного потока процесса. Таким образом избегаются конфликты между потоками при обращении к
разделяемым компонентам или файлам. А вот в TThread для этого используется метод Synchronize(),
который выполняет то же самое – ставит сообщения в очередь главного потока процесса, за счёт чего можно
избежать конфликтов между потоками.
0
-11 / 0 / 1
Регистрация: 02.09.2015
Сообщений: 76
25.04.2016, 08:24  [ТС]
Возник новый вопрос. Изначально программа работы с COM-портом использовалась для общения с микроконтроллером (далее МК). COM-порт компьютера (далее ПК) соединялся с МК через преобразователь MAX 3232. Все работало на ура.

Одно уточнение: при общении с ПК, микроконтроллер отправлял на ПК различное количество байт, и программа на ПК не знает заранее сколько придет байт от МК. Когда МК делал одну отправку (например 7 байт), программа на ПК один раз исполняла главную функцию потока void __fastcall ReadThread::Execute(), где за один вход в эту функцию считывала все байты (все 7 за раз) и далее за один раз выводила все принятые байты в Memo. Все работало хорошо.

Затем решил подключить МК к ПК другим образом. В МК имеется приемо/передатчик UART, выводы которого я подключаю к преобразователю интерфейса (буферу) RS-485, от преобразователя выходит витая пара, которая подключается к преобразователю MOXA UPort1130, а он, в свою очередь, соединен с ПК. Преобразователи UPort от компании MOXA подключаются к USB-порту компьютера и предназначены для создания в компьютере дополнительных последовательных портов в операционных системах Windows. Короче говоря, соединил МК с ПК не через COM-порт, а через RS-485.

И вот тут возникла странность. Раньше, при отправке с МК нескольких байт (например 7 байт), программа на ПК воспринимала данные как одну посылку из нескольких байт (один раз выполняла функцию void __fastcall ReadThread::Execute() и считывала все 7 байт), теперь же программа ту же посылку воспринимает как несколько отдельных посылок по 1-му байту (функция void __fastcall ReadThread::Execute() выполняется 7 раз для каждого байта). Соответственно и вывод в Memo производится несколько раз (7 раз по одному байту).

С чем это связано? Как это исправить?

Пробовал поиграться с таймаутами: ReadIntervalTimeout; ReadTotalTimeoutMultiplier; ReadTotalTimeoutConstant - не дало результата. Как это исправить?
0
0 / 0 / 0
Регистрация: 11.12.2015
Сообщений: 41
25.04.2016, 09:39
Здравствуйте! хотел создать новую тему, но вопрос схож с данной темой
У меня та-же проблема при приеме, при передачи все нормально. Попробовал два примера методом winapi и TThread
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void __fastcall ReadThread::Execute()
{
 COMSTAT comstat;       
 DWORD btr, temp, mask, signal; 
 
 overlapped.hEvent = CreateEvent(NULL, true, true, NULL);   
 SetCommMask(COMport, EV_RXCHAR);                               
 while(!Terminated)                     
  {
   WaitCommEvent(COMport, &mask, &overlapped);                  
   signal = WaitForSingleObject(overlapped.hEvent, INFINITE);   
   if(signal == WAIT_OBJECT_0)                     
    {
     if(GetOverlappedResult(COMport, &overlapped, &temp, true))  
      if((mask & EV_RXCHAR)!=0)                 
       {
        ClearCommError(COMport, &temp, &comstat);       
        btr = comstat.cbInQue;                              
        if(btr)                                     
        {
 
         ReadFile(COMport, bufrd, btr, &temp, &overlapped);     
         counter+=btr;                                          
         Synchronize(Printing);                             
        }
       }
    }
  }
 CloseHandle(overlapped.hEvent);        
}
данные считываю по таймеру
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
 
  String name_port="Com";
  String temp;
 
  static unsigned char i=1;
 switch(Time_state)
 {
  case Send_id:
    temp=name_port+IntToStr(i);
    if(COMOpen(temp))
    {
      memset(bufwr,0,BUFSIZE);          
      PurgeComm(COMport, PURGE_TXCLEAR);             
      strcpy(bufwr,"ID");     
      writer = new WriteThread(false);               
      writer->FreeOnTerminate = true;               
      Time_state=Wait_id;
      Timer1->Interval=100;
    }
    else
    {
      i++;
      i&=127;
      Timer1->Interval=1;
    }
  break;
 
  case Wait_id:
    if(strstr((const char*)bufrd,"RU_2"))
    {
     StatusBar1->Panels->Items[0]->Text ="Ïðèáîð: Ãåðñà-2.1";
      Time_state=Wait_Connection;
      StatusBar1->Visible=true;
      Label1->Enabled=true;
      Timer1->Interval=100;
    }
    else
    {
     COMClose();
      Time_state=Send_id;
      i++;
      i&=255;
      Timer1->Interval=1;
     }
  break;
 
  case Send_password:
            memset(bufwr,0,BUFSIZE);    
      PurgeComm(COMport, PURGE_TXCLEAR);
      strcpy(bufwr,"PASSWORD");
      strcat(bufwr,Form2->Edit1->Text.c_str());        
      writer = new WriteThread(false);  
      writer->FreeOnTerminate = true;  
      Time_state=Wait_password;
      TimeCount=3;
      Timer1->Interval=100;
  break;
 
  case Wait_password:
   if(strstr((const char*)bufrd,"PASSWORD OK"))
    {
      Memo1->Lines->Add("ïàðîëü âåðåí");
      Form1->PageControl1->Visible=true;
      Form1->TabSheet1->TabVisible=false;
      Form1->TabSheet2->TabVisible=false;
      Form1->TabSheet3->TabVisible=true;
      Form1->TabSheet4->TabVisible=false;
      Form1->StatusBar1->Visible=true;
      memset(bufrd,0,BUFSIZE);
    }
    if(strstr((const char*)bufrd,"PASSWORD ERROR"))
    {
      Memo1->Lines->Add("ïàðîëü íå âåðåí");
      Time_state=Wait_Connection;
      ShowMessage("Ïàðîëü íå âåðåí");
      memset(bufrd,0,BUFSIZE);
    }
 
 
     if(TimeCount==1)
       {
         Time_state=Send_id;
         COMClose();
         TimeCount=0;
         StatusBar1->Panels->Items[0]->Text ="";
         Label1->Enabled=false;
         }
     else TimeCount--;
      Timer1->Interval=100;
  break;
 
  case Send_status:
       Timer1->Interval=100;
  break;
 
  case Wait_status:
        Timer1->Interval=100;
  break;
 
  case Send_Read_Conf:
       Timer1->Interval=100;
  break;
 
  case Wait_Read_Conf:
        Timer1->Interval=100;
  break;
 
  case Send_Write_Conf:
       Timer1->Interval=100;
  break;
 
  case Wait_Write_Conf:
        Timer1->Interval=100;
  break;
 
  case Wait_Connection:
 
     Timer1->Interval=100;
  break;
 
 default: Time_state=Send_id;Timer1->Interval=1;  break;
 }
 
 
  Timer1->Enabled=true;
 
}
да если работать с ко кого либо терминала то все нормально данные идут как надо(мк работает правильно)
на рисунке в рамочке виден перенос, но иногда вижу и пропуск байтов
Миниатюры
Работа с COM-портом в асинхронном режиме  
0
-11 / 0 / 1
Регистрация: 02.09.2015
Сообщений: 76
25.04.2016, 16:20  [ТС]
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Главная функция потока, реализует приём байтов из COM-порта.
void __fastcall ReadThread::Execute()
{
COMSTAT comstat; // Структура текущего состояния порта, в данной программе используется для определения количества принятых в порт байтов.
DWORD btr, temp, mask, signal; // Переменная temp используется в качестве заглушки.
overlapped.hEvent = CreateEvent(NULL, true, true, NULL); // Создать сигнальный объект-событие для асинхронных операций.
SetCommMask(COMport, EV_RXCHAR); // Установить маску на срабатывание по событию приёма байта в порт.
while(!Terminated) // Пока поток не будет прерван, выполняем цикл.
  {
  WaitCommEvent(COMport, &mask, &overlapped); // Ожидать события приёма байта (это и есть перекрываемая операция).
  signal = WaitForSingleObject(overlapped.hEvent, INFINITE); // Приостановить поток до прихода байта.
  if(signal == WAIT_OBJECT_0) // Если событие прихода байта произошло.
    {
    if(GetOverlappedResult(COMport, &overlapped, &temp, true)) // Проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent.
      if((mask & EV_RXCHAR)!=0)                                // Если произошло именно событие прихода байта.
        {
        ClearCommError(COMport, &temp, &comstat); // Заполнить структуру COMSTAT
        btr = comstat.cbInQue;                    // и получить из неё количество принятых байтов.
        if(btr)                               // Если действительно есть байты для чтения
          {
          ReadFile(COMport, bufrd, btr, &temp, &overlapped); // Прочитать байты из порта в буфер программы.
          counterRX += btr;                                  // Увеличиваем счётчик байтов.
          Num_of_ReadByte = btr;
          Synchronize(Printing);                        // Вызываем функцию для вывода данных на экран и в файл.
          }
        }
    }
  }
CloseHandle(overlapped.hEvent); // Перед выходом из потока закрыть объект-событие.
}
//******************************************************************************
При отладке программы заметил такую вещь, если поставить Break в потоке чтения на строчке btr = comstat.cbInQue, сделать отправку с МК в ПК к примеру 6-ти байт, то будет осуществлено 2 входа в функцию, при первом входе btr равняется 1 и в Memo будет выведен один принятый байт, а при втором входе btr = 5 и в Memo будет выведено 5 байт за один раз. Получается, что изначальная посылка из 6 байт с МК будет восприниматься на ПК как 2 посылки: 1 байт и 5 байт. Как сделать так, чтобы программа на ПК воспринимала посылку целиком а не по байтам?

Добавлено через 3 часа 1 минуту
Решил проблему "ЗАПЛАТКОЙ". Сделал задержку (функция wait(3000000)перед выполнением функции ClearCommError(COMport, &temp, &comstat):

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if(GetOverlappedResult(COMport, &overlapped, &temp, true)) // Проверяем, успешно ли завершилась перекрываемая операция WaitCommEvent.
      if((mask & EV_RXCHAR)!=0)                                // Если произошло именно событие прихода байта.
        {
        wait(3000000); // ЗАПЛАТКА.
        ClearCommError(COMport, &temp, &comstat); // Заполнить структуру COMSTAT
        btr = comstat.cbInQue;                    // и получить из неё количество принятых байтов.
        if(btr)                               // Если действительно есть байты для чтения
          {
          ReadFile(COMport, bufrd, btr, &temp, &overlapped); // Прочитать байты из порта в буфер программы.
          counterRX += btr;                                  // Увеличиваем счётчик байтов.
          Num_of_ReadByte = btr;
          Synchronize(Printing);                        // Вызываем функцию для вывода данных на экран и в файл.
          }
        }
    }
Оно, конечно, заработало, но хотелось бы решить проблему правильно. Как это сделать?
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
25.04.2016, 16:20
Помогаю со студенческими работами здесь

Работа с Com портом
Привет всем! У меня собственно такая ситуация, есть bluetooth и телефон Nokia устанавливаю соединение через виртуальный com порт, мне...

Работа с COM портом
Здравствуйте! В процессе написания программы работы с устройством, подключенным к ПК через преобразователь интерфейсов RS485 - USB,...

Работа с COM портом
по COM порту через Agilen подключается вольтметр с адресом 1. необходимо &quot;прозвонить&quot; все порты и автоматически сделать рабочим тот,...

Работа с COM-портом
Подскажите, какие компоненты нужны для работы с ком-портами? а именно (хотя бы для начала) отправка шестнадцатиричного кода на порт, к...

Работа с COM портом и вообще...
Друзья спасайте! Для обмена необходимо выдать сигналы DTR = 1(+) и RTS = 0(-). Как это сделать? Я знаю регистры в которые нужно...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
Подключение Box2D v3 к SDL3 для Android: физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
Инструменты COM: Сохранение данный из VARIANT в файл и загрузка из файла в VARIANT
bedvit 28.01.2026
Сохранение базовых типов COM и массивов (одномерных или двухмерных) любой вложенности (деревья) в файл, с возможностью выбора алгоритмов сжатия и шифрования. Часть библиотеки BedvitCOM Использованы. . .
Загрузка PNG с альфа-каналом на SDL3 для Android: с помощью SDL_LoadPNG (без SDL3_image)
8Observer8 28.01.2026
Содержание блога SDL3 имеет собственные средства для загрузки и отображения PNG-файлов с альфа-каналом и базовой работы с ними. В этой инструкции используется функция SDL_LoadPNG(), которая. . .
Загрузка PNG с альфа-каналом на SDL3 для Android: с помощью SDL3_image
8Observer8 27.01.2026
Содержание блога SDL3_image - это библиотека для загрузки и работы с изображениями. Эта пошаговая инструкция покажет, как загрузить и вывести на экран смартфона картинку с альфа-каналом, то есть с. . .
Влияние грибов на сукцессию
anaschu 26.01.2026
Бифуркационные изменения массы гриба происходят тогда, когда мы уменьшаем массу компоста в 10 раз, а скорость прироста биомассы уменьшаем в три раза. Скорость прироста биомассы может уменьшаться за. . .
Воспроизведение звукового файла с помощью SDL3_mixer при касании экрана Android
8Observer8 26.01.2026
Содержание блога SDL3_mixer - это библиотека я для воспроизведения аудио. В отличие от инструкции по добавлению текста код по проигрыванию звука уже содержится в шаблоне примера. Нужно только. . .
Установка Android SDK, NDK, JDK, CMake и т.д.
8Observer8 25.01.2026
Содержание блога Перейдите по ссылке: https:/ / developer. android. com/ studio и в самом низу страницы кликните по архиву "commandlinetools-win-xxxxxx_latest. zip" Извлеките архив и вы увидите. . .
Вывод текста со шрифтом TTF на Android с помощью библиотеки SDL3_ttf
8Observer8 25.01.2026
Содержание блога Если у вас не установлены Android SDK, NDK, JDK, и т. д. то сделайте это по следующей инструкции: Установка Android SDK, NDK, JDK, CMake и т. д. Сборка примера Скачайте. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru