Конференция "WinAPI" » "Принудительная" запись в COM-порт [D7, WinXP]
 
  • Alex_C (24.11.09 08:35) [0]
    В отдельном треде раз в N-милисекунд посылаю в COM-порт определенную комманду. Комманда очень короткая - всего 3 символа. Посылку контролирую при помощи монитора COM-портов HHD Free Serial Port Monitor. Так вот - пока время между коммандами "большое" - комманды идут как и положено - одна за другой. Но при уменьшении промежутка времени меньше какой то величины - комманды "накопливаются", и поступают в COM-порт как бы пакетом (сразу порядка 20 "накопленных" комманд.) Вопрос: как этого избежать и контролировать чтоб комманты шли не пакетами? Время между коммандами должно быть максимально маленьким.
  • Leonid Troyanovsky © (24.11.09 08:49) [1]

    > Alex_C   (24.11.09 08:35)  

    http://pda.delphimaster.net/?id=1258959429&n=18
    See [5]

    --
    Regards, LVT.
  • Alex_C (24.11.09 08:53) [2]

    > Leonid Troyanovsky


    Да я это помню! Но есть одно НО: к оборудованию, которым я управляю по ком-порту идет "пробная" программа. Так вот: если в этой программе я задаю допустим интервал 100Мс - там все идет как надо, не пакетами, а по одной комманде. А если делаю у себя в программе - пакетами. Выходит все же как то это можно контролировать?
  • Alex_C (24.11.09 08:58) [3]
    Возможно это поможет:
    я пишу в ком-порт при помощи этой ф-ции


    function TComPort.WriteBytes(ByteArray: Pointer; Count: integer): cardinal;
    var
     Signaled, RealWrite, BytesTrans: cardinal;
     WriteOL: TOverLapped; {структура для асинхронной записи}
     Success: boolean;
    begin
     Result := 0;

     {создание события для асинхронной записи}
     FillChar(WriteOL, SizeOf(WriteOL), 0);
     WriteOL.hEvent := CreateEvent(nil, True, True, nil);

     try
       {начало асинхронной записи}
       WriteFile(FHandle, ByteArray^, Count, RealWrite, @WriteOL);
       {ожидания завершения асинхронной операции}
       Signaled := WaitForSingleObject(WriteOL.hEvent, INFINITE);
       {получение результата асинхронной операции}
       Success  :=
         (Signaled = WAIT_OBJECT_0) and
         (GetOverlappedResult(FHandle, WriteOL, BytesTrans, False));
       if not Success then
         raise Exception.Create('Can not write to COM port!');
       Result := RealWrite;
     finally
       {освобождение дескриптора события}
       CloseHandle(WriteOL.hEvent);
     end;
    end;



    Ф-ция взята из очень хорошей книги "Работа с последовательным портом" - кажется так она называется.
  • Leonid Troyanovsky © (24.11.09 09:16) [4]

    > Alex_C   (24.11.09 08:58) [3]

    > я пишу в ком-порт при помощи этой ф-ции

    Пардон, а зачем писать асинхронно? Если уж 3 байта.

    --
    Regards, LVT.
  • Сергей М. © (24.11.09 09:24) [5]
    Что за мода такая - не анализировать рез-т вызова API-функциии ?

    Ведь сказано же в справке :

    If hFile was opened with FILE_FLAG_OVERLAPPED and lpOverlapped is not NULL, ..WriteFile may return before the write operation has been completed. In this case, WriteFile returns FALSE and the GetLastError function returns ERROR_IO_PENDING

    may - это не must !
  • MBo © (24.11.09 09:31) [6]
    FlushFileBuffers для порта работает?
  • Alex_C (24.11.09 09:49) [7]

    > Что за мода такая - не анализировать рез-т вызова API-функциии
    > ?


    Поверил умной книжке - книга действительно классная и плюс единственная по работе с последовательными портами, где все так хорошо рассказано :)


    > ..WriteFile may return before the write operation has been
    > completed. In this case, WriteFile returns FALSE and the
    > GetLastError function returns ERROR_IO_PENDING


    ...WriteFile может возвратить результат до того, как операция будет завершена. В этом случае WriteFile возвратит False и функция GetLastError возвратит ERROR_IO_PENDING.

    Получается надо так:


    function TComPort.WriteBytes(ByteArray: Pointer; Count: integer): cardinal;
    var
     Signaled, RealWrite, BytesTrans: cardinal;
     WriteOL: TOverLapped; {структура для асинхронной записи}
     Success: boolean;
     y: Integer;
    begin
     Result := 0;

     {создание события для асинхронной записи}
     FillChar(WriteOL, SizeOf(WriteOL), 0);
     WriteOL.hEvent := CreateEvent(nil, True, True, nil);

     try
       {начало асинхронной записи}
       if not WriteFile(FHandle, ByteArray^, Count, RealWrite, @WriteOL) then
       //*********** Результат False - операция WriteFile не завершена
       begin
          y := GetLastError;
          if y = ERROR_IO_PENDING then
            WaitForSingleObject(WriteOL.hEvent, INFINITE);
       end
       else
         {ожидания завершения асинхронной операции}
         Signaled := WaitForSingleObject(WriteOL.hEvent, INFINITE);
       {получение результата асинхронной операции}
       Success  :=
         (Signaled = WAIT_OBJECT_0) and
         (GetOverlappedResult(FHandle, WriteOL, BytesTrans, False));
       if not Success then
         raise Exception.Create('Can not write to COM port!');
       Result := RealWrite;
     finally
       {освобождение дескриптора события}
       CloseHandle(WriteOL.hEvent);
     end;
    end;

  • Alex_C (24.11.09 09:52) [8]

    > FlushFileBuffers для порта работает?


    Попробовал - не помогает!
  • Сергей М. © (24.11.09 10:18) [9]

    > Получается надо так


    Не получается.

    Если WriteFile вернула True, то это означает, что запрошенная операция была успешно выполнена синхронно. Какой смысл ждать сигнал ивента и следом читать overlapped-результат, если операция заведомо успешно завершена на момент возврата из WriteFile ?
  • Alex_C (24.11.09 10:49) [10]

    > Какой смысл ждать сигнал ивента и следом читать overlapped-
    > результат, если операция заведомо успешно завершена на момент
    > возврата из WriteFile ?


    Ок!
    Понял! Так получается:


    function TComPort.WriteBytes(ByteArray: Pointer; Count: integer): cardinal;
    var
    Signaled, RealWrite, BytesTrans: cardinal;
    WriteOL: TOverLapped; {структура для асинхронной записи}
    Success: boolean;
    y: Integer;
    begin
    Result := 0;

    {создание события для асинхронной записи}
    FillChar(WriteOL, SizeOf(WriteOL), 0);
    WriteOL.hEvent := CreateEvent(nil, True, True, nil);

    try
      {начало асинхронной записи}
      if not WriteFile(FHandle, ByteArray^, Count, RealWrite, @WriteOL) then
      // Операция WriteFile не завершена
      // Ожидаем завершение и анализируем результат
      begin
         y := GetLastError;
         if y = ERROR_IO_PENDING then
         begin
           {ожидания завершения асинхронной операции}
           Signaled := WaitForSingleObject(WriteOL.hEvent, INFINITE);
          {получение результата асинхронной операции}
          Success  :=
            (Signaled = WAIT_OBJECT_0) and
            (GetOverlappedResult(FHandle, WriteOL, BytesTrans, False));
          if not Success then
            raise Exception.Create('Can not write to COM port!');
          else
            Result := BytesTrans;
         end
         else
           raise Exception.Create('Can not write to COM port!');
      end
      else
        Result := RealWrite;
    finally
      {освобождение дескриптора события}
      CloseHandle(WriteOL.hEvent);
    end;
    end;


  • vastani (24.11.09 11:20) [11]
    Alex_C очень похоже, что Ваша беда называется FIFO последовательного порта!!! Ее можно рулить только в реестре(если программно) или ручками если зайти в система-девайсы-Ваш порт-пареметры порта-дополнительно(кнопка!)
    Установить надо ПОБАЙТНУЮ работу т.е. FIFO =1 в два ручья, тогда железяка не должна ждать уровня переполнения и сразу отдать системе.
    Второе. Надо в ините порта тоже поставить отдавать побайтно, т.е. чтобы уже связка(слой) система+драйвер не накапливали в буфере, а сразу трезвонили проге СОБЫТИЕ пришел(отправлен) байт!
    Если непонятно могу ширше... )
  • Alex_C (24.11.09 21:53) [12]

    > Если непонятно могу ширше... )


    Хотя бы общий пример хотелось бы!
  • vastani (26.11.09 15:35) [13]
    Меня не было тут немного :) надеюсь интерес не погиб...!
    Ох-ох-ох.... тема то интересная, кто плавал уже, но разноплановая блин... с точки зрения комплексного затрагивания букета разных причин и свойств и пр...... ладно, придется подвинуть свою лень.
    Итак, сначала по поводу сказанного и "с чем его едят":
    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ACPI\PNP0501\1\Device Parameters


    содержит 3 строковых параметра(текущие уставки) ответственных за FIFO COM1 порта в данном случае:
    ForceFifoEnable (вкл/выкл вообщем),RxFIFO,TxFIFO=размерчики.
    программно рулим естественно функциями работы с реестром.
    Стандартные функции, включая API винды фифо не рулят вообще!
    Лично я не находил даже упоминания об этом...
  • vastani (26.11.09 15:37) [14]
    Работу фифо прочувствовать просто. Берем терминалочку, например мне нравится в таких случаях эта http://kolibdb.100free.com/files/wTerm.zip
    далее соединяем два порта COM1+COM2 простейшим нульмодемным соединением Tx1+Rx2 и Rx1+Tx2. Затем запуск двух копий приложения и открытие этих портов с минимальной скоростью. Давим и удерживаем символьную клавишу и лицезреем, как на приемной стороне БАЙТЫ ПОЛУЧАЮТСЯ и отображаются ПАЧКАМИ= FIFO обьема! Т.е. они выплевываются, скажем по 8 байт, видно, что накапливаются и именно выплевываются, достигнув порога срабатывания. Рулим FIFO и зрим эффект.
    Помимо фифо в сабже "замешаны" параметры при открытии порта, а именно "...Поля структуры COMMTIMEOUTS...".
  • vastani (26.11.09 15:38) [15]
    В связи с этим НАСТОЯТЕЛЬНО советую перечитать статейку "Работа с коммуникационными портами (COM и LPT) в программах для Win32" (например тут http://www.citforum.ru/hardware/articles/comports/)
    ЭТО КАЧЕСТВЕННЫЙ ПРОФЕССИОНАЛЬНЫЙ ПОДХОД при оседлании последовательных портов в WIN32! Для многих сведения там излагаемые просто открытие глаз на принципы и то как надо грамотно писать коммуникационные проги, особенно, когда работа идет с N портами одновременно (мультипортовка).
    Еще важно знать это: "Точное время: измеряем, применяем" (http://habrahabr.ru/blogs/delphi/75234/)
    Вот ИМЕННО на ПЕРЕСЕЧЕНИИ этих 3-х китов и только после хотябы вникания(лучше изучения) будет истина(решение) проблем сабжа и иже с ним.
    Подобные вещи ипроблемы неоднократно возникают у спецов работающих с железом, контроллерами, сетями "нижнего уровня" и их протоколами, напрмер MODBUS и проблемы полудуплекса RS-485....
    Считаю, что выдал обильную инфу, переборов свою лень и ничего не закуркулил :) успехов и продвинутых результатов!
  • Alex_C (27.11.09 08:46) [16]

    > надеюсь интерес не погиб...!


    Интерес очень большой :)
    Темя для меня очень важная - хочу досканально в ней разобраться.
    Спасибо за инфо - буду читать и разбираться!
 
Конференция "WinAPI" » "Принудительная" запись в COM-порт [D7, WinXP]
Есть новые Нет новых   [134431   +15][b:0][p:0.003]