این مطلب بخش هفتم از آموزش جامع آردوینو (مبتدی و پیشرفته) است. در این آموزش با ابزاری که معمولا برای تولید صدا با آردوینو استفاده میشود یا همان بازر (Buzzer) آشنا خواهید شد، نحوه استفاده از سیگنال دیجیتال برای تولید صدا به چند روش را خواهید آموخت و در نهایت خواهید توانست یک آهنگ ساده را با بازر بنوازید. شما میتوانید با استفاده از بازر یک هشداردهنده وضعیت آب و هوا برای گلخانه بسازید، یا اینکه با ترکیب آن با فاصلهسنج آلتراسونیک، تغییرات فاصله خودروتان با موانع را با تغییر صدا اعلام کنید.
قطعات مورد نیاز:
معرفی پیزوالکتریک و نحوه ایجاد صوت
صوت شنیداری یک موج فشاری است که از طریق هوا منتقل میشود. شکل این موج به صورت سینوسی است. دامنه این موج، میزان بلندی صدا را تعیین میکند. هرچه این دامنه بیشتر باشد، صدا بلندتر خواهد بود و اصلاحا Volume صدا بیشتر است. فرکانس موج صوتی، میزان زیر و بم بودن صدا را مشخص میکند. هرچه فرکانس موج بالاتر باشد، صدا زیرتر شنیده خواهد شد. موج صوتی میتواند هر فرکانسی داشته باشد، اما برای آنکه شنیده شود باید در محدوده شنوایی انسان یعنی بین 20Hz تا 20kHz باشد.
در بیشتر وسایل تولید صوت، از قطعهای از جنس پیزوالکتریک استفاده میشود. پیزوالکتریک مادهای است که در صورت اعمال ولتاژ الکتریکی به آن، تغییر شکل میدهد. اگر به صورت تناوبی به پیزوالکتریک ولتاژ الکتریکی اعمال شود، شروع به منقبض و منبسط شدن کرده و این باعث میشود هوای اطراف خود را نیز منقبض و منبسط کند که یک موج فشاری در هوا ایجاد کرده و در صورتی که در محدوده شنوایی باشد، به صورت صدا شنیده میشود. خود ماده پیزوالکتریک صوت قویای ایجاد نمیکند و به همین دلیل معمولا به یک صفحه ثانویه چسبانده شده و نوسان آن صفحه صدای اصلی را تولید میکند.
اکثر تجهیزاتی که تولید صدا میکنند، مانند بلندگو، بازر و هدفون از همین مکانیزم استفاده میکنند. اگر یک بلندگو را باز کنید احتمالا خواهید دید که درون آن یک پیزودیسک مانند شکل زیر وجود دارد.
تغییر شکل این پیزودیسک هنگام اعمال ولتاژ شبیه به نوسانات یک طبل هنگام نواختن است. در بعضی موارد این پیزودیسک به نوبه خود یک دیافراگم انعطافپذیر را به نوسان در آورده و دیافراگم صوت اصلی را تولید میکند. بلندگوهای قویتر بجای پیزودیسک از یک هسته آهنربایی برای تولید صوت استفاده میکنند.
راه اندازی بازر
کار با بازر بسیار ساده است. در قسمت پائین بازر دو پایه وجود دارد. پایه بلندتر، مثبت و پایه کوتاهتر منفی است.
بنابراین کافی است که پایه منفی را به GND آردوینو و پایه مثبت را با یک مقاومت220Ω به یک پین دیجیتال وصل کنید. این مقاومت برای محافظت در برابر جریان زیاد است. با افزایش ظرفیت مقاومت میتوانید بلندی صدای بازر را کاهش دهید. حتی برای راحتی میتوانید بازر را مستقیما و بدون استفاده از برد بورد به آردوینو متصل کنید. در صورتی که به پایه بازر ولتاژ High اعمال شود، یک صوت با فرکانس طبیعی خود ایجاد میکند و در صورتی که ولتاژ Low اعمال شود، صدا قطع خواهد شد. به این ترتیب بازر تنها دو حالت صدایی خواهد داشت. با این وجود آیا بازر میتواند صداهای متفاوت تولید کند؟ بله! اما برخلاف انتظار اولیه، این کار به روش PWM نیز نمیتواند صورت بگیرد. با کمی دقت متوجه دلیل آن میشوید: PWM نسبت طول زمانی که پایه دیجیتال روشن است به زمان خاموشی آن را تعیین میکند و نه فرکانس سیگنال را. برای اینکه فرکانس سیگنال تغییر کند، باید سرعت خاموش و روشن شدن آن را تغییر دهید. موج صوتی، یک موج سینوسی پیوسته است و با سیگنال دیجیتال نمیتوان دقیقا آن را تولید کرد. در عوض با سیگنال دیجیتال میتوان با روشن و خاموش کردن متناوب پایه، موج مربعی تولید کرد که تقریبی از موج سینوسی است. حال اگر سرعت خاموش و روشن کردن پایه دیجیتال را تغییر دهید، فرکانس موج خروجی تغییر میکند.
سیگنال مربعی تخمین خیلی دقیقی از موج سینوسی نیست و به همین دلیل صدای شنیده شده از بازر کمی با صداهای واقعی تفاوت دارد.
تولید یک صوت ساده
برای شروع سعی کنید که یک صدا با استفاده از بازر تولید کنید. مثلا میتوانید یک صدای آژیر مانند ایجاد کنید به این صورت که یک ثانیه بازر روشن و یک ثانیه خاموش باشد و این عمل تکرار شود. پایه منفی بازر را به GND و پایه مثبت را با یک مقاومت220Ω به پین ٩ دیجیتال وصل کنید.
با استفاده از دستور digitalWrite پایه ٩ را در وضعیت High قرار دهید؛ به مدت ١ ثانیه صبر کنید و سپس همان پایه را در وضعیت Low قرار دهید و مجددا ١ ثانیه منتظر بمانید. برای این کار میتوانید کد زیر را در آردوینو بارگذاری کرده و نتیجه را بشنوید.
/*
automee
Arduino Tutorial Series
Author: Davood Dorostkar
Website: www.automee.ir
*/
const int buzzerPin=9;
int duration=1000; //in miliseconds
int rest=2000; //in miliseconds
void setup(){
pinMode(buzzerPin,OUTPUT);
}
void loop() {
digitalWrite(buzzerPin,HIGH);
delay(duration);
digitalWrite(buzzerPin,LOW);
delay(rest);
}
تولید صوت با استفاده از سیگنال مربعی
همان طور که قبلا توضیح داده شد، ابتداییترین روش تولید صدا با فرکانس مشخص استفاده از سیگنالهای مربعی است. اتصالات قطعات را مانند حالت قبل نگه دارید. از آنجا که میخواهیم مجموعهای از عملیاتهای تکراری را انجام دهیم، بهتر است که یک تابع برای ایجاد صوت تعریف کنیم.
تابع در آردوینو
در آردوینو تابع به تعدادی از عملیاتهای پشت سر هم گفته میشود که به صورت یک مجموعه واحد تعریف شده و به یک نتیجه خاص منجر میشود. نحوه نگارش تابع شکل خاصی دارد. هر تابع با اسم خاصی مشخص میشود و در تمام طول برنامه با آن نام شناخته میشود. تعریف تابع باید خارج از بخشهای setup و loop صورت بگیرد. شکل کلی تعریف تابع به صورت زیر است:
int functionName (int input1, int input2, …){
//do something
return output;
}
قواعد کلی توابع به طور خلاصه به این صورت است:
- هر تابع نام یکتایی دارد
- بعد از نام تابع پرانتر () قرار میگیرد و درون پرانتزها ورودی تابع نوشته میشود؛ تابع میتواند بدون ورودی باشد
- بعد از پرانتز، آکولاد {} قرار میگیرد و عملیاتهای تابع درون آن نوشته میشود
- نوع متغیر خروجی تابع قبل از نام تابع تعیین میشود (... ,long, int)؛ تابع میتواند بدون خروجی باشد، در این صورت نوع تابع با void مشخص میشود؛ تعیین خروجی تابع با دستور return مشخص میشود
در اینجا ما یک تابع نیاز داریم که ورودی آن پایه متصل به بازر، فرکانس صوت و طول زمان ایجاد صوت باشد و یک سیگنال مربعی با فرکانس و مدت خواسته شده به پایه بازر ارسال کند. برای این کار به صورت زیر عمل میکنیم:
تعداد تناوب موج صوتی برابر است با فرکانس موج ضرب در زمان کل:
همچنین طول مدت هر تناوب برابر است با عکس فرکانس:
این نکته را هم در نظر داشته باشید که هر تناوب شامل دو بخش قله و دره (High و Low) است. بنابراین نصف طول هر تناوب به هرکدام از این دو بخش اختصاص میگیرد.
دستور دیگری که در این قسمت استفاده شده است، delayMicroseconds است. این دستور مشابه delay عمل میکند با این تفاوت که ورودی آن بر حسب میکروثانیه (به جای میلیثانیه) است. بنابراین تابع تولید موج مربعی به صورت زیر خواهد بود:
void buzzer(int buzzerPin,int frequency,int duration){
unsigned long cycles=frequency*duration;
unsigned int period=1000000/frequency; //in microseconds
for(int i=0;i<cycles;i++){
digitalWrite(buzzerPin,HIGH);
delayMicroseconds(period/2);
digitalWrite(buzzerPin,LOW);
delayMicroseconds(period/2);
}
}
به بخشهای مختلف تابع و نیز نحوه ایجاد موج دقت کنید و آن را با آنچه در بالا در مورد تعریف توابع گفته شد تطبیق دهید و شکل کلی آن را به خاطر بسپارید چرا که تابع یکی از بخشهای مهم برنامهنویسی است و خیلی به آن نیاز پیدا خواهید کرد.
مطلب آخری که در مورد توابع اهمیت دارد نحوه استفاده از آن است. در صورتی که تابع بدون خروجی باشد، نوشتن نام تابع به همراه ورودیهای آن، تابع را یک بار اجرا میکند. مثلا:
functionName(input1, inpt2, …)
در صورتی که تابع دارای خروجی باشد، باید تابع را به یک متغیر اختصاص دهید. در این صورت خروجی تابع در آن متغیر ذخیره خواهد شد. مانند زیر:
Variable=functionName(input1, inpt2, …)
کد زیر را در آردوینو بارگذاری کنید. این برنامه یک صوت با فرکانس ٥٠٠ هرتز به مدت ١ ثانیه تولید کرده، ٢ ثانیه مکث کرده و دوباره همان کار را تکرار میکند:
/*
automee
Arduino Tutorial Series
Author: Davood Dorostkar
Website: www.automee.ir
*/
const int buzzerPin=9;
unsigned int frequency=500; // in Hertz
int duration=1000; //in miliseconds
int rest=2000; //in miliseconds
void setup(){
pinMode(buzzerPin,OUTPUT);
}
void loop() {
buzzer(buzzerPin,frequency,duration);
delay(rest);
}
void buzzer(int buzzerPin,int frequency,int duration){
unsigned long cycles=frequency*duration;
unsigned int period=1000000/frequency; //in microseconds
for(int i=0;i<cycles;i++){
digitalWrite(buzzerPin,HIGH);
delayMicroseconds(period/2);
digitalWrite(buzzerPin,LOW);
delayMicroseconds(period/2);
}
}
تولید صوت با استفاده از دستور tone
در بخش قبل یک صوت را به کمک سیگنال مربعی به صورت دستی ایجاد کردیم. اگر بخواهید کارهای حرفهایتری انجام دهید دانستن نحوه ایجاد صوت به شما کمک زیادی خواهد کرد. با این وجود اگر فقط نیاز به ایجاد یک صوت ساده با فرکانس و مدت مشخص داشته باشید، در آردوینو دستور tone مخصوص به این کار تعریف شده است. دستور tone دارای ٢ یا ٣ ورودی است. ورودی اول پایه بازر، ورودی دوم فرکانس صوت بر حسب هرتز و ورودی سوم که اختیاری است مدت زمان ایجاد صوت بر حسب میلیثانیه است. فرکانس یک عدد صحیح از نوع unsigned int است و برای دستور tone مقدارهای بین ٣١ تا ٦٥٥٣٥ قابل قبول است. بعضی از انواع متغیرها مانند int و long دارای نوع unsigned هستند که به معنی پذیرفتن اعداد مثبت و صفر است. در صورتی که از دستور tone استفاده میکنید نیازی به تعریف پین مربوطه در بخش setup نیست.
tone(pin,frequency,duration)
برای استفاده از دستور tone با سه ورودی، نکته مهمی وجود دارد. فرض کنید بخواهید صوت به مدت ١ ثانیه پخش شود، ٢ ثانیه تاخیر ایجاد شود و سپس دستور بعد اجرا شود. اگر این کار را به صورت زیر بنویسید، خواهید دید که فقط ١ ثانیه تاخیر ایجاد خواهد شد:
tone(pin,frequency,1000);
delay(2000);
دلیل این موضوع آن است که دستور tone از یک تایمر داخلی میکروکنترلر استفاده میکند و این باعث میشود که تقریبا همزمان دستور tone و delay شروع شود و در حالی که تاخیر در حال سپری شدن است، صوت هم در حال پخش باشد. بنابراین ١ ثانیه از زمان تاخیر صوت پخش شده و فقط ١ ثانیه سکوت واقعی داشته باشیم.
برای حل این مشکل دو راه وجود دارد: اول اینکه مدت زمان صوت را به زمان تاخیر مورد نیاز اضافه کنید. مثلا برای 2 ثانیه سکوت:
tone(pin,frequency,1000);
delay(3000);
دوم اینکه از دستور tone با ٢ ورودی استفاده کنید. در این صورت باید پس از مدت زمان پخش صوت از دستور دیگری به نام noTone برای خاموش کردن صوت استفاده کنید. یعنی حالت قبل به این صورت خواهد بود:
tone(pin,frequency);
delay(1000);
noTone(pin);
delay(2000);
در کنار مزایای گفته شده، استفاده از دستور tone چند محدودیت نیز دارد که عبارتند از:
- امکان استفاده از دستور tone هنگامی که همزمان بر روی پینهای ٣ یا ١١ ارتباط PWM برقرار است (بجز در آردوینو Mega) وجود ندارد. این به خاطر این است که آردوینو از همان تایمری برای دستور tone استفاده میکند که دستور analogWrite را بر روی این دو پین پیادهسازی میکند. در صورت استفاده همزمان، نتایج دچار اختلال میشود.
- فرکانس ورودی در آردوینو UNO میتواند در بازه ٣١ تا ٦٥٥٣٥ هرتز باشد. فرکانسهای کمتر از ٣١ نتایج نادرستی خواهد داد. این مقدار برای بعضی بردها متفاوت است.
- امکان استفاده از دستور tone به صورت همزمان برای دو پین وجود ندارد. برای اینکه بتوانید دستور tone را روی یک پین دیگر اجرا کنید، باید ابتدا با دستور noTone پین قبل را غیرفعال کرده و سپس پین جدید را فعال کنید.
کد زیر را در آردوینو بارگذاری کنید. این برنامه یک صوت آژیرمانند را ایجاد میکند. صوت ایجاد شده از فرکانس ٢٠٠ هرتز تا ٢٥٠٠ هرتز افزایش یافته و سپس کاهش یافته و مجددا به ٢٠٠ هرتز میرسد. این کار در ٣ ثانیه صورت گرفته و پس از آن ٢ ثانیه سکوت ایجاد میشود.
/*
automee
Arduino Tutorial Series
Author: Davood Dorostkar
Website: www.automee.ir
*/
const int buzzerPin=9;
float maxFrequency=2500; // in Hertz
float minFrequency=200;
float duration=3000; // in miliseconds
float timeStep=1; //in miliseconds
int rest=2000; // in miliseconds
#define frequencydelta (maxFrequency-minFrequency)
#define frequencyStep timeStep*frequencydelta/(duration/2)
void setup(){
}
void loop(){
for(float i=minFrequency;i<maxFrequency;i+=frequencyStep)
{
tone(buzzerPin,i);
delay(timeStep);
}
for(float i=maxFrequency;i>minFrequency;i-=frequencyStep)
{
tone(buzzerPin,i);
delay(timeStep);
}
noTone(buzzerPin);
delay(rest);
}
ایجاد آهنگ با بازر
تا اینجا با نحوه کار با بازر به روشهای مختلف آشنا شدید. زمانی متوجه جذابیت این کار میشوید که بتوانید با آن یک آهنگ را پخش کنید. قبل از آن باید کمی با نتهای موسیقی آشنا شوید.
نتهای موسیقی
هر نت موسیقی با یک عنوان مشخص میشود. برای مثال نتهای اصلی شامل Do, Re, Mi, Fa, Sol, La, Si هستند. هر نت در موسیقی معرف یک صوت با فرکانس منحصر به فرد است. فرکانسهای بالاتر صداهای زیرتر و فرکانسهای پائینتر صداهای بمتری هستند. هر نت را میتوان در گامهای مختلفی نواخت؛ گامهای بالاتر نیز به نوبه خود فرکانس بالاتری از گامهای پائینتر دارند. به علاوه بین هر دو نت نیز بعضا یک نت میانی تعریف میشود که به آن نیمپرده گفته میشود. به این ترتیب تعداد بسیار زیادی نت در موسیقی قابل تعریف است که هر کدام فرکانس مشخصی دارد.
پارامتر دیگری که در قطعه موسیقی مهم است، تمپو است که نشان دهنده تعداد ضرب در هر دقیقه است. هر چه عدد تمپو بیشتر باشد، سرعت نواختن موسیقی بیشتر میشود. علاوه بر فرکانس، در یک قطعه موسیقی هر نت دارای یک مدت زمانی است که نشان میدهد آن نت به چه مدت نواخته میشود. طول هر نت میتواند یک نت کامل (4 ضرب تمپو)، ½ نت، ¼ نت و … باشد.
در این قسمت میخواهیم یک قطعه موسیقی را با استفاده از بازر و آردوینو ایجاد کنیم. برای این کار ابتدا باید فرکانس نتهای استفاده شده را تعریف کنیم. پس از آن دو آرایه تعریف میکنیم که در اولی نتهای پخش شده به ترتیب و در دومی طول زمان هر نت نوشته شده است. همچنین برای اینکه نتها به صورت جدا از هم پخش شوند، یک زمان سکوت کوتاه بین هر دو نت تعریف شده است. طول نتها و سکوت میانی آنها تابعی از تمپوی آهنگ است که در ابتدای برنامه تعریف شده است. برنامه زیر یک قطعه معروف را اجرا میکند. در این برنامه یک حلقه تعریف شده است که به ترتیب نتهای آهنگ را با دستور tone پخش کرده و بعد از هر نت سکوت متناظر با آن را ایجاد میکند.
/*
automee
Arduino Tutorial Series
Author: Davood Dorostkar
Website: www.automee.ir
*/
#define buzzerPin 9
#define e4 329.63 // define note pitches
#define d4s 311.13
#define b3 246.94
#define d4 293.66
#define c4 261.63
#define a3 220
#define d3 146.83
#define f3 174.61
#define a3s 233.08
#define g3 196
#define f4 349.23
#define e3 164.81
float tempo = 150;
#define beat 60000 / tempo
// describing music notes
float notes[] = {e4, d4s, e4, d4s, e4, b3, d4, c4, a3, d3, f3, a3, b3, f3, a3s, b3, c4,
e4, d4s, e4, d4s, e4, b3, d4, c4, a3, d3, f3, a3, b3, f3, c4, b3, a3,
b3, c4, d4, e4, g3, f4, e4, d4, e3, e4, d4, c4, d3, d4, c4, b3, e4,
d4s, e4, d4s, e4, b3, d4, c4, a3, d3, f3, a3, b3, f3, a3, b3, c4, e4,
d4s, e4, d4s, e4, b3, d4, c4, a3, d3, f3, a3, b3, f3, c4, b3, a3};
float durations[] = {3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 3, 3, 3,
3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1,
3, 3, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3,
3, 3, 3, 3, 3, 3, 1, 3, 3, 3, 1, 3, 3, 3, 1};
int rests[] = {350, 350, 350, 350, 350, 400, 400, 400, 1000, 350, 400, 400, 1000, 400,
400, 400, 1300, 400, 400, 400, 400, 400, 400, 400, 400, 1000, 400, 400,
400, 1000, 400, 400, 400, 1000, 400, 400, 400, 1000, 400, 400, 400, 1000,
400, 400, 400, 1000, 400, 400, 400, 1400, 400, 350, 350, 350, 350, 400,
400, 400, 1000, 350, 400, 400, 1000, 400, 400, 400, 1300, 400, 400, 400,
400, 400, 400, 400, 400, 1000, 400, 400, 400, 1000, 400, 400, 400, 1000};
void setup()
{
pinMode(buzzerPin, OUTPUT);
}
void loop()
{
for (int i = 0; i < (sizeof(notes) / sizeof(double)); i++)
{
tone(buzzerPin, notes[i], beat / durations[i]);
delay(rests[i] * (100 / tempo));
}
delay(5000);
}
نتیجهگیری
در این آموزش کار با بازر و نحوه ساختن صوت با سیگنال دیجیتال را آموختید و همچنین توانستید یک قطعه موسیقی را با آردوینو بازسازی کنید. اگر علاقه دارید که آهنگ مورد علاقهتان را با آردوینو بسازید، دست به کار شوید؛ چرا که نت بسیاری از قطعات موسیقی را میتوانید در اینترنت پیدا کنید.
در آموزش بعدی، نحوه راهاندازی و کار با السیدی کاراکتری را خواهید آموخت. نظرات شما باعث بهبود محتوای آموزشی ما میشود. اگر این آموزش را دوست داشتید، همینطور اگر سوالی در مورد آن دارید، از شنیدن نظراتتان خوشحال خواهیم شد.