понедельник, 6 июля 2015 г.

COM порт. Синхронный обмен данными

    В этой статье мы рассмотрим синхронный обмен данными (чтение/запись) через СОМ порт, при помощи API функций. Для этого нам понадобятся, руки, голова, нуль-модемный кабель (DB9), и не большой кусочек провода, диаметром примерно 0,5 – 0,8 мм.
Создадим новый проект в Delphi и поместим на форму проекта 1 компонент Memo и 4 кнопки.
В свойствах Caption кнопок пишем:

1) Открыть порт      
2) Отправить данные;
3) Принять данные
4) Закрыть порт;

Поле компонента Memo очищаем от надписи и делаем его пустым.


1)    Для того чтобы начать работу с COM, его нужно для начала инициализировать и должным образом настроить. Для этого создадим 2 глобальные переменные:

var

Form1: TForm1;

ComFile:THandle;
DCB:TDCB;

И в событии OnClick первой кнопки, что отвечает у нас за открытие файла, записываем:

procedure TForm1.Button1Click(Sender: TObject);

begin

ComFile:=CreateFile('COM1', generic_read+generic_write,0,nil,OPEN_EXISTING,FILE_ATTRIBUTE_READONLY,0);
if ComFile=INVALID_HANDLE_VALUE  then
begin
ShowMessage('Не удалось открыть порт ');
exit;
end;

SetupComm(ComFile, 4096, 4096);

GetCommState(ComFile, DCB);
with DCB do begin
BaudRate:=9600;
ByteSize:=8;
Parity:=NoParity;
StopBits:=OneStopBit;
end;

if not SetCommState(ComFile, DCB) then

begin
ShowMessage('Порт не настроен');
CloseHandle(ComFile);
exit;

end;
end;

Тут в первой строчке кода у меня, установлен порт COM1, номера портов на разных конфигурациях или компьютерах, могут отличаться. Поэтому зайдите Мой компьютер > Свойства > Диспетчер устройств,


посмотрите номер своего порта и если он отличается от моего замените COM1 на номер своего порта. Или можете адаптировать пример из моей статьи "Как получить список доступных COM портов"

2) Для чтения и записи данных в порт, в разделе private записываем  функции:

private

function WritePort(var Buf; Size: Word): Integer;
function ReadPort(var Buf; Size: Word): Integer;

выделяем  function WritePort(var Buf; Size: Word): Integer;  жмем Ctrl+Shift+C и в появившемся блоке описываем ее следующим образом:

function TForm1.WritePort(var Buf; Size: Word): Integer;
var
p: Pointer;
i: Cardinal;
begin
p:=@Buf;
Result:= 0;
while Size > 0 do
begin
if not WriteFile(ComFile, p^, 1, i, nil) then
exit;
Inc(Result, i);
Inc(Integer(p));
Dec(Size);
Application.ProcessMessages;
end;
end;

с функцией ReadPort(var Buf; Size: Word): Integer; проделываем, тоже самое, только описываем ее так:

function TForm1.ReadPort(var Buf; Size: Word): Integer;
var i: Cardinal;
ovr: TOverlapped;
begin
FillChar(Buf, Size, 0);
FillChar(ovr, SizeOf(ovr), 0);
i := 0;
Result := -1;
if not ReadFile(Comfile, Buf, Size, i, @ovr) then exit;
Result := i;
end;

3) В событии OnClick – второй кнопки (отправить данные) записываем:
var

OutputBuffer: array [0..15] of char;  // выходной буфер
ErrorCode: integer;  // переменная для приёма кода ошибки

begin

OutputBuffer[0]:='П'; //Данные
OutputBuffer[1]:='р';
OutputBuffer[2]:='и';
OutputBuffer[3]:='в';
OutputBuffer[4]:='е';
OutputBuffer[5]:='т';
OutputBuffer[6]:='и';
OutputBuffer[7]:='к';

ErrorCode:=Writeport(OutputBuffer, Length(OutputBuffer));  // передача пакета

if ErrorCode > 0 then MessageDlg('Пакет передан', MtConfirmation, [mbOK], 0)
else MessageDlg('Ошибка передачи пакета', MtWarning, [mbOK], 0);

end;

Тут мы побуквенно, с помощью массива, передаем в порт, слово - "Приветик" ну и разумеется при приеме, оно должно будет отобразиться в нашем Memo.

3) В событии OnClick – третьей кнопки (принять данные) пишем:

var
InputBuffer: array [0..255] of char;  // приёмный буфер
ErrorCode: integer;

begin
ErrorCode:= ReadPort(InputBuffer, Length(InputBuffer));  // приём пакета
if ErrorCode = -1 then MessageDlg('Ошибка приёма пакета', MtWarning, [mbOK], 0)
else
begin
Form1.Memo1.Lines.Add(InputBuffer);
MessageDlg('Порт прочитан', MtConfirmation, [mbOK], 0);
end;
end;

4) В событии OnClick – четвертой кнопки (закрыть порт), а также в OnClose главной формы  напишем такой код:

procedure TForm1.Button4Click(Sender: TObject);

begin
CloseHandle(ComFile); //Закрытие порта;
end;

  С программной частью покончено! Переходим к железу. Подключаем один конец нуль-модемного кабеля к COM порту нашего компьютера. На другом конце разъема, находим номера контактов 2 и 3. И замыкаем их заранее приготовленным для этого проводком.


   Если этого не сделать, то ничего не получится, потому как отправляются данные - 5 (Gnd) + 3 (Td) контакт, а принимаются - 5 (Gnd) + 2 (Rd) контакт. То-есть, 3 контакт разъема передающего устройства, соединяется со вторым контактом принимающего, а постольку по скольку обе машины могут как принимать, так и передавать сигналы, то распайка нуль-модемного кабеля выглядит следующим образом:


   На этой схеме, приведена не полная распайка нуль-модемного кабеля, но для простого обмена данными достаточно и этого. По крайней мере, понятно отличие COM кабеля нуль-модемного, от обычного удлиняющего кабеля.
   Поэтому, то чтобы посылка прошла, и чтобы мы могли проверить пример написанной нами программы используя только один компьютер, контакты 2 и 3 необходимо замкнуть. Или если у вас есть 2 ПК с COM портами, можете соединить их этим кабелем. Программку с первого компьютера копируете на второй. Запускаете её и там, и там. И теперь, то что будет отправлено с первого компьютера, будет отображаться на втором и наоборот). Программа 100% рабочая, исходник выкладывать не буду, если кому понадобится, отпишитесь в комментариях.

14 комментариев:

  1. Наконец, то появилась возможность кометны без регистрации оставлять. заработоало, со второго раза)), думал фигня, спасибо доступно все разжевано спасибо...

    ОтветитьУдалить
  2. Класс, то че искал), спс А не могли бы Вы рассмотреть работу с Com в асинхронном режиме? Наверное многим было бы интересно.

    ОтветитьУдалить
  3. Не простая тема для меня, если честно. Подумаю на досуге). Есть в принципе задумки на тему COM порта...

    ОтветитьУдалить
  4. Всем привет! Спасибо за инфу. Попробовал все работает. Поддерживаю (Serenik) было б интересно, хотя б чтоб работало сделай пожалуста про ассинхронную работу

    ОтветитьУдалить
  5. Спасибо! И мне интересно, не плохо было бы как в вашей статье http://delartidea.blogspot.com/2015/01/com.html только при помощи API
    функций. В инете много всего про COM порты, но новичку сложно разобраться, все разрозненно как то, хрен в кучу соберешь.

    ОтветитьУдалить
    Ответы
    1. http://delartidea.blogspot.com/2018/06/com-rs-232-4.html Вот здесь про асинхронное чтение порта, отправку данных надеюсь сами осилите. Чуть позже планирую об этом отдельную статью написать, только когда это будет пока не знаю).

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

    ОтветитьУдалить
  7. здравствуйте, а можете показать пример снифера com-порта?

    ОтветитьУдалить
    Ответы
    1. Доброго времени суток! К сожалению, пока такими знаниями не обладаю)

      Удалить
  8. почему то при нажатии кнопки принять программа зависает? В чем может быть проблема?

    ОтветитьУдалить
    Ответы
    1. Попробуйте повторно и более внимательно все выполнить. Код абсолютно рабочий и пример работает не только у меня...

      Удалить

Примечание. Отправлять комментарии могут только участники этого блога.