|
|
  |
Windows7: прием байтов через COM-порт без потерь, Кто-то имеет личный опыт? чем побороть потерю отдельных байтов? |
|
|
|
May 23 2017, 11:17
|
Гуру
     
Группа: Свой
Сообщений: 2 360
Регистрация: 6-03-06
Из: Кишинев
Пользователь №: 15 025

|
Цитата(AlexRayne @ May 23 2017, 12:20)  Вот это именно и имел ввиду - что вы путаете всех использованием слова "драйвер". Хм. Обычно я стараюсь следить за тем что говорю и не "путать" людей. Например я ни разу нигде не упоминал слово "Драйвер". Я также дал ссылку на то на базе чего писал софт. И в том документе тоже их программа ни разу нигде не названа драйвером. Цитата(AlexRayne @ May 23 2017, 12:20)  В венде уже написаны дрова под множество материнок и уж тем более под известные стандартные контроллеры УАРТ. Вот они как раз напрямую с железом и прерыванием работают. писать их не требуется - они готовы и встроены в Ось. Овь прелагает довольно простое стандартное АПИ для работы с Сомм портами. Если вы поставили борланд - покурите их хелпы Win32 SDK или чтото вроде этого. Это огромный хелп по всем ресурсам венды. и в частности там описан и Comm интерфейс. Если вы полезете в изходники АсинкПро - то наглядно увидите как вендовые вызовы используются. АсинкПро - это обертка вендовых вызовов. То есть из Ваших слов следует, что предложенный по моей ссылке способ использования винапишных функций негодный, так как Вы советуете мне начинать с нуля и смотреть на другие функции? Я правильно понял? Я понимаю, что "АсинкПро - это обертка вендовых вызовов", я не знаю каких. Поэтому и спросил, что именно вызывать-то и как это использовать чтобы побороть потерю байтов. Спасибо, буду читать про Comm. Цитата(AlexRayne @ May 23 2017, 12:20)  Вам уже посоветовали использовать 1,5-2 стопбита. это хорошее предложение, если оно конечно возможно для ваших абонентов. Это не предложение а костыль. Чтобы его применить- нужны аргументы и пояснения, почему "8N1" теряет байты, а вот "8N2" - уже "хорошее предложение". То есть у Винды (или у моего FIFO в железе) есть глюк, полное исправление которого возможно удлинением времени передачи байта на 1 бит? Цитата(AlexRayne @ May 23 2017, 12:20)  С адаптерами КОМ-УСБ советую не играться если есть нормальный порт на матери или PCI плате ввода вывода. Я встретил кривой адаптер от МОХи, народ жаловался на ФТДИ - у обоих проблемы с буфером приемника на больших трафиках. Попробую, конечно, вдруг таки да, но только на столе. В проекте не покатит- система необслуживаемая и увеличивать количество возможных проблем добавкой USB канала совсем не хочется. Особенно при наличии нормальных COM-портов. Кстати да, просто опять поставить АсинкПро и попробовать из-под него тоже неплохая идея- вряд ли я сделаю общение с виндовыми апи лучше чем они. Или есть что-то еще уровня Асинк Про для стареньких билдеров?
|
|
|
|
|
May 23 2017, 11:20
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Цитата(neiver @ May 22 2017, 11:25)  У стандартного аппаратного СОМ порта есть внутренний буфер, как правило размером 15 байт. Это не тот буфер, который задается в SetupComm. Если драйвер порта по каким-то причинам не успеет прочитать этот буфер, то принятые байты теряются, о чем извещают соответствующие флаги возвращаемые ClearCommError. Чтение этого буфера выполняется системным драйвером, причем на самом высоком приоритете (аппаратных прерываний). И что бы он (драйвер) не успел вычитать буфер (причем с учетом того, что этот буфер аппаратный, и у драйвера есть аж целых 15 символьных интервалов, что бы его прочесть), Windows должен быть загружен по самые помидоры  Да и скорость 115200 не есть что то из ряда вон выходящее. 2 ТС - покажите код вашего потока чтения. И настройки при открытии порта. Сто процентов бага где то там
|
|
|
|
|
May 23 2017, 12:42
|
Гуру
     
Группа: Свой
Сообщений: 2 360
Регистрация: 6-03-06
Из: Кишинев
Пользователь №: 15 025

|
Цитата(XVR @ May 23 2017, 13:20)  Чтение этого буфера выполняется системным драйвером, причем на самом высоком приоритете (аппаратных прерываний). И что бы он (драйвер) не успел вычитать буфер (причем с учетом того, что этот буфер аппаратный, и у драйвера есть аж целых 15 символьных интервалов, что бы его прочесть), Windows должен быть загружен по самые помидоры  Да и скорость 115200 не есть что то из ряда вон выходящее. 2 ТС - покажите код вашего потока чтения. И настройки при открытии порта. Сто процентов бага где то там  Винда не загружена больше ничем, только самой собой  . Из приложений- еще AVG крутится и Тимвьювер в режиме ожидания входящего соединения. И загрузка CPU 10-20%, обычно меньше. Я тоже думаю что бага у меня, текст привожу ниже. Текст сильно укороченный, но всю работу с портом оставил как есть.
Ruslan1_serial_port_reading_20170523.txt ( 12.62 килобайт )
Кол-во скачиваний: 51 (это сишный файл) Проблема- уже в моем кольцевом буфере байтов не хватает.
|
|
|
|
|
May 23 2017, 12:51
|
Профессионал
    
Группа: Свой
Сообщений: 1 700
Регистрация: 2-07-12
Из: дефолт-сити
Пользователь №: 72 596

|
Цитата(@Ark @ May 23 2017, 14:04)  Вероятная причина, все-таки, несовпадение тактовых частот - ошибка более 1%. Если есть возможность, стоит посмотреть какая тактовая там реально получается в устройстве для скорости 115200.
--- P.S. Кстати, порт компьютера тоже стоит проверить, на всякий случай. Передавайте в него непрерывный поток нулей на 115200 и посмотрите реальную скорость на выходе. считаю, что расхождение частот компового железа создать таких проблем не может. Стандарты на ком-порт растут ногами из ITU-T на телеграф лохматых годов, поэтому приемник обязан принимать всё, что отличается по частоте на +- 20%. Само собой все реализации UART начиная 16550, и заканчивая современными FTDI - им соответствуют. (Хотя за все китайские клоны переходников USB -UART ручаться не могу.)
--------------------
провоцируем неудовлетворенных провокаторов с удовольствием.
|
|
|
|
|
May 23 2017, 12:51
|
Гуру
     
Группа: Свой
Сообщений: 2 360
Регистрация: 6-03-06
Из: Кишинев
Пользователь №: 15 025

|
Цитата(ViKo @ May 23 2017, 13:31)  Посмотрел, сколько стопов использую, оказалось, 1. Раньше было 2. Работает безупречно. Длина передаваемого кадра (непрерывного пакета) до 16 кБ. Понял, спасибо. У меня потеря раз в час-два, иногда чаще, иногда реже. То есть одна потеря на десяток или более мегабайт траффика. Причем часто ошибки идут в соседних пакетах, может действительно пиковая кратковременная загрузка системы, а у меня что-то не так с буферизацией.
|
|
|
|
|
May 23 2017, 13:15
|
Знающий
   
Группа: Участник
Сообщений: 688
Регистрация: 13-05-16
Пользователь №: 91 710

|
Цитата(krux @ May 23 2017, 15:51)  считаю, что расхождение частот компового железа создать таких проблем не может... Вы пишите очевидные вещи - так как должно быть. Я же предлагаю ТС проверить, что есть в реальности. Особенно это касается внешнего устройства. Частоты для UART там обычно получаются делением основной тактовой частоты. Она далеко не всегда достаточно точно делиться на 115200. Ошибку более 1% можно легко получить, если не смотреть за этим. Цитата(krux @ May 23 2017, 15:51)  ... приемник обязан принимать всё, что отличается по частоте на +- 20%. Это не верно. Чтобы гарантированно без ошибок принимать непрерывный проток - разность частот должна быть не более 1% (по многолетнему опыту). Все, что выше - с вероятностью сбоев. Тем выше, чем больше разность частот. Разброс 2% - это уже очень плохо, 5% - как правило не работает ничего.
Сообщение отредактировал @Ark - May 23 2017, 13:25
|
|
|
|
|
May 23 2017, 13:33
|

Профессионал
    
Группа: Свой
Сообщений: 1 818
Регистрация: 15-10-09
Из: Владивосток
Пользователь №: 52 955

|
Цитата(Ruslan1 @ May 23 2017, 22:42)  Я тоже думаю что бага у меня, текст привожу ниже. Текст сильно укороченный, но всю работу с портом оставил как есть.
Ruslan1_serial_port_reading_20170523.txt ( 12.62 килобайт )
Кол-во скачиваний: 51 (это сишный файл) 1. Не видно, где организуется поток для ввода (CreateThread). Я обычно это делаю в функции открытия главного окна приложения. Если сомневаюсь, открываю поток с приоритетом ABOVE_NORMAL, но чаще всего - с нормальным приоритетом 2. После WaitCommEvent не вызываю WaitForSingleObject: вообще не знаю, что это за функция, как-то обхожусь, хотя тоже задействую структуры OVERLAPPED. В остальном обработка похожая Дома исходников нет, завтра посмотрю на работе: когда-то давал студентам-дипломникам шаблонные советы по организации обмена по com-порту
|
|
|
|
|
May 23 2017, 14:06
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Вызов Synchronize(RxDataProcessing); из нити чтения COM порта тормозит эту самую нить до тех пор, пока RxDataProcessing не вернется. Видимо это может быть долго и байт другой может потеряться. Более того, совершенно непонятно почему у вас эта самая RxDataProcessing вызывается как из главной формы (по таймеру), так и из COM нити - это явно неправильно. Далее, при вызове RxDataProcessing из main потока она у вас читает и модифицирует переменные, которые используются в COM потоке (RxBuff.wrpnt) - это совершенно недопустимо. В общем вам надо переработать схему передачи прочтенных данных между нитями. Далее, конструкция if (RxBuff.wrpnt >= RX_RINGBUFF_SIZE) RxBuff.wrpnt = 0; не должна срабатывать никогда. Это переполнение приемного буфера. Надо бы ее отследить и показать пользователю, а не тихо выбрасывать все, что накопилось  Далее, результат вызова ClearCommError надо бы проверить на ошибки. Вызов ReadFile тоже было бы неплохо проверить на успешность (так, на всякий случай)
|
|
|
|
|
May 23 2017, 15:09
|
Местный
  
Группа: Участник
Сообщений: 319
Регистрация: 27-09-07
Пользователь №: 30 877

|
Цитата(Ruslan1 @ May 23 2017, 15:17)  Хм. Обычно я стараюсь следить за тем что говорю и не "путать" людей. Например я ни разу нигде не упоминал слово "Драйвер". Я также дал ссылку на то на базе чего писал софт. И в том документе тоже их программа ни разу нигде не названа драйвером. Да действительно не используете. видимо слово "прерывание" меня в ступор ввело, и многочисленные упоминания как работает контроллер УАРТА. напрямую с контроллером ,кроме драйвера, ведь венда никому работать не даст. Цитата(Ruslan1 @ May 23 2017, 15:17)  То есть из Ваших слов следует, что предложенный по моей ссылке способ использования винапишных функций негодный, так как Вы советуете мне начинать с нуля и смотреть на другие функции? Я правильно понял? Нет, я советую как раз не тратить время на различные компоненты и библиотеки билтера - коли в них есть сомнения а идти прямиком на винапи. Цитата(Ruslan1 @ May 23 2017, 15:17)  Это не предложение а костыль. Чтобы его применить- нужны аргументы и пояснения, почему "8N1" теряет байты, а вот "8N2" - уже "хорошее предложение". То есть у Винды (или у моего FIFO в железе) есть глюк, полное исправление которого возможно удлинением времени передачи байта на 1 бит? когда у вас появятся помехи на линии, вы вспомните про этот костыль. потому что 8N1 не имеет никакого признака начала фрейма за исключением качественной синхронизации 2х абонентов. как правило эта синхронизация регулярно обновляется появлением пауз между байтами. но если вы шлете долго долго плотный поток на предельной скорости - нет никакого способа приемнику определить начало байта. Цитата(Ruslan1 @ May 23 2017, 15:17)  Кстати да, просто опять поставить АсинкПро и попробовать из-под него тоже неплохая идея- вряд ли я сделаю общение с виндовыми апи лучше чем они. Или есть что-то еще уровня Асинк Про для стареньких билдеров? работа с винапи будет не сложнее, У вас все нормально же написано. сделать сильно проще не получится. Цитата(XVR @ May 23 2017, 18:06)  Вызов Synchronize(RxDataProcessing); из нити чтения COM порта тормозит эту самую нить до тех пор, пока RxDataProcessing не вернется. Видимо это может быть долго и байт другой может потеряться. Более того, совершенно непонятно почему у вас эта самая RxDataProcessing вызывается как из главной формы (по таймеру), так и из COM нити - это явно неправильно. Далее, при вызове RxDataProcessing из main потока она у вас читает и модифицирует переменные, которые используются в COM потоке (RxBuff.wrpnt) - это совершенно недопустимо. В общем вам надо переработать схему передачи прочтенных данных между нитями. +1 Все еще хуже Synchronize тут использовать это жупел. он делает блокируеще исполнение RxDataProcessing в нитке VCL - когда все формочки перерисовываются. это тормоз который трудно описать. используйте сигналы (Event) или семафоры чтобы передавать другой нитке ваш буфер. да и обработку этого буфера лучше перенести сюда из нитки VCL , и отдавать уже гтовые распознаные и провереные пакеты.
Сообщение отредактировал AlexRayne - May 23 2017, 15:11
|
|
|
|
|
May 23 2017, 17:13
|
Гуру
     
Группа: Свой
Сообщений: 2 360
Регистрация: 6-03-06
Из: Кишинев
Пользователь №: 15 025

|
Большое спасибо всем за кучу отличных идей и советов, буду разбираться! Спасибо за замечание про Synchronize, именно по озвученным причинам я этот метод и не использую (вначале использовал, но в процессе борьбы с теряющимися байтами перестал). Сильно извиняюсь что не удалил из текста неиспользуемые строки. Они там просто закомментарены в том исходнике который вы смотрите, и снабжены комментарием: Цитата //the method is changed, just periodic polling in main task by a timer // Synchronize(RxDataProcessing); То есть эта нитка сама по себе и валит в глобальный буфер, не останавливаясь на этот Synchronize(). Цитата(XVR @ May 23 2017, 16:06)  Вызов Synchronize(RxDataProcessing); из нити чтения COM порта тормозит эту самую нить до тех пор, пока RxDataProcessing не вернется. Видимо это может быть долго и байт другой может потеряться. там "//" перед этим, есть строка не используется. Цитата(XVR @ May 23 2017, 16:06)  Далее, при вызове RxDataProcessing из main потока она у вас читает и модифицирует переменные, которые используются в COM потоке (RxBuff.wrpnt) - это совершенно недопустимо. О!! вот тут может и есть мой ошибк. Я думал что достаточно их как volatile объявить и использовать. Цитата(XVR @ May 23 2017, 16:06)  В общем вам надо переработать схему передачи прочтенных данных между нитями. дада, спасибо, буду думать. А как без Synchronize это сделать? неужто через глобальные переменные нельзя? Просто скажите в каком направлении копать, я копать умею только где не знаю.... (Upd: извиняюсь, уже прочитал совет про евенты и семафоры, значит через них буду. Тогда уже сразу про очереди поищу, чтоб просто мессадж с новым указателем записи передать в ожидающую нитку- надеюсь оно тут есть где-то). Цитата(XVR @ May 23 2017, 16:06)  Далее, конструкция if (RxBuff.wrpnt >= RX_RINGBUFF_SIZE) RxBuff.wrpnt = 0; не должна срабатывать никогда. Это переполнение приемного буфера. Надо бы ее отследить и показать пользователю, а не тихо выбрасывать все, что накопилось  У меня классический кольцевой буфер, в который один процесс что-то записывает по указателю записи, другой процесс -что-то читает по указателю чтения. Само собой, когда-то доходим до конца буфера и должны следующий байт в его начало записать. Я не понял Вашу идею про "никогда". Указатель записи доходит до RX_RINGBUFF_SIZE периодически, каждые RX_RINGBUFF_SIZE байт.
|
|
|
|
|
May 23 2017, 17:58
|
Гуру
     
Группа: Свой
Сообщений: 3 123
Регистрация: 7-04-07
Из: Химки
Пользователь №: 26 847

|
Через Synchronize можно, но в нем должно выполняться минимум действий - перекладывание данных из буфера приемного потока в буфер главной формы и немедленный возврат. Так же надо при открытии COM порта задать внутренние буфера системы на COM порт побольше, что бы при вызовах Synchronize данные не терялись, а складывались в этот буфер. Если все же будет не хватать времени, то можно подумать и об отказе от Synchronize Далее, в нити приема нужно делать не кольцевой буфер, а прием пакета (например в массив). Потом полностью принятый пакет (нарезанный по его границе) внутри Synchronize копируется в AnsiString и засовывается в очередь (например в std::deque<AnsiString>). Извлечение из очереди делается по таймеру. Этот подход хорош тем, что он весьма простой, и оставляет много возможностей для оптимизации (начиная от собственного менеджера памяти, что бы не тягать данные через AnsiString и кончая собственной асинхронной очередью без привлечения Synchronize) У меня даже такая очередь есть (была сделана в качестве примера для лекций по программированию) CODE //---------------------------------------------------------------------------
#ifndef queueH #define queueH //--------------------------------------------------------------------------- #include <deque>
template<class Item> class WQueue { std::deque<Item> queue;
CRITICAL_SECTION cs; HANDLE sema; HANDLE not_emp_sig;
public: WQueue(int max_size) { sema=CreateSemaphore(NULL,max_size,max_size,NULL); not_emp_sig=CreateEvent(NULL,FALSE,FALSE,NULL); InitializeCriticalSection(&cs); } ~WQueue() { CloseHandle(sema); CloseHandle(not_emp_sig); DeleteCriticalSection(&cs); }
void put(const Item& it) { WaitForSingleObject(sema,INFINITE); EnterCriticalSection(&cs); queue.push_back(it); SetEvent(not_emp_sig); LeaveCriticalSection(&cs); }
Item get() { for(;;) { WaitForSingleObject(not_emp_sig,INFINITE); EnterCriticalSection(&cs); if (queue.empty()) { LeaveCriticalSection(&cs); continue; } Item rv=queue.front(); queue.pop_front(); if (!queue.empty()) SetEvent(not_emp_sig); ReleaseSemaphore(sema,1,NULL); LeaveCriticalSection(&cs); return rv; } }
size_t get_cur_size() { EnterCriticalSection(&cs); size_t rv=queue.size(); LeaveCriticalSection(&cs); return rv; }
};
#endif Как раз от BCB6.0 Только семафор оттуда удалите - он был сделан для ограничения максимального размера очереди
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|