Модераторы: feodorv, GremlinProg, xvr, Fixin

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Обмен по COM порту, Проблема пересылки больших сообщений 
:(
    Опции темы
xvr
Дата 7.4.2009, 13:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(TheDestroyer @ 7.4.2009,  13:02)
Да, как только начал тестировать, оказалось, что пока ReadFile ждет, то несмотря на то, что ждет она в отдельном потоке, это ожидание подвешивает все. Например, пока идет ожидание - жму кнопку отправки данных, функции PurgeComm и WriteFile не выполняются пока ReadFile не завершит ожидание. Конечно, можно уменьшить таймауты, чтобы ReadFile "крутилась" быстрее, но это неправильно. Похоже, все-таки, нужно использовать overlapped. 

Если нужно читать в Overlapped, то WaitCommEvent все равно не нужен. Нужно сразу запускать ReadFile, и если она вернулась с ошибкой IO_STILL_PENDING (кажется так), запускать WaitForSingleObject на event'е из OVERLAPPED, а затем GetOverlappedResult. (А можно сразу GetOverlappedResult и поставить ему TRUE в последнем параметре)
Цитата

Если в режиме overlapped передавать 15 байт, то ReadFile читает 14+1 байт.
Если же указать явно вместо btr - 15 байт, то прочитает сразу 15 байт. Т.е. надо явно указывать сколько читать байт. 
Указывайте по максимуму. И timeout'ы не забудьте выставить
Цитата

Если указать буфер чтения больше, чем передано байт, то принимается ноль байт.
Принимается больше, но надо ждать  smile 
Цитата

Как бы принимать в режиме overlapped ровно столько байт сколько было передано за один раз?
'За один раз' - это за какой раз? UART не режет посылки на пакеты, единственный способ различить 'разы' это timeout'ы. Причем, если ДО вызова ReadFile уже пришло несколько пакетов, то их отличить друг от друга (на уровне ReadFile) уже невозможно

PM MAIL   Вверх
TheDestroyer
Дата 8.4.2009, 12:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



в режиме Overlapped получился такой поток чтения:
Код

DWORD WINAPI ReadThread(LPVOID)
{
 COMSTAT comstat;    
 DWORD Error, btr, bytes_read, mask, signal;    
 bool result = false;

 overlapped.hEvent = CreateEvent(NULL, true, true, NULL);
 SetCommMask(COMport, EV_RXCHAR);          
 while(1)    
  {
     result = ReadFile(COMport, bufrdHEX, sizeof(bufrdHEX), &bytes_read, &overlapped);
     if (!result)
     {
       Error = GetLastError();
       if (Error == ERROR_IO_PENDING)
        {
          if(GetOverlappedResult(COMport, &overlapped, &bytes_read, true))  // true - Specifies whether the function should wait for the pending overlapped operation to be completed
          { 
           if(bytes_read)      
           {
            counter+=bytes_read;                 
            Bytes_received+=bytes_read;
            if (show_info)
            {
             ReadPrinting(bytes_read);           
            }
            SetEvent(info_read); 
            if (slave_mode)
            {
            memcpy(bufwrHEX,bufrdHEX,bytes_read);
            ResumeThread(writer);           
            } // if (slave_mode)
           } // if(btr)
          } // if(GetOverlappedResult(COMport, &overlapped, &temp, true))
        } // if (Error == ERROR_IO_PENDING)
     } // if (!result)
  } // while(1)
}

ReadFile стоит чуть больше секунды и возвращает ERROR_IO_PENDING всегда. Приняты данные или нет можно судить по bytes_read. Так поток и "крутится". В Overlapped режиме, в отличие от обычного, ReadFile не создает описанных ранее подвисаний.
Таймауты такие:
Код

 timeouts.ReadIntervalTimeout = 100;     
 timeouts.ReadTotalTimeoutMultiplier = 15;
 timeouts.ReadTotalTimeoutConstant = 100;  
 timeouts.WriteTotalTimeoutMultiplier = 15;   
 timeouts.WriteTotalTimeoutConstant = 100;    

Цитата(xvr @  7.4.2009,  13:32 Найти цитируемый пост)
'За один раз' - это за какой раз? 

В первом посте я описывал, что принимается 14+1 байт при отправке 15-ти байт. В потоке чтения в цикле принималось сначала 14 байт (как сообщалось в comstat.cbInQue), на втором цикле принимался еще один байт (также сообщалось в comstat.cbInQue).

Делаю замер скорости на двух компьютерах. 
Настройки COM порта такие:
Код

dcb.BaudRate = StrToInt(Form1->ComboBox2->Text);   // 9600
 dcb.fBinary = TRUE;                       
 dcb.fOutxCtsFlow = FALSE;          
 dcb.fOutxDsrFlow = FALSE;          
 dcb.fDtrControl = DTR_CONTROL_DISABLE;      
 dcb.fDsrSensitivity = FALSE;                  
 dcb.fNull = FALSE;                                
 dcb.fRtsControl = RTS_CONTROL_DISABLE;            
 dcb.fAbortOnError = FALSE;                         
 dcb.ByteSize = 8;                                  
 dcb.Parity = 0;                   
 dcb.StopBits = 0;       
       
На 2х компьютерах запускаю написанную программу, на 2-ом устанавливаю флаг slave_mode (т.е. сразу после чтения он отправляет принятое назад).
Передаю циклически буфер из 20 байт, содержимое каждый раз меняю для контроля правильной передачи и приема.Время контролирую с помощью компонента Timer из стандартного набора Borland. Интервал таймера выставлен 55мс, т.к. меньшие интервалы, похоже, таймер не чувствует.
Делаю 15 циклов передачи, соответственно вижу, что на компьютере, с которого отправлял: отправлено 300 байт (15 раз по 20 байт) и принято 300 байт. Все без ошибок. Затраченное время 3740мс = 3,74с.
Подсчитываю скорость: 300 байт * 2 / 3,74 c ~ 160,43 байт/с =  1283 бит/с. Никак не 9600 бит/с
Учтем, что пересылка идет, наверное, по 8 информационных бит, к которым добавляется 1 старт и 1 стоп бит. Итого на 300 байт информационных издержек: 300*2 =600 бит = 75 байт. Т.к. в тесте пересылка идет туда и обратно: 75*2 = 150 байт. 
Итого передано всего (300+75)*2 байт / 3,74 с ~ 200,54 байт/с = 1604,3 бит/с. Опять далеко не 9600 бит/с

Не учитываю время затраченное компьютерами на запись информации в буфера, т.к. считаю его незначительным по сравнению со временем, затраченным на передачу данных.
В чем ошибка расчета скорости?  Может не по 8 информационных бит передается? 


PM MAIL   Вверх
xvr
Дата 8.4.2009, 14:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Что то не так. Передается по 10 бит (минимум). Попробуй отправить большой пакет данных (все 300 байт за раз), и замерять время не таймером, а функцией замера времени (GetTickCount() например)

PM MAIL   Вверх
TheDestroyer
Дата 8.4.2009, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Сделал таким образом:
Код

DWORD WINAPI MainThread(LPVOID)
{
LARGE_INTEGER startTime, stopTime;
LARGE_INTEGER freq;
bool active = false;
long int t=0;

stopfl = false;
info_read = CreateEvent(NULL, true, true, NULL); 

 while (stopfl != true)
 {

   if (WaitForSingleObject(info_read, 5000) == WAIT_OBJECT_0 )
     {
      if (active) // если была записана инф. в порт и вернулась от slave
      {
      QueryPerformanceCounter(&stopTime);
      QueryPerformanceFrequency(&freq);
      __int64 d = (__int64)freq.QuadPart/1000000; // f(MHz)
      t = __int64(stopTime.QuadPart - startTime.QuadPart)/d;  // t(мкс)
      stopfl = true;
      active = false;
      }
      else
      {
      ResetEvent(info_read); 
      for (int j=0;j < sizeof(bufwrHEX);j++)
      {
        bufwrHEX[j] = char(j);
      } // for (int j=0;j < sizeof(bufwrHEX);j++)

      QueryPerformanceCounter(&startTime);
      active = true;

      PurgeComm(COMport,PURGE_TXCLEAR);
      ResumeThread(writer);  
      }// else if (active)

      } //  if (WaitForSingleObject(info_sent, 5000) == WAIT_OBJECT_0 ) 
   else
     { 
      stopfl = true;
     }

} // while (stopfl != true)
Cycle_stop();
} //  DWORD WINAPI MainThread(LPVOID)

Запускается поток MainThread, который запоминает значение точного счетчика, запускает запись в порт, дожидается окончания чтения ответа и сравнивает текущее значение счетчика с запомненным ранее. В итоге, вроде как, точность - 10ки микросекунд.
Пересылаю буфер размером 300 байт.
Результат: время пересылки к slave и обратно = 628623 мкс (в среднем)
Скорость: 300 байт * 2 / 0,63 с = 952,5 байт/с = 7619 бит/с
Транспортных издержек (1 старт и 1 стоп бит):  300 * 2 = 600 бит = 75 байт
Итого: (300 +75)байт * 2 / 0,63 с = 1190,5 байт/с = 9523,8 бит/с
Это уже похоже на правду.
Почему же при пересылке маленькими пачками скорость так падает?

PM MAIL   Вверх
xvr
Дата 8.4.2009, 16:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(TheDestroyer @ 8.4.2009,  16:03)
Почему же при пересылке маленькими пачками скорость так падает?

Переключение thread'ов отнимает много времени. Возможно еще где то есть лишние системные вызовы

PM MAIL   Вверх
TheDestroyer
Дата 9.4.2009, 14:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Выяснил, что если в ReadFile ставить приемный буфер больше, чем реально пересылается байт, то возникает задержка.
Так при передаче 300 байт и с 300 байтным буфером приема пересылка туда и обратно происходила за 0,052сек. 
При передаче 17 байт с 300 байтным буфером приема такая пересылка занимает более 0,2сек.
Если же выставить 17 байтный буфер приема, то пересылка занимает 0,0037сек, что соответствует скорости 93176 Бит/с (при настройке порта на 115200 Бит/с).
Видимо на эту задержку в зафисимости от размера приемного буфера влияют таймауты.

PM MAIL   Вверх
xvr
Дата 9.4.2009, 17:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(TheDestroyer @ 9.4.2009,  14:47)
Выяснил, что если в ReadFile ставить приемный буфер больше, чем реально пересылается байт, то возникает задержка.

Естественно
Цитата

Видимо на эту задержку в зафисимости от размера приемного буфера влияют таймауты.
Влияют, причем вне зависимости от размера  smile 
Если в процессе приема данных закончился буфер, то ReadFile немедленно прекращает чтение, остаток данных останется лежать в буфере системы (и timeout'ы на это не влияют). 
Если же в процессе приема закончился входной поток, то система будет как минимум ждать ReadIntervalTimeout ms после приема последнего байта (ну нету другого способа узнать, что поток прекратился), и только потом ReadFile прекратит чтение.

PM MAIL   Вверх
TheDestroyer
Дата 10.4.2009, 11:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Цитата(xvr @  9.4.2009,  17:41 Найти цитируемый пост)
ReadFile немедленно прекращает чтение, остаток данных останется лежать в буфере системы

Имеется ввиду буфер системы, который устанавливается  SetupComm(COMport,2000,2000) ?
Правильно ли я понимаю, что на пути принятой информации всего два буфера: аппаратный FIFO буфер, который можно отключить или задать размер 1-14 байт на прием и 1-16 на запись и программный, устанавливаемый SetupComm?
Что будет если отключить FIFO буфер? Данные сразу будут писаться в программный буфер? Тогда для чего вообще нужен FIFO буфер, он как-то ускоряет обмен?
PM MAIL   Вверх
xvr
Дата 10.4.2009, 12:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 40
Всего: 223



Цитата(TheDestroyer @ 10.4.2009,  11:43)
Цитата(xvr @  9.4.2009,  17:41 Найти цитируемый пост)
ReadFile немедленно прекращает чтение, остаток данных останется лежать в буфере системы

Имеется ввиду буфер системы, который устанавливается  SetupComm(COMport,2000,2000) ?

Да
Цитата

Правильно ли я понимаю, что на пути принятой информации всего два буфера: аппаратный FIFO буфер, который можно отключить или задать размер 1-14 байт на прием и 1-16 на запись и программный, устанавливаемый SetupComm?
Да
Цитата

Что будет если отключить FIFO буфер? Данные сразу будут писаться в программный буфер? 
Да
Цитата

Тогда для чего вообще нужен FIFO буфер, он как-то ускоряет обмен?
Нет. Без него могут теряться байты на больших скоростях. UART будет посылать прерывания в CPU на каждый принятый байт, и если драйвер порта не прочтет этот байт до прихода следующего, то он потеряется (и установится ошибка приема)
Это было актуально на старых машинах, на современных машинах это может проявится только на мегабодных скоростях и 100% загрузке процессора  smile Хотя отключать его (FIFO) все же не стоит - надежности оно все таки добавляет

PM MAIL   Вверх
TheDestroyer
Дата 13.4.2009, 16:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 112
Регистрация: 5.11.2007

Репутация: нет
Всего: нет



Спасибо большое за ответы. Поставил бы плюс, если б у меня было достаточно постов. 
Похоже, данная тема форума единственное место, где можно найти ответы на почти все возникающие вопросы при написании обмена по COM порту во всем инете. Все примеры, что раскиданы по инету (по большому счету их около 3х видов) не помогли мне, как помогли здесь.
PM MAIL   Вверх
GremlinProg
Дата 13.4.2009, 17:48 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 2706
Регистрация: 9.8.2005
Где: Тюмень

Репутация: 99
Всего: 106



Цитата(TheDestroyer @  13.4.2009,  18:20 Найти цитируемый пост)
Поставил бы плюс, если б у меня было достаточно постов. 

xvr +1


--------------------
"Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины."
PM WWW ICQ   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Системное программирование и WinAPI"
Fixin
GremlinProg
xvr
feodorv
  • Большое количество информации и примеров с использованием функций WinAPI можно найти в MSDN
  • Описание сообщений, уведомлений и примеров с использованием компонент WinAPI (BUTTON, EDIT, STATIC, и т.п.), можно найти в MSDN Control Library
  • Непосредственно, перед созданием новой темы, проверьте заголовок и удостоверьтесь, что он отражает суть обсуждения.
  • После заполнения поля "Название темы", обратите внимание на наличие и содержание панели "А здесь смотрели?", возможно Ваш вопрос уже был решен.
  • Приводите часть кода, в которой предположительно находится проблема или ошибка.
  • Если указываете код, пользуйтесь тегами [code][/code], или их кнопочными аналогами.
  • Если вопрос решен, воспользуйтесь соответствующей ссылкой, расположенной напротив названия темы.
  • Один топик - один вопрос!
  • Перед тем как создать тему - прочтите это .

На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы .


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема »


 




[ Время генерации скрипта: 0.1159 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.