Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Для новичков > Com порт. Перекодировать принятый байт.


Автор: zi4rox 12.5.2009, 19:25
Всем привет, 

Прошу помочь разобраться. Работаю с COM-портом, при помощи компоненты ComPort Library version 3.10, всё здорово, всё работает. Вот только загвоздка вот в чем:

От устройства я принимаю байты (вида: 0х01 0хBA 0xFF ... - вобщем что угодно но только не символы), а этот компонент, да и другие подобные читают из ком порта именно символы. Как мне преобразовать строковый символ что я принял в реально переданный числовой байт?
(попробывал StrToInt и т.п - не очень помогает)

Добавлено через 14 минут и 40 секунд
Попробывал через Byte() - но работает не на все символы. Вернее весь диапазон от 0х00 до 0хFF так передать не удастся, только те байты код символа которых существует

Автор: zi4rox 12.5.2009, 20:12
Сейчас проверил обратную процедуру - вывел через Chr() все символы от 0х00 до 0хFF
Все есть. Тогда получается всё глубже и запутаннее.

Народ, кто из ком-порта читал, как боролись с этой проблемой. 
* Поставил при чтении сейчас:
Код

Str: string;
i: char;
byte: integer;

i:=Str[1];
byte:=ord(i);


Некоторые он нормально преобразует, а некоторые принятый байты - просто на просто срезает вообще и не отображает.

Передается следующая последовательность: 0xFF 0x01 0x80 0x01 0x80 0x01 0x80 0x01 0x80 ....

после преобразований получаю только: 255 1 1 1 1 1 ....

[/code]

Автор: Keeper89 12.5.2009, 20:12
Подойдет?
Код

function HexToInt(const hexStr: String): Integer;
const
  PRFX = '0x';
  NEW_PRFX = '$';
begin
  // Преобразуем шестн. число 0x... в дес.

  Result := StrToInt(StringReplace(hexStr, PRFX, NEW_PRFX, [rfReplaceAll]));
end;


Добавлено через 4 минуты и 23 секунды
Цитата(zi4rox @  12.5.2009,  19:25 Найти цитируемый пост)
(попробывал StrToInt и т.п - не очень помогает)

Кстати тоже работает, и прямо с  префиксом '0x'. Что не помогает?

Автор: zi4rox 12.5.2009, 20:35
C HexToInt никогда проблем не было, там вот как происходит дело:

С ком-порта считывается символ в формате string. Так вот мне этот символ, что я считал необходимо преобразовать в число(десятиричное/шестнадцетиричное - без разницы.)

Я опробывал конструкции по переводу символа в его числовой код [ф-ции Byte() , ord()], так вот - некоторые символы они переводят, а с некоторыми у них проблема и их попросту зарезает. 

Вот попробую со скриншотами:

Вот как происходит прием и конвертация сейчас:
http://ipicture.ru/

а вот как должно на самом деле(программа Terminal 1.9b): 
user posted image

Как видно - 2 байта почему то сливаются в один, либо происходит неправильная конвертация символ-байт.

Вобщем мне нужно добиться эффекта, как на 2ом скриншоте

Автор: Keeper89 12.5.2009, 20:40
Цитата(zi4rox @  12.5.2009,  20:35 Найти цитируемый пост)
Вот как происходит прием и конвертация сейчас:

Прием каких символом происходит? ASCII или какие?

Автор: zi4rox 12.5.2009, 20:50
В Terminal - точно ASCII, а через компоненту не уверен. Написано что принимает строку и всё, в исходнике компоненты, тоже не нашел ничего путного. 

Если есть разные варианты, для разных форматов символов - скажите пожалуйста все - буду пробывать методом перебора

Автор: zi4rox 12.5.2009, 22:03
Нашел причину сам: 

Всё работает на самом деле, и перекодировка идет нормально - ошибка вот в чем:

Все эти компоненты для работы с ком портом: BCPort, Com Port Library - все они работают жутко медленно (если ставить задержку хотя бы в 1 милисекунду (а это ужасно долго) между отправляемыми данными - они не успевают различать принятые байты и лепят всё в одно) - вобщем в топку их.

Пожалуйста посоветуйте максимально быстрый способ чтения/записи/работы с ком-портом?

Автор: kami 12.5.2009, 22:03
Цитата(zi4rox @  12.5.2009,  19:25 Найти цитируемый пост)
этот компонент, да и другие подобные читают из ком порта именно символы. 

Они читают не символы, а байты.
Что используется в качестве хранилища для принятых байт - строки, буфер памяти, TStream

Поэтому правильный код это:
Код

b:byte;

b:=byte(s[i]);


при условии, что s - это AnsiString.
Но я не вижу смысла доступа отдельно к каждому байту.
Здесь нужно смотреть протокол обмена.

Добавлено через 2 минуты и 18 секунд
Цитата(zi4rox @  12.5.2009,  22:03 Найти цитируемый пост)
они не успевают различать принятые байты и лепят всё в одно

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

Принцип работы программы должен основываться на событиях этих компонентов OnRead.
В этом событии считывать ВСЕ, что пришло из порта.

И потом, в случае необходимости - разделять каждый принятый буфер на байты.
Как - показал в предыдущем посте.

Автор: kami 12.5.2009, 22:37
Цитата(kami @  12.5.2009,  22:03 Найти цитируемый пост)
Что используется в качестве хранилища для принятых байт - строки, буфер памяти, TStream

забыл добавить :
Что используется в качестве хранилища для принятых байт - строки, буфер памяти, TStream - это личные трудности используемого компонента, и ничто не мешает перезагрузить их в любое другое хранилище.

Автор: Christoph 12.5.2009, 22:37
У меня тоже что то похожее...вот у меня есть программатор(почти готовый) написал прогу, можете проверить? с ком портом не работал...может автор выложит свой код, если можно

Код

unit Comport;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, BCPort, StdCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    BComPort1: TBComPort;
    LabeledEdit1: TLabeledEdit;
    Memo1: TMemo;
    Label1: TLabel;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  BComPort1.Open;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  BComPort1.Close;
end;

procedure TForm1.Button3Click(Sender: TObject);
var
  Operation1: PAsync;
  Str: String;
begin
  Str := 'Hello';
  InitAsync(Operation1);
  try
    BComPort1.WriteAsync(Str[1], Length(Str), Operation1);
    BComPort1.WaitForAsync(Operation1);
  finally
    DoneAsync(Operation1);
  end;
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  Buff: array[1..100] of byte;
begin
  BComPort1.Read(Buff,SizeOf(Buff));
end;

end.

Автор: zi4rox 12.5.2009, 22:58
2 kami Спасибо Вам огромное. Ваше объяснение натолкнуло на мысль. Сейчас уже в нужном направлении покопался - пока трудностей не вижу. Ещё раз спасибо Вам.

2Christoph Для своих тестов ком порта, я использовал один из примеров, что шел вместе с BComPort

Автор: dizJis 18.5.2009, 12:03
Ребят, хотите совет? Если хотите научиться работать с com-портом, то забейте на компоненты, юзайте WinAPI, меньше проблем потом у вас будет. Я по началу тоже компоненты юзал, но в итоге отказался от них. Ручками порт проинициализировать, настроить, как тебе надо и работать с ним проще и быстрее

Автор: NewDima 18.5.2009, 12:09
dizJis, ты юзал компоненты, пользовался боле высоким уровнем абстракции, научиться было проше, потом пошел уровнем ниже, сложнее. А остальным предлагаешь сразу на WinAPI писать?

Автор: dizJis 19.5.2009, 10:01
Цитата(NewDima @  18.5.2009,  12:09 Найти цитируемый пост)
ользовался боле высоким уровнем абстракции, научиться было проше, потом пошел уровнем ниже, сложне

как раз таки наоборотsmile В начале я юзал винапи, потом поюзал компоненты, а вот после них понял, что винапи лучшеsmile

Автор: Gres 31.5.2009, 15:26
Приветствую всех, похожая проблема с компонентом ComPort Library, тока версию последнюю поставил 4 бета.
Вывожу данные через СОМ порт на железяку(пик контроллер), все замечательно выводится, НО... компонент выводит данные в ASCII коде...
Посылаю "0", а на железяке получаю "48" , при посылке "1" получаю "49" и т.д.
Программированием СОМ порта не занимался, вроде не сложно все, но не получается.
Подскажите пожалуйста в чем моя ошибка или этот компонент предназначен только для работы в терминале? 

Автор: kami 31.5.2009, 18:27
Цитата(Gres @  31.5.2009,  15:26 Найти цитируемый пост)
Посылаю "0", а на железяке получаю "48" , при посылке "1" получаю "49" и т.д.

Дались вам всем побайтовые посылки...

Посылай не символы, а байты.
Дело в том, что символ "0" ни разу <> байт 0.
Делай так:
Код

var
  byteToSend:byte;
begin
  byteToSend:=0; // или_что_там_нужно_отправить
  myComPort.Send(byteToSend, SizeOf(byte));

или, если уж работаешь с символами, то:
Код

var
  CharToSend:AnsiChar;
begin
  CharToSend:=Chr(0);
  myComPort.Send(CharToSend, SizeOf(AnsiChar));

  

Автор: Gres 1.6.2009, 13:21
Цитата

kami     
Дались вам всем побайтовые посылки...
Посылай не символы, а байты.
Дело в том, что символ "0" ни разу <> байт 0.

Спасибо Вам огромное за подсказку, в чем отличие символа от его кода я знаюsmile
В общем проблема была в компоненте ComPort Library, эта приблуда заточена специально для работы в режиме терминала(просто эмулирует нажатие клавиш), для отправки данных там всего две функции: Write и Writestr, в обоих функциях передаваемый параметр является типом PChar...
В общем его получилось использовать со вторым методом предложенным Вами, но это слишком длинная и бесполезная работа(конвертировать символы туда и обратно).
Пришлось "спрыгнуть с удобного кресла" под названием ComPort Library и вернуться к Win API, там с байтами все нормально работает...

Автор: kami 1.6.2009, 18:10
Цитата(Gres @  1.6.2009,  13:21 Найти цитируемый пост)
Write и Writestr, в обоих функциях передаваемый параметр является типом PChar...

Цитата(Gres @  1.6.2009,  13:21 Найти цитируемый пост)
Пришлось "спрыгнуть с удобного кресла" под названием ComPort Library

Вы просто его готовить не умеете. smile 

Хотя может и ошибаюсь - не пользовался этой библиотекой. Нужно смотреть на объявление функций Write и WriteStr.

Автор: Gres 5.6.2009, 07:48
Цитата(kami @  1.6.2009,  18:10 Найти цитируемый пост)
Хотя может и ошибаюсь - не пользовался этой библиотекой. Нужно смотреть на объявление функций Write и WriteStr.


Прочитав Ваше сообщение решил проверить исходники этого компонента, хотя неблагодарное это дело перелапачивать горы исходного кода ради одной функции которую можно гораздо проще реализовать на Win API. 
Можно и свой компонент накидать, но для меня это разовая работа и пока в нем нет нужды, а скопировать кусок кода из своих же исходников при необходимости я всегда смогуsmile

Вот то, что я нашел в том компоненте:


Код

 { The main TComPort component is defined in this unit. }
unit CPort;
{$I CPort.inc}
{$R-}
{$Q-}

interface

uses
  Windows, Messages, Classes, SysUtils, IniFiles, Registry, CPortTypes;

type

...(дальнейший код)
function TCustomComPort.Write(const Buffer: TCPortBytes;
  Count: Integer): Integer;
begin
 if FOverlapped then begin
    result := _WriteAsyncWrapper(Buffer,Count); // Do a synchronous write using overlapped APIs.
 end else begin
   result := 0;
   if _SyncWrite(Buffer,Count) then
      result := Count;
 end;
end;



Далее кусок из другого паса объявленного в этом:

Код

unit CPortTypes;
(******************************************************
 * ComPort Library ver. 4.0                           *
 *   for Delphi  7, 2007, 2009                        *
 * written by Dejan Crnila, 1998 - 2002               *
 * maintained by Lars B. Dybdahl, 2003                *
 *  and Warren Postma, 2008                           *
 *                                                    *
 * Fixed up for Delphi 2009 by W.Postma.  Oct 2008    *
 * More like completely rewritten, actually.          *
 *****************************************************)

 { Data Type Declarations used in various CPort code units }

interface

uses   Windows, Messages, Classes, SysUtils, IniFiles, Registry;

type

// this is the type Warren changed most of the untyped-parameters used
// in CPort.pas and CPortCtl.pas to:
  TCPortBytes = PAnsiChar;

  // various types formerly in CPort.pas:
  TCPortChar = AnsiChar;
  TPort = string;



Как видно из всего этого, данный компонент не пригоден для моих целей, его конечно можно подправить, но не стоит тратить на это время если есть другой способ.

Все равно спасибо Вам большое за помощь, я ни когда не работал с СОМ портом, это первый опыт, да и задача не совсем стандартная или вернее совсем не стандартная, научить железяку(пик контролер, в который тоже ваяю код) общаться с большим братомsmile


M
THandle
Модератор: используйте подсветку кода!


Автор: kami 5.6.2009, 13:18
Цитата(Gres @  5.6.2009,  07:48 Найти цитируемый пост)
Как видно из всего этого, данный компонент не пригоден для моих целей

ошибочное мнение.

Автор: Gres 5.6.2009, 14:40
Цитата(kami @  5.6.2009,  13:18 Найти цитируемый пост)
ошибочное мнение.

Ошибочное в чем? Поясните пожалуйста.
Насколько я понял из исходников данная функция позволяет передавать только символ типа PAnsiChar или родственные ему.
Мне нужно передать один байт(десятичное число от 0 до 255) и принять такой же ответ.

Цитата(kami @  31.5.2009,  18:27 Найти цитируемый пост)
Дело в том, что символ "0" ни разу <> байт 0.

Эта функция не позволяет записать байт в порт, она передает значение(числовое значение или код) символа или я ошибаюсь?
Поясните в чем.

Модераторам: спасибо за поправку, впредь буду учитывать.

Автор: kami 5.6.2009, 17:58
Цитата(Gres @  5.6.2009,  14:40 Найти цитируемый пост)
Эта функция не позволяет записать байт в порт

Позволяет.

Код

var
  b:byte;
begin
  b:=254; // или $FE
  myComPort.Write(@b, Length(byte));



Автор: kami 5.6.2009, 23:36
ай-я...
не заметил const в описании Write...
тогда так:

Код

var
  b:PAnsiChar;// или TCPortBytes, что одно и то же...
begin
  b:=AllocMem(SizeOf(byte));
  b[1]:=#0;
  Send(b, SizeOf(byte));

Автор: Gres 6.6.2009, 07:12
Цитата(kami @  5.6.2009,  23:36 Найти цитируемый пост)
не заметил const в описании Write...
тогда так:

Это практически тоже самое, что и во втором примере в верхнем посте на этой странице, как я и сказал оно работает нормально. Но для меня это лишнее преобразование, которое вызовет кучу перекодировки после приема...
Просто компонент видимо еще сыроват, там есть функции внутри которые не доступны пользователю, но были бы очень интересны. Например, что мешает константу заменить на переменную и вывести ее наружу для смены типа посылаемых данных одним кликом...
Изначально компонент использует вызовы все того же win api, просто как и все компоненты предназначен для более удобного использования и уменьшения рукописного труда разработчиков, тока заточен он изначально под терминальные (АТ) команды...

Автор: Yump 21.7.2009, 23:23
Подскажите, плз.
Перешел на TComPort4Beta (Delphi2009) с CPortLib3.1.(Delphi2006)

при асинхронном чтении из компорта в версии 3.1 можно было использовать любой тип в качестве буфера и я использовал такую конструкцию:

Код

...
procedure TForm1.ComPort1RxFlag(Sender: TObject);
var
  ....
  Operation1: PAsync;
  hex_ar: array [1..5] of byte;
  ...
begin
  InitAsync(Operation1);
  try
    ComPort1.ReadAsync(hex_ar, 5, Operation1);
  ....


и дальше уже работаю с элементами массива...

в 4-ой версии в качестве буфера можно использовать только TCPortBytes (PAnsiChar) и что-то я туплю, как с этим работать:

Код

procedure TForm1.ComPort1RxFlag(Sender: TObject);
var
  Operation1: PCPortAsync;
  ???
begin
  CportInitAsync(Operation1);
  ...
  try
    ...
    ComPort1.ReadAsync(...,5,Operation1);


может кто-нибудь подсказать, что тут делать?

Автор: alex003 20.2.2010, 20:24
  Доброе время суток. Подскажите пожалуйста
 Надо получить архивы с прибора. Подключаюсь через мою термин. программу к прибору и получаю с СОМ порта  следующее:

 ---/00016661<0
 >6==;83<<49;:8
 3><0;1>?4388><
 2:000000003;


 Это  ответ прибора на запрос из программы (здесь данные о температуре, расходе, давлении, перепаде).
Как эту последовательность преобразовать в читабельный формат. 
С уважением Александр.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)