
آموزش حلقه های while و do-while در آردوینو
مقدمه
در برنامه نویسی آردوینو، گاهی لازم است یک دستور یا مجموعه ای از دستورات چندین بار اجرا شوند. برای این کار از حلقه ها (Loops) استفاده می کنیم.
تا اینجا ممکن است با حلقه for آشنا شده باشید. این حلقه معمولا زمانی کاربرد دارد که تعداد تکرارها مشخص باشد؛ مثلا روشن کردن ۸ LED به ترتیب یا اجرای یک دستور ۱۰ بار.
اما در بسیاری از پروژه ها، تعداد تکرارها از ابتدا مشخص نیست. برای مثال:
- تا زمانی که دکمه فشرده است، یک عملیات انجام شود.
- تا زمانی که مقدار سنسور به حد مشخصی نرسیده است، بررسی ادامه پیدا کند.
- تا زمانی که داده ای از ورودی دریافت نشده است، برنامه منتظر بماند.
- یک منو تا زمان دریافت فرمان کاربر فعال بماند.
در چنین شرایطی، حلقه های while و do-while می توانند مفید باشند.
۱. حلقه while چیست؟
حلقه while مجموعه ای از دستورات را تا زمانی اجرا می کند که شرط آن درست باشد.
ساختار کلی:
while (condition) {
// دستورات تکرارشونده
}در این ساختار، ابتدا شرط بررسی می شود. اگر شرط درست باشد، دستورات داخل حلقه اجرا می شوند. پس از پایان دستورات، شرط دوباره بررسی می شود.
اگر شرط نادرست باشد، اجرای حلقه متوقف می شود و برنامه به دستور بعد از حلقه می رود.
مثال ساده
int counter = 0;
void setup() {
Serial.begin(9600);
while (counter < 5) {
Serial.println(counter);
counter++;
}
}
void loop() {
}خروجی در Serial Monitor
0
1
2
3
4توضیح کد
- متغیر
counterدر ابتدا برابر صفر است.
- شرط
counter < 5بررسی می شود.
- اگر شرط درست باشد، مقدار متغیر چاپ می شود.
- دستور
counter++مقدار متغیر را یک واحد افزایش می دهد.
- حلقه تا زمانی ادامه پیدا می کند که مقدار
counterبه ۵ برسد.
وقتی مقدار متغیر برابر ۵ شود، شرط نادرست خواهد شد و حلقه پایان می یابد.
نکته: در این مثال، حلقه داخل تابع setup() قرار دارد؛ بنابراین فقط یک بار، هنگام راه اندازی برد اجرا می شود. در پروژه های واقعی می توان از while در داخل loop() یا توابع دیگر نیز استفاده کرد.
۲. حلقه while چگونه کار می کند؟
مهم ترین ویژگی حلقه while این است که شرط را پیش از اجرای دستورات بررسی می کند.
بنابراین ممکن است دستورات داخل حلقه حتی یک بار هم اجرا نشوند.
برای مثال:
int counter = 10;
void setup() {
Serial.begin(9600);
while (counter < 5) {
Serial.println(counter);
counter++;
}
Serial.println("Loop finished");
}
void loop() {
}در این برنامه، مقدار counter از ابتدا ۱۰ است. چون شرط counter < 5 نادرست است، بدنه حلقه اجرا نمی شود و فقط پیام زیر نمایش داده می شود:
Loop finishedاین رفتار زمانی اهمیت دارد که اجرای دستورات داخل حلقه به برقرار بودن یک شرط وابسته باشد.
۳. حلقه do-while چیست؟
حلقه do-while نیز مانند while برای تکرار دستورات استفاده می شود؛ اما یک تفاوت مهم دارد: ابتدا دستورات را اجرا می کند و سپس شرط را بررسی می کند.
ساختار کلی:
do {
// دستورات تکرارشونده
} while (condition);در پایان ساختار do-while باید بعد از پرانتز شرط، علامت سمی کالن ; قرار گیرد.
مثال ساده
int counter = 10;
void setup() {
Serial.begin(9600);
do {
Serial.println(counter);
counter++;
} while (counter < 5);
}
void loop() {
}خروجی
10چرا فقط عدد ۱۰ چاپ می شود؟
زیرا دستورات داخل do پیش از بررسی شرط اجرا می شوند. پس از چاپ عدد ۱۰ و افزایش متغیر به ۱۱، شرط counter < 5 بررسی می شود. این شرط نادرست است؛ بنابراین حلقه پایان می یابد.
در نتیجه، حتی اگر شرط از ابتدا نادرست باشد، حلقه do-while حداقل یک بار اجرا خواهد شد.
۴. تفاوت while و do-while
تفاوت اصلی این دو حلقه در زمان بررسی شرط است.
| ویژگی | while | do-while |
|---|---|---|
| زمان بررسی شرط | قبل از اجرای دستورات | بعد از اجرای دستورات |
| حداقل تعداد اجرا | صفر بار | یک بار |
| کاربرد متداول | تکرار تا برقرار بودن شرط | اجرای اولیه و سپس بررسی شرط |
| علامت سمی کالن بعد از شرط | لازم نیست | لازم است |
مقایسه با یک مثال
در این مثال، هر دو حلقه با شرطی شروع می شوند که از ابتدا نادرست است.
void setup() {
Serial.begin(9600);
int a = 10;
while (a < 5) {
Serial.println("while");
}
int b = 10;
do {
Serial.println("do-while");
} while (b < 5);
}
void loop() {
}خروجی:
do-whileحلقه while اجرا نمی شود، اما حلقه do-while یک بار دستورات خود را اجرا می کند.
۵. استفاده از while برای خواندن دکمه
یکی از کاربردهای حلقه while، اجرای یک بخش از برنامه تا زمان رها شدن دکمه است.
فرض کنید دکمه ای بین پایه دیجیتال ۲ و GND متصل شده و از مقاومت پولآپ داخلی استفاده می کنیم.
در این حالت:
- دکمه رها است:
HIGH - دکمه فشرده است:
LOW
کد نمونه
const int buttonPin = 2;
const int ledPin = LED_BUILTIN;
void setup() {
pinMode(buttonPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
}
void loop() {
if (digitalRead(buttonPin) == LOW) {
digitalWrite(ledPin, HIGH);
while (digitalRead(buttonPin) == LOW) {
// منتظر رها شدن دکمه می مانیم
}
digitalWrite(ledPin, LOW);
}
}توضیح عملکرد
- برنامه بررسی می کند که آیا دکمه فشرده شده است یا خیر.
- اگر دکمه فشرده شده باشد، LED روشن می شود.
- حلقه
whileتا زمانی ادامه پیدا می کند که دکمه همچنان فشرده باشد.
- پس از رها شدن دکمه، حلقه پایان می یابد و LED خاموش می شود.
نکته مهم: این مثال ساده برای آموزش است. حلقه انتظار، اجرای سایر دستورات برنامه را تا زمان رها شدن دکمه متوقف می کند. همچنین برای تشخیص دقیق فشار و رها شدن دکمه باید لرزش مکانیکی کنتاکت ها (Debounce) را در نظر گرفت.
۶. استفاده از do-while برای دریافت ورودی
حلقه do-while زمانی مناسب است که بخواهیم یک عملیات حداقل یک بار اجرا شود و سپس درباره ادامه آن تصمیم بگیریم.
برای مثال، می توانیم یک مقدار را از ورودی سریال دریافت کنیم و تا زمانی که مقدار معتبر نشده است، از کاربر بخواهیم دوباره آن را وارد کند.
مثال زیر یک نمونه ساده از بررسی ورودی عددی است:
int value;
void setup() {
Serial.begin(9600);
do {
Serial.println("Enter a number from 1 to 10:");
while (Serial.available() == 0) {
// منتظر دریافت داده از Serial Monitor می مانیم
}
value = Serial.parseInt();
// پاک کردن داده های باقی مانده در ورودی سریال
while (Serial.available() > 0) {
Serial.read();
}
} while (value < 1 || value > 10);
Serial.print("Valid value: ");
Serial.println(value);
}
void loop() {
}روش آزمایش
- برنامه را روی برد بارگذاری کنید.
- Serial Monitor را با سرعت 9600 باز کنید.
- مقدار عددی بین ۱ تا ۱۰ وارد کنید و ارسال را انجام دهید.
- اگر عدد خارج از این محدوده باشد، پیام درخواست ورودی دوباره نمایش داده می شود.
- پس از وارد کردن عدد معتبر، برنامه مقدار را نمایش می دهد.
توجه: این مثال برای آموزش مناسب است، اما Serial.parseInt() تنظیمات زمانی و رفتار خاص خود را دارد. در پروژه های حساس بهتر است ورودی سریال به صورت کامل و با مدیریت پایان خط و خطاهای ورودی پردازش شود.
۷. استفاده از while برای رسیدن سنسور به مقدار مشخص
فرض کنید یک سنسور آنالوگ به پایه A0 متصل است و می خواهیم مقدار آن را تا زمانی که از یک آستانه کمتر است بررسی کنیم.
const int sensorPin = A0;
const int threshold = 500;
void setup() {
Serial.begin(9600);
while (analogRead(sensorPin) < threshold) {
Serial.println("Sensor value is below threshold");
delay(200);
}
Serial.println("Threshold reached");
}
void loop() {
}در این برنامه، تا زمانی که مقدار خوانده شده از سنسور کمتر از ۵۰۰ باشد، پیام نمایش داده می شود. وقتی مقدار به ۵۰۰ یا بیشتر برسد، حلقه پایان می یابد.
نکته مهم درباره این روش
چون حلقه در setup() قرار دارد، اگر مقدار سنسور هیچ وقت به آستانه نرسد، اجرای setup() ادامه پیدا نمی کند و برنامه به loop() نمی رسد.
در پروژه های واقعی باید برای چنین انتظاری، در صورت نیاز، زمان پایان (Timeout) در نظر گرفت. همچنین اگر لازم است سایر بخش های برنامه در این مدت فعال بمانند، بهتر است به جای انتظار مسدودکننده از بررسی دوره ای مقدار سنسور در loop() استفاده شود.
۸. حلقه بی نهایت چیست؟
حلقه بی نهایت (Infinite Loop) زمانی ایجاد می شود که شرط حلقه همیشه درست باقی بماند یا متغیرها به گونه ای تغییر نکنند که شرط نادرست شود.
مثال:
int counter = 0;
void setup() {
Serial.begin(9600);
while (counter < 5) {
Serial.println(counter);
}
}
void loop() {
}در این برنامه، مقدار counter هیچ وقت افزایش نمی یابد. بنابراین شرط counter < 5 همیشه درست است و حلقه پایان پیدا نمی کند.
روش اصلاح
int counter = 0;
void setup() {
Serial.begin(9600);
while (counter < 5) {
Serial.println(counter);
counter++;
}
}
void loop() {
}با اضافه کردن counter++، مقدار متغیر به تدریج افزایش می یابد و در نهایت شرط نادرست می شود.
آیا حلقه بی نهایت همیشه اشتباه است؟
خیر. در آردوینو، تابع loop() به طور مداوم اجرا می شود و عملا ساختاری تکرارشونده دارد. همچنین گاهی در برنامه های تعبیه شده از حلقه بی نهایت به صورت عمدی استفاده می شود.
مسئله زمانی ایجاد می شود که حلقه ناخواسته مانع انجام کارهای دیگر شود؛ مثلا ارتباط سریال، کنترل نمایشگر، خواندن سنسور یا پاسخ به ورودی های دیگر متوقف بماند.
۹. استفاده از break و continue
در حلقه های while و do-while می توان از دو دستور break و continue استفاده کرد.
دستور break
دستور break اجرای حلقه را بلافاصله متوقف می کند و برنامه را به اولین دستور پس از حلقه می برد.
int counter = 0;
void setup() {
Serial.begin(9600);
while (counter < 10) {
if (counter == 5) {
break;
}
Serial.println(counter);
counter++;
}
Serial.println("Loop ended");
}
void loop() {
}در این مثال، اعداد صفر تا چهار چاپ می شوند و پس از آن حلقه پایان می یابد.
دستور continue
دستور continue باعث می شود ادامه دستورات همان دور از حلقه نادیده گرفته شود و اجرای دور بعدی آغاز شود.
int counter = 0;
void setup() {
Serial.begin(9600);
while (counter < 5) {
counter++;
if (counter == 3) {
continue;
}
Serial.println(counter);
}
}
void loop() {
}در این مثال، عدد ۳ چاپ نمی شود؛ زیرا با رسیدن مقدار counter به ۳، دستور continue اجرا می شود.
نکته: هنگام استفاده از continue در حلقه ها باید مطمئن شوید که متغیرهای لازم برای تغییر شرط به روز می شوند؛ در غیر این صورت ممکن است حلقه بی نهایت ایجاد شود.
۱۰. نکات مهم در استفاده از while و do-while
نکته اول: شرط حلقه باید امکان پایان یافتن داشته باشد
اگر انتظار دارید حلقه پس از مدتی تمام شود، بررسی کنید که شرط آن بتواند نادرست شود.
نکته دوم: مراقب توقف سایر کارهای برنامه باشید
حلقه های انتظار می توانند مانع اجرای بخش های دیگر برنامه شوند. در پروژه های دارای نمایشگر، شبکه، سنسور یا چند دکمه، این موضوع اهمیت زیادی دارد.
نکته سوم: از Timeout استفاده کنید
اگر منتظر دریافت داده یا رسیدن سنسور به مقدار مشخصی هستید، بهتر است یک زمان محدود برای انتظار تعیین کنید.
نکته چهارم: حلقه do-while حداقل یک بار اجرا می شود
اگر اجرای بدنه حلقه حتی در صورت نادرست بودن شرط اولیه مناسب نیست، از while استفاده کنید.
نکته پنجم: سمی کالن انتهای do-while را فراموش نکنید
ساختار صحیح:
do {
// دستورات
} while (condition);نبودن سمی کالن انتهای حلقه می تواند باعث خطای کامپایل شود.
۱۱. مقایسه حلقه های for، while و do-while
| ویژگی | for | while | do-while |
|---|---|---|---|
| کاربرد متداول | تعداد تکرار مشخص | تکرار تا برقرار بودن شرط | اجرای حداقل یک بار و سپس تکرار |
| محل بررسی شرط | پیش از هر دور | پیش از هر دور | پس از هر دور |
| حداقل تعداد اجرا | صفر بار | صفر بار | یک بار |
| مدیریت شمارنده | معمولا در سرآیند حلقه | معمولا در بدنه | معمولا در بدنه |
| مثال | پیمایش آرایه | انتظار برای یک شرط | دریافت ورودی و بررسی آن |
هیچ کدام از این حلقه ها همیشه بهترین انتخاب نیستند. انتخاب درست به نیاز برنامه، نحوه مدیریت زمان و این موضوع بستگی دارد که آیا اجرای بدنه حلقه باید حداقل یک بار انجام شود یا خیر.
۱۲. تمرین های پیشنهادی
برای یادگیری بهتر، تمرین های زیر را انجام دهید:
- برنامه ای بنویسید که با استفاده از
whileاعداد ۱ تا ۲۰ را در Serial Monitor چاپ کند.
- برنامه ای بنویسید که با استفاده از
do-whileحداقل یک بار یک پیام را نمایش دهد.
- یک دکمه به پایه دیجیتال ۲ متصل کنید و با استفاده از
whileتا زمان رها شدن دکمه، LED را روشن نگه دارید.
- برنامه ای بنویسید که مقدار یک پتانسیومتر متصل به
A0را تا رسیدن به مقدار مشخصی بررسی کند.
- در یک حلقه
whileازbreakاستفاده کنید تا با رسیدن شمارنده به عدد ۷، حلقه متوقف شود.
- برنامه ای طراحی کنید که در انتظار دریافت داده از Serial Monitor، پس از گذشت مدت مشخصی از ادامه انتظار منصرف شود.
جمع بندی
حلقه های while و do-while از ابزارهای مهم برنامه نویسی آردوینو هستند.
- حلقه
whileابتدا شرط را بررسی می کند و فقط در صورت درست بودن آن، دستورات را اجرا می کند. - حلقه
do-whileابتدا دستورات را اجرا می کند و سپس شرط را بررسی می کند؛ بنابراین حداقل یک بار اجرا می شود. - با
breakمی توان حلقه را زودتر متوقف کرد و باcontinueادامه دستورات یک دور را نادیده گرفت. - اگر شرط حلقه هیچ وقت نادرست نشود، ممکن است حلقه بی نهایت ایجاد شود.
- در پروژه های واقعی باید به زمان انتظار و توقف احتمالی سایر وظایف برنامه توجه کرد.
با شناخت تفاوت این حلقه ها، می توانید ساختار برنامه های آردوینو را متناسب با نیاز پروژه انتخاب کنید و کنترل بهتری بر تکرار دستورات داشته باشید.
نمونه کد :
int counter = 0;
void setup() {
Serial.begin(9600);
while (counter < 5) {
Serial.println(counter);
counter++;
}
int value = 10;
do {
Serial.println(value);
value++;
} while (value < 12);
}
void loop() {
}