Введение.
Автоматизация различных систем с помощью компьютера меня интересовала всегда. Но
когда я начал заниматься этой задачей, то столкнулся с множеством проблем. Одна
из главных проблем это литература, в которой в доступной для меня форме был бы
освещен данный вопрос. Но литературы по данной теме очень мало, особенно в нашем
небольшом городке. Взять, например книгу в магазине за 300 руб. в которой
уделяется искомому вопросу 2-3 страницы неинтересно, а покупать 2-3 книги
дорого. Вы скажете "Сходи в библиотеку и нет проблем", о библиотеке я тоже
думал. Но и там проблема с книгами стоит остро. Денег на новые книги у них нет,
так как книги по компьютерной тематике в основном печатаются в коммерческих
типографиях и поэтому стоят дорого.
А тот мизер который выделяет государство на покупку книг настолько мал что
его хватает только на содержание старых наиболее читаемых произведений. И тогда
я решил поискать в интернете. И он меня не разочаровал. В первый же час поиска я
нашел много интересного. В основном это статьи людей занимающиеся аналогичным
вопросом . Они делятся своим опытом с начинающими и в примерах показывают, как
реализовать ту или иную задачу.
Данная статья была задумана для объединения в себя всю ту информацию, которую я
почерпнул в ходе своего изыскания в интернете.
История стандарта rs-232.
В 1969 г. Группой ведущих промышленных корпораций США был введен стандарт на
соединение оборудования. Ассоциация электронной промышленности США (eia)
опубликовала вариант С своего рекомендуемого стандарта (recommended standart -
rs) номер 232. Этот стандарт был озаглавлен "Интерфейс между оконечным
оборудованием обработки данных и оконечным оборудованием линии с использованием
последовательного обмена данными в двоичной форме" и известен просто как
стандарт rs-232c. МККТТ ввел свой собственный вариант этого стандарта в виде
стандартов v.24 и v.28.
Министерство обороны США выпустило практически идентичный стандарт mil-std-188c.
Хотя стандарт rs-232c был весьма популярен, определяемый им физический интерфейс
далек от совершенства. Система передачи данных (передатчик, приемник,
соединительные кабеля), реализованная в соответствии с техническими условиями
стандарта rs-232c, должна гарантированно обеспечивать передачу сигнала со
скоростями, не превышающими всего лишь 20 Кбит/с . Ассоциация электронной
промышленности США ввела рекомендуемые стандарты для систем, работающих при
больших скоростях, но стандарт rs-232c продолжает оставаться основной реализации
последовательного интерфейса для ibm-совместимых персональных компьютеров.
Модификация d этого стандарта была введена в 1987 г. В ней были определены
некоторые дополнительные линии тестирования, а также закреплено то, что многие
рассматривали как недостаток стандарта rs-232c.
Самой последней (июль 1991 г.) модификацией стандарта rs-232 является стандарт
eia/tia-232e. В модификации Е нет никаких технических изменений, которые могли
бы привести к проблемам совместимости с оборудованием, согласованным с
предыдущими вариантами этого стандарта.
Проблема.
Под ms-dos приложение управляет всем компьютером. Это развязывало программисту
руки. Достижение максимальной скорости работы осуществлялось непосредственным
доступом к аппаратным средствам.
Под windows 3.x эта свобода отчасти была ограничена. К примеру вы уже не имели
полный доступ к экрану. Проблема объясняется легко: с тех пор, как пользователь
мог запускать любое количество приложений, не было никакой гарантии, что
приложения не получали одновременно те же самые аппаратные средства.
Другая проблема - вы уже должны были считаться с параллельно запущенными
задачами, а не требовать у компьютера в свое распоряжение все ресурсы. win 3.x
осуществляет кооперацию параллельных задач, означая, что каждое приложение
должно исходить из концепции совместного существования и не монополизировать
ресурсы, а пользоваться услугами специализированного диспетчера. Захват cpu на
длительное время здесь не приветствуется.
Но тем не менее монополизированный доступ к аппаратным средствам также возможен,
но вся ответственность за работу других приложений ложится на программиста.
Получается борьба вашего приложения с системой: если вы захватываете все рабочее
время cpu, контроль над портами или работу с памятью, то система милостиво ждет,
пока вы не отдадите бразды правления в ее руки, при этом другие приложения (если
они не успели это сделать до вас) могут ругаться, выплевывать на экран грязные
ругательства и пугать не в чем не повинного пользователя.
Факт, но тенденция отбивания рук от прямого доступа к железу победила на
платформе win32 (windows nt и windows 95). Это операционные системы с истинной
многозадачностью. Каждый поток (выполняемый модуль) получает определенный квант
процессорного времени. Когда лимит процессорного времени исчерпан, или
появляется поток с более высоким приоритетом, система прекращает обслуживать
первый поток, даже в случае, если он не завершен. Это переключение между
потоками может произойти между двумя ассемблерными инструкциями, нет никакой
гарантии, что поток сможет завершить определенное количество инструкций, прежде
чем у него отнимут процессорное время, к тому же неизвестно как долго ждать
следующей порции процессорного времени. Это приводит к проблеме с прямым
доступом к аппаратным средствам. Например, типичное чтение из порта формируется
из нескольких ассемблерных инструкций:
mov dx, addressport
mov al, address
out dx, al
jmp wait
wait:
mov dx, dataport
in al, dx
Состояние всех регистров при переключении потоков сохраняется, состояние i/o
портов (последовательные порты, порты ввода/вывода) - нет. Так, велика
вероятность что другие приложения производят другие операции с i/o портом, в то
время как вы "застряли" между инструкциями 'out' и 'in'.
Документированный путь.
Для решения этой проблемы мы должны как-то сообщить всем другим приложениям, что
"К настоящему времени myprog использует порт 546, и всем оставаться на своих
местах до моего особого распоряжения." В этом случае подошел бы мьютекс. К
сожалению, для использования созданного мьютекса все приложения должны знать его
имя. Но даже если бы это было возможно, вы легко можете наткнуться на другие
заковыристые проблемы. Рассмотрим два приложения - app1 и app2. Оба пытаются
выполнить вышеприведенный код. К несчастью, они созданы разными программистами с
разным взглядом на технологию доступа, поэтому app1 сначала требует
addressportmutex, в то время как app2 требует dataportmutex. И, по печальному
совпадению, когда app1 получает addressportmutex, система переключается на app2,
которое захватывает dataportmutex и получается праздник смертельного объятия.
app2 не может получить адрес порта, т.к. его захватило app1. app1 не может
получить данные порта, т.к. это захватило app2. И все чего-то ждут...
Правильное решение - создание драйвера устройства, которой единолично владеет
портами/памятью. Доступ к аппаратным средствам осуществляется посредством api.
Вот типичный вызов:
getioportdata(addressport, dataport : word) : byte;
getioportdata сначала создает мьютекс, который защищает от вторжения (возможно
все) порты, затем дает доступ к портам и, наконец, уничтожает его перед
возвратом в вызвавшему функцию оператору. В случае, когда функцию пытаются
вызвать несколько потоков, управление получает только один, остальные в это
время ждут.
Создание драйвера устройства дело нелегкое. Он должен быть создать с помощью
ассемблера или c и невероятно труден в отладке. Более того, из-за соображений
безопасности драйверы устройств для windows 95 (vxd) не совместимы с драйверами
для windows nt (vdd, virtual device driver - виртуальный драйвер устройства).
Говорят, что в будущих версиях они будут совместимы, и windows nt 6.0 и windows
2000 будут использовать одни и те же драйвера, но пока разработчики вынуждены
заниматься созданием двух различных версий.
Для получения более подробной информации рекомендую обратиться к следующим
ресурсам:
microsoft windows 95 device driver kit
microsoft windows nt device driver kit
microsoft press "systems programming for windows 95" автора walter oney
Также вы можете ознакомиться с библиотекой vireo vtoolsd на предмет написания
vxd в c, расположенной по адресу
Не документированный путь.
Вышеуказанная проблема не слишком реальна. Приложение, которое имеет
непосредственный доступ к аппаратным средствам, обычно использует некоторые
специализированные аппаратные средства. Конфигурация типа той, которая
стремиться запустить только одно приложение имеет единственную цель - получить
монопольный доступ к этим аппаратным средствам. В этом случае создание драйверов
устройств очень нерентабельно. В конце концов, причина хотя бы в том, что это
работает под windows, что можно получить свободно (почти) классный gui, а не в
том, чтобы 10 приложений работало одновременно.
К счастью, в windows 95 заложена совместимость с windows 3.x. Это означает, что
директивное использование i/o портов также возможно, поскольку до сих пор
находятся в эксплуатации множество 16-битных программ, которые просто не могут
работать по другому. Просто в этом случае при кодировании вам придется
спуститься до уровня ассемблера. Автор следующего кода arthur hoornweg (hoornweg@hannover.sgh-net.de
):
//Базовые адреса двух com портов, для справки:
com1 - 3f8h
com2 - 2f8h
function getport(p:word):byte; stdcall;
begin
asm
push edx
push eax
mov dx,p
in al,dx
mov @result,al
pop eax
pop edx
end;
end;
procedure setport(p:word;b:byte);stdcall;
begin
asm
push edx
push eax
mov dx,p
mov al,b
out dx,al
pop eax
pop edx
end;
end;
francois piette также предлагает свое
решение прямого доступа к портам i/o на страничке
.
Как насчет nt?
Но все вышесказанное под windows nt работать не будет. nt более "прочная"
операционная система, поэтому если она позволит в любое время кому попало
обращаться к любым аппаратным средствам, она не была бы такой устойчивой. Кроме
того, nt является кроссплатформенной системой, поэтому доступ к i/o портам может
кардинально различаться при работе на различных процессорах.
Но тем не менее даже под nt можно добраться непосредственно до i/o портов,
правда только на x86 процессорах. Это не является документированной
особенностью, и, вероятно, исчезнет в будущих версиях этой операционной системы.
Я не обладаю достаточно полной информацией по этому вопросу, но интересующая нас
статья d. roberts в майском номере журнала dr. dobb's journal за 1996 год так и
называется "direct port i/o and windows nt." К сожалению, я так и не нашел
времени проверить приведенный там код. Статью и посвященный ей флейм вы можете
почитать по адресу.
Также рекомендую ознакомиться с опубликованной в windows developer journal
статьей "port i/o under windows." Опубликована karen hazzah в июне 1996 года.
Статью и посвященный ей флейм вы можете найти по адресу.
Визуальный компонент comm32.
Вы спросите "Все это хорошо. Но есть ли визуальный компонент сторонних фирм,
работающих с com портом?". Да есть. И он называется comm32. На мой взгляд, он
один из лучших на сегодняшний день. Чтобы вам было легче с ним разобраться я
приведу пример, реализации данного компонента.
Программа называется psion. Она задумывалась для тестирования теплосчетчиков
clorius.
В первый edit программы мы вводим сетевой адрес теплосчетчика. По умолчанию он
равен 0. С помощью второго мы посылаем команды теплосчетчику. Третий edit служит
для вывода информации, которую теплосчетчик посылает нам.
Вот исходный текст программы написанной на delphi5:
type txxxx=array[1..255] of char; //Определяем символьный массив pxxxx=^txxxx;
//Функция отвечающая за подсчет контрольной суммы
function tform1.checksum(astr: string): char;
var crc,i: integer; //Вводим свои целочисленные переменные
begin
crc:=0;
for i := 1 to length(astr) do
crc:=crc+ord(astr[i]);
crc:=(crc and $3f) + $30;
result:=chr(crc);
end;
//Функция сравнивания контрольной суммы с полученными данными
function tform1.comparechecksum(astr: string; cs: char): boolean;
begin
result:=checksum(astr)=cs;
end;
//Возвращает тело пакета без сетевого адреса и контрольной суммы
function tform1.getinput: string;
var l:integer;
begin
result:='';
l:=length(finput);
if inputstate = 1 then
begin
if starttime+3000 < gettickcount then inputstate := 2;
exit;
end;
if l<3 then exit;
if comparechecksum(copy(finput,1,l-2),copy(finput,l-1,1)[1])=true then
begin
inputstate := 0;
netnumber:=finput[1];
addredt.text:=netnumber;
result:=copy(finput,2,l-3);
end
else
inputstate := 3;
end;
//Данная процедура возникает, когда мы пытаемся послать команду
//устройству
procedure tform1.setoutput(const value: string);
var xxxx:txxxx;
s:string;
l,i:integer;
begin
s:=netnumber+value;
s:=s+checksum(s)+#13;
l:=length(s);
if l>255 then exit;
for i:=1 to l do xxxx[i] := s[i];
inputstate := 1;
finput:='';
commportdriver1.senddata(@xxxx,l);
starttime:=gettickcount;
end;
//Процедура возникает при запуске программы
procedure tform1.formcreate(sender: tobject);
begin
netnumber:='0';
commportdriver1.connect;
end;
//Процедура возникает при выходе из программы
procedure tform1.formdestroy(sender: tobject);
begin
commportdriver1.disconnect;
end;
//Процедура возникает при ответе устройства
procedure tform1.commportdriver1receivedata(sender: tobject; dataptr: pointer; datasize: integer);
var px:pxxxx;
i:integer;
begin
inputstate := 4;
application.processmessages; finput:='';
px:=dataptr;
for i := 1 to datasize do
begin
finput:=finput+px^[i];
end;
inputstate := 5;
application.processmessages;
edit2.text:=input;
end;
//Процедура возникает при подборе визуального состояния программы
procedure tform1.setinputstate(const value: integer);
begin
finputstate := value;
case value of
0: caption:='Данные успешно приняты';
1: caption:='Ждем ответа';
2: caption:='Таймаут';
3: caption:='Пакет принят с ошибкой';
4: caption:='Принимаем ответ';
5: caption:='Ответ получен';
end;
end;
//Процедура возникает при нажатии клавиши "Отправить"
procedure tform1.sendbtnclick(sender: tobject);
begin
output:=outputedt.text;
sendbtn.enabled:=false;
repeat
edit2.text:=input;
until inputstate<>1;
sendbtn.enabled:=true;
end;
//Процедура возникает при изменении сетевого адреса устройства
procedure tform1.addredtchange(sender: tobject);
begin
netnumber:=addredt.text[1];
end;
Если возникнут, какие либо вопросы или вы встретите какие
либо технические неувязки в данной статье, вы можете мне написать по e-mail.
Автор статьи:
Фофанов Дмитрий
|