Полная версия этой страницы:
Работа с COM-портом
Тема не раз поднималась, но все таки остались вопросы.
Есть датчик, который посылает на ПК некие промежуточные результаты. На ПК все нормально принимается и обсчитывется конечный результат. Пакеты по 8 байт. Скорость UART 115200 kbps. Использую ReadFile() и CPort ( В CPort тоже используется ReadFile() ). Но как только частота передачи возрастает до 1000 посылок в секунду, то в винде начинатся проблемы: приходят неполные пакеты, часто приходит только заголовок пакета, а остальная часть нули или заголовок может оказаться в середине пакета. Увеличение приемного буфера в винде не решило проблемы. При отключении графики и прочего пакеты иногда принимаются нормально. Но без отображения результатов никак.
ЗЫ
В Terminal все принимается нормально - там есть возможность записать в лог принимаемые байты.
Пробовал DLPortIО, но так и не понял как подружить COM с этим драйвером.
beer_warrior
Feb 3 2007, 00:37
А каким образом вызывается ReadFile()? По событию, таймеру, в отдельном потоке?
beer_warrior
Feb 3 2007, 04:07
А portmonом его? Такое впечетление, что принимается оно правильно, а из буфера вынимается не в тот момент.
Дык, я ж говорю, что терминальной программой все нормально принимается (то что она несколько зависает, то ничего - главное все пакеты целые). Дело именно в моей программе =)
Этим читаю:
Код
procedure InComm1(var i:byte;var IsIn:boolean);
var
bytes1:dword;
begin
IsIn:=false;
clearcommerror(hcom1,com_error,@stat1);
if stat1.cbInQue>0
then
begin
if readfile(hcom1,databuf1,bufsize1,bytes1,nil)
then
begin
i:=databuf1;
IsIn:=true;
end
else
showmessage('Ошибка приема');
end;
end;
Читаю просто вызовом функции в потоке.
(та же проблема остается при использовании CPort310)
lolikandr
Feb 3 2007, 12:45
ReadFile() вызывается в отдельном потоке по какому событию? При помощи SetCommMask или еще как?
PS: Пока писал, ответ появился. Почитайте что-нибудь типа такого:
http://www.delphimaster.ru/articles/comport2/index.htmlСразу скажу, что лучше Overlapped режим.
Сегодня выяснилось, что и в Terminal приходят обрывки пакетов, реже чем в моей программе, но все-таки они есть. portmon показывает, что там приходят именно обрывки

Сейчас железо очередной раз перепроверяю. Попробую снизить скорость.
Блин, где же собака порылась ?
Andrew2000
Feb 3 2007, 22:25
Цитата(CSB @ Feb 3 2007, 16:05)

Попробую снизить скорость
Скорость чего? Ваши 8 байт раз в 1мс только на 115200 и пролезут (если я математику не забыл)
Может в паузе между байтами или пакетами?
Цитата
Скорость чего? Ваши 8 байт раз в 1мс только на 115200 и пролезут (если я математику не забыл)
Скорость передачи. Это тестовая задача, потому можно варьировать некоторыми параметрами. Просто буду снимать меньше показаний и все. Такое впечатление, что пакеты теряются по дороге к ПК, хотя если смотреть по JTAG'у, то все нормально.
Математика: 115200 kbps - это 11520 kBps (10 байт кадр). У меня получается 8 kBps. Поэтому остается некоторый запас.
Цитата
Может в паузе между байтами или пакетами?
Не понял что вы имели в виду. (Паузы между пакетами разные - зависят от воздействия на датчик.)
Ну путь винда как-то отделяет во времени пакеты, но теряться-то при этом они не должны, т.к. входного буфера должно вполне хватать.
Andrew2000
Feb 4 2007, 01:33
Цитата(CSB @ Feb 4 2007, 00:43)

Скорость передачи. ...
Я имел ввиду, что на 57600 уже не пролезет.
Цитата(CSB @ Feb 4 2007, 00:43)

... (10 байт кадр). ...
Бит? т.е. без контроля - тока старт и стоп.
Обычно, хвосты теряются из-за ошибок передающей стороны - когда выдаете следующий байт не дождавшись "ухода" предыдущего в линию.
Цитата
Бит? т.е. без контроля - тока старт и стоп.
угу, конечно бит.
Цитата
Обычно, хвосты теряются из-за ошибок передающей стороны - когда выдаете следующий байт не дождавшись "ухода" предыдущего в линию.
Следующий байт записываю в регистр передачи только по окончанию передачи предыдущего байта.
С байтами понятно вы их не затираете.
Между пакетами тоже у вас пауза выдерживается, но независмо от ПК насколько я понял.
Стартовый байт есть в пакете? Байт стаффинг или 9битный формат передачи для стартового байта используется?
Пакет обрамлен стартовыми и оконечными байтами (при таком обрамлении пакета мне проще онадизировать целостность пакета). Формат передачи: 10 бит, стартовый бит + 8 бит данных + 1 стоп-бит. Все стандартно.
immelstorm
Feb 4 2007, 22:45
В принципе, можно попробовать работать с портом напрямую.
Я писал прогу, работающую как цифровой осциллограф, у меня получились следующие результаты:
Погрешность измерения временных интервалов:
На частоте до 1 кГц: не более 1%;
На частоте 1-10 кГц: не более 10%;
На частоте 10-50 кГц: не более 50%;
Вот кусок кода, отвечающий за доступ к порту:
//Маска битов для определения линии запуска
WaitMask:=$0;
if CTS_Start.Checked then WaitMask:=WaitMask or $10;
if DSR_Start.Checked then WaitMask:=WaitMask or $20;
if RI_Start.Checked then WaitMask:=WaitMask or $40;
if DCD_Start.Checked then WaitMask:=WaitMask or $80;
//Настройка порта
with SPort do begin
OpenDriver;
WriteByte(Base+3,$83);
WriteWord(Base,$0001);
WriteByte(Base+3,$3);
end;
HoldByte:=0;
WR:=SPort.ReadByte(Base+4);
SPort.WriteByte(Base+4,WR and $FC);
if HoldRTS.Checked then HoldByte:=HoldByte or $02;
if HoldDTR.Checked then HoldByte:=HoldByte or $01;
//Зарядка конденсатора, ожидание переходных процессов.
StartTimer.Enabled:=true;
While StartTimer.Enabled and (Aborted = false) do begin
//Держать сигналы
WR:=SPort.ReadByte(Base+4);
SPort.WriteByte(Base+4,WR or HoldByte);
Application.ProcessMessages;
end;
//Ожидание первого импульса.
D:=SPort.ReadByte(Base+6) and WaitMask;
//D1:=D and WaitMask;
repeat
Application.ProcessMessages;
WR:=SPort.ReadByte(Base+4);
SPort.WriteByte(Base+4,WR or HoldByte);
D1:=SPort.ReadByte(Base+6);
D1:=D1 and WaitMask;
until (D<>D1) or Aborted;
//Байт для удержания RTS и/или DTR
WR:=SPort.ReadByte(Base+4);
SPort.WriteByte(Base+4,WR or $02);
//Включение таймеров
HighResTimer1.Enabled:=true;
HighResTimer1.StartTimeMeasure;
STime:=GetTickCount;
while ((GetTickCount-STime)<MeasureTime) and (not Aborted) do begin
//RTS / DTR
SPort.WriteByte(Base+4,WR);
//Сигналы
D:=SPort.ReadByte(Base+6);
if D<>D1 then begin
HighResTimer1.StopTimeMeasure;
Time:=HighResTimer1.GetTimeDifference;
HighResTimer1.StartTimeMeasure;
SetLength(Data, I+1);
Data[I,0]:=Time+Data[I-1,0];
Data[I,1]:=D;
D1:=D;
Inc(I);
end;
end;
EndTime:=GetTickCount;
HighResTimer1.StopTimeMeasure;
Разумеется, нужно знать спецификации UART.
Сразу предупреждаю: в windows работа с железками в реальном времени возможна только из RING 0. Всё остальное - разные степени приближения.
Цитата
Пакет обрамлен стартовыми и оконечными байтами (при таком обрамлении пакета мне проще онадизировать целостность пакета). Формат передачи: 10 бит, стартовый бит + 8 бит данных + 1 стоп-бит. Все стандартно.
1.Байт стаффинг используется? Что например получится если один из неадресных байтов будет таким же как адресный?
2.Вы говорили что пакеты неполные получаете => целостность пакетов нарушена. Если п.1 не поможет попробуйте оставить всё как есть, но передавать из железяки всё время одинаковый пакет типа
s 1 2 3 4 5 e, где s, e стартовый и стоповый байты, а цифирки - значения передаваемых байтов. Может локализуете проблему.
Байт стаффинг не используется: пакет-то обрамлен с двух сторон и имеет фиксированную длину, поэтому отличать адресный байт от неадресного просто.
Цитата
2.Вы говорили что пакеты неполные получаете => целостность пакетов нарушена. Если п.1 не поможет попробуйте оставить всё как есть, но передавать из железяки всё время одинаковый пакет типа
s 1 2 3 4 5 e, где s, e стартовый и стоповый байты, а цифирки - значения передаваемых байтов. Может локализуете проблему.
Передавал и смотрел в железяку JTAG'ом - отправлялось все нормально. Кабель для связи - экранированный.
lolikandr
Feb 6 2007, 09:52
Была такая же бага (потери кусков данных), когда читал просто в цикле по такому же принципу:
Код
if stat1.cbInQue>0 then
И тоже терминальной программой все нормально принималось.
Только, когда использовал компонент TComm от AsyncPro и событие OnRxChar, получил все данные без потерь от непрерывного потока на обычном COM-порту при скорости 115200.
На том же компоненте удалось получать данные и при скорости 921600 на виртуальном COM-порту от FTDI.
По поводу проверки железа - Rx и Tx закорачивается в конце кабеля. Пишется микрокусок программы (а то и отдельная микропрограмма) на передачу-прием-контроль. Сразу видно, что не так. Для виндовс программы тоже желательно провести такую процедуру, чтобы быть увереным в своей программе.
Цитата(CSB @ Feb 2 2007, 16:11)

Тема не раз поднималась, но все таки остались вопросы.
Есть датчик, который посылает на ПК некие промежуточные результаты. На ПК все нормально принимается и обсчитывется конечный результат. Пакеты по 8 байт. Скорость UART 115200 kbps. Использую ReadFile() и CPort ( В CPort тоже используется ReadFile() ). Но как только частота передачи возрастает до 1000 посылок в секунду, то в винде начинатся проблемы: приходят неполные пакеты, часто приходит только заголовок пакета, а остальная часть нули или заголовок может оказаться в середине пакета. Увеличение приемного буфера в винде не решило проблемы. При отключении графики и прочего пакеты иногда принимаются нормально. Но без отображения результатов никак.
ЗЫ
В Terminal все принимается нормально - там есть возможность записать в лог принимаемые байты.
Пробовал DLPortIО, но так и не понял как подружить COM с этим драйвером.
была похожая проблемма: никак не удавалось получить полный пакет. решил с помощью побайтового приема (ReadFile() в цикле принимает один байт, который потом складывается в буфер)
Цитата
терминальной программой все нормально принималось.
Повторюсь: проблема была выявлена и при работе с терминалом.
Цитата
решил с помощью побайтового приема
По-байтовый прием и делаю.
Тут на форуме читал, что ReadFile() весьма медленная функция и нужно писать свой драйвер или юзать что-то типа DriverLINX Port IO. Но DriverLINX Port IO пока так и не смог прикрутить к СОМ-порту.
Цитата
Тут на форуме читал, что ReadFile() весьма медленная функция и нужно писать свой драйвер
Ну хз. Ни в Win98 ни в WinXP подобной проблемы у меня не было, хотя пакеты имели бо'льший размер!
ReadFile должна возвращать количество принятых байт, возвращает 8? Было бы неплохо посмотреть код настройки ком порта.
AndrewKirs
Feb 9 2007, 17:41
По-моему, надо использовать асинхронный режим IO (CreateFile с флагом OVERLAPPED), и буфер достаточно большого размера (ну хоть несколько Кб).
Georgy
Feb 23 2007, 02:08
А ни слова про длительность и амплитуду импульсов.
Проверьтесь - было дело, бодался с таким явлением, оптопара H11Ll1 уродовала длительность. После подбора тока диода сбои истребились.
Удачи!
Убедится в наличии паузы по сигналу Rx между пакетами.
Установить ПРАВИЛЬНО таймауты - SetCommTimeouts()
Проверят количество принятых байт после ReadFile()
Для просмотра полной версии этой страницы, пожалуйста,
пройдите по ссылке.