مقدمه
گاهی اوقات در پروژههای الکترونیک لازم میشود از دو یا چند میکروکنترلر به طور همزمان در مدار خود استفاده کنید. قاعدتا این میکروکنترلرها با هم در ارتباط بوده و با یکدیگر انتقال دیتا انجام میدهند. برای انتقال دیتا بین دو یا چند میکروکنترلر میتوانید از پروتکلهای مختلفی مانند SPI ،UART ،CAN ،USB و …. استفاده کنید. یکی از این موارد پروتکل I2C میباشد.
توسط این پروتکل میتوانید یک میکروکنترلر را Master و دیگری را Slave قرار دهید و بین این دو میکروکنترلر انتقال دیتا انجام دهید. شما از این طریق میتوانید توسط هر میکروکنترلر، دیگری را کنترل و مانیتور نمائید. آن میکروکنترلری که Master میباشد، ارتباط را آغاز کرده و پالس کلاک خط SCL را تولید میکند.
دو برد آردوینو نیز نقش دومیکروکنترلر AVR را دارند. بدین ترتیب میتوان دو یا چند آردوینو را از طریق پروتکل I2C به یکدیگر متصل کرد. یکی را به عنوان Master در نظر گرفته و دیگری Slave میشود.
آنچه در این آموزش یاد میگیرید
در این آموزش ابتدا مقدمهای در مورد پروتکل I2C و به ویژه در مورد کتابخانه Wire که مخصوص ارتباط I2C است، بیان میکنیم. با توابع پرکاربرد این کتابخانه آشنا میشویم و یاد میگیریم که چگونه توسط پروتکل I2C بین دو آردوینو انتقال دیتا را انجام دهیم. سپس مداری عملی با پتانسیومتر و LED آماده کرده تا بتوانیم نتیجه این کار را مشاهده کنیم.
توسط پتانسیومتر آردوینوی مستر LED آردوینوی Slave را کنترل کرده و بالعکس. یعنی از طریق ورودی Master، خروجی Slave را کنترل کرده و از طریق ورودی Slave، خروجی Master را کنترل میکنیم. مقادیر انتقال یافته از طریق باس I2C نیز از طریق Serial Monitor دو آردوینو نمایش میدهیم.
در نهایت مداری مانند زیر درست کرده و نتایج آموزش را به صورت عملی مشاهده میکنیم.
پروتکل I2C چیست و چگونه کار می کند؟
Inter-Integrated Circuit یک پروتکل ارتباطی است که آنرا با نامهای دیگری مانند IIC و I2C و… نیز نمایش میدهند. این پروتکل به طور گسترده در میکروکنترلرها کاربرد دارد. در این ارتباط Master ها و Slave ها (Master ها عموما تجهیزات اصلی مانند میکروکنترلر ها و Slave ها سنسورها و دیگر تجهیزات مدار هستند.) از طریق دو سیم با هم در ارتباط هستند:
- SDA(Serial Data): خطی برای ارسال و دریافت دیتا و اطلاعات بین Master و Slave
- SCL(Serial Clock): خطی برای ارسال کلاک
نکته
در آردوینو UNO پینSDA ،A4 و پین A5 نیز SCL میباشد.
برای بردهای دیگر و همچنین میکروکنترلرهای مختلف این پایه ها متفاوت میباشد. به راحتی میتوانید از دیتاشیت، شماره پایه های I2C آنها را پیدا کنید.
از مهمترین ویژگی های پروتکل I2C عبارت است از:
- خیلی سریع نمیباشد (اما برای استفاده از آن به اندازه کفایت سرعت دارد)
- مناسب برای فواصل کوتاه (برای مثال فاصله مجاز در سرعت پایین 100 KHz حدود یک متر است)
- این ارتباط سنکرون میباشد.
- اطلاعات به صورت سریال ارسال میگردد.
- ولتاژ آن 5 یا 3.3 ولت میباشد.
در پروتکل I2C دو نوع تجهیز برای اتصال به باس I2C وجود دارد: Master و Slave. در این ارتباط هم میتوان چند Slave متفاوت را به یک عدد Master مانند یک میکروکنترلر متصل کرد و هم میتوان یک Slave را از طریق چند Master مختلف کنترل و مانیتور نمود اما در هر لحظه فقط یک Master میتواند فعال باشد. هر Master میتواند انتخاب کند که با کدام یک از Slave ها میخواهد ارتباط برقرار کند. این انتخاب از طریق آدرس های 7 بیتی Slave ها (در بعضی موارد 10 بیت) مشخص میشود. پس تقریبا میتوان حدود 128 عدد تجهیز Slave را به باس I2C متصل کرد. بنابراین هر Slave آدرس مخصوص خود را دارد و در ضمن آدرسی برای Master ها اختصاص داده نشده است.
دیتای ارسال شده بر روی باس I2C توسط پایه کلاک ، سنکرون میشود. عمل سنکرونسازی توسط پایه کلاک بر عهده Master میباشد.
نکته
2 پایه SDA و SCL در مدار بایستی Pull-Up شوند. در سرعت های بالا مانند 400kbps مقاومت 2KΩ کافی است و در سرعت های پایینتر مثل 100kbps مقاومت 10KΩ استفاده میشود.
کتابخانه Wire
برای کار با پروتکل I2C از کتابخانهی خود آردوینو به نام Wire استفاده میکنیم. به کارگیری این کتابخانه، استفاده از ارتباط I2C را بسیار آسان میکند. این کتابخانه شامل توابع کاربردی و مفیدی به شرح زیر میباشد:
- ()begin: این تابع کار با کتابخانه را آغاز کرده و توسط آن میتوان مشخص کرد که آردوینو Master باشد و یا Slave
- ()requestFrom: توسط این تابع که در سمت Master به کاربرده میشود، میتوان درخواست انتقال دیتا از سمت Slave را صادر نمود. تعداد بایت و همچنین آدرس Slave در این بخش وارد میگردد.
- ()beginTransmiossion: این تابع که توسط Master به کار برده میشود، ارتباط با یک Slave مخصوص را با وارد کردن آدرس آن آغاز میکند.
- ()endTransmiossion: ارتباطی که توسط تابع قبلی شروع شد، توسط این تابع خاتمه مییابد.
- ()write: هم Master و هم Slave از این تابع برای ارسال اطلاعات به باس I2C استفاده میکنند.
- ()available: هم Master و هم Slave از این تابع برای محاسبه تعداد بایت های دیتای دریافتی (توسط تابع ()read) استفاده میکنند.
- ()read: تابعی برای خواندن یک بایت داده از باس I2C (هم برای Master و هم برای Slave)
- ()setClock: از تابع برای تنظیم فرکانس کلاک بر روی یک میزان مشخص توسط Master استفاده میشود.
- ()onReceive: این مورد به عنوان یک تابع در Slave تعریف شده و زمانی که دیتا از سمت Master دریافت شود، فراخوانی میشود.
- ()onRequest: این مورد به عنوان یک تابع در Slave تعریف شده و زمانی که درخواست دیتا از سمت Master باشد، فراخوانی میشود.
لوازمی که به آن احتیاج دارید
جهت ادامه آموزش و مشاهده نتایج، علاوه بر دو عدد برد آردوینو UNO به قطعات و لوازم زیر نیاز دارید:
سخت افزار های موردنیاز
نرم افزارهای موردنیاز
ارتباط دو آردوینو توسط I2C یکی به عنوان Master و دیگری به عنوان Slave
حال رسیدیم به بخش اصلی آموزش و میخواهیم بین دو آردوینو UNO، از طریق پروتکل I2C ارتباط برقرار کنیم. یکی از آردوینو ها به عنوان Master و دیگری به عنوان Slave انتخاب میشوند. ابتدا مدار عملی آن را آماده کرده و سپس برنامه آن توضیح داده و بعد از آپلود برنامه ها بر روی دو آردوینو، خروجی آن را مشاهده میکنیم.
گام اول: سیمبندی
قبل از هر کاری ابتدا باید مدار آن را آماده کنید. نحوه اتصالات دو آردوینو با دیگر قطعات به صورت زیر است:
همانطور که در شکل بالا مشاهده میکنید، ارتباط دو آردوینو فقط از طریق باس I2C میباشد. آردوینوی سمت چپ، Master و آردوینوی سمت راست، Slave میباشد. هر آردوینو دارای یک ورودی پتانسیومتر و یک خروجی LED میباشند. قرار است LED هر آردوینو را از طریق آردوینوی دیگر کنترل کنیم.
نکته
اگر هر دو آردوینو به یک کامپیوتر متصل باشند، نیازی نیست پایه های GND دو آردوینو را به هم متصل کنید.
اما اگر آردوینوها را به دو کامپیوتر مختلف وصل کرده باشید، بایستی پایههای GND دو آردوینو را به هم متصل کنید.
گام دوم: کد برنامه Master
پس از اینکه مدار آن را آماده کردید، بایستی برنامه های Master و Slave جدا جدا بر روی آردوینو ها آپلود نمایید. ابتدا برنامه Master را بر روی آردوینوی مورد نظر آپلود نمایید:
/*
Made on 8 June 2021
By Amirmohammad Shojaei
https://electropeak.com/learn/
*/
#include <Wire.h>
int value = 0;
void setup() {
Wire.begin(); // join i2c bus (address optional for master)
Serial.begin(9600);
pinMode(5, OUTPUT);
}
void loop() {
int y = analogRead(A1); //Read from A1 & Save to value
value = map(y, 0, 1023, 0, 255);
Wire.beginTransmission(8); // transmit to device #8
Wire.write("Master value : "); // sends these bytes
Wire.write(value); // sends one byte
Wire.endTransmission(); // stop transmitting
Wire.requestFrom(8, 1); // request 1 byte from slave device #8
while (Wire.available()) { // slave may send less than requested
int c = Wire.read(); // receive a byte as character
analogWrite(5, c);
Serial.print("Slave value : ");
Serial.println(c); // print the character
}
delay(100);
}
میخواهیم مقدار ورودی پتانسومتر Master را برای Slave ارسال کنیم و یک بیت دیتای پتانسیومتر را از طرف Slave دریافت کرده و آن را بر روی Serial Monitor نمایش داده و روشنایی LED را با آن کنترل کنیم.
نکات زیر در رابطه با برنامه بالا قابل توجه میباشند:
- ابتدا کتابخانه Wire را پیوست میکنیم.
- توسط تابع ()begin کار با کتابخانه را آغاز میکنیم.
- ابتدا در حلقهی اصلی، مقدار ورودی آنالوگ را بر روی متغیر value ذخیره، و مقدار آن را به بازهی 0 تا 255 تبدیل میکنیم.
- سپس توسط دستور beginTransmission(8) شماره آدرس Slave وارد شده و ارتباط I2C برای ارسال داده آغاز میشود.توسط تابع ()Write، ابتدا چند کاراکتر شامل تعدادی حرف و علامت ارسال کرده و سپس یک بایت داده اصلی ارسال میگردد. در آخر نیز با دستور ()endTransmission این انتقال خاتمه مییابد.
- در گام بعدی Master درخواست دریافت 1 بایت دیتا از سمت Slave را با آدرس 8 میدهد. این عمل توسط دستور requestFrom(8,1) انجام میشود. سپس دیتای دریافتی از سمت Slave را ذخیره (این دیتا متغیر از 0 تا 255 میباشد) و توسط آن نور LED را کنترل کرده و همچنین بر روی Serial Monitor نمایش میدهیم.
نکته
در زمان آپلود دقت کنید که Port موردنظر را به درستی انتخاب کرده باشید.
گام سوم: کد برنامه Slave
اینک برنامه Slave را بر روی آردوینوی مورد نظر آپلود نمایید:
/*
Made on 8 June 2021
By Amirmohammad Shojaei
https://electropeak.com/learn/
*/
#include <Wire.h>
int x;
int value=0;
void setup() {
Wire.begin(8); // join i2c bus with address #8
Wire.onRequest(requestEvent); // register event
Wire.onReceive(receiveEvent); // register event
Serial.begin(9600); // start serial for output
pinMode(9,OUTPUT);
}
void loop() {
delay(100);
analogWrite(9,x);
int t=analogRead(A0); //Read from A0 & Save to value
value=map(t,0,1023,0,255);
}
// function that executes whenever data is received from master
void receiveEvent() {
while (1 < Wire.available()) { // loop through all but the last
char c = Wire.read(); // receive byte as a character
Serial.print(c); // print the character
}
x = Wire.read(); // receive byte as an integer
Serial.println(x); // print the integer
}
// function that executes whenever data is requested by master
void requestEvent() {
Wire.write(value); // respond with message of 1 byte
// as expected by master
}
میخواهیم مقدار ورودی پتانسومتر Slave را برای Master ارسال کنیم و دیتایی را که از طرف Master ارسال شده را دریافت کنیم. و آن را بر روی Serial Monitor نمایش داده و روشنایی LED را با آن کنترل کنیم.
نکات زیر در رابطه با برنامه بالا قابل توجه میباشند:
- ابتدا کتابخانه Wire را پیوست میکنیم.
- توسط تابع begin(8) کار با کتابخانه را آغاز میکنیم و توسط عدد آن، آدرس Slave را مشخص میکنیم.
- توابع onRequest و onRecieve به ترتیب برای درخواست ارسال دیتا و همچنین دریافت اطلاعات استفاده میشوند. در ادامه درون هر تابع را نیز شرح میدهیم.
- تابع ()receiveEvent: توسط این تابع، دیتای ارسالی از طرف Master دریافت میشود. در ابتدا توسط حلقه While همه ی بایت ها به غیر از بایت آخر دریافت میشود. این بایت ها همان کاراکترهای عبارت “:Master value” میباشند. سپس از حلقه While عبور کرده و بایت آخر را که همان دیتای پتانسیومتر Master است را دریافت کرده و در متغیر x ذخیره میکند.
- تابع ()requestEvent: زمانی که Master درخواست دیتا کند، Slave توسط این تابع و با دستور ()Write دیتای موردنظر را به سمت Master ارسال میکند.
- حال در حلقه اصلی، مقدار x که از سمت Master ارسال شد، باعث کنترل نور LED میشود. یعنی با تغییر پتانسومتر Master، متغیر x از 0 تا 255 تغییر کرده و باعث تغییر نور LED میشود.
در ادامه حلقه اصلی نیز، مقدار ورودی پتانسیومتر را خوانده و به مقدار 0 تا 255 تبدیل کرده و آن را در متغیر value ذخیره کرده و آن را آماده ارسال به سمت Master میکنیم.
نکته
اگر از یک کامپیوتر برای اتصال به دو آردوینو استفاده میکنید، بایستی به انتخاب Port هر آردوینو دقت کنید.
برای آپلود برنامه Slave یک صفحه جدید باز کنید تا بتوانید بر روی هر دو Port، برنامه موردنظرشان را بدون مشکل آپلود کنید.
گام چهارم: مشاهده خروجی
پس از اینکه هر دو برنامه را به درستی بر روی دو آردوینو آپلود کردید، سریال مانیتور هر دو Port را باز کرده و مانند فیلم زیر تغییرات LED ها و همچنین سریال مانیتور را به ازای تغییر در پتانسیومترهای ورودی بررسی کنید و از عملکرد صحیح آن مطمئن شوید.
در فیلم بالا مشاهده میکنید که با ازای تغییر در پتانسیومتر Master میتوانید LED سمت Slave را کنترل کنید. همچنین این مقدار بر روی Serial Monitor سمت Slave نیز قابل مشاهده است. دقیقا همین حالت برای سمت Master نیز برقرار است. یعنی با تغییر پتانسومتر ورودی Slave، نور LED در سمت Master و مقدار دیتا بر روی Serial Monitor آن تغییر میکند.
یک گام جلوتر
پس از انجام کامل این آموزش، موضوعات و طرح های زیر را میتوانید به عنوان گام بعدی انجام دهید:
- به کاربردن بردهای دیگر مانند رزبری و یا ESP در ارتباط با یکدیگر
- از سنسورهای دیگر به عنوان Slave نیز بهره بگیرید.
- سعی کنید دیتاهایی با تعداد بایت بالاتر را ارسال و دریافت کنید.
- سراغ پروتکل های دیگر مانند SPI ،CAN و…. رفته و سعی کنید با آنها ارتباط بین دو آردوینو را برقرار کنید.
- استفاده از دو Master و چند Slave در ارتباط با باس I2C
Comments (5)
عالی??
ممنون از لطف شما.?
بسیار عالی
و کاربردی
ممنون از شما
سلام. این کد برای من قسمت slave اش یک مشکل داشت که اینجوری درستش کردم:
void receiveEvent( int y)
اضافه کردن متغییر y. بخاطر اروری که تابع onRequest میداد