Перейти к содержанию
    

Работа с СОМ портом

... классный класс.
Да неплохой. Использовал под C++Builder что-бы не нарушать лицензионного соглашения.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

в архиве cport310 есть файл readme.txt всё (правда по английски) написано

 

а если кратко, то так (на примере Delphi 7):

- открываем в Delphi файл cport-3.10\sources\CPortLib7.dpk

- в открывшемся окне "Package - CPortLib7.dpk" жмём кнопку "Compile"

- закрываем окно "Package - CPortLib7.dpk", но запрос "Сохранить" жмём "Нет"

- открываем в Delphi файл cport-3.10\sources\DsgnCPort7.dpk

- в открывшемся окне "Package - DsgnCPort7.dpk" жмём кнопку "Compile", потом "Install"

- закрываем окно "Package - DsgnCPort7.dpk", но запрос "Сохранить" жмём "Нет"

- проверяем на вкладке компонентов наличие вкладки "CPortLib"

- все файлы cport которые будет требовать компилятор копируем из cport-3.10\sources\ в C:\Program Files\Borland\Delphi7\Projects\Bpl\, как правило это *.dcu, *.dfm, *.pas, CPort.inc, CPortImg.res

 

вроде всё

Изменено пользователем dm37

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Добрый день!

Пишу программу для асинхронного приема в ком порт.  Прием в целом идет успешно, но возникает глюк при смене порта. То есть при асинхронном ожидании приема, порт закрывается, открывается другой, создается новый поток. Ну в общем все действия которые были с предыдущим портом. В этот момент и возникает дефект.

Эксперименты и отладка показали что проблема возникла от того что поток висит в несигнальном состоянии после вызова WaitForSingleObject. Из за этого поток не завершается, и по цепочке идут другие ошибки.

Итак вопрос. Как можно принудительно завершить ожидание по WaitForSingleObject?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 часов назад, paskal сказал:

Итак вопрос. Как можно принудительно завершить ожидание по WaitForSingleObject?

Заменить WaitForSingleObject на WaitForMultipleObjects. В список ожидаемых объектов добавить свой event, его сигналить при необходимости завершить работу потока.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

22 часа назад, paskal сказал:

Итак вопрос. Как можно принудительно завершить ожидание по WaitForSingleObject?

Послать ему event конечно же. А вы чего ожидали?

Ещё можно таймаут установить.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 hours ago, jcxz said:

Послать ему event конечно же. А вы чего ожидали?

Ну я и посылал через SetEvent. Ничего не произошло. И я поискал информацию - выходит и не должно. Пока поток спит в WaitForSingleObject, сигнальное состояние нельзя установить. Заколдованный круг.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

15 hours ago, AHTOXA said:

Заменить WaitForSingleObject на WaitForMultipleObjects. В список ожидаемых объектов добавить свой event, его сигналить при необходимости завершить работу потока.

А можно подробнее как завершать поток с использованием WaitForMultipleObjects ?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я делал из MFC ручками без WaitForMultipleObjects и аналогичных функций:

1. Заводил глобальный указатель на переменную lpvNeedReceive - флаг управления потоком, № Com-порта (инициировал lpvNeedReceive при открытии порта).

2. В потоке приема данных заводил цикл while(*(UINT *)lpvNeedReceive != 0), в котором делаю WaitCommEvent - ожидание одного из событий EV_RXCHAR|EV_TXEMPTY|EV_BREAK.

3. По EV_RXCHAR принимаю информацию обычным образом.

4. Если нужно закрыть порт и приемный поток, в основном потоке делаю *lpvNeedReceive = 0, отправляю в порт ничего не значащий нулевой байт и жду изменения *lpvNeedReceive.

5. Приемный поток выходит из WaitCommEvent  и из цикла по п.2, закрывает компорт и в конце своей работы устанавливает *lpvNeedReceive = 0xFF.

6. Основной поток закрывает приемный поток

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 часов назад, paskal сказал:

Ну я и посылал через SetEvent. Ничего не произошло. И я поискал информацию - выходит и не должно. Пока поток спит в WaitForSingleObject, сигнальное состояние нельзя установить.

Вы сами то поняли смысл того что написали? По вашему выходит что WaitForSingleObject вообще нельзя использовать.  :wacko2:

Если поток ждёт event-а в WaitForSingleObject, то естественно он должен переходить в активное состояние после того как кто-то сделает SetEvent для этого объекта. Или истечёт таймаут WaitForSingleObject.

Ищите где накосячили.

1 час назад, V_G сказал:

отправляю в порт ничего не значащий нулевой байт и жду изменения *lpvNeedReceive.

Зачем такие кривые костыли? Достаточно просто установить сигнальное состояние для event-а вручную и всё.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

28 минут назад, jcxz сказал:

Зачем такие кривые костыли? Достаточно просто установить сигнальное состояние для event-а вручную и всё.

Согласен, но софт компорта писался лет 20 назад (почти сразу же, как появилась многопоточность) и с тех пор особо не менялся, бо работает. Я и сейчас-то не все виндовые функции знаю наизусть, а тогда - и подавно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

10 часов назад, paskal сказал:

А можно подробнее как завершать поток с использованием WaitForMultipleObjects ?

Создайте ещё один Event:

HANDLE terminateEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

Замените свой WaitForSingleObject(oldEvent, ...) на

HANDLE events[] = {oldEvent, terminateEvent};

WaitForMultipleObjects(2, events,...);

И вы сможете в любой момент вывести поток из ожидания, взведя terminateEvent.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

24 минуты назад, V_G сказал:

Согласен, но софт компорта писался лет 20 назад (почти сразу же, как появилась многопоточность) и с тех пор особо не менялся, бо работает. Я и сейчас-то не все виндовые функции знаю наизусть, а тогда - и подавно.

Тоже писалось ровно 10 лет назад, использовалось во множестве проектов: :wink:

Спойлер

// Receiver thread
//---------------------------------------------------------------------------
void __fastcall TCommRxThread::Main()
{
  DWORD i, ii, j;
  do {
    EnterCriticalSection(&cs);
    terminate = amount = 0;
    LeaveCriticalSection(&cs);
    if (purge) {
      purge = false;
      SetEvent(evPurge);
    }
    SetEvent(hServiceEvent);
    while (1) {
      j = 0;
      do {
        ReadFile(hComm, buf + posIObuf + j, bufSize - j, &i, &ovr);
        if (!terminate) ii = GetOverlappedResult(hComm, &ovr, &i, true);
        if (terminate) break;
        if (!ii) {
          if (OnError) Synchronize(OnError);
          i = 0;
        }
        j += i;
      } while (!j || (j < bufSize && amount));
      WaitForSingleObject(hServiceEvent, INFINITE);
      if (terminate) break;
      EnterCriticalSection(&cs);
      amount = j;
      posServBuf = posIObuf;
      posIObuf ^= bufSize;
      LeaveCriticalSection(&cs);
      if (OnReady) Synchronize(OnReady);
    }
    PurgeComm(hComm, PURGE_RXABORT | PURGE_RXCLEAR);
  } while (purge);
}

uint __fastcall TCommRxThread::Read(char *data, uint size)
{
  EnterCriticalSection(&cs);
  if (size >= amount) {
    SetEvent(hServiceEvent);
    size = amount;
  }
  memcpy(data, buf + posServBuf, size);
  posServBuf += size;
  amount -= size;
  LeaveCriticalSection(&cs);
  return size;
}

void __fastcall TCommRxThread::Purge()
{
  if (terminate) return;
  purge = true;
  EnterCriticalSection(&cs);
  Terminate();
  LeaveCriticalSection(&cs);
  WaitForSingleObject(evPurge, INFINITE);
}

void __fastcall TCommThread::Terminate()
{
  terminate = true;
  SetEvent(hServiceEvent);
  SetEvent(ovr.hEvent);
}

class TCommRxThread : public TCommThread {
...
};  

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Всем добрых суток !
У меня проблема приема данных по СОМ порту. Данные выдаются пакетом из устройства в порт ПК каждые 100 мс размером 2500 байт. Но происходит потеря данных, вернее последовательность данных при чтении буфера порта нарушается, скажем так, последний номер пакета был с номером 2000, а при следующем чтении порта начинается не 2001, а ,например, 2020.
Настройка порта следующая:
8 бит;
Контроль четности - нет;
Число стоп бит - 1;
Скорость обмена -460800;
Таймауты по умолчанию, кроме MAXWORD все нули.
Размер буфера порта 4096, увеличивал до 15000.
В программе сначала использовал библиотеку Async32, пробовал делать свой поток с функциями API (ReadFile), тут результат еще хуже, чтение производится, то 14000 байт, то 45 байт, что приводит к потере.
Чтение происходит по событию RXchar.

Выкладываю код в потоке.

procedure MyThread.execute;
 var S: string; i:integer;
begin
  
  //inherited;
  OverRead.hEvent:=CreateEvent(nil,True,True,nil);
  while not MyThr.Terminated do
  begin
   WaitCommEvent(FHandle,Mask,@OverRead);
   signal:=WaitForSingleObject(OverRead.hEvent,infinite);
   if(signal=Wait_Object_0) then
    begin
     if GetOverlappedResult(FHandle,OverRead,Temp,true) then
      begin
       if((Mask and EV_RXchar)<>0) then
        begin
         ClearCommError(FHandle,Temp,@ComStat);
         Btr:=ComStat.cbInQue;
          if Btr<>0 then
           begin
             ReadFile(FHandle,Buffer,SizeOf(Buffer1),Temp,@OverRead);
           
           end;
        end;
      end;
    end;

Еще один вариант. Использовал библиотеку API для устройств FTDI. 

Функция в потоке.

Function Drive:Boolean;
var B:Byte; Sgn,RR,BT : DWORD; CS : TComStat; i:integer;
 ReadOL : TOverLapped;

begin
   while not FReadThread.Terminated do  
    begin
     FillChar(ReadOL,SizeOf(ReadOL),0);
     ReadOL.hEvent:=CreateEvent(nil,True,True,nil);
     FT_W32_ReadFile(FHandle,B,1,RR,@ReadOL);          
     Sgn:=WaitForSingleObject(ReadOL.hEvent,INFINITE); 
     Result:=False;
     if (Sgn=WAIT_OBJECT_0) then                       
      begin

       if GetOverlappedResult(FHandle,ReadOL,BT,False)
        then
         begin    
           PutS(inp,B);    // очередной байт сообщения
     
         end;
      end;
  
   FT_W32_CloseHandle(ReadOL.hEvent); 
  end;
end;

Здесь картина несколько иная. Если читаю в буфер, то прием хоть и фиксированный, но потери такие же как описал выше. А если читаю по одному байту, то читается как надо, но постоянно до определенного момента (около 4000 пакетов принимаются как надо), потом идут потери данных, вероятно буфер драйвера все-же переполняется.


Хотелось бы узнать, реально читать данные при такой скорости и настройке без потерь или в Windows это дело гиблое? И есть ли еще инструменты читать данные без потерь?

Пожалуйста, помогите, буду признателен за любую помощь

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У вас какая-то каша в коде... Вызывать нужно ReadFile(), а затем - GetOverlappedResult(). А не наоборот. Советую прочитать описание WinAPI.

А всякие WaitCommEvent(), ClearCommError(), WaitForSingleObject() вообще не понятно с какого рожна приплетены? Это же поток читающий символы из UART. Вот это он и должен делать.

 

1 час назад, klod сказал:

И есть ли еще инструменты читать данные без потерь?

Инструмент только один: внимательное чтение документации на WinAPI.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 часа назад, klod сказал:

. . . . Данные выдаются пакетом из устройства в порт ПК каждые 100 мс размером 2500 байт. Но происходит потеря данных 

. . . .
Скорость обмена -460800;

Таймауты по умолчанию, кроме MAXWORD все нули.

. . . 
Хотелось бы узнать, реально читать данные при такой скорости и настройке без потерь или в Windows это дело гиблое? И есть ли еще инструменты читать данные без потерь?

Попробуйте поработать с таймаутами, для этого размер буфера поставьте заведомо больше ваших 2500, например 50000, тогда функция будет отрабатывать не по заполнению буфера, а по отработке таймаута (они должны соотв-ть длительности пакета и паузы между ними). Их там (таймаутов) задается 3 (насколько помню). Это работало для небольших скоростей, до 115200. Насколько эффективно это, и будет ли работать для 460800 сказать не могу. Отладьте работу на низкой скорости, затем пробуйте на высокой.  

Скорость достаточно высокая, но драйвер порта должен обеспечивать получение данных. Вопрос в правильной его настройке и своевременном считывании данных из буфера драйвера. 

Инфорамция к размышлению:

See also SetCommTimeouts function, etc &nbsp;https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setcommtimeouts
--------------
COMMTIMEOUTS
Communications Functions
Communications Resources
GetCommTimeouts

SetCommTimeouts
ReadFile
ReadFileEx
WriteFile
WriteFileEx

Структура DCB
 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...