![]() |
Модераторы: 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 ровно столько байт сколько было передано за один раз? |
|||
|
||||
![]() ![]() ![]() |
Правила форума "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. |