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