مقدمه
در فصل های گذشته به این نکته اشاره کردیم که قدرتمندترین زبان برنامه نویسی میکروکنترلرها زبان c و c++ می باشد . همچنین اشاره کردیم که برنامه نویسی برای یک ماشین بر مبنای پردازنده های RISC با برنامه نویسی برای یک ماشین بر مبنای پردازنده های CISC تفاوت اساسی دارد و آن هم حساسیت بیشتر RISC نسبت به CISC می باشد که برنامه نویس را مجبور می کند تا با دقت بیشتر و درک بیشتر سخت افزار برنامه نویسی کند . با این دید از میکروکنترلر ها در این فصل گذری بر برنامه نویسی به زبان C ویژه میکروکنترلر ها خواهیم داشت و نکات و مفاهیمی که یک برنامه نویس میکرو باید آنها را رعایت کند به همراه انجام مثال ها و پروژه های عملی مربوطه شبیه سازی خواهیم کرد .
معرفی کوتاه زبان C
زبان برنامهنویسی سی ، زبانی همه منظوره، ساخت یافته و روندگرا میباشد که در سال ۱۹۷۲ توسط دنیس ریچی در آزمایشگاههای بل ساخته شد . به طور کلی زبان های برنامه نویسی را می توان در سه سطح دسته بندی کرد : ٫زبان های سطح بالا ٫زبان های سطح میانی ٫زبان های سطح پایین . زبان سی یک زبان سطح میانی است که در آن هم می توان به سطح بیت و بایت و آدرس دسترسی داشت و هم از مفاهیم سطح بالا که به زبان محاوره ای انسان نزدیکتر است ( مانند حلقه های شرطی if … else و حلقه های تکرار for و while و … ) ، بهره گرفت . در زبان سی هیچ محدودیتی برای برنامه نویس وجود ندارد و هر آنچه را که فکر می کنید ، می توانید پیاده سازی کنید . ارتباط تنگاتنگی بین زبان c و اسمبلی وجود دارد ، به این صورت که می توان از برنامه نویسی اسمبلی در هر کجای برنامه زبان سی استفاده کرد .
کلمات کلیدی در زبان C
زبان سی زبان کوچکی است چرا که در آن تعداد کلمات کلیدی ۳۲ کلمه است . کم بودن کلمات کلیدی در یک زبان مبنی بر ضعف آن نیست . زبان بیسیک حاوی ۱۵۰ کلمه کلیدی است اما قدرت زبان سی به مراتب بالاتر است . زبان سی به حروف کوچک و بزرگ حساس است ( Case Sensitive ) . در این زبان بین حروف کوچک و بزرگ تفاوت است و تمام کلمات کلیدی باید با حروف کوچک نوشته شوند . در شکل زیر تمام کلمات کلیدی زبان سی را مشاهده می کنید .
نکته ۱ : کلمه کلیدی auto از کامپایلر Codevision حذف شده است.
نکته ۲ : کلمات کلیدی زیر به کامپایلر کدویژن اضافه شده است :
interrupt | defined | bit |
inline | eeprom | flash |
sfrw | sfrb |
ویژگی های یک برنامه به زبان C
– هر دستور در زبان سی با ; به پایان می رسد .
– حداکثر طول هر دستور ۲۵۵ کاراکتر است .
– هر دستور می تواند در یک یا چند سطر نوشته شود .
– برای توضیحات تک خطی از // در ابتدای خط استفاده می شود و یا توضیحات چند خطی در بین */ و /* قرار می گیرد .
ساختار یک برنامه به زبان C در کامپیوتر
هر زبان برنامه نویسی دارای یک ساختار کلی است . این ساختار یک قالب را برای برنامه نویس فراهم می کند . ساختار کلی یک برنامه به زبان C را در زیر مشاهده می کنید .
1 2 3 4 5 6 7 8 |
#include < HeaderFiles.h > محل معرفی متغیرهای عمومی ، ثوابت و توابع void main (void) { محل مربوط به کدهای برنامه } |
بنابراین همانطور که مشاهده می شود :
- خطوط ابتدایی برنامه ، دستور فراخوانی فایل های سرآمد ( Header Files ) می باشد . فایل های سرآمد فایل هایی با پسوند h هستند که حاوی پیش تعریف ها و الگو های توابع می باشند .
- قالب اصلی برنامه ب مبنای تابعی به نام main بنا شده است . تابعی که اصولا ورودی و خروجی ندارد و کدهای اصلی برنامه را در خود دارد .
تفاوت برنامه نویسی برای کامپیوتر و میکروکنترلر
ساختار فوق یک قالب کلی در برنامه نویسی زبان C در کامپیوتر ( ماشین CISC ) با هدف اجرا در کامپیوتر را نشان می دهد . برنامه نویسی میکروکنترلر ها ( ماشین RISC ) با هدف اجرا در میکروکنترلر ، با برنامه نویسی در کامپیوتر کمی متفاوت است . تفاوت اصلی در کامپیوتر این است که عامل اجرای برنامه ، در زمان نیاز به عملکرد آن ، یک کاربر است . هر زمان که کاربر نیاز به عملکرد برنامه داشته باشد آن را اجرا می کند و نتیجه را بررسی می کند . اما در میکروکنترلر رفتار یک آی سی است که عامل اجرای برنامه منبع تغذیه است . با وصل منبع تغذیه برنامه شروع به کار می کند و تا زمانی که منبع تغذیه وصل است باید کارهای مورد نیاز را دائما اجرا نماید .
ساختار برنامه میکروکنترلر به زبان C
برنامه ای که کاربر می نویسد باید طوری نوشته شود که وقتی روی آی سی پروگرام شد دائما اجرا شود. راه حل این مسئله قرار دادن کدهای برنامه درون یک حلقه نامتناهی است . این عمل باعث می شود تا میکروکنترلر هیچگاه متوقف نشود و بطور مداوم عملکرد طراحی شده توسط کاربر را اجرا کند. بنابراین ساختار یک برنامه به صورت زیر در می آید.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include < HeaderFiles.h > محل معرفی متغیرهای عمومی ، ثوابت و توابع void main (void) { کدهایی که در این محل قرار میگیرند فقط یکبار اجرا می شوند معمولا مقدار دهی اولیه به رجیستر ها در این ناحیه انجام می شود while(1) { کدهایی که باید مدام در میکروکنترلر اجرا شوند } } |
متغیرها در زبان C
یک متغیر محدوده ای از فضای حافظه است که با یک نام مشخص می شود. یک متغیر بسته به نوع آن می تواند حامل یک مقدار عددی باشد . یک متغیر می تواند در محاسبات شرکت کند و یا نتیجه محاسبات را در خود حفظ کند. در کل میتوان گفت که نتایج بخش های مختلف یک برنامه ، در متغیر ها ذخیره می شود. در جدول زیر انواع متغیرها ، فضایی که در حافظه اشغال می کنند و بازه مقدار پذیری آنها را در کامپایلر کد ویژن مشاهده می کنید .
نحوه تعریف متغیرها
متغیر ها به صورت زیر تعریف می شوند :
1 |
;مقدار اولیه = نام متغیر نوع متغیر |
مثال :
1 2 |
Unsigned char A=12; int a,X,j; |
توضیح : در خط اول یک متغیر ۸ بیتی بدون علامت با نام A که تنها میتواند مقادیر ۰ تا ۲۵۵ بگیرد ، با مقدار اولیه ۱۲ تعریف شده است . در خط دوم نیز ۳ متغیر علامت دار با نام های a و X و j که هر سه مقدار اولیه ۰ دارند تعریف شده است .
نکته : در صورت عدم تعریف مقدار اولیه در هنگام تعریف یک متغیر ، مقدار اولیه در حالت default برابر ۰ تعریف می شود .
ویژگی های نام متغیر
– اولین کاراکتر نام متغیر عدد نمیتواند باشد .
– نام متغیر بیشتر از ۳۱ کاراکتر مورد استفاده نیست .
– نام متغیر تنها ترکیبی از حروف a تا z و A تا Z و اعداد و کاراکتر _ می تواند باشد .
انواع متغیر ها از نظر محل تعریف در برنامه
متغیرها از نظر مکانی که در برنامه تعریف می شوند ، به دو دسته کلی تقسیم می شوند :
- متغیرهای عمومی ( Global )
- متغیرهای محلی ( Local )
متغیر هایی که قبل از تابع main تعریف می شوند را متغیرهای عمومی گویند و در همه جای برنامه می توان به آن دسترسی داشت . اما متغیرهای محلی در بدنه توابع تعریف می شوند و در بیرون از آن تابع ، دسترسی به آن ممکن نیست. در واقع با تعریف یک متغیر عمومی در ابتدای برنامه ، مقدار مشخصی از حافظه برای همیشه به آن متغیر تخصیص می یابد اما متغیرهای محلی تنها در زمان احتیاج تعریف شده و در حافظه می نشینند و بعد از مدتی از حافظه پاک می شوند .
محل تعریف متغیرها در حافظه میکروکنترلر
زمانی که یک متغیر به صورتی که در بالا گفته شد ، تعریف می شود آن متغیر در اولین مکان خالی در حافظه SRAM ذخیره می شود . برای تعریف متغیر در حافظه EEPROM از کلمه کلیدی eeprom و برای تعریف متغیر در حافظه FLASH از کلمه کلیدی flash قبل از تعریف متغیر استفاده می شود . بنابراین باید توجه داشت که با قطع منبع تغذیه کلیه حافظه SRAM پاک خواهد شد و متغیرهایی که باید ذخیره دائمی شوند می بایست در حافظه EEPROM یا FLASH تعریف و ذخیره شوند . مثال :
1 2 3 |
int a; eeprom char b; flash float c; |
نکته : همانطور که قبلا گفتیم ، حافظه Flash ، حافظه برنامه کاربر است یعنی برنامه به زبان C بعد از کامپایل و ساخته شدن توسط کدویژن و پروگرام شدن روی میکروکنترلر در حافظه Flash ذخیره می شود . بنابراین برای تعریف متغیر در حافظه Flash تنها در صورت خالی بودن قسمتی از آن امکان پذیر است .
نکته : حافظه SRAM تنها با قطع تغذیه پاک می شود و میتوان کاری کرد که با ریست شدن میکرو متغیرهای عمومی موجود در SRAM ریست نشده و مقدار قبلی خود را حفظ نمایند .
توابع در زبان c
تابع یکی از مهمترین بخش های زبان سی می باشد . یک تابع همانند دستگاهی است که مواد اولیه را دریافت می کند و بعد از انجام عملیات مورد نظر روی آنها خروجی مطلوب را تحویل می دهد . توابع در زبان C یا توابع کتابخانه ای هستند یا توابعی هستند که کاربر بر حسب نیاز برنامه خود اضافه می کند .
زبان سی دارای توابعی است که از قبل نوشته شدهاند، و توابع کتابخانهای نامیده میشوند. در واقع فرایندهایی که پر کاربرد هستند و در اغلب برنامهها مورد استفاده قرار میگیرند به صورت توابع مستقل قبلاً نوشته شدهاند و درون فایلهایی قرار داده شده اند . با اضافه کردن فایل های سرآمد که تعریف آن توابع در آنها قرار دارد می توان از آن توابع استفاده کرد .
تابع اصلی برنامه نویسی به زبان C تابع main نام دارد که در تمامی برنامه ها وجود داشته و بدون ورودی و خروجی است . توابع دیگر را می توان در بالای تابع main ، در پایین تابع main و یا در فایل های کتابخانه ای تعریف کرد.
انواع توابع در زبان c
هر تابع مجموعه ای از دستورات است که بر روی داده ها پردازش انجام می دهد. ورودی و خروجی یک تابع مقادیری هستند که تابع در برنامه دریافت می کند و به برنامه باز می گرداند. توابع بر اساس ورودی و خروجی به ۴ دسته زیر تقسیم می شود :
-
تابع با ورودی ، با خروجی
مثال : تابع با دو ورودی از جنس کاراکتر و یک خروجی از جنس کاراکتر
1 |
Char F1( char x , char y ); |
-
تابع با ورودی ، بدون خروجی
مثال : تابع دارای یک ورودی int و بدون خروجی
1 |
void F2( int x ); |
-
تابع بدون ورودی ، با خروجی
مثال : تابع بدون ورودی اما دارای خروجی int
1 |
int F3(void); |
-
تابع بدون ورودی ، بدون خروجی
مثال : تابع بدون ورودی و خروجی
1 |
void F4(void); |
بنابراین در اولین قدم باید مشخص کنیم که این تابع چه خروجی را به ما می دهد ( در اصطلاح برنامه نویسی بر می گرداند ) و فقط به ذکر نوع خروجی بسنده می کنیم ، یعنی مثلا اگر عدد صحیح برگرداند از int ، اگر کاراکتر برگرداند از char و به همین ترتیب برای انواع دیگر و اگر هیچ مقداری را برنگرداند از void استفاده می کنیم .
در قدم بعدی نام تابع را مشخص می کنیم . یک تابع باید دارای یک نام باشد تا در طول برنامه مورد استفاده قرار گیرد. هر نامی را می توان برای توابع انتخاب نمود که از قانون نامگذاری متغیرها تبعیت می کند، اما سعی کنید که از نامهایی مرتبط با عمل تابع استفاده نمایید .
همینطور باید ورودیهای تابع را نیز مشخص کنیم که در اصطلاح برنامه نویسی به این ورودیها، پارامترها یا آرگومان تابع نیز گفته می شود. اگر تابع بیش از یک پارامتر داشته باشد باید آنها را با استفاده از کاما از یکدیگر جدا نماییم و اگر تابع پارامتری نداشت از کلمه void استفاده می کنیم. نام پارامتر نیز میتواند هر نام دلخواهی باشد . بخاطر داشته باشید که قبل از نام هر پارامتر باید نوع آنرا مشخص نماییم و بدانیم که کامپایلر هیچ متغیری بدون نوع را قبول نکرده و در صورت برخورد با این مورد از برنامه خطا می گیرد و در نتیجه برنامه را اجرا نخواهد کرد .
بنابراین در زبان c توابع حداکثر دارای یک خروجی می باشند و اگر تابعی خروجی داشته باشد آن را باید با دستور return به خروجی تابع و برنامه اصلی بر گردانیم .
تعریف توابع در زبان c
برای نوشتن و اضافه کردن یک تابع باید دقت کرد که هر تابع سه بخش دارد : اعلان تابع ، بدنه تابع و فراخوانی تابع
نحوه تعریف تابع به یکی از سه صورت زیر است :
1- اعلان تابع قبل از تابع main باشد و بدنه تابع بعد از تابع main باشد .
2- اعلان تابع و بدنه تابع هر دو قبل از تابع main باشد .
3- اعلان تابع و بدنه تابع درون فایل کتابخانه ای باشد .
فراخوانی تابع نیز در درون تابع main صورت می گیرد . در صورتی که اعلان و فراخوانی تابع درون فایل کتابخانه ای باشد فقط نیاز به فراخوانی آن در main است .
نکته : هیچ تابعی را نمیتوان درون تابعی دیگر تعریف نمود و فقط به صورت های گفته شده صحیح است اما میتوان از فراخوانی توابع در داخل یکدیگر استفاده کرد بدین صورت که تابعی که داخل تابع دیگر فراخوانی می شود باید در برنامه زودتر تعریف شود .
اعلان و بدنه تابع :
;( … , نام ورودی دوم نوع ورودی دوم , نام ورودی اول نوع ورودی اول ) نام تابع نوع داده خروجی تابع
مثال :
1 |
void sample ( int x, int y ); |
در صورتی که بخواهیم اعلان تابع قبل از main باشد آن را به صورت فوق اعلان می کنیم و بعد از تابع main به صورت زیر دوباره اعلان را به همراه بدنه تابع می نویسیم .
} ( … , نام ورودی دوم نوع ورودی دوم , نام ورودی اول نوع ورودی اول ) نام تابع نوع داده خروجی تابع
{ بدنه تابع : دستوراتی که تابع انجام می دهد
مثال :
1 2 3 4 5 6 |
void sample ( int x, int y ) { . . . } |
در صورتی که بخواهیم قبل از تابع main اعلان و بدنه تابع باشد ، به همان صورت فوق این کار را انجام می دهیم با این تفاوت که یکجا هم اعلان و هم بدنه قبل از main تعریف می شود .
فراخوانی تابع :
صدا زدن تابع درون برنامه را فراخوانی گویند . در فراخوانی توابع باید نام تابع و مقدار ورودی ها را بیان کنیم که به آن آرگومانهای تابع نیز گفته می شود . در مقدار دهی آرگومان تابع در هنگام فراخوانی دیگر نباید نوع آرگومانها را ذکر کنیم. مثال :
1 2 3 4 5 6 |
void main(void) { ... sample (a, b); ... } |
در مورد نوع خروجی تابع دو حالت وجود دارد. اول اینکه اگر تابع بدون خروجی باشد لازم نیست از void استفاده کنیم و دوم اینکه اگر تابع دارای خروجی باشد باید آنرا برابر با مقدار متغیر از همان نوع قرار دهیم تا مقدار برگشتی را در متغیر مذکور ریخته و در جای مناسب از آن استفاده نماییم . مثال :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <mega32.h> #include <delay.h> unsigned char i=0; unsigned char count (void) { i++; delay_ms(300); return i; } void main (void) { DDRA=0xff; PORTA=0x00; while(1){ PORTA= count(); } } |
توضیح : در برنامه فوق ابتدا هدرفایل مربوط به میکروکنترلر Atmega32 به برنامه اضافه می شود . با اضافه شدن این فایل برنامه تمام رجیسترهای میکروکنترلر را می شناسد . سپس هدر فایل delay برای اینکه بتوان از تابع delay_ms استفاده کرد به برنامه اضافه شده است . سپس یک متغیر ۸ بیتی از نوع عدد بدون علامت ( unsigned char ) با مقدار اولیه صفر تعریف می شود . یک تابع دارای خروجی و بدون ورودی اعلان و بدنه آن تعریف شده است . در تابع main ابتدا رجیستر DDRA به منظور اینکه تمام ۸ بیت موجود در پورت A خروجی شود ، به صورت ۰xff مقدار دهی شده است . مقدار اولیه منطق پایه های خروجی پورت A در خط بعدی همگی برابر ۰ شده است . در نهایت در حلقه بی نهایت while ، تابع فراخوانی شده است و مقدار خروجی که تابع برمیگرداند در درون PORTA ریخته می شود . در صورتی که روی هر پایه از پورت LED قرار دهیم ، برنامه به صورت شمارنده عمل خواهد کرد و در هر مرحله تعدادی LED روشن می گردد .
نکته : نوع متغیر رجیسترها در هدرفایل mega32.h همگی به علت ۸ بیتی بودن رجیسترها در میکروکنترلرهای AVR ، از نوع unsigned char می باشد .
ثابت ها در زبان C
ثابت یک مقدار مشخص است که در ابتدای برنامه قبل از main تعریف می شود و یک نام به آن تعلق می گیرد. این مقدار هیچگاه قابل تغییر توسط کدهای برنامه نمی باشد. معمولا مقادیر ثابت عددی که در طول برنامه زیاد تعریف می شود را یک ثابت با نامی مشخص تبدیل می کنند. برای تعریف ثابت می توان به دو روش زیر عمل کرد.
-
استفاده از دستور Const
مثال :
1 |
const float pi=3.14; |
-
استفاده از دستور #define
مثال :
1 |
#define pi 3.14 |
تعریف ثابت ها معمولا در ابتدای برنامه با استفاده از دستور #define صورت می گیرد زیرا این دستور پیش پردازنده بوده و به بهبود برنامه کمک می کند . به طور کلی در زبان سی دستوراتی که با # آغاز می شوند پیش پردازنده هستند یعنی کامپایلر ابتدا آنها را پردازش و سپس بقیه برنامه را کامپایل می کند .
دستورات شرطی
اگر بخواهیم تحت شرایطی تعدادی از دستور ها اجرا شوند و یا تعدادی دیگر اجرا نشوند، باید از ساختار های شرطی استفاده کنیم. دستورات شرطی در تمام زبان های برنامه نویسی از اجزای اصلی هستند. یک دستور شرطی، شرطی که کاربر مشخص می کند را بررسی کرده و در صورت صحیح بودن یک دسته از کدهای کاربر و در صورت غلط بودن دستهی دیگری از کدهای کاربر را اجرا میکند.
دستور شرطی if
در حلقه شرطی if ، اگر شرط موردنظر برقرار باشد، کدهای درون { } در زیر if اجرا خواهند شد. اگر شرط موردنظر برقرار نباشد، کدهای درون { } در زیر else اجرا خواهند شد به مثال زیر توجه کنید:
مثال :
1 2 3 4 |
if(a==7) PORTD=0b11110000; else PORTD=0b00001111; |
توضیح : در مثال بالا اگر متغیر a برابر با عدد ۷ باشد، مقدار باینری ۱۱۱۱۰۰۰۰ به رجیستر PORTDنسبت داده میشود و اگر برابر با ۷ نباشد، مقدار باینری ۰۰۰۰۱۱۱۱ به رجیستر PORTD نسبت داده میشود. دقت شود، در سطرهای بالا دو علامت { } وجود ندارد و این بهاینعلت است در زیر دستور if و else تنها یک دستور وجود دارد، اگر تعداد دستورات بیشتر از یک دستور باشد حتماً باید از { } استفاده شود. وجود دستور else در if ضروری نیست و بسته به نیاز کاربر قرار داده میشود.
دستور شرطی switch
ساختار switch یکی از ساختار های مهم و جالب در زبان C است. از این ساختار برای تصمیم گیری های چندگانه بر اساس مقادیر مختلف یک عبارت، استفاده می شود. به طور کلی، در تمام تصمیم گیری هایی که بیش از سه انتخاب وجود داشته باشد بهتر است از ساختار switch استفاده شود. دستور شرطی switch نسبت به دستور شرطی if از توانمندی کمتری برخوردار است . این دستور فقط می تواند برابری را مورد شرط قرار دهد . قالب دستور شرطی switch را در زیر مشاهده می کنید :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Switch( عامل مورد شرط ) { Case مقدار اول : کدهایی که در صورت برابر بودن عامل شرط با مقدار اول باید اجرا شود Break; Case مقدار دوم : کدهایی که در صورت برابر بودن عامل شرط با مقدار دوم باید اجرا شود Break; . . . Default : کدهایی که در صورت برابر نبودن عامل شرط با هیچ یک از مقادیر باید اجرا شود } |
گزینه Default در ساختار دستور switch ضروری نیست و بسته به نیاز کاربر قرار داده می شود. در دستور switch مقادیر صحیح مورد بررسی قرار می گیرند . هر دستور case یک شرط برابری را مورد بررسی قرار می دهد و در صورت برابر بودن ، کد های قرار گرفته در زیر آن اجرا شده و توسط دستور Break از ساختار switch خارج می شود. اگر در زیر هر case دستور Break قرار داده نشود، هر case با case بعدی orمنطقی (یا) می شود. در بعضی شرایط به این صورت می توان ۲ مقدار را بررسی کرد.
حلقه های تکرار
یکی دیگر از از اجزا اصلی زبان های برنامه نویسی حلقه ها هستند. حلقه های تکرار تحت شرایط خاصی، یک یا چند دستور را چندین بار اجرا می کنند. به عنوان مثال، اگر بخواهیم تعداد ۱۰۰ عدد را از ورودی بخوانیم و آن ها را با هم جمع کنیم. باید عمل خواندن عدد را ۱۰۰ بار تکرار کنیم. عملکرد یک حلقه به این صورت است که کد مربوط به حلقه تا زمانی که شرط حلقه برقرار باشد، تکرار می شود و تا زمانی که حلقه در حال اجرا است، برنامه در محل حلقه می ماند.
حلقه while
در ابتدا شرط حلقه مورد بررسی قرار می گیرد اگر شرط برقرار باشد یکبار کدهای درون حلقه اجرا می شود و دوباره شرط حلقه چک می شود و این روند تا زمانی که شرط برقرار است ادامه می یابد.
1 2 |
while ( شرط حلقه ) { کدهایی که تا زمان برقراری شرط حلقه تکرار می شود ; } |
مثال :
1 2 3 4 5 6 |
char a=0; while( a<16 ) { PORTD = a; a++; } |
حلقه do…while
این حلقه عملکردی بسیار شبیه به حلقه while دارد. در این حلقه یکبار کدهای درون حلقه اجرا می شود و سپس شرط حلقه بررسی می گردد. بنابراین برای اینکه شرط در آخر بررسی شود از حلقه do…while استفاده می شود.
1 2 3 4 5 |
do { //کد هایی که در زمان برقراری شرط حلقه تکرار می شوند } While( شرط حلقه ); |
حلقه for
از این حلقه در حالتی که تعداد دفعات تکرار حلقه از قبل مشخص باشد، به کار می رود. در این حلقه، متغیری وجود دارد که تعداد دفعات تکرار حلقه را کنترل می کند. این متغیر را اندیس حلقه تکرار یا شمارنده می گویند. اندیس حلقه دارای یک مقدار اولیه است که طی هر بار اجرای دستورات حلقه، مقداری به آن اضافه می شود. به این مقدار اضافه شده در هر بار اجرای حلقه، گام حرکت گویند. گام حرکت می تواند عددی صحیح یا اعشاری، مثبت یا منفی ویا کاراکتری باشد. یکی دیگر از اجزای حلقه for شرط حلقه است. شرط حلقه مشخص می کند که دستورات داخل حلقه for تا کی باید اجرا شوند. اگر این شرط دارای ارزش درستی باشد، دستورات داخل حلقه اجرا می شوند وگرنه کنترل برنامه از حلقه خارج می شود. حلقه for دارای یک متغیر شمارشی است که نسبت به تعداد تکرار حلقه، طول آن مشخص شده و در ابتدای برنامه باید معرفی شود. به طور مثال اگر تعداد تکرار حلقه ایی ۲۰۰ مرتبه باشد، می توان متغیر شمارشی از نوع unsigned char معرفی کرد، اما اگر تعداد تکرار حلقه ۱۰۰۰ مرتبه باشد، باید متغیری با طول بیشتر مثل نوع int را برای متغیر شمارشی انتخاب کرد. این متغیر شمارشی در بدنه حلقه کاربرد فراوانی دارد. دستور for را به شکل زیر می توان به کار برد:
1 2 |
For( مقدار اولیه شمارنده حلقه ; شرط حلقه ; گام حرکت حلقه ) { کدهایی که تا زمان برقراری شرط حلقه تکرار می شود ; } |
مثال :
1 2 3 4 |
int i; … for (i=0 ; i<8 ; i++) a[i]=i; |
توضیح : در ابتدای حلقه for مقدار متغیر i برابر با مقدار صفر قرار داده شده است و شرط حلقه تا وقتی برقرار است که مقدار متغیر i کوچکتر از عدد ۸ باشد. گام حلقه به شکلی تنظیم شده که هر بار یک واحد به متغیر شمارشی اضافه می کند. در ابتدا مقدار متغیر i برابر با صفر است و حلقه شروع می شود، کد های درون حلقه اجرا شده سپس یک واحد به متغیر i اضافه می شود. تکرار کد های درون این حلقه، طبق شرط حلقه ۸ مرتبه می باشد و به متغیر i مقادیر ۰ تا ۷ تعلق می گیرد. از تغییرات متوالی مقدار متغیر i می توان در حلقه استفاده کرد. همان طور که در مثال بالا مشاهده می شود، یک آرایه با نام a قبلا معرفی شده است، به جای اندیس آرایه a، متغیر i قرار گرفته است؛ بنابراین با هر بار تکرار حلقه و تغییر مقدار متغیر i، اندیس آرایه نیز تغییر می کند و هر بار مقدار i به عضو i ام آرایه a انتساب داده می شود. به طور مثال اگر حلقه در حرکت سوم قرار گیرد، یعنی مقدار i برابر با عدد ۲ است. در این حالت کد داخل حلقه به صورت a[2]=2 می شود.
دستور break و continue در حلقه ها
برنامه با دیدن دستور break در هر نقطه از حلقه ، در همان نقطه از حلقه خارج شده و کدهای زیر حلقه را اجرا می کند . دستور break در حلقه خروج بدون شرط از حلقه است. برنامه با دیدن دستور continue در هر نقطه از حلقه ، کدهای زیر دستور continue را رها کرده و به ابتدای حلقه باز می گردد.
اتصال کلید به میکرو
یکی از ساده ترین و پرکاربرد ترین دستگاههای ورودی کلید است . توسط کلید میتوان منطق ۰ یا ۱ را به میکرو وارد کرد و بر اساس آن پردازش را انجام داد. برای خواندن منطق کلید از رجیستر PIN استفاده می شود . نحوه استفاده از رجیستر PIN به این صورت است که : ” اگر کلید زده شد آنگاه کار مورد نظر انجام شود ” . عبارت اخیر معادل استفاده از دستور شرطی if به صورتی است که اگر کلید زده شده بود یک منطق و در غیر این صورت منطق دیگری وارد پایه میکرو شود . بنابراین اتصال کلید را به دو صورت PullUp و PullDown انجام می شود که در شکل زیر مشاهده می کنید . در کلید pull up در حالتی که کلید زده نشده منطق ۱ و در حالت فشردن کلید منطق ۰ وارد میکرو می شود . معمولا از مقاومت ۱۰k برای این کار استفاده می شود .
بنابراین برای فهماندن کلید به میکرو به صورتی که ” اگر کلید زده شد آنگاه کار مورد نظر انجام شود ” برای کلیدهای فوق به صورت زیر می شود :
1 2 |
if(PINB.0==0) {...} //PullUp if(PINB.1==1) {...} //PulDown |
اما مشکلی که در کلید وجود دارد ، بوجود آمدن لرزش یا bounce در هنگام قطع و وصل کلید است . در زمانی که کاربر کلید را فشار می دهد تقریبا ۲۰ میلی ثانیه طول می کشد تا منطق کلید از ۰ به ۱ ( یا بالعکس ) ثابت شود تا قبل این بعلت وجود جرقه کلید منطق ثابتی ندارد . در زمانی که کاربر دست خود را از روی کلید بر می دارد نیز تقریبا ۲۰ میلی ثانیه طول می کشد تا ۰ و ۱ شدن های کلید تمام شده و کلید به منطق اولیه خود برگردد . در واقع این مشکل زمانی برای ما مسئله ساز می شود که زمانی که کاربر کلید را یکبار فشار می دهد و انتظار دارد تا میکرو متوجه یکبار فشار دادن آن شود اما به علت قرار گرفتن if در درون حلقه نامتناهی while چندین بار شرط PINB.0==0 برقرار شده و کار مورد نظر چندین بار انجام می شود . راه حل این مشکل ساده است و آن هم قرار دادن مقداری delay در حلقه if است . بنابراین برای حل این مشکل بسته به نوع برنامه یکی از سه روش زیر قابل استفاده است :
کلید نوع ۱ :
1 2 3 4 |
if(PINB.0==0) { دستورات مربوط به بعد از زدن کلید delay_ms(200); } |
توضیح : به محض فشار دادن کلید توسط کاربر شرط if برقرار شده و دستورات مورد نظر اجرا می شود سپس به علت ایجاد تاخیر زیاد توسط تابع delay_ms ( در اینجا ۲۰۰ میلی ثانیه ) با این کار احتمال اینکه زمانی که برنامه در حلقه while به if می رسد و شرط برقرار باشد ، کاهش می یابد . مزیت این کلید این است که در صورتی که کاربر کلید را فشار داده و نگه دارد تقریبا در هر ۲۰۰ میلی ثانیه یکبار کار مورد نظر صورت می گیرد . عیب این روش نیز این است که هنوز احتمال دارد که زمانی که یکبار کلید زده شود دوبار کار مورد نظر انجام شود .
کلید نوع ۲ :
1 2 3 4 5 |
if(PINB.0==0) { delay_ms(20); while(PINB.0==0); دستورات مربوط به بعد از زدن کلید } |
توضیح : به محض فشار دادن کلید توسط کاربر شرط if برقرار شده و برنامه به مدت ۲۰ میلی ثانیه صبر می کند تا منطق کلید ثابت شود و از منطقه bounce عبور کند سپس توسط حلقه while با همان شرط برقراری کلید در این مرحله برنامه تا زمانی که کلید توسط کاربر فشرده شده است در حلقه گیر می کند و هیچ کاری انجام نمی دهد . به محض اینکه کاربر دست خود را بر می دارد ، شرط برقرار نبوده و خط بعدی یعنی دستورات مربوطه اجرا می شود . مزیت این روش این است که در هر بار فشردن کلید برنامه تنها یکبار اجرا می شود . معایب این روش این است که تا زمانی که کاربر کلید را نگه داشته اتفاقی نمی افتد و به محض رها کردن کلید کار مورد نظر انجام می شود .
کلید نوع ۳ :
1 2 3 4 5 6 |
if((PINB.0==0 ) && (flag==0)) { flag=1; start=!start; } else if ( PINB.0 == 1) flag=0; if(start){ دستورات مربوط به بعد از زدن کلید } |
توضیح : این کلید به صورت start/stop عمل می کند یعنی بار اولی که کاربر کلید را فشار می دهد دستورات مربوط به بعد از زدن کلید دائما اجرا می شود تا زمانی که کاربر دست خود را از روی کلید رها کرده و دوباره کلید را فشار دهد ، در این صورت دستورات دیگر اجرا نمی شود . دو متغیر از نوع bit با نام های flag و start با مقدار اولیه ۰ برای این کلید باید تعریف شود . زمانی که کاربر برای اولین بار کلید را فشار می دهد شرط if برقرار شده و flag=1 و start=1 می شود . در این صورت شرط if دوم برقرار بوده و دستورات مربوطه با هر بار چرخش برنامه درون حلقه نامتناهی while یکبار اجرا می شود . زمانی که کاربر دست خود را از روی کلید بر می دارد و منطق ۱ وارد میکرو می شود flag=0 شده و برنامه دوباره آماده این می شود که کاربر برای بار دوم کلید را فشار دهد . زمانی که کاربر بار دوم کلید را می فشارد start=0 شده و دستوراط مربوطه اجرا نخواهد شد سپس با برداشته شدن دست کاربر از روی کلید ، همه چیز به حالت اول بر میگردد . این کلید طوری نوشته شده است که bounce در آن کمترین تاثیر مخرب ممکن را دارد .
مثال عملی شماره ۲
برنامه ای بنویسید که یک کلید روی پورت PA0 و ۸ عدد LED روی پورت B وجود داشته باشد و با هر بار زدن کلید ، led های موجود به صورت ۱٫ شمارنده بالا شمار ۲٫ شمارنده پایین شمار ۳٫شمارنده جانسون ۴٫ شمارنده حلقوی عمل نماید . سعی کنید برنامه کمترین عیب ممکن را داشته باشد و کلید به بهترین نحو ممکن کار کند .
حل :
مرحله اول : طراحی سخت افزار
مرحله دوم : طراحی نرم افزار
سپس به سراغ نرم افزار CodeVision رفته و طبق مراحل گفته شده در فصل قبل پروژه جدید را می سازیم و فرکانس میکرو را روی ۱Mhz داخلی قرار می دهیم . برنامه خواسته شده به صورت زیر است .
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 |
#include <mega32.h> #include <delay.h> unsigned char i,j=0; bit flag=1; void main (void) { DDRB=0xff; PORTB=0x00; while(1){ if(PINA.0==0){ delay_ms(25); i++; if(i==5) i=0; j=0; PORTB=0x00; flag=1; while(PINA.0==0); } if(i==1){ PORTB++; delay_ms(200); } if(i==2){ PORTB--; delay_ms(200); } if(i==3){ PORTB=0x01; PORTB=PORTB<<j; delay_ms(200); if(flag) j++; else j--; if(j==7) flag=0; if(j==0) flag=1; } if(i==4){ if(flag) PORTB=PORTB+(0x01<<j); else PORTB=PORTB-(0x01<<j); delay_ms(200); if(flag) j++; else j--; if(j==8) flag=0; if(j==255) flag=1; } } } |
توضیح : متغیر i حالت کار میکرو را نشان می دهد که با هر باز زدن کلید یک واحد به متغیر i اضافه می شود . در حالت پیش فرض مقدار این متغیر ۰ است و برنامه هیچ کاری انجام نمی دهد تا زمانی که کاربر کلید را فشار دهد . با اولین باری که کلید زده می شود i=1 شده و برنامه مربوط به شمارنده بالا شمار اجرا می شود . با دومین باری که کلید زده می شود i=2 شده و برنامه مربوط به شمارنده پایین شمار اجرا می شود . دفعه سوم i=3 شده و برنامه به صورت شمارنده حلقوی و در نهایت زمانی که برای بار چهارم کلید زده شود i=4 شده و برنامه شمارنده جانسون می شود . متغیر flag از آن جهت ایجاد شده است که زمانی که شمارنده جانسون و حلقوی به آخر رسیدند ، برنامه از آخر به اول شروع به کار کند و زیباتر می شود .
نکته : همانطور که مشاهده کردید در برنامه از حلقه for استفاده نکردیم تا برنامه روان تر و حرفه ای تر باشد .
آرایه ها در C
آرایه اسمی برای چند متغیر هم نوع می باشد یا به عبارت دیگر آرایه از چندین کمیت درست شده است که همگی دارای یک نام می باشد و در خانه های متوالی حافظه ذخیره می گردند. هر یک از این کمیت ها را یک عنصر می گویند، برای دسترسی به عناصر آرایه باید اسم آرایه و شماره ی اندیس آرایه را ذکر کنیم. آرایه ها در زبان سی از جایگاه ویژه ای برخوردارند، به طوری که در پروژه های خود به طور مکرر به آن برخورد خواهید کرد زیرا ارسال و دریافت داده به صورت رشته (آرایه ای از کاراکترها) و سریال انجام می شود .
تعریف آرایه یک بعدی :
با تعریف آرایه به همان مقدار خانه های حافظه بسته به نوع متغیر و تعداد خانه های آرایه تخصیص می یابد که در شکل زیر مثالی از آن را مشاهده می کنید
میزان حافظه ای که به آرایه اختصاص داده می شود،به این شکل استفاده می شود:
( طول آرایه ) ضرب در (طول نوع آرایه) = میزان حافظه آرایه (برحسب بایت)
برای دسترسی به هر یک از خانه ها آدرس آن را لازم داریم. آدرس هر خانه از ۰ تا n-1 است که در آن n تعداد خانه های تعریف شده است . هر عضو آرایه به تنهایی می تواند در محاسبات شرکت کند . مثال :
1 2 |
unsigned char a[5]={7,12,0,99,1}; a[0]=a[4]*a[2]; |
نکته ۱ : اندیس آرایه خود می تواند متغیر باشد و این قابلیت می تواند در برنامه ها کاربرد زیادی داشته باشد .
نکته ۲ : در صورتی که می خواهید آرایه به صورت دائمی ذخیره شود ( مثلا وقتی که میخواهید لوگو شرکت خود را همیشه داشته باشید ) باید به ابتدای تعریف کلمه کلیدی eeprom یا flash را اضافه کنید تا در حافظه های دائمی ذخیره گردد .
نکته ۳ : در صورتی که اندیس آرایه را ننویسیم ، یک آرایه با طول اتوماتیک بوجود می آید. یعنی به تعدادی که در ابتدای برنامه آرایه مقدار می گیرد ، به همان اندازه از حافظه مصرف می شود. مثال :
1 |
int a[]={1,2,3,4,5,6}; |
آرایه های چند بعدی
در تعریف آرایه دو بعدی باید ۲ اندیس و در تعریف آرایه سه بعدی باید ۳ اندیس و در تعریف آرایه n بعدی باید n اندیس را ذکر کرد .
آرایه های چند بعدی در نمایشگر های lcd کاربرد دارند .به عنوان مثال :
1 |
int table [10] [10]; |
1 |
int k [5] [10] [15]; |
نکته : تعریف آرایه ها با مجموعه عناصر زیاد در حافظه SRAM به علت محدودیت در حجم حافظه ممکن است باعث ایجاد مشکل شود. بنابراین معمولا آرایه های با حجم زیاد را در بخش خالی حافظه flash ذخیره می کنند.
مقدار دهی به آرایه های چند بعدی
برای مقدار دهی اولیه به آرایه های دو بعدی سطر ها را به ترتیب پر می کنیم . مثال :
1 |
Int a[2][3]={ {3,1,2} , {7,4,6} } |
برای مقدار دهی به آرایه های سه بعدی ابتدا سطرهای طبقه اول و سپس سطرهای بقیه طبقات را مقدار دهی می کنیم . مثال :
1 |
Int a[2][3][4]={ { {1,2,3,4} ,{5,4,3,2} , {2,3,4,5} } , { {7,6,5,4} , {9,0,8,7} , {6,7,4,1} } } |
برای مقدار دهی ثانویه در حین برنامه میتوان به صورت زیر عمل کرد :
1 |
a[0][1][3]=2354; |
نکته 1 : برای مقدار دهی ثانویه اندیس ها از 0 تا n-1 میتواند باشد.
نکته 2 : در مقدار اندیس آرایه های چند بعدی ، به جای عدد میتوان از متغیر استفاده کرد.
رشته ها در C
در زبان سی برای نمایش کلمات و جملات از رشته ها استفاده می شود . رشته همان آرایه ای از کاراکتر ها است که حاوی اطلاعاتی می باشد . تمام کاراکتر ها شامل اعداد و حروف و برخی کاراکترهای دیگر که روی صفحه کلید کامپیوتر وجود دارند ، دارای کد شناسایی ASCII ( American Standard Code For Information Interchange ) می باشند . کدهای اسکی که توسط استاندارد آمریکایی در سال ۱۹۶۷ ابداع شد و در سال ۱۹۸۶ دست خوش تغییراتی شد .
کاراکتر ست اسکی خود به دو نوع تقسیم میشود. نوع ۷ بیتی که با نام اسکی استاندارد (Standard ASCII) شناخته شده و دارای ۲ به توان ۷ یعنی ۱۲۸ کاراکتر مختلف است که از ۰ تا ۱۲۷ استفاده میشوند. نوع دیگر آن حالت ۸ بیتی است که با نام اسکی توسعه یافته (Extended ASCII) شناخته شده و دارای ۲ به توان ۸ یعنی ۲۵۶ کاراکتر مختلف است که از ۰ تا ۲۵۵ استفاده میشود. حالت توسعه یافته جدا از حالت استاندارد نیست بلکه از ۰ تا ۱۲۷ کاراکتر اول آن درست مانند حالت استاندارد بوده و فقط بقیه کاراکترها (از ۱۲۸ تا ۲۵۵) به آن اضافه شده است. کاراکترهای اضافی دارای هیچ استانداردی نبوده و ممکن است در دستگاهها و کامپیوترهای مختلف فرق داشته باشد و به منظور ایجاد کاراکترهای زبان دوم ( مثلا زبان فارسی ) ایجاد شده است . یعنی ممکن است در یک کامپیوتر کاراکتر اسکی ۱۵۰ معادل حرف û و در کامپیوتر دیگر که روی زبان دوم فارسی تنظیم شده است ، معادل حرف ب باشد . اما کاراکترهای قبل از ۱۲۸ همگی ثابت هستند. کاراکترهای فارسی در اینکدینگ Iranian System شرکت ایرانیان سیستم که یکی از قدیمی ترین اینکدینگهای ASCII فارسی است را میتوانید در این لینک ببینید.
در هر دو نوع ذکر شده (۷ و ۸ بیتی) تعداد ۳۲ کاراکتر اول (یعنی از ۰ تا ۳۱) و آخرین کاراکتر (۱۲۷) با عنوان کاراکترهای کنترلی (Control Characters) شناخته میشود. این کاراکترها غیرقابل چاپ بوده و فقط برای کنترل متن مورد استفاده قرار میگیرد (مثلاً مشخص کننده ابتدای هدر، حذف، کنسل و …). بقیه کاراکترها یعنی از ۳۲ تا ۱۲۶ قابل چاپ هستند. این کاراکترها شامل نمادها، حروف و اعداد انگلیسی هستند. در حالت توسعه یافته، از کاراکترهای ۱۲۸ تا ۲۵۵ نیز قابل چاپ هستند.
در شکل زیر این ۹۵ کاراکتر قابل چاپ انگلیسی را به همراه کد اسکی آن در مبنای دسیمال مشاهده می کنید .
تعریف یک کاراکتر
تعریف یک کاراکتر توسط نوع متغیر char صورت می گیرد و مقدار اولیه آن داخل کوتیشن ( ‘ ‘ ) قرار می گیرد . در زبان سی وقتی یک حرف بین ‘ و ‘ قرار می گیرد کد اسکی آن درون متغیر ذخیره می شود . مثال :
1 |
char c='H'; |
تعریف رشته ( آرایه ای از کاراکتر ها )
برای تعریف رشته از آرایه ای از کاراکترها استفاده می شود و مقدار اولیه آن داخل دابل کوتیشن ( ” ” ) قرار می گیرد . مثال :
1 |
char s[10]="Hello!"; |
اگر تعداد خانه های آرایه ذکر شود ، آرایه سایز مشخصی دارد و در صورتی که تعداد کاراکترهای عبارت یا جمله ای که درون آن میریزیم بیشتر از سایز آرایه باشد ، عبارت ناقص ذخیره خواهد شد و در صورتی که تعداد کاراکترهای مورد نظر کمتر باشد بقیه آرایه خالی خواهد بود . اگر تعداد خانه های آرایه ذکر نشود یعنی سایز آرایه بر اساس مقداری که درون آن ریخته می شود محاسبه شود . مثال :
1 |
char str[]="Hello!..." |
کاراکترهای کنترلی
در محتویات یک رشته علاوه بر کاراکترهای قابلچاپ میتوان کاراکترهای کنترلی قرارداد. این کاراکتر ها برای کنترل صفحه نمایش میباشند و به شکل جدول زیر میباشند:
کاراکتر کنترلی | کاری که انجام میشود |
\n | به خط بعد میرود |
\t | به اندازه 8 فاصله به جلو میرود(تب) |
\a | بوق سیستم را به صدا در می آورد |
\\ | کاراکتر \ را چاپ میکند |
\” | کاراکتر ” را چاپ میکند |
\v | به 8 خط بعد میرود |
\b | کاراکتر قبل از خودش را حذف میکند(بک اسپیس) |
\r | کلید را مشخص میکند |
\? | علامت ? را چاپ میکند |
\: | علامت : را چاپ میکند |
عملگرها در C
با استفاده از عملگرها می توان روی اعداد ، متغیرها ، آرایه ها ، رشته ها و … عملیات حسابی ، منطقی ، مقایسه ، بیتی ، بایتی و … انجام داد.
عملگرهای محاسباتی
عملگرهای محاسباتی، عملگرهایی هستند که اعمال محاسباتی را روی عملوندها انجام میدهند. عملگر % برای محاسبه باقیمانده تقسیم به کار میرود. این عملگر عملوند اول را بر عملوند دوم تقسیم میکند (تقسیم صحیح) و باقیمانده را برمیگرداند. در جدولهای زیر، عملگرهای محاسباتی و تقدم آنها در یک معادله مشاهده میشود:
تقدم عملگر های محاسباتی
دو عملگر ++ و– همانطور که مشاهده میکنید در طرف چپ و راست متغیر قرارگرفتهاند. اگر بهتنهایی و در یک خط دستور بهکاربرده شوند، فرقی نمیکند اما اگر در یک معادله بهکاربرده شوند، اینکه طرف راست یا چپ قرار گیرند، متفاوت است. به مثال زیر توجه کنید:
1 2 3 4 |
x++; ++x; y = ++x; y = x++; |
همانطور که در مثال مشاهده میکنید، در دو دستور اول به متغیر x یک واحد اضافه میشود. در دستور سوم، ابتدا یک واحد به مقدار متغیر x اضافهشده و سپس درون متغیر y قرار میگیرد. در دستور چهارم، ابتدا مقدار متغیر x درون متغیر y قرارگرفته و سپس یک واحد به آن اضافه میشود. اگر تا قبل از رسیدن به دستور چهارم، مقدار x برابر با ۱۰ باشد، پس از گذشتن از دستور چهارم، مقدار y برابر ۱۰ و x برابر با ۱۱ است.
عملگرهای مقایسه ای و منطقی
عملگرهای مقایسهای ارتباط بین عملوند ها را مشخص می کنند و عملگر های منطقی بر روی عبارات منطقی عمل می کنند. عبارات منطقی دارای دو ارزش درستی و نادرستی اند و زمانی که باید یک شرط مورد بررسی قرار بگیرد، استفاده می شوند. به طور مثال برای بررسی مساوی بودن دو متغیر از عملگر == استفاده می شود. در زبان c، ارزش نادرستی با ۰ و ارزش درستی با مقادیر غیر صفر مشخص می شود.
تقدم عملگرهای رابطه ای و منطقی
عملگرهای ترکیبی
این عملگر ها ترکیبی از عملگر مساوی و عملگرهای دیگر هستند. به طور مثال عملگر =+ در نظر بگیرید، اگر به شکل x += a نوشته شود، برابر با x = x+a است؛ یعنی هر بار متغیر x را با متغیر a جمع می کند و درون متغیر x قرار میدهد. برای دیگر عملگر های ترکیبی نیز به همین صورت است. این عملگر ها پایین ترین تقدم را در بین عملگرهای دیگر دارند. این عملگر ها، عملگر های حسابی را شامل می شوند. در جداول زیر عملگرهای ترکیبی و تقدم آنها را میبینید.
تقدم عملگرهای ترکیبی
تعریف عملگرهای بیتی
عملگر های بیتی بر روی هر بیت یک بایت اثر می گذارند . به طور مثال ، a یک متغیر ۸ بیتی با مقدار باینری ۱۱۱۰۰۱۰۱ است ، اگر عملگر not را روی a اثر دهیم ، نتیجه معکوس شدن هر بیت هست . a =~ a برابر با مقدار باینری ۰۰۰۱۱۰۱۰ است . عملگرهای دیگر نیز به همین شکل اثر خود را اعمال میکنند
در جدولهای زیر عملگرهای بیتی و تقدم آنها را مشاهده میکنید:
تقدم عملگرهای بیتی
تقدم کلی در عملگرها
همانطور که مشاهده می شود عملگر پرانتز دارای بیشترین اولویت است . با استفاده از این عملگر میتوان انجام محاسبات را اولویت داد به طوری که اولویت اول همیشه با داخلی ترین پرانتز است و سپس به ترتیب تا پرانتز بیرونی اولویت دارند .مثال :
1 |
y=(x+(a/(3+g)))*2; |
همانطور که در مثال بالا مشاهده میکنید، ابتدا g+3 انجام میشود، در مرحله بعد ((a/(3+g) انجام میشود، در مرحله بعدی (((x+(a/(3+g) انجام میشود و در انتها (x+(a/(3+g)))*2 انجام میشود.
تبدیل نوع در محاسبات
برای درک بهتر در مورد تبدیل نوع در محاسبات ، به مثال زیر توجه کنید.
1 2 3 4 5 |
int a=9,b=2; float x,y,z; x=a/b; y=(float) a/b; z=9.0/2.0; |
در مثال بالا با اینکه متغیرهای x و y و z هر سه از نوع float هستند ولی مقدار x برابر با ۴ و مقدار y و z برابر با ۴٫۵ است. اگر متغیر های a و b از نوع float بودند ، مقدار x نیز برابر ۴٫۵ می شد اما چون متغیر های a و b از نوع int هستند باید یک تبدیل نوع در محاسبه توسط عبارت (float) در ابتدای محاسبه انجام شود.
اتصال سون سگمنت به میکرو
یکی از نمایشگرهای پرکاربرد سون سگمنت است که می توان توسط آن اعداد و برخی حروف ها را نشان داد . روش های متفاوت و مختلفی برای راه اندازی سون سگمنت توسط avr وجود دارد . ساده ترین روش اتصال سون سگمنت به میکرو استفاده از مداری به شکل زیر است . همانطور که مشاهده می شود در این روش از ۸ بیت ( یک پورت ) به طور کامل استفاده می شود . ( نرم افزار proteus بیت هشتم که به digit سون سگمنت وصل می شود را ندارد )
برای نشان دادن اعداد روی سون سگمنت کافی است پایه مربوطه را پس از خروجی کردن یک کنیم برای مثال برای نشان دادن عدد یک باید PA1 و PA2 را ۱ نمود بنابراین دستور PORTA=0x06 عدد ۱ را روی سگمنت نمایش می دهد . بنابراین در این روش برای راحتی هنگام کار با سون سگمنت آرایه زیر را که شامل کد سون سگمنت اعداد ۰ تا ۹ می باشد ، تعریف می کنیم .
1 |
unsigned char seg[10]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; |
هر عددی که بخواهیم نمایش دهیم کافی است داخل [ ] قرار دهیم تا به کد سون سگمنت تبدیل شود . برای مثال :
1 |
PORTA=seg[ i ]; |
همانطور که دیدید در این روش پایه های زیادی از میکرو صرف نمایش تنها یک سون سگمنت می شود. بجای استفاده از مدار فوق میتوان از آی سی ۷۴۴۸ استفاده کرد که در این صورت تنها ۴ بیت از پورت میکرو اشغال می شود و همچنین بعلت تبدیل اتوماتیک کدهای سون سگمنت دیگر نیازی به استفاده از آرایه تعریف شده در قبل نیست . همانطور که در شکل زیر نیز مشاهده می کنید در این اتصال نصف پورت اشغال می شود .
راهنمای آی سی ۷۴۴۸ :
کار این آی سی خواندن یک عدد باینری ۴بیتی بوسیله پایه های ورودی اش و نمایش آن روی سون سگمنت می باشد . این آی سی ۱۶ پایه دارد و نحوه شماره بندی پایه های آن بصورت زیر می باشد:
نکته : در استفاده از آی سی ۷۴۴۸ ، حتما می بایست از سون سگمنت کاتد مشترک استفاده کنید. برای استفاده از سون سگمنت آند مشترک آی سی دیگری به نام ۷۴۴۷ وجود دارد .
نحوه کار آی سی :
ورودی این آی سی همانطور که در جدول مشاهده کردید ، شامل چهار پایه ۱ ، ۲ ،۶ و ۷ ( که با حروف A ، B ، C و D نشان داده می شود ) می باشد. هر کدام از این پایه می توانند ۰ یا ۱ باشند (ولتاژ ۰ یا ۵ ولت داشته باشند). پس عدد ورودی ما به آی سی یک عدد باینری (در مبنای ۲ ) می باشد که فقط چهار رقم دارد.
از طرفی خروجی این آی سی کد های مخصوص سون سگمنت است که پس از نمایش روی سون سگمنت و در مبنای ۱۰ نمایش می یابد. ( خروجی این آی سی تنها اعداد ۰ تا ۹ میتواند باشد )
تغذیه آی سی :
آی سی هایی که شماره آنها با ۷۴ شروع می شود ، اصطلاحا TTL نامیده می شوند. این آی سی ها نسبت به ولتاژ تغذیه بسیار حساسند و ولتاژ آنها می بایست دقیقا ۵ ولت باشد. حداکثر تغییرات ولتاژی که می توان اعمال کرد در حد ۴٫۷۵ تا ۵٫۲۵ ولت است .
پیاده سازی مدار روی بردبورد :
-ابتدا آی سی ۷۴۴۸ را روی بردبورد جا می زنیم (روی شیار وسط برد بورد قرار می گیرد)
-در قسمت دیگری از برد بورد یک سون سگمنت کاتد مشترک قرار داده و جا می زنیم(سون سگمنت نیز روی شیار برد بورد قرار می گیرد و هر دسته از پایه ها در یک سمت )
– مطابق جدول مربوط به پایه های آی سی ۷۴۴۸ ، پایه های ۳ و ۴ و ۱۶ را به ردیف ولتاژ مثبت برد بورد وصل می کنیم.
– پایه ۸ به ولتاژ منفی وصل می شود.
– یکی از پایه های مشترک سون سگمنت را به ولتاژ منفی وصل می کنیم.
– هر کدام از پایه های خروجی آی سی را با سیم به پایه هم نامش روی سون سگمنت وصل می کنیم.
– ورودی های ۷۴۴۸ نیز که باید به میکرو وصل شود مطابق شکل وصل می کنیم .
– در گوشه دیگری از بردبورد یک رگولاتور ۷۸۰۵ جا می زنیم.
– سیمی که ولتاز مثبت آداپتور را حمل می کند به پایه input وصل می کنیم.
– سیمی که ولتاژ منفی آداپتور را حمل می کند به پایه Ground وصل می کنیم.
– پایه های output و Goround رگولاتور را به ردیف های مثبت و منفی کنار بردبورد وصل می کنیم تا آی سی ها از این خطوط تغذیه شوند.
سون سگمنت های مالتی پلکس
در برخی طراحی ها ممکن است به بیش از یک سون سگمنت نیاز باشد . در این طراحی ها از سون سگمنت مالتی پلکس شده استفاده می شود. تنها تفاوت این سون سگمنت در این است که ۸ بیت دیتا ( a ، b ، c … ) برای همه سگمنت ها با هم یکی شده است ( مشترک هستند ) .پایه های سون سگمنت مالتی پلکس شده ۴ تایی را در شکل زیر مشاهده می کنید . در صورت اتصال پایه های Com سون سگمنت مربوطه روشن می شود .
بنابراین مدار مورد نظر در این طراحی به صورت زیر است . وجود ترانزیستور npn در این طراحی بعلت تامین مناسب جریان برای هر سگمنت است. زیرا طبق دیتاشیت هر پایه میکرو جریان بیش از ۲۰ میلی آمپر را نمیتواند تامین کند اما در حالتی که چندین سگمنت همزمان روشن است جریانی در حدود ۱۰۰ میلی آمپر مورد نیاز است و بنابراین اگر ترانزیستور نباشد سگمنت ها به خوبی روشن دیده نخواهد شد.
روش نمایش بر روی این نمایشگر به این صورت است که ابتدا همه کاتد ها خاموش است یعنی دارای منطق یک است ( مقاومت های ۱۰ کیلو اهمی برای همین منظور طراحی شده اند ) سپس دیتایی که میخواهیم روی سگمنت اول نمایش دهیم روی پایه های دیتا قرار می گیرد . سپس کاتد سگمنت اول را برای مدت کوتاهی صفر می کنیم تا عدد مورد نظر نشان داده شود ( یک کردن Base ترانزیستور توسط میکرو باعث اتصال زمین به کاتد سون سگمنت می شود ) سپس دوباره کاتد سگمنت اول را یک می کنیم و این کار را برای سگمنت های بعدی نیز تکرار می کنیم . اگر این کار با سرعت انجام شود همه سون سگمنت ها همزمان روشن دیده خواهد شد . برای استفاده از سون سگمنت شکل فوق تابعی به نام display به صورت زیر تعریف می کنیم .
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
void display(void) { PORTA=data[0]; PORTA.4=1; delay_ms(5); PORTA.4=0; PORTA=data[1]; PORTA.5=1; delay_ms(5); PORTA.5=0; PORTA=data[2]; PORTA.6=1; delay_ms(5); PORTA.6=0; PORTA=data[3]; PORTA.7=1; delay_ms(5); PORTA.7=0; } |
توضیح : قبل از این تابع در برنامه آرایه data از نوع unsigned char و سایز ۴ تعریف شده و در برنامه این آرایه مقدار مورد نظری که میخواهیم نمایش دهیم را به خود می گیرد . سپس با فراخوانی تابع display در این تابع ابتدا دیتای مربوط به سون سگمنت اول روی پورت قرار می گیرد سپس با ۱ کردن پایه com و صبر کردن به مدت ۵ میلی ثانیه ، دیتای مورد نظر روی سون سگمنت به نمایش در می آید . سپس سون سگمنت اول را خاموش کرده و دیتای دوم روی پورت قرار می گیرد و …
اتصال صفحه کلید به میکرو
صفحه کلید نیز یک وسیله ورودی پرکاربرد دیگر است که دارای مجموعه ای از کلیدها است که به صورت ماتریسی به هم بسته شده اند. همانطور که می دانیم اتصال صفحه کلید به میکروکنترلرها در بسیاری از موارد برای ما مهم و کاربردی است ، به عنوان مثال شما می خواهید یک ماشین حساب طراحی کنید یا یک قفل رمز دار و یا هر سیستم دیگری که نیاز است از کاربر اطلاعاتی توسط صفحه کلید دریافت شود. صفحه کلید ها انواع مختلفی دارند. از صفحه کلید تلفن گرفته تا صفحه کلید کامپیوتر ، به تعداد سطر ها و ستون های آن خروجی دارند . صفحه کلیدهای پرکاربرد موجود در بازار معمولا ۴ سطر و ۳ یا ۴ ستون دارند . در شکل زیر انواع صفحه کلیدها را در نرم افزار proteus که در بازار نیز موجود است ، مشاهده می کنید .
همانطور که در شکل زیر ساختار داخلی یک صفحه کلید ۴*۴ نشان داده شده است ، برای خواندن صفحه کلید ابتدا همه ستون ها را به صورت شکل زیر توسط مقاومت به تغذیه مثبت وصل کرده و از همه سطرها و ستون ها مستقیم به میکرو وصل می کنیم . سپس سطرها را به عنوان خروجی و ستون ها را به عنوان ورودی میکرو تنظیم می کنیم .
بنابراین در حالت عادی همه سطرها ۱ و ستون ها نیز چون توسط مقاومت به Vcc متصل هستند ۱ خوانده می شوند . در شکل فوق اگر Row0 توسط میکرو ۰ شود و بقیه ردیف ها همان ۱ باشند ، هر کدام از دکمه های ردیف اول که زده شود ، سیگنال ۰ مستقیم توسط ستون ها به پایه میکرو منتقل شده و توسط میکرو خوانده می شود . بدین ترتیب می توان با خواندن منطق ستون ها به دکمه زده شده پی برد .
مدار داخلی صفحه کلیدهای دیگر نیز دقیقا به صورت فوق هستند . در شکل زیر نحوه چیدمان پایه های صفحه کلید ۴ در ۳ را مشاهده می کنید .
بنابراین برای اتصال هرگونه صفحه کلید کافی است همه ستون ها از یک طرف به مقاومت pullup و از طرف دیگر به میکرو ، و همه سطر ها نیز مستقیم به میکرو متصل گردد . در اینجا صفحه کلید ۴ در ۳ را به صورت زیر به پورت D میکرو متصل کردیم .
برای خواندن صفحه کلید ابتدا یک سطر را صفر و سطرهای بعدی را یک می کنیم . سپس کمی صبر میکنیم ( ۱ تا ۲ میلی ثانیه ) و ستون ها را می خوانیم اگر همه ستون ها یک باشد یعنی در آن سطر کلیدی زده نشده است ، آن سطر را یک و سطر بعدی را صفر می کنیم و دوباره ستون ها را می خوانیم و اگر باز هم یک بود به سراغ سطر های بعدی می رویم تا اینکه تمام سطر ها خوانده شود و این کار را با سرعت بالا ادامه می دهیم . اما اگر کلیدی زده شود سطر و ستونی که کلید روی آن قرار دارد به هم وصل می شوند در نتیجه در هنگام خواندن سطرها متوجه صفر شدن می شویم . در برنامه نیز میتوان تابعی مانند تابع زیر را تعریف کرد که تمام این کارها را انجام دهد .
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 |
void keyboard(void) { //---- ROW1 ---- PORTD.3=0; delay_ms(2); if(PIND.0==0) key=1; if(PIND.1==0) key=2; if(PIND.2==0) key=3; PORTD.3=1; //---- ROW2 ---- PORTD.4=0; delay_ms(2); if(PIND.0==0) key=4; if(PIND.1==0) key=5; if(PIND.2==0) key=6; PORTD.4=1; //---- ROW3 ---- PORTD.5=0; delay_ms(2); if(PIND.0==0) key=7; if(PIND.1==0) key=8; if(PIND.2==0) key=9; PORTD.5=1; //---- ROW4 ---- PORTD.6=0; delay_ms(2); if(PIND.1==0) key=0; PORTD.6=1; } |
توضیح : قبل از این تابع در برنامه متغیر key را از جنس unsigned char و مقدار اولیه دلخواه به جز اعداد ۰ تا ۹ تعریف کرده ایم . با فراخوانی تابع keyboard در برنامه تابع فوق اجرا می شود . این تابع برای همه سطرها ابتدا سطر مورد نظر را صفر کرده و بعد از تاخیر منطق ستون ها را می خواند در صورت ۰ بودن منطق هر یک یعنی کلید مورد نظر زده شده است و بنابراین مقدار متغیر key برابر با عدد کلید زده شده می شود . متغیر key نماینده کلید زده شده است که در برنامه از آن استفاده می شود .
مثال عملی شماره ۳
یک صفحه کلید ۴ در ۳ و یک سون سگمنت مالتی پلکس شده mpx4 کاتد مشترک را به میکروکنترلر Atmega32 وصل نمایید . برنامه ای بنویسید که با فشار دادن صفحه کلید عدد مربوطه از صفحه کلید دریافت و روی سون سگمنت نمایش داده شود .
حل :
مرحله اول : طراحی سخت افزار
مرحله دوم : طراحی نرم افزار
در این مرحله به سراغ نرم افزار CodeVision رفته و طبق مراحل گفته شده در فصل قبل پروژه جدید را می سازیم و فرکانس میکرو را روی ۱Mhz داخلی قرار می دهیم . برنامه نهایی به صورت زیر است .
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 |
#include <mega32.h> #include <delay.h> unsigned char data[4]={0x0f,0x0f,0x0f,0x0f}; void display(void){ register unsigned char i; unsigned char select[4]={0x80,0x40,0x20,0x10}; for(i=0;i<4;i++){ PORTA=data[i]; PORTA=PORTA | select[i]; delay_ms(5); PORTA=0x0f; } } unsigned char keyboard(void){ register unsigned char i,j; unsigned char select[4]={0xF0,0x68,0x58,0x38}; for(i=0;i<4;i++){ PORTD=select[i]; delay_ms(2); if((PIND & 0x07 )!= 0x07){ for(j=0;j<3;j++) if((PIND & (1<<j))==0) return i*3+j+1; delay_ms(2); } PORTD=0xf8; } return 20; } void main (void){ unsigned char j,key=20; unsigned int i=0,i1; DDRD=0xf8; PORTD=0xf8; DDRA=0xff; PORTA=0x0f; while(1){ key=keyboard(); if(key==11)key=0; if((key!=20) && (key<10)) { i=i*10+key; key=20; i1=i; for(j=0;j<4;j++) { data[j]=i1%10; i1=i1/10; } } for(j=0;j<10;j++) display(); } } |
توضیح : آرایه data همان اعداد روی سگمنت هستند که چون در تمام برنامه به آن احتیاج داریم در ابتدای برنامه و به صورت global تعریف شده است . توابع display و keyboard دقیقا کار همان توابع گفته شده را انجام می دهند و حتی میتوان آن ها را با هم جایگزین کرد . تنها تفاوت این دو تابع با قبل این است که به صورت برنامه وار و حرفه ای تر نوشته شده اند تا خطوط کمتری اشغال و حجم حافظه برنامه را کاهش دهد . در حلقه while ابتدا عدد گرفته شده از صفحه کلید درون متغیر key ریخته می شود . تابع keyboard در صورتی که هیچ کلیدی زده نشده باشد مقدار ۲۰ را بر می گرداند و اگر مقدار key برابر ۱۱ باشد یعنی عدد ۰ را کاربر فشار داده و توسط if مقدار آن اصلاح می شود . در صورتی که کاربر کلیدی را زده باشد و کلیدی که زده شده یکی از کلید های ۰ تا ۹ باشد شرط if دوم برابر شده و داخل آن می شود . متغیر i همان عددی است که روی ۴ عدد سون سگمنت توسط متغیرهای data باید نمایش داده شود . بنابراین در حلقه for موجود عدد i به ۴ قسمت (یکان ، دهگان و …) تبدیل می شود و هر کدام روی متغیر data مربوطه قرار می گیرد . و در نهایت تابع display برای ۱۰ بار اجرا می شود که اگر یک بار اجرا شود برنامه بسیار سریع عمل میکند .
اتصال صفحه کلید 4 در 4 به میکرو
فرض میکنیم صفحه کلیدی با ۴ سطر و ۴ ستون را به صورت شکل زیر به میکرو متصل کرده باشیم.
در این صورت برای خواندن از صفحه کلید به روش سطری/ستونی که برای صفحه کلید ۳ در ۴ نیز گفته شد ، دقیقا به همان صورت عمل می کنیم. بنابراین برنامه به صورت زیر در می آید.
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 |
void keyboard(void) { PORTD=0xf0; //---- ROW1 ---- PORTD.4=0; delay_ms(2); if(PIND.0==0) PORTA=7; if(PIND.1==0) PORTA=4; if(PIND.2==0) PORTA=1; if(PIND.3==0) PORTA=10; PORTD.4=1; //---- ROW2 ---- PORTD.5=0; delay_ms(2); if(PIND.0==0) PORTA=8; if(PIND.1==0) PORTA=5; if(PIND.2==0) PORTA=2; if(PIND.3==0) PORTA=0; PORTD.5=1; //---- ROW3 ---- PORTD.6=0; delay_ms(2); if(PIND.0==0) PORTA=9; if(PIND.1==0) PORTA=6; if(PIND.2==0) PORTA=3; if(PIND.3==0) PORTA=11; PORTD.6=1; //---- ROW4 ---- PORTD.7=0; delay_ms(2); if(PIND.0==0) PORTA=12; if(PIND.1==0) PORTA=13; if(PIND.2==0) PORTA=14; if(PIND.3==0) PORTA=15; PORTD.7=1; } |
تمرین : برنامه فوق را با برنامه صفحه کلید ۳ در ۴ مقایسه کنید. مشاهده می شود در هر سطر برای خواندن ستون ها از ۴ دستور if اسفاده شده است.
نتیجه : برای خواندن صفحه کلید به صورت ستونی ، به تعداد ستون ها از دستور شرطی if استفاده می شود.
برنامه حرفه ای تر اتصال صفحه کلید 4 در 4 به میکرو
روش خواندن از صفحه کلید در برنامه قبلی به صورت سطری/ستونی بود. برای حرفه ای تر شدن برنامه یک آرایه select تعریف می کنیم که بتوان از آن آرایه در حلقه for استفاده کرد و اندیس حلقه for را به جای آرگومان آرایه استفاده کرد. بنابراین با فرض اتصال یک صفحه کلید ۴ در ۴ به همان صورت شکل قبلی به میکروکنترلر ، برنامه زیر را داریم :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
char keypad(void) { char code[16]={'7','8','9','/','4','5','6','*','1','2','3','-','c','0','=','+'}; unsigned char select[4]={0x0E,0x0C,0x0B,0x07}; unsigned char i,j; for(i=0;i<4;i++) { PORTD=select[i]; delay_ms(2); if((PIND & 0xF0)!=0xF0) for(j=0;j<4;j++) { if((PIND & 1<<(4+j)) != 1<<(4+j)) return code[i*4+j]; } } return 20; PORTD=0x0F; } |
توضیح : عملکرد این تابع نیز دقیقا به همان صورت گفته شده در مثال عملی شماره ۳ است با این تفاوت که یک آرایه به نام code برای اصلاح اعداد صفحه کلید در نظر گرفته شده است. در صورتی که این آرایه نباشد با زدن مثلا کلید ۷ عدد ۰ نمایش داده می شود ! بنابراین به جای اینکه i*4+j به خروجی فرستاده شود ، [code[i*j+j به خروجی فرستاده می شود تا بدین وسیله اعداد اصلاح شود.
نکته : آرایه code را میتوان به صورت char یا unsigned char تعریف کرد. زمانی که در پروژه از 7segment استفاده می شود باید آرایه code را به صورت unsigned char تعریف کرد و در صورت استفاده از LCD باید آرایه code را به صورت char تعریف کرد. البته در صورت استفاده از سون سگمنت نمیتوان کاراکترهایی مثل ضرب ، جمع و … را روی آن نمایش داد.
تذکر : به تفاوت های میان برنامه نوشته شده برای صفحه کلید ۳ در ۴ با صفحه کلید ۴ در ۴ توجه کنید.
با کلیک بر روی تصویر زیر به بخش بعدی آموزش بروید
در صورتی که این مطلب مورد پسندتان بود لایک و اشتراک گذاری فراموش نشود.
دیدگاه (34)
با سلام و خسته نباشید.
توضحات شما عالی بودن ولی اگر امکانش هست همین توضیحات رو بصورت یه فایل pdf بزارید توی سایتتون که بشه دانلود کرد
سلام دوست عزیز سلامت باشید . با تکمیل مباحث حتما این کار را خواهم کرد .
سلام دوست گرامی مطالب بسیار عالی و با ارزش هستند امیدوارم همیشه هم بهتر بشه
اما یک سوال هم داشتم اونم اینه که اگه بخوایم یه کلید واسه میکرو بزاریم که وقتی کلید رو فشار دادیم و نگه داریم یه کاری رو انجام بده و وقتی کلید رو رها کنیم اون کار رو دیگه انجام نده ؟آیا چنین چیزی امکانش هست؟لطفا راهنماییم کنید
با تشکر
سلام دوست عزیز . خیلی ممنون لطف دارید
بله کلید نوع ۱ که در بالا به آن اشاره و توضیحات آن داده شد دقیقا همین کار را میکند . با نگه داشتن کلید پشت سر هم کار مورد نظر انجام می شود و به محض رها شدن کلید کار پایان می یابد .
سلام دوستان.من یه متغیر در eepromتعریف کردم از نوع int و متوجه شدم که هر مقداری که بهش بدم اون فقط مقدار -۲۲۱۱۷ رو داره.و اگه از نوع unsigned char تعریف کنم فقط ۱۵۵ رو داره هر چند که من مقدار دیگه ای بهش بدم.این کارو بعد از نمایش روی lcd متوجه شدم.کسی میدونه مشکل از کجاس؟
سلام
ازتون راهنمایی می خواستم
چطور می تونم یک شمارنده باینری با mega16 درست کنم . یعنی چهارتا یایه رو انتخاب کنم و ابتدا led های متصل به آن ها خاموش باشند ( ۰۰۰۰) بعد با یکم تاخیر یکی یکی به اون اضافه بشه تا اینکه همه led ها روشن بشن (۱۱۱۱ ) . یعنی در واقع برنامه یک شمارنده باینری رو می خواستم.
ممنون
سلام دوست عزیز در واقع شما یک شمارنده باینری بالا شمار می خواهید که در همین تایپیک مثال عملی شماره ۲ توضیح داده شده است …
سلام
ببخشید یه سوال داشتم. آی سی ۷۴۴۷ سه تا پایه داره به اسم های BI/RBO – RBI – LT . این پایه ها برای چی هستن؟
الان دیدم شما برای ۷۴۴۸ اون ۳ پایه رو دادین به تغذیه. ولی من یه برنامه نوشتم که آی سی ۷۴۴۷ رو بدون این که اون ۳ تا پایه به جایی وصل باشند گذاشتم و کامل هم درست جواب داد. حالا حتمآ باید وصل باشند؟
سلام خواهش میکنم . این پایه ها فعال کننده ورودی ها و خروجی ها هستند که در هنگام پیاده سازی مدار حتما باید به منبع متصل شوند. پروتئوس فقط یک نرم افزار شبیه سازی هستش که هر چند این پایه ها در این نرم افزار متصل نشده باشند ، مدار درست کار می کند اما در عمل و در پیاده سازی اینگونه نیست و این نکته باید رعایت شود.
کلا این ۳ تا پایه برای ۷۴۴۸ یا ۷۴۴۷ یا اگه تو هر آی سی دیگه ای بود باید به Vcc وصل بشن؟ یا بسته به دیتاشیت اونها ممکنه به زمین باید وصل بشن؟
خیلی خیلی ممنون از پاسختون.
ببخشید انقدر سوال می پرسم.
فرق کلید – شاسی – میکروسوئیچ چیه؟ تو پروتئوس هر کدوم چه اسمی دارند؟ آخه از هر قطعه تو پروتئوس چند نوع مختلف با یک اسم هست.
خواهش میکنم … شاید سوال شما سوال بقیه هم باشه …
در کل نمیشه به پروتئوس اعتماد کرد و باید به دیتاشیت قطعه مراجعه کرد …
کلید انواع مختلفی داره که توی پروتئوس شامل DIP Switch ، Switch ، Push Button میشه که هر کدوم کاربرد خاص خودشو داره و اگه تو پروتئوس این اسم ها رو بزنید میاره …
سلام با تشکر از مطالب خوب شما
اگر بخوایم مقدار یک رشته رو صفر کنیم باید از چه دستوری استفاده کنیم؟
سلام ممنونم
برای صفر کردن یک آرایه یا یک رشته از کاراکترها باید تمامی عناصر آن را با استفاده از حلقه فور صفر کرد مثلا برای یک آرایه 8 کاراکتری به نام str باید نوشت :
for(i=0;i<8;i++) str[i]=0
سلام
من یه سوال دارم
من وقتی صفحه کلید 3*4 رو به پایه های میکرو وصل میکنم ومدارو تو پروتئوس ران میکنم, ارور میده که پایه های AتاD
صفحه کلید به تغذیه وصل نشده.چیکار کنم؟
یعنی بیام سطرهارو مثل ستونها PULL UP کنم؟
با عرض سلام
با تشکر از سایت خوبتون
ببخشید من یه سوال داشتم من می خوام یه تاخیر 40 ثانیه در خروجی داشته باشم بدین صورت که شما فکر کنید دو تا ورودی داریم و دو تا خروجی و با تغییر وضعیت ورودی خروجی نیز تغییر می کند.
حالت اولیه ورودی هام یکه و خروجی هام صفر وقتی ورودی یکم صفر بشه خروجی یکم یک میشه و وقتی ورودی صفر شد 40 ثانیه صبر کنه و بعد خروجیم صفر بشه
حالا مشکل اینه که در این زمان تاخیر که به میکرو می گم اگه ورودی دو فعال بشه اونو نمی بینه…….!!!!!
میشه راهنماییم کنید
میشه تاخیر یا تایمری ساخت که بیرون از مین برنامه باشه…….؟؟؟؟؟؟؟؟؟؟؟
سلام دوست عزیز ممنونم
بله میتونید تایمر یک ثانیه ایجاد کنید و داخل روتین وقفه هر ثانیه یک واحد اضافه کنید و به محض ۶۰ شدن خروجی را تغییر وضعیت دهید. به بخش تایمر مراجعه کنید
سلام با تشکر از مطالب خیلی مفیدتون
میخواستم بدونم چطور میشه مقدار متغیرها رو طوری حفظ کرد که با ریست شدن میکرو از بین نره. البته میدونم همون طور که گفتید باید محل تعریف اونها رو flash یا eeprom در نظر بگیرم مثلا بنویسیم flash int; یا eeprom int; اما من میخوام بدونم دقیقا چطور باید عمل کنیم یا اگر مثلا بخواهیم این متغیرهایی که در حافظه فلش هستن رو با یک متغیر موجود در sram مقدار دهی کنیم چه کار باید کرد چون من این کارو که میکنم توی کدویژن خطای زیر رو به من میده:
the expression must be a modifiable ivalue
سوال دیگه اینکه دلیل اینکه میکرو AVR هر چند وقت یک بار به طور خودکار ریست میشه چیه و چطور این مشکل حل میشه؟ آیا مشکل از تغذیه است؟ حتی وقتی که از رگولاتور 7405 با ورودی 12 ولت هم استفاده میکنم این مشکل وجود داره. و در کل به نظر شما استفاده از سری MEGA AVR برای یک کاربرد واقعی یا صنعتی که نیاز به عملکرد 24 ساعته میکرو داره مناسب هست یا نه باید سراغ میکروکنترلر دیگه ای برم.
خواهش میکنم کمکم کنید بنده یک پروژه واقعی در دست اجرا دارم و باید در قبال آن جوابگو باشم خدا خیرتون بده
یا علی
عرض سلام و معذرت بابت تاخیر در پاسخگویی
بله فقط کافیه در هنگام تعریف از نوع flash یا eeprom تعریف شوند
خطایی که به شما میدهد برای چیز دیگری است
علت ریست شدن میکرو از تغذیه میتونه باشه یا واحد تایمر Watchdog آن فعال است
میکروکنترلرهای AVR در صورت رعایت تمهیدات سخت افزاری و نرم افزاری کاملا صنعتی بوده و میتوان در محیط های پر نویز هم از آن ها استفاده نمود موفق باشید
سلام وخسته نباشید
برنامه ledدر قسمت ششم اموزش مستقیمابا#includeنوشته شده
اما همون برنامه در این قسمت اموزش باspan styleشرو شده میخواستم بدونم تفاوت این دو نوع برنامه نویسی که هر دو یک کارو انجام میدن چیه دقیقا؟
سلام دوست عزیز span style یک اشتباه تایپی هستش و شما نباید در نظر بگیرید
اها مرسی.
سوال دیگرم اینه که
دستورamp& در کلید نوع سه چی هستش دقیقا؟
چون بالاتر در موردش توضیح ندادین
و دیگر اینکه در برنامه کلید نوع دو بلافاصله بعد از فشردن کلید دستورات باید اجرا بشن
در حالی که بلافاصله پس از فشردن کلید حالتbounceپیش میاد و کلید منطق ثابتی نداره
به نظرم باید خط دو وسه جاشون عوض شه باز نمیدونم
اصلاح میکنم سوال دومم رو که گفتم کلید نوع دو اما باید مینوشتم کلید نوع یک
bounceو…
سلام و درود فراوان
من این شرط رو (if(flag متوجه نمیشم مثال عملی 2
میشه در موردش توضیح بدین
فلگ چه نوع متغییری هست که ایف داره اونو بررسی می کنه
سلام دوست عزیز
متغیر flag یک متغیر کمکی هست برای زمانی که دو حالت عملکرد داریم در حالت اول flag=0 و یک کار خاصی انجام می شود و در حالت دوم flag=1 و یک کار خاص دیگری انجام می شود
سلام دوست عزیز،
من یه سوال دارم
چطور میشه به ال سی دی کاراکتری، کاراکتر های کنترلی رو ارسال کرد، من فایل alcd.h رو خوندم اما بنظرم تابع خاصی برای این کار نداره، یا من نمی دونم، حتی با خود lcd_putchar هم عدد اسکی مربوط به کاراکترهای کنترلی مثلا 1 تا 30 رو فرستادم اما تغییری ندیدم، من می خوام مکان نما برای رفتن روی کاراکتری خاص جابجا بشه، تا مثلا روی ساعت تغییر ایجاد کنم، یعنی کاربر بتونه ساعت رو تنظیم کنه، خب باید کاربر بدونه الآن داره چی رو تغییر میده، بخاطر همین می خوام کاربر مکان نما رو جابجا کنه تا به کاراکتر مخصوص برسه و اون رو تغییر بده، ممنون میشم راهنماییم کنی،
راستی یه سوال دیگه، میگن تو السیدی کاراکتری فقط میشه 8 تا کاراکتر فارسی ایجاد کرد، بنظرت راسته، یعنی نمیشه بیشتر بشه؟
بازم ممنون
یا علی
سلام بله میتوان به LCD کاراکترهای کنترلی ارسال کرد. برای اطلاعات بیشتر کافی است به help نرم افزار کدویژن سپس help topic سپس Library Function Refference و سپس Alphanumeric LCD Function مراجعه نمایید. ضمنا به علت محدودیت حافظه LCD تنها میتوان 8 کاراکتر ایجاد کرد.
سلام
segment اعداد ناقص نمایش میده مثلا برای صفر تمامی led ها روشن میشن به جز d مشکل کجاست
با سلام ضمن عرض خسته نباشی به خاطر زحمات خلصانه شما.خواهشمندیم اگر امکان اره بحث شمارنده جانسون وحلقوی رو یه مقدار ساده تر توضیح بدید ممنون میشیم.
سلام دوست عزیز ممنون از نظرتون حتما سعی میکنم مجددا توضیح جامع تری دهم
با سلام و عرض ادب
من میخوام روی atmega8 از سه تا پورت مختلف به صورت همزمان استفاده کنم
یعنی همزمان که پورت B چشمک میزنه پورت C و D یه کار متفاوت کنن
چجوری باید دستورش رو بنویسم
سلام
ممنون از توضیحات جامع تون
یه سوال
چجوری می تونیم با استفاده از سیستم تیک یه کیپد 4 در 4 رو راه بندازیم. چون بالاخره تو میکرو استفاده زیاد از delay زیاد جالب نیست. همچنین جلوی بانس هارو با delay در همه مواقع نمیشه گرفتن. ممنون میشم راهنمایی کنید یا اگه سایتی هست که خوب توضیح داده معرفی کنید.
سلام. ممنون از توضیحات جامع و خوبتون. در مثال عملی دوم، توی پروتئوس قبل سوئیچ فشاری یک مقاومت 10k اهمی هست بعدش با یک فلش زده بالا. فلشه رو از کجا باید پیدا کرد؟ و کارش چیه؟
بعد شما با مقاومت ساختین سوئیچ رو با خازن هم میشه درسته؟
سلام
سپاس از توضیحات عالی این سایت! من می خواهم چند سنسور را از طریق مد باس rs485 به هم ارتباط بدهم. حال برای هر میکرو(سنسور) چطور باید یک آدرس تخصیص دهم؟