Обмен данными через COM-порт в Windows

Разработчики высокоуровневых языков программирования, очевидно, считают прием/передачу данных по протоколу rs232 через коммуникационный порт экзотической процедурой: мол, рядовому пользователю как бы и без надобности, а нерядовой - разберется самостоятельно. Потому ни в turbo pascal, ни в delphi нет штатных средств обмена данными таким способом. Однако в последнее время, особенно в связи с распространением микропроцессорных устройств, такая задача встает в любительских программах все чаще - в силу простоты и дешевизны реализации последовательным портом оборудованы многие научные и инженерные приборы, разнообразные датчики и измерители.

Простота аппаратного исполнения (для обычной двусторонней связи требуется всего три провода) асинхронного коммуникационного порта ведет, однако, к некоторому усложнению необходимого программного обеспечения. Можно, конечно, попробовать послать байт на устройство com1 средствами dos, подобно тому, как это делается для lpt1 (оно же prn), но успех вряд будет достигнут - как минимум, надо сначала настроить скорость обмена. Потому для dos-программ это делается средствами bios или прямым программированием порта "по железу". А в windows, к счастью, есть соответствующие функции api.

Для организации обмена нужно проделать следующие шаги:

получить дескриптор порта (handle - указатель, куда посылать все относящееся к порту);
получить адрес dcb - data control block;
установить новые параметры dcb;
послать установленные параметры в порт;
приступить к чтению принимаемых данных или к передаче.
Рассмотрим все по порядку. Получить дескриптор можно с помощью универсальной функции createfile. Так как СОМ - устройство последовательное, то его можно рассматривать как файл, что и делается (и в dos, между прочим, тоже). У этой функции множество применений (описание в help, если распечатать, занимает страниц семь). Для нее требуется масса входных параметров, но большинство из них нам не нужны и приравниваются, в зависимости от типа, либо к 0, либо к величине nil (указатели, которые никуда не указывают). В нашем случае получается следующий синтаксис функции:

createfile (stcom, generic_read+generic_write, 0, nil, open_existing, 0, 0);

Здесь stcom - строка типа pchar, в которой записано имя файла, в данном случае - просто 'com1' или 'com2', смотря какой порт нужен. Параметр generic_read+generic_write означает, что порт открывается как для вывода, так и для ввода. open_existing означает проверку существования, и если объявленного порта нет, после вызова функции возникнет ошибка, которую можно проанализировать обычным методом delphi: try ... except.

Получить адрес dcb можно, если применить функцию getcommstate (pcom, pdcb); здесь pcom - дескриптор порта, pdcb - возвращаемый адрес структуры dcb. Установить параметры порта в этой структуре можно непосредственно, но если вы обратитесь к ее описанию в help, то бессонная ночь вам обеспечена. Поэтому проще сделать это с помощью вызова функции buildcommdcb (stcom, pdcb), в которой stcom в данном случае содержит набор устанавливаемых параметров в виде строки (см. пример ниже). Установленные параметры посылаются в порт с помощью функции setcommstate (pcom, pdcb). Все эти функции возвращают значение типа boolean, которое равно true, если операция прошла успешно. Чтение из порта и запись в порт осуществляются с помощью симметричных функций readfile и writefile, также универсальных и потому содержащих ненужный нам параметр, который мы приравняем к nil:

boolean readfile (pcom, xb, 1, xn, nil);

Здесь xb - переменная любого целого типа, в которой возвращается прочитанное значение (для writefile в ней содержится, наоборот, записываемое), 1 - столько байт прочесть из xb (для СОМ-порта это, очевидно, всегда 1), xn - сколько байт действительно прочитано. Следующий программный фрагмент инициализирует порт СОМ1 и осуществляет прием данных до тех пор, пока не будет нажата любая клавиша на клавиатуре. Принимаемые данные преобразуются в строку и выводятся на экран через пробел в компоненте label1 (с переводом строки через каждые 32 значения; разумеется, можно выводить в любой компонент, имеющий свойство text или caption, или еще куда-нибудь):

stcom:='com1';
try
pcom:= createfile (stcom, generic_read+generic_write, 0, nil, open_existing, 0, 0);
except ;
if getcommstate(pcom,pdcb)
then stcom:='com1: baud=9600 parity=n data=8 stop=1'
else ;
if buildcommdcb(stcom,pdcb) then setcommstate(pcom,pdcb)
else ;
while (msg.message <> wm_keydown) do
begin
bb:=readfile(pcom,xb,1,xn,nil);
if not bb then break;
st:=st+' '+inttostr(xb);
if length(st) mod 32=0
then st:=st+chr(10)+chr(13);
label1.caption:=st;
peekmessage(msg,form1.handle,0, 0,pm_remove); {читаем сообщение из очереди, если есть - удаляем}
application.processmessages; {очищаем очередь сообщений - на всякий случай}
end;

Прежде чем подключать к компьютеру какие-либо устройства, следует сказать пару слов о технике безопасности. Если прибор беспаспортный или, тем более, самодельный, следует проверить следующее: а) напряжение на выходе порта прибора не должно превышать значений +/-15 В (минимум +/-3 В), и на экране осциллографа не должно наблюдаться заметных выбросов и "шпилек", превышающих эти значения, - в противном случае вы рискуете лишиться com-порта (это особенно неприятно, если порт расположен на материнской плате; в сомнительных случаях лучше экспериментировать с дополнительной isa-картой, оборудованной com-портами; б) нужно убедиться, что выход прибора электрически развязан с сетью.

Автор: Юрий Ревич
журнал "Компьютерра"
http://offline.computerra.ru/



Опубликовал admin
21 Май, Понедельник 2007г.



Программирование для чайников.