Последовательный порт RS-232.
Последовательный порт RS-232.
Последовательный порт RS-232. Введение. Автоматизация различных систем с помощью компьютера меня интересовала всегда. Но когда я начал заниматься этой задачей, то столкнулся с множеством проблем. Одна из главных проблем это литература, в которой в доступной для меня форме был бы освещен данный вопрос. Но литературы по данной теме очень мало, особенно в нашем небольшом городке. Взять, например книгу в магазине за 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;