nikolayk |
Отправлено: 07.04.2005, 22:12 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
Кто работал с COM-портом, помогите разобраться.
Для непрерывного приема через COM-порт создан THREAD.
Принятые данные выводятся на Form1->Memo1.
Прием работает, но бывают пропуски байтов по восемь.
Ниже приведен код THREAD.
В чем тут может быть дело?
CODE | #include <vcl.h>
#pragma hdrstop
#include "CommRead1.h"
#include "Unit1.h"
#pragma package(smart_init)
extern HANDLE hCom;
extern DCB dcb;
extern OVERLAPPED ovReader;
COMSTAT csStat;
DWORD Error;
DWORD dwCommEvent, dwRead, dwWait, result_nbr;
char chRead;
char buf[11];
unsigned int size;
char szText2[10];
char ReadBuffer[100];
int i=0;
__fastcall CommRead1::CommRead1(bool CreateSuspended)
: TThread(CreateSuspended)
{
}
//---------------------------------------------------------------------------
void __fastcall CommRead1::Execute()
{
do {
if (Terminated) break;
if(!ReadFile(hCom,&buf,10,&dwRead,&ovReader)){
if(GetLastError() != ERROR_IO_PENDING)
ShowMessage("Ошибка чтения данных");
else {
// Ждём завершения операции чтения
dwWait = WaitForSingleObject(ovReader.hEvent,5000);
switch (dwWait) {
case WAIT_OBJECT_0: //Операция чтения закончена
if (GetOverlappedResult(hCom,&ovReader,&result_nbr,FALSE)){
ResetEvent(ovReader.hEvent);
MessageBeep(MB_OK);// Стандартный звук
Synchronize(ReadData);
}
else //Обработка полученных данных
ShowMessage("Ошибка GetOverlapedResult");
break;
case WAIT_TIMEOUT: //Операция чтения еще не закончилась
//MessageBeep(MB_OK);// Стандартный звук
//ShowMessage("Наступил WAIT_TIMEOUT");
;
break;
default: //Ошибка выполнения WaitForSingleObject
ShowMessage("Ошибка выполнения WaitForSingleObject");
break;
}//swich
}//else
}//if(!ReadFile
}while(!Terminated);//do
}
//---------------------------------------------------------------------------
void __fastcall CommRead1::ReadData()
{
buf[result_nbr]=0;
Form1->Memo1->Text =Form1->Memo1->Text+ AnsiString(buf);
}
//-----------------------------------------------------
| |
|
Asher |
Отправлено: 08.04.2005, 08:09 |
|
Мастер участка
Группа: Модератор
Сообщений: 550
|
ReadFile может выполнится и успешно, и тогда у вас теряются эти данные.
Требуется ветвь else для условия if(!ReadFile(hCom,... где следует выполнить те-же действия, что и в разделе case WAIT_OBJECT_0:
|
|
nikolayk |
Отправлено: 08.04.2005, 16:09 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
Спасибо, помогло. |
|
nikolayk |
Отправлено: 20.04.2005, 21:06 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
При внимательном тестировании оказалось, что иногда бывают пропуски по 10 байт. По совету Asher я сделал вот так:
CODE | //---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include "CommRead1.h"
#include "Unit1.h"
#pragma package(smart_init)
extern HANDLE hCom;
extern DCB dcb;
extern OVERLAPPED ovReader;
DWORD dwCommEvent, dwRead, dwWait, result_nbr;
char buf[11];
//---------------------------------------------------------------------------
__fastcall CommRead1::CommRead1(bool CreateSuspended)
: TThread(CreateSuspended)
{
Priority =tpHighest;
}
//---------------------------------------------------------------------------
void __fastcall CommRead1::Execute()
{
//---- Place thread code here ----
do {
if (Terminated) break;
if(!ReadFile(hCom,&buf,10,&dwRead,&ovReader)){
if(GetLastError() != ERROR_IO_PENDING)
ShowMessage("Ошибка чтения данных");
else {
// Ждём завершения операции чтения
dwWait = WaitForSingleObject(ovReader.hEvent,5000);
switch (dwWait) {
case WAIT_OBJECT_0: //Операция чтения закончена
if (GetOverlappedResult(hCom,&ovReader,&result_nbr,FALSE)){
ResetEvent(ovReader.hEvent);
Synchronize(ReadData);
}
else
ShowMessage("Ошибка GetOverlapedResult");
break;
case WAIT_TIMEOUT: //Операция чтения еще не закончилась
//ShowMessage("Наступил WAIT_TIMEOUT");
;
break;
default: //Ошибка выполнения WaitForSingleObject
ShowMessage("Ошибка выполнения WaitForSingleObject");
break;
}//swich
}//else
}//if(!ReadFile
else {
if (GetOverlappedResult(hCom,&ovReader,&result_nbr,FALSE)){
ResetEvent(ovReader.hEvent);
Synchronize(ReadData);
}
else
ShowMessage("Ошибка GetOverlapedResult");
}
}while(!Terminated);//do
}
//---------------------------------------------------------------------------
void __fastcall CommRead1::ReadData()
{
buf[result_nbr]=0;
Form1->Memo1->Text =Form1->Memo1->Text+ AnsiString(buf);
}
//-----------------------------------------------------
|
Может ли функция передачи данных в Form1->Memo1 ReadData() настолько задерживать поток, что получаются пропуски приема? Или есть другие причины пропусков?
|
|
Георгий |
Отправлено: 21.04.2005, 07:39 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
знаешь, по логике, у тебя отдельный поток занимается приёмом данных. в этом случае зачем нужен Overlaped режим? убери.
смотрим дальше и.. видим метод ReadData который поток чтения выполняет в основном потоке. т.е. работа твоего высокоприоритетного потока останавливается, разгребаются Windows сообщения и, после них, срабатывает метод ReadData. это время? время, за которое вполне могут 10 байт вылететь из 15 байтного fifo буфера rs232. сделай нормальный промежточный буфер, на несколько десятков пакетов, обложенный семаформами, и пиши в него, а в основном потоке обновление изображения выполняй по таймеру, например каждые 250 ms.
кстати, как порт настроен? на работу с fifo? |
|
nikolayk |
Отправлено: 21.04.2005, 11:54 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
Спасибо Георгий за ответ.
Я подозревал, что метод ReadData может тормозить. Ты советуещь организовать буфер, к которому будет доступ из читающего потока и основной формы. Но как сделать, чтобы совместное использование буфера не вызвало конфликтов и, опять же, потерю данных? Если можно, приведи простой пример.
И как настроить работу COM-порта с fifo?
|
|
Георгий |
Отправлено: 23.04.2005, 00:00 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
тебе нужен кольцевой буфер (очередь) обложенный объектами типа TMultiReadExclusiveWriteSynchronizer.
CODE | TMultiReadExclusiveWriteSynchronizer Synch;
//в потоке чтения порта
Synch.BeginWrite();
queue.Add();//запись в буфер данных
Synch.EndWrite();
//в потоке визуализации
data tmp;
Synch.BeginRead();
tmp=queue.Extract();//извлечение данных из очереди и создание локальной копии этих данных
Synch.EndRead();
visualize(tmp);//только после того, как закончили заботать с буфером отправляем на визуализацию _копию_ данных |
следует заметить, что все операции внутри критической секции (между BeginXXX и EndXXX) должны выполняться быстро. поэтому с данных делаем копию, удаляем оригинал из буфера, разрешаем другим потокам работать с буфером, и только теперь отправляем данные на визуализацию
о поводу очередей — в STL есть объект адаптер queue. он может практически из любого контейнера сделать fifo очередь. подробнее — help->Standart C++ Library Help
fifo — включал очень просто — на машине, куда ПО инсталлировалось, заходил в "панель управления-менеджер устройств-порты" и уже там, в настройках контретного порта, ставил галочку — использовать fifo.
так же есть возможность заказать буферизацию на уровне драйвера — функция SetupComm. но, похоже, у тебя она не работает — по умолчанию буфер должен устанавливаться на 2kB (по крайней мере в win98 было так) попробуй вызвать, сказать, что на 2kB хочешь буфер
забыл сказать, что далеко не факт, что визуализация данных будет идти быстрее чем, чтение их из порта — винда любит иногда что то с винта читать, да и просто тормозить, попутно притормаживая потоки с пользовательскими приоритетами, а поток с приоритетом tpHighest (кстати, я использовал tpRealtime) будет качать и качать данные в буфер.
так что надо предусмотреть корректную обработку переполнения временного буфера и посмореть что делать в этом случае — очистить его и продолжить писать или остановить запись, до очистки буфера. и так же учесть что должен делать блок визуализации в этих случаях.
Отредактировано Георгий — 23/04/2005, 00:12
|
|
nikolayk |
Отправлено: 26.04.2005, 11:47 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
Георгий, спасибо за совет. Я учел твои замечания и использовал критическую секцию и двойную буферизацию.
В потоке чтения порта я сделал так. Из буфера порта buf накапливаю данные в промежуточный буфер ReadBuffer[] размером 1000 байт. Вот функция, которая использована вместо функции Synchronize()
CODE |
//---------------------------------------------------------------------------
void __fastcall CommRead1::UpdateReadBuffer()
{
unsigned int i;
EnterCriticalSection(&CS);
for (i=0;i<result_nbr;i++){
ReadBuffer[i+NumBytesReadBuffer]=buf[i];
}//NumBytesReadBuffer — это количество записанных в ReadBuffer байт
NumBytesReadBuffer=NumBytesReadBuffer+ result_nbr;
LeaveCriticalSection(&CS);
}
|
В потоке визуализации я по таймеру(200мс) в критической секции (быстро) копирую данные из буфера ReadBuffer в буфер ReadBuffer2 и из ReadBuffer2 вывожу на экран
CODE |
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
int i;
EnterCriticalSection(&CS);
//Копирование данных из буфера ReadBuffer[] в
//буфер ReadBuffer2[] размером 1000 байт
if (NumBytesReadBuffer>0){
for (i=0;i<NumBytesReadBuffer;i++){
ReadBuffer2[i] =ReadBuffer[i];
}
NumBytesReadBuffer2=NumBytesReadBuffer;
NumBytesReadBuffer=0;
}
LeaveCriticalSection(&CS);
//Вывод данных из буфера ReadBuffer2[] на экран(Memo1)
if (NumBytesReadBuffer2>0){
ReadBuffer2[NumBytesReadBuffer2]=0;
Memo1->Text =Memo1->Text+ AnsiString(ReadBuffer2);
NumBytesReadBuffer2=0;
SendMessage(Memo1->Handle,WM_VSCROLL, SB_BOTTOM, 0);
}
}
|
Все работает замечательно. Загрузка процессора не более 4%. Но когда я пытаюсь обеспечить прием любых байтов, включая ноль, а не только строк, и делаю вывод на экран так
CODE |
for (i=0;i<NumBytesReadBuffer2;i++){
Memo1->Text =Memo1->Text+ ReadBuffer2[i];
}
|
вместо
CODE |
Memo1->Text =Memo1->Text+ AnsiString(ReadBuffer2);
|
то загрузка процессора сильно возрастает и через некоторое время доходит до 100%.
Что тут можно сделать? |
|
Георгий |
Отправлено: 26.04.2005, 19:27 |
|
Почетный железнодорожник
Группа: Модератор
Сообщений: 874
|
даже и не знаю в чём дело может быть..
в принципе TMemo далеко не резиновое, а ты в него новые строки добавляешь и добавляешь — может оно при перерисовке и начинает тормозить. т.к. выполняешь прокрутку на самую последнюю строку, то думаю остальные тебе не очень то и нужны — попробуй очищать Memo
CODE | Memo1->Lines->Clear(); | кстати, зачем так жестоко текст в Memo добавляешь — есть более гуманные методы:CODE | Memo1->Lines->Add("test string");
вместо
Memo1->Text = Memo1->Text + AnsiString("test string"); |
и ещё:
1. мне не нравится как с ReadBuffer в методе UpdateReadBuffer работаешь. Конечно понимаю, что это эскизная версия, но, тем не менее, проверку на переполнение, на выход за границу буфера не плохо бы реализовать уже сейчас. Вдруг начал по памяти гулять?
2. похоже буферы ты реализовал как обычные массивы тогда копирование из буфера в буфер можно реализовать вызвав функцию memcpy — и быстрее работает и код читать проще.
3. приняв произвольные данные (как понимаю байты в диапазоне 0x00-0xFF ) не хочешь их к текстовому представлению преобразовать? IntToStr что ли. тогда вывод на экран будет вот так выглядеть:CODE | AnsiString tmp;
for (i=0;i<NumBytesReadBuffer2;i++)
{
AnsiString localBuf;
localBuf.printf("%02X ",ReadBuffer2[i]);
tmp+=localBuf;
};
Memo1->Lines->Add(tmp);
| А то вдруг там попадаются байты которые TMemo переварить не может
PS. пропускать при приёме перестал?
Отредактировано Георгий — 26/04/2005, 21:31 |
|
nikolayk |
Отправлено: 28.04.2005, 23:52 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
Спасибо огромное, Георгий, за помощь!
Обязательно поробую все ,что ты предложил.
Пропусков при приеме не стало.
Еще один вопрос.
При приеме текст в MEMO добавляется в нижнюю строку и интересно видеть свежую информацию, т.е. нижние строки. А MEMO все время старается показывать верхние строки. Чтобы видеть нижние строки я посылаю сообщение
CODE |
SendMessage(Memo1->Handle,WM_VSCROLL, SB_BOTTOM, 0);
|
Это вызывает непрерывное дергание MEMO с верхних строк на нижние. Можно ли как-то заставить MEMO все время показывать нижнюю строку? |
|
nikolayk |
Отправлено: 29.04.2005, 16:30 |
|
Ученик-кочегар
Группа: Участник
Сообщений: 19
|
Попробовал
CODE |
Memo1->Lines->Add("test string");
вместо
Memo1->Text = Memo1->Text + AnsiString("test string"); |
Теперь после каждой "test string" добавляется перенос строки.
Но зато всегда стали показываться нижние строки без перескоков на верхние.
|
|
|