C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
проблема чтением из COM порта, загрузка проца на 100% при опросе CОM по
** slan
Отправлено: 06.07.2006, 17:08


Не зарегистрирован







в приложении постоянно опрашивается компорт и считываются с него приходящие данные, вот такой функцией

CODE

unsigned char readcom()
{
unsigned char chRead;
DWORD dwRead = 0;
// Начинаем чтение
do
{
  ReadFile(hCom, &chRead, 1, &dwRead, NULL);
  // sleep(5);
}
while ((!dwRead) && (GetLastError() != ERROR_IO_PENDING));

return chRead;
}


именно такая обработка приводит к 100% загрузке процесора, как этого избежать ? чтобы не терять токо данные приходящие в ком порт, это однин из работающих потоков в приложении, другие потоки отправляют данные в ком, но не грузят его.

Если раскоментить // sleep, то загрузка спадает но данные теряются
Konstantine
Отправлено: 06.07.2006, 23:43


Мастер участка

Группа: Модератор
Сообщений: 545



разберитесь с какой скоростью передаются данные?
если 5 мс за шаг — то это 200 Байт/сек т.е. ~25 кБ/сек
если скорость Ваших данных больше, то попробуйте уменьшить время задержки на подходящее значение, и неплохо бы запас иметь.
писать отдельный поток и ставить его в Idle (я так сразу подумал) — нельзя, т.к. при загрузке проца другой программой будет 100% потеря данных.
Asher
Отправлено: 07.07.2006, 08:49


Мастер участка

Группа: Модератор
Сообщений: 550



Привет.

Почитайте про overlapped режим работы с Com-портом
Хоть в MSDN, хоть в Internete.
slan
Отправлено: 07.07.2006, 09:23


Ученик-кочегар

Группа: Участник
Сообщений: 3



Konstantine, скорость 9600

Asher, сейчас почитаю
Георгий
Отправлено: 22.07.2006, 23:10


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



у нас в форуме есть примеры кода, которой постоянно из порта читает и не грузит проц на 100%.

если в кратце, от работаешь с портом в потоке и открываешь порт в режиме чтения с ожиданием. в этом случае загрузка будет около 2-3% на свежих машинах и 5-10% на древних вроде P200mmx. но нужен современный контролер com порта — мультикарта на isa не подойдёт.

гм... только сейчас заметил дату поста smile.gif

Отредактировано Георгий — 23/07/2006, 00:10
Михаил
Отправлено: 18.01.2007, 09:31


Ученик-кочегар

Группа: Участник
Сообщений: 1



QUOTE (Георгий @ 22.07.2006, 23:10)
у нас в форуме есть примеры кода, которой постоянно из порта читает и не грузит проц на 100%.


Георгий!
Вы писали: "в форуме есть примеры кода, которой постоянно из порта читает и не грузит проц на 100%."
Можене мне скинуть эти примеры кода (или ссылки на них) по E-mail?
А может у Вас есть готовый модуль(компонент) для работы с СОМ-портом?
Помогите,пожалуйста!!!
Я работаю в С++ Builder 6, под WinXP.
Георгий
Отправлено: 19.01.2007, 02:20


Почетный железнодорожник

Группа: Модератор
Сообщений: 874



QUOTE (Михаил @ 18.01.2007, 10:31)
QUOTE (Георгий @ 22.07.2006, 23:10)
у нас в форуме есть примеры кода, которой постоянно из порта читает и не грузит проц на 100%.


Георгий!
Вы писали: "в форуме есть примеры кода, которой постоянно из порта читает и не грузит проц на 100%."
Можене мне скинуть эти примеры кода (или ссылки на них) по E-mail?
А может у Вас есть готовый модуль(компонент) для работы с СОМ-портом?
Помогите,пожалуйста!!!
Я работаю в С++ Builder 6, под WinXP.

пример кода работающего по принципу запрос-ответ.
вычищать от остатков моего кода не стал — ничего секретного у нём нет, а ляпы, особенно с оператором goto, полезно посмотреть.

код, с которого начинается эта вертка — правильный, но, наверняка, автор не настроил таймауты — вот и выходит 100% загрузка проца.
я у себя настраиваю таймауты и код работает так:
1 формирую запрос на чтение (вызов readfile)
2 ОС передаёт запрос на чтение драйверу
3 драйвер напрягает контроллер com порта, что б он сгенерил прерывание, как получит 1 байт
4 драйвер ловит все прерывания контроллера и складывает байтики в свой буфер в ОЗУ
5 как только драйвер не получил прерывания от порта в течение таймаута, то он пинает ОС
6 ОС копирует данные из буфера драйвера в буфер переданный функции readfile и пинает (будит) поток
усё.
единственное отличие моего кода от вашего — у меня таймауты не нулевые и, если из порта ничего не приходит, то ЦПУ занимается другими задачами — вот поэтому и нет 100% загрузки.
а в вашем случае вы постоянно дёргаете драйвер — "скажика там есть что-нибудь?", вместо того, что б попросить драйвер — "разбуди меня, когда что-нибудь придёт."
обратите внимание на код настраивающий порт.
а когда будите свою реализацию делать, то не забудьте о правильной, не блокирующей основной поток на длительное время, синхронизации передачи данных из потока опроса порта и основного потока. а то всё распараллеливание работы с портом пойдёт насмарку.

код работы с портом:
CODE
void ShowLastError(void)
{
LPVOID lpMsgBuf;

FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);

// Display the string.
MessageBox( NULL,(char*) lpMsgBuf, "GetLastError", MB_OK|MB_ICONINFORMATION );

// Free the buffer.
LocalFree( lpMsgBuf );
};

Device ::Device(void)
{
this->hCom=INVALID_HANDLE_VALUE;
this->PackageNumber=0;
this->OutPutBuffer=0;
this->InPutBuffer=0;
};

Device ::~Device(void)
{
};

bool Device::Connect(const AnsiString& ComName,const AnsiString& CfgString,char StartByte,char DeviceId)
{
this->StartByte=StartByte;
this->DeviceId=DeviceId;
_COMMTIMEOUTS ctmo;
DCB dcb;
COMMCONFIG cc;
cc.dwSize=sizeof (COMMCONFIG);
cc.wVersion=1;
cc.dwProviderSubType=PST_RS232;
cc.dwProviderOffset=0;
cc.dwProviderSize=0;

//по непонятным причинам функция BuildCommDCB не заполняет ВСЕ поля
//структуры описания контекста устройства так что заполняем их
//своими руками (копия того, что устанавливает CommConfigDialog)
dcb.DCBlength=28;

dcb.BaudRate=38400;
dcb.fBinary=1;
dcb.fParity=1;
dcb.fOutxCtsFlow=0;
dcb.fOutxDsrFlow=0;
dcb.fDtrControl=1;
dcb.fDsrSensitivity=0;
dcb.fTXContinueOnXoff=0;
dcb.fOutX=0;
dcb.fInX=0;
dcb.fErrorChar=0;
dcb.fNull=0;
dcb.fRtsControl=0;
dcb.fAbortOnError=0;
//dcb.wReserved;//это поле по непонятным причинам не находит компилятор
*(&dcb.BaudRate+2)=0;//приходится обнулять таким образом
dcb.XonLim=2048;
dcb.XoffLim=512;
dcb.ByteSize=8;
dcb.Parity=0;

dcb.StopBits=2;
dcb.XonChar=17;
dcb.XoffChar=19;
dcb.ErrorChar=0;
dcb.EofChar=0;
dcb.EvtChar=0;

if (hCom!=INVALID_HANDLE_VALUE) CloseHandle(this->hCom);
this->hCom=CreateFile(
ComName.c_str(),
GENERIC_READ|GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
0,
0);

if (!BuildCommDCB(CfgString.c_str(),&dcb))
ShowLastError();

if (!SetCommState(this->hCom,&dcb))
ShowLastError();
return true;
};

bool Device::DisConnect(void)
{
if (hCom==INVALID_HANDLE_VALUE)
return false;
return CloseHandle(hCom);
};
void Device::SetTimeOuts(const DeviceData* d)
{
_COMMTIMEOUTS ctmo;
ctmo.ReadIntervalTimeout=0;
ctmo.ReadTotalTimeoutMultiplier=0;
ctmo.ReadTotalTimeoutConstant=d->GetInputTimeOut();
ctmo.WriteTotalTimeoutMultiplier=0;
ctmo.WriteTotalTimeoutConstant=d->GetOutputTimeOut();
SetCommTimeouts(hCom,&ctmo);
};

void Device::TransmitRecive(DeviceData* d)
{
DWORD i,size,nBytes;
size=d->GetOutputSize();
this->OutPutBuffer=new char[size+4+2];
this->PackPackage(d);
this->SetTimeOuts(d);

PurgeComm(this->hCom,PURGE_TXCLEAR|PURGE_RXCLEAR);
WriteFile(hCom,this->OutPutBuffer,4+size+2,&nBytes,NULL);
if (nBytes!=(size+2+4))
{
AddEvent(InvalidWriteLen,nBytes);
if (!d->OnWriteFauled())
goto TransmitReciveAbort;
};

size=d->GetInputSize();
this->InPutBuffer=new char[4+size+2];
ReadFile(hCom,this->InPutBuffer,4+size+2,&nBytes,NULL);
if (nBytes!=(size+4+2))
{
AddEvent(InvalidLen,nBytes);
d->OnReadFauled();
}
else
this->AnalizePackage(d);

TransmitReciveAbort:
delete this->OutPutBuffer;
delete this->InPutBuffer;
};

DeviceData ::DeviceData()
{
};
DeviceData ::~DeviceData(){};
bool DeviceData::OnWriteFauled(void)
{
return false;
};
void DeviceData::OnReadFauled(void){};


код выполняющийся в параллельном потоке:
CODE
void __fastcall TBackgroundReq::Execute()
{
DeviceData *dd;
QueryData* qd;
DWORD StartTime,EndTime,WorkTime,SecondReqTime;
while (this->ContinueWork)
{
StartTime=GetTickCount();
if (!this->ReqQuery[0]->IsEmpty())
{
qd=this->ReqQuery[0]->ReadReq();
dd=dynamic_cast<DeviceData*>(qd);
if (dd)
{
this->Controller->TransmitRecive(dd);
this->ReqQuery[0]->FreeReq();
};
};
EndTime=GetTickCount();
WorkTime=this->CalculateWorkTime(StartTime,EndTime);
if (WorkTime>=(this->CycleTime-this->ReqTime))
{//нет времени на очередной запрос
if (WorkTime<this->CycleTime)
Sleep(this->CycleTime-WorkTime);
}
else
{//как минимум есть время на один запрос
//ожидание следующего строба
if (this->ReqTime>WorkTime)
Sleep(this->ReqTime-WorkTime);
while (WorkTime<(this->CycleTime-this->ReqTime))//пока есть хоть один период
{
SecondReqTime=GetTickCount();
if (!this->ReqQuery[1]->IsEmpty())
{
qd=this->ReqQuery[1]->ReadReq();
dd=dynamic_cast<DeviceData*>(qd);
if (dd)
{
this->Controller->TransmitRecive(dd);
this->ReqQuery[1]->FreeReq();
};
}
SecondReqTime=this->CalculateWorkTime(StartTime,EndTime);//время работы подзапроса
if (this->ReqTime>SecondReqTime)
Sleep(this->ReqTime-SecondReqTime);
WorkTime=GetTickCount()-StartTime;//общее время работы
};// while
}//выполнение подзапросов
};//общий цикл выполнения
}

ps сейчас я в консалтинг ушёл, так что непосредственно о программировании лучше не меня спрашивать, а тех, кто до сих пор программирует, я же последние 2 года только в ворде пишу.

pps sleep`а в коде потока не пугайтесь — он выполняет разрежение потока запросов в железку — если я свою железку слишком часто опрашивал, то её глючило (сторожевой таймер посылал железку на ресет) и потому я, если с момента ответа на предыдущий запрос прошло слишком мало времени, предпочитал поспать ещё до начала следующего периода опроса, чем дёргать железку.

Вернуться в Работа с внешними устройствами