سلامی دیگر به علاقهمندان به میکروکنترلرهای AVR. با قسمت پنجم از آموزشهای AVR در خدمت شما هستیم و در این بخش سعی بر این داریم که با مفاهیم ورودی و خروجی، مقاومت pullup و عملکرد رجیسترهای IO در ATmega16 آشنا بشیم. ابتدا بررسی میکنیم که رجیستر در avr چیست ؛ سپس در مورد رجیسترها یا همان ثباتهای ورودی و خروجی که شامل DDR، PIN و PORT میشود صحبت خواهیم نمود. با ما همراه باشید.
رجیستر در avr چیست ؟
در تمام میکروکنترلرهای سری AVR قسمتی از حافظه RAM وجود دارد که به آن رجیسترهای SFR گفته میشود. طول هر کدام از این رجیسترها 8 بیت یا یک بایت است و هر کدام وظایف خاصی برعهده دارند. وظایفی مثل کنترل کردن ورودیها و خروجیها، راهاندازی تایمرهای میکرو، راه اندازی ارتباطات سریال و … . در واقع مقداردهی رجیسترها یا ثباتها، سبب تغییر در عملکرد سختافزاری میکروکنترلر شده و به ما اختیارات زیادی در کنترل میکروکنترلر میدهند. شکل زیر فضای حافظه RAM را نشان میدهد.
حافظه RAM میکرو از سه بخش اصلی تشکیل شده است:
- رجیسترهای R که نزدیکترین ثباتها به CPU میکرو هستند و به نوعی کَش (Cache) پردازنده محسوب میشوند. Cache به قسمتی از حافظه گویند که نزدیکترین تماس را با CPU دارد و با سرعت بیشتری میتوان بر روی آن اطلاعات را خواند یا نوشت.
- رجیسترهای SFR که اول همین پست در مورد آنها گفتیم و وظیفه اصلی آن ایجاد تغییرات در سخت افزار داخلی میکرو است.
- Internal SRAM که همان قسمت اصلی RAM میکروکنترلر است و مانند رم کامپیوتر برای نگهداری دادههای موقت از آن استفاده میشود.
در تمام این سری از آموزش ما تنها با قسمت رجیسترهای SFR کار میکنیم. گفتنی است که برنامهنویسان اسمبلی مجبور به کار کردن با دو قسمت رجیسترهای R و Internal SRAM هستند و چون زبان برنامهنویسی ما C است، مدیریت این دو قسمت توسط کامپایلر CodeVision انجام میشود.
رجیسترهای ورودی و خروجی
تا اینجا جواب این سوال برای مشخص شد که رجیستر در AVR چیست . حال به بررسی رجیسترهای IO میپردازیم.
در فضای SFR مربوط به ATmega16 رجیسترهایی هستند که تنها مخصوص کار کردن با پایهها میباشند. کارهایی نظیر مقداردهی، خواندن مقدار منطقی هرپایه یا مجموعهای از پایهها و فعال یا غیرفعالسازی مقاومت pullup. بیایید قبل از اینکه بحث را تخصصیتر کنیم، ابتدا نگاهی به پایههای میکرو بیندازیم.
ATmega16 دارای چهار پورت D ، C ، B ، A است که هرکدام دارای 8 پایه هستند. به عنوان مثال PB0، بیت صفرم از پورت B بوده و پایه 1 میکرو میباشد. اگر دقت کنید کنار نام هر پایه، نامی دیگر در داخل پرانتز نوشته شده که وظیفه دوم آن پایه را بیان میکند. وظیفه دوم هر پایه متفاوت است و بهتر است در جلسات آینده در مورد آن توضیح دهیم.
اما وظیفه اول تمام پایههای یک پورت، عمل کردن به صورت ورودی و خروجی است. هر پورت برای اینکار، سه رجیستر بسیار مهم در فضای SFR دارد: DDR ، PORT و PIN. در مجموع با احتساب چهار پورت 12 رجیستر برای این کار منظور شده است.
رجیسترهای DDR
وظیفهی این رجیسترها مشخص کردن جهت هر پایه است. به عبارتی دیگر هر پایه میتواند ورودی و یا خروجی باشد. اگر بر روی هر کدام از بیتهای این رجیستر 1 نوشته شود، پایه متناظر آن به صورت خروجی و اگر 0 نوشته شود بصورت ورودی عمل میکند.
به عنوان مثال برنامه زیر را در نظر بگیرید.
1 2 3 4 5 6 7 |
#include <mega16.h> void main(void){ DDRB = 0xFF; DDRC = 0x0F; while(1); } |
اول برنامه کتابخانه مربوط به ATmega16 را وارد کردیم. سپس در تابع main مقدار رجیستر DDRB (رجیستر DDRB مربوط به پورت B) را برابر 0xFF قرار دادیم. با این کار تمام بیتهای این رجیستر 1 شده و باعث میشود تمام پایههای پورت B به صورت خروجی عمل کنند. همینطور مقدار DDRC را هم 0x0F کردیم و این باعث میشود 4 پایه کم ارزش پورت C یعنی از PC0 تا PC3 به صورت خروجی و 4 پایه پر ارزش یعنی از PC4 تا PC7 به صورت ورودی تعریف شوند.
رجیسترهای PIN
کار این رجیسترها فقط و فقط خواندن مقدار پورت است. بنابراین تنها زمانی میتوان این رجیستر را خواند که بیتهای متناظر آن بصورت ورودی تعریف شده باشند.
همانطور که در شکل بالا میبینید، زیرِ تمام بیتهای این رجیسترها حرف R نوشته شده که بیانگر فقط خواندنی بودن (Read) آن است. به مثال زیر توجه کنید.
1 2 3 4 5 6 7 8 9 |
int x; bit y; void main(void){ DDRA = 0x00; x = PINA; y = PINA.5; while(1); } |
دو متغیر x و y که به ترتیب int و bit هستند، تعریف کردیم. سپس رجیستر DDRA را تماما برابر 0 کردیم. با اینکار تمام 8 بیت این رجیستر 0 شده و تمام پایههای پورت A به صورت ورودی عمل میکنند. در خط بعد برای خواندن مقدار این پورت از رجیستر PINA استفاده کردیم و مقدار آن را درون x قرار دادیم. در نهایت بیت پنجم از PINA را درون y ریختیم.
نکته: در هر سه رجیستر DDR و PIN و PORT (هنوز این مورد رو نگفتم) با قرار دادن یک نقطه و یک عدد از 0 تا 7 میتوان به صورت مستقیم به بیت مورد نظر مقدار 0 یا 1 را داد یا اینکه مقدار آن را خواند.
رجیسترهای PORT
این رجیستر دو حالت کاری دارد:
- اگر بیت متناظر در رجیستر DDR یک باشد (یعنی آن پایه خروجی باشد)، با نوشتن بر روی بیت متناظر این رجیستر، میتوان آن پایه را 0 یا 1 کرد.
- اگر بیت متناظر در رجیستر DDR صفر باشد (یعنی آن پایه ورودی باشد)، اگر روی بیت متناظر این رجیستر 0 نوشته شود، پایه به صورت ورودی عادی و اگر 1 نوشته شود، پایه ورودی بوده اما مقاومت pullup آن فعال میشود.
به عنوان مثال برای حالت اول کد زیر را مینویسیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <mega16.h> #include <delay.h> void main(void) { DDRA.0 = 1; while(1){ PORTA.0 = 1; delay_ms(500); PORTA.0 = 0; delay_ms(500); } } |
همانطور که گفتیم، بیت صفرم از رجیستر DDRA را برابر 1 کردیم. با این کار پایه صفرم پورت A خروجی میشود. سپس برنامه وارد حلقه بیانتهای while میشود. در این حلقه به صورت مداوم با تاخیر 500 میلی ثانیهای، بیت صفرم رجیستر PORTA بین 0 و 1 تغییر حالت میدهد. با این کار اگر یک LED به پایه PA0 متصل شده باشد، به صورت چشمک زدن روشن و خاموش میشود.
برای حالت دوم برنامه زیر را مینویسیم.
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <mega16.h> #include <delay.h> void main(void) { DDRA.0 = 1; DDRA.1 = 0; PORTA.1 = 1; while(1){ PORTA.0 = !PINA.1; } } |
خط اول: بیت صفرم DDRA برابر 1 و در نتیجه پایه صفرم پورت A خروجی میشود.
خط دوم: بیت یکم DDRA برابر 0 و در نتیجه پایه یکم پورت A ورودی میشود.
خط سوم: بیت یکم PORTA برابر 1 و در نتیجه مقاومت pullup پایه یکم فعال میشود.
حلقه while: در این حلقه مقدار پایه PINA.1 که ورودی است خوانده شده و با علامت ! معکوس آن درون پایه صفرم ریخته میشود.
در گیف بالا مشاهده میکنید که پایه یکم از پورت A خوانده شده و معکوس آن درون پایه صفرم ریخته میشود. لازم به ذکر است که در شبیهساز Proteus رنگ آبی بیانگر 0 منطقی و رنگ قرمز بیانگر 1 منطقی است.
جمعبندی
خب در این مطلب که در مورد رجیسترها یا همون ثباتها بود سعی کردیم که به شما دوستان و همراهان عزیز بگیم که اصلا رجیستر در avr چیست ؛ بعد از اون در مورد رجیسترهای ورودی و خروجی (IO) که شامل DDR، PIN و PORT میشود صحبت کردیم؛
دوستان این قسمت از آموزش هم تمام شد. اگر مشکلی یا ابهامی در مورد این رجیسترها هنوز ذهنتان را مشغول کرده است (حتی اگه هنوز متوجه نشدید که رجیستر در avr چیست 😅)، زیر همین پست کامنت بگذارید تا تیم رزدینو بتونه به سرعت پاسخگوی شما عزیزان باشه. تا جلسه بعدی آموزش مقدماتی AVR خدانگهدار.
14 پاسخ
عالی بود . ممنون
در مورد رجیستر پورت من متوجه کاربردش نشدم .
تا اونجایی که من فهمیدم مقدار رجیستر پین در هر لحظه برابر رجیستر پوزته.
اگه اینطوریه به چه درد میخوره پس !
کلا از پورت استفاده کنیم دیگه .
ممنون🙏
چجوری میتونیم رجیستر ها رو بخونیم یا روشون بنویسیم
عالی خدا خیرت بده
عالی بود استفاده کردم.کاش مقاومت pullup و اهمیتش رو هم میگفتین واینکه چه وقت نیازه ورودی عادی باشه یا مقاومت Pullup داشته باشه
مقاومت pullup زمانی کاربرد داره که مثلا کلیدی متصل به یک پایه آست و آن طرف کلید به زمین وصله. توی این حالت زمانی که کلید متصل میشه مقدار پایه برابر 0 و زمانی که کلید باز است، از آنجا که مقاومت pullup فعاله، مقدار پایه 1 خوانده میشه.
یک سوال دیگه هم داشتم و اینکه رجیستر PORT برای مقداردهی به خروجی ها استفاده میشه؟با توجه به جلسه ۶ آموزش و مثالی که زدین می پرسم.چون من فکر میکردم خروجی فقط توسط میکرو مقدار میگیره
بله رجیستر PORT فقط برای مقدار دهی به خروجی ها یا فعال سازی مقاومت pullup ورودی هاست.
سلام وقتتون بخیر،فایل کاملشو بزارین بصورت pdf
سلام. دوست عزیز مطالب به صورت pdf موجود نیست. بهتره که صفحه رو به صورت html سیو کنید.
سلام ممنون از آموزشتون
چطور میشه داخل یک حلقه پورت های مختلف رو مقدار دهی کرد
من همچین ساختاری رو نوشتم ولی ارور میگیرم
for (j=0; j<i ; j++){
PORTA.i = 1;
}
سلام. هر پورت 8 پایه داره. واسه مقدار دهی به هر پایه باید به این صورت عمل کنید:
واسه 1 کردن پایه: PORTA = PORTA | (1<
ممنون از مقاله خوبتون