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

LXF158:Arduino: Немного по­шу­мим

Материал из Linuxformat
Версия от 07:27, 16 сентября 2018; Olkol (обсуждение | вклад)

(разн.) ← Предыдущая | Текущая версия (разн.) | Следующая → (разн.)
Перейти к: навигация, поиск

Элек­тро­ни­ка Ва­шей пла­те под си­лу сыг­рать Бет­хо­ве­на... или нечто вро­де

Arduino: Немного по­шу­мим В этой гром­кой ста­тье маэ­ст­ро Ник Вейч пре­вра­ща­ет свой Arduino в му­зы­каль­ный ин­ст­ру­мент, упот­реб­ляя сло­во «на­строй­ка» в бу­к­валь­ном смысле.

Во мно­гих про­ек­тах Arduino – будь то хоровод све­то­дио­дов или кон­так­ты ЖК-эк­ра­на – ве­ду­щую роль иг­ра­ет свет. Это очень ми­ло, но есть дру­гие из­ме­рение, ко­то­ры­м мы по­ка пренеб­рега­ли: это звук. По­ми­гать пе­ред кем-то све­то­дио­да­ми – шту­ка хо­ро­шая, но лю­ди ведь долж­ны на них сна­ча­ла по­смот­реть; пре­лесть же зву­ка в том, что его не так лег­ко иг­но­ри­ро­вать, в чем ско­ро убе­дят­ся ва­ши дру­зья и род­ные.

Соз­да­ем му­зы­ку

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

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

Биб­лио­те­ка то­нов фор­ми­ру­ет пря­мо­уголь­ные ко­ле­бания (т. е. «вкл» и «выкл»), манипу­ли­руя внут­ренними генера­то­ра­ми так­тов мик­ро­схе­мы Atmega. Она про­сто за­да­ет час­то­ту и на­прав­ля­ет ре­зуль­тат на нуж­ный вы­вод. Пря­мо­уголь­ные ко­ле­бания да­ют гру­бый и сы­рой звук, но в этом есть и свои плю­сы. В ре­аль­ном ми­ре вы вряд ли под­клю­чи­те эту схе­му к сво­ей сис­те­ме Hi-Fi, но она пре­крас­но по­дой­дет на роль двер­но­го звон­ка. Пом­ня обо всем этом, да­вай­те по­ко­ре­жим клас­си­че­­ский опус – «Оду к ра­до­сти» Бет­хо­ве­на. Так или ина­че она нам под­хо­дит. Пер­вое, что нам нуж­но знать – как сгенери­ро­вать звук. Это про­сто:

tone(pin, pitch, duration);

Обя­за­тель­ных ар­гу­мен­тов у функ­ции tone два: но­мер вы­во­да (pin) и тон (pitch). Тре­тий, необя­за­тель­ный ар­гу­мент – про­дол­жи­тель­ность зву­чания (duration). Но­мер вы­во­да – обыч­ный но­мер кон­так­та Arduino. Тон – это про­из­воль­но ука­зан­ная час­то­та; воспро­из­во­дить стан­дарт­ные но­ты вы не обя­за­ны.

Для ва­ше­го удоб­ст­ва, в биб­лио­те­ке есть за­го­ло­воч­ный файл с оп­ре­де­ления­ми нот, по­это­му мож­но поль­зо­вать­ся та­ки­ми обо­зна­чения­ми:

tone(7,1568);

tone(7,NOTE_G6);

Оба ва­ри­ан­та вы­зо­ва воспро­из­ве­дут од­ну и ту же но­ту [«соль» средней ок­та­вы, – прим. ред.]. Ес­ли про­дол­жи­тель­ность зву­чания (це­лое чис­ло мил­ли­се­кунд на воспро­из­ве­дение но­ты) не ука­за­на, то в нуж­ный мо­мент при­дет­ся его оста­но­вить:

noTone(pin);

В один мо­мент вре­мени мож­но воспро­из­во­дить несколь­ко нот; в стан­дарт­ном Duemilanove Arduino – до трех, но в боль­шин­ст­ве слу­ча­ев на прак­ти­ке есть ог­раничение в две но­ты. Это свя­за­но с тем, что для по­лу­чения пря­мо­уголь­ной вол­ны ис­поль­зу­ет­ся встро­ен­ный генера­тор так­тов, ко­то­рый так­же обес­пе­чи­ва­ет генера­цию ШИМ-сиг­на­лов и функ­цию внут­реннего тай­ме­ра мил­ли­се­кунд. За­няв его тре­мя но­та­ми, вы боль­ше не смо­же­те от­сле­дить про­хо­ж­дение вре­мени, что мо­жет стать про­бле­мой. Учи­ты­вая ка­че­­ст­во зву­ка, по­лу­чае­мо­го с та­кой схе­мой, час­то доста­точ­но во­об­ще од­но­го ка­на­ла.

Со­хра­ня­ем ме­ло­дии

Итак, зву­ки из­да­вать мы те­перь уме­ем, но как соз­дать му­зы­ку? Для ка­ж­дой но­ты нам нуж­но знать три ве­щи – тон, вре­мя зву­чания и ин­тер­вал до сле­дую­щей но­ты. В при­ме­рах из биб­лио­теки ме­ло­дий эти па­ра­мет­ры хра­нят­ся в раз­лич­ных мас­си­вах, что, с од­ной сто­ро­ны, уп­ро­ща­ет код, а с дру­гой – услож­ня­ет его понимание. В иде­аль­ном слу­чае всю ин­фор­ма­цию о ме­ло­дии нуж­но хранить в од­ном мас­си­ве. Тут нам по­мо­гут струк­ту­ры – struct, это поль­зо­ва­тель­ский тип дан­ных, ко­то­рый лег­ко оп­ре­де­лить и ко­то­рым удоб­но поль­зо­вать­ся. Он при­шел из стан­дарт­но­го язы­ка C, на ко­то­ром, кста­ти, мы и пи­шем боль­шую часть ко­да для Arduino (удив­ле­ны? – а мы вро­де об этом упо­ми­на­ли). Вот син­так­сис объ­яв­ления струк­ту­ры:

struct mydata { <type> <propertyname>;

<type> <propertyname>;

}

Струк­ту­ру для но­ты мож­но ор­га­ни­зо­вать так:

struct note{

uint16_t pitch;

uint8_t duration;

uint8_t pause;

};

note first={880,12,8};

int duration =first.duration*25;

Мы соз­да­ли про­стую струк­ту­ру дан­ных с тре­мя зна­чения­ми. Час­то­та – 16-бит­ное це­лое чис­ло (0 – 65535), а про­дол­жи­тель­ность зву­чания и пау­за – 8-бит­ные чис­ла (0 – 255). Два по­следних зна­чения яв­ля­ют­ся ско­рее услов­ны­ми, чем ис­тин­ны­ми. Вре­мя нуж­но за­да­вать в мил­ли­се­кун­дах в диа­па­зоне где-то от 50 до 2000. Но ес­ли хранить эти зна­чения в 16-бит­ных чис­лах, для хранения каж­дой но­ты по­на­до­бит­ся на 50 % боль­ше мес­та (шесть байт вме­сто че­ты­рех), и мы силь­но раз­ду­ем объ­ем тре­буе­мой па­мя­ти. Так как нам вряд ли нуж­но воспро­из­во­дить но­ты с мил­ли­се­кунд­ной точ­но­стью, про­ще хранить в па­мя­ти мень­шие чис­ла, а при ис­поль­зо­вании ум­но­жать их на ко­эф­фи­ци­ент. Ес­ли ум­но­жать их на 25, по­лу­чит­ся диа­па­зон от 0 до 6375, т. е. бо­лее шес­ти се­кунд, и это­го долж­но быть вполне доста­точ­но.

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

// notes in the melody:

enum { GG,A,B,C,D,E,F,G};

static const uint16_t frequency[] ={

784, // GG

880, //A

988, //B

1047,

1175,

1319,

1397,

1568//G

};

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

Те­перь мож­но пе­рей­ти к ме­ло­дии. Для краткости мы при­ве­дем толь­ко пер­вый такт «Оды к ра­до­сти», но в лис­тин­ге DVD при­ве­де­на вся ме­ло­дия:

note tune[] ={ {E,10,12},{E,10,12},{F,10,12} ,{G,10,12},

{G,10,12},{F,10,12},{E,10,12} ,{D,10,12},

{C,10,12},{C,10,12},{D,10,12} ,{E,10,12},

{E,12,14},{D,7,8},{D,24,32},

Про­бе­лы при пар­син­ге ко­да не воспринимаются, и вы мо­же­те от­фор­ма­ти­ро­вать свой код так, что­бы его бы­ло про­ще чи­тать или ре­дак­ти­ро­вать. Для ка­ж­дой но­ты при­ве­де­ны час­то­та, про­дол­жи­тель­ность и пау­за. Пау­за всегда длиннее про­дол­жи­тель­но­сти, по­то­му что в ко­де мы сде­ла­ем так, чтобы пау­за вклю­чала вре­мя воспро­из­ве­дения всей но­ты. На­конец, нам ну­жен код для воспро­из­ве­дения ме­ло­дии:

void loop() {

for (int i = 0; i < (sizeof(tune)/3); i++) {

tone(7, frequency[tune[i].pitch], tune[i].duration*25);

delay(tune[i].pause*25);

}

delay(15000);

}

Этот код, ра­зу­ме­ет­ся, сто­ит обер­нуть в соб­ст­вен­ную функ­цию, а не по­вто­рять все вре­мя в глав­ном цик­ле. Хо­тя в этих стро­ках мно­го че­го про­ис­хо­дит, код оче­ви­ден.

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

На­страи­ва­ем­ся

В сле­дую­щей стро­ке мы воспро­из­во­дим но­ту с по­мо­щью функ­ции биб­лио­те­ки то­нов. Мы вы­да­ем ее на вы­вод 7 (он не ху­же лю­бо­го дру­го­го), и это пер­вый ар­гу­мент функ­ции. Час­то­ту но­ты возь­мем из соз­дан­но­го ранее мас­си­ва. В ка­че­­ст­ве ин­дек­са мы ис­поль­зу­ем зна­чения из пе­ре­чис­ления, ко­то­рые хра­нят­ся в на­шем мас­си­ве tune в по­ле струк­ту­ры pitch. Так, tune[i].pitch со­дер­жит чис­ло от 0 до 7 (числами мы обо­зна­чи­ли но­ты, для про­стоты про­чтения), ко­то­рое ис­поль­зу­ет­ся в ка­че­­ст­ве ин­дек­са мас­си­ва, где хранятся на­стоя­щие час­то­ты.

По­следний нуж­ный нам па­ра­метр – про­дол­жи­тель­ность зву­чания. Он дол­жен быть в мил­ли­се­кун­дах, но мы для эко­но­мии па­мя­ти де­ли­ли зна­чения на 25, и те­перь нуж­но ум­но­жать их на 25.

Пау­за ме­ж­ду но­та­ми реа­ли­зу­ет­ся стан­дарт­ной функ­ци­ей delay(), ко­то­рая ана­ло­гич­ным об­ра­зом ис­поль­зу­ет дан­ные из мас­си­ва tune. Что­бы га­ран­ти­ро­ван­но пре­рвать воспро­из­ве­дение, мож­но бы­ло вы­звать функ­цию noTone(7), но на прак­ти­ке в этом нет необ­хо­ди­мо­сти.

Для воспро­из­ве­дения ме­ло­дии мож­но под­клю­чить неболь­шой ди­на­мик пря­мо к вы­во­дам Arduino. Но что­бы не по­вре­дить сам ди­на­мик, по­на­до­бит­ся неболь­шой ре­зи­стор – где-то в диа­па­зоне от 100 Ом до 1 кОм. Ес­ли вы ис­поль­зуе­те пье­зо­эле­мент или пье­зо­ди­на­мик, без ре­зи­сто­ра мож­но и обой­тись: они час­то об­ла­да­ют боль­шим внут­ренним со­про­тив­лением, рас­счи­тан­ным на сиг­на­лы ло­ги­че­­ских уровней. Фор­му­ла, ко­то­рую вам нуж­но знать, вы­гля­дит так:

P =

248868.png Vpeak2

2 R

Это все­го лишь при­бли­жение! Ди­на­ми­ки уст­рое­ны сложнее, чем вы ду­ма­ли – они со­дер­жат ин­дук­тив­ную на­груз­ку, и в оп­ре­де­лении мощ­но­сти по от­но­шению к ди­на­ми­ку есть мас­са тон­ко­стей. Нам же важ­но про­сто его не сжечь, по­это­му доста­точ­но и при­бли­жен­ной фор­му­лы; но тре­бу­ют­ся ме­ры пре­досто­рож­но­сти. В дан­ном слу­чае R – ре­зи­стив­ная на­груз­ка (со­про­тив­ление ди­на­ми­ка долж­но быть ука­за­но на его задней по­верх­но­сти – обыч­но это 4, 8 или 16 Ом), а V – пи­ко­вое на­пря­жение – в на­шем слу­чае 5 В без ис­поль­зо­вания уси­ли­те­ля мощ­но­сти. С на­груз­кой 16 Ом это даст нам при­мер­но 1,5 Вт.

Пре­дот­вра­ща­ем пе­ре­груз­ку

Что про­изой­дет при пе­ре­груз­ке ди­на­ми­ка? Пье­зо­эле­мент, ес­ли не до­хо­дить до бес­пре­де­ла, про­сто бу­дет из­да­вать ужас­ные зву­ки, по­ка не вый­дет из строя, но вообще-то на удив­ление вы­носли­в. В обыч­ном ди­на­ми­ке с бу­маж­ным ко­ну­сом и сер­дечником вы ли­бо по­вре­ди­те сер­дечник, ли­бо рас­тря­се­те его на час­ти, ли­бо по­вре­ди­те ко­нус – а час­то и то, и дру­гое, и третье; по­это­му здесь то­же нуж­но при­нять ме­ры пре­досто­рож­но­сти. Из-за спо­со­ба сво­его из­го­тов­ления ди­на­ми­ки чре­ва­ты так­же опас­но­стью для Arduino. Кро­шеч­ный пье­зо­эле­мент не при­чинит ему вре­да, но чем боль­ше ин­дук­тив­ность, тем боль­ше шу­мы скач­ков на­пря­жения и тем боль­ше мощ­ность це­пи. Хо­тя в це­пях для управ­ления ди­на­ми­ками с по­мо­щью Arduino, ко­то­рые вам мо­гут встре­тить­ся, его обыч­но нет, ре­ко­мен­ду­ет­ся до­ба­вить раз­вя­зы­ваю­щий кон­ден­са­тор ем­ко­стью око­ло 250 мкФ, под­клю­чив его вы­ход к «плю­су» ди­на­ми­ка. Он не толь­ко сгла­жи­ва­ет пе­ре­ход­ные про­цес­сы, но и по­гло­ща­ет по­сто­ян­ную ком­понен­ту сиг­на­ла, и сер­дечник бу­дет пе­ре­ме­щать­ся в обо­их на­прав­лениях, как и пред­пи­са­но его при­ро­дой.

До­полнитель­ный звук

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

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

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

Reactance(X) =

248871.png 2πfC

1

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

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

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

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

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