آردوینومتوسط۹ دقیقه مطالعهArduino IDE 2.x / Uno R3

توابع و محدودهٔ متغیرها

نوشتن تابع در C++ آردوینو؛ پارامتر، مقدار بازگشتی و void، محدوده و عمر متغیرهای محلی و سراسری، پیش‌تعریف (Prototype) و بازنویسی تمیز یک چشمک‌زن با توابع کمکی.

آنچه در این درس می‌آموزید

  • تابع با پارامتر و مقدار بازگشتی تعریف کنید و آن را صدا بزنید
  • تفاوت تابع بازگشت‌دهنده و تابع void را توضیح دهید
  • محدوده (Scope) و عمر متغیر محلی و سراسری را تشخیص دهید
  • کد تکراری را به توابع کمکی مثل blinkPattern و readAverage منتقل کنید
نویسنده: تیم داکنامه · بازبینی: ۲۰۲۶-۱۰-۳۱
فهرست مطالب

کد چراغ راهنمای درس قبل همه در loop() بود؛ اگر یک فاز دیگر اضافه می‌کردید، تابع اصلی شلوغ‌تر می‌شد. ابزار مهار این شلوغی «تابع» (Function) است: بلوکی نام‌دار از کد که یک بار می‌نویسید و هر جا لازم شد صدا می‌زنید. توابع کد را کوتاه و خوانا می‌کنند و اجازه می‌دهند یک قطعهٔ امتحان‌شده را بدون بازنویسی در برنامه‌های دیگر هم بردارید. در این درس تعریف تابع، پارامتر و مقدار بازگشتی، و مفهوم مهم «محدوده» (Scope) را یاد می‌گیرید و یک چشمک‌زن شلوغ را با توابع بازنویسی می‌کنید.

چرا تابع؟

دو انگیزه اصلی دارید:

  • حذف تکرار — همین حالا در loop() شما احتمالاً چند جفت digitalWrite و delay کپی شده است. هر تکرار، یک جا برای خطا و یک دردسر در اصلاح است؛ کد را عوض کنید باید همهٔ نسخه‌ها را بگردید.
  • چیدمان ذهنی — تابع با نام گویا، برنامه را به فهرستی از کارها تبدیل می‌کند: blinkPattern(3, 200) یعنی «سه بار با فاصلهٔ ۲۰۰ میلی‌ثانیه بزن»؛ نیازی به خواندن جزئیات نیست.

هر برنامهٔ آردوینو از قبل با دو تابع آشنا هستید: setup() و loop() هر دو تابع‌اند؛ شما از این درس به بعد تابع خودتان را می‌سازید.

تعریف تابع: پارامتر، بازگشت و void

یک تابع از چهار بخش ساخته می‌شود: نوع بازگشت، نام، پارامترها و بدنه:

cpp
int addTwo(int a, int b) {  // Return type, name, parameters
  int sum = a + b;          // Local variable, lives only here
  return sum;               // Send the result back to the caller
}
  • نوع بازگشت — چیزی که تابع برمی‌گرداند؛ این تابع یک عدد صحیح می‌دهد، پس int. اگر تابع چیزی برنگرداند، نوعش void است؛ setup() و loop() همین‌اند.
  • پارامتر (Parameter) — متغیرهای ورودی تابع؛ a و b فقط داخل همین تابع وجود دارند. هنگام صدا زدن، به آن‌ها «آرگومان» (Argument) می‌دهید: int result = addTwo(3, 4);
  • return — اجرای تابع را تمام می‌کند و مقدار را به محل صدا زدن می‌فرستد. در تابع void هم می‌توانید یک return; تنها بنویسید تا زودتر خارج شوید.
  • پیش‌تعریف (Prototype) — اعلامِ «چنین تابعی وجود دارد» پیش از استفاده: int addTwo(int a, int b);. در زبان C++ تابع باید قبل از محل صدا زدن دیده شود، اما IDE آردوینو به‌طور خودکار پیش‌تعریف‌ها را می‌سازد؛ پس ترتیب تعریف توابع در فایل .ino معمولاً مهم نیست و می‌توانید تابع را بعد از loop() هم بنویسید.

محدوده و عمر متغیرها

جایی که متغیر تعریف می‌شود، تعیین می‌کند چه کسی آن را می‌بیند و چه زمانی زنده است:

  • متغیر محلی (Local Variable) — داخل تابع یا بلوک تعریف می‌شود؛ فقط در همان‌جا معتبر است و با پایان تابع از حافظه پاک می‌شود. هر بار صدا زدن تابع، نسخهٔ تازه‌ای از آن ساخته می‌شود.
  • متغیر سراسری (Global Variable) — بیرون از همهٔ توابع، بالای فایل تعریف می‌شود؛ همهٔ توابع آن را می‌بینند و از روشن‌شدن برد تا خاموشی زنده است. شمارهٔ پین‌ها و وضعیت‌هایی که چند تابع باید بدانند سراسری‌اند.

قاعدهٔ عملی: تا می‌توانید محلی بنویسید. متغیر سراسری از هر تابعی قابل‌تغییر است، پس وقتی مقدارش عجیب شد، باید همهٔ برنامه را بگردید تا مقصر را پیدا کنید. «برخورد نام» (Naming Collision) هم همین‌جاست: اگر متغیر محلی هم‌نام یک متغیر سراسری بسازید، داخل آن تابع نامِ محلی برنده است و سراسری پنهان می‌شود؛ کامپایلر خطا نمی‌گیرد و گیجی از نوع خاموش است.

بازنویسی: از کد تکراری به توابع

این چشمک‌زن را ببینید؛ همان الگوی دو بار کپی‌شده:

blink-before.ino
const int LED_PIN = 8;

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  // Three quick blinks, copy-pasted by hand
  digitalWrite(LED_PIN, HIGH);
  delay(200);
  digitalWrite(LED_PIN, LOW);
  delay(200);
  digitalWrite(LED_PIN, HIGH);
  delay(200);
  digitalWrite(LED_PIN, LOW);
  delay(200);
  digitalWrite(LED_PIN, HIGH);
  delay(200);
  digitalWrite(LED_PIN, LOW);
  delay(200);
  delay(1000);
}

همین برنامه با یک تابع کمکی:

blink-after.ino
const int LED_PIN = 8;

// Blink the LED on pin 8, "times" times, each phase "ms" milliseconds
void blinkPattern(int times, int ms) {
  for (int i = 0; i < times; i++) {
    digitalWrite(LED_PIN, HIGH);
    delay(ms);
    digitalWrite(LED_PIN, LOW);
    delay(ms);
  }
}

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  blinkPattern(3, 200);  // Three quick blinks
  delay(1000);
}

هجده خط تکراری به یک حلقه در تابع و یک خط صدا زدن تبدیل شد. اگر فردا خواستید الگو را عوض کنید، فقط تابع را تغییر می‌دهید؛ و همین تابع را در هر برنامهٔ دیگری هم می‌توانید کپی کنید.

دو تابع کمکی کاربردی

دو الگوی پرتکرار که ارزش داشتن در جعبه‌ابزار را دارند:

cpp
// Pulse a pin once: on for "onMs", off for "offMs"
void pulse(int pin, int onMs, int offMs) {
  digitalWrite(pin, HIGH);
  delay(onMs);
  digitalWrite(pin, LOW);
  delay(offMs);
}

// Read an analog pin "samples" times and return the average
int readAverage(int pin, int samples) {
  int sum = 0;  // Local: this sum belongs to this call only
  for (int i = 0; i < samples; i++) {
    sum += analogRead(pin);
    delay(5);
  }
  return sum / samples;
}

تابع pulse یک فلاش می‌سازد و blinkPattern از قبل می‌توانست با آن نوشته شود؛ ترکیب توابع کوچک، ساخت توابع بزرگ‌تر را ارزان می‌کند. تابع readAverage هم خواندن سنسور آنالوگ را پایدار می‌کند: به‌جای یک نمونهٔ شلوغ، میانگین چند نمونه برمی‌گرداند — و چون sum محلی است، هر صدا زدن از صفر شروع می‌شود و اثری از صدا زدن قبلی نمی‌ماند.

تله‌های رایج

  • تغییر مخرب متغیر سراسری — اگر چند تابع مقدار یک متغیر سراسری را عوض کنند، ردیابی باگ سخت می‌شود. به‌جای آن، داده را با پارامتر بدهید و نتیجه را با return بگیرید؛ جریان داده در کدتان آشکار می‌ماند.
  • متغیر محلی بی‌مقدار اولیه — متغیر محلی مقدار اولیهٔ خودکار ندارد و محتوایش زبالهٔ حافظه است؛ همیشه در تعریف، مقدار بدهید: int sum = 0;
  • توابع خیلی بزرگ — اگر تابع‌تان بیش از یک صفحه شد، احتمالاً چند کار می‌کند؛ به چند تابع کوچک با نام‌های گویا بشکنید.

اشتباهات رایج و عیب‌یابی

خطا یا نشانه علت رایج راه‌حل
'blinkPattern' was not declared in this scope غلط املایی در نام هنگام صدا زدن، یا فراموش‌کردن تعریف تابع نام را با تعریف تابع حرف‌به‌حرف مقایسه کنید؛ تعریف را بالای فایل یا بیرون از setup و loop بگذارید
too few arguments to function صدا زدن تابع بدون همهٔ آرگومان‌ها تعداد و ترتیب آرگومان‌ها را با پارامترهای تعریف تطبیق دهید
return-statement with no value return; در تابعی که نوع بازگشت دارد، یا برعکس تابع بازگشت‌دار حتماً مقدار برگرداند؛ تابع void مقدار برنگرداند
متغیر داخل تابع همیشه صفر یا مقدار عجیب دارد متغیر محلی بدون مقدار اولیه، یا پنهان‌شدن سراسری زیر محلی هم‌نام در تعریف مقدار بدهید و از نام هم‌سان با متغیرهای سراسری پرهیز کنید
مقدار سراسری بی‌دلیل عوض می‌شود چند تابع همان متغیر را تغییر می‌دهند تغییرها را فقط در یک تابع متمرکز کنید یا داده را با پارامتر و return جابه‌جا کنید

تمرین‌ها

تمرین

تمرین ۱ — چشمک‌زن مورس با توابع

تابعی به نام flash بنویسید که یک فلاش کوتاه (۱۰۰ میلی‌ثانیه روشن، ۱۰۰ میلی‌ثانیه خاموش) روی پینی که با پارامتر می‌گیرد بزند؛ سپس با صدا زدن چندبارهٔ همین تابع، الگوی «کوتاه کوتاه کوتاه» روی پین ۸ بسازید و میان دو الگو یک ثانیه مکث بگذارید.

نمایش راه‌حل

تابع flash پین را با پارامتر می‌گیرد و pulse درس با زمان‌های ثابت کوتاه می‌شود؛ تکرار الگو را هم یک حلقهٔ for در loop() انجام می‌دهد:

cpp
const int LED_PIN = 8;

// One short flash on the given pin
void flash(int pin) {
  digitalWrite(pin, HIGH);
  delay(100);
  digitalWrite(pin, LOW);
  delay(100);
}

void setup() {
  pinMode(LED_PIN, OUTPUT);
}

void loop() {
  for (int i = 0; i < 3; i++) {  // Three short flashes
    flash(LED_PIN);
  }
  delay(1000);  // Pause between patterns
}

چون flash پین را با پارامتر می‌گیرد، همین تابع را برای هر LED دیگری هم می‌توانید صدا بزنید؛ مثلاً flash(9) بدون هیچ تغییر دیگری کار می‌کند.

:::

خلاصه

  • تابع بلوکی نام‌دار از کد است؛ نوع بازگشت، نام، پارامترها و بدنه دارد و با نامش صدا زده می‌شود.
  • تابعی که مقدار برمی‌گرداند نوعش همان مقدار است؛ تابع بی‌بازگشت void است.
  • متغیر محلی فقط در تابع خودش زنده است و با هر صدا زدن تازه می‌شود؛ متغیر سراسری همه‌جا دیده می‌شود — پس کم و محتاط بسازید.
  • IDE آردوینو پیش‌تعریف (Prototype) توابع را خودکار می‌سازد و ترتیب تعریف در فایل .ino را آزاد می‌کند.
  • کد تکراری را به توابع کوچک با نام گویا منتقل کنید؛ همین الگو پایهٔ کتابخانه‌سازی است.

توابع کد را تمیز کردند؛ اما برنامه‌ها هنوز فقط حرف می‌زنند و گوش نمی‌دهند. در درس بعدی نخستین ورودی واقعی را می‌خوانید: یک دکمهٔ فشاری که LED را کنترل می‌کند.

منابع و ارجاع‌ها

درس‌های مرتبط