Журнал LinuxFormat - перейти на главную

LXF134:Умный дом

Материал из Linuxformat
Перейти к: навигация, поиск

Содержание

Про­грам­ми­ру­ем пе­ри­фе­рию

При­ня­то го­во­рить, что Linux уме­ет все, кро­ме вар­ки ко­фе – но это не­прав­да. Ан­д­рей Бо­ров­ский не бу­дет пы­тать­ся вос­про­из­ве­сти ста­рое HOWTO на эту те­му, но пред­ста­вит вам ин­фор­ма­цию, по ко­то­рой вы смо­же­те на­пи­сать его са­ми.

Лет 50 на­зад в мо­де бы­ли про­гно­зы от­но­си­тель­но то­го, как бу­дет вы­гля­деть по­все­днев­ная жизнь в XXI ве­ке. Чи­тать их в на­ше вре­мя и за­бав­но, и ин­те­рес­но, и по­учи­тель­но од­но­вре­мен­но. Са­ми же фу­ту­ро­ло­ги про­шло­го, гля­дя на на­ше вре­мя, неиз­беж­но ис­пы­та­ли бы до­са­ду. И де­ло не толь­ко в том, что мы до сих пор не жи­вем до двух­сот лет и не ле­та­ем на Лу­ну по пу­тев­кам. По­смот­ри­те хо­тя бы на то, как мы ис­поль­зу­ем ком­пь­ю­те­ры! Вме­сто то­го, что­бы со­чи­нять му­зы­ку и про­кла­ды­вать мар­шру­ты для звез­до­ле­тов, мощней­шие вы­чис­ли­тель­ные сред­ства ис­поль­зу­ют­ся для об­ме­на кол­ко­стя­ми в Жи­вом жур­на­ле и про­смот­ра ви­део со­мнитель­но­го со­дер­жания. В этой ста­тье я пред­ла­гаю дру­гое при­менение этих мощ­но­стей – в ка­че­стве элек­трон­но­го моз­га для управ­ления уст­рой­ства­ми на­ше­го до­ма. Хо­ро­шем моз­гу нуж­ны хо­ро­шие муску­лы (или хо­тя бы пе­ри­фе­ри­че­ские нерв­ные окон­чания), так что мы со­сре­до­то­чим­ся на про­бле­мах со­пря­жения до­машнего ком­пь­ю­те­ра с внешними уст­рой­ства­ми.


Уст­рой­ства, ко­то­рые мы рас­смот­рим, от­ли­ча­ют­ся про­сто­той. Де­та­ли, необ­хо­ди­мые для их сбор­ки, най­дут­ся в ар­се­на­ле лю­бо­го за­яд­ло­го ком­пь­ю­тер­щи­ка. Ес­ли вы не лю­би­те па­ять – не бе­да, обой­дем­ся и без пай­ки. Та­кой под­ход име­ет обо­рот­ную сто­ро­ну: ис­поль­зуе­мые на­ми ре­шения не яв­ля­ют­ся про­фес­сио­наль­ны­ми и, ско­рее все­го, не по­дой­дут для уст­ройств, имею­щих хо­тя бы от­да­лен­ное про­мыш­лен­ное на­зна­чение. Стро­го го­во­ря, это имен­но то, что на­зы­ва­ет­ся сло­вом «hack». Ис­поль­зуе­мые мной под­хо­ды не пред­став­ля­ют ниче­го су­ще­ствен­но но­во­го. Опи­сания мно­гих из этих трю­ков мож­но най­ти в Ин­тернете, где они ори­ен­ти­ро­ва­ны в основ­ном на ОС Windows. Но когда я за­нял­ся ин­тел­лек­туа­ли­за­ци­ей сво­его до­ма, то об­на­ру­жил, что Linux го­раз­до луч­ше под­хо­дит для ре­шения этих за­дач, неже­ли ОС от Microsoft. Об этом я и со­би­ра­юсь вам рас­ска­зать. Пре­ж­де чем про­дол­жить, про­изнесу обя­за­тель­ное ри­ту­аль­ное за­кли­нание: сле­дуя при­ве­ден­ным здесь со­ве­там, вы дей­ствуе­те на свой страх и риск. Я за­ранее снимаю с се­бя от­вет­ствен­ность. Все при­ве­ден­ные элек­три­че­ские схе­мы но­сят при­бли­зи­тель­но-по­ка­за­тель­ный ха­рак­тер (па­ра­мет­ры ре­зи­сто­ров и кон­ден­са­то­ров не ука­зы­ваю). Ес­ли вы за­ду­мае­те реа­ли­зо­вать их – уточните па­ра­мет­ры са­мо­стоя­тель­но.

Наш луч­ший друг — параллельный порт

Па­рал­лель­ный порт как нель­зя луч­ше под­хо­дит для на­ших це­лей. Стан­дарт­ные на­пря­жения сиг­на­лов на его вы­во­дах со­от­вет­ству­ют уров­ням на­пря­жения TTL-элек­троники, и мы об­ла­да­ем над ними пол­ным кон­тро­лем: мо­жем уста­нав­ли­вать и сбра­сы­вать их на сколь угод­но про­дол­жи­тель­ное вре­мя. Хо­тя из­на­чаль­но па­рал­лель­ный порт пред­на­зна­чал­ся для вы­во­да дан­ных на прин­тер, в бо­лее поздних вер­си­ях стан­дар­та бы­ли до­бав­ле­ны воз­мож­но­сти чтения дан­ных с внешнего уст­рой­ства (те из вас, кто сел за ком­пь­ю­тер в 90‑х или ранее, на­вер­ное, пом­нят сканеры, ко­то­рые под­клю­ча­лись к ком­пь­ю­те­ру че­рез па­рал­лель­ный порт). Впро­чем, да­же са­мый «ту­пой» па­рал­лель­ный порт уме­ет счи­ты­вать внешние сиг­на­лы – на­при­мер, сиг­нал о том, что в прин­те­ре за­кон­чи­лась бу­ма­га. Схе­ма кон­так­тов па­рал­лель­но­го пор­та пред­став­ле­на на рис. 1. Мы рас­по­ла­га­ем во­се­мью линия­ми для пе­ре­да­чи дан­ных (в ба­зо­вой вер­сии пор­та – от ком­пь­ю­те­ра ко внешне­му уст­рой­ству), тре­мя линия­ми управ­ления (так­же ра­бо­таю­щи­ми на вы­вод) и пя­тью линия­ми со­стояния (ввод дан­ных). На ка­ж­дой из этих линий мо­жет быть уста­нов­ле­на ло­ги­че­ская единица (+5 В) или ноль (0 В). Для управ­ления внешним уст­рой­ством – на­при­мер, ре­ле – мы вы­во­дим уро­вень ло­ги­че­ской 1 на со­от­вет­ствую­щие ли­нии дан­ных пор­та (рис. 2).

Управ­лять па­рал­лель­ным пор­том из Linux мож­но дву­мя спо­со­ба­ми: с по­мо­щью фай­ла уст­рой­ства /dev/parport* и пу­тем непо­сред­ствен­но­го досту­па к ад­ре­сам пор­та. В обо­их слу­ча­ях для по­лу­чения досту­па к пор­ту про­грам­ма долж­на об­ла­дать пра­ва­ми root.

Рас­смот­рим, как ре­шить эту за­да­чу ме­то­дом непо­сред­ствен­но­го досту­па к пор­там (здесь нуж­но раз­ли­чать «порт» в том смыс­ле, о ко­то­ром мы го­во­ри­ли вы­ше, и «порт уст­рой­ства» как ка­нал об­ме­на дан­ны­ми, ото­бра­жен­ный в ад­рес­ное про­стран­ство).

Про­грам­ма, пред­став­лен­ная на листин­ге, пред­ла­га­ет поль­зо­ва­те­лю вве­сти чис­ло и уста­нав­ли­ва­ет сиг­на­лы на линиях дан­ных па­рал­лель­но­го пор­та в со­от­вет­ствии с маской его би­тов.

 #include <fcntl.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/io.h>
 #include <iostream>
 using namespace std;
 int main()
 {
    int addr = 0x378;
    if (ioperm(addr,1,1)) {
              cout << “Невозмож но ус тановить разрешения
 на дос туп к порту. Вы долж ны быть root'ом.” << endl;
              return ­1;
    }
    char i = 1;
    while (i) {
      cout << “Введи те чис ло”;
      cin >> i;
      outb(i, addr);
      cout << “Записано” << endl;
    }
    return 0;
 }


0x378 – ад­рес ре­ги­ст­ра дан­ных пер­во­го па­рал­лель­но­го пор­та ком­пь­ю­те­ра. Ес­ли в систе­ме есть вто­рой па­рал­лель­ный порт, до­ступ к его ре­ги­ст­ру дан­ных осу­ще­ств­ля­ет­ся че­рез ячей­ку по ад­ре­су 0x278. Что­бы по­лу­чить доступ к ад­ре­су ре­ги­ст­ра пор­та, про­грам­ма долж­на за­про­сить и по­лу­чить со­от­вет­ствую­щие пол­но­мо­чия. Мы де­ла­ем это с по­мо­щью функ­ции ioperm(), ко­то­рая доступ­на толь­ко про­цес­сам, вы­пол­няю­щим­ся с пра­ва­ми root. Пер­вый ее ар­гу­мент – на­чаль­ный ад­рес пор­та уст­рой­ства, к ко­то­ро­му мы хо­тим по­лу­чить доступ. Вто­рой ар­гу­мент – раз­мер об­ласти па­мя­ти в бай­тах. Тре­тий ар­гу­мент ука­зы­ва­ет, дол­жен ли доступ быть вклю­чен (1) или вы­клю­чен (0). Ес­ли функ­ция вы­полнена успеш­но, она воз­вра­ща­ет ноль, в про­тив­ном слу­чае – -1. По­лу­чив до­ступ к пор­ту уст­рой­ства, мы мо­жем запи­сы­вать дан­ные с по­мощью функ­ций outb(), outw(), outl(), где b оз­на­ча­ет byte, w – word, l – long. Так, на­при­мер, ес­ли мы за­пи­шем в ре­гистр по ад­ре­су 0x378 чис­ло 4, на вы­во­де 4‑го разъ­е­ма па­рал­лель­но­го пор­та (линия D2) бу­дет уста­нов­ле­но на­пря­жение, со­от­вет­ствую­щее ло­ги­че­ской 1. Про­ве­рить же ра­бо­ту про­грам­мы, управ­ляю­щей пор­том, очень про­сто. Для это­го нам по­на­до­бят­ся несколь­ко све­то­дио­дов и ка­бель Centronics, ко­то­рым в бы­лые вре­ме­на со­еди­ня­ли ком­пь­ю­те­ры и прин­те­ры (рис. 3). По­сколь­ку па­рал­лель­ный порт пре­достав­ля­ет нам 8 линий дан­ных, с по­мо­щью од­но­го толь­ко ре­ги­ст­ра дан­ных мы мо­жем управ­лять во­се­мью уст­рой­ства­ми. Этим воз­мож­но­сти па­рал­лель­но­го пор­та не ис­чер­пы­ва­ют­ся.

Как уже от­ме­ча­лось, че­рез ре­гистр со­стояния мы мо­жем пе­ре­да­вать дан­ные от внешнего уст­рой­ства к ком­пь­ю­те­ру. Про­стей­шее та­кое уст­рой­ство – дат­чик раз­мы­кания. Он мо­жет, на­при­мер, со­об­щить систе­ме, что кто-то от­крыл вход­ную дверь, ок­но или двер­цу сей­фа. Ка­ким об­ра­зом взаи­мо­дей­ству­ет внешнее уст­рой­ство и линия со­стояния пор­та? По умол­чанию на линиях S3–S7 уста­нов­ле­ны уровни ло­ги­че­ской 1, и маска ре­ги­ст­ра со­стояния вы­гля­дит как 11111000b. Что­бы уста­но­вить на од­ной из линий со­стояния ло­ги­че­ский ноль, мы про­сто за­мы­ка­ем эту линию с зем­лей пор­та че­рез со­про­тив­ление 300–500 Ом. В ре­зуль­та­те зна­чение со­от­вет­ствую­ще­го би­та в ре­ги­ст­ре ста­но­вит­ся рав­ным ну­лю. Та­ким об­ра­зом, сбро­шен­ный бит сви­де­тель­ству­ет о том, что цепь замк­ну­та; уста­нов­лен­ный – о том, что ра­зомк­ну­та. Ниже при­во­дит­ся фраг­мент про­грам­мы, ко­то­рая кон­тро­ли­ру­ет со­стояние дат­чи­ка раз­мы­кания (пред­по­ла­га­ет­ся, что дат­чик под­клю­чен к линии S5 (вы­вод 12).

 int main()
 {
    int addr = 0x379;
    if (ioperm(addr,1,1)) {
               cout << “Невозмож но ус тановить разрешения
 на дос туп к порту. Вы долж ны быть root'ом.” << endl;
               return ­1;
    }
    char i;
    while (1) {
      i = inb(addr);
      if (i & 32)
                  cout << “Разомкнуто” << endl;
      else
               cout << “Замкнуто” << endl;
      sleep(1);
    }
    return 0;
 }

Ре­гистр со­стоя­ния пор­та LPT1 дос­ту­пен по ад­ре­су 0x379, а пор­та LPT2 – со­от­вет­ст­вен­но 0x279.

Как бы­ло от­ме­че­но вы­ше, с па­рал­лель­ным пор­том мож­но ра­бо­тать и че­рез файл уст­рой­ст­ва (/dev/parport*, где * – чис­ло, обо­зна­чаю­щее но­мер пор­та). По­сле то­го как мы от­кры­ли уст­рой­ст­во пор­та

fd = open(“/dev/parport0”, O_RDWR|O_NONBLOCK);

мы мо­жем на­стро­ить его с по­мо­щью вы­зо­вов ioctl(). На­при­мер, ко­ман­да PPCLAIM от­кры­ва­ет про­грам­ме дос­туп к пор­ту:

if (ioctl(fd,PPCLAIM) <0)
  printf (“Ошиб­ка!”);

Порт, за­ня­тый с по­мо­щью вы­зо­ва PPCLAIM, мо­жет быть ос­во­бо­ж­ден с по­мо­щью вы­зо­ва PPRELEASE. Кон­стан­ты и мак­ро­сы, имею­щие от­но­ше­ние к па­рал­лель­но­му пор­ту, оп­ре­де­ле­ны в за­го­ло­воч­ных фай­лах <linux/parport.h> и <linux/ppdev.h>.

Для за­пи­си дан­ных в порт его мож­но пе­ре­вес­ти в ре­жим бай­то­вой пе­ре­да­чи:

int mode = IEEE1284_MODE_BYTE; /* or IEEE1284_MODE_NIB­BLE, etc. */
ioctl(fd,PPNEGOT,&mode);

по­сле че­го мож­но ис­поль­зо­вать обыч­ную функ­цию write(). Для чте­ния со­стоя­ния пор­та мы вос­поль­зу­ем­ся вы­зо­вом PPRSTATUS:

int status;
ioctl(fd, PPRSTATUS, &status);

По­сле­до­ва­тель­ный порт

Этот порт го­раз­до мень­ше под­хо­дит для на­ших экс­пе­ри­мен­тов – пре­ж­де все­го по­то­му, что он бо­лее «ин­тел­лек­туа­лен», неже­ли па­рал­лель­ный. Ес­ли в па­рал­лель­ном пор­ту для пе­ре­да­чи од­но­го бай­та со­от­вет­ствую­щие уровни вы­став­ля­ют­ся од­но­вре­мен­но на вось­ми линиях, то в по­сле­до­ва­тель­ном од­на линия ис­поль­зу­ет­ся для пе­ре­да­чи всех вось­ми би­тов (элек­троника, управ­ляю­щая пор­том, «зна­ет», когда за­кан­чи­ва­ет­ся пе­ре­да­ча од­но­го би­та и на­чи­на­ет­ся пе­ре­да­ча сле­дую­ще­го). Ис­поль­зо­вать по­сле­до­ва­тель­ный порт для пол­но­цен­но­го управ­ления внешним уст­рой­ством мож­но толь­ко в том слу­чае, ес­ли внешнее уст­рой­ство осна­ще­но та­кой же ум­ной элек­троникой. Тем не менее, как минимум 4 ли­нии по­сле­до­ва­тель­но­го пор­та мож­но ис­поль­зо­вать так же, как мы ис­поль­зу­ем линии па­рал­лель­но­го. Из этих че­ты­рех линий две пред­на­зна­че­ны для пе­ре­да­чи дан­ных от уст­рой­ства со­пря­жения в ком­пь­ю­тер, а две – на­обо­рот, от ком­пь­ю­те­ра к уст­рой­ству со­пря­жения; их мы и рас­смот­рим. Речь идет о линиях DTR (Data Terminal Ready – ком­пь­ю­тер го­тов к об­ме­ну дан­ны­ми) и RTS (Re­quest To Send) – за­прос на пе­ре­да­чу дан­ных. В от­ли­чие от са­мих линий пе­ре­да­чи дан­ных, сиг­на­лы на этих слу­жеб­ных линиях мо­гут уста­нав­ли­вать­ся на сколь угод­но дли­тель­ное вре­мя (так же, как и в слу­чае с па­рал­лель­ным пор­том). Ес­ли мы хо­тим пе­ре­да­вать сиг­на­лы от внешнего уст­рой­ства в ком­пь­ю­тер, мы мо­жем восполь­зо­вать­ся линия­ми DSR (Data Set Ready – внешнее уст­рой­ство го­то­во к ра­бо­те) и CTS (Clear To Send – внешнее уст­рой­ство го­то­во к пе­ре­да­че дан­ных).

Од­на­ко ра­до­вать­ся ра­но. Еще од­на про­бле­ма свя­за­на с тем, что уровни на­пря­жения, ис­поль­зуе­мые по­сле­до­ва­тель­ным пор­том для ото­бра­жения ну­лей и единиц, от­ли­ча­ют­ся от уровней, ко­то­рые ис­поль­зу­ет па­рал­лель­ный порт (и дру­гая рас­про­странен­ная циф­ро­вая элек­троника). При ра­бо­те с по­сле­до­ва­тель­ным пор­том ло­ги­че­ской единице со­от­вет­ству­ет на­пря­жение от -3 до -25 В, а ло­ги­че­ско­му ну­лю – от +3 до +25 В (в дру­гих источниках – от -3 до -12 В и от +3 до +12 В со­от­вет­ствен­но). В мо­ей систе­ме на­пря­жения со­став­ля­ли -12 В и +3 В. Их, мяг­ко го­во­ря, неудоб­но ис­поль­зо­вать на­пря­мую. Ско­рее все­го, вам при­дет­ся вы­полнить пре­об­ра­зо­вание уровней на­пря­жения по­сле­до­ва­тель­но­го пор­та к стан­дарт­ным уров­ням TTL. Са­мый по­пу­ляр­ный пре­об­ра­зо­ва­тель та­ко­го ро­да – мик­ро­схе­ма MAX232 (рис. 4) и ее мно­го­чис­лен­ные ана­ло­ги. Под­роб­ные све­дения об этой мик­ро­схе­ме мож­но по­лу­чить по ад­ре­су http://www.maxim-ic.com/datasheet/index.mvp/id/1798. По­сколь­ку сиг­на­лы по­сле­до­ва­тель­но­го пор­та прак­ти­че­ски все­гда при­хо­дит­ ся пре­об­ра­зо­вы­вать в сиг­на­лы ло­ги­ки TTL, по­доб­ные пре­об­ра­зо­ва­те­ли есть поч­ти в ка­ж­дом уст­рой­стве, ко­то­рое мо­жет по­лу­чать дан­ные с по­сле­до­ва­тель­но­го пор­та. Мик­ро­схе­ма по­зво­ля­ет пре­об­ра­зо­вы­вать сиг­на­лы с че­ты­рех линий (две линии в пря­мом, две линии в об­рат­ном на­прав­лении). Об­щая схе­ма с ис­поль­зо­ванием пре­об­ра­зо­ва­те­ля MAX232 при­ве­де­на на рис. 5. Сиг­нал, по­дан­ный с по­сле­до­ва­тель­но­го пор­та на вход RS232 In (1), поя­вит­ся в пре­об­ра­зо­ван­ном ви­де на вы­хо­де TTL OUT (1) и так да­лее.

Для про­грамм­но­го управ­ления по­сле­до­ва­тель­ным пор­том мы восполь­зу­ем­ся штат­ны­ми сред­ства­ми Linux. Необ­хо­ди­мые кон­стан­ты и мак­ро­сы оп­ре­де­ле­ны в фай­ле <termios.h>. По­че­му termios? Де­ло в том, что в ста­ро­давние вре­ме­на к по­сле­до­ва­тель­но­му пор­ту Unix ча­ще все­го под­клю­ча­ли тер­ми­нал (ли­бо на­пря­мую, ли­бо че­рез «про­слой­ку» из двух мо­де­мов и те­ле­фон­ной линии). Ниже при­во­дит­ся про­стень­кая про­грам­мка, ко­то­рая уста­нав­ли­ва­ет и сбра­сы­ва­ет сиг­нал RTS с ин­тер­ва­лом при­мер­но в од­ну се­кун­ду.

 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <stdio.h>
 #include <sys/ioctl.h>
 #include <termios.h>
 int main() {
   int fd;
   int status;
   fd = open(/dev/ttyS0”, O_RDWR | O_NOCTTY | O_NDELAY);
   if (fd == ­1) {
      perror(“Не могу от крыть файл уст ройст ва ­ “);
      return ­1;
   }
   else
     fcntl(fd, F_SETFL, 0);
   while (1) {
     ioctl(fd, TIOCMGET, &status);
     if (!status) {
       printf(“Порт недос ту пен\n”);
       return ­1;
     }
     status &= ~TIOCM_RTS;
     ioctl(fd, TIOCMSET, &status);
     sleep(1);
     ioctl(fd, TIOCMGET, &status);
     status |= TIOCM_ RTS;
     ioctl(fd, TIOCMSET, &status);
     sleep(1);
   }
   return 0;
 }


По­сле­до­ва­тель­ные пор­ты пред­став­ле­ны в Linux уст­рой­ства­ми /dev/ttyS*, где * – чис­ло (как пра­ви­ло, от 0 до 7), со­от­вет­ствую­щее но­ме­ру пор­та. Пoрту COM1 со­от­вет­ству­ет уст­рой­ство /dev/ttyS0 и так да­лее. Пре­фикс tty то­же на­ме­ка­ет на то, что когда-то пор­ты ис­поль­зо­ва­лись для под­клю­чения тер­ми­на­лов. С этим же свя­за­на необ­хо­ди­мость ис­поль­зо­вать флаг O_NOCTTY при от­кры­тии фай­ла уст­рой­ства – без него от­кры­тое уст­рой­ство-тер­ми­нал ста­ло бы управ­ляю­щим тер­ми­на­лом на­шей про­грам­мы (и весь стан­дарт­ный ввод/вы­вод вы­пол­нял­ся бы че­рез него). Флаг O_NDELAY го­во­рит систе­ме, что на на­ши опе­ра­ции вво­да/вы­во­да не долж­но вли­ять со­стояние сиг­на­ла DCD (об­на­ру­жение несу­щей дан­ных). По­лу­чив де­ск­рип­тор фай­ла пор­та, мы мо­жем управ­лять им с по­мо­щью ioctl(). Вы­зов ioctl() с кон­стан­той TIOCMGET воз­вра­ща­ет в треть­ем па­ра­мет­ре маску би­тов со­стояния пор­та. Сре­ди этих би­тов есть и ин­те­ре­сую­щие нас DTR, RTS, DSR и DTS. Вы­де­лить со­от­вет­ствую­щие би­ты в маске со­стояния мож­но с по­мо­щью кон­стант TIOCM_DTR, TIOCM_RTS, TIOCM_DSR и TIOCM_CTS. Что­бы из­менить со­стояние пор­та со сто­ро­ны ком­пь­ю­те­ра, нуж­но запи­сать но­вую маску сиг­на­лов с по­мо­щью вы­зо­ва ioctl() с коман­дой TIOCMSET.

Про­грам­ма управ­ля­ет сиг­на­ла­ми пор­та COM1. Ес­ли ука­зан­ный порт в систе­ме от­сут­ству­ет, в маске би­тов со­стояния по­сле вы­зо­ва ioctl() со вто­рым па­ра­мет­ром TIOCMGET бу­дет запи­са­но зна­чение 0.

Про­грам­му сле­ду­ет за­пускать от имени поль­зо­ва­те­ля root, ина­че ей бу­дет от­ка­за­но в досту­пе к фай­лу пор­та. Са­мый про­стой спо­соб про­тести­ро­вать ра­бо­ту про­грам­мы – про­ве­рить на­пря­жения на кон­так­тах разъ­е­ма. На­пря­жение за­ме­ря­ет­ся ме­ж­ду вы­во­дом 5 (зем­ля) и 4 (DTR) или 7 (RTS). Для удоб­ства мож­но под­клю­чить нуль-мо­дем­ный ка­бель. Уч­ти­те толь­ко, что ка­бе­ли бы­ва­ют пол­ные и непол­ные. В непол­ном ка­бе­ле ин­те­ре­сую­щие нас линии DTR и RTS замк­ну­ты на линии DSR и CTS то­го же уст­рой­ства, то есть со­пря­жен­ное уст­рой­ство про­сто не по­лу­чит сиг­на­лы че­рез та­кой ка­бель. Помните так­же, что сиг­нал, ко­то­рый по­яв­ля­ет­ся на вы­хо­де DTR пор­та ком­пь­ю­те­ра, бу­дет пе­ре­ки­нут ка­бе­лем на линию DSR внешнего уст­рой­ства, а сиг­нал RTS поя­вит­ся, со­от­вет­ствен­но, на линии CTS. В ка­бе­лях, пред­на­зна­чен­ных для под­клю­чения мо­де­мов, линии не пе­ре­кре­щи­ва­ют­ся.

Па­лоч­ка ра­до­сти

Еще один ин­те­рес­ный для нас порт – порт джой­сти­ка или иг­ро­вой. Ско­рее все­го, он не вы­ве­ден на зад­нюю панель ва­ше­го ПК, но его мож­но най­ти на ма­те­рин­ской пла­те (рис. 6) или на зву­ко­вой кар­те (для экс­пе­ри­мен­тов луч­ше все­го по­дой­дет ста­рая ненуж­ная «зву­ко­ву­ха»). В от­ли­чие от дру­гих рас­смот­рен­ных пор­тов, порт джой­сти­ка ра­бо­та­ет толь­ко на ввод дан­ных. Он досту­пен по ад­ре­су 0x201, и с ним мож­но ра­бо­тать с по­мо­щью функ­ций inb() и outb() так же, как мы ра­бо­та­ем с па­рал­лель­ным пор­том.

Стар­шие 4 би­та бай­та счи­тан­но­го из ре­ги­ст­ра 0x201 от­ра­жа­ют со­стояние кно­пок джой­сти­ка. Их мож­но ис­поль­зо­вать так же, как мы ис­поль­зо­ва­ли линии со­стояния па­рал­лель­но­го пор­та. Млад­шие 4 би­та ин­те­реснее: они свя­за­ны с че­тырь­мя из­ме­ри­те­ля­ми со­про­тив­ления (в ин­тер­ва­ле при­мер­но от 0 до 100 кОм). В прин­ци­пе, порт джой­сти­ка мож­но ис­поль­зо­вать как про­стой АЦП. Ве­ли­чи­на со­про­тив­ления из­ме­ря­ет­ся по ско­ро­сти за­ряд­ки кон­ден­са­то­ра сле­дую­щим об­ра­зом: по умол­чанию кон­ден­са­то­ры за­ря­же­ны, и зна­чения со­от­вет­ствую­щих им би­тов в ре­ги­ст­ре пор­та рав­ны 1. В от­вет на запись лю­бо­го зна­чения в порт кон­ден­са­то­ры раз­ря­жа­ют­ся, а со­от­вет­ствую­щие им би­ты принима­ют зна­чение 0. Че­рез неко­то­рое вре­мя (про­пор­цио­наль­ное ве­ли­чине из­ме­ряе­мых со­про­тив­лений) кон­ден­са­то­ры сно­ва за­ря­дят­ся, и со­от­вет­ствую­щие би­ты ре­ги­ст­ра по­лу­чат зна­чение 1. Из ска­зан­но­го оче­вид­но, что для кор­рект­но­го из­ме­рения ве­ли­чи­ны со­про­тив­ления необ­хо­ди­мо точ­но из­ме­рить вре­мя с мо­мен­та, когда би­ты со­стояния кон­ден­са­то­ров бы­ли сбро­ше­ны и до мо­мен­та их восста­нов­ления (ин­тер­вал вре­мени со­став­ля­ет несколь­ко мик­ро­се­кунд). В мно­го­за­дач­ной систе­ме это, мяг­ко го­во­ря, непро­сто сде­лать. Непро­сто, но не невоз­мож­но!

Для об­ра­бот­ки сиг­на­лов с пор­та джой­сти­ка мы мо­жем восполь­зо­вать­ся «са­мым мощ­ным ору­жи­ем» поль­зо­ва­тель­ских про­грамм Linux – систем­ным вы­зо­вом iopl(). Этот вы­зов (ко­то­рый ра­бо­та­ет толь­ко для про­цес­со­ров, осно­ван­ных на ар­хи­тек­ту­ре i386) по­зво­ля­ет про­грам­ме, вы­пол­няю­щей­ся с пра­ва­ми root, по­лу­чить осо­бые при­ви­ле­гии при ра­бо­те с про­цес­со­ром и пор­та­ми вво­да-вы­во­да. По­сле вы­зо­ва iopl() с ар­гу­мен­том 3 про­грам­ма по­лу­ча­ет воз­мож­ность управ­лять фла­гом пре­ры­ваний с по­мо­щью команд про­цес­со­ра CLI и STI. За­бло­ки­ро­вав об­ра­бот­ку пре­ры­ваний (на как мож­но мень­шее вре­мя) про­грам­ма смо­жет из­ме­рить про­дол­жи­тель­ность вре­мени за­ряд­ки с мак­си­маль­но доступ­ной точ­но­стью. Ра­зу­ме­ет­ся, с точ­ки зрения «боль­шо­го сти­ля» про­грам­ми­ро­вания, бло­ки­ро­вание пре­ры­ваний поль­зо­ва­тель­ской про­грам­мой не одоб­ря­ет­ся, так как при­во­дит к сни­же­нию про­из­во­ди­тель­но­сти сис­те­мы (и это еще не са­мое худ­шее, что мо­жет быть), но мы еще в на­ча­ле до­го­во­ри­лись, что бу­дем иг­рать не по пра­ви­лам.

Внеш­ний COM-порт

Тот факт, что в на­ших про­грам­мах взаи­мо­дей­ст­вия с по­сле­до­ва­тель­ным пор­том мы не об­ра­ща­ем­ся к ре­ги­ст­рам обо­ру­до­ва­ния на­пря­мую, при­но­сит нам не­ожи­дан­ную поль­зу. Бла­го­да­ря это­му мы мо­жем ис­поль­зо­вать на­ши про­грам­мы с та­ки­ми уст­рой­ст­ва­ми, как пе­ре­ход­ник USB-Serial (см. рис.).

Ес­ли в Windows для под­клю­че­ния та­ко­го уст­рой­ст­ва при­дет­ся спе­ци­аль­но ус­та­нав­ли­вать драй­ве­ра, то в Linux c яд­ра­ми 2.6.x все драй­ве­ра уже ус­та­нов­ле­ны и за­гру­жа­ют­ся при под­клю­че­нии уст­рой­ст­ва ав­то­ма­ти­че­ски. Этим уст­рой­ст­вам со­от­вет­ст­ву­ют фай­лы /dev/ttyUSB*, где * – но­мер пор­та. Для ра­бо­ты с эти­ми пор­та­ми мож­но ис­поль­зо­вать те же вы­зо­вы ioctl(), что и для встро­ен­ных пор­тов, но нуж­но учи­ты­вать не­ко­то­рые ню­ан­сы. Ес­ли уст­рой­ст­во по ка­кой-то при­чи­не от­клю­чи­лось (а в хо­де экс­пе­ри­мен­тов с пор­том это про­изой­дет не­од­но­крат­но), драй­вер от­кро­ет но­вое, ис­поль­зуя пер­вое сво­бод­ное имя фай­ла, от­лич­ное от ра­нее ис­поль­зо­ван­но­го (в от­ли­чие от уст­ройств /dev/ttyS*, для уст­ройств /dev/tty­USB* су­ще­ст­ву­ют толь­ко фай­лы, со­от­вет­ст­вую­щие ре­аль­но под­клю­чен­ным уст­рой­ст­вам). В мо­их экс­пе­ри­мен­тах име­на уст­ройств «пры­га­ли» ме­ж­ду /dev/ttyUSB0 и /dev/ttyUSB1. В са­мом про­стом слу­чае мож­но про­сто пе­ре­би­рать до­пус­ти­мые име­на уст­ройств и пы­тать­ся от­крыть их. Мож­но вос­поль­зо­вать­ся так­же со­дер­жи­мым ди­рек­то­рий /dev/serial/by-id/ и /dev/serial/by-path/. Эти ди­рек­то­рии со­дер­жат сим­во­ли­че­ские ссыл­ки на пор­ты, под­клю­чен­ные к сис­те­ме в дан­ный мо­мент. Име­на фай­лов-ссы­лок так­же бо­лее ин­фор­ма­тив­ны, не­же­ли про­стые име­на фай­лов уст­ройств.

Персональные инструменты
купить
подписаться
Яндекс.Метрика