Цитата(OlegIT @ 21.6.2010, 11:54) | Ошибка, по-моему, одна, функция чтения возвращает меньшее количество байт, всегда разное. Когда прочитано байт столько сколько нужно в корректности данных проблем нет. Как работает функция чтения, если пришли не все байты, а она вызвана? |
нет, пока свободного времени, чтоб разбираться с вашей проблемой, вод код (100% рабочий),он поможет разобраться, если возникнут проблемы отпишите Код | #define BUFSIZE 255 //ёмкость буфера unsigned char bufrd[BUFSIZE], bufwr[BUFSIZE]; //приёмный и передающий буферы //--------------------------------------------------------------------------- HANDLE COMport; //дескриптор порта //структура OVERLAPPED необходима для асинхронных операций, при этом для операции чтения и записи нужно объявить разные структуры //эти структуры необходимо объявить глобально, иначе программа не будет работать правильно OVERLAPPED overlapped; //будем использовать для операций чтения (см. поток ReadThread) //--------------------------------------------------------------------------- unsigned long counter; //счётчик принятых байтов, обнуляется при каждом открытии порта
void COMOpen(void); //открыть порт void COMClose(void); //закрыть порт //--------------------------------------------------------------------------- //поток для чтения последовательности байтов из COM-порта в буфер class ReadThread : public TThread { private: void __fastcall Printing(); //вывод принятых байтов на экран и в файл protected: void __fastcall Execute(); //основная функция потока public: __fastcall ReadThread(bool CreateSuspended); //конструктор потока }; //--------------------------------------------------------------------------- ReadThread *reader; //объект потока ReadThread //--------------------------------------------------------------------------- //конструктор потока ReadThread, по умолчанию пустой __fastcall ReadThread::ReadThread(bool CreateSuspended) : TThread(CreateSuspended) {} //--------------------------------------------------------------------------- //главная функция потока, реализует приём байтов из 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==10) // <- тут проверяем равно ли 10 байтам принятое ..я,сука,МОНСТР!!! //если действительно есть байты для чтения { ReadFile(COMport, bufrd, btr, &temp, &overlapped); //прочитать байты из порта в буфер программы counter+=btr; //увеличиваем счётчик байтов Synchronize(Printing); //вызываем функцию для вывода данных на экран и в файл } } } } CloseHandle(overlapped.hEvent); //перед выходом из потока закрыть объект-событие }
//---------------------------------------------------------------------------
//выводим принятые байты на экран и в файл (если включено) void __fastcall ReadThread::Printing() {
//Form1->Memo1->Clear(); //очистить Memo1 Form1->Memo1->Lines->Add((char*)bufrd); //выводим принятую строку в Memo Form1->StatusBar1->Panels->Items[2]->Text = "Всего принято " + IntToStr(counter) + " байт"; //выводим счётчик в строке состояния memset(bufrd, 0, BUFSIZE); //очистить буфер (чтобы данные не накладывались друг на друга) }
//--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } //---------------------------------------------------------------------------
//обработчик нажатия на кнопку "Открыть порт" void __fastcall TForm1::SpeedButton1Click(TObject *Sender) { if(SpeedButton1->Down) { COMOpen(); //если кнопка нажата - открыть порт Form1->SpeedButton1->Caption = "Закрыть порт"; counter = 0; //сбрасываем счётчик байтов } else { COMClose(); //если кнопка отжата - закрыть порт Form1->SpeedButton1->Caption = "Открыть порт"; } }
//---------------------------------------------------------------------------
//обработчик закрытия формы void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { if(reader)reader->Terminate(); //завершить поток чтения из порта, проверка if(reader) обязательна, иначе возникают ошибки if(COMport)CloseHandle(COMport); //закрыть порт } //--------------------------------------------------------------------------- //кнопка "Очистить поле" void __fastcall TForm1::Button3Click(TObject *Sender) { Form1->Memo1->Clear(); //очистить Memo1 }
//--------------------------------------------------------------------------- //функция открытия и инициализации порта void COMOpen() { String portname; //имя порта (например, "COM1", "COM2" и т.д.) DCB dcb; //структура для общей инициализации порта DCB COMMTIMEOUTS timeouts; //структура для установки таймаутов
portname = "COM3" ; //получить имя выбранного порта //открыть порт, для асинхронных операций обязательно нужно указать флаг FILE_FLAG_OVERLAPPED 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) //если ошибка открытия порта { Form1->SpeedButton1->Down = false; //отжать кнопку Form1->StatusBar1->Panels->Items[0]->Text = "Не удалось открыть порт"; //вывести сообщение в строке состояния return; }
//инициализация порта dcb.DCBlength = sizeof(DCB); //в первое поле структуры DCB необходимо занести её длину, она будет использоваться функциями настройки порта для контроля корректности структуры
//считать структуру DCB из порта if(!GetCommState(COMport, &dcb)) //если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния { COMClose(); Form1->StatusBar1->Panels->Items[0]->Text = "Не удалось считать DCB"; return; }
//инициализация структуры DCB dcb.BaudRate = StrToInt(Form1->ComboBox2->Text); //задаём скорость передачи по дефолту там 9600 но х.з. как там дальше dcb.fBinary = TRUE; //включаем двоичный режим обмена dcb.fOutxCtsFlow = FALSE; //выключаем режим слежения за сигналом CTS dcb.fOutxDsrFlow = FALSE; //выключаем режим слежения за сигналом DSR dcb.fDtrControl = DTR_CONTROL_DISABLE; //отключаем использование линии DTR dcb.fDsrSensitivity = FALSE; //отключаем восприимчивость драйвера к состоянию линии DSR dcb.fNull = FALSE; //разрешить приём нулевых байтов dcb.fRtsControl = RTS_CONTROL_DISABLE; //отключаем использование линии RTS dcb.fAbortOnError = FALSE; //отключаем остановку всех операций чтения/записи при ошибке dcb.ByteSize = 8; //задаём 8 бит в байте dcb.Parity = 0; //отключаем проверку чётности dcb.StopBits = 0; //задаём один стоп-бит
//загрузить структуру DCB в порт if(!SetCommState(COMport, &dcb)) //если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния { COMClose(); Form1->StatusBar1->Panels->Items[0]->Text = "Не удалось установить DCB"; return; }
//установить таймауты timeouts.ReadIntervalTimeout = 0; //таймаут между двумя символами timeouts.ReadTotalTimeoutMultiplier = 0; //общий таймаут операции чтения timeouts.ReadTotalTimeoutConstant = 0; //константа для общего таймаута операции чтения timeouts.WriteTotalTimeoutMultiplier = 0; //общий таймаут операции записи timeouts.WriteTotalTimeoutConstant = 0; //константа для общего таймаута операции записи
//записать структуру таймаутов в порт if(!SetCommTimeouts(COMport, &timeouts)) //если не удалось - закрыть порт и вывести сообщение об ошибке в строке состояния { COMClose(); Form1->StatusBar1->Panels->Items[0]->Text = "Не удалось установить тайм-ауты"; return; }
//установить размеры очередей приёма и передачи SetupComm(COMport,2000,2000); PurgeComm(COMport, PURGE_RXCLEAR); //очистить принимающий буфер порта reader = new ReadThread(false); //создать и запустить поток чтения байтов reader->FreeOnTerminate = true; //установить это свойство потока, чтобы он автоматически уничтожался после завершения } //--------------------------------------------------------------------------- //функция закрытия порта void COMClose() { if(reader)reader->Terminate(); //если поток чтения работает, завершить его; проверка if(reader) обязательна, иначе возникают ошибки CloseHandle(COMport); //закрыть порт COMport=0; //обнулить переменную для дескриптора порта } //---------------------------------------------------------------------------
|
в приложен файле сама прога, так проще разобраться ..правда там 10 байт принимает, но код хорошо от комментирован, поэтому поправить не составит труда. |