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

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

Я пересылал в комп пакеты данных на скорости 115200, сбоев не замечал. Формат был 8N2. Вот его и посоветую попробовать.

Какое количество байтов в сутки? что такое "сбоев не замечал"? Их не было? Или может быть были, но их никто не логгировал? И, ключевой вопрос- чем принимали (какой софт)?

К сожалению, 8N1.5 или 8N2 не подходит. Как и не подходит "снизить скорость до [тут вставить желаемое]".

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


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

Я не сутками пересылал, а кадрами. По ним - обработка. Количество байтов на стороне передачи и на стороне приема совпадало. К результатам обработки тоже не в претензии.

Принимал и терминальной программой HTerm и Матлабом.

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


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

Вероятная причина, все-таки, несовпадение тактовых частот - ошибка более 1%.

Если есть возможность, стоит посмотреть какая тактовая там реально получается в устройстве для скорости 115200.

 

---

P.S. Кстати, порт компьютера тоже стоит проверить, на всякий случай.

Передавайте в него непрерывный поток нулей на 115200 и посмотрите реальную скорость на выходе.

 

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

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


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

Вот это именно и имел ввиду - что вы путаете всех использованием слова "драйвер".

Хм. Обычно я стараюсь следить за тем что говорю и не "путать" людей. Например я ни разу нигде не упоминал слово "Драйвер". Я также дал ссылку на то на базе чего писал софт. И в том документе тоже их программа ни разу нигде не названа драйвером.

 

В венде уже написаны дрова под множество материнок и уж тем более под известные стандартные контроллеры УАРТ. Вот они как раз напрямую с железом и прерыванием работают. писать их не требуется - они готовы и встроены в Ось. Овь прелагает довольно простое стандартное АПИ для работы с Сомм портами. Если вы поставили борланд - покурите их хелпы Win32 SDK или чтото вроде этого. Это огромный хелп по всем ресурсам венды. и в частности там описан и Comm интерфейс. Если вы полезете в изходники АсинкПро - то наглядно увидите как вендовые вызовы используются. АсинкПро - это обертка вендовых вызовов.

 

То есть из Ваших слов следует, что предложенный по моей ссылке способ использования винапишных функций негодный, так как Вы советуете мне начинать с нуля и смотреть на другие функции? Я правильно понял?

 

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

Спасибо, буду читать про Comm.

 

 

Вам уже посоветовали использовать 1,5-2 стопбита. это хорошее предложение, если оно конечно возможно для ваших абонентов.

Это не предложение а костыль. Чтобы его применить- нужны аргументы и пояснения, почему "8N1" теряет байты, а вот "8N2" - уже "хорошее предложение". То есть у Винды (или у моего FIFO в железе) есть глюк, полное исправление которого возможно удлинением времени передачи байта на 1 бит?

 

С адаптерами КОМ-УСБ советую не играться если есть нормальный порт на матери или PCI плате ввода вывода. Я встретил кривой адаптер от МОХи, народ жаловался на ФТДИ - у обоих проблемы с буфером приемника на больших трафиках.

Попробую, конечно, вдруг таки да, но только на столе. В проекте не покатит- система необслуживаемая и увеличивать количество возможных проблем добавкой USB канала совсем не хочется. Особенно при наличии нормальных COM-портов.

 

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

Или есть что-то еще уровня Асинк Про для стареньких билдеров?

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


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

У стандартного аппаратного СОМ порта есть внутренний буфер, как правило размером 15 байт. Это не тот буфер, который задается в SetupComm. Если драйвер порта по каким-то причинам не успеет прочитать этот буфер, то принятые байты теряются, о чем извещают соответствующие флаги возвращаемые ClearCommError.
Чтение этого буфера выполняется системным драйвером, причем на самом высоком приоритете (аппаратных прерываний). И что бы он (драйвер) не успел вычитать буфер (причем с учетом того, что этот буфер аппаратный, и у драйвера есть аж целых 15 символьных интервалов, что бы его прочесть), Windows должен быть загружен по самые помидоры :rolleyes: Да и скорость 115200 не есть что то из ряда вон выходящее.

 

2 ТС - покажите код вашего потока чтения. И настройки при открытии порта. Сто процентов бага где то там :smile3046:

 

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


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

Посмотрел, сколько стопов использую, оказалось, 1. Раньше было 2. Работает безупречно. Длина передаваемого кадра (непрерывного пакета) до 16 кБ.

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


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

Чтение этого буфера выполняется системным драйвером, причем на самом высоком приоритете (аппаратных прерываний). И что бы он (драйвер) не успел вычитать буфер (причем с учетом того, что этот буфер аппаратный, и у драйвера есть аж целых 15 символьных интервалов, что бы его прочесть), Windows должен быть загружен по самые помидоры :rolleyes: Да и скорость 115200 не есть что то из ряда вон выходящее.

 

2 ТС - покажите код вашего потока чтения. И настройки при открытии порта. Сто процентов бага где то там :smile3046:

Винда не загружена больше ничем, только самой собой :). Из приложений- еще AVG крутится и Тимвьювер в режиме ожидания входящего соединения. И загрузка CPU 10-20%, обычно меньше.

Я тоже думаю что бага у меня, текст привожу ниже. Текст сильно укороченный, но всю работу с портом оставил как есть.

Ruslan1_serial_port_reading_20170523.txt (это сишный файл)

Проблема- уже в моем кольцевом буфере байтов не хватает.

 

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


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

Вероятная причина, все-таки, несовпадение тактовых частот - ошибка более 1%.

Если есть возможность, стоит посмотреть какая тактовая там реально получается в устройстве для скорости 115200.

 

---

P.S. Кстати, порт компьютера тоже стоит проверить, на всякий случай.

Передавайте в него непрерывный поток нулей на 115200 и посмотрите реальную скорость на выходе.

считаю, что расхождение частот компового железа создать таких проблем не может.

Стандарты на ком-порт растут ногами из ITU-T на телеграф лохматых годов, поэтому приемник обязан принимать всё, что отличается по частоте на +- 20%.

Само собой все реализации UART начиная 16550, и заканчивая современными FTDI - им соответствуют. (Хотя за все китайские клоны переходников USB -UART ручаться не могу.)

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


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

Посмотрел, сколько стопов использую, оказалось, 1. Раньше было 2. Работает безупречно. Длина передаваемого кадра (непрерывного пакета) до 16 кБ.

Понял, спасибо.

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

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


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

считаю, что расхождение частот компового железа создать таких проблем не может...

Вы пишите очевидные вещи - так как должно быть. Я же предлагаю ТС проверить, что есть в реальности.

Особенно это касается внешнего устройства. Частоты для UART там обычно получаются делением основной тактовой частоты.

Она далеко не всегда достаточно точно делиться на 115200. Ошибку более 1% можно легко получить, если не смотреть за этим.

 

... приемник обязан принимать всё, что отличается по частоте на +- 20%.

Это не верно.

Чтобы гарантированно без ошибок принимать непрерывный проток - разность частот должна быть не более 1% (по многолетнему опыту).

Все, что выше - с вероятностью сбоев. Тем выше, чем больше разность частот. Разброс 2% - это уже очень плохо, 5% - как правило не работает ничего.

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

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


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

Я тоже думаю что бага у меня, текст привожу ниже. Текст сильно укороченный, но всю работу с портом оставил как есть.

Ruslan1_serial_port_reading_20170523.txt (это сишный файл)

1. Не видно, где организуется поток для ввода (CreateThread). Я обычно это делаю в функции открытия главного окна приложения. Если сомневаюсь, открываю поток с приоритетом ABOVE_NORMAL, но чаще всего - с нормальным приоритетом

2. После WaitCommEvent не вызываю WaitForSingleObject: вообще не знаю, что это за функция, как-то обхожусь, хотя тоже задействую структуры OVERLAPPED. В остальном обработка похожая

Дома исходников нет, завтра посмотрю на работе: когда-то давал студентам-дипломникам шаблонные советы по организации обмена по com-порту

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


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

Вызов Synchronize(RxDataProcessing); из нити чтения COM порта тормозит эту самую нить до тех пор, пока RxDataProcessing не вернется. Видимо это может быть долго и байт другой может потеряться.

Более того, совершенно непонятно почему у вас эта самая RxDataProcessing вызывается как из главной формы (по таймеру), так и из COM нити - это явно неправильно.

Далее, при вызове RxDataProcessing из main потока она у вас читает и модифицирует переменные, которые используются в COM потоке (RxBuff.wrpnt) - это совершенно недопустимо.

В общем вам надо переработать схему передачи прочтенных данных между нитями.

 

Далее, конструкция if (RxBuff.wrpnt >= RX_RINGBUFF_SIZE) RxBuff.wrpnt = 0; не должна срабатывать никогда. Это переполнение приемного буфера. Надо бы ее отследить и показать пользователю, а не тихо выбрасывать все, что накопилось :)

 

 

Далее, результат вызова ClearCommError надо бы проверить на ошибки.

 

Вызов ReadFile тоже было бы неплохо проверить на успешность (так, на всякий случай)

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


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

Хм. Обычно я стараюсь следить за тем что говорю и не "путать" людей. Например я ни разу нигде не упоминал слово "Драйвер". Я также дал ссылку на то на базе чего писал софт. И в том документе тоже их программа ни разу нигде не названа драйвером.

Да действительно не используете. видимо слово "прерывание" меня в ступор ввело, и многочисленные упоминания как работает контроллер УАРТА. напрямую с контроллером ,кроме драйвера, ведь венда никому работать не даст.

 

То есть из Ваших слов следует, что предложенный по моей ссылке способ использования винапишных функций негодный, так как Вы советуете мне начинать с нуля и смотреть на другие функции? Я правильно понял?

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

 

Это не предложение а костыль. Чтобы его применить- нужны аргументы и пояснения, почему "8N1" теряет байты, а вот "8N2" - уже "хорошее предложение". То есть у Винды (или у моего FIFO в железе) есть глюк, полное исправление которого возможно удлинением времени передачи байта на 1 бит?

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

 

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

Или есть что-то еще уровня Асинк Про для стареньких билдеров?

работа с винапи будет не сложнее, У вас все нормально же написано. сделать сильно проще не получится.

 

Вызов Synchronize(RxDataProcessing); из нити чтения COM порта тормозит эту самую нить до тех пор, пока RxDataProcessing не вернется. Видимо это может быть долго и байт другой может потеряться.

Более того, совершенно непонятно почему у вас эта самая RxDataProcessing вызывается как из главной формы (по таймеру), так и из COM нити - это явно неправильно.

Далее, при вызове RxDataProcessing из main потока она у вас читает и модифицирует переменные, которые используются в COM потоке (RxBuff.wrpnt) - это совершенно недопустимо.

В общем вам надо переработать схему передачи прочтенных данных между нитями.

+1

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

используйте сигналы (Event) или семафоры чтобы передавать другой нитке ваш буфер.

да и обработку этого буфера лучше перенести сюда из нитки VCL , и отдавать уже гтовые распознаные и провереные пакеты.

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

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


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

Большое спасибо всем за кучу отличных идей и советов, буду разбираться!

 

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

//the method is changed, just periodic polling in main task by a timer

// Synchronize(RxDataProcessing);

 

То есть эта нитка сама по себе и валит в глобальный буфер, не останавливаясь на этот Synchronize().

 

Вызов Synchronize(RxDataProcessing); из нити чтения COM порта тормозит эту самую нить до тех пор, пока RxDataProcessing не вернется. Видимо это может быть долго и байт другой может потеряться.

там "//" перед этим, есть строка не используется.

 

Далее, при вызове RxDataProcessing из main потока она у вас читает и модифицирует переменные, которые используются в COM потоке (RxBuff.wrpnt) - это совершенно недопустимо.

О!!

вот тут может и есть мой ошибк.

Я думал что достаточно их как volatile объявить и использовать.

 

В общем вам надо переработать схему передачи прочтенных данных между нитями.

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

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

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

 

Далее, конструкция if (RxBuff.wrpnt >= RX_RINGBUFF_SIZE) RxBuff.wrpnt = 0; не должна срабатывать никогда. Это переполнение приемного буфера. Надо бы ее отследить и показать пользователю, а не тихо выбрасывать все, что накопилось :)

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

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


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

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

 

Далее, в нити приема нужно делать не кольцевой буфер, а прием пакета (например в массив). Потом полностью принятый пакет (нарезанный по его границе) внутри Synchronize копируется в AnsiString и засовывается в очередь (например в std::deque<AnsiString>). Извлечение из очереди делается по таймеру.

 

Этот подход хорош тем, что он весьма простой, и оставляет много возможностей для оптимизации (начиная от собственного менеджера памяти, что бы не тягать данные через AnsiString и кончая собственной асинхронной очередью без привлечения Synchronize)

 

У меня даже такая очередь есть (была сделана в качестве примера для лекций по программированию)

//---------------------------------------------------------------------------

#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

 

Только семафор оттуда удалите - он был сделан для ограничения максимального размера очереди

 

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


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

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

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

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

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

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

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

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

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

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