![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
сбои в асинхронном приеме с сом порта [D7, XP]
Who_is_you? © (2010-06-22 12:48:00)Посылаю устройству команду прислать данные в комп. Смотрю: светодиод мигает, что данные идут.
Но данные то читаются, то нечитаются. Такое ощущение, что проблема во временах и реакции на событие.
Вот настройки порта и программа чтения:
procedure TForm1.SetupOpenPort(Sender: TObject); //Setup & open порт
Var
TimeOuts : TCommTimeouts; // таймауты порта
begin
ComX:= PChar('COM' + IntToStr(NumCom));
//создание порта и получение его хэндла в асинхронном режиме
hCom:=CreateFile(PChar('\\.\COM' + IntToStr(NumCom)),Generic_Read or GENERIC_WRITE,0,nil,Open_Existing,
File_Attribute_normal or FILE_FLAG_OVERLAPPED,0);
if hCom=Invalid_handle_Value then
begin
ShowMessage('Не могу открыть порт. Задайте параметры порта.');
CloseHandle(hCom); //закрытие порта
end;
PurgeComm(hCom,PURGE_TXABORT or PURGE_RXABORT or
PURGE_TXCLEAR or PURGE_RXCLEAR);
//устанавливаем маску эвентов (фактически маску прерываний)
//в данном случае будем иметь возникновение эвентов по принятию
//хотя бы одного байта и возможности записи в порт еще байт(ов)
if not (SetCommMask(hCom,EV_RXCHAR or EV_DSR or EV_CTS)
//устанавливаем размер внутренних буферов приема-передачи в драйвере порта
and SetupComm(hCom,256,256)
// SetupComm(hCom,BufSize_rd,BufSize_rd);
//получаем текущее DCB порта
//построение DCB и иницализация порта
//что бы не заполнять всю структуру самим,
// сначалf считываем ее, потом поменяем нужные поля
and GetCommState(hCom,DCB)
)
then begin
CloseHandle(hCom);
ShowMessage('Порт не настроен');
Exit;
end;
with DCB do
begin
BaudRate:=9600;
ByteSize:=8;
Parity:=NoParity;
StopBits:=OneStopBit;
end;
if not SetCommState(hCom,DCB) then //устанавливаем DCB
begin
CloseHandle(hCom);
ShowMessage('Порт не настроен');
end;
//получаем текущие параметры таймаутов
GetCommTimeouts(hCom,TimeOuts);
//настраиваем текущие параметры таймаутов таким образом,
//чтобы ReadFile и WriteFile возвращали значения немедленно
TimeOuts.ReadIntervalTimeout := MAXDWORD;
TimeOuts.ReadTotalTimeoutMultiplier := 0;
TimeOuts.ReadTotalTimeoutConstant := 0;
TimeOuts.WriteTotalTimeoutMultiplier := 0;
TimeOuts.WriteTotalTimeoutConstant := 0;
//выполняем настройку порта с новыми таймаутами
if not SetCommTimeouts(hCom,TimeOuts) then
begin
CloseHandle(hCom);
ShowMessage('Tаймауты порта не настроены.');
Exit;
end;
//опускаем флаг завершения дочернего потока
CloseReadComm := False;
end;
procedure ReadComm;
var
i :byte;
Trans, ModemState : DWord;
begin
i_wr:= 0;
//выполняем инициализацию структуры для асинхронного режима
FillChar(Ovr,SizeOf(TOverlapped),0);
Ovr.hEvent := CreateEvent(nil,TRUE,FALSE,#0);
while not CloseReadComm do
begin
for i:=1 to BufSize_Data_rd do Data_rd[i]:=0; // ??
//**
Mask := 0;
if not WaitCommEvent(hCom,Mask,@Ovr) then //ждем
begin
if ERROR_IO_PENDING = GetLastError() then
begin
//это случай, когда ожидание переводится в фоновый режим
//и для получения маски эвентов нужно дождаться
//выставления эвента Ovr.hEvent в сигнальное состояние
//и вызвать GetOverlappedResult
if WaitForSingleObject(Ovr.hEvent,INFINITE)= WAIT_OBJECT_0 then
GetOverlappedResult(hCom,Ovr,Trans,False);
end;
end;
if CloseReadComm then break;
if (Mask and EV_RXCHAR)=EV_RXCHAR then //проверяем нужное событие
begin
FlagRead:=true; //Прием данных
ClearCommError(hCom,Errs,@Stat);//сбрасываем флаг, читаем Stat
byteRead := Stat.cbInQue;
ReadFile(hCom,Data_rd,byteRead,byteRead,@Ovr);//читаем
// обработка принятой информации из Data_rd
if byteRead > BufSize_Data_rd then
byteRead:=BufSize_Data_rd;
for i:=1 to byteRead do
begin
i_wr := i_wr+1;
Data_wr[i_wr]:=Data_rd[i];
end;
if (i_wr=i_wrEND) or (i_wr>i_wrEND) then //??
begin
i:=3;
while i
Вариант (2010-06-22 15:54:00) [1]
> RWolf © (22.06.10 15:51) [8]
Увы это так, в свое время что только не пробовал, и маску менять ... но это такой неприятный факт для NT систем,для 95 и 98 уже не помню как
> tesseract © (22.06.10 15:51) [9]
В синхронном режиме WaitCommEvent ?
RWolf (2010-06-22 15:51:00) [2]
> Вариант (22.06.10 15:24) [7]
Попробовал — не работает, и даже вешает весь процесс. Увы мне.
Нелогично, конечно — из ReadFile/WriteFile выкидывает, а из WaitCommEvent — нет.
tesseract (2010-06-22 15:51:00) [3]
> ПРобовал?
Да. Возвращает ошибку типа "операция прервана".
Вариант (2010-06-22 15:24:00) [4]
> RWolf © (22.06.10 15:21) [6]
ПРобовал?
RWolf (2010-06-22 15:21:00) [5]
> Вариант (22.06.10 15:14) [5]
> синхронном режиме WaitCommEvent
> проблемный вызов из-за возможностью долго висеть.... и так
> и не отвиснуть.
Чтобы WaitCommEvent завершился с кодом ошибки, достаточно закрыть переданный ему хэндл в любом из работающих потоков.
Вариант (2010-06-22 15:14:00) [6]
> RWolf © (22.06.10 14:58) [4]
В синхронном режиме WaitCommEvent проблемный вызов из-за возможностью долго висеть.... и так и не отвиснуть. Его можно не использовать конечно, но overlapped режим все же удобнее(гибче) для COM порта когда надо действительно отслеживать несколько типов событий, и конечно когда правильно им пользуешься, .
RWolf (2010-06-22 14:58:00) [7]
> синхронный всегда в ожидании,а асинхронный по приходу события:
> потому меньше загружает комп.
синхронный ReadFile всегда в ожидании, именно поэтому он не загружает процессор вовсе.
Вариант (2010-06-22 14:54:00) [8]
> Who_is_you? © (22.06.10 12:48)
Проблемный код, слишком много разной по функциональности логики в одной процедуре ReadComm. Удобней было бы разбить на разные функции, читать тяжело.
Почти нигде нет анализа возврата функций на возвращаемое значение (не предусматривается возможность возврата ошибки)
КРоме того есть вопрос к тому что я заметил:
Ovr.hEvent := CreateEvent(nil,TRUE,FALSE, #0);-что бы создать неименованный Event, надо передать nil, а не #0 Не знаю как это будет ли это валидным именем в NT подобных системах, поэтому если и ты не знаешь, то думаю следует заменить. Далее
> if WaitForSingleObject(Ovr.hEvent,INFINITE)= WAIT_OBJECT_0
> then
> GetOverlappedResult(hCom,Ovr,Trans,False);
А если не равно WAIT_OBJECT_0....??? То все равно продолжим выполнение кода без GetOverlappedResult.
ClearCommError(hCom,Errs,@Stat);//сбрасываем флаг, читаем Stat
byteRead := Stat.cbInQue;
ReadFile(hCom,Data_rd,byteRead,byteRead,@Ovr);//читаем
Какое значение вернули функции мы не знаем. И хотя скорее всего true для ClearCommError, и хотя скорее всего ReadFile вернет true, так как читаем уже то, что есть в буфере - но дать 100 % за это я не могу. А потому следовало бы делать проверку.
if byteRead > BufSize_Data_rd then
byteRead:=BufSize_Data_rd;
Не знаю бывает ли у тебя ситуация, когда число пришедших из порта байт больше буфера.... но если да, ты просто взял и выкинул часть данных.... Насколько это верно?
Да и вызывают сомнение переменные типаFlagReadData:=true; //пришли данныеГде они потом используются и как? Это сигнализация в другой поток? Если да, то как там эта переменная используется, если она там пишется - то это уже не правильно....
Who_is_you? (2010-06-22 14:53:00) [9]Ранее работал в синхронном режиме с СОМ портом, но тоже были подобные проблемы в ХР.
Сейчас перешел на асинхронный, а он задается через FILE_FLAG_OVERLAPPED.
Насколько я понимаю синхронный всегда в ожидании,
а асинхронный по приходу события: потому меньше загружает комп.
RWolf (2010-06-22 13:15:00) [10]а обязательно использовать Overlapped-режим? это нужно только, если пишем в файл из двух и более потоков.