رابط SPI یا ارتباط جانبی سریال (Serial Peripheral Interface)، یک رابط دو طرفه بوده و برای اولین بار توسط شرکت موتورولا استفاده و نامگذاری شد. این نوع ارتباط بر پایهی سیستم Master/Slave (ارباب و برده) است و بر خلاف دو رابط سریال USART و I2C، سرعت بسیار بالایی دارد. از طرفی به دلیل بالا بودن سرعت، باید در فواصل کوتاه استفاده شود. در میکروکنترلرهای AVR از رابط SPI برای برقراری ارتباط با میکرو SD یا ماژولهایی که مجهز به این رابط هستند، استفاده میشود. NRF24L01 از ماژولهاییست که به عنوان فرستنده و گیرنده در بردهای الکترونیکی استفاده میشود و از رابط SPI استفاده میکند.
عملکرد رابط SPI
تبادل داده در رابط SPI به صورت Master/Slave است؛ طوری که تنها یک Master وجود دارد و تعداد Slave ها یک یا بیشتر است. همچنین ارتباط بین Master و Slave همواره دو طرفه بوده و به صورت همزمان اطلاعات جابجا میشود. پایههای رابط SPI در شکل زیر با مستطیل قرمز رنگ مشخص شدهاند.
- Master Input Slave Output) MISO) : این پایه در Master به عنوان دریافت کننده اطلاعات و در Slave به عنوان فرستنده اطلاعات عمل میکند. این پایه در Master و Slave باید به یکدیگر وصل شوند.
- Master Output Slave Input) MOSI) : این پایه در Master به عنوان فرستنده و در Slave به عنوان گیرنده عمل خواهد کرد. این پایه هم باید در Master و Slave به یکدیگر متصل شوند.
- Serial Clock) SCK) : پایه کلاک است که سیگنال آن به صورت سخت افزاری توسط Master تولید شده و باید به Slave متصل شود.
- Slave Select) SS) : این پین در Slave به عنوان ورودی بوده و تنها زمانی تبادل داده با Master انجام میشود که این پایه 0 شده باشد. به عبارتی در حالت پیش فرض، این پایه باید در وضعیت 1 منطقی قرار گیرد. 0 کردن این پایه توسط پایهای دلخواه از Master انجام میشود.
شکل موج و زمانبندی رابط SPI به صورت زیر است.
در شکل بالا، دو بیت CPOL و CPHA که در رجیستر SPCR قرار دارند، تنظیمات مربوط به لبههای شکل موج را انجام میدهند. در مورد این دو بیت در بحث رجیسترها صحبت خواهیم کرد.
گفتیم که تبادل داده به طور همزمان بین Master و Slave انجام میشود. تصویر زیر این امر را نشان میدهد.
در هر Master یا Slave بافری وجود دارد که اطلاعات آن فرستاده و به صورت همزمان اطلاعاتی درون آن ریخته میشود. در میکروکنترلرهای AVR این بافر SPDR نام دارد که یکی از رجیسترهای رابط SPI است. در GIF فوق، مشخص است که اطلاعات از Master به Slave توسط پایه MOSI فرستاده میشود و از Slave به Master توسط پایه MISO، تبادل خواهد شد.
رجیسترهای رابط SPI
در میکروکنترلر AVR برای راهاندازی SPI سه رجیستر SPSR ،SPDR و SPCR منظور شده است که به ترتیب به عنوان دیتا، وضعیت و کنترل ایفای نقش میکنند.
رجیستر SPI Data Register) SPDR)
رجیستر ارسال و دریافت بوده و هم در Master و هم در Slave وجود دارد. در صورتی که میکروکنترلر در حالت Master باشد، نوشتن اطلاعات بر روی این رجیستر، باعث ارسال بلافاصله آن خواهد شد. اما در حالت Slave با نوشتن روی این رجیستر، تبادل زمانی انجام میشود که Master آن را آغاز کند.
رجیستر SPI Status Register) SPSR)
این رجیستر جهت نشان دادن وضعیت رابط SPI بوده و دارای 3 بیت است.
بیت SPI Interrupt Flag) SPIF)
پس از کامل شدن انتقال اطلاعات، این بیت 1 میشود و اگر بیت SPIE در رجیستر SPCR فعال شده باشد، باعث وقوع وقفه خواهد شد. به عبارتی دیگر، 1 شدن این بیت نشان دهنده اتمام انتقال بایت جاری است.
بیت Write Collision) WCOL)
اگر رابط SPI در حال تبادل داده باشد و بایتی روی رجیستر SPDR نوشته شود، این بیت 1 خواهد شد.
بیت SPI2X
اگر این بیت 1 شود، فرکانس کلاک پایه SCK که توسط Master تولید میشود، 2 برابر خواهد شد.
رجیستر SPI Control Register) SPCR)
این رجیستر برای کنترل رابط SPI بوده و شامل بیتهایی برای پیکربندی SPI است.
بیت SPI Interrupt Enable) SPIE)
با 1 کردن این بیت، اجازه وقوع وقفه صادر شده و با هر بار 1 شدن بیت SPIF در رجیستر SPSR وقفه رخ میدهد.
بیت SPI Enable) SPE)
برای استفاده از رابط SPI، باید این بیت را 1 کنیم تا رابط فعال شود. بنابراین هم در Master و هم در Slave باید این بیت 1 باشد.
بیت Data Order) DORD)
اگر این بیت 0 باشد، ابتدا بیت کم ارزش داده منتقل میشود (مانند فایل GIF بالا 👆).
اگر این بیت 1 باشد، ابتدا بیت پرارزش فرستاده خواهد شد.
نکته: مقدار بیت DORD در Master و Slave باید برابر باشد.
بیت Master/Slave Select) MSTR)
اگر این بیت 0 باشد، رابط به صورت Slave عمل میکند.
اگر این بیت 1 باشد، رابط به صورت Master خواهد بود.
بیت Clock Polarity) CPOL)
این بیت وضعیت عادی پایه SCK را مشخص میکند. وضعیت عادی پایه SCK به این صورت است که:
اگر این بیت 0 باشد، در حالت عادی پایه SCK برابر 0 منطقی است.
اگر این بیت 1 باشد، در حالت عادی پایه SCK برابر 1 منطقی میباشد.
نکته: اصطلاحا به لبه اول کلاک Leading Edge و به لبه دوم کلاک Trailing Edge گفته میشود.
اگر CPOL برابر 0 شود، طبق انتظار لبه بالارونده لبهی اول کلاک است و لبه پایینرونده لبهی دوم کلاک میباشد و برعکس.
بیت Clock Phase) CPHA)
هر کلاک دو لبه دارد. در یکی از لبهها باید نمونهبرداری و در لبه دیگر باید مقدار بیت بعدی اطلاعات منتقل شود.
اگر این بیت 0 باشد، نمونهبرداری در لبه اول و انتقال بیت بعدی در لبه دوم کلاک انجام میشود.
اگر این بیت 1 باشد، انتقال بیت در لبه اول و نمونهبرداری در لبه دوم انجام میشود.
نکته: وضعیت دو بیت CPOL و CPHA باید در Master و Slave یکسان باشد.
بیتهای SPR0 و SPR1
این دو بیت نقشی در Slave ندارند و در میکروکنترلر Master با توجه به جدول زیر در انتخاب فرکانس SCK دخیل هستند.
با توجه به مقدار بیتهای SPR1 ،SPR0 و SPI2X یکی از فرکانسهای فوق به عنوان فرکانس SCK انتخاب خواهد شد. منظور از Fosc فرکانس کاری میکروکنترلر است.
مثال 1 : ارسال یک بایت داده از Master به Slave
میخواهیم برنامهای بنویسیم که مقدار پورت D میکروکنترلر Master به میکروکنترلر Slave منتقل شده و بر روی پورت D آن نشان داده شود. فرکانس کاری هر دو میکرو 8 مگاهرتز است.
برنامه Master
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <mega16.h> #include <delay.h> unsigned char ignore; void main(void){ DDRB = 0xB0; DDRD = 0x00; PORTD = 0xFF; SPCR = 0x55; SPSR = 0x00; while(1){ //////////////////////////// Data Sending PORTB.4 = 0; SPDR = ~PIND; while((SPSR&0x80)==0x00); ignore = SPDR; PORTB.4 = 1; //////////////////////////// delay_ms(10); } } |
توضیح برنامه
خط 6: در Master باید پایههای MOSI ،SCK و SS به صورت خروجی و پایه MISO به صورت ورودی تعریف شود. بنابراین مقدار رجیستر DDRB برابر 0xB0 خواهد شد.
خط 7 و 8: پورت D به صورت ورودی تعریف شده و مقاومتهای pullup آن هم فعال شده است.
خط 9: به رجیستر SPCR مقدار 0x55 دادیم. با اینکار بیتهای CPHA ،MSTR ،SPE و SPR0 برابر 1 شده و به ترتیب باعث فعالسازی رابط SPI، قرار گرفتن رابط به صورت Master، مقدار CPHA برابر 1 و در نهایت با مقداردهی دو بیت SPR با مقدار “01” باعث انتخاب فرکانس Fosc/16 برای تولید کلاک پایه SCK خواهد شد.
خط 10: مقدار 0x00 در SPSR ریخته میشود که باعث 0 شدن بیت SPI2X شده و تاثیری بر دو بیت آخر این رجیستر ندارد. چون مقدار ریخته شده 0 است، بودن یا نبودن این خط تاثیری ندارد.
خط 12: حلقه بی پایان while که دستورات داخل آن هر 10 میلی ثانیه انجام میشود.
بلاک Data Sending
در هر انتقال بین Master و Slave قبل از آغاز ارسال اطلاعات باید پایه SS مربوط به Slave از 1 به 0 تغییر کند. به همین منظور اینکار توسط برنامه Master باید انجام شود. در Master پایه PORTB.4 متصل به پایه SS مربوط به Slave است. پس با 0 کردن PORTB.4 آغاز شدن ارسال داده به Slave مورد نظر را اعلام میکنیم.
در خط بعدی اطلاعات پورت D (که در حالت پیشفرض 0xFF است) معکوس شده و در SPDR ریخته میشود. با اینکار، ارسال اطلاعات شروع خواهد شد. سپس با دستور while منتظر میمانیم تا بیت SPIF در رجیستر SPSR به نشانه پایان انتقال برابر 1 شود. در نهایت با توجه به توضیحات اول مطلب، باید داده ارسالی از طرف Slave که هم اکنون در رجیستر SPDR جایگزین شده است، خوانده شود. چون مقدار ارسالی برای ما اهمیتی ندارد، آن را در متغیری به نام ignore ریختیم.
در مرحله آخر باید پایه SS میکرو Slave به حالت 1 منطقی درآید. بنابراین PORTB.4 را 1 خواهیم کرد.
برنامه Slave
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <mega16.h> #include <delay.h> void main(void){ DDRB = 0x40; DDRD = 0xFF; SPCR = 0x44; SPSR = 0x00; while(1){ //////////////////////////// Data Receiving SPDR = 0x00; while((SPSR&0x80)==0x00); PORTD = SPDR; //////////////////////////// delay_us(10); } } |
توضیح برنامه
خط 5: در Slave باید پایه MISO به صورت خروجی و پایههای MOSI ،SCK و SS به صورت ورودی مقداردهی شوند. پس مقدار رجیستر DDRB برابر 0x40 خواهد شد.
خط 6: اطلاعات دریافتی قرار است روی پورت D ریخته شود. پس پورت D را خروجی میکنیم.
خط 7: مقدار رجیستر SPCR را برابر 0x44 میکنیم. با اینکار رابط SPI فعال شده (SPE = 1)، چون بیت MSTR مقدار 0 میگیرد، SPI به صورت Slave عمل میکند و مقدار CPHA را هم 1 میکنیم تا مشابه بیت CPHA در Master شود.
خط 8: رجیستر SPSR را با 0x00 مقداردهی میکنیم. بودن یا نبودن این خط تاثیری در عملکرد ندارد.
بلاک Data Receiving
چون شروع کننده تبادل داده Master است، رجیستر SPDR را مقدار دلخواهی میدهیم (این همان مقداری است که در متغیر ignore در Master ریخته میشود). سپس با دستور while منتظر میمانیم تا تبادل داده شروع شده و پایان یابد. در نهایت هم مقدار جایگزین شده در SPDR که مقدار PIND~ مربوط به میکرو Master است را خوانده و در PORTD میریزیم.
خط 16: پس از یک تاخیر 10 میکروثانیهای، به اول حلقه برگشته و دوباره تمام مراحل بلاک Data Receiving انجام میشود.
مثال 2 : راه اندازی SPI با وقفه
برنامه مثال قبل را طوری مینویسیم که ارسال داده در Master و دریافت داده در Slave توسط وقفه انجام شود.
برنامه 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 |
#include <mega16.h> #include <delay.h> unsigned char ignore; void main(void){ DDRB = 0xB0; DDRD = 0x00; PORTD = 0xFF; SPCR = 0xD5; SPSR = 0x00; #asm ("sei") ///////////////////// Frist Sending PORTB.4 = 0; SPDR = ~PIND; ///////////////////// while(1); } interrupt[11]void spi_complete(void){ ignore = SPDR; PORTB.4 = 1; delay_ms(10); PORTB.4 = 0; SPDR = ~PIND; } |
برنامه فوق 4 تفاوت با برنامه Master مثال 1 دارد:
خط 9: مقدار رجیستر SPCR به 0xD5 تغییر پیدا کرد که باعث فعال شدن وقفه رابط SPI میشود (SPIE = 1).
خط 11: در این خط بیت وقفه عمومی 1 شده که اجازه وقوع وقفهها در میکروکنترلر صادر میشود.
بلاک Frist Sending
در این بلاک PORTB.4 را 0 کرده تا پایه SS میکروکنترلر Slave به نشانه شروع ارسال 0 شود. سپس با نوشتن مقدار PIND~ روی رجیستر SPDR، ارسال اطلاعات آغاز میشود. در نهایت CPU وارد حلقه بدون دستور while خواهد شد.
بلاک interrupt
این بلاک، سرویس روتین وقفه مربوط به SPI بوده و وقفه زمانی رخ میدهد که ارسال اطلاعات به پایان برسد. پس از وقوع وقفه با اجرا شدن دستورات این بخش، به ترتیب پایه PORTB.4 به نشانه پایان ارسال 1 شده، سپس اطلاعات جایگزین شده در SPDR (که در این مثال اهمیت ندارد) در ignore ذخیره میشود و در نهایت پس از 10 میلیثانیه تاخیر، بلاک Frist Sending تکرار میشود و CPU از سرویس روتین خارج میشود.
پس از اینکه ارسال اطلاعات به پایان برسد، دوباره سرویس روتین اجرا خواهد شد و تمام مراحل فوق تکرار میشود.
برنامه Slave
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#include <mega16.h> #include <delay.h> void main(void){ DDRB = 0x40; DDRD = 0xFF; SPCR = 0xC4; SPSR = 0x00; #asm ("sei"); while(1); } interrupt[11]void spi_complete(void){ PORTD = SPDR; } |
برنامه بالا 3 تفاوت با برنامه Slave مثال 1 دارد:
خط 7: مقدار SPCR به 0xC4 تغییر کرد که باعث فعال شدن وقفه میشود (SPIE = 1).
خط 9: بیت وقفه عمومی فعال میشود.
بلاک interrupt
هرگاه ارسال اطلاعات (که از طرف Master شروع میشود) به پایان برسد، وقفه رخ داده و این سرویس روتین اجرا میشود. تنها کافی است که مقدار SPDR خوانده شده و در PORTD قرار گیرد.
برای آموزشهای بیشتر با رزدینو همراه باشید موضوع مبحث بعدی ADC میباشد.
16 پاسخ
سلام
بسیار عالی توضیح دادین . کامل
ممنون از شما
انجام وظیفه کردیم 🙂
عاللییی .مرسی از توضیحات خوب
خیلی خوب مرسی از توضیحات عالی.
باسلام.مثال اول فرکانس 8 مگاهرتز را در صورت سوال داده است اما ریجستر ها بر فرکانس 16 مگاهرتز تنظیم شده اند اگه نیاز به اصلاح است اقدام فرمایید ممنون.
سلام. ممنون از شما. خیلی سریع رسیدگی میشه
مهندس جان سلام انشاالله که خوب هستید ببخشید مزاحم شدم میخواستم بدونم اگر بخواهم برنامه مثال1 را با ماژول nrf24l01 راه اندازی کنم کدها به چه صورت میشه ممنون از لطف شما
سلام. مچکرم از شما. از طریق پشتیبانی به شماره 09909355855 به واتس آپ پیام بدید.
سلام .واقعا خیلی واضح و عالی توضیح دادین این مطلب رو. من چند تا مطلب آموزشی دیگه در مورد اس پی آی دیده بودم ولی درست متوجه مطلب نمی شدم .اما با خوندن توضیحات شما واقعا یاد گرفتم .ممنون از شما
خواهش میکنم. سعی ما بر اینه که مطالب کامل باشن
مهندس یه سوال دارم.اگر در اسلیو SPDR را مقداردهی نکنیم و در سمت مستر SPDR را نخوانیم چه مشکلی پیش میاد.وقتی نیازی به ارسال دیتا از اسلیو به مستر نباشه مثل همین مثال شما چه اجباری به این کار هست؟خواهشا اینو کامل توضیح بدید چون تنها ابهامی که در مورد SPI برام پیش اومده همینه.
سلام وقت بخیر. اطلاعات در هر صورت بین مستر و اسلیو جابجا میشه. منتهی اگه فقط و فقط اطلاعاتی که مستر به اسلیو میفرسته مهم باشه، نیازی به نوشتن در SPDR نیست. نوشتن در SPDR هم مشکلی ایجاد نمیکنه.
ببخشید اگر مستر یک درایور سنسور مثل max31856 باشد برنامه master چطور میشه
سلام. تنها راه اینکه از دیتاشیت آیسی مشخصات پروتکل و نحوه تبادل اطلاعات رو متوجه بشید و در داخل مستر که همان میکروکنترلر هست، پیاده سازی بکنید.
سلام
قسمت توضیح بیت CPHA ، منظور از نمونه برداری چیه؟
سلام. منظور لحظه ای هست که بیت اعمال شده روی خطوط MISO و MOSO وارد رجیستر SPDR میشن.