در قسمت بیست و پنجم از آموزش آردوینو به بررسی استفاده از سوئیچ پرداختیم. در این قسمت قصد داریم درباره تعیین مدتزمان فشردهشدن یک کلید صحبت کنیم.
در اسکچ زیر، نحوه تنظیم یک تایمر کانتر معکوس نمایشدادهشده است. با فشاردادن یک کلید، تایمر تنظیم میشود و مقدار تایمر کانتر افزایش مییابد؛ با رهاکردن کلید، شمارش معکوس آغاز میشود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | /* SwitchTime sketch Countdown timer that decrements every tenth of a second lights an LED when 0 Pressing button increments count, holding button down increases rate of increment */ const int ledPin = LED_BUILTIN; // the number of the output pin const int inPin = 2; // the number of the input pin const int debounceTime = 20; // the time in milliseconds required // for the switch to be stable const int fastIncrement = 1000; // increment faster after this many // milliseconds const int veryFastIncrement = 4000; // and increment even faster after // this many milliseconds int count = 0; // count decrements every tenth of a // second until reaches 0 void setup() { pinMode(inPin, INPUT_PULLUP); pinMode(ledPin, OUTPUT); Serial.begin(9600); } void loop() { int duration = switchTime(); if(duration > veryFastIncrement) { count = count + 10; } else if (duration > fastIncrement) { count = count + 4; } else if (duration > debounceTime) { count = count + 1; } else { // switch not pressed so service the timer if( count == 0) { digitalWrite(ledPin, HIGH); // turn the LED on if the count is 0 } else { digitalWrite(ledPin, LOW); // turn the LED off if the count is not 0 count = count - 1; // and decrement the count } } Serial.println(count); delay(100); } // return the time in milliseconds that the switch has been pressed (LOW) long switchTime() { // these variables are static - see Discussion for an explanation static unsigned long startTime = 0; // when switch state change was detected static bool state; // the current state of the switch if(digitalRead(inPin) != state) // check to see if switch has changed state { state = ! state; // yes, invert the state startTime = millis(); // store the time } if(state == LOW) { return millis() - startTime; // switch pushed, return time in milliseconds } else { return 0; // return 0 if the switch is not pushed (in the HIGH state); } } |
هسته اصلی این برنامه، تابع switchTime است. این تابع مدت زمانی (بر حسب میلیثانیه) که کلید فشرده شده است را برمیگرداند.
که خود این تابع از تابع millis آردوینو استفاده میکند تابع millis تعداد میلیثانیههایی را که از زمانی که برد آردوینو شروع به اجرای برنامه فعلی کرده است را برمیگرداند. این عدد تقریباً پس از 50 روز سرریز میشود (به صفر برمیگردد). برای فهمیدن مقدار گذر زمان مقدار قبلی این تابع را از مقدار فعلی آن کم میکنیم. (برای تفهیم بیشتر به کامنت های انگلیسی داخل کد دقت کنید)
1 2 3 4 5 6 7 8 9 | if(digitalRead(inPin) != state) // check to see if switch has changed state { state = ! state; // yes, invert the state startTime = millis(); // store the time } if(state == LOW) { return millis() - startTime; // switch pushed, return time in milliseconds } |
حلقه اصلی اسکچ مقدار برگشتی از تابع switchTime را بررسی میکند تا تصمیم بگیرد چه اقدامی باید انجام شود. مراحل بهصورت زیر است:
شما میتوانید از تابع switchTime فقط برای حذف نویز (debouncing) یک سوئیچ استفاده کنید. کد زیر منطق حذف نویز را با فراخوانی تابع switchTime مدیریت میکند:
1 2 3 4 5 6 | // the time in milliseconds that the switch needs to be stable const int debounceTime = 20; if( switchTime() > debounceTime) { Serial.print("switch is debounced"); } |
این روش برای حذف نویز (debouncing) زمانی مفید است که بیش از یک سوئیچ داشته باشید، زیرا میتوانید زمان فشردهشدن یک سوئیچ را بررسی کرده و همزمان وظایف دیگر را پردازش کنید، درحالیکه منتظر پایدار شدن وضعیت سوئیچ هستید. برای پیادهسازی این روش، باید وضعیت فعلی سوئیچ (فشرده شده یا نه) و زمان آخرین تغییر وضعیت را ذخیره کنید.
روشهای مختلفی برای انجام این کار وجود دارد—در این مثال، برای هر سوئیچ از یک تابع جداگانه استفاده خواهید کرد.
شما میتوانید متغیرهای مربوط به تمام سوئیچها را در ابتدای اسکچ بهعنوان متغیرهای سراسری (که به آنها “global” گفته میشود، زیرا در همهجا قابلدسترسی هستند) ذخیره کنید. بااینحال، راحتتر است که متغیرهای هر سوئیچ در داخل تابع مربوط به آن سوئیچ قرار داده شوند.
برای حفظ مقادیر متغیرهایی که در داخل یک تابع تعریف شدهاند، این اسکچ از متغیرهای استاتیک (static variables) استفاده میکند.
از این نظر، متغیرهای استاتیک مشابه متغیرهای سراسری (global variables) هستند (متغیرهای سراسری متغیرهایی هستند که خارج از یک تابع و معمولاً در ابتدای اسکچ تعریف میشوند و در سایر توابع نیز قابلدسترسی هستند.)
اما برخلاف متغیرهای سراسری، متغیرهای استاتیک که در یک تابع تعریف میشوند، فقط در همان تابع قابلدسترسی هستند.
مزیت متغیرهای استاتیک این است که نمیتوانند به طور تصادفی توسط توابع دیگر تغییر داده شوند که این ویژگی باعث میشود امنیت بیشتری داشته باشند.
این اسکچ یک مثال را نشان میدهد که چگونه میتوانید برای سوئیچهای مختلف، توابع جداگانه اضافه کنید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | /* SwitchTimeMultiple sketch Prints how long more than one switch has been pressed */ const int switchAPin = 2; // the pin for switch A const int switchBPin = 3; // the pin for switch B void setup() { pinMode(switchAPin, INPUT_PULLUP); pinMode(switchBPin, INPUT_PULLUP); Serial.begin(9600); } void loop() { unsigned long timeA; unsigned long timeB; timeA = switchATime(); timeB = switchBTime(); if (timeA > 0 || timeB > 0) { Serial.print("switch A time="); Serial.print(timeA); Serial.print(", switch B time="); Serial.println(timeB); } } unsigned long switchTime(int pin, bool &state, unsigned long &startTime) { if(digitalRead(pin) != state) // check to see if the switch has changed state { state = ! state; // yes, invert the state startTime = millis(); // store the time } if(state == LOW) { return millis() - startTime; // return the time in milliseconds } else { return 0; // return 0 if the switch is not pushed (in the HIGH state); } } long switchATime() { // these variables are static - see text for an explanation // the time the switch state change was first detected static unsigned long startTime = 0; static bool state; // the current state of the switch return switchTime(switchAPin, state, startTime); } long switchBTime() { // these variables are static - see text for an explanation // the time the switch state change was first detected static unsigned long startTime = 0; static bool state; // the current state of the switch return switchTime(switchBPin, state, startTime); } |
این اسکچ محاسبات زمانی خود را در تابعی به نام switchTime() انجام میدهد. این تابع وضعیت کلید و مدتزمان را بررسی و بهروزرسانی میکند.
یک تابع برای هر کلید ( switchATime() و switchBTime() ) استفاده میشود تا زمان شروع و وضعیت هر کلید را حفظ کند. ازآنجاکه متغیرهای نگهدارنده این مقادیر بهصورت استاتیک تعریف شدهاند، مقادیر هنگام خروج از تابع حفظ خواهند شد.
نگهداری این متغیرها درون تابع تضمین میکند که از متغیر اشتباهی استفاده نخواهد شد.
پینهای مورداستفاده برای کلیدها بهعنوان متغیرهای سراسری تعریف شدهاند؛ زیرا این مقادیر در تابع setup برای پیکربندی پینها موردنیاز هستند. اما چون این متغیرها با کلیدواژه const تعریف شدهاند، کامپایلر اجازه تغییر این مقادیر را نخواهد داد؛ بنابراین، هیچ خطری برای تغییر تصادفی آنها توسط کد اسکچ وجود ندارد.
درون حلقه loop، این اسکچ بررسی میکند که دکمه برای چه مدت نگه داشته شده است. اگر هر یک از دکمهها برای مدت بیش از صفر میلیثانیه فشرده شده باشند، اسکچ مدت زمانی را که هر کلید نگه داشته شده است، چاپ میکند.
اگر سریال مانیتور را باز کنید و یکی یا هر دو دکمه را نگه دارید، مشاهده خواهید کرد که مقادیر زمانی افزایش مییابند و بهصورت پیمایشی نمایش داده میشوند. اگر هر دو دکمه را رها کنید، تابع switchTime مقدار صفر را برمیگرداند؛ بنابراین، اسکچ تا زمانی که یکی یا هر دو دکمه را دوباره فشار دهید، خروجی را متوقف میکند.
من کاپیتان آردوینو، اسمم میلاده و اینجا هستم تا تجربیاتم در رابطه با آردوینو رو با شما به اشتراک بزارم!
مقالات بیشتر
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.