مقدمه
شاید برای شما هم بارها این سوال پیش آمده باشد که در مواقعی که از میکروکنترلرهای کوچک با تعداد پایههای بسیار محدود استفاده کردهایم، چگونه میتوانیم تعداد پورتهای ورودی/خروجی بیشتری بسازیم. برای نمونه، میخواهیم تعداد زیادی المان الکترونیکی نظیر LED، سونسگمنت، رله و یا صرفا جهت انتقال دادههای بالاتر از 8 بیت، دسترسی به تعداد بیشتری پورت ورودی/خروجی داشته باشیم، ولی با توجه به محدودیتهای بعضی از میکروکنترلرها، نمیتوانیم این کار را انجام دهیم. نمونهای دیگر از این مسئله را میتوان برای نمایشگرهای ماتریسی (تابلوهای روان) بیان کرد که چگونه میتوان تابلو با ابعاد وسیع تولید کرد که تعداد پورت زیادی را اشغال نکند.
جواب به این سوالها را در ادامه این مطلب بررسی خواهیم کرد.
گسترش تعداد پورتهای خروجی
در بیشتر اوقات، از تکنیکهایی نظیر مولتیپلکسینگ جهت رفعنمودن این مسئله بهره میبرند؛ بهعنوان نمونه، در درایوکردن تعداد زیادی سونسگمنت؛ ولی یکی از مشکلات ملموس این تکنیک که شاید شما نیز با آن برخورد کرده باشید، پایین آمدن Refresh Rate در نمایشگرها میباشد که تاثیر مستقیمی در ظاهر مسئله دارد؛ به این دلیل که در روش مولتیپلکسینگ معمولی، یک خط انتقال داده مشترک برای همه بلاکها مورداستفاده قرار میگیرد و این خط انتقال بهصورت نوبتی، دادههای مختص آن بلاکها را تبادل میکند. دادههای هر بلاک، تنها برای مدت بسیار کوتاهی روی آن بلاک قرار گرفته و باقی میماند و سریعا از مدار آن بلاک خارج میشود؛ و چنانچه این بلاکها بسیار زیاد باشد، سیکل روشنایی هر بلاک کاهش مییابد و در ظاهر موجب کمنورشدن کلی نمایشگر میشود. برای درک بهتر موضوع، یک نمونه از مولتیپلکسینگ در زیر آورده شده است:
همانطور که در انیمیشن مشاهده میکنید، هر بلاک برای مدتی معین روشن شده و سپس خاموش میشود. این مدت بستگی به تعداد سونسگمنتهای استفاده شده دارد. بهطور معمول هر سونسگمنت نیاز به حداقل یک میلیثانیه روشنبودن دارد تا بتواند نمایش مناسبی را انجام دهد. این مورد برای تعداد سونسگمنتهای کم اهمیت چندانی ندارد و چشم غیرمسلح تصویر را پیوسته خواهد دید؛ ولی در صورتی که این تعداد بیشتر شود، این پیوستگی از بین خواهد رفت و مولتیپلکسینگ عملا دیده خواهد شد، در کنار آن، با توجه به اینکه مدت روشنایی هر سگمنت نمیبایست از مقدار مشخص (حداقل یک میلیثانیه) کمتر شود، میزان Refresh Rate بسیار پایین خواهد آمده و این یک نقطه ضعف برای نمایشگرها محسوب میشود. برای درک بهتر موضوع، فرض کنید در ابتدا میخواهیم 8 سونسگمنت را مولتیپلکس کنیم، در بهترین حالت داریم: 8 عدد × یک میلیثانیه = 8 میلیثانیه؛ بهعبارتی میزان Refresh Rate برابر میشود با 1000 میلیثانیه تقسیمبر 8 میلیثانیه که برابر میشود با 125 هرتز که مقدار قابلقبولی است؛ حال با فرض اینکه خواسته باشیم 20 عدد سونسگمنت را با این روش مولتیپلکس کنیم، در نهایت میزان Refresh Rate برابر 50Hz خواهد شد که در واقع به مرز پیوستگی بینایی چشم انسان میرسد؛ این نشان میدهد که عملا نمیتوانیم بیش از 20 سونسگمنت را با این روش درایو کنیم.
تکنیک اول: رفع این مسئله با اضافهنمودن تکنیک Data Put & Hold با استفاده از آیسیهای Latch نظیر 74573 امکانپذیر است. تمامی موارد مشابه مولتیپلکسینگ بوده ولی در اینجا دادهها بر روی آن بلاک تا زمانی که داده جدیدی آماده شود، باقی خواهد ماند. بهعبارتی، در این روش، مشکل عدمپیوستگی کاملا رفع شده و در کنار آن مشکل Refresh Rate نیز تا حد زیادی رفع خواهد شد. در این روش، بهدلیل اینکه از آیسی لچ استفاده میشود و با توجه به پشتیبانی از فرکانسهای بالای Clock، زمان صرفشده برای ایجاد یک فریم از نمایش بسیار کوتاه خواهد شد. با فرض اینکه زمان صرفشده برای هر آیسی (و در نتیجه بلاک متناظر با آن) حداکثر یک میکروثانیه باشد (یعنی هر بلاک طی زمان یک میکروثانیه داده خود را دریافت کند و آنرا نمایش دهد — که به این زمان Response Time نیز گفته میشود)، با این اوصاف میتوان بیش از 16 هزار سونسگمنت را با این روش مولتیپلکس کرد بهطوریکه میزان Refresh Rate از 60Hz پایینتر نرود.
تکنیک دوم: حال نکتهای که در اینجا حایز اهمیت میشود این است که چطور میتوان این تعداد بسیار زیاد آیسی لچ را کنترل و بهعبارتی فرماندهی کرد. با توجه به اینکه پروسه مولتیپلکسینگ بهترتیب و طی یک Sequence قانونمند انجام میگیرد، نیازی به اشغالکردن یک یا چند پایه از میکروکنترلر بهازای هر آیسی نیست و میتوان از آیسیهای شیفت رجیستر (Shift Register) مانند 74164 استفاده کرد.
تکنیک سوم: بهطور کلی، آیسیهای شیفت رجیستر قابلیت تبدیل/آمادهکردن داده از سری به موازی را مهیا میکنند. بهعبارتی، میتوان با استفاده از کنترل تنها دو پایه از یک آیسی 74164، 8 عدد داده باینری را بر روی پایههای خروجی آن قرار داد. نقطه قوت آن این است که میتوان تعداد نامحدودی از این آیسیها را به یکدیگر ادغام کرد تا بتوان به تعداد نامحدود داده باینری را صرفا با استفاده از تنها 3 پایه (وجود پایه Reset/MR در این مورد نسبت به مورد قبلی ضروری خواهد بود) آماده کرد. از همین روش میتوان بهعنوان تکنیکی برای خطوط انتقال داده نیز استفاده کرد که تعداد پورتهای اشغال از میکروکنترلر را به حداقل میرساند و متقابلا، سیستم نیز کندتر خواهد شد و در میزان Refresh Rate نیز تاثیر خواهد گذاشت.
در پروژه آموزشی که در این مطلب ارایه شده است، این سه تکنیک با هم ادغام، و در دسترس مخاطبان قرار گرفته است.
مدار و توضیحات سختافزاری
همانطور که در مدار بالا مشاهده میکنید، صرفا با اشغال تنها 6 پایه از میکروکنترلر، قابلیت راهاندازی و نمایش داده بر روی 16 عدد سونسگمنت با میزان Refresh Rate بسیار بالا مهیا شده است؛ قابل ذکر است که این تعداد قابل گسترش و تعمیم به تعداد بیشتری سونسگمنت نیز میباشد، ولی بایستی توجه داشت که با افزایش تعداد این پورتهای خروجی، میزان Refresh Rate نیز بهمراتب کاهش خواهد یافت. 16 عدد آیسی لچ 74573 برای Data Put & Hold برای سونسگمنتها، یکعدد آیسی شیفت رجیستر (U3) جهت آمادهکردن خطوط انتقال داده (8 بیتی؛ در نتیجه تنها یک عدد آیسی جوابگو است)، و دو عدد آیسی شیفت رجیستر (U4 و U8) نیز جهت کنترل آیسیهای لچ مورداستفاده قرار گرفته است (که در واقع 8×2 پایه کنترلی آیسیهای لچ را کنترل میکند). برای نمونه، اگر خواسته باشیم مدار را برای 128 سونسگمنت گسترش دهیم، به 128 عدد آیسی لچ و 8 عدد آیسی شیفت رجیستر (جهت کنترل آیسیهای لچ) و یکعدد نیز برای خط انتقال داده نیاز خواهیم داشت و نکته جالب این است که صرفا با همان 6 پایه قابل انجام است.
توضیحات نرمافزاری (سورس کد)
برای راحتی و درک بهتر کد، توابعی تعبیه شده است که توسط آن میتوان دادهها بر روی پورت دلخواه قرار داد. قرارگرفتن این دادهها بر روی پورتها طی دو مرحله انجام میگیرد؛ مرحله اول، تنظیم دادهها (که توسط تابع setPort
) و مرحله دوم اعمال آنها (که توسط تابع apply
) انجام میگیرد:
تابع setPort
void setPort(int port, byte data);
این تابع داده ورودی آرگومان data
را بر روی شماره پورتی که توسط آرگومان ورودی port
مشخص میشود، تنظیم میکند. توجه داشته باشید که این تابع صرفا تنظیمکنند است، نه اعمال کننده.
تابع apply
void apply();
پس از تنظیم دادههای مختص هر پورت، حال نیاز داریم آن را در صورت نیاز اعمال کنیم؛ که با فراخوانی این تابع این عمل محقق میشود. پیچیدگی زمانی این تابع برابر است با: (تعداد بیتهای داده + 2) × تعداد پورتها + 1 میکروثانیه؛ که در این پروژه برابر است با (8 + 2) × 16 + 1 = 257 میکروثانیه. از این محاسبات میتوان دریافت که میزان Refresh Rate ایدهآل در این پروژه تقریبا برابر است با 3.8kHz؛ چنانکه میتوان تعداد پورتها را تا بیش از 1600 عدد افزایش داد تا بتوانیم Refresh Rate = 60Hz را داشته باشیم.
گسترش تعداد پورتهای ورودی
مکانیزم گسترش پورتهای ورودی نیز همانند بخش قبل (گسترش پورتهای خروجی) میباشد و تنها بایستی از آیسیهایی استفاده شود که بتوانند این قابلیت را مهیا کنند. همانطور که در بخش قبلی، دادهها بهترتیب بر روی خروجی قرار میگرفت، در این بخش نیز دادهها بهترتیب از روی ورودیها و بهصورت مولتیپلکسینگ خوانده میشود و از این لحاظ، نیاز به آیسیهای مولتیپلکسینگ نظیر 74251 احساس میشود. در بخش قبل، بیتهای داده طی تکنیک سوم، توسط یک آیسی شیفترجیستر آماده میشد؛ ولی در این بخش، توسط یک آیسی مولتیپلکسینگ بهترتیب خوانده میشود.
به دیاگرام منطقی آیسی 74251 توجه کنید؛ 8 خط ورودی داده منطقی که توسط سه پایه کنترل A و B و C میشود که قابلیت انتخاب شماره خط ورودی را به کاربر میدهد. به اینصورت که کاربر با مقداردهی به این سه پایه (در مبنای دو) میتواند 8 حالت مختلف (که در واقع شماره خط ورودی موردنظر را مشخص میکند) را پدید آورد. پس از انتخاب شماره خط ورودی توسط این سه پایه، داده متناظر با همان خط، به خروجی (یعنی پایه Y) منتقل خواهد شد. با بررسی این مورد، دریافت میشود که میتوانیم با روش مولتیپلکسینگ (که بهکمک آیسیهای لچ انجام میشود) این کار را میتوان بهراحتی انجام داد؛ به اینصورت که آیسیهای لچ وظیفه وارد/خارجکردن (بهترتیب) خطوط داده ورودی بر روی خطوط داده ورودی اصلی (که در واقع خطوط ورودی آیسی مولتیپلکسر است) را دارند. حال مسئله مهم دیگری که ایجاد میشود، نحوه کنترل این آیسیها میباشد؛ که همانطور که در بخش قبل نیز به آن اشاره شد، بهدلیل اینکه این سیستم بهصورت قانونمند و بهترتیبی مشخص قرار است عمل کند، میتوانیم برای کنترل آیسیهای لچ از همان آیسیهای شیفترجیستر استفاده کرد. علاوه بر این، برای کنترل پایههای کنترلی آیسی مولتیپلکسر، میتوان از آیسیهای شمارنده نظیر 74393 استفاده کرد.
به دیاگرام منطقی آیسی 74393 توجه کنید؛ صرفا با استفاده از دو پایه کنترلی Clock و Reset (که بهترتیب با عبارات CP و MR مشخص شدهاست) میتوان شمارش بر مبنای دو را تا سقف 4 بیت (اعداد صفر الی 15) انجام داد؛ که در این پروژه بهدلیل اینکه شمارش قرار است برای 8 خط انجام شود، تنها به شمارش سهبیتی نیاز داریم (در این پروژه، بیت چهارم برای پایه Output Enable آیسی مولتیپلکسر استفاده میشود).
مدار و توضیحات سختافزاری
همانطور که در مدار بالا مشاهده میکنید، صرفا با اشغال تنها 6 پایه از میکروکنترلر، قابلیت خواندن و نمونهبرداری از 16 پورت ورودی مجزا (یا بهعبارتی 8×16 پایه ورودی) مهیا شده است؛ قابل ذکر است که این تعداد قابل گسترش و تعمیم به تعداد بیشتری پورت ورودی نیز میباشد، ولی بایستی توجه داشت که با افزایش تعداد این پورتها، نرخ/فرکانس نمونهبرداری نیز بهمراتب کاهش خواهد یافت. 16 عدد آیسی لچ 74573 برای وارد/خارجکردن هر پورت به مسیر اصلی ورودی داده (که پایههای ورودی آیسی مولتیپلکسینگ 74251 میباشد)، یکعدد آیسی مولتیپلکسر (U2) جهت اسکن دادههای ورودی اصلی، یکعدد آیسی شمارنده (U4) برای کنترل آیسی مولتیپلکسر و دو عدد آیسی شیفت رجیستر (U5 و U6) نیز جهت کنترل آیسیهای لچ مورداستفاده قرار گرفته است. برای نمونه، اگر خواسته باشیم مدار را برای 128 پورت ورودی گسترش دهیم، به 128 عدد آیسی لچ و 8 عدد آیسی شیفترجیستر (جهت کنترل آیسیهای لچ) نیاز خواهیم داشت و نکته جالب این است که صرفا با همان 6 پایه قابل انجام است. مقادیر ورودی خواندهشده از روی پورتها، بر روی متغیری واقع بر حافظه SRAM میکروکنترلر ذخیره میشود (که در شکل مشاهده و در بخش توضیحات نرمافزاری به آن خواهیم پرداخت).
توضیحات نرمافزاری (سورس کد)
برای راحتی و درک بهتر کد، توابعی تعبیه شده است که توسط آن میتوان دادهها را از روی پورت دلخواه خواند. خواندن این دادهها از روی پورتها طی دو مرحله انجام میگیرد؛ مرحله اول، اسکن کامل و دریافت مقادیر همه پورتهای ورودی (که توسط تابع sync
)، و مرحله دوم دریافت داده مربوط به یک پورت مشخص (که توسط تابع getPort
) انجام میگیرد. مقادیر خواندهشده از روی پورتها، بر روی یک متغیر آرایهای از نوع بایت (در حافظه SRAM) ذخیره شده و سپس میتوان بهصورت مستقیم (از روی همین آرایه) و یا بهصورت غیرمستقیم (از طریق تابع getPort
) مقدار 8 بیتی مختص هر پورت را خواند. اهمیت استفاده از تابع sync
در این است که از روش Pooling در برنامهنویسی تا حدودی جلوگیری میکند و تنها زمانی که لازم باشد، از ورودیها نمونهبرداری کند و این به حفظ فرکانس آزاد CPU نیز کمک خواهد کرد.
تابع sync
void sync();
قبل از خواندن مقادیر، در صورت نیاز لازم است ابتدا مقادیر موجود بر روی حافظه SRAM با مقادیر حقیقی روی پورتها، همگامسازی یا به بیانی دیگر Sync شود که با فراخوانی این تابع محقق خواهد شد.
تابع getPort
byte getPort(int port);
با دادن شماره پورت ورودی به آرگومان port
، میتوان مقدار 8 بیتی آن پورت مشخص را در خروجی تابع دریافت کرد.
بخش دانلود
شامل موارد زیر:
- فایل شبیهسازی Proteus ورژن 8.9 و یا بالاتر
- سورسکد کامل بهزبان سی C تحت کامپایلر CodeVision AVR
توجهات:
عالی