در قسمت بیست و ششم از آموزش آردوینو به بررسی تعیین مدتزمان فشردهشدن یک سوئیچ پرداختیم. در این قسمت قصد داریم درباره خواندن کیبورد (صفحهکلید)، خواندن مقادیر آنالوگ و تغییر دامنه مقادیر صحبت کنیم.
فرض کنید شما یک کیبورد ماتریسی دارید و میخواهید سوئیچهای فشردهشده را در اسکچ خود بخوانید. برای مثال، صفحهکلید 12 دکمهای که روی بسیاری از دستگاهها مانند تلفن رومیزی وجود دارد.
در این کد ما از روشی موسوم به اسکن یا جاروب استفاده می کنیم. در این روش ستونها را خروجی و سطرها را ورودی در نظر میگیریم و از ساختار for تو در تو کمک میگیریم تا کلید فشرده شده را پیدا کنیم. پس ابتدا ستون اول را صفر می کنیم و سطرها را به ترتیب میخوانیم (اسکن میکنیم) و این کار برای ستون های دیگر هم ادامه می دهیم تا مشخص شود آیا سطری صفر شده یا نه. صفر شدن پایه مربوط به هر سطر نشان دهنده این است که کلید نگاشت شده به این سطر و ستون، فشرده شده است.
ردیفها و ستونها را از کانکتور کیبورد به آردوینو متصل کنید، همانطور که در شکل 1 نشان داده شده است:
اگر آردوینو و کیبورد خود را همانطور که در شکل 1 نشان داده شده است، سیمکشی کردهاید، کد زیر سوئیچ های فشردهشده را در Serial Monitor چاپ خواهد کرد:
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 | /* Keypad sketch prints the key pressed on a keypad to the serial port */ const int numRows = 4; // number of rows in the keypad const int numCols = 3; // number of columns const int debounceTime = 20; // number of milliseconds for switch to be stable // keymap defines the character returned when the corresponding key is pressed const char keymap[numRows][numCols] = { { '1', '2', '3' } , { '4', '5', '6' } , { '7', '8', '9' } , { '*', '0', '#' } }; // this array determines the pins used for rows and columns const int rowPins[numRows] = {8, 7, 6, 5}; // Rows 0 through 3 const int colPins[numCols] = {4, 3, 2}; // Columns 0 through 2 void setup(){ Serial.begin(9600); for (int row = 0; row < numRows; row++) { pinMode(rowPins[row],INPUT_PULLUP); // Set row pins as input with pullups } for (int column = 0; column < numCols; column++) { pinMode(colPins[column],OUTPUT); // Set column pins as outputs digitalWrite(colPins[column],HIGH); // Make all columns inactive } } void loop() { char key = getKey(); if( key != 0) { // if the character is not 0 then it's a valid key press Serial.print("Got key "); Serial.println(key); } } // returns the key pressed, or zero if no key is pressed char getKey() { char key = 0; // 0 indicates no key pressed for(int column = 0; column < numCols; column++) { digitalWrite(colPins[column],LOW); // Activate the current column. for(int row = 0; row < numRows; row++) // Scan all rows for key press { if(digitalRead(rowPins[row]) == LOW) // Is a key pressed? { delay(debounceTime); // debounce while(digitalRead(rowPins[row]) == LOW) ; // wait for key to be released key = keymap[row][column]; // Remember which key // was pressed. } } digitalWrite(colPins[column],HIGH); // De-activate the current column. } return key; // returns the key pressed or 0 if none } |
این کد فقط در صورتی بهدرستی کار خواهد کرد که سیمکشی با کد مطابقت داشته باشد. جدول 1نشان میدهد که چگونه باید ردیفها و ستونها به پینهای آردوینو متصل شوند. اگر از کیبورد دیگری استفاده میکنید، دیتاشیت آن را بررسی کنید تا بتوانید اتصالات ردیفها و ستونها را مشخص کنید. این موضوع را با دقت بررسی کنید… زیرا سیمکشی نادرست میتواند باعث اتصال کوتاه پینها شده و به چیپ کنترلکننده شما آسیب برساند!
جدول 1: متصل کردن پین (پایه) های آردوینو به سطرها و ستونهای کیپد
کیپدهای ماتریسی معمولاً از سوئیچهای Normally Open تشکیل شدهاند که هنگام فشاردادن، یک سطر را به یک ستون متصل میکنند. سوئیچ Normally Open فقط زمانی که فشرده شود، اتصال الکتریکی برقرار میکند.
شکل 1 نشان میدهد که چگونه رساناهای داخلی، سطرها و ستونهای دکمهها را به کانکتور کیبورد متصل میکنند. هر یک از چهار سطر به یک پایه ورودی و هر ستون به یک پایه خروجی متصل است.
تابع setup حالت پینها (پایهها) را تنظیم میکند تا مقاومتهای Pull-up را روی پایههای ورودی فعال کند. (برای اطلاعات بیشتر، به بخش مقاومتهای Pull-up قسمتهای قبلی مراجعه کنید)
تابع getkey بهصورت متوالی پایهی هر ستون را در حالت LOW (پایین) تنظیم کرده و سپس بررسی میکند که آیا هیچیک از پایههای سطر LOW شدهاند یا نه.
ازآنجاکه از مقاومتهای Pull-up استفاده شده است، سطرها در حالت عادی HIGH (بالا) هستند، مگر اینکه یک سوئیچ فشرده شود. فشردن یک سوئیچ باعث میشود سیگنال LOW در پایهی ورودی ایجاد شود) اگر پایهی سطر LOW باشد، این نشان میدهد که سوئیچ مربوط به آن سطر و ستون فشرده شده است.)
برای جلوگیری از تأثیر نویز (پرش سوئیچ)، یک تأخیر اعمال میشود. سپس کد منتظر میماند تا سوئیچ رها شود و کاراکتر مرتبط با سوئیچ از آرایهی keymap پیدا شده و از تابع بازگردانده میشود. اگر هیچ سوئیچی فشرده نشده باشد، مقدار 0 بازگردانده میشود.
کتابخانه Keypad مدیریت تعداد مختلفی از سوئیچها را در آردوینو آسانتر میکند و میتواند بهگونهای تنظیم شود که برخی از پینها را با نمایشگر کاراکتری LCD به اشتراک بگذارد. این کتابخانه در Arduino Library Manager موجود است.
فرض کنید میخواهید ولتاژ روی یک پین آنالوگ را بخوانید. ممکن است بخواهید مقدار یک پتانسیومتر (pot)، یک مقاومت متغیر یا یک سنسوری را که ولتاژ متغیری ارائه میدهد، دریافت کنید.
کد زیر، ولتاژ ورودی از یک پین آنالوگ (A0) را میخواند و یک LED را با نرخی متناسب با مقدار بازگشتی از تابع analogRead فلش میکند. ولتاژ توسط یک پتانسیومتر به صورتی که در شکل 2 نشان داده شده است، تنظیم میشود.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | /* Pot sketch blink an LED at a rate set by the position of a potentiometer */ const int potPin = A0; // select the input pin for the potentiometer const int ledPin = LED_BUILTIN; // select the pin for the LED int val = 0; // variable to store the value coming from the sensor void setup() { pinMode(ledPin, OUTPUT); // declare the ledPin as an OUTPUT } void loop() { val = analogRead(potPin); // read the voltage on the pot digitalWrite(ledPin, HIGH); // turn the ledPin on delay(val); // blink rate set by pot value (in milliseconds) digitalWrite(ledPin, LOW); // turn the ledPin off delay(val); // turn led off for same period as it was turned on } |
شکل 2: اتصال یک پتانسیومتر به آردوینو
این کد از تابع analogRead برای خواندن ولتاژ از وایپرwiper) ) پتانسیومتر (پین وسط) استفاده میکند. یک پتانسیومتر دارای سه پین است؛ دو پین به یک ماده مقاومتی متصل هستند و پین سوم (معمولاً وسط) به wiper متصل است که میتوان آن را چرخاند تا در هر نقطهای از ماده مقاومتی تماس برقرار کند.
با چرخش پتانسیومتر، مقاومت بین wiper و یکی از پینها افزایش مییابد، درحالیکه مقاومت آن نسبت به پین دیگر کاهش پیدا میکند. نمودار شماتیک این مدار (شکل 2) میتواند به درک بهتر نحوه عملکرد پتانسیومتر کمک کند.
هنگامی که wiper به سمت پایین حرکت میکند، مقاومت آن نسبت بهGND کاهشیافته و مقاومت آن نسبت به 5 ولت (یا 3.3 ولت بسته به نوع برد) افزایش مییابد.
با حرکت wiper به سمت پایین، ولتاژ روی پین آنالوگ کاهش مییابد و میتواند حداقل به 0 ولت برسد.
در مقابل، حرکت wiper به سمت بالا، ولتاژ روی پین آنالوگ را افزایش میدهد که حداکثر مقدار آن 5 ولت (یا 3.3 ولت) خواهد بود.
اگر با افزایش چرخش پتانسیومتر، ولتاژ روی پین بهجای اینکه افزایش پیدا کند، کاهش مییابد، باید اتصالات پینهای 5 ولت و GND را جابجا کنید.
ولتاژ با استفاده از تابع analogRead اندازهگیری میشود که مقداری متناسب با ولتاژ واقعی روی پین آنالوگ ارائه میدهد. این مقدار 0 خواهد بود زمانی که ولتاژ روی پین 0 ولت باشد و زمانی برابر با 1023 خواهد بود که ولتاژ 5 ولت (یا 3.3 ولت برای بردهای 3.3 ولتی مانند اکثر بردهای 32 بیتی) باشد. همچنین، مقدارهای بین این دو مقدار متناسب با نسبت ولتاژ روی پین 5 ولت (یا 3.3 ولت بسته به نوع برد) متفاوت خواهند بود.
پتانسیومترهای با مقدار 10 کیلواهم بهترین گزینه برای اتصال به پینهای آنالوگ هستند.
نیازی به تنظیم potPin بهعنوان ورودی (input) نیست، زیرا در هر بار فراخوانی تابع analogRead، این کار بهصورت اتوماتیک انجام میشود.
فرض کنید میخواهید دامنه یک مقدار را تغییر دهید، مثلاً مقداری که از analogRead با اتصال یک پتانسیومتر (pot) یا دستگاه دیگری که ولتاژ متغیر ارائه میدهد، بهدست میآید.
برای مثال، تصور کنید میخواهید موقعیت دستگیره پتانسیومتر را بهصورت درصدی از 0٪ تا 100٪ نمایش دهید.
از تابع map در آردوینو برای مقیاسبندی مقادیر به دامنهی موردنظر خود استفاده کنید.
اسکچ (کد نمونه) زیر مقدار ولتاژ پتانسیومتر را خوانده و آن را در متغیر val ذخیره میکند، سپس مقدار خواندهشده را از 0 تا 100 مقیاسبندی میکند، بهطوری که با چرخش پتانسیومتر از یک سر به سر دیگر، مقدار به صورت متناسب تغییر می کند.
همچنین، این کد باعث میشود یک LED با نرخی متناسب با ولتاژ روی پایه چشمک بزند و مقدار مقیاسبندی شده را روی پورت سریال چاپ میکند.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | * * Map sketch * map the range of analog values from a pot to scale from 0 to 100 * resulting in an LED blink rate ranging from 0 to 100 ms.* and Pot rotation percent is written to the serial port */ const int potPin = A0; // select the input pin for the potentiometer int ledPin = LED_BUILTIN; // select the pin for the LED void setup() { pinMode(ledPin, OUTPUT); // declare the ledPin as an OUTPUT Serial.begin(9600); } void loop() { int val; // The value coming from the sensor int percent; // The mapped value val = analogRead(potPin); // read the voltage on the pot (val ranges // from 0 to 1023) percent = map(val,0,1023,0,100); // percent will range from 0 to 100. digitalWrite(ledPin, HIGH); // turn the ledPin on delay(percent); // On time given by percent value digitalWrite(ledPin, LOW); // turn the ledPin off delay(100 - percent); // Off time is 100 minus On time Serial.println(percent); // show % of pot rotation on Serial Monitor } |
در قسمتهای قبلی توضیح دادیم که چگونه موقعیت پتانسیومتر را به یک مقدار تبدیل کنید. اما در این قسمت آموزش میدهیم که چگونه از این مقدار همراه با تابع map برای مقیاسبندی مقدار به دامنهی موردنظر خود استفاده کنید.
در این مثال، مقدار دریافتشده ازanalogRead (0 to 1023) به صورت درصد (از 0 تا 100) نوشته میشود. سپس از این درصد برای تنظیم چرخه کاری (Duty Cycle) LED استفاده میشود.
چرخه کاری (Duty Cycle) درصدی از زمان است که LED فعال است. این مقدار در یک بازهی زمانی به نام پریود (Period) اندازهگیری میشود که در اینجا 100 میلیثانیه است.
مدتزمانی که LED خاموش میماند، با کمکردن مقدار چرخه کاری از 100 محاسبه میشود.
بنابراین، اگر مقدار analogRead برابر 620 باشد، با استفاده از تابع map مقدار آن به 60 مقیاسبندی میشود. در نتیجه، LED به مدت 60 میلیثانیه روشن و سپس 40 میلیثانیه خاموش خواهد شد (100 – 60 = 40).
درصورتیکه ولتاژ ورودی بین 0 تا 5 ولت باشد (یا 3.3 ولت در بردهای 3.3 ولتی)، مقادیر خواندهشده از analogRead در بازهی 0 تا 1023 قرار دارند. اما شما میتوانید از هر بازهی مناسب برای مقادیر ورودی و خروجی استفاده کنید.
بهعنوانمثال، یک پتانسیومتر معمولی تنها 270 درجه از یک انتها به انتهای دیگر میچرخد. اگر بخواهید زاویهی چرخش دستگیره پتانسیومتر را نمایش دهید، میتوانید از کد زیر استفاده کنید.
1 | int angle = map(val,0,1023,0,270); // angle of pot derived from analogRead val |
مقادیر دامنه میتوانند منفی نیز باشند. اگر بخواهید مقدار 0 را در مرکز پتانسیومتر نمایش دهید، بهطوری که مقادیر منفی هنگام چرخش به چپ و مقادیر مثبت هنگام چرخش به راست نشان داده شوند، میتوانید از کد زیر استفاده کنید.
1 2 3 | > // show angle of 270 degree pot with center as 0 angle = map(val,0,1023,-135,135); |
تابع map زمانی مفید است که بازهی ورودی موردنظر شما از صفر شروع نشود.
بهعنوانمثال، اگر یک باتری داشته باشید که ظرفیت قابلاستفاده آن متناسب با ولتاژی بین 1.1 ولت تا 1.5 ولت باشد، میتوانید از کد زیر استفاده کنید:
1 2 3 4 5 6 | const int board_voltage = 5.0; // Set to 3.3 on boards that use 3.3 volt logic const int empty = 1.1/(5.0/1023.0); // voltage is 1.1V (1100mv) when empty const int full = 1.5/(5.0/1023.0); // voltage is 1.5V (1500mv) when full int val = analogRead(potPin); // read the analog voltage int percent = map(val, empty, full, 0,100); // map the voltage to a percent Serial.println(percent); |
اگر از مقادیر سنسور همراه با تابع map استفاده میکنید، باید حداقل و حداکثر مقدار خروجی سنسور را مشخص کنید.
برای این کار، میتوانید مقدار خواندهشده را روی پورت سریال، مشاهده کنید تا کمترین و بیشترین مقدار را تعیین کنید. سپس این مقادیر را بهعنوان کران پایین و کران بالا (lower and upper Bound) در تابع map وارد کنید.
اگر دامنهی مقادیر سنسور را از قبل نمیدانید، میتوانید با کالیبره کردن سنسور این مقادیر را به دست آورید.
به خاطر داشته باشید که اگر مقادیری را به تابع map وارد کنید که خارج از محدودهی کران بالا و پایین هستند، خروجی نیز خارج از دامنهی مشخصشده خواهد بود.
برای جلوگیری از این مشکل، میتوانید از تابع constrain استفاده کنید.
تابع map از محاسبات عدد صحیح (integer math) استفاده میکند، بنابراین فقط اعداد صحیح را در بازهی مشخصشده برمیگرداند و هر مقدار اعشاری حذف میشود و گرد نمیشود.
من کاپیتان آردوینو، اسمم میلاده و اینجا هستم تا تجربیاتم در رابطه با آردوینو رو با شما به اشتراک بزارم!
مقالات بیشتر
نویسنده شو !
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.