Jump to content
    

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

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

Share this post


Link to post
Share on other sites

в архиве 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

 

вроде всё

Edited by dm37

Share this post


Link to post
Share on other sites

Добрый день!

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

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

Share this post


Link to post
Share on other sites

3 hours ago, jcxz said:

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

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

Share this post


Link to post
Share on other sites

15 hours ago, AHTOXA said:

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

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

Share this post


Link to post
Share on other sites

Я делал из 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. Основной поток закрывает приемный поток

Share this post


Link to post
Share on other sites

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

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

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

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

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

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

Share this post


Link to post
Share on other sites

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

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

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

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

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

HANDLE events[] = {oldEvent, terminateEvent};

WaitForMultipleObjects(2, events,...);

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

Share this post


Link to post
Share on other sites

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 {
...
};  

 

 

Share this post


Link to post
Share on other sites

Всем добрых суток !
У меня проблема приема данных по СОМ порту. Данные выдаются пакетом из устройства в порт ПК каждые 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 это дело гиблое? И есть ли еще инструменты читать данные без потерь?

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

Share this post


Link to post
Share on other sites

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

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

 

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

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

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

Share this post


Link to post
Share on other sites

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
 

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...