مدار منطقی – منطق دیجیتال

منطق دیجیتال  یا منطق بولی (Boolean logic)،یکی از اساسی‌ترین مفاهیم در ساخت سیستم‌های کامپیوتری مدرن است. منطق دیجیتال مجموعه قواعدی است که گرفتن تصمیمات پیچیده را بر اساس سوال‌های «بله/خیر» ممکن می‌کند. در این آموزش چکیده ای از کتاب مدار منطقی را مطرح می‌کنیم.

مدار منطقی - منطق دیجیتال

مدار منطقی

مدارهای منطقی دیجیتال به دو دسته تقسیم می‌شوند:

  • مدار منطقی ترکیبی (Combinational Circuits)
  • مدار منطقی ترتیبی (Sequential Circuits)

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

عموماً مدارات ترتیبی به وسیله‌ی بلوک‌هایی از مدارات ترکیبی ساخته می‌شوند. این بلوک‌ها توسط عناصر حافظه‌ای که به وسیله‌ی سیگنال کلاک فعال می‌شوند از هم جدا شده‌اند.

مدار منطقی و برنامه‌ نویسی

منطق دیجیتال اهمیت زیادی در برنامه‌نویسی دارد. آشنایی با مدار منطقی گرفتن تصمیم‌های پیچیده در برنامه‌نویسی را ممکن می‌کند. برنامه‌نویسی نکات ریزی دارد که فهم آنها بسیار مهم است.حالا بعضی اصول پایه ای آن را بیان میکنیم.

مدار منطقی ترکیبی

مدار منطقی ترکیبی

تمامی مدارهای ترکیبی از پنج گیت (Gate) منطقی اساسی تشکیل شده‌اند:

  • گیت AND – خروجی وقتی ۱ است که هر دو ورودی ۱ باشند.
  • گیت OR – اگر حداقل یکی از ورودی‌ها ۱ باشد، خروجی ۱ خواهد بود.
  • گیت XOR – خروجی ۱ است، اگر فقط یکی از ورودی‌ها ۱ باشد.
  • گیت NAND – اگر حداقل یکی از ورودی‌ها ۰ باشد، خروجی ۱ خواهد بود.
  • گیت NOR – خروجی وقتی ۱ است که هر دو ورودی ۰ باشند.

گیت دیگری نیز وجود دارد که گیت نات (NOT) یا وارونگر می‌باشد. وارونگرها در واقع گیت منطقی محسوب نمی‌شوند، زیرا تصمیم خاصی نمی‌گیرند. سایر گیت‌ها با توجه به مقادیر ورودی، به نوعی درباره خروجی تصمیم می‌گیرند ولی گیت نات تنها یک ورودی را دریافت می‌کند و نقیض آن را در خروجی قرار می‌دهد. اگر ورودی یک وارونگر ۱ باشد، خروجی ۰ خواهد بود و اگر ورودی ۰ باشد، خروجی ۱ خواهد بود.

برای شکل بالا به نکات زیر توجه کنید:

  • معمولاً اسم گیت روی آن نوشته نمی‌شود و برای شناسایی تنها شکل آن کافی خواهد بود.
  • نشانه‌گذاری استاندارد برای پایه‌های ورودی و خروجی به صورت A-B-Q می‌باشد.
  • گیت‌های استاندارد دارای دو ورودی هستند، اما گیت‌هایی نیز وجود دارند که بیشتر از دو ورودی دارند. اما تمامی گیت‌ها فقط و فقط یک خروجی دارند.

معمولاً مدارهای منطقی با استفاده از همین شش نماد نشان داده می‌شوند. ورودی‌ها در سمت چپ قرار گرفته و خروجی‌ها نیز در سمت راست قرار می‌گیرند. با وجود اینکه ورودی‌ها ‌می‌توانند به یکدیگر وصل شوند؛ اما خروجی گیت‌ها هرگز نباید مستقیماً به هم متصل شوند. اگرچه یک خروجی می‌تواند به یک یا چندین ورودی وصل شود.

جدول صحت مدار منطقی

برای توصیف بلوک‌های ساده و ابتدایی توضیحات ارائه شده کافی به نظر می‌رسد. اما می‌توان از ابزار مفیدی به نام «جدول صحت» نیز استفاده کرد. جداول صحت خروجی یک مدار را با توجه به ورودی‌های ممکن تعیین می‌کنند. جداول صحت شش گیت اصلی در زیر آورده شده‌است:

جدول صحت مدار منطقی

جداول صحت را می‌توان به اندازه‌ی دلخواه و با تعداد ورودی و خروجی دلخواه توسعه داد. به عنوان مثال جدول صحت مداری با چهار ورودی به صورت زیر است:

جدول صحت مدار منطقی

جبر بولی

برای راحتی بیشتر می‌توان یک عملیات منطقی را در قالب عبارات ساده‌ی ریاضی نشان داد. برای این منظور به عملیات‌های AND، OR، XOR و NOT نمادهای منحصر به فردی اختصاص داده می‌شود.

  • A AND B به صورت AB یا A • B نوشته می‌شود.
  • A OR B به صورت A + B نوشته می‌شود.
  • A XOR B به صورت A ⊕ B نوشته می‌شود.
  • NOT A به شکل A’ یا نوشته می‌شود.

دو گیت NAND و NOR با متمم گرفتن از عبارات AND و OR نشان داده می‌شوند.

  • A NAND B به شکل‌های (AB)’، (A • B)’  و یا  نشان داده می‌شود.
  • A NOR B نیز به شکل ()’ یا  نمایش داده می‌شود.

مدار منطقی ترتیبی

منطق ترکیبی بسیار سودمند است، اما امروزه تنها با تکیه بر منطق ترکیبی و بدون استفاده از منطق ترتیبی، پردازش‌های کامپیوتری امکان‌پذیر نخواهد بود.

منطق ترتیبی استفاده از حافظه را در سیستم‌ها ممکن می‌کند. همانگونه که قبلاً اشاره شد، خروجی مدارات ترتیبی پس از یک تأخیر مشخص تولید می‌شود. مقدار این تأخیر به عوامل بسیار زیادی از جمله فرآیند ساخت قطعات مورد استفاده، دمای سیلیکون و پیچیدگی مدار بستگی دارد. اگر خروجی نهایی یک مدار به نتایج دو مدار ترکیبی دیگر وابسته باشد و این نتایج در زمان‌های متفاوتی آماده شوند (که در واقعیت همین موضوع اتفاق می‌افتد)، مدار ترکیبی دچار یک «خطای لحظه‌ای» (glitch) می‌شود و در نتیجه ممکن است خروجی ثابتی مطابق با عملیات مورد نظر نداشته باشد.

یک مدار ترتیبی در زمان‌های معینی از خروجی نمونه برداری کرده و آن را منتشر می‌کند. اگر ورودی مدار در بین این زمان‌های معین موقتاً تغییر کند از آن چشم‌پوشی می‌شود. معمولاً این زمان نمونه برداری در تمامی مدار، همزمان یا سنکرون (synchronized) است که به آن «کلاک» (Clock) گفته می‌شود. وقتی از سرعت یک کامپیوتر صحبت می‌کنیم، منظور همان مقدار کلاک است. البته می‌توان مدارهای غیرهمزمان یا آسنکرون (asynchronous) نیز طراحی کرد که با سیگنال کلاک سنکرون نیستند.اما این سیستم ها مشکلات بزرگی را ایجاد میکنند برای همین در اینجا به آنها نمی پردازیم.

هر مدار دیجیتال دو مشخصه‌ی حداکثر زمان تأخیر و حداقل زمان تأخیر دارد. اگر مدار از حد انتظار سریع‌تر بوده و تأخیر آن کمتر از حداقل زمان تأخیر باشد، مدار به درستی کار نخواهد کرد. به طور مثال اگر این مدار جزئی از یک دستگاه بزرگتر مانند CPU کامپیوتر باشد، کل دستگاه غیر قابل استفاده خواهد شد. در صورتی که مدار از حد انتظار کندتر بوده و تأخیر آن از حداکثر زمان تأخیر مدار بیشتر باشد، می‌توان سرعت کلاک را برای تطبیق با کندترین بخش سیستم کاهش داد. با افزایش دمای سیلیکون‌های سازنده‌ی یک مدار، حداکثر زمان‌های تأخیر آن نیز افزایش می‌یابند. به همین دلیل کامپیوترها با افزایش دما و یا افزایش سرعت کلاک (overclocking)، عملکرد بی‌ثباتی دارند.

مطلب پیشنهادی:  سون سگمنت چیست؟

عناصر مدارهای منطقی ترتیبی

همانند منطق ترکیبی، تعدادی عنصر مداری اصلی وجود دارند که بلوک‌های سازنده‌ی مدارهای ترتیبی را شکل می‌دهند. این عناصر در واقع از همان گیت‌های منطقی ساخته شده‌اند. با این تفاوت که برای تثبیت ورودی، از خروجی فیدبک (بازخورد) گرفته شده است. این عناصر به دو دسته‌ی «لچ‌ها» (latch) و «فلیپ‌فلاپ‌ها» (flip-flop)  تقسیم می‌شوند. اگرچه گاهی اوقات این اصطلاحات به جای یکدیگر نیز به کار می‌روند، اما لچ‌ها به دلیل نداشتن کلاک، کاربرد کمتری دارند. در ادامه درباره فلیپ‌فلاپ‌ها صحبت خواهیم کرد.

فلیپ‌فلاپ نوع D

فلیپ‌فلاپ نوع D

فلیپ‌فلاپ D ساده‌ترین نوع فلیپ‌فلاپ است. در این فلیپ‌فلاپ‌ها،در لبه‌ی کلاک، ورودی به خروجی وصل میشود. (در بیشتر موارد فلیپ‌فلاپ‌ها به لبه‌ی بالارونده‌ی کلاک حساس هستند، اما بعضاً در ورودی کلاک یک وارونگر (NOT) قرار داده می‌شود تا حساس به لبه‌ی پایین‌رونده شود).

معمولاً ورودی کلاک با قرارگیری شکل یک مثلث کوچک چسبیده به کنار شماتیک از سایر ورودی‌ها متمایز می‌گردد. اکثر فلیپ‌فلاپ‌ها دو پایه‌ی خروجی دارند: یک خروجی معمولی و یک خروجی متمم (وارون).

فلیپ‌فلاپ نوع T

مدار-منطقی- فلیپ‌فلاپ نوع T

فلیپ‌فلاپ T تنها کمی پیچیده‌تر از فلیپ‌فلاپ D است T. مخفف کلمه‌ی Toggle به معنای «تغییر وضعیت» است. اگر ورودی T مقدار ۱ باشد، با لبه‌ی کلاک وضعیت خروجی تغییر خواهد کرد (یعنی اگر خروجی ۱ باشد ۰ می‌شود و برعکس)؛ و اگر ورودی ۰ باشد، خروجی همان مقدار قبلی خود باقی مانده و تغییری نمی‌کند. این فلیپ-فلاپ نیز دارای دو خروجی است که دومی همان متمم خروجی اصلی است.

یکی از کاربردهای فلیپ‌فلاپ T در مدارات تقسیم فرکانس کلاک است. اگر ورودی T همواره در وضعیت ۱ نگه داشته شود، فرکانس سیگنال خروجی نصف فرکانس سیگنال کلاک خواهد بود. در نتیجه با به‌کارگیری زنجیره‌ای از فلیپ‌فلاپ‌های T می‌توان کلاک‌هایی کندتر از کلاک اصلی تولید کرد.

فلیپ‌فلاپ نوع JK

مدار-منطقی- فلیپ‌فلاپ نوع JK

برای تشریح عملکرد فلیپ‌فلاپ JK برخلاف دو مورد قبلی به جدول صحت نیاز داریم. این فلیپ‌فلاپ دارای دو ورودی J و K بوده و خروجی بسته به وضعیت قبلی خودش و همچنین بر اساس وضعیت دو ورودی، می‌تواند ثابت بماند یا ۱شود (set) یا ۰ شود (clear) و یا تغییر وضعیت دهد. مانند سایر فلیپ‌فلاپ‌ها مقادیر ورودی و خروجی تنها در لحظات وقوع لبه‌ی کلاک اهمیت دارند.

زمان‌های راه‌اندازی ، توقف و انتشار در مدار های منطقی

مدارهای ترتیبی علاوه بر «تأخیر انتشار» (Propagation Delay)، دارای پارامترهای «زمان راه‌اندازی» (Setup Time) و «زمان توقف» (Hold Time) نیز می‌باشند. آشنایی با این سه موضوع برای طراحی صحیح مدارهای ترتیبی بسیار ضروری است.

زمان راه‌اندازی، حداقل فاصله‌ی زمانی است که سیگنال ورودی فلیپ‌فلاپ باید زودتر از لبه‌ی بالارونده‌ی کلاک برسد تا مقدار آن به درستی دریافت و نگه داشته شود. به همین ترتیب، زمان توقف حداقل فاصله‌ی زمانی پس از لبه‌ی بالارونده‌ی کلاک است که سیگنال باید ثابت و بدون تغییر بماند.

در حالی که زمان راه‌اندازی و توقف به صورت مقادیر کمینه بیان شدند، مقدار تأخیر انتشار به عنوان یک مقدار بیشینه بیان می‌شود. به بیان ساده‌تر، تأخیر انتشار حداکثر فاصله‌ی زمانی است که پس از یک لبه‌ی پایین‌رونده‌ی کلاک انتظار دارید تا سیگنال جدید را در خروجی مشاهده کنید. برای درک بهتر این مفاهیم به شکل زیر توجه کنید:

مدارات منطقی

توجه کنید در شکل بالا، حالات گذار یا لبه‌های سیگنال‌ها با خطوط مورب نشان داده شده‌اند. این عمل به دو علت انجام گرفته است: اولاً لبه‌های کلاک و دیتا هیچ‌گاه زاویه‌ی قائمه ندارند و همواره زمان نزول و زمان صعود آنها غیر صفر است. ثانیاً زمان‌های مختلفی که خطوط عمودی سیگنال‌ها را قطع کرده‌اند، بهتر مشخص است.

ترکیب این سه مقدار، بالاترین سرعت کلاک قابل استفاده در سیستم را مشخص می‌کند. اگر مجموع تأخیر انتشار یک بخش از مدار و زمان راه‌اندازی بخش بعدی، از فاصله‌ی زمانی بین لبه‌ی پایین رونده‌ی یک پالس کلاک و لبه‌ی بالارونده‌ی پالس بعدی بیشتر باشد، سیگنال دیتا در ورودی جزء دوم ثابت و پایدار نخواهد بود و باعث بروز رفتار پیش‌بینی نشده‌ای در مدار خواهد شد.

شبه‌پایداری

عدم توجه به زمان‌های راه‌اندازی و توقف در هنگام طراحی مدار موجب بروز پدیده‌ای به نام «شبه‌پایداری» (Metastability) می‌شود. هنگامی که یک مدار در وضعیت شبه‌پایدار قرار دارد، ممکن است خروجی فلیپ‌فلاپ بین دو وضعیت ۰ و ۱ با سرعت زیادی (اغلب بالاتر از فرکانس کلاک مدار) نوسان کند.

مدار-منطقی

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

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

یک راه حل برای این مشکل این است که تمام ورودی‌ها را از یک جفت فلیپ‌فلاپ D به صورت طبقه‌طبقه (cascade) عبور دهیم. در این وضعیت حتی اگر فلیپ‌فلاپ اول به وضعیت شبه‌پایداری برود، امیدوار خواهیم بود تا قبل از رسیدن لبه‌ی بعدی کلاک به وضعیت پایدار برسد و اجازه دهد تا فلیپ‌فلاپ دوم داده را به درستی بخواند. این کار باعث ایجاد تأخیر یک‌سیکلی (یک دوره‌ی تناوب از سیگنال کلاک) در لبه‌های داده‌ی ورودی می‌شود که در مقایسه با ریسک ناشی از شبه‌پایداری چندان مهم نیست.

مطلب پیشنهادی:  ارتباط SPI - معرفی پروتکل ارتباطی سریال SPI

منطق بولی در برنامه‌نویسی

منطق بولی در دنیای برنامه‌نویسی نیز کاربرد بسیار زیادی دارد. اکثر برنامه‌ها درخت‌های تصمیمی (Decision Trees) هستند که در صورت برقراری یک شرط، کار خاصی را انجام می‌دهند. برای توضیح این موضوع، از کدهای C در محیط آردوئینو استفاده خواهیم کرد.

منطق بیتی

منظور از «منطق بیتی»، دسته‌ای از عملیات‌های منطقی است که نتیجه‌ی آنها تنها یک مقدار می‌باشد. به طور مثال در قطعه کد زیر:

language:cpp
byte a = b01010101;
byte b = b10101010;
byte c;

می‌توان با استفاده از a و b یک عملیات بیتی انجام داده و نتیجه را در c ریخت. چگونگی این کار در قطعه کد زیر مشخص است:

c = a & b; // bitwise AND-ing of a and b; the result is b00000000
c = a | b; // bitwise OR-ing of a and b; the result is b11111111
c = a ^ b; // bitwise XOR-ing of a and b; the result is b11111111
c = ~a; // bitwise complement of a; the result is b10101010

به عبارت دیگر، در نتیجه‌ی به دست آمده هر بیت از انجام عملیات موردنظر روی دو بیت منتاظر در عملوندها به دست می‌آید.
استفاده از عملگرهای بیتی، دستکاری رجیسترها را راحت‌تر کرده است. با این عملگرها می‌توان به‌طور انتخابی تک بیت‌ها را یک (set) و صفر (clear) کرد و یا تغییر وضعیت داد (toggle). همچنین می‌توان ۰ یا ۱ بودن یک یا چندین بیت را تشخیص داد. مثال‌های زیر نحوه‌ی استفاده از این عملگرها را نشان می‌دهد. ضمناً هر «نیبل» (nibble) چهار بیت است:

c = b00001111 & a; // clear the high nibble of a, but leave the low nibble alone.
// the result is b00000101.
c = b11110000 | a; // set the high nibble of a, but leave the low nibble alone.
// the result is b11110101.
c = b11110000 ^ a; // toggle all the bits in the high nibble of a.
// the result is b10100101.

همه عملگرهای بیتی می توانند با علامت مساوی ترکیب شده و به خود بیت اعمال شوند:

a ^= b11110000; // XOR a with b11110000 and store the result back in a
b |= b00111100; // OR b with b00111100 and store the result back in b

شیفت بیتی

یکی از عملیات‌های بیتی پرکاربرد بر روی داده‌ها، شیفت بیتی است. در این عملیات، بیت‌های عملوند به تعداد دفعات مشخصی به سمت چپ یا راست منتقل می‌شوند. با یک شیفت به هر طرف، آخرین بیت از همان سمت حذف شده و به سمت دیگر یک ۰ اضافه می‌شود.

byte d = b11010110;
byte e = d>>2; // right-shift d by two positions; e = b00110101
e = e<<3; // left-shift e by three positions; e = b10101000

بعداً در مورد کاربردهای شیفت بیتی بیشتر صحبت خواهیم کرد.یکی از مهم‌ترین کاربردهای شیفت بیتی در عملیات‌های ضرب و تقسیم است. هر شیفت به راست معادل تقسیم بر دو بوده (اگرچه باقیمانده از دست می‌رود) و هر شیفت به چپ معادل ضرب در دو می‌باشد. معمولاً عملیات‌های ضرب و تقسیم در پردازنده‌های کوچک، زمان زیادی طول می‌کشد (مانند آردوئینو)؛ اما شیفت‌های بیتی کارآمدتر بوده و کاربرد زیادی نیز دارند.

عملگرهای مقایسه‌ای

دسته‌ای از عملگرها وجود دارند که دو مقدار را مقایسه کرده و با توجه به نتیجه‌ی مقایسه، در خروجی مقدار «صحیح» (TRUE) یا «غلط» (FALSE) می‌دهند.

  • «== »:  مساوی است با (اگر مقادیر برابر باشند نتیجه true و در غیر این صورت false خواهد بود)
  • :«=!»  مساوی نیست با (اگر مقادیر متفاوت باشند true است)
  • :« > » بزرگتر است از (اگر عملوند سمت چپ بزرگتر از عملوند سمت راست باشد true است)
  • : «<» کوچکتر است از (اگر عملوند سمت چپ کوچکتر از عملوند سمت راست باشد true است)
  • : «>=» بزرگتر یا مساوی است با (اگر عملوند سمت چپ بزرگتر یا مساوی با عملوند سمت راست باشد true است)
  • : «<= »کوچکتر یا مساوی است با (اگر عملوند سمت چپ بزرگتر یا مساوی با عملوند سمت راست باشد true است)

معمولاً یکسان بودن نوع داده‌هایی که مقایسه می‌شوند اهمیت زیادی دارد. به طور مثال در صورت مقایسه‌ی یک داده از نوع int با داده‌ای از نوع byte خطا رخ می‌دهد.

عملگرهای منطقی

این دسته از عملگرها بدون تولید مقدار جدیدی، خروجی true یا false می‌دهند. عملگرهای منطقی شباهت بسیار زیادی به «حروف ربط» دارند. به طور مثال معادل جمله‌ی «اگر هوا بارانی نباشد و باد بوزد، بادبادک هوا خواهیم کرد.» در زبان C به صورت زیر است:

if ( (raining != true) && (windy == true) ) flyKite();

به پرانتزها در اطراف دو بند شرطی if دقت کنید. اگرچه وجود آنها الزامی نیست، اما گروه‌بندی این بندهای شرطی با پرانتز، به خوانایی بیشتر برنامه کمک زیادی می‌کند.

همچنین عملگر AND منطقی (&&) بر اساس اینکه بندهای شرطی برقرار باشند و یا نباشند، یک جواب true یا false تولید می‌کنند. البته می‌توان در هر یک از بندهای شرطی مقدار عددی نیز قرار داد.

if ( (raining != true) && ( (windSpeed >= 5) || (reallyBusy != true) ) ) flyKite();

معنی عبارت بالا این است که اگر هوا بارانی نباشد، مادامی که سرعت وزش باد بیشتر از ۵ باشد یا سرمان شلوغ نباشد، بادبادک هوا خواهیم کرد.

توجه کنید اگر پرانتزهای دور عبارت (windSpeed >= 5) || (reallyBusy != true) را حذف کنیم، دستور مبهمی نوشته‌ایم که ممکن است نتیجه‌ی دلخواه ما را تولید نکند. اکنون که با نحوه‌ی نوشتن دستورات پیچیده‌ی منطقی آشنا شده‌ایم، می‌توانیم با انواع دستورات شرطی آشنا شده و کنترل بهتری بر روی جریان اجرای کدهای برنامه داشته باشیم.

نحوه کنترل

حالا که می توانیم دستورات منطقی پیچیده ایجاد کنیم، بیایید به کارهایی که می توانیم با پاسخ به آن سوالات انجام دهیم، نگاه کنیم.

مطلب پیشنهادی:  ارتباط سریال - پروتکل UART

دستور if / else if / else

در ساده‌ترین ساختارهای تصمیم‌گیری از if/else استفاده می‌شود. دستور if/else if امکان قرار دادن مجموعه‌ای از تست‌ها را به ما می‌دهد؛ اما در هر لحظه تنها امکان اجرای یکی از آنها وجود دارد.

if ( reallyBusy == true ) workHarder();
else if ( (raining != true) && (windy == true) ) flyKite();
else work();

با توجه به دستورات بالا، اگر خیلی سرمان شلوغ باشد، هرگز بادبادک هوا نمی‌کنیم؛ ولی اگر آنقدرها سرمان شلوغ نباشد و شرایط آب‌وهوایی مطابق میل ما نباشد، به کار روزانه‌ی خودمان می‌پردازیم.در مثال بالا دستور ()else if را به ()if تبدیل می‌کنیم.

if ( reallyBusy == true ) workHarder();
if ( (raining != true) && (windy == true) ) flyKite();
else work();

حالا، اگر روز خوبی را برای بادبادک هوا کردن داشته باشیم، حتی اگر واقعا سرم شلوغ باشد، برای مدت بسیار کوتاهی سخت کار می کنم،تا زمانی که احساس کنم کافی است. از سوی دیگر، اگر روز خوبی نباشد، وضعیت سخت کار کردن من سریعا بعد از شروع به کار کردنم به درست کردن کار های قبلی اختصاص می یابد!

می توانید تصور کنید که اگر “سخت کار کردن” با “روشن کردن ” LED و “کار کردن” با   “خاموش کردن”LED  جایگزین یکدیگر شوند چه اتفاقی خواهد افتاد. در مورد اول، چراغ ممکن است برای مدتی روشن، یا خاموش باشد. در حالت دوم، حتی بدون در نظر گرفتن وضعیت پرچم ” reallyBusy “،  LED بعد از اینکه اولین دستور if () آن را روشن کرد، خاموش خواهد شد، وتعجب میکنید که چرا نور ” reallyBusy”  هرگز روشن نمی شود.

دستور switch / case / default

به‌جای استفاده از زنجیره‌ای از دستورات if/else می‌توان از دستور switch/case/default استفاده کرد. اگرچه دستور ()if دستور قدرتمندتری است، اما این دستور خوانایی بیشتری دارد.

switch(menuSelection) {
case '1':
doMenuOne();
break; 
case '2':
doMenuTwo();
break;
case '3':
doMenuThree();
break;
default:
flyKite();
break;
}

اگرچه دستور ()switch تنها قابلیت بررسی برابر بودن مقادیر را دارد، اما همین موضوع نیز بسیار پرکاربرد است. دو مورد واقعا مهم در مورد این موضوع وجود دارد: دستور “break؛” و default” case “.

default قسمتی است که در صورت اجرا نشدن هیچ یک از case ها، اجرا می‌شود. نوشتن default کاملاً الزامی نیست و اگر default وجود نداشته باشد، در صورت اجرا نشدن هیچ یک از case ها، هیچ عملی انجام نمی‌شود. البته بهتر است که این قسمت را در برنامه‌ی خود قرار دهید.

break از شرط فعلی خارج شده و اجرای case مورد نظر را خاتمه می‌دهد. استفاده از break در تمام دستورات شرطی امکان‌پذیر است. عدم استفاده از break در انتهای هر case، باعث ایجاد خطا در برنامه خواهد شد.

حلقه‌های while و do/while

تا این جای کار با کدهایی آشنا شده‌ایم که تنها برای یک بار تصمیم‌گیری به کار می‌روند؛ اما برای تکرار یک عمل (مادامی که شرط برقرار باشد) از دستورات ()while و ()do…while استفاده می‌کنیم.

while (windy == true) flyKite();

هنگامی که برنامه به دستور while می‌رسد، ابتدا شرط را بررسی می‌کند (در مثال بالا بارانی بودن هوا) و اگر این شرط برقرار بود، کد را اجرا می‌کند. بعد از اجرای کد، بار دیگر شرط ارزیابی شده و در صورت برقرار بودن، دوباره کد اجرا می‌شود. این فرآیند تا زمانی که شرط حلقه false شود و یا برنامه با دستور break مواجه شود، تکرار خواهد شد.

در صورت نیاز می‌توان از دستور if()، دستور switch()، حلقه‌ی while() دیگر و … درون حلقه‌ی while() استفاده کرد.

while (windy == true) {
flyKite();
if (bossIsMad == true) break;
}

در حلقه‌ی بالا تا زمانی که هوا بارانی باشد بادبادک هوا خواهیم کرد، مگر اینکه باران ببارد.

حلقه‌ی while() را با اندکی تغییر می‌توان به حلقه‌ی do…while() تبدیل کرد.

do {
flyKite();
} while (windy == true);

در do…while() کدهای داخل کروشه حداقل یک بار اجرا می‌شوند، حتی اگر شرط حلقه برقرار نباشد. مثلاً در مثال زیر بادبادک هوا کردن حداقل یک بار اتفاق می‌افتد، حتی در نبود باد.

به عنوان نکته‌ی پایانی، با نوشتن عبارت TRUE به جای شرط حلقه، آن کد بی‌نهایت بار اجرا می‌شود.

while(true) {
flyKite();
}

در کد بالا، بدون توجه به بارش باران، وزش باد و … بادبادک هوا خواهیم کرد. همانگونه که اشاره شد اجرای این کد خودبه‌خود متوقف نمی‌شود، اما کماکان می‌توان با استفاده از دستور break از حلقه خارج شد.

حلقه‌ی ()for

آخرین نوع دستورات شرطی که ما باید در نظر بگیریم حلقه ()for است از حلقه‌ی for() برای اجرای قطعه‌ای از کد به تعداد دفعات مشخص استفاده می‌شود. در مثال زیر نحوه‌ی استفاده از حلقه‌ی for() مشخص است:

for (byte i = 0; i < 10; i++) {
Serial.print("Hello, world!");
}

درون پرانتز مربوط به حلقه‌ی for سه قسمت قرار دارد که به وسیله‌ی سمی‌کالن (;) از هم جدا شده‌اند. در قسمت اول یک متغیر تعریف و مقدار دهی می‌شود. این متغیر در قسمت‌های شرطی و شمارنده کاربرد دارد. قسمت دوم شرط حلقه است. در صورت برقراری این شرط، حلقه اجرا شده و در غیر این صورت برنامه از حلقه خارج می‌شود. آخرین قسمت نیز گام یا شمارنده‌ی حلقه است که کاهنده و یا یک افزاینده قرار می‌گیرد.

رایج‌ترین خطا در حلقه‌ی ()for خطای (off-by-one) می‌باشد؛ مثلاً قصد دارید کدی ۱۰ مرتبه اجرا شود، اما این کد ۹ یا ۱۱ بار اجرا می‌شود. این خطا معمولاً در اثر استفاده از «<=» به جای «<» یا برعکس رخ می‌دهد.

source::https://learn.sparkfun.com/tutorials/digital-logic

یادگیری منطق دیجیتال و مدار منطقی در الکترونیک دیجیتال مهارت بسیار مهمی است. و برای اینکه بتوانید با میکروکنترلرها و تراشه های FPGA مدار طراحی کنید باید با مدار منطقی کاملا آشنا باشید.

اگر این نوشته‌ برایتان مفید بود لطفا کامنت بنویسید.

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

یک دیدگاه

  1. سلام مطلب کامل و خوب بود ولی ای کاش فایلpdfبود