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

LXF162:Arduino

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


Элек­тро­ни­ка. Ап­па­рат­ные про­ек­ты с от­кры­тым ко­дом, рас­ши­ряющие ваш кру­го­зор.

Содержание

Arduino: Плюс кла­виа­ту­ра

Нуж­ны уст­рой­ст­ва вво­да? Ник Вейч зна­ко­мит нас со ска­ни­руе­мой мат­ри­цей и рас­ска­зы­ва­ет о ва­ри­ан­тах под­клю­че­ния кно­пок к Arduino.

(thumbnail)
Наш эксперт. Ко­гда LXF толь­ко поя­вил­ся, его дер­жа­ли на пла­ву исключительно скрип­ты Bash от Ни­ка Вей­ча. По­том их за­ме­ни­ли «лю­ди», и это, по мне­нию Ни­ка, ста­ло ша­гом на­зад...

На этом уро­ке мы по­го­во­рим о мат­ри­це. Нет, не той, что c крас­ны­ми и синими пи­люль­ка­ми, а о кла­виа­ту­ре. Рань­ше мы уже крат­ко о ней упо­ми­на­ли, а се­го­дня де­таль­но по­зна­ко­мим­ся с ва­ри­ан­та­ми и стра­те­ги­ей ра­бо­ты с ними. Начнем с бо­лее про­сто­го.

Что нам на­до
  • ПО Arduino 1.0
  • Не­сколь­ко кно­пок
  • Столь­ко же им­пульс­ных дио­дов
  • Сдви­го­вый ре­гистр SIPO
  • Сдви­го­вый ре­гистр PISO
  • Мон­таж­ная пла­та, мно­го разъ­е­мов и про­вод­ни­ков

Как всегда, спо­со­бов под­клю­чения кно­пок к Arduino несколь­ко. В са­мом про­стом слу­чае од­на кноп­ка на­зна­ча­ет­ся на один кон­такт. Ес­ли кно­пок не боль­ше че­ты­рех, это и во­об­ще луч­ший ва­ри­ант: тут вы все рав­но ма­ло что сэ­ко­но­ми­те. Ес­ли кно­пок боль­ше, при­дет­ся по­ду­мать о мат­ри­це, или о неко­то­ром уп­лотнении дан­ных.

Ме­то­ды уп­лотнения

Про­стое уп­лотнение мож­но реа­ли­зо­вать с по­мо­щью раз­лич­ных уст­ройств – на­при­мер, есть спе­ци­аль­ные мик­ро­схе­мы, сво­ра­чи­ваю­щие во­семь линий дан­ных в од­ну, ис­поль­зуя 3 би­та как ад­рес. Са­мый по­пу­ляр­ный муль­ти­п­лек­сор/де­муль­ти­п­лек­сор – мик­ро­схе­ма 4051 и ее ва­ри­ан­ты; все они ра­бо­та­ют по опи­сан­но­му вы­ше прин­ци­пу. По­сколь­ку это все­го лишь пе­ре­клю­ча­тель, он мо­жет ра­бо­тать в обо­их на­прав­лениях для циф­ро­вых и ана­ло­го­вых вхо­дов и вы­хо­дов, но под­хо­дит и для соз­дания кла­виа­ту­ры.

Схе­ма бу­дет до­воль­но про­стой (см. Схе­му 1). При вось­ми пе­ре­клю­ча­те­лях до­воль­но лег­ко пе­ре­брать зна­чения на вы­во­дах ад­ре­са и счи­тать вы­ход­ное зна­чение. Един­ст­вен­ная слож­ность по час­ти ко­да – это за­дание зна­чений на ад­рес­ных вы­во­дах. К сча­стью, есть удоб­ная функ­ция bitread(x,y), ко­то­рая воз­вра­ща­ет зна­чение би­та для за­дан­но­го це­ло­го чис­ла x. Для пе­ре­бо­ра зна­чений нам по­на­до­бит­ся нечто вро­де

int data;

for (int count=0; count<=7; count++) {

digitalWrite(4, bitRead(count,0));

digitalWrite(5, bitRead(count,1));

digitalWrite(6, bitRead(count,2));

bitWrite(data, count, digitalRead(7));

}

Функ­ция bitWrite() со­би­ра­ет от­дель­ные би­ты в од­но це­лое зна­чение, а ка­ж­дый вход со­от­вет­ст­ву­ет од­но­му би­ту ре­зуль­ти­рую­ще­го зна­чения. Здесь пред­по­ла­га­ет­ся, что 4, 5 и 6 – ад­рес­ные би­ты, а 7 – вход­ной кон­такт.

Это­го спо­со­ба вполне доста­точ­но, чтобы читать дан­ные с про­стой кла­виш­ной панели, не при­бе­гая к реа­ли­за­ции пол­ной мат­ри­цы. Ог­раничения это­го под­хо­да про­яв­ля­ют­ся при боль­шем ко­ли­че­­ст­ве кла­виш. Мож­но, конеч­но, восполь­зо­вать­ся муль­ти­­плек­со­ра­ми с боль­шим ко­ли­че­­ст­вом вхо­дов, но ес­ли их дей­ст­ви­тель­но мно­го, схе­ма мо­жет стать довольно неук­лю­жей и мед­лен­ной. В этих слу­ча­ях нам по­на­до­бит­ся мат­ри­ца...

Стро­ки и столб­цы

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

Как по­лу­чить этот сиг­нал? Ва­ри­ан­тов опять-та­ки несколь­ко, в том чис­ле – про­сто за­пи­тать линии на­пря­мую с вы­хо­дов (см. врез­ку о ма­лень­ких мат­ри­цах). Мы восполь­зу­ем­ся сдви­го­вым ре­ги­ст­ром, ко­то­рый лег­ко на­стро­ить для этой це­ли (см. Схе­му 2).

Функ­ция­ми сдви­го­во­го ре­ги­ст­ра, ко­то­рый фор­ми­ру­ет ток для столб­цов, нуж­но управ­лять на бо­лее низ­ком уровне – сдви­го­вые ре­ги­ст­ры в Arduino пе­ре­ме­ща­ют по бай­ту дан­ных за цикл, но мы восполь­зу­ем­ся ре­ги­ст­ром на уровне би­тов – за­гру­зим бит в ре­гистр и за­тем с по­мо­щью им­пуль­сов бу­дем сдви­гать его на од­ну позицию за раз. Гло­баль­ная пе­ре­мен­ная glob_COLUMN со­дер­жит но­мер те­ку­ще­го столб­ца.

Лож­ное сра­ба­ты­вание и мас­ки­ро­вание

У неко­то­рых кла­виа­тур на осно­ве сканируе­мых мат­риц есть од­на про­бле­ма – лож­ное сра­ба­ты­вание. При на­жа­тии несколь­ких кла­виш сканирую­щей це­пи ка­жет­ся, что на­жа­та со­вер­шен­но дру­гая кла­ви­ша. Ес­ли кла­ви­ши на­хо­дят­ся в раз­ных стро­ках и столб­цах, то со­единения, ко­то­рые они фор­ми­ру­ют, по­зво­ля­ют то­ку течь в обо­их на­прав­лениях. В неко­то­рых слу­ча­ях, на­при­мер, в уг­лах мат­ри­цы, это мо­жет при­во­дить к то­му, что ток, обыч­но про­те­каю­щий че­рез один вы­клю­ча­тель и че­рез дру­гой в про­ти­во­по­лож­ном на­прав­лении, да­ет лож­ный сиг­нал.

Обыч­но это не про­бле­ма, кро­ме тех слу­ча­ев, когда вам нуж­но про­чи­тать несколь­ко од­но­вре­мен­ных на­жа­тий кла­виш. Ре­шение в этом слу­чае то­же до­воль­но про­стое, хо­тя и несколь­ко тру­до­ем­кое – дио­ды. Вклю­чение дио­да по­сле­до­ва­тель­но с ка­ж­дой кла­ви­шей не по­зво­лит сиг­на­лу прой­ти об­рат­но че­рез за­кры­тый вы­клю­ча­тель и снимет про­бле­му лож­но­го сра­ба­ты­вания. Есть и дру­гой ва­ри­ант, ис­поль­зуе­мый в биб­лио­теч­ной мат­ри­це кла­виа­ту­ры Arduino: оп­ра­ши­вать толь­ко од­ну стро­ку и один стол­бец в один мо­мент вре­мени. По раз­лич­ным при­чи­нам, опи­сан­ным в основ­ном тек­сте, это ре­шение пло­хо мас­шта­би­ру­ет­ся.

glob_COLUMN++;

digitalWrite(SR_LATCH, LOW);

if (glob_COLUMN>NUMBER_OF_COLUMNS) {

glob_COLUMN=0;

digitalWrite(SR_DATA, HIGH); //за­но­во за­гру­жа­ем 1 на вы­вод

digitalWrite(SR_CLOCK, HIGH); //сдвигаем

digitalWrite(SR_CLOCK, LOW);

digitalWrite(SR_DATA, LOW); // за­гру­жа­ем 0 на вы­вод

}else{

digitalWrite(SR_CLOCK, HIGH);

digitalWrite(SR_CLOCK, LOW);

}

digitalWrite(SR_LATCH, HIGH);

(thumbnail)
> Схе­ма 1. Муль­ти­п­лек­сор по­мо­жет оп­ре­де­лить со­стоя­ние не­сколь­ких пе­ре­клю­ча­те­лей, не ис­поль­зуя слиш­ком мно­го вхо­дов. Это несложно и с точ­ки зре­ния про­грам­ми­ро­ва­ния.

Пе­ре­мен­ные SR_ со­от­вет­ст­ву­ют кон­так­там Arduino, ис­поль­зуе­мым для под­клю­чения к этим сиг­на­лам на сдви­го­вом ре­ги­ст­ре. Ни­ка­ких со­гла­шений на этот счет нет – мож­но ис­поль­зо­вать лю­бые кон­так­ты, но луч­ше объ­я­вить их в на­ча­ле ко­да, что­бы при необ­хо­ди­мо­сти лег­ко ме­нять в за­ви­си­мо­сти от то­го, ка­кие кон­так­ты ис­поль­зу­ют­ся/сво­бод­ны.

По­ка счет­чик столб­цов не достиг ко­ли­че­­ст­ва столб­цов в на­шей мат­ри­це, код лишь уста­нав­ли­ва­ет им­пульс в «единицу», а за­тем сно­ва в «ноль». Час­тот­ная ха­рак­те­ри­сти­ка стан­дарт­но­го сдви­го­во­го ре­ги­ст­ра 59581 с за­па­сом по­кры­ва­ет по­сле­до­ва­тель­ные вы­зо­вы digitalWrite(), но ес­ли вы бу­де­те поль­зо­вать­ся дру­ги­ми ком­понен­та­ми или ес­ли в це­пи боль­шой шум, мо­жет по­тре­бо­вать­ся неболь­шая за­держ­ка ме­ж­ду вы­зо­ва­ми. В боль­шин­ст­ве слу­ча­ев бу­дет доста­точ­но та­кой:

delayMicroseconds(5);

Когда мы дости­га­ем ко­ли­че­­ст­ва столб­цов, сра­ба­ты­ва­ет усло­вие if. Оно сбра­сы­ва­ет счет­чик столб­цов и вы­да­ет еще од­ну «единицу» на вы­ход­ной кон­такт, за­тем сдви­га­ет его, и цикл на­чи­на­ет­ся сно­ва. Так, для трех столб­цов по­сле­до­ва­тель­ность бу­дет та­кой: 100100100... а для че­ты­рех – та­кой: 100010001... Ра­зу­ме­ет­ся, мож­но объ­е­динить несколь­ко ре­ги­ст­ров сдви­га в це­поч­ку и ис­поль­зо­вать боль­шие зна­чения счет­чи­ков для мат­ри­цы.

При по­да­че сиг­на­ла на ка­ж­дый из столб­цов нуж­но про­сканиро­вать стро­ки. Сде­лать это мож­но по-раз­но­му – с по­мо­щью муль­ти­п­лек­со­ра (как мы сде­ла­ли это ранее), рас­ши­ри­те­ля вхо­да/вы­хо­да или сдви­го­во­го ре­ги­ст­ра дру­го­го ти­па.

Рас­ши­ри­тель вхо­да/вы­хо­да (на­при­мер, MCP28S17) хо­ро­шо ис­поль­зо­вать в том слу­чае, ес­ли мат­ри­ца боль­шая. Эта мик­ро­схе­ма ис­поль­зу­ет по­сле­до­ва­тель­ный ин­тер­фейс (SPI), для ко­то­ро­го нуж­но 4 кон­так­та, но за­то он очень быстр – помните, что с раз­рас­танием мат­ри­цы вре­мя на ее сканиро­вание уве­ли­чи­ва­ет­ся, по­это­му ес­ли нуж­но и об­ра­ба­ты­вать дан­ные, и счи­ты­вать зна­чения с кла­виа­ту­ры, сканиро­вание долж­но быть бы­ст­рым.

Впро­чем, в ка­че­­ст­ве рас­ши­ри­те­ля про­ще все­го ис­поль­зо­вать еще один сдви­го­вый ре­гистр. Ранее в этой се­рии мы при­ме­ня­ли сдви­го­вые ре­ги­ст­ры SIPO (с по­сле­до­ва­тель­ным вхо­дом и па­рал­лель­ным вы­хо­дом), на ко­то­рые по­да­ет­ся по­сле­до­ва­тель­ность би­тов, вы­во­ди­мых на вы­ход­ные кон­так­ты. Есть ре­ги­ст­ры и с па­рал­лель­ны­ми вхо­да­ми и по­сле­до­ва­тель­ным вы­хо­дом (PISO), та­кие как 74165 и CD4021. В та­ких схе­мах зна­чения вось­ми вхо­дов снима­ют­ся и вы­да­ют­ся в ви­де по­сле­до­ва­тель­но­сти на один вы­ход под управ­лением сиг­на­ла внешнего генера­то­ра – по су­ти, их дей­ст­вие про­ти­во­по­лож­но ре­ги­ст­рам SIPO. Как и дру­гие сдви­го­вые ре­ги­ст­ры, их то­же мож­но со­еди­нять в це­поч­ку, и мы смо­жем по­лу­чить дан­ные в стро­ках с чис­лом эле­мен­тов боль­ше 8. Для их ра­бо­ты нуж­но все­го три кон­так­та.

Не­мно­го о Define

Ди­рек­ти­ва #define очень по­лез­на: сто­ит до­ба­вить несколь­ко та­ких в на­ча­ло ко­да – и при необ­хо­ди­мо­сти мож­но лег­ко из­менить ка­кую-то функ­цию без необ­хо­ди­мо­сти про­смат­ри­вать весь код. Это, в от­ли­чие от пе­ре­мен­ных, так­же эко­но­мит па­мять. За­ме­на зна­чений в ко­де на объ­яв­ление про­из­во­дит­ся пе­ред ком­пи­ля­ци­ей и рав­но­цен­но ука­занию са­мих зна­чений. Ди­рек­ти­вы луч­ше раз­ме­щать в на­ча­ле ко­да. За­помните син­так­сис! Без зна­ка + и без ; в кон­це:

#define MYVARIABLE 42

Име­на па­ра­мет­ров в та­ких ди­рек­ти­вах обыч­но пи­шут­ся за­глав­ны­ми бу­к­ва­ми – так их про­ще об­на­ру­жить в ко­де. Кро­ме то­го, не бой­тесь вы­пол­нять над ними ариф­ме­ти­че­­ские опе­ра­ции – они то­же вы­пол­ня­ют­ся пре­ком­пи­ля­то­ром.

Скорая помощь

Мат­ри­ца кла­виа­ту­ры не обя­за­на быть квад­рат­ной. На­при­мер, с точ­ки зре­ния ад­ре­са­ции мат­ри­ца мо­жет быть раз­ме­ром 8 × 8, а фи­зи­че­ски она мо­жет быть лю­бой фор­мы.

Вот каким образом сканиру­ет­ся стро­ка:

digitalWrite(SRIN_LATCH, HIGH);

delayMicroseconds(25);

digitalWrite(SRIN_LATCH, LOW);

data = shiftIn(SRIN_DATA, SRIN_CLOCK);

Пе­ре­вод кон­так­та-«за­щел­ки» в «единицу» функ­ци­ей digitalWrite() «за­гру­зит» вход «за­щел­ки». Не­боль­шая за­держ­ка по­мо­жет зна­чениям уста­но­вить­ся, пре­ж­де чем мы сно­ва пе­ре­ве­дем «за­щел­ку» в ноль для «за­мо­роз­ки» вхо­да.

По­сле это­го нуж­но лишь восполь­зо­вать­ся функ­ци­ей shiftin() стан­дарт­ной биб­лио­те­ки Arduino для фик­са­ции зна­чения это­го бай­та. Ес­ли сдви­го­вый ре­гистр не один, то вхо­ды «за­щел­ки» объ­е­ди­ня­ют­ся вме­сте, и по­на­до­бит­ся еще один вы­зов shiftin() для чтения до­полнитель­но­го бай­та дан­ных.

Борь­ба с дре­без­гом кон­так­тов

Од­на из про­блем с фи­зи­че­­ски­­ми пе­ре­клю­ча­те­ля­ми, будь то мем­бран­ные кла­виа­ту­ры, так­тиль­ные кноп­ки или пе­ре­клю­ча­те­ли с са­мо­воз­вра­том лю­бых ти­пов, за­клю­ча­ет­ся в том, что сиг­нал мо­жет «дро­жать». Это тер­мин, ис­поль­зуе­мый для опи­сания эф­фек­та фи­зи­че­­ской при­ро­ды са­мих кон­так­тов, ко­то­рые вклю­че­ны и вы­клю­че­ны не чет­ко. Та­кое бы­ва­ет из-за фи­зи­че­­ской виб­ра­ции кон­так­та, по­это­му, на­при­мер, при от­пускании кла­ви­ши кон­так­ты мо­гут дрог­нуть и сно­ва замк­нуть цепь, пусть и нена­дол­го.

Ес­ли вы хо­ти­те от­фильт­ро­вать по­доб­ные неже­ла­тель­ные со­бы­тия, в принципе это де­ла­ет­ся на уровне элек­троники, но тут мо­гут быть про­бле­мы, так что по­за­бо­тим­ся об этом на уровне про­грам­мы. До­воль­но про­стой и яс­ный спо­соб – про­сто объ­е­динить вхо­дя­щие дан­ные с по­следним на­бо­ром дан­ных, по­лу­чен­ных из мат­ри­цы, по схеме И.

data &= shiftIn(SRIN_DATA, SRIN_CLOCK);

Вре­мя ме­ж­ду сканиро­ванием двух на­бо­ров дан­ных (ес­ли вы не сканируе­те бы­ст­ро – в этом случае мо­жет ис­поль­зо­вать­ся несколь­ко бу­фе­ров) долж­но быть доста­точ­ным для то­го, что­бы со­стояние кла­виш уста­но­ви­лось. Конеч­но, оно долж­но быть и доста­точ­но ко­рот­ким, что­бы из­бе­жать си­туа­ции, когда бы­ст­ро на­жа­тая кла­ви­ша не рас­по­зна­ет­ся. По об­щей рекомендации, вре­мя ме­ж­ду опе­ра­ция­ми сканиро­вания долж­но быть менее 200 мс.

Объ­е­ди­ня­ем все вме­сте

На прак­ти­ке функ­ция чтения кла­виа­ту­ры ско­рее все­го реа­ли­зу­ет­ся как сер­вис пре­ры­ваний. В этом слу­чае нуж­но пра­виль­но за­дать тай­мер (см. LXF155), что­бы пре­ры­вание вы­да­ва­лось доста­точ­но ре­гу­ляр­но для пе­ре­хва­та на­жа­тий кла­виш, но не слиш­ком час­то, что­бы не съе­дать все про­цес­сор­ное вре­мя. Оно за­ви­сит от раз­ме­ра мат­ри­цы и ме­то­да сканиро­вания – помните, что с ростом раз­ме­ра мат­ри­цы вре­мя на ее сканиро­вание уве­ли­чи­ва­ет­ся по квад­ра­тич­ной функ­ции. В при­ме­ре ко­да на DVD есть про­це­ду­ра пре­ры­вания для мат­ри­цы 4 × 4 с ис­поль­зо­ванием сдви­го­вых ре­ги­ст­ров для управ­ления стро­ка­ми и столб­ца­ми.

В за­ви­си­мо­сти от на­жа­тия кла­виш вам так­же нуж­но вы­пол­нять со­от­вет­ст­вую­щие дей­ст­вия и, конеч­но, по­ме­щать дан­ные ту­да, где глав­ная про­грам­ма их най­дет. На­зна­чение кла­виш за­ви­сит от ва­ше­го при­ло­жения, но для хранения на­жа­тий кла­виш удоб­но поль­зо­вать­ся бу­фе­ром buffer и счет­чи­ком count. В основ­ной про­грам­ме мож­но про­ве­рять счет­чик, что­бы уз­нать, бы­ли ли на­жа­ты кла­ви­ши.

if (data > 0){

if (buffercount < BUFFER_LIMIT){

buffercount++;

buffer[buffercount] - data;

}

}

Тща­тель­но по­доб­рав соответствующие ком­по­нен­ты, лег­ко про­ска­ни­ро­вать мат­ри­цу 16 × 16 без осо­бых затрат про­цес­сор­но­го вре­ме­ни.

matrix

Ма­кет – не самое замечательное в мире ме­сто для сбор­ки мат­рич­ной кла­виа­ту­ры: уж слиш­ком мно­го на нем со­единений. Про­ще будет взять мон­таж­ную пла­ту и раз­мес­тить кла­ви­ши на ней (в тех местах, где это необ­хо­ди­мо, до­рож­ки надо будет пе­ре­резать).

Ма­лень­кие мат­ри­цы

В ПО Arduino есть биб­лио­те­ка мат­ри­цы кла­виа­ту­ры, ко­то­рая сканиру­ет мат­ри­цу кла­виш, не вы­зы­ва­ет бло­ки­ров­ки и об­ла­да­ет за­ча­точ­ной об­ра­бот­кой со­бы­тий. Она ра­бо­та­ет с раз­лич­ны­ми кла­виа­ту­ра­ми и ис­поль­зу­ет про­стой ре­жим сканиро­вания, ис­клю­чаю­щий эф­фект лож­но­го сра­ба­ты­вания на кла­виа­ту­рах без дио­дов.

По­че­му мы не восполь­зо­ва­лись ею? Един­ст­вен­ная про­бле­ма в том, что для нее нуж­но по кон­так­ту на ка­ж­дую стро­ку и стол­бец, т. е. для мат­ри­цы 4 × 4 нуж­но во­семь кон­так­тов – за­мет­ная часть вы­во­дов Arduino. Ис­поль­зо­вание сдви­го­вых ре­ги­ст­ров (в основ­ном ко­де) по­зво­ля­ет снизить их ко­ли­че­­ст­во до шес­ти, а при объ­е­динении несколь­ких сдви­го­вых ре­ги­ст­ров мож­но ра­бо­тать с го­раз­до боль­ши­ми мат­ри­ца­ми. |

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