Показать сообщение отдельно
  #1  
Старый 29.12.2021, 13:37
Serje88 Serje88 вне форума
Прохожий
 
Регистрация: 29.12.2021
Сообщения: 2
Версия Delphi: Delphi 10.4
Репутация: 10
По умолчанию Работа с 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;
Изображения
Тип файла: jpg 1 — small.jpg (72.1 Кбайт, 3 просмотров)
Тип файла: jpg 2 — small.jpg (76.7 Кбайт, 3 просмотров)
Ответить с цитированием