مقدمه
همانطور که در قسمت های قبلی آموزش گفته شد ، در میکروکنترلرهای AVR ارتباط سریال در 4 پروتکل زیر وجود دارد :
- USART : پایه های Rx و Tx
- SPI : پایه های MISO ، MOSI ، SCK و SS
- I2C : پایه های SDA و SCL
- USB : پایه های D+ و D-
نکته : تنها برخی از میکروکنترلرهای AVR از ارتباط USB پشتیبانی می کنند و در Atmega32 ارتباط USB وجود ندارد.
معرفی ارتباط سریال I2C
I2C مخفف عبارت Inter Integrated Circuit به معنای مدار مجتمع یکپارچه می باشد. به علت اینکه در این پروتکل تنها از دو سیم برای ارتباط دو یا چند وسیله استفاده می شود ، این پروتکل را ارتباط دو سیمه ( TWI ) مخفف Two Wire Interface نیز می نامند. این پروتکل توسط شرکت philips در سال 1982 طراحی و به کار گرفته شد.
از خصوصیات این رابط در میکروکنترلرهای AVR میتوان به موارد زیر اشاره نمود :
- رابطی انعطاف پذیر ، قدرتمند و با سرعتی نسبتا مناسب که فقط نیاز به دو خط انتقال دارد.
- قابلیت پشتیبانی از مدهای عملکرد Master و Slave در حالت های فرستنده و گیرنده.
- دارای 7 بیت آدرس دهی که قابلیت ارتباط یک Master با حداکثر 128 Slave را فراهم می کند.
- پشتیبانی از ارتباط تعدادی Master با هم ( Multi Masters )
- حداکثر سرعت انتقال اطلاعات تا 400KHz
- حذف نویز مدارات روی گذرگاه
- محدودیت در نرخ چرخش ( SlewRate ) در خروجی درایور ها
- بیدار شدن خودکار میکروکنترلر از حالت Sleep به محض ارسال آدرس آن
- قابلیت فراخوانی عمومی همه Slave ها توسط Master
شبکه بندی Master و Slave ها در پروتکل I2C
همانطور که در شکل فوق مشاهده می کنید ، در این پروتکل تمامی دستگاه ها به دو سیم SDA و SCL متصل شده و هر یک از خطوط توسط یک مقاومت بالاکش PullUp به منبع تغذیه وصل می شوند. خط SDA برای انتقال دیتای سریال ( Serial Data ) و خط SCL برای انتقال کلاک سریال ( Serial Clock ) به کار می رود. طبق استاندارد ، مقدار مقاومت پول آپ برای منبع تغذیه 5 ولت برابر 4.7K و برای منبع تغذیه 3.3 ولت برابر 1.5K می باشد.
نکته : استفاده از پروتکل I2C در فواصل بسیار کوتاه میان Master و Slave ها ( کمتر از 20 سانتی متر ) امکان پذیر است. برای فواصل طولانی تر باید تقویت سیگنال صورت گیرد. بنابراین در ارتباطات i2c با فاصله بین 20 تا 70 سانتی متر میتوان از مدار تقویت کننده زیر به جای مقاومت های پول آپ استفاده کرد.
قالب بندی ارتباط در پروتکل I2C
ابتدا به منظور جلوگیری از تداخل دستگاه ها با یکدیگر ، به هر دستگاه یک آدرس منحصر به فرد بین 0 تا 127 اختصاص می یابد. سپس برای ارسال داده به یکی از دستگاه های متصل به باس I2C ، دستگاه Master ابتدا پالس 7 بیتی مربوط به آدرس دستگاه مورد نظر را به صورت سریال ( از MSB با ارزشترین بیت تا LSB کم ارزش ترین بیت ) به همراه یک بیت برای تعیین خواندن از Slave یا نوشتن بر روی آن ( Read/Write ) به باس SDA ارسال می کند و سپس بعد از دریافت آن توسط Slave مورد نظر ، یک بیت مبتنی بر تایید در دسترس بودن Slave به Master ارسال می شود (Aknowledge) سپس دستگاه Master داده های مورد نظر را در قالب 8 بیتی ارسال می کند و پس از ارسال هر بسته 8 بیتی یک بیت Aknowledge دریافت می کند. دریافت داده نیز تقریبا همین قالب را دارد با تفاوت یک بیت که نشان دهنده دریافت است.
باس SCL همواره در حال ارسال کلاک از Master به تمام Slave ها است. به صورتی که هر بیتی که از باس SDA از Master ارسال می گردد ، تنها در لبه های کلاک SCL توسط Slave ها خوانده می شود. در شکل زیر دو باس SDA و SCL را به همراه عملکرد آن مشاهده می کنید.
بنابراین قالب دیتا در پروتکل I2C به صورت سه وضعیت زیر می باشد :
وضعیت Start/Stop
در حالت عادی هر دو خط SCL و SDA در وضعیت سکون ( High ) قرار دارند. زمانی که قصد ارسال/دریافت توسط Master را داشته باشیم ، ابتدا یک لبه پایین رونده به منظور شروع ارسال/دریافت توسط Master ایجاد می شود. بعد از وضعیت شروع ، گذرگاه مشغول می باشد و هیچ Master دیگری نباید سعی کند کنترل گذرگاه را بر عهده بگیرد. در پایان عملیات نیز یک لبه بالا رونده به منظور توقف ارسال/دریافت رو گذرگاه قرار می گیرد و از آن به بعد دوباره گذرگاه به حالت آزاد ( عادی ) بر می گردد.
وضعیت ارسال آدرس
در وضعیت آدرس ، یک بسته بندی 9 بیتی بر روی گذرگاه داده از فرستنده به گیرنده ارسال می شود. این 9 بیت تشکیل شده است از 7 بیت آدرس به همراه یک بیت کنترل خواندن یا نوشتن Raed/Write و یک بیت به نام ACK است که گیرنده برای فرستنده ارسال می کند تا مشخص شود کد آدرس دریافت شده صحیح است. بنابراین زمانی که Slave آدرس خود را تشخیص داد ، باید با پایین بردن خط SDA در نهمین سیکل کلاک SCL ، بیت ACK را ارسال نماید. اگر بیت مربوط به R/W یک شده باشد ، زمانی که از وضعیت ارسال آدرس به وضعیت بعدی یعنی وضعیت ارسال/دریافت دیتا برویم ، عملکرد نوشتن Read روی Slave و در صورت صفر بودن این بیت عملکرد خواندن Write روی Slave اجرا می شود.
وضعیت ارسال/دریافت دیتا
در این وضعیت نیز یک بسته بندی 9 بیتی شامل 8 بیت دیتا و یک بیت ACK وجود دارد. تعداد داده ها ممکن است بیشتر از 8 بیت باشد. در این صورت تا زمان پایان همه داده ها ، سیستم در وضعیت ارسال/دریافت دیتا باقی می ماند تا اینکه تمامی داده های در بسته بندی 9 بیتی ارسال/دریافت شوند. در هر بار ارسال/دریافت گیرنده می بایست بعد از دریافت 8 بیت دیتا با Low نمودن خط SDA در نهمین سیکل کلاک SCL ، صحت دریافت را ACK نماید. در غیر این صورت با بالا رفتن خط SDA ، فرستنده متوجه عدم دریافت صحیح شده و دیتا را مجددا ارسال می نماید.
مدهای عملکرد واحد TWI
بر اساس فرستنده ( Transmitter ) یا گیرنده ( Reciever ) بودن هر میکرو و اینکه هر میکرو میتواند Master یا Slave باشد. 4 مد عملکرد در واحد TWI به صورت زیر بوجود می آید. یعنی هر یک از میکروکنترلرهای AVR می تواند یکی از چهار حالت زیر را به خود بگیرد :
MT ( یعنی Master و فرستنده ) : در این مد ابتدا توسط Master یک وضعیت Start ایجاد می شود. سپس بایت های داده به سمت گیرنده Slave ارسال می شود.
MR ( یعنیMaster و گیرنده ) : در این مد Master منتظر وضعیت Start می ماند و سپس تعدادی بایت از Slave ارسال و توسط Master دریافت می شود.
ST ( یعنی Slave و فرستنده ) : در این مد فرستنده یک Slave است که اطلاعاتی را به یک MR ارسال می کند.
SR ( یعنیSlave و گیرنده ) : در این مد گیرنده یک Slave است که اطلاعاتی را از یک MT دریافت می کند.
انواع دسترسی به رابط I2C در کدویژن
برای دسترسی به رابط دو سیمه و استفاده از آن در میکروکنترلرهای AVR بوسیله نرم افزار Codevision به دو صورت زیر میتوان عمل کرد :
- دسترسی به واسط I2C با استفاده از واحد سخت افزاری TWI
- دسترسی به واسط I2C به صورت نرم افزاری با اضافه کردن هدرفایل i2c.h
تفاوت های استفاده از واسط I2C سخت افزاری با نرم افزاری
1- در صورت استفاده از I2C سخت افزاری تنها میتوان پایه های SDA و SCL در میکروکنترلرهای AVR را استفاده نمود در حالی که در صورت استفاده از I2C نرم افزاری میتوان هر دو پایه دلخواه را به عنوان SDA و SCL تعریف و استفاده کرد.
2- در صورت استفاده از I2C نرم افزاری ، بخشی از CPU درگیر تولید پالس های SDA و SCL می شود در حالی که در صورت استفاده از I2C سخت افزاری ، یک واحد مجزا درگیر می شود و سرعت برنامه بیشتر می شود.
3- در صورت استفاده از I2C نرم افزاری ، با اضافه شدن هدرفایل مربوطه میتوان از توابع آماده موجود فقط در حالت های MR/MT استفاده کرد اما در استفاده از I2C سخت افزاری هدر فایل آماده ای نیست و باید با رجیسترها کار کرد. البته میتوان در هر چهار حالت توابع مورد نیاز را به صورت دستی وارد کرد.
فعالسازی رابط I2C در کدویزارد
در ابزار کدویزارد برای فعالسازی I2C سخت افزاری به سربرگ TWI و برای فعالسازی I2C نرم افزاری به سربرگ i2c می رویم. شکل زیر تفاوت آن را نشان می دهد.
راه اندازی I2C نرم افزاری
برای فعالسازی واحد ارتباطی دو سیمه نرم افزاری بعد از رفتن به سربرگ i2c ، ابتدا پورتی را که میخواهیم به صورت نرم افزاری از آن به عنوان رابط استفاده نماییم را انتخاب و سپس شماره پایه پورت دلخواه را در قسمت SDA Bit و SCL Bit به ترتیب برای خطوط SDA و SCL انتخاب می نماییم. در قسمت پایین سربرگ ابزار کدویزارد برای برخی از آی سی های پرکاربرد مانند LM75 ، DS1307 ، DS1621 ، PCF8563 و PCF8583 تنظیمات خاصی درنظر گرفته است که میتوان با فعال کردن آنها توابع خاصی را برای استفاده در پروژه خود به برنامه افزود.
بعد از تولید کد توسط برنامه کدویزارد مشاهده می شود که هدرفایل i2c.h اتوماتیک به پروژه افزوده شده و تنظیمات مربوط به بیت های SDA و SCL قبل از آن اضافه شده است. با اضافه شدن این هدر فایل میتوان از توابع زیر در پروژه برای ارتباط سریال استفاده نمود.
توابع موجود در کتابخانه i2c.h
- تابع i2c_init
این تابع گذرگاه TWI نرم افزاری را روی مقادیر SDA و SCL اولیه فراخوانی و راه اندازی می کند. به همین دلیل باید قبل از فراخوانی توابع دیگر این تابع را صدا زد. الگوی این تابع به صورت زیر است :
1 |
void i2c_init(void) |
- تابع i2c_start
با اجرای این تابع یک وضعیت Start ایجاد می شود و اگر گذرگاه I2C آزاد باشد ، مقدار یک توسط این تابع باز می گردد و در غیر این صورت مقدار صفر باز خواهد گشت. الگوی این تابع به صورت زیر است :
1 |
unsigned char i2c_start(void) |
- تابع i2c_stop
با اجرای این تابع یک وضعیت stop بر روی گذرگاه I2C ایجاد می شود. الگوی این تابع به صورت زیر است :
1 |
void i2c_stop(void) |
- تابع i2c_read
این تابع از گذرگاه I2C یک بایت را می خواند. ورودی این تابع یک بیت ack است. در صورتی که ack=0 باشد بایت وارد شده صحیح ارزیابی نشده است و در صورتی که ack=1 باشد بایت وارد شده صحیح بوده است. الگوی این تابع به صورت زیر است :
1 |
unsigned char i2c_read(unsigned char ack) |
- تابع i2c_Write
این تابع یک بایت را به گذرگاه I2C ارسال می کند. اگر گیرنده Slave بیت ACK را صادر کرده باشد ، این تابع مقدار یک را باز می گرداند و در غیر این صورت مقدار بازگشتی صفر خواهد بود. الگوی این تابع به صورت زیر است :
1 |
unsigned char i2c_write(unsigned char data) |
نکته : این توابع در میکروکنترلرهای سری Atxmega پشتیبانی نمی شود.
نکته : فرکانس I2C نرم افزاری ثابت بوده و قابل تنظیم نمی باشد. در صورتی که فرکانس کاری میکروکنترلر 1 مگاهرتز باشد ، فرکانس I2C نرم افزاری روی 62.5 کیلوهرتز است و هنگامی که فرکانس کاری میکروکنترلر روی 8 مگاهرتز باشد ، فرکانس I2C نرم افزاری روی 400 کیلوهرتز می باشد. ( همواره روی حداکثر فرکانس ممکن است )
نکته مهم : به علت محدودیت های استفاده از توابع I2C نرم افزاری ، فقط میتوان از این توابع در میکروکنترلر Master و در یکی از حالت های MR/MT استفاده نمود.
نتیجه : با استفاده از i2c نرم افزاری توسط یک میکروکنترلر Master میتوان با انواع Slave ها نظیر انواع سنسورها ، EEPROM ها و … ارتباط برقرار کرد اما Slave نمیتواند خود یک میکروکنترلر باشد که از i2c نرم افزاری استفاده می کند. بنابراین برای ارتباط میان چند میکروکنترلر بوسیله پروتکل I2C باید از واحد TWI سخت افزاری استفاده کرد.
نحوه استفاده از توابع i2c.h
بعد از انجام تنظیمات مربوط به I2C نرم افزاری در کدویزارد و تولید کد میتوان از توابع موجود در هدرفایل i2c.h برای ساخت توابع جدیدی در پروژه استفاده نمود. این توابع عبارتند از یک تابع READ_I2C برای خواندن از دستگاه اسلیوی که به گذرگاه I2c متصل است و یک تابع WRITE_I2C برای نوشتن در دستگاه اسلیوی که به گذرگاه I2C متصل است. ساختار درونی این دو تابع جدید برای هر وسیله ای که به گذرگاه متصل شده باشد متفاوت است. مثلا برای اتصال EEPROM به گذرگاه یک ساختاری باید رعایت شود و برای اتصال سنسورها یا وسیله های دیگر ساختار مخصوص به آن باید رعایت شود که چگونگی این ساختار از روی دیتاشیت آن قطعه بدست می آید. به مثال زیر که در آن یک EEPROM راه اندازی می شود و توابع ساخته شده در آن توجه کنید.
مثال عملی شماره 9
برنامه ای برای ارتباط با EEPROM سریال AT24CXX بنویسید که از طریق ارتباط نرم افزاری I2C ، دیتاهایی دلخواه را در EEPROM ذخیره کند و سپس آن ها را روی LCD کاراکتری نمایش دهد.
معرفی آی سی های سری AT24CXX
این سری که آی سی های 8 پایه ساخت شرکت Atmel می باشد ، یک حافظه EEPROM به حجم های 2 ، 4 ، 8 ، 16 ، 64 ، 128 ، 256 ، 512 و 1024 کیلوبایت را به صورت ارتباط I2C در اختیار کاربر قرار می دهد. عددی که در آخر نام قطعه مشاهده می شود نشان دهنده حجم آن می باشد برای مثال آی سی AT24C512 دارای 512 کیلوبایت حافظه است. در شکل زیر پایه های این سری را مشاهده می کنید.
سه پایه A0,A1,A2 آدرس قطعه را مشخص می کند که شرکت اتمل برای اینکه امکان استفاده از چندین حافظه EEPROM در یک گذرگاه I2C را داشته باشیم ، آن را تعبیه کرده است. بنابراین تا 8 آی سی حافظه را میتوان روی گذرگاه قرار داد.
نکته : برخی از حافظه های سری AT24CXX سه آدرس قطعه ندارند و به جای آن دو یا یک یا صفر بیت آدرس دارند.
پایه حفاظت از نوشتن ( WP ) در صورتی که ولتاژ High داشته باشد ، دیگر نمیتوان در آی سی نوشت.
عملیات نوشتن در آی سی EEPROM
طبق گفته دیتاشیت ، برای نوشتن در این آی سی از پروتکل I2C به دو صورت خاص استفاده می شود. اولی برای نوشتن یک بایت در آن و دومی نوشتن یک صفحه ( page ) که متشکل از چندین بایت پشت سر هم ، می باشد.
نوشتن به صورت بایتی
در نوشتن به صورت بایتی ابتدا آدرس قطعه مشخص می شود ، سپس آدرس خانه ای از حافظه که میخواهیم در آن بنویسیم مشخص می شود و در نهایت دیتای مورد نظر برای ذخیره ارسال می شود. سیگنال DEVICE ADDRESS خود به صورت زیر می باشد :
در شکل فوق که سیگنال DEVICE ADDRESS را برای حافظه های 2 تا 16 کیلوبایت مشاهده می کنید. برای بقیه حافظه ها نیز مشابه همین می باشد. به طوری که در همه آن ها ابتدا 1010 ارسال می شود و سپس آدرس قطعه به صورت A2,A1,A0 ارسال می شود. برای قطعاتی که دو یا یک یا صفر بیت آدرس دارند 0 یا 1 بودن آن اهمیتی ندارد.
نوشتن به صورت صفحه ای
نوشتن صفحه ای همانند نوشتن بایتی است با این تفاوت که بعد از ارسال بایت اول ، بایت های دیتاهای بعدی (که در خانه بعد از WORD ADDRESS(N) در حافطه قرار خواهند گرفت ) ارسال می شود و سپس سیگنال STOP ارسال خواهد شد.
تابع نوشتن بایتی روی AT24CXX
با توجه به ساختار گفته شده برای نوشتن بایتی روی آی سی میتوان تابع WRITE_I2C را به صورت زیر تعریف کرد :
1 2 3 4 5 6 7 8 9 |
void write_eeprom(unsigned char data,unsigned int address) { i2c_start(); i2c_write(write_address_bus); i2c_write(address); i2c_write(data); i2c_stop(); delay_ms(10); } |
که در آن write_address_bus همان DEVICE ADDRESS می باشد که برای A2=0,A1=0,A0=0 برابر 160 است.
عملیات خواندن از آی سی EEPROM
طبق گفته دیتاشیت ، خواندن از EEPROM به سه صورت امکان پذیر است :
- خواندن از آدرس فعلی
- خواندن از آدرس مورد نظر
- خواندن متوالی
1- خواندن از آدرس فعلی
در این حالت دیتای آخرین آدرسی که وجود دارد برای Master ارسال می شود .
2- خواندن از آدرس مورد نظر
در این حالت ابتدا آدرس قطعه با بیت R/W=0 به Slave ارسال شده و سپس آدرس خانه حافظه مورد نظر ارسال می گردد. سپس دوباره آدرس قطعه این بار با R/W=1 ارسال شده و سپس دیتایی که در آدرس فرستاده شده بود برای Master ارسال می گردد.
3- خواندن متوالی
در این حالت از آخرین آدرس موجود یکی یکی دیتا ها را برای Master ارسال می کند.
تابع خواندن از آدرس مورد نظر آی سی EEPROM
با توجه به ساختار گفته شده برای خواندن از آی سی میتوان تابع READ_I2C را به صورت زیر تعریف کرد :
1 2 3 4 5 6 7 8 9 10 11 12 |
unsigned char read_eeprom(unsigned int address) { unsigned char data_read; i2c_start(); i2c_write(write_address_bus); i2c_write(address); i2c_start(); i2c_write(read_address_bus); data_read=i2c_read(0); i2c_stop(); return data_read; } |
که در آن write_address_bus همان DEVICE ADDRESS می باشد که برای A2=0,A1=0,A0=0 برابر 160 و read_address_bus با R/W=1 و برابر 161 است.
حل مثال عملی شماره 9 :
مرحله اول : طراحی سخت افزار
در این مرحله LCD و آی سی EEPROM با هر حجم دلخواهی را از کتابخانه آورده و به هر پورت دلخواهی از میکرو متصل می نماییم. در اینجا از 24C512 استفاده کردیم.
مرحله دوم : طراحی نرم افزار
در شکل زیر تنظیمات کدویزارد برای این مثال را مشاهده می کنید.
بعد از تولید کد توسط کدویزارد و حذف کدهای غیر ضروری برنامه اصلی را به صورت زیر خواهیم داشت :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
#include <mega32.h> #include <delay.h> #include <stdio.h> // I2C Bus functions #asm .equ __i2c_port=0x15 ;PORTC .equ __sda_bit=6 .equ __scl_bit=7 #endasm #include <i2c.h> // Alphanumeric LCD Module functions #include <alcd.h> #define write_address_bus 160 #define read_address_bus 161 char buffer[20]; void write_eeprom(unsigned char data,unsigned int address); unsigned char read_eeprom(unsigned int address); void main(void) { unsigned char read_data,write_data; int i; // I2C Bus initialization i2c_init(); // Alphanumeric LCD initialization // Connections specified in the // Project|Configure|C Compiler|Libraries|Alphanumeric LCD menu: // RS - PORTA Bit 0 // RD - PORTA Bit 1 // EN - PORTA Bit 2 // D4 - PORTA Bit 4 // D5 - PORTA Bit 5 // D6 - PORTA Bit 6 // D7 - PORTA Bit 7 // Characters/line: 16 lcd_init(16); write_data=55; for(i=0;i<100;i++){ write_eeprom(write_data,i); read_data=read_eeprom(i); lcd_clear(); sprintf(buffer,"add=%d,data=%d",i,read_data); lcd_puts(buffer); delay_ms(100); } while (1); } //------------------------ void write_eeprom(unsigned char data,unsigned int address) { i2c_start(); i2c_write(write_address_bus); i2c_write((address & 0xff00)>>8); //high byte address i2c_write((address & 0x00ff)); //low byte address i2c_write(data); i2c_stop(); delay_ms(10); } //-------------------------- unsigned char read_eeprom(unsigned int address) { unsigned char data_read; i2c_start(); i2c_write(write_address_bus); i2c_write((address & 0xff00)>>8); //high byte address i2c_write((address & 0x00ff)); //low byte address i2c_start(); i2c_write(read_address_bus); data_read=i2c_read(0); i2c_stop(); return data_read; } |
توضیح برنامه :
در این برنامه یک متغیر دلخواه 8 بیتی با مقدار 55 در همه خانه های حافظه EEPROM از آدرس 0 تا 99 ریخته می شود و سپس دوباره از آی سی خوانده شده و در آرایه کاراکتری buffer برای نمایش در LCD ریخته می شود. توابع خواندن و نوشتن در آی سی EEPROM به علت 512 کیلو بایتی بودن آن تغییر کوچکی کرده است به طوری که پهنای آدرس 16 بیتی است و در دو مرحله برای آی سی باید ارسال شود.
مرحله سوم : شبیه سازی در پروتئوس
راه اندازی پروتکل I2C به صورت سخت افزاری
برای فعالسازی واحد ارتباطی دو سیمه پس از رفتن به سربرگ TWI ابتدا آن را با زدن تیک مربوطه فعال می کنیم. در صورتی که میکروکنترلر در یکی از مدهای SR/ST قرار خواهد داشت ، انتخاب گزینه General Call Recognition موجب پاسخگویی این میکروکنترلر به فراخوانی عمومی ( در آدرس 00Hex ) می گردد. برای پاسخ گویی معمولی میکروکنترلر در یکی از مدهای SR/ST باید آدرس آن را در بخش Slave Adress تنظیم نمود.
انتخاب گزینه General Acknowledge موجب تولید پالس ACK در سه حالت زیر خواهد شد :
- Slave آدرس خود را تشخیص داده باشد.
- یک فراخوانی عمومی دریافت شود ( باید گزینه General Call Recognition فعال بوده باشد )
- داده جدیدی در مد MR یا SR دریافت شود.
از بخش Bit Rate نیز جهت تنظیم حداکثر فرکانس پالس های روی خط SCL استفاده می شود. حداکثر فرکانس قابل انتخاب 400 کیلو هرتز است. اگر نیاز به فعال کردن وقفه سریال دوسیمه داشته باشید گزینه 2wire Interrupt را فعال نمایید تا تابع سابروتین وقفه زیر در ابتدای برنامه اضافه شود.
نحوه استفاده از واحد TWI
برای استفاده از ارتباط I2C سخت افزاری ابتدا شبکه بندی دستگاه ها را به صورت استاندارد انجام داده و سپس تنظیمات کدویزارد را برای برنامه مورد نظر انجام می دهیم. حال در برنامه تولید شده توسط کدویزارد می بایست با رجیسترهای واحد TWI کار کرد. همانند توابعی که در قسمت قبل برای I2C نرم افزاری وجود داشت ، برای I2C سخت افزاری نیز وجود دارد که از آنها استفاده خواهیم کرد.
معرفی رجیسترهای واحد TWI
TWAR : مخفف TWI Address Register می باشد. یک رجیستر 8 بیتی است که 7 بیت پر ارزش آن آدرس Slave و بین 0 تا 127 است. همچنین بیت کم ارزش آن برای فعال/غیرفعال کردن قابلیت General Call یا فراخوانی عمومی است.
TWBR : مخفف TWI Bit Rate Register می باشد. عددی که در این رجیستر قرار می گیرد طبق رابطه زیر فرکانس کلاک کاری واحد TWI (فرکانس SCL) را مشخص می کند.
TWCR : مخفف TWI Control Register می باشد. بیت های این رجیستر به صورت شکل زیر می باشد.
بیت TWIE( مخفف TWI Interrupt Enable ) : فعال/غیرفعال کردن وقفه واحد TWI
بیت TWEN( مخفف TWI Enable) : فعال/غیرفعال کردن کل واحد TWI
بیت TWWC( مخفف TWI Write Collision ) : اگر به هنگام یک بودن بیت TWINT اقدام به نوشتن بر روی بیت ثبات TWDR کنیم این بیت یک می شود.در صورت نوشتن بر روی TWDR وقتی که TWINT صفر است، این بیت صفر می شود.
بیت TWSTO( مخفف TWI Stop Condition Bit) : اگر در حالت Master باشیم با یک کردن این بیت یک وضعیت پایان ارسال می شود. به هنگام ارسال حالت آغاز این بیت به صورت سخت افزاری صفر می شود.
بیت TWATA( مخفف TWI Start Condition Bit) : اگر درحالت Master باشیم و این بیت را یک کنیم، در صورتی که گذرگاه آزاد باشد حالت آغاز ارسال می شود.
بیت TWEA( مخفف TWI Enable Acknowledge ) : یک کردن این بیت باعث فعال شدن تایید دریافت یا ACK می شود.
نکته : اگر این بیت را صفر کنیم دستگاه در هیچ حالتی تایید دریافت ارسال نخواهد کرد، گویی از خط جدا شده است.
بیت TWINT ( مخفف TWI Interrupt ) : وقتی که سخت افزار واحد TWI وظیفه جاری خود را به پایان برساند این بیت ۱ می شود. چنانچه وقفه فعال باشد یک شدن این بیت باعث اجرای وقفه TWI می شود. با صفر کردن این بیت واحد TWI آغاز به کار می کند. دسترسی به ثبات های TWDR,TWSR و TWCR باید قبل از صفر کردن این بیت انجام شود. اگر در زمان یک بودن این بیت مقدار ثبات TWDR را تغییر دهیم تداخل به وجود می آید و بیت TWWC یک می شود. برای صفر کردن این بیت باید مقدار یک را درون آن بنویسیم.
TWDR : مخفف TWI Data Register می باشد. در این رجیستر آخرین داده دریافت شده قرار می گیرد. همچنین در حالت ارسال برای ارسال داده باید داده را در داخل آن قرار دهیم. تنها زمانی که مقدار TWINT یک است میتوان به این رجیستر دسترسی داشت.
TWSR : مخفف TWI Status Register می باشد. پنج بیت از این ثبات جهت نمایش وضعیت گذرگاه و وضعیت داخلی واحد TWI اختصاص دارد و دو بیت اول بیت های پیش تقسیم کننده پالس ساعت هستند. برای آنکه بتوانیم مقدار بیت های وضعیت را بدست آوریم باید دو بیت کم ارزش را صفر در نظر بگیریم.
راه اندازی واحد TWI در میکروکنترلر Master
برای استفاده راحت تر از قابلیت های واحد TWI در میکروکنترلر Master یک کتابخانه با نام twi_master.h ایجاد می کنیم. درون این کتابخانه یک سری ثوابت به صورت define و نیز یک سری توابع برای کار با واحد TWI تعریف شده است. با اضافه کردن این هدر فایل به صورت دستی به برنامه میکروکنترلر Master توابع زیر در برنامه قابل استفاده هستند. با استفاده از این توابع میتوان توابع دیگری بسته به برنامه مورد نظر ایجاد کرد.
- تابع twi_start
با اجرای این تابع یک وضعیت Start ایجاد می شود. الگوی این تابع به صورت زیر است :
1 |
void twi_start(void) |
- تابع twi_write
این تابع ورودی خود را به Slave ارسال می کند. الگوی این تابع به صورت زیر است :
1 |
void twi_write(unsigned char) |
- تابع twi _read
این تابع ابتدا وضعیت ACK را برای دریافت داده بوجود آورده و سپس دیتا را از Slave دریافت می کند و به خروجی تابع می فرستد. الگوی این تابع به صورت زیر است :
1 |
unsigned char twi _read(void) |
- تابع twi_stop
با اجرای این تابع یک وضعیت stop بر روی گذرگاه I2C ایجاد می شود. الگوی این تابع به صورت زیر است :
1 |
void twi_stop(void) |
تابع مد Master در حالت گیرنده ( MR )
این تابع آدرس دستگاه Slave را به عنوان ورودی می پذیرد و منتظر دریافت داده از Slave مورد نظر می ماند. سپس دیتای مورد نظر را به خروجی تابع بر می گرداند.
1 2 3 4 5 6 7 8 9 |
unsigned char MR_TWI(unsigned char address_device) { unsigned char data; twi_start(); twi_write((address_device<<1)|0x01); data = twi_read(); twi_stop(); return data; } |
تابع مد Master در حالت فرستنده ( MT )
این تابع که یک آدرس و یک دیتا در ورودی خود دریافت می کند ، ابتدا آدرس دستگاه Slave و سپس دیتای مورد نظر را به گذرگاه I2C ارسال می کند.
1 2 3 4 5 6 7 |
void MT_TWI(unsigned char data,unsigned char address_device) { twi_start(); twi_write((address_device<<1)&0xFE); twi_write(data); twi_stop(); } |
راه اندازی واحد TWI در میکروکنترلر Slave
برای هر تعداد میکرو کنترلر اسلیوی که در پروژه وجود دارد باید آدرس منحصر به فردی را در ابتدای برنامه به صورت دلخواه برای آن وارد نمود. برای این منظور #define Slave_address در برنامه وجود دارد. همچنین لازم است وقفه TWI آن در هنگام تنظیمات کدویزارد فعال شود. بعد از فعال شدن وقفه در زیر برنامه وقفه کد زیر نوشته می شود. در زیر برنامه وقفه متغیر TWIRecievedData مقدار دهی می شود که از آن در جاهای دیگر برنامه استفاده می شود.
تعریف وضعیت ها در میکروکنترلر Slave
کلیه وضعیت های موجود در پروتکل TWI که در رجیستر TWSR وجود دارد برای راحتی به عنوان ثابت در ابتدای برنامه تعریف می شود. چون همیشه به این ثوابت در برنامه احتیاج داریم ، آن ها را درون هدر فایل twi_slave.h می ریزیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#define Slave_address 0x45 #define START 0x08 // Slave Transmitter #define ST_SLA_ACK 0xA8 #define ST_ARB_LOST_SLA_ACK 0xB0 #define ST_DATA_ACK 0xB8 #define ST_DATA_NACK 0xC0 #define ST_LAST_DATA 0xC8 // Slave Receiver #define REP_START 0x10 #define SLA_R 0xC9 #define SR_SLA_ACK 0x60 #define SR_ARB_LOST_SLA_ACK 0x68 #define SR_GCALL_ACK 0x70 #define SR_ARB_LOST_GCALL_ACK 0x78 #define SR_DATA_ACK 0x80 #define SR_DATA_NACK 0x88 #define SR_GCALL_DATA_ACK 0x90 #define SR_GCALL_DATA_NACK 0x98 #define SR_STOP 0xA0 |
زیر برنامه وقفه TWI در میکروکنترلر Slave
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
interrupt [TWI] void twi_isr(void) { unsigned char status; status = TWSR & 0xF8; switch(status) { case SR_GCALL_DATA_ACK: TWIReceiveData = TWDR; break; case SR_GCALL_DATA_NACK: TWCR=(1<<TWINT);break; case SR_DATA_ACK: TWIReceiveData = TWDR; break; case ST_SLA_ACK: TWDR=44; TWCR|=(1<<TWINT); break; } TWCR|=(1<<TWINT); } |
مثال عملی شماره 10
برنامه ای برای ارتباط دو میکروکنترلر Master و Slave به صورت شبکه ، از طریق واسط TWI بنویسید ، به طوری که یک عدد برای یکدیگر ارسال کرده و عدد ارسالی روی LCD طرف دیگر نمایش داده شود.
حل :
مرحله اول : رسم سخت افزار در پروتئوس
مرحله دوم : تنظیمات کدویزارد
برای میکروکنترلر Master
برای میکروکنترلر Slave
یک آدرس دلخواه مثلا 45 قرار می دهیم.
مرحله سوم : تکمیل برنامه
برای میکروکنترلر Master :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#include <mega32.h> #include <stdio.h> #include <delay.h> #include <alcd.h> #include "twi_master.h" unsigned char MR_TWI(unsigned char address_device) { unsigned char data; twi_start(); twi_write((address_device<<1)|0x01); data = twi_read(); twi_stop(); return data; } void MT_TWI(unsigned char data,unsigned char address_device) { twi_start(); twi_write((address_device<<1)&0xFE); twi_write(data); twi_stop(); } char buffer[50]; unsigned char Receivedata,Senddata,Slaveaddress; void main() { // TWI initialization // Bit Rate: 6.250 kHz TWBR=0x48; // Two Wire Bus Slave Address: 0x0 // General Call Recognition: Off TWAR=0x00; // Generate Acknowledge Pulse: Off // TWI Interrupt: Off TWCR=0x04; TWSR=0x00; // Alphanumeric LCD initialization // Connections specified in the // Project|Configure|C Compiler|Libraries|Alphanumeric LCD menu: // RS - PORTA Bit 0 // RD - PORTA Bit 1 // EN - PORTA Bit 2 // D4 - PORTA Bit 4 // D5 - PORTA Bit 5 // D6 - PORTA Bit 6 // D7 - PORTA Bit 7 // Characters/line: 16 lcd_init(16); Senddata=55; MT_TWI(Senddata,GlobalAddress); delay_ms(200); Slaveaddress=0x45; Senddata=10; MT_TWI(Senddata,Slaveaddress); delay_ms(200); Slaveaddress=0x45; Receivedata=MR_TWI(Slaveaddress); lcd_gotoxy(0,0); sprintf(buffer,"data=%d",Receivedata); lcd_puts(buffer); while (1) { // Place your code here } } |
برای میکروکنترلر Slave :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
#include <mega32.h> #include <stdio.h> #include <delay.h> #include <alcd.h> #include "twi_slave.h" #define Slave_address 0x45 char TWIReceiveData; char buffer[50]; // 2 Wire bus interrupt service routine interrupt [TWI] void twi_isr(void) { unsigned char status; #asm("cli") status = TWSR & 0xF8; switch(status) { case SR_GCALL_DATA_ACK: TWIReceiveData = TWDR; sprintf(buffer,"Globaldata=%d",TWIReceiveData); lcd_puts(buffer); break; case SR_GCALL_DATA_NACK: TWCR=(1<<TWINT); break; case SR_DATA_ACK: TWIReceiveData = TWDR; sprintf(buffer,"ReceiveData=%d",TWIReceiveData); lcd_gotoxy(0,1); lcd_puts(buffer); break; case ST_SLA_ACK: TWDR=44; TWCR|=(1<<TWINT); break; } TWCR|=(1<<TWINT); #asm("sei") } void main() { // TWI initialization // Bit Rate: 6.250 kHz TWBR=0x48; // Two Wire Bus Slave Address: 0x45 // General Call Recognition: On TWAR=0x8B; // Generate Acknowledge Pulse: On // TWI Interrupt: On TWCR=0x45; TWSR=0x00; // Alphanumeric LCD initialization // Connections specified in the // Project|Configure|C Compiler|Libraries|Alphanumeric LCD menu: // RS - PORTA Bit 0 // RD - PORTA Bit 1 // EN - PORTA Bit 2 // D4 - PORTA Bit 4 // D5 - PORTA Bit 5 // D6 - PORTA Bit 6 // D7 - PORTA Bit 7 // Characters/line: 16 lcd_init(16); // Global enable interrupts #asm("sei") while (1) { // Place your code here } } |
توضیح برنامه : در برنامه Master طبق پروتکل I2C ، ابتدا عدد 55 به تمامی Slave ها ( فراخوان عمومی ) ارسال می شود. سپس به میکروکنترلر Slave که آدرس آن 0x45 است ، دیتای 10 ارسال می شود و در نهایت میکروکنترلر Master دیتایی را از Slave دریافت کرده و روی LCD نمایش می دهد. میکروکنترلر Slave نیز طبق پروتکل I2C عمل می کند. تمام برنامه Slave درون تابع وقفه اتفاق می افتد. به طوری که در هر وقفه ، وضعیت کنونی میکرو شناسایی شده و طبق آن وضعیت به ارسال یا دریافت داده می پردازد. در صورت دریافت دیتا از جانب Master آن را روی LCD نمایش می دهد. در صورت درخواست Master برای ارسال دیتا عدد 44 برای آن ارسال می گردد.
مرحله چهارم : شبیه سازی
دانلود سورس مثال عملی شماره 10
- تبریک میگم شما به پایان دوره مقدماتی میکروکنترلر های AVR رسیدید. برایتان بهترین ها را آرزومندم
در صورتی که این مطلب مورد پسندتان بود لایک و اشتراک گذاری فراموش نشود.
دیدگاه (5)
با سلام و تشکر از جزوه آموزشی بسیار خوبتان. من درس I2C را تقریبا بطور کامل فهمیده ام اما برای اجرا نیاز به هدر فایل twi_master.h دارم. چگونه میتوانم این فایل و توابع درون آن را بسازم یا دریافت کنم. با تشکر
سلام دو هدر فایل twi masterو slave رو چجوری میشه بهشون دسترسی پیدا کرد
سلام وقتتون بخیر من یه سوال داشتم یعنی میکروکنترلرatmega328نمیتونه ارتباطi2cسخت افزاری داشته باشه؟
سلام ممنونم چرا میتونه
در کل این میکروکنترلر و تمام میکروکنترلرهایی که پایه SCL و SDA داشته باشند یعنی دارای واحد I2C سخت افزاری هستند
سلام. واقعا خیلی خوب بود. مخصوصا توابع خواندن و نوشتن i2c. سپاس.