در قسمت قبل این سری از آموزش، در مورد مفهموم سریال و کاربرد مهم آن در ارتباطات بین میکروکنترلرها، کامپیوترها و از همه مهمتر، تبادل داده با سنسورها و ماژولهای الکترونیکی، صحبت کردیم. در نهایت هم نوع خاصی از ارتباط سریال به نام USART به طور کامل شرح داده شد. در این مطلب در مورد سریال I2C یا TWI صحبت میکنیم. TWI یا Two Wire Interface به معنی رابط 2 سیمه، کاربرد گستردهای در ارتباطات شبکهای دارد و در جایی استفاده میشود که چند میکروکنترلر به همراه چند ماژول با یکدیگر شبکه شدهاند و قرار است با یکدیگر تبادل داده انجام دهند. اما چگونه؟ همراه رزدینو باشید.
سریال I2C یا TWI
در سریال USART برای تبادل دیتا از دو پایه RXD و TXD استفاده میکردیم که به ترتیب برای دریافت و ارسال داده بودند. اما در I2C قضیه متفاوت است و تمامی دستگاهها (میکروها، ماژولها و …) از دو پایه SCL و SDA برای رد و بدل کردن دادهها استفاده میکنند.
نکته: در توضیحات از کلمه دستگاه به جای میکروکنترلر استفاده شده است؛ چون محدوده گستردهتری را شامل میشود.
در شکل فوق، Device همان میکروکنترلر یا هر چیزی است که به I2C مجهز باشد. ارتباط I2C، از دو پایه SCL و SDA تشکیل شده که SCL به عنوان خط کلاک و SDA به عنوان خط دیتا شناخته میشود. به عبارتی دیگر تمام SCL های دستگاهها به یکدیگر و تمام SDA دستگاهها هم به یکدیگر وصل میشوند.
مورد آخر هم مقاومتهای R1 و R2 هستند. چون پایههای SCL و SDA تمام دستگاهها بصورت کلکتور باز است، باید توسط این دو مقاومت pullup شوند. مقدار این مقاومت به طول خط بستگی دارد؛ اما برای خط انتقالی کمتر از 30 سانتی متر، مقدار 10 کیلواهم کافیست.
نکته: در حالت عادی که هیچکدام از دستگاهها اطلاعاتی را مبادله نمیکنند، هر دو خط SCL و SDA در حالت 1 منطقی قرار دارند.
مفاهیم پایه I2C
برای اینکه دادهای از یک دستگاه به دستگاه دیگر منتقل شود، باید یکی از آنها به صورت Master و دیگری به صورت Slave عمل کند.
تعریف Master
به دستگاهی که درخواست تبادل داده را میدهد، Master یا ارباب گویند. Master میتواند درخواست ارسال داده (Master Transmitter) و یا درخواست دریافت داده (Master Receiver) را بدهد.
تعریف Slave
به دستگاهی که منتظر درخواست تبادل داده است، Slave یا برده گویند. Slave یا اطلاعات میفرستد (Slave Transmitter) و یا اطلاعات میگیرد (Slave Receiver).
مد تبادل داده
با توجه به توضیحات بالا، در رابط I2C چهار نوع مد ارتباطی وجود دارد.
- Master Transmitter یا MT
- Master Receiver یا MR
- Slave Transmitter یا ST
- Slave Receiver یا SR
در زمان تبادل داده، دو حالت میتواند رخ دهد:
- یکی از دستگاهها MT و دیگری SR باشد.
- یکی از دستگاهها MR و دیگری ST باشد.
فریم اطلاعاتی در I2C
هر دادهای که بین Master و Slave منتقل میشود، دارای شرط آغاز، آدرس Slave، دادهها، بیت تصدیق و در نهایت شرط پایان است.
شرط آغاز یا Start Condition
شرط آغاز توسط Master ایجاد میشود و شروع کنندهی یک انتقال داده است. به عبارتی دیگر هر تبادلی که بخواهد آغاز شود، نیازمند یک شرط آغاز است.
در حالت عادی هر دو خط در وضعیت 1 هستند. اما برای ایجاد شرایط آغاز، ابتدا SDA صفر شده و سپس SCL صفر میشود.
آدرس Slave
هر دستگاهی در حالت Slave دارای یک آدرس منحصر به فرد 7 بیتی است. بنابراین، 2 به توان 7 یا به عبارتی 128 دستگاه در شبکه میتوانند باشند. پس از هر شرط آغاز، Master باید آدرس Slave که میخواهد با آن تبادل دیتا کند را بفرستد.
آدرس از 8 بیت تشکیل شده است که 7 بیت با ارزش آن، آدرس Salve و بیت کم ارزش آن یعنی R/W مشخص کننده خواندن یا نوشتن بر روی Slave است.
- اگر R/W برابر 0 باشد، بیانگر نوشتن اطلاعات است و Master در حالت ارسال داده (MT) و Slave در حالت دریافت داده (SR) قرار میگیرد.
- اگر R/W برابر 1 شود، نشانه خواندن اطلاعات است و Master در حالت دریافت داده (MR) و Slave در حالت ارسال داده (ST) قرار میگیرد.
به عنوان مثال فرض کنید در یک شبکه، Master بخواهد اطلاعاتی را از Slave به آدرس 63 دریافت کند. چون قرار است اطلاعات خوانده شود، بیت R/W باید 1 شود. از طرفی 63 در مبنای 16 برابر 0x3F شده که اگر بیت R/W را به اول آن اضافه کنیم، حاصل نهایی که باید ارسال شود، 0x7F خواهد شد.
نکته: اصطلاحا به آدرسی که بیت R/W آن 0 باشد، SLA+W میگویند و اگر بیت R/W آن 1 باشد، به آن SLA+R گفته خواهد شد.
بیت تصدیق یا ACK
بیت تصدیق یا Acknowledge Bit آخر هر بایت آدرس یا بایت اطلاعاتی میآید و صادر کننده آن گیرنده بوده و مشخص میکند که گیرنده آماده تبادل دادهی بعدی است. منظور از گیرنده، Slave Receiver یا Master Receiver است. مقدار بیت ACK (تصدیق)، 0 منطقی و مقدار بیت NACK (عدم تصدیق) برابر 1 منطقی است.
بایتهای داده
پس از آدرس Slave، به تعداد n بایت داده میتوان تبادل کرد که آخر هر بایت یک بیت ACK (تصدیق) میآید. اما برای بایت آخر، بیت عدم تصدیق (NACK) ارسال میشود که این نشان دهنده پایان تبادل از طرف گیرنده است.
شرط پایان یا Stop Condition
شرط پایان پس از آخرین تبادل بایت داده، توسط Master ایجاد میشود و بیانگر خاتمه انتقال است.
در شرط پایان، اول SCL یک شده و سپس SDA یک میشود.
شکل یک فریم ارسالی
با توجه به توضیحات بالا، شکل نهایی یک انتقال با 2 بایت داده بصورت زیر است.
- S: شرط آغاز است.
- Salve Address: آدرس Slave مورد نظر برای تبادل داده است و 7 بیت میباشد.
- R/W: بیت خواندن یا نوشتن که چسبیده به Slave Address است و به عنوان یک بایت با هم ارسال میشوند.
- A: منظور ACK است و توسط گیرنده ارسال میشود. مقدارش 0 است.
- Data: داده انتقالی بوده و 1 بایت است.
- Ā: منظور NACK است که توسط گیرنده ارسال میشود و مقدارش 1 است.
- P: شرط پایان است.
رجیسترهای واحد I2C
واحد I2C یا TWI از 5 رجیستر برای کنترل و ارسال داده استفاده میکند که کار کردن با این رجیسترها کمی مشکل است؛ اما نرم افزار CodeVision توابعی برای راهاندازی I2C هم بصورت سخت افزاری و هم بصورت نرم افزاری دارد. در این قسمت، I2C به روش سخت افزاری و با کار کردن توسط رجیسترها پیادهسازی خواهد شد و در جلسه آینده، چگونگی استفاده از توابع را خواهیم گفت.
رجیستر Two Wire Address Register) TWAR)
این رجیستر، آدرس منحصر به فرد در حالت Slave را مشخص میکند. گفتیم که هر Slave در شبکه یک آدرس مخصوص دارد که توسط این رجیستر مشخص میشود.
بیتهای Two Wire Address) TWA)
این 7 بیت یعنی از TWA0 تا TWA6 آدرس Slave هستند و تنها یکبار مقداردهی شده و قانونا نباید تغییر کنند.
بیت Two Wire General Call Enable) TWGCE)
هیچ کدام از Slave ها نباید آدرس 0x00 داشته باشند. در واقع آدرس 0 بیانگر فراخوان عمومی است. فراخوان عمومی زمانی اتفاق میافتد که Master آدرس 0 را ارسال کند. در این حالت هر Slave که بیت TWGCE آن 1 شده باشد، به این فراخوان پاسخ داده و تبادل داده همگانی صورت میگیرد.
نکته: در فراخوان عمومی تمام Slave ها در حالت گیرنده و Master در حالت فرستنده بوده و اطلاعاتی را به تمام Slave ها میفرستند.
رجیستر Two Wire Bit rate Register) TWBR)
نرخ ارسال بیت با این رجیستر مشخص میشود. بهتر است نرخ ارسال بین عدد 100 تا 400 کیلو بیت بر ثاینه تنظیم شود.
مقداری که باید در این رجیستر ریخته شود، از رابطه زیر به دست میآید.
- SCL Frequency نرخ ارسال بیت است.
- CPU Clock Frequency همان فرکانس کاری میکروکنترلر است.
- TWBR که مشخص است و مقدار رجیستر TWBR میباشد.
- TWPS که یکی از اعداد 0، 1، 2 و یا 3 است. این مقدار 2 بیت بوده و در رجیستر TWSR قرار دارد.
مثال: مقدار رجیستر TWBR را برای نرخ ارسال 100 کیلوبیت بر ثاینه به دست آوردید. فرکانس میکرو 8 مگاهرتز است.
جواب: با فرض 0 بودن TWPS، طبق رابطه بالا، مقدار نهایی TWBR برابر 31 خواهد شد.
رجیستر Two Wire Data Register) TWDR)
این رجیستر همان رجیستری است که بایتهای ارسالی یا دریافتی درون آن ریخته میشود.
رجیستر Two Wire Status Register) TWSR)
این رجیستر دارای بیتهایی است که وضعیت لحظهای رابط I2C را نشان میدهند.
بیتهای Two Wire Prescaler Select) TWPS)
این دو بیت که کمی قبل در مورد آن گفته شد، مقداری از 0 تا 3 دارند و در تنظیم نرخ ارسال دخیل هستند.
بیتهای Two Wire Status) TWS)
مقدار این 5 بیت مشخص کننده وضعیت حال حاضر باس I2C است. وضعیتهایی مثل: آیا شرایط آغاز ایجاد شده است؟ آیا باس آزاد است؟ آیا تبادل داده به درستی انجام شده است؟ و … . وضعیتهای این 5 بیت به صورت مفصل داخل دیتاشیت میکروکنترلر آورده شده است که میتوانید به آن رجوع کنید.
در برنامههایی که در ادامه خواهیم نوشت، وضعیت این 5 بیت آنچنان حائز اهمیت نیست.
رجیستر Two Wire Control Register) TWCR)
این رجیستر اصلیترین رجیستر رابط I2C است و شرایط آغاز، پایان و شروع ارسال دادهها توسط این رجیستر انجام میشود.
بیت Two Wire Interrupt Enable) TWIE)
اگر این بیت 1 شود، وقفه I2C فعال خواهد شد که البته برای عمل کردن نیاز است که بیت وقفه عمومی 1 شده باشد.
بیت Two Wire Enable) TWEN)
برای فعال شده I2C و در دست گرفتن کنترل پایههای SDA و SCL باید این بیت را 1 کرد. در غیر این صورت I2C خاموش است.
بیت Two Wire Write Collision) TWWC)
نوشتن اطلاعات بر روی رجیستر TWDR زمانی جایز است که بیت TWINT (بیت آخر) در وضعیت 1 باشد. در غیر این صورت این بیت 1 میشود و باعث بروز خطا خواهد شد.
بیت Two Wire Stop) TWSTO)
اگر روی این بیت 1 بنویسیم، یک شرط پایان ایجاد میشود. پس از اینکه شرط پایان ایجاد شد، این بیت اتوماتیک 0 خواهد شد.
بیت Two Wire Start) TWSTA)
اگر روی این بیت 1 نوشته شود، شرط آغاز ایجاد شده و پس از ایجاد شدن، به طور اتوماتیک 0 میشود.
بیت Two Wire Enable Acknowledge) TWEA)
اگر این بیت 1 باشد، پس از دریافت بایت آدرس یا بایتهای داده، بیت ACK و اگر 0 باشد بیت NACK ارسال میشود.
بیت Two Wire Interrupt Flag) TWINT)
برای شروع شدن شرط آغاز، ارسال آدرس و ارسال داده باید بر روی این بیت 1 نوشته شود. نوشتن 1 بر روی این بیت باعث 0 شدن آن خواهد شد و سپس در مواقع زیر 1 میشود:
- شرط آغاز ایجاد شده باشد.
- بایت آدرس ارسال یا دریافت شده باشد.
- بایت داده ارسال یا دریافت شده باشد.
در صورت 1 شدن این بیت، اگر بیت TWIE برابر 1 باشد، وقفه رخ میدهد.
مثال کاربردی
برنامهای مینویسیم که رشته “Hello” را از Master به Slave انتقال دهد و بر روی LCD متصل به Slave چاپ کند. عمل ارسال 1 ثاینه یکبار و مدت زمان نشان دادن عبارت روی LCD هم 0.5 ثاینه باشد. فرکانس کاری میکرو 8 مگاهرتز است.
برنامه Master
در این برنامه Master به صورت فرستنده عمل میکند (Master Transmitter).
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 |
#include <mega16.h> #include <delay.h> unsigned char Array[5] = {'H','e','l','l','o'}; unsigned char i; void main(void){ TWAR = 0x30 << 1; TWBR = 31; TWCR = 0x04; while(1){ delay_ms(1000); ////////////////////////////////// Start Condition TWCR = 0xA4; while((TWCR&0x80)==0x00); ////////////////////////////////// Send Slave Address : 0x32 ////////////////////////////////// SLA+W => R/W = 0 TWDR = (0x32<<1); TWCR = 0x84; while((TWCR&0x80)==0x00); ////////////////////////////////// Send 5 byte data for(i=0;i<5;i++){ TWDR = Array[i]; TWCR = 0x84; while((TWCR&0x80)==0x00); } ////////////////////////////////// Stop Condition TWCR = 0x94; } } |
توضیح برنامه
خط 1 تا 4 مربوط به هدرها و تعریف آرایه و متغیر i است. آرایه 5 عضوی بوده و رشته Hello در آن قرار دارد.
خط 7 مقدار رجیستر TWAR برابر 0x60 میشود که همان 1>>0x30 است. دلیل اینکه 1 بار به سمت چپ شیفت دادهایم، این است که در برنامه به خاطر داشته باشیم که 7 بیت با ارزش TWAR مربوط به آدرس هستند و بیت کم ارزش آن TWGCE بوده که در این مثال 0 است.
از طرفی چون این برنامه برای Master نوشته شده است، مقدار TWAR چندان مهم نیست؛ چون تنها در حالت Slave کاربرد دارد.
خط 8 مقدار TWBR را برابر 31 کردیم که طبق فرمول، مقدار نرخ ارسال 100 کیلو بیت بر ثانیه میشود.
خط 9 که با 1 کردن بیت TWEN در رجیستر TWCR، رابط I2C را فعال کردیم.
حلقه while
در این حلقه شرط آغاز، ارسال بایت آدرس، ارسال بایتهای داده و شرط پایان به صورت منظم و هر 1 ثانیه انجام میشود.
بلاک Start Condition
برای ایجاد یک انتقال نیاز به شرط آغاز از طرف Master داریم که باید بیت TWSTA یک شود. گفتیم برای اینکه عملیات انجام شود باید بر روی بیت TWINT یک بنویسیم. پس مقدار TWCR برابر 0xA4 خواهد شد.
سپس با دستور (while(TWCR&0x80)==0x00 منتظرم میمانیم تا شرط آغاز ایجاد شود.
بلاک Send Slave Address
پس از شرط آغاز باید آدرس Slave مورد نظر را بفرستیم. همچنین چون میخواهیم بر روی آن عمل نوشتن انجام شود، باید بیت کم ارزش بایت ارسالی 0 باشد. آدرس Slave برابر 0x32 است که به دلایل فوق باید 1 بیت به سمت چپ شیفت داده شده و سپس در TWDR ریخته شود. برای شروع عملیات، روی بیت TWINT در رجیستر TWCR دوباره 1 مینویسیم.
در آخر هم با دستور while منتظر میمانیم تا بایت آدرس ارسال شود.
بلاک Send 5 byte data
با استفاده از حلقه for، به صورت تک تک اطلاعات Array در TWDR ریخته شده و بیت TWINT را 1 میکنیم تا ارسال اطلاعات آغاز شود. سپس با دستور while منتظر پایان ارسال میمانیم. این روند 5 بار انجام شده تا رشته Hello ارسال شود.
بلاک Stop Condition
پس از ارسال دادهها، از طرف Master یک شرط پایان ایجاد میشود که با 1 کردن بیت TWSTO صورت میپذیرد. البته باید بیت TWINT را هم 1 کنیم تا شرط پایان شروع شود.
برنامه Slave
در این برنامه، Slave به صورت گیرنده عمل میکند (Slave Receiver).
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 |
#include <mega16.h> #include <delay.h> #include <alcd.h> unsigned char input[6]; unsigned char i; void main(void){ lcd_init(16); lcd_clear(); //////////////////////////// TWAR = 0x32 << 1; TWBR = 31; while(1){ /* Set TWEA to 1 to Enable Acknowledge Wait for Receive Slave Address */ TWCR = 0x44; while((TWCR&0x80)==0x00); ////////////////////////////////// Receive 5 byte data for(i=0;i<5;i++){ if(i==4) TWCR = 0x84; else TWCR = 0xC4; while((TWCR&0x80)==0x00); input[i] = TWDR; } TWCR = 0x84; ////////////////////////////////// Show Array on LCD lcd_gotoxy(0,0); lcd_puts(input); delay_ms(500); lcd_clear(); } |
توضیح برنامه
خط 1 تا 5 هدرهای برنامه و تعریف آرایه و متغیر i است. نکتهای که وجود دارد، اگر آرایهای را بخواهیم بر روی lcd چاپ کنیم، لازم است که یک عضو بیشتر از بایتهای دریافتی داشته باشد. به عبارتی آخرین عضو آرایه همواره باید 0 باقی بماند تا دستور lcd_puts به درستی کار کند.
خط 8 و 9 مربوط به پیکربندی lcd کاراکتری است و در اولین اقدام یکبار lcd پاک میشود.
خط 11 آدرس Slave را برابر 0x32 میکند. از آنجایی که میکرو قرار است در مد Salve کار کند، این آدرس اهمیت دارد.
خط 12 مقدار TWBR را 31 میکنیم. بودن یا نبودن این خط بیتاثیر است؛ چون Slave نقشی در تولید کلاک ندارد و کلاک از طرف Master ایجاد میشود.
حلقه while
در این حلقه ابتدا منتظر دریافت بایت آدرس شده و سپس 5 بایت دریافت میشود و بر روی lcd چاپ خواهد شد.
بلاک Wait for Receive Slave Address
رجیستر TWCR برابر 0x44 شده و رابط I2C فعال و بیت TWEA یک میشود. با اینکار پس از دریافت آدرس Slave، بیت Ack ارسال میشود. سپس با دستور while منتظر میمانیم تا آدرس Slave (که در خط 11 مشخص شد) دریافت شود.
بلاک Receive 5 byte data
یک حلقه for داریم که 5 مرتبه تکرار میشود؛ چهار مرتبه اول مقدار TWCR برابر 0xC4 و مرتبه آخر (یعنی i==4) برابر 0x84 میشود. گفتیم که برای بایت آخر باید Nack ارسال کنیم که با مقدار 0x84 بیت TWEA برابر 0 شده و پس از دریافت بایت داده، Nack صادر میشود. سپس با دستور while منتظر دریافت بایت داده خواهیم ماند و در نهایت، بایت دریافتی در آرایه ریخته میشود.
بلاک Show Array on LCD
محتوای Array در سط و ستون اول چاپ میشود و پس از 0.5 ثانیه، تمام lcd پاک خواهد شد.
نتیجهگیری
کار کردن با رجیسترهای میکروکنترلر همواره سخت و گیج کننده است. به خصوص اگر بخواهیم برای I2C اینکار را انجام دهیم. اما مزیتی که دارد، درک کردن جز به جز مراحل است. در این مطلب سعی بر این بود مفاهیم پایه بیان شود تا در جلسه آینده، رابط I2C را با استفاده از توابع CodeVision راه اندازی کنیم.
برای آموزشهای بیشتر با رزدینو همراه باشید.
10 پاسخ
واقعا عالی بود….جامع و کامل
ممنون که با نظرات مثبتتان به ما انگیزه میدهید
عالللللی.
سلام،ممنون از مطالبتون
ببخشید من یک مشکل داشتم، اگر بخواهیم رشته ای با طول متغییر را ارسال و دریافت کنیم به چه صورته؟
ممنون میشم راهنمایی کنید
باتشکر
سلام، خسته نباشید
من چندتا ماژول فشار توی پروژه استفاده کردم که از پروتکل i2c پشتیبانی میکنند،میخواستم بدونم ادرس گیرنده هرکدوم رو از کجا باید بفهمم تا برای ارسال استفاده کنم ؟؟
ممنون …
باید به دیتاشیت ماژول مراجعه کنید
سلام وقت بخیر عزیز میخواستم راه اندازی کنم ماژول ttp229البته پرتکول ارتباطی آن هست ۲wireبرای راه اندازی میشه با i2cهم استفاده کرد(یعنی هم i2cهم۲wire)..؟
سوال دوم !؟آدرس این نو ماژول برای ارتباط برقرار کردن چی هست؟
راه اندازی با استفاده از کد ویژن منطورم …
ممنون میشم پاسخ سوال بدهین
سلام وقت بخیر.
بنده با این ماژول کار نکردم. بهترین مرجع راه اندازی هم دیتاشیت خود ماژول هست. آدرس برقراری ارتباط هم داخل دیتاشیت باید باشه.
سلام جناب نصر
عالی _تمیز_روان و ساده توضیح دادید
خیلی ممنون سوالی که دارم این هست
چطور یک میکرو هم میتونه فرمانده باشه هم فرمانبر
کدوم قسمت کدی که گذاشتید تغییر میکنه؟
ممنون از پاسخ شما
سلام ممنون.
کافیه هر جایی که میخواهید اطلاعات رو بفرستید به یک slave از تابع twi_master_trans استفاده کنید. قسمت دوم آموزش i2c در موردش صحبت کردم کامل.