در قسمت 42 از آموزش آردوینو به بررسی کنترل تعداد زیادی LED رنگی، ترتیبدهی چند LED (ایجاد یک نمودار میلهای) پرداختیم. در این قسمت قصد داریم درباره روشن کردن چند LED به صورت پشتسرهم، کنترل ماتریس LED با استفاده از مالتیپلکسینگ و نمایش تصاویر روی ماتریس LED صحبت کنیم.
فرض کنید میخواهید LEDها را بهصورت یک توالی «چراغهای دنبالهدار» روشن کنید. از این افکت در جلوههای ویژهٔ سریالهای Knight Rider و Battlestar Galactica استفاده شده است؛ هر دو توسط گلن ای. لارسون ساخته شدهاند. به همین دلیل، این افکت را «اسکنر لارسون» نیز مینامند.
برای این کار شما میتوانید از همان اتصالی استفاده کنید که در شکل 1 نشان داده شده است.
|
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 |
/* Chaser */ const int NbrLEDs = 6; const int ledPins[] = {2, 3, 4, 5, 6, 7}; const int wait_time = 30; // Swap values of the following two #defines if cathodes are connected to Gnd #define LED_ON LOW #define LED_OFF HIGH void setup() { for (int led = 0; led < NbrLEDs; led++) { pinMode(ledPins[led], OUTPUT); } } void loop() { for (int led = 0; led < NbrLEDs - 1; led++) { digitalWrite(ledPins[led], LED_ON); delay(wait_time); digitalWrite(ledPins[led + 1], LED_ON); delay(wait_time); digitalWrite(ledPins[led], LED_OFF); delay(wait_time * 2); } for (int led = NbrLEDs - 1; led > 0; led--) { digitalWrite(ledPins[led], LED_ON); delay(wait_time); digitalWrite(ledPins[led - 1], LED_ON); delay(wait_time); digitalWrite(ledPins[led], LED_OFF); delay(wait_time * 2); } } |
در این کد، پایهها (پینها) در یک ترتیب ثابت روشن و خاموش میشوند و وابسته به مقدار سنسور نیستند. دو حلقهی for در برنامه وجود دارد:
مدتِ تأخیر بین روشنشدن هر LED با متغیر wait تنظیم میشود و میتوان آن را طوری انتخاب کرد که جلوهی بصری بهتری ایجاد کند.
فرض کنید یک ماتریس LED دارید و میخواهید تعداد پینهای موردنیاز آردوینو برای روشن و خاموشکردن LEDها را به حداقل برسانید.
این اسکچ از یک ماتریس ۶۴تایی LED استفاده میکند که آندها در ردیفها و کاتدها در ستونها قرار گرفتهاند، مانند مدل Jameco 2132349 (شکل 1 نحوهی اتصالها را نشان میدهد.)
نمایشگرهای ماتریس 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 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 |
/* * matrixMpx sketch * * Sequence LEDs starting from first column and row until all LEDs are lit * Multiplexing is used to control 64 LEDs with 16 pins */ const int columnPins[] = {2, 3, 4, 5, 6, 7, 8, 9}; const int rowPins[] = {10,11,12,A1,A2,A3,A4,A5}; int pixel = 0; // 0 to 63 LEDs in the matrix int columnLevel = 0; // pixel value converted into LED column int rowLevel = 0; // pixel value converted into LED row void setup() { for (int i = 0; i < 8; i++) { pinMode(columnPins[i], OUTPUT); // make all the LED pins outputs pinMode(rowPins[i], OUTPUT); } } void loop() { pixel = pixel + 1; if(pixel > 63) pixel = 0; columnLevel = pixel / 8; // map to the number of columns rowLevel = pixel % 8; // get the fractional value for (int column = 0; column < 8; column++) { digitalWrite(columnPins[column], LOW); // connect this column to GND for(int row = 0; row < 8; row++) { if (columnLevel > column) { digitalWrite(rowPins[row], HIGH); // connect all LEDs in row to +V } else if (columnLevel == column && rowLevel >= row) { digitalWrite(rowPins[row], HIGH); } else { digitalWrite(columnPins[column], LOW); // turn off all LEDs in this row } delayMicroseconds(300); // delay gives frame time of 20 ms for 64 LEDs digitalWrite(rowPins[row], LOW); // turn off LED } // disconnect this column from Ground digitalWrite(columnPins[column], HIGH); } } |

شکل 1: یک ماتریس LED متصل به ۱۶ پین دیجیتال
ردیفهای آند و ستونهای کاتد را همانطور که اشاره شد، وصل کنید، اما از شماره پینهای LED در Datasheet خود استفاده کنید.
شکل 1 نحوهی قرارگیری پینها را نشان میدهد و ارتباط آنها با ستونها و ردیفها را توضیح میدهد. شمارههای پینها در نمودار با چیدمان فیزیکی واقعی آنها مطابقت دارند.
بهطورکلی، شمارهگذاری پینها الگوی U شکل دارد که از بالا سمت چپ شروع میشود:
نکته مهم، جهتدهی صحیح قطعه است بهطوریکه پین ۱ در بالای سمت چپ قرار گیرد. برای پیداکردن پین ۱، به دنبال یک فرورفتگی کوچک (معمولاً به شکل یک نقطه کوچک) روی بدنه قطعه باشید که نشان میدهد پین ۱ کجاست.
اگر شک داشتید، همیشه Datasheet قطعه را بررسی کنید.
مقدار مقاومت باید بهگونهای انتخاب شود که جریان حداکثر از هر پین از ۴۰ میلیآمپر روی Arduino Uno (و دیگر بردهای مبتنی بر ATmega328) تجاوز نکند. این راهحل برای بردهای ۳.۳ ولتی یا هر بردی که نمیتواند ۴۰ میلیآمپر در هر پین تحمل کند، مناسب نیست.
چون جریان برای حداکثر هشت LED میتواند از هر پین ستون عبور کند، حداکثر جریان برای هر LED باید یکهشتم ۴۰ میلیآمپر باشد، یعنی ۵ میلیآمپر. هر LED در یک ماتریس کوچک قرمز معمولی، ولتاژ مستقیم حدود ۱.۸ ولت دارد.
با محاسبه مقاومت موردنیاز برای جریان ۵ میلیآمپر و ولتاژ مستقیم ۱.۸ ولت، مقدار مقاومت برابر ۶۸۰ اهم میشود. ولتاژ مستقیم ماتریسی که میخواهید استفاده کنید را حتماً در Datasheet بررسی کنید.
هر ستون ماتریس از طریق مقاومت سری به یک پین دیجیتال متصل میشود. وقتی پین ستون پایین (LOW) و پین ردیف بالا (HIGH) باشد، LED مربوطه روشن میشود. برای همه LEDهایی که پین ستون HIGH یا پین ردیف LOW باشد، جریانی از LED عبور نمیکند و روشن نمیشود.
حلقه for از طریق هر ردیف و ستون عبور میکند و LEDها را به ترتیب روشن میکند تا زمانی که تمام LEDها روشن شوند. حلقه با ستون و ردیف اول شروع میکند و شمارنده ردیف را افزایش میدهد تا تمام LEDهای آن ردیف روشن شوند؛ سپس به ستون بعدی میرود و به همین ترتیب، با هر بار اجرای حلقه، یک LED دیگر روشن میشود تا تمام LEDها روشن شوند.
شما میتوانید تعداد LEDهای روشن را متناسب با مقدار یک سنسور کنترل کنید.
سه خط زیر را از ابتدای حلقه کامنت کنید یا حذف کنید:
|
1 2 3 |
pixel = pixel + 1; if(pixel > 63) pixel = 0; |
آنها را با خطوط زیر جایگزین کنید تا مقدار سنسوری که به پین ۰ وصل است خوانده شود و این مقدار به تعداد LEDها از ۰ تا ۶۳ تبدیل شود:
|
1 2 |
int sensorValue = analogRead(0); // read the analog in value pixel = map(sensorValue, 0, 1023, 0, 63); // map sensor value to pixel (LED) |
میتوانید این را با یک مقاومت متغیر (پتانسیومتر) که به پین آنالوگ ۰ متصل شده، آزمایش کنید. تعداد LEDهای روشن متناسب با مقدار سنسور خواهد بود.
لازم نیست که یک ردیف کامل را همزمان روشن کنید. اسکچ زیر هر بار یک 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 25 26 27 28 29 30 31 |
/* * matrixMpx sketch, one at a time * * Sequence LEDs starting from first column and row, one at a time * Multiplexing is used to control 64 LEDs with 16 pins */ const int columnPins[] = {2, 3, 4, 5, 6, 7, 8, 9}; const int rowPins[] = {10,11,12,A1,A2,A3,A4,A5}; int pixel = 0; // 0 to 63 LEDs in the matrix void setup() { for (int i = 0; i < 8; i++) { pinMode(columnPins[i], OUTPUT); // make all the LED pins outputs pinMode(rowPins[i], OUTPUT); digitalWrite(columnPins[i], HIGH); } } void loop() { pixel = pixel + 1; if(pixel > 63) pixel = 0; int column = pixel / 8; // map to the number of columns int row = pixel % 8; // get the fractional value digitalWrite(columnPins[column], LOW); // Connect this column to GND digitalWrite(rowPins[row], HIGH); // Take this row HIGH delay(125); // pause briefly digitalWrite(rowPins[row], LOW); // Take the row low digitalWrite(columnPins[column], HIGH); // Disconnect the column from GND } |
فرض کنید میخواهید یک یا چند تصویر را روی یک ماتریس LED نمایش دهید؛ شاید با تعویض سریع چند تصویر، یک افکت انیمیشنی ایجاد کنید.
این اسکچ (برنامه) افکتی مانند تپش قلب ایجاد میکند؛ با روشنشدن لحظهای LEDها که به شکل یک قلب چیده شدهاند. برای هر ضربان قلب، ابتدا یک قلب کوچک و سپس یک قلب بزرگتر چشمک میزنند (تصاویر شبیه شکل 2 هستند).
|
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 |
* * matrixMpxAnimation sketch * animates two heart images to show a beating heart */ // the heart images are stored as bitmaps - each bit corresponds to an LED // a 0 indicates the LED is off, 1 is on byte bigHeart[] = { B01100110, B11111111, B11111111, B11111111, B01111110, B00111100, B00011000, B00000000}; byte smallHeart[] = { B00000000, B00000000, B00010100, B00111110, B00111110, B00011100, B00001000, B00000000}; const int columnPins[] = { 2, 3, 4, 5, 6, 7, 8, 9}; const int rowPins[] = {10,11,12,A1,A2,A3,A4,A5}; void setup() { for (int i = 0; i < 8; i++) { pinMode(rowPins[i], OUTPUT); // make all the LED pins outputs pinMode(columnPins[i], OUTPUT); digitalWrite(columnPins[i], HIGH); // disconnect column pins from Ground } } void loop() { int pulseDelay = 800 ; // milliseconds to wait between beats show(smallHeart, 80); // show the small heart image for 80 ms show(bigHeart, 160); // followed by the big heart for 160 ms delay(pulseDelay); // show nothing between beats } // Show a frame of an image stored in the array pointed to by the image // parameter. The frame is repeated for the given duration in milliseconds. void show(byte * image, unsigned long duration) { unsigned long start = millis(); // begin timing the animation while (start + duration > millis()) // loop until the duration has passed { for(int row = 0; row < 8; row++) { digitalWrite(rowPins[row], HIGH); // connect row to +5 volts for(int column = 0; column < 8; column++) { bool pixel = bitRead(image[row],column); if(pixel == 1) { digitalWrite(columnPins[column], LOW); // connect column to Gnd } delayMicroseconds(300); // a small delay for each LED digitalWrite(columnPins[column], HIGH); // disconnect column from Gnd } digitalWrite(rowPins[row], LOW); // disconnect LEDs } } } |

شکل 2: دو تصویر قلبی که در هر ضربان نمایش داده میشوند…
در اینجا، ستونها و ردیفها مانند بخش ۷.۸ بهصورت مالتیپلکس (بهنوبت سوئیچ) میشوند، اما مقدار اعمالشده به هر LED بر اساس تصاویری است که در آرایههای bigHeart و smallHeart ذخیره شدهاند. هر عنصر در آرایه نمایانگر یک پیکسل (یک LED منفرد) است و هر ردیف آرایه نمایانگر یک ردیف در ماتریس است. یک ردیف شامل هشت بیت است که با فرمت باینری نمایش داده میشود (که با حرف بزرگ B در ابتدای هر ردیف مشخص شده است). بیت با مقدار 1 نشان میدهد که LED مربوطه باید روشن باشد و 0 یعنی خاموش. افکت انیمیشن با سوئیچ سریع بین این آرایهها ایجاد میشود.
تابع loop بین هر ضربان مدت کوتاهی (800 میلیثانیه) صبر میکند و سپس تابع show را فراخوانی میکند؛ ابتدا با آرایه smallHeart و سپس با آرایه bigHeart.
تابع show هر عنصر در تمام ردیفها و ستونها را بررسی میکند و اگر بیت مربوطه 1 باشد، LED را روشن میکند. برای تعیین مقدار هر بیت از تابع bitRead استفاده میشود.
یک تأخیر کوتاه ۳۰۰ میکروثانیهای بین هر پیکسل، به چشم اجازه میدهد LED را به طور واضح ببیند. این زمانبندی بهگونهای انتخاب شده است که هر تصویر سریعاً تکرار شود (۵۰ بار در ثانیه) تا چشم، چشمکزدن LED را تشخیص ندهد.
در اینجا یک نسخهٔ تغییریافته وجود دارد که سرعت تپش قلب را بر اساس مقدار دریافتی از یک سنسور تغییر میدهد. میتوانید این را با یک مقاومت متغیر متصل به پایهی آنالوگ ۰ تست کنید. از سیمکشی و کد نشاندادهشده قبلی استفاده کنید، با این تفاوت که تابع loop را با این کد جایگزین کنید:
|
1 2 3 4 5 6 7 8 |
void loop() { int sensorValue = analogRead(A0); // read the analog in value int pulseRate = map(sensorValue,0,1023,40,240); // convert to beats / minute int pulseDelay = (60000 / pulseRate); // milliseconds to wait between beats show(smallHeart, 80); // show the small heart image for 100 ms show(bigHeart, 160); // followed by the big heart for 200 ms delay(pulseDelay); // show nothing between beats } |
این نسخه زمان تأخیر بین ضربانها را با استفاده از تابع map محاسبه میکند تا مقدار دریافتی از سنسور را به ضربان در دقیقه تبدیل کند. این محاسبه زمان لازم برای نمایش قلب را در نظر نمیگیرد، اما اگر بخواهید زمانبندی دقیقتری داشته باشید، میتوانید ۲۴۰ میلیثانیه (۸۰ میلیثانیه به اضافه ۱۶۰ میلیثانیه برای دو تصویر) را از آن کم کنید.
من کاپیتان آردوینو، اسمم میلاده و اینجا هستم تا تجربیاتم در رابطه با آردوینو رو با شما به اشتراک بزارم!
سیسوگ با افتخار فضایی برای اشتراک گذاری دانش شماست. برای ما مقاله بنویسید.