|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
|||
|
|||
Работа с COM портом на Delphi
Добрый день, прошу помочь людей с опытом работы с COM портом на Delphi.
Разрабатываю программу на Delphi 10.4 для связи с цифровым манометром через протокол RS-232. Физически связь осуществляется ПК - USB преобразователь RS-232 - витая пара UTP 4P - цифровой манометр. скорость обмена данными 9600 б/с количество бит данных 8 без проверки на четность количество стоп-бит – 1 Программа через таймер каждую секунду шлёт запрос, на что цифровой манометр отвечает, отправляя значение одного параметра (давления) формируя ответ согласно своего протокола, пример байт в HEX формате: FF 00 FF FF 86 FF FF FF FF 04 01 05 00 00 01 3D 6E 7F 28 83 из них самый последний байт является контрольной суммой сообщения, а перед ним 4 байта - значение в формате IEEE754(Float) Для работы с COM портом использую библиотеку ComPort Library v4.11, сообщение через COM порт приходит не сразу, а частями (причём длина частей не постоянна), поэтому перед запросом я очищаю буфер, а по мере прихода частей собираю пакет (буфер) через глобальную переменную проблема заключается в следующем, в результате получения ответов и сборки их в пакет (буфер) я получаю неправильный результат (неправильное значение параметра (давления) и неверную контрольную сумму), но только стоит мне добавить лишнее ненужное действие (например вывод промежуточных результатов в Memo1) происходит следующее, при добавлении строк в Memo1 происходить прокручивание Memo1 вниз (через ScrollBars) и обратное поднятие вверх, программа как бы замедляется и в результате я получаю правильный пакет (буфер), у которого верное значение и контрольная сумма. что самое для меня непонятное, приведу пример, сначала я запускаю родную программу от манометра, и запрашиваю к примеру 20 раз значение манометра и все 20 раз приходит правильное значение параметра, закрываю программу, открываю свою программу, делаю запрос и вывод без лишних действий, сколько бы я не отправлял запрос, ответ приходит неправильный, стоит включить лишние действия (вывод в Memo1), всё становится нормально, результат и контрольная сумма правильная. так же есть интересный эффект, после нескольких запросов с лишними действиями, при отключении лишних действий, начинают приходить правильные ответы даже без лишних действий, что вообще не поддаётся моей логике. у меня есть несколько вариантов (возможно вы предложите свой, более правильный): 0) другой алгоритм сборки пакета (может быть ошибка в нём) 1) специальное замедление программы (выполнение каких-то лишних действий) 2) замедление программы через функции Sleep, Delay (подскажите как правильно сделать? какое значение задержки? или пример кода) 3) формирование пакета (буфера) не в ручную, а через TComDataPacket, как только его правильно настроить? есть параметр StartString, но мне нужно начало пакета определять не по стартовой строке, а по 4-ом стартовым байтам (пакет начинается с FF 00 FF FF, просто FF может повторяться в сообщении) 4) выставление временных задержек COM-port'а, сейчас значения стоят по умолчанию Timeouts.ReadIntervalTimeout:=-1; Timeouts.ReadTotalTimeoutConstant:=0; Timeouts.ReadTotalTimeoutMultiplier:=0; Timeouts.WriteTotalTimeoutConstant:=1000; Timeouts.WriteTotalTimeoutMultiplier:=100; пробовал выставить следующие значения: Timeouts.ReadIntervalTimeout:=50; Timeouts.ReadTotalTimeoutConstant:=100; Timeouts.ReadTotalTimeoutMultiplier:=70; Timeouts.WriteTotalTimeoutConstant:=100; Timeouts.WriteTotalTimeoutMultiplier:=60; результат тот же, приходит не верный ответ так же есть родная программа от манометра, в которой заданы временные интервалы (смотреть прилагаемые картинки), но какие задать мне значения Timeouts исходя из параметров родной программы (может кто-то подскажет верные)? так же в целом прошу проверить код, может опытные люди найдут ошибки или предложат оптимизировать процедуру? код получения и сборки пакета: Код:
var CPdata:array[0..25] of byte; //переменная для сборки пакета из ком порта CPcount:integer=0; //количество собранных бит procedure TForm1.ComPortRxChar(Sender: TObject; Count: Integer); var data:array[0..18] of Byte; //полученная информация с COM порта src:array[0..3] of Byte; //переменная для вытаскивания значения давления vali:integer; //значение давления в integer i:integer; //i - счётчик str:string; //str - для консоли vals:single absolute src; //конвертирование из IEEE754 в single begin ComPort.Read(data,Count); //читаем данные из COM порта for i := 0 to Count do CPdata[CPcount+i]:=data[i]; //добавляем инф-ию к пакету for i := 0 to Count do Memo1.Lines.Add('i: '+IntToStr(i)+' CPdata['+IntToStr(CPcount+i)+']:='+IntToHex(data[i])+' CPcount:'+IntToStr(CPcount)); //эта строчка не нужна, но даёт правильный результат CPcount:=CPcount+count+1; //количество собранных бит if CPcount<20 then exit; //если не набрали 20 байт в пакете - выходим из процедуры // src[0]:=CPdata[18]; //value 4 src[1]:=CPdata[17]; //value 3 src[2]:=CPdata[16]; //value 2 src[3]:=CPdata[15]; //value 1 // vali:=Round(vals); //округляем из single в integer Memo1.Lines.Add('Значение: '+IntToStr(vali)+' ('+format('%10.5F',[vals])+') ['+IntToHex(src[0])+' '+IntToHex(src[1])+' '+IntToHex(src[2])+' '+IntToHex(src[3])+']'); //выводим в консоль значение end; Последний раз редактировалось Serje88, 29.12.2021 в 13:59. |
#2
|
|||
|
|||
Ну, без железок отладить трудно, но пару идей подкину.
Похоже, затык происходит либо в компоненте (COMPort), либо где-то в самой VCL. Т.е. если происходят доп. действия (которые, как мне кажется, обрабатывают очередь сообщений) и все работает, то тут скорее дело в том, что работа основной программы (твоего кода) просто блокирует получение данных. Что можно попробовать. 1. Вставить Aplication.ProcessMessages (кажется так) в цикл работы твоей программы, что принудительно вызовет обработку сообщений в очереди. 2. Вынести работу с железкой в отдельный поток. Там посылать запросы, вычитывать ответ и по получению полного пакеты передавать результат в основной поток программы для отображения. |
#3
|
|||
|
|||
спасибо за помощь, ошибка была элементарная, вместо "for i:=0 to Count" должно быть "for i:=0 to Count-1", всё работает отлично с таймаутами по умолчанию
|