Перейти к содержанию
    

Windows7: прием байтов через COM-порт без потерь

дада, спасибо, буду думать. А как без Synchronize это сделать? неужто через глобальные переменные нельзя?

Просто скажите в каком направлении копать, я копать умею только где не знаю....

(Upd: извиняюсь, уже прочитал совет про евенты и семафоры, значит через них буду. Тогда уже сразу про очереди поищу, чтоб просто мессадж с новым указателем записи передать в ожидающую нитку- надеюсь оно тут есть где-то).

Можно конечно и глобальные.

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

 

У меня классический кольцевой буфер, в который один процесс что-то записывает по указателю записи, другой процесс -что-то читает по указателю чтения. Само собой, когда-то доходим до конца буфера и должны следующий байт в его начало записать. Я не понял Вашу идею про "никогда". Указатель записи доходит до RX_RINGBUFF_SIZE периодически, каждые RX_RINGBUFF_SIZE байт.

С RX_RINGBUFF_SIZE - у вас все правильно, но переполнение кольцевого буфера вы действительно не контролируете. некрасиво это.

я ушел от кольцевого буфера, ибо гемора с ним много - следить где голова где хвост.

использую линейный буфер - в котором надо накопить целый пакет. как только пакет набрался - отдаю его, а остаток буфера перемащаю в начало.

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

Изменено пользователем AlexRayne

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Добавлю в копилку способов передачи данных от потока приёма к основному потоку: PostMessage().

Эта функция помещает сообщение в очередь на обработку и тут же возвращает управление. А уж когда главный поток его заберёт и обработает - это его дело.

Я делал так: в потоке приёма создавал на куче пакет, собирал его, проверял валидность, потом отправлял главному окну указатель на этот пакет при помощи PustMessage(). Главный поток обрабатывал пакет и удалял его.

Всё очень просто, надёжно, никто никого не ждёт, и не надо париться с синхронизацией.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Здравствуйте!

Есть Windows7 Pro, 32-bit, компьютер- китайский одноплатник на Intel 1037U, 4GB RAM.

СОМ-порты- 4 штуки прямо на материнке.

И есть внешний передатчик, посылающий в COM-порт пакеты.

 

Заранее спасибо за любые советы (по существу).

 

Насколько вижу ни Intel 1037U ни его Platform Controller Hub (PCH) не имеют UART-ов.

Значит ваши UART-ы виртуальные через USB.

Я бы посмотрел что у вас еще на USB висит (камера, Wi-Fi...) и поотключал бы их жестким сносом драйверов из системы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Насколько вижу ни Intel 1037U ни его Platform Controller Hub (PCH) не имеют UART-ов.

Значит ваши UART-ы виртуальные через USB.

Я бы посмотрел что у вас еще на USB висит (камера, Wi-Fi...) и поотключал бы их жестким сносом драйверов из системы.

Спасибо, интересная идея, не подумал про такое.

Сериальные порты в BIOS Setup уже видны, или по этому признаку не определить через что оно работает? Есть какие-то низкоуровневые тестеры, чтоб подергали железо и сказали через какой чип оно работает и что еще на каком хабе сидит (если это USB)? Машинка используется вот такая. (кстати, эта модель очень понравилась по качеству исполнения)

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спаисибо, интересная идея, не подумал про такое.

Сериальные порты в BIOS Setup уже видны, или по этому признаку не определить через что оно работает? Есть какие-то низкоуровневые тестеры, чтоб подергали железо и сказали через какой чип оно работает и что еще на каком хабе сидит (если это USB)? Машинка используется вот такая. (кстати, эта модель очень понравилась по качеству исполнения)

 

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

А в диспетчере устройств это хозяйство не разглядеть?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Напишите хоть в чем выявился источник потерь

Я обязательно отпишусь как разберусь/не разберусь, пока руки не дошли, через день-два возьмусь. Приоритеты задач меняются на ходу...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В коде меня удивляет использование overlapped WaitCommEvent(). Можно ли сразу вызывать WaitForSingleObject() и GetOverlappedResult(), не проверив по коду возврата WaitCommEvent() и GetLastError(), что WaitCommEvent() ушла в асинхронный режим, как это всегда делается в примерах от Микрософт?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

WaitForSingleObject вызывать можно. Если WaitCommEvent не ушла в wait, то event останется в том состоянии, в котором был. А был он в установленом состоянии.

А вот GetOverlappedResult неизвестно - MSDN на этот счет молчит

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Тоже думаю, что проблема просто в переполнении ФИФО аппаратного приёмника.

Потому, что винда тупо не успевает выгребать данные.

Не думайте ибо этого не может быть.

Даже если-б загрузка CPU была == 100%, драйвер COM-порта, работающий на высоком уровне привилегий, всё равно успевал бы всё выгребать. Тем более на такой низкой скорости. Тем более, что и загрузка CPU никакая.

 

Я делал так: в потоке приёма создавал на куче пакет, собирал его, проверял валидность, потом отправлял главному окну указатель на этот пакет при помощи PustMessage(). Главный поток обрабатывал пакет и удалял его.

Хмм... А очередь сообщений, связанная с окном и её обработка тредом окна гарантирует, что сообщения из этой очереди будут обрабатываться в том же порядке, в котором они постились в очередь? Тем более если постинг идёт из разных тредов?

Я бы на это не рассчитывал.

Да и вроде в случае межпоточной передачи сообщений, PostMessage аналогичен SendMessage-у.

 

В 7 похоже, есть баг в COM драйвере. Многие программы, нормально работавшие с XP, глючат с ком-портами на 7. Единственное, что надежно под ней работает -USB-COM адаптер, причем с FTDI чипом.

Моя программа, написанная много лет назад под WinXP, сейчас работает одновременно с 3-я компортами на Win8, каждый - на скорости 921600 бод (отладочные потоки 3-х устройств). Сейчас это COM-порты - на PCI-карте, раньше были - на USB-UART-ах (FTDI и CP210x), а также - комбинации того и другого.

До этого всё так же работало на Win10 на другом компе. Работает это целыми днями.

Так что проблема 99.9% не в виндовых дровах. А как всегда - в кривых руках написателей этих "многих программ".

Проблемы возникают только с PL230x на высоких скоростях. На всех опробованных виндах. Вот тут явно дело в дровах Prolific.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

WaitForSingleObject вызывать можно. Если WaitCommEvent не ушла в wait, то event останется в том состоянии, в котором был. А был он в установленом состоянии.

А вот GetOverlappedResult неизвестно - MSDN на этот счет молчит

У меня все работает нормально с WaitCommEvent и без WaitForSingleObject. Два подряд ожидания не есть хорошо: тут может быть потенциальная причина пропуска информации.

После того, как WaitCommEvent дождалась события, я просто делаю ResetEvent, проверяю код события, по EV_RXFLAG делаю обработку, по другим либо возвращаюсь к WaitCommEvent, либо закрываю порт (если в основном потоке устанавливаю специальный флаг, сигнализирующий о необходимости закрытия)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Хмм... А очередь сообщений, связанная с окном и её обработка тредом окна гарантирует, что сообщения из этой очереди будут обрабатываться в том же порядке, в котором они постились в очередь?

Да, гарантирует:

With the exception of the WM_PAINT message, the WM_TIMER message, and the WM_QUIT message, the system always posts messages at the end of a message queue. This ensures that a window receives its input messages in the proper first in, first out (FIFO) sequence.

Тем более если постинг идёт из разных тредов?

Без разницы.

Я бы на это не рассчитывал.

А здесь не надо гадать, надо просто читать документацию.

Да и вроде в случае межпоточной передачи сообщений, PostMessage аналогичен SendMessage-у.

Нет.

 

ЗЫ. Совершенно очевидно, что вы не разбираетесь в теме ©.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да, гарантирует:

А здесь не надо гадать, надо просто читать документацию.

Читать лень - сейчас это не нужно. Но помнится, что в WinAPI есть функции, позволяющие считывать сообщения из очереди по маске. А значит - в произвольном порядке.

 

Нет.

ЗЫ. Совершенно очевидно, что вы не разбираетесь в теме ©.

Да ладно! А если внимательнее прочитать описание WinAPI? ;)

 

Я делал так: в потоке приёма создавал на куче пакет, собирал его, проверял валидность, потом отправлял главному окну указатель на этот пакет при помощи PustMessage().

А если надо отправить сообщение не главному окну? Или у Вас всегда приложения только с одним окном? :laughing:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Тут есть стандартная проблема - после приема пришедшего байта, прежде, чем сбрасывать событие, необходимо проверить не пришел-ли еще один байт и, если пришел - выбрать и его. Если этого не сделать а просто сбросить событие - он будет потерян. Т.е. его приход вызовет второе событие, но оно будет сброшено вместе с первым.

 

Вероятность лишнего сброса события есть всегда. Нужна атомарная команда сброса события при условии отсутствия принятых байт, но ее нет и все зависит от реализации ОС.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Вероятность лишнего сброса события есть всегда. Нужна атомарная команда сброса события при условии отсутствия принятых байт, но ее нет и все зависит от реализации ОС.

Проблем нет никаких при грамотном построении алгоритма:

Принимающий тред пишет байты в кольцевой буфер. После каждого обновления содержимого буфера (или не после каждого, а при достижении некоего уровня + по таймауту) посылает нотификацию (оконным сообщением) треду, в котором находится управляющее окно. Управляющее окно, получив нотификацию, читает кольцевой буфер из приёмного треда.

Естественно - парсинг потока байт на пакеты и уж тем более - обработку пакетов, желательно делать уже в треде управляющего окна. Не надо смешивать уровни обработки протокола.

PS: И желательно, без реальной необходимости, избегать передачи указателей на блоки данных в куче между тредами. Как тут советуют некоторые товарищи....

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...