![]() |
Модераторы: feodorv, GremlinProg, xvr, Fixin |
![]() ![]() ![]() |
|
TheDestroyer |
|
||||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
Здравствуйте.
Для тестирования и отработки протокола обмена между микроконтроллером и управляющим блоком необходимо сделать обмен по RS-232 между двумя компьютерами. Сделано так: помимо основного потока программы, есть еще 3 потока - чтение, запись и управляющий поток (чтобы не зависало окно приложения). У программы есть два режима работы - master и slave. Master запускаю на одном компьтере, slave на другом. Master отсылает сообщение (его длина указывается в #define BUFSIZE 100 - количество байт), и ждет пока не прийдет сообщение назад, как только сообщение приходит назад, он отправляет новое сообщение и т.д. Slave ждет сообщение от Master-а, как только он его получает - тут же отправляет назад Master-у. Также есть возможность посылать Master-ом как единичные сообщения, так и посылать сообщения циклически. (Размер сообщения, как было сказано выше, можно менять). Запуск разового и циклического обмена происходит соответствующими кнопками. Среда Borland C++ 6, но стараюсь писать на чистом с++, т.к. часть кода будет использоваться при написании программы под микроконтроллер. Проблема: при пересылке небольших сообщений 1байт, 10 байт, как единично так и циклически - все работает нормально. При пересылке больших сообщений (100 байт) даже при единичной пересылке возникают проблемы - иногда Master- ом принимается все нормально, но чаще принимается не 100 байт, а 186 байт. При отладке стало видно, что прием происходит не за один цикл (пришло сообщение, определилось, что пришло 100 байт и считалось), а за два цикла (происходит событие прихода сообщения, определяется, что принято 14 байт, считывается, затем снова происходит событие приема, определяется что принято 86 байт и считывается. Иногда во втором цикле приема приходит больше чем 86 байт). Заметил, что если поставить точку останова на строке сразу после отправки сообщения в порт:
то приходит 100 байт, т.е. ровно столько сколько было отправлено и приходит за один раз. Догадка: возможно прием большого сообщения происходит не сразу из-за того, что в цикле чтения стоит ожидание события приема байта (EV_RXCHAR), а надо бы дождаться когда прийдет все сообщение. Как решить данную проблему? Возможно ли вообще передавать таким образом большие сообщения? (Большие файлы-то по COM порту качать можно) Как связана данная передача с протоколом UART? При пересылке большого сообщения оно перепаковывается в небольшие слова со стоп и старт битами и передается, или же сразу пересылается все сообщение разом? Привожу код программы (постарался оставить самое необходимое):
Заранее спасибо. |
||||
|
|||||
Dem_max |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1780 Регистрация: 12.4.2007 Репутация: 16 Всего: 39 |
У UART имеется ограниченный буффер приема, так что винда будет твои большие сообщения отдавать порциями. Так что постоянно приходяться считывать эти порции.
-------------------- Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte") |
|||
|
||||
TheDestroyer |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
На счет буфера, обращаю внимание, что он выставляется
Если конечно про этот буфер идет речь. |
|||
|
||||
Dem_max |
|
|||
![]() Эксперт ![]() ![]() ![]() Профиль Группа: Завсегдатай Сообщений: 1780 Регистрация: 12.4.2007 Репутация: 16 Всего: 39 |
Это промежуточный виндовый буффер, а я упоминал про реальный железковый.
Кстати программый SetupComm(COMport,2000,2000); задается чтобы не терять байты если вдруг ваша прога немножко подзависла, винда сохранит байты у себя. Это сообщение отредактировал(а) Dem_max - 3.4.2009, 16:06 -------------------- Американские программисты долго не могли понять, почему русские при зависании Windоws всё время повторяют "Твой зайка написал" ("Yоur bunnу wrоte") |
|||
|
||||
TheDestroyer |
|
||||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
Обнаружил интересный эффект: Master и Slave режим пока не рассматриваем. Просто делаю передачу с одного компьютера на другой.
При разовой передаче 15 байт максимальное число байт, которое передается за один цикл равно 14, потом идет второй цикл приема (событие приема опять в сигнальном подложении) и принимается еще 1 байт. Итого всего 15 байт принято, но за два цикла. Число 14 - какое-то магическое. Больше никак - меньше пожалуйста. Если сделать точку останова на ClearCommError(COMport, &temp, &comstat); , то число принятых байт сразу будет равно 15-ти, т.е. сразу определится, правильное число принятых байт, а не 14+1.
Далее, если вообще не проверять количество принятых байт, а просто принудительно читать ровно столько байт, сколько было отослано:
то прочитается все нормально. Также вместо 15-ти можно ставить и другое число. В итоге получается, что структура comstat (ее параметр comstat.cbInQue) определяется неправильно. Из-за чего это может случаться? Как это поправить? Это сообщение отредактировал(а) TheDestroyer - 3.4.2009, 17:07 |
||||
|
|||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 40 Всего: 223 |
Установите timeout'ы (SetCommTimeouts) в какое нибудь осмысленное значение. Все ваши проблемы с недочитанными байтами именно из за этого. UART очень медленное устройство (по сравнению со скоростью работы CPU). Установка timeout'ов в 0 заставляет ReadFile вернуть немедленно столько байтов, сколько было прочитанно из порта к этому моменту. Ваше магическое число 14 байтов - это размер очереди (FIFO) у аппаратного UART. Он осуществляет прием с некоторым timeout'ом, на который ваши нулевые установки не распространяются.
Далее, размер прочтенных данных надо брать из вызова ReadFile (переменная temp, в строке 95), а не из переменной btr. В ReadFile можно передавать запрос сразу на чтение всего буффера, она прочтет не более того, что реально есть. В вашей реализации Overlapped ввод/вывод не нужен, используйте обычные блокирующие ReadFile/WriteFile. Я уже не говорю о странной конструкции с WaitCommEvent/WaitForSingleObject/ReadFile - первые 2 части явно лишние, ReadFile и сам умеет работать в Overlapped режиме Далее, вызывать методы VCL объектов напрямую не из главного потока категорически нельзя - VCL не расчитанна на такое использование, будет масса трудноуловимых глюков (используйте класс TThread и его метод Synchronize) И последнее - для синхронизации потоков обычно используют Event'ы или семафоры, а уж никак не запуск и остановку этих самых потоков |
|||
|
||||
TheDestroyer |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
А размер FIFO какой у контроллера? Почему именно 14 байт... Это стандарт? Никак не могу найти спецификацию, где бы говорилось об этом аппаратном буфере. Посоветуйте какие таймауты выставить, из каких соображений они выставляются, в инете в разных примерах разные таймауты. Про вызов VCL из потока согласен, это пока для отладочных целей. Про странную на ваш взгляд конструкцию - я взял пример из http://piclist.ru/S-COM-THREAD-RUS/S-COM-THREAD-RUS.html , он оказался наиболее полным и рабочим, т.к. все примеры, что нашел в инете если и работают, то не так как ожидается. Буду признателен за любые советы. |
|||
|
||||
xvr |
|
||||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 40 Всего: 223 |
Читатйте datasheet на микросхему UART (16550 кажется)
Сделайте обычный не Overlapped read |
||||||
|
|||||||
TheDestroyer |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
Нашел в настройках COM порта компьютера - диспетчер устройств - COM1 - параметры порта - дополнительно - использовать буферы FIFO, как раз там стоял размер 14 байт. Выключил этот буфер, результат не поменялся.
По поводу использования не Overlapped read, в этом случае поток будет непрерывно читать из порта? Хорошо ли это, ведь рациональнее ждать прихода информации и по приходу читать ее.
|
|||
|
||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 40 Всего: 223 |
Функция ReadFile будет блокироваться, пока в буфере не появится хотя бы один байт. После чего она начнет читать байты, пока не сработает один из timeout'ов или не исчерпается буфер. Насколько я понимаю именно это вам и надо. (Timeout'ы должны быть не нулевые!)
|
|||
|
||||
TheDestroyer |
|
||||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
Следуя вашим советам сделал так:
установил таймауты, почитав в хэлпе про них:
Поток чтения сделал таким:
Цикл в потоке крутится непрерывно, и даже если выставить timeouts.ReadTotalTimeoutConstant = 10000, т.е. 10-ти секундный таймаут ожидания чтения функцией ReadFile, то визуально ReadFile как мгновенно выполнялась, так и продолжает выполняться мгновенно. В переменной temp всегда ноль. Подскажите, плз, что не так делаю. |
||||
|
|||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 40 Всего: 223 |
FILE_FLAG_OVERLAPPED из CreateFile убрали?
|
|||
|
||||
TheDestroyer |
|
||||||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
Нет, забыл убрать. Убрал - заработало так, как ожидалось!
Для полного понимания происходящего: В реализации потока чтения без overlapped:
Есть вопросы: 1. С вышеуказанными таймаутами ReadFile ждет появления принятых байт 100*15+100мс= 1,6 сек и возвращает false если ничего так и не пришло, true если что-то пришло и считалось, это так? 2. Если было отправлено, например, 1кБ или более, то чтение будет продолжаться, пока передача не прекратится? 3. Если 2 - верно, то возникает вопрос, если передавать, гигабайты, то не объявишь же гигабайтный буфер приема bufrd, как следует поступать? 4. Если COM порт настроен на работу на скорости 9600 бод/с, это скорость передачи полезной информации, невключая транспортные издержки в виде старт, стоп, ... битов? 5. Чтобы добиться максимальной скорости передачи информации, наверное необходимо разбивать ее на части по сколько-то байт, чтобы принимать сразу целые части, или это несущественно? Из документации по контроллеру:
т.е. кадр может содержать от 1 до 63 информационных бита. Следует ли перепаковывать информационные кадры специальным образом, если их размер около 16 байт? В реализации потока чтения c overlapped:
1. Так и не удалось получить за один цикл приема все 15 байт, только 14+1. Для чего может понадобиться использовать Overlapped вариант? Казалось бы, в строке 12 происходит ожидание прихода информации, как раз тут можно было бы дождаться пока вся информация не придет и продолжить. Спасибо. |
||||||
|
|||||||
xvr |
|
||||||||||||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 40 Всего: 223 |
В первом приближении так. Только возвращается TRUE в любом случае, и еще учитывается межсимвольный timeout, т.е. если данные приходят аостоянно, то он будет постоянно читать. Если данных изначально не было, то он будет ждать первого байта бесконечно
|
||||||||||||||
|
|||||||||||||||
TheDestroyer |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
Да, как только начал тестировать, оказалось, что пока ReadFile ждет, то несмотря на то, что ждет она в отдельном потоке, это ожидание подвешивает все. Например, пока идет ожидание - жму кнопку отправки данных, функции PurgeComm и WriteFile не выполняются пока ReadFile не завершит ожидание. Конечно, можно уменьшить таймауты, чтобы ReadFile "крутилась" быстрее, но это неправильно. Похоже, все-таки, нужно использовать overlapped.
Если в режиме overlapped передавать 15 байт, то ReadFile читает 14+1 байт.
Если же указать явно вместо btr - 15 байт, то прочитает сразу 15 байт. Т.е. надо явно указывать сколько читать байт. Если указать буфер чтения больше, чем передано байт, то принимается ноль байт. Как бы принимать в режиме overlapped ровно столько байт сколько было передано за один раз? |
|||
|
||||
xvr |
|
||||||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 40 Всего: 223 |
Если нужно читать в Overlapped, то WaitCommEvent все равно не нужен. Нужно сразу запускать ReadFile, и если она вернулась с ошибкой IO_STILL_PENDING (кажется так), запускать WaitForSingleObject на event'е из OVERLAPPED, а затем GetOverlappedResult. (А можно сразу GetOverlappedResult и поставить ему TRUE в последнем параметре)
![]()
|
||||||||
|
|||||||||
TheDestroyer |
|
||||||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
в режиме Overlapped получился такой поток чтения:
ReadFile стоит чуть больше секунды и возвращает ERROR_IO_PENDING всегда. Приняты данные или нет можно судить по bytes_read. Так поток и "крутится". В Overlapped режиме, в отличие от обычного, ReadFile не создает описанных ранее подвисаний. Таймауты такие:
В первом посте я описывал, что принимается 14+1 байт при отправке 15-ти байт. В потоке чтения в цикле принималось сначала 14 байт (как сообщалось в comstat.cbInQue), на втором цикле принимался еще один байт (также сообщалось в comstat.cbInQue). Делаю замер скорости на двух компьютерах. Настройки COM порта такие:
На 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 информационных бит передается? |
||||||
|
|||||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 40 Всего: 223 |
Что то не так. Передается по 10 бит (минимум). Попробуй отправить большой пакет данных (все 300 байт за раз), и замерять время не таймером, а функцией замера времени (GetTickCount() например)
|
|||
|
||||
TheDestroyer |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
Сделал таким образом:
Запускается поток 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 бит/с Это уже похоже на правду. Почему же при пересылке маленькими пачками скорость так падает? |
|||
|
||||
xvr |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 40 Всего: 223 |
Переключение thread'ов отнимает много времени. Возможно еще где то есть лишние системные вызовы |
|||
|
||||
TheDestroyer |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
Выяснил, что если в ReadFile ставить приемный буфер больше, чем реально пересылается байт, то возникает задержка.
Так при передаче 300 байт и с 300 байтным буфером приема пересылка туда и обратно происходила за 0,052сек. При передаче 17 байт с 300 байтным буфером приема такая пересылка занимает более 0,2сек. Если же выставить 17 байтный буфер приема, то пересылка занимает 0,0037сек, что соответствует скорости 93176 Бит/с (при настройке порта на 115200 Бит/с). Видимо на эту задержку в зафисимости от размера приемного буфера влияют таймауты. |
|||
|
||||
xvr |
|
||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 40 Всего: 223 |
Естественно
![]() Если в процессе приема данных закончился буфер, то ReadFile немедленно прекращает чтение, остаток данных останется лежать в буфере системы (и timeout'ы на это не влияют). Если же в процессе приема закончился входной поток, то система будет как минимум ждать ReadIntervalTimeout ms после приема последнего байта (ну нету другого способа узнать, что поток прекратился), и только потом ReadFile прекратит чтение. |
||||
|
|||||
TheDestroyer |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
Имеется ввиду буфер системы, который устанавливается SetupComm(COMport,2000,2000) ? Правильно ли я понимаю, что на пути принятой информации всего два буфера: аппаратный FIFO буфер, который можно отключить или задать размер 1-14 байт на прием и 1-16 на запись и программный, устанавливаемый SetupComm? Что будет если отключить FIFO буфер? Данные сразу будут писаться в программный буфер? Тогда для чего вообще нужен FIFO буфер, он как-то ускоряет обмен? |
|||
|
||||
xvr |
|
||||||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 7046 Регистрация: 28.8.2007 Где: Дублин, Ирландия Репутация: 40 Всего: 223 |
Да
Это было актуально на старых машинах, на современных машинах это может проявится только на мегабодных скоростях и 100% загрузке процессора ![]() |
||||||
|
|||||||
TheDestroyer |
|
|||
Шустрый ![]() Профиль Группа: Участник Сообщений: 112 Регистрация: 5.11.2007 Репутация: нет Всего: нет |
Спасибо большое за ответы. Поставил бы плюс, если б у меня было достаточно постов.
Похоже, данная тема форума единственное место, где можно найти ответы на почти все возникающие вопросы при написании обмена по COM порту во всем инете. Все примеры, что раскиданы по инету (по большому счету их около 3х видов) не помогли мне, как помогли здесь. |
|||
|
||||
GremlinProg |
|
|||
Эксперт ![]() ![]() ![]() ![]() Профиль Группа: Комодератор Сообщений: 2706 Регистрация: 9.8.2005 Где: Тюмень Репутация: 99 Всего: 106 |
xvr +1 -------------------- "Гений всегда разумнее, чем умнее. Ум — это машина, разум — водитель этой машины." |
|||
|
||||
![]() ![]() ![]() |
Правила форума "C/C++: Системное программирование и WinAPI" | |
|
На данный раздел распространяются Правила форума и Правила раздела С++:Общие вопросы . Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Chipset, Step, Fixin, GremlinProg, xvr. feodorv. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | C/C++: Системное программирование и WinAPI | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |