در تمام میکروکنترلرها مثل AVR و PIC و یا میکروهای سطح بالا نظیر ARM، همواره برای تبادل اطلاعات میان میکروکنترلرها و یا برقراری ارتباط با بردهای الکترونیکی مثل سنسورهایی که با سریال اطلاعات را رد و بدل میکنند، پورتها یا رابطهایی مثل USART وجود دارد که این امر را میسر میسازد. دقت شود که USART نوعی از یک ارتباط سریال است و وقتی اسمی از ارتباط سریال برده میشود، تنها USART مد نظر نیست. چون به غیر از USART ارتباطات سریال دیگری مثل SPI و I2C هم وجود دارد که این دو مورد در جلسات آینده بیان میشوند. با شرح عملکرد USART همراه رزدینو باشید.
رابط سریال USART
ارتباط سریال به نوعی از ارتباط گفته میشود که بیت به بیت اطلاعات به صورت سری از یک رشته سیم منتقل میشود. چون ارسال تمام بیتها بصورت پشت سرهم و با فاصله زمانی مشخص است، سرعت این نوع تبادل نسب به ارتباط parallel یا موازی کمتر میباشد. همانطور که گفتیم USART نوعی ارتباط سریال است که اول حروف Universal Synchronous Asynchronous Receiver Transmitter بوده و به معنای فرستنده گیرنده سنکرون و آسنکرون جهانی است. در میکروکنترلرهای AVR سه پایه برای سریال USART تعبیه شده که این سه پایه مطابق شکل زیر عبارتاند از: TXD ،RXD و XCK.
TXD: پایه Transmit یا ارسال داده است و باید به پایه RXD دستگاه مقابل متصل شود.
RXD: پایه Receive یا دریافت داده است و باید به پایه TXD دستگاه مقابل متصل شود.
XCK: این پایه مربوط به زمانی است که ارتباط سریال ما سنکرون یا همزمان باشد. در این صورت این پایه به پایه XCK میکروکنترلری که میخواهیم با آن ارتباط برقرار کنیم متصل میشود.
نکته: پایه XCK جز استاندارد RS232 (استاندارد جهانی ارتباط سریال UART) نبوده و تنها در میکروکنترلر AVR وجود دارد.
نکته: منظور از “دستگاه مقابل” یک میکروکنترلر، کامپیوتر، سنسور و یا هرچیزی است که دارای ارتباط USART میباشد.
شرح عملکرد USART
برای تبادل ارتباط میان دستگاهها باید RXD ها به TXD ها متصل شوند. با توجه به شکل زیر پایه TXD مربوط به Device 1 به پایه RXD مربوط به Device 2 متصل است. باید گفت که در شکل زیر فقط میتوان اطلاعات را از Device 1 به Device 2 انتقال داد و برای اینکه ارتباط دو طرفه شود باید TXD دستگاه 2 را به RXD دستگاه 1 متصل کنیم. اما برای شرح عملکرد سریال، فعلا ما ارتباط را بصورت یک طرفه در نظر میگیریم.
اگر Device 1 بخواهد اطلاعاتی را به Device 2 بفرستد، این اطلاعات در قالب frame ارسال میشود که ساختار آن بصورت زیر است.
هر فریم از 4 قسمت اصلی تشکیل شده است:
- بیت (Start (St که همواره 0 منطقی است و در اول شروع فریم میآید.
- بیتهای داده که حامل داده اصلی هستند و بین 5 تا 9 بیت میتوانند باشند. اینکه تعداد بیتهای داده چقدر باشد، توسط ما مشخص میشود.
- بیت (Parity (P که به آن بیت توازن هم گفته میشود. این بیت میتواند وجود داشته باشد یا اینکه اصلا مورد استفاده قرار نگیرد. در ادامه بیت توازن بصورت کامل گفته خواهد شد.
- بیت (Stop (Sp که همیشه 1 منطقی است و حداقل 1 بیت و حداکثر 2 بیت میتواند باشد که این مورد هم توسط ما مشخص میشود. بیت Sp بر خلاف بیت St در آخر هر فریم میآید.
نکته 1: بیت Start، بیت Parity (درصورت فعال سازی) و بیت Stop بصورت اتوماتیک و سخت افزاری به داده چسبانده میشوند و ما تنها باید دادههایی را که میخواهیم بفرستیم مشخص کنیم.
نکته 2: در حالت عادی (IDLE) اگر اطلاعاتی رد و بدل نشود، وضعیت خط انتقال در حالت 1 منطقی قرار دارد. به همین خاطر است که بیت استارت بصورت همیشه 0 ظاهر میشود تا آغاز یک فریم را مشخص کند.
نکته 3: سرعت انتقال داده یکی از فاکتورهای اصلی ارتباط USART است و به آن نرخ ارسال گویند و واحد آن بیت بر ثانیه (bps) میباشد. مثلا نرخ ارسال 9600 یعنی تعداد 9600 بیت میتواند در 1 ثانیه ارسال شود.
بیت توازن
دلیل وجود این بیت، تشخیص خطا در فریم است. بیت توازن دو نوع است:
- توازن زوج (Even Parity) که تعداد 1 موجود در داده و بیت توازن باید زوج شوند.
- توازن فرد (Odd Parity) که تعداد 1 موجود در داده و بیت توازن باید فرد شوند.
مقدار بیت توازن بستگی به دادهای که میخواهیم منتقل کنیم دارد. فرض کنید نوع توازن را زوج انتخاب کردهایم و دادهای که میخواهیم ارسال شود 8 بیتی بوده و بصورت 11001001 باشد. با توجه به این داده، تعداد 1 ها 4 میباشد. پس چون 4 عددی زوج است، بیت توازن 0 منطقی میشود.
مثال 1
سوال: اگر توازن زوج و داده ارسالی 01101110 باشد، مقدار بیت توازن چقدر است؟
جواب: چون تعداد 1 های داده برابر 5 است، بیت توازن 1 میشود تا تعداد 1 های بیتهای توازن و داده برابر 6 شده و زوج شود.
مثال 2
سوال: اگر توازن فرد و داده ارسالی ما 10100001 باشد، مقدار بیت توازن چقدر است؟
جواب: چون تعداد 1 های داده برابر 3 است، بیت توازن باید 0 باشد تا تعداد 1 های بیتهای توازن و داده، همان 3 باقی بماند.
مزیت بودن بیت توازن
مثال 1 را در نظر بگیرید. چون تعداد 1 برابر 6 میشود و زوج است، در طرف گیرنده هم باید همین تعداد 1 شناسایی شود. اگر در طول مسیر یکی از بیتهای ارسالی تغییر وضعیت دهد، تعداد 1 ها فرد شده و گیرنده از این طریق متوجه رخ دادن خطا در داده میشود. خطای مربوط به بیت توازن با PE نشان داده میشود.
تذکر: نوع توازن بیت Parity، در هر دو طرف فرستنده و گیرنده، باید هر دو فرد و یا هر دو زوج باشد.
رجیسترهای USART در AVR
برای دسترسی به ارتباط سریال USART در تمامی میکروکنترلرهای AVR رجیسترهایی تعبیه شدهاند که تمامی موارد فوق را پیادهسازی میکنند. مواردی مثل توازن فرد یا زوج، تعداد بیتهای داده و نرخ ارسال همگی از طریق این رجیسترها تنظیم میشوند. 7 رجیستر برای ارتباط USART وجود دارد که عبارت اند از : UCSRB ،UCSRA ،UBRRH ،UBRRL و UCSRC و 2 رجیستر با اسم یکسان UDR.
رجیسترهای UBRRL و UBRRH برای تنظیم نرخ ارسال داده و رجیسترهای UCSRA تا UCSRC وظیفه کنترل و تنظیم نوع عملکرد رابط سریال را بر عهده دارند. همچنین دو رجیستر که نام هر دوی آنها UDR است، یکی برای ارسال داده و دیگری برای دریافت داده خواهد بود.
رجیسترهای UBRRL و Usart Baud Rate Register) UBRRH)
این دو رجیستر برای تنظیم نرخ ارسال هستند که مقدار کم ارزش آن UBRRL و مقدار با ارزش آن UBRRH است.
مقدار UBRR از 12 بیت تشکیل شده که 8 بیت کم ارزش آن در UBRRL و 4 بیت با ارزش آن در UBRRH قرار دارد. نرخ ارسال با توجه به یکی از سه فرمول زیر محاسبه میشود.
فرمول اول
در اول توضیحات این مطلب گفتیم که USART در دو مد سنکرون و آسنکرون میتواند عمل کند. اگر USART در مد آسنکرون باشد و بیت U2X در رجیستر UCSRA برابر 0 باشد، از فرمول زیر برای تنظیم Baud Rate استفاده میکنیم.
Fosc فرکانس کاری میکروکنترلر، UBRR مقدار رجسترهای UBRR که 12 بیت هستند و BAUD که میزان نرخ ارسال یا همان Baud Rate است. اعداد 1 و 16 هم که ثابت هستند.
سوال: اگر فرکانس کاری میکروکنترلر 8MHz باشد و بخواهیم نرخ ارسال داده 9600 بیت بر ثانیه باشد، مقدار UBRRL و UBRRH را به دست آورید؟
جواب: با طرفین وسطین کردن معادله بالا، مقدار UBRR برابر 51.08 به دست میآید که با گرد کردن آن باید 51 را درون UBRR بریزیم. پس با تبدیل 51 به مقدار معادل باینری آن یعنی “000000110011” باید مقدار UBRRH برابر “0000” و مقدار UBRRL برابر “00110011” شود.
فرمول دوم
اگر USART در مد آسنکرون باشد و بیت U2X در رجیستر UCSRA برابر 1 شود، سرعت انتقال داده 2 برابر خواهد شد. در این حالت باید از فرمول زیر استفاده کرد.
تنها تفاوت این فرمول نصف شدن عدد ثابت 16 و تبدیل به 8 است. چون سرعت 2 برابر شده است.
فرمول سوم
اگر مد کاری بصورت سنکرون انتخاب شود، بیت U2X تاثیری در نرخ ارسال ندارد و باید از رابطه زیر برای تنظیم Baud Rate استفاده کرد.
این فرمول هم مشابه فرمولهای ذکر شده است و تنها عدد ثابت 2 در مخرج قرار میگیرد.
رجیسترهای Usart Data Register) UDR)
وقتی صحبت از ارسال و یا دریافت اطلاعات میشود، باید به این دو رجیستر توجه کرد. این دو رجیستر کاملا همنام هستند و یکی از آنها برای دریافت اطلاعات (Read) و دیگری برای ارسال اطلاعات (Write) است.
نکته: اگر تعداد بیتهای داده را 8 بیت انتخاب کنیم که اکثرا مواقع هم همینگونه است، اطلاعات دریافتی در قالب یک بایت رد و بدل میشوند.
اگر یک بایت دریافت شود (یک بایت از طریق پایه RXD وارد شود)، با خواندن UDR، در اصل مقدار رجیستری که برای دریافت داده است، خوانده میشود. به عبارتی دیگر تمایز بین این دو رجیستر را کامپایلر (CodeVision) انجام میدهد.
1 2 3 |
unsigned char A; A = UDR; |
اگر بخواهیم یک بایت را ارسال کنیم، باید بر روی UDR بنویسیم. بلافاصله پس از مقدار دادن به UDR، ارسال اطلاعات از پایه TXD میکروکنترلر آغاز میشود.
1 2 |
UDR = 0xAA; |
به عنوان مثال، یک بایت با مقدار “10101010” (0xAA) بر روی UDR نوشتیم که ارسال این بایت بصورت سختافزاری انجام میشود.
رجیستر Usart Control Status Register A) UCSRA)
رجیسترهای UCSR جهت کنترل و تعیین وضعیت رابط سریال عمل میکنند. اما رجیستر UCSRA بیشتر وضعیتها و اخطارهای جاری رابط سریال را نشان میدهد.
بیت RXC
اگر یک بایت دریافت شود، این بیت 1 میشود. در غیر این صورت 0 است.
بیت TXC
پس از اتمام ارسال یک بایت این بیت 1 میشود. در غیر این حالت 0 است.
بیت Usart Data Register Empty) UDRE)
پس از اتمام ارسال یک بایت، مدت زمانی طول میکشد تا رجیستر UDR ارسالی خالی شود. پس از خالی شدن UDR، این بیت 1 شده و میتوان بایت بعدی را برای ارسال روی آن نوشت.
نکته: اگر این بیت 1 نشده باشد و بایت بعدی بلافاصله روی UDR نوشته شود، خطای سخت افزاری رخ میدهد.
بیت Frame Error) FE)
این بیت در گیرنده کاربرد دارد و زمانی 1 میشود که در فریم دریافتی خطاهایی مثل یکسان نبودن نرخ انتقال یا عدم تشخیص بیتهای Start و Stop رخ دهد. مثلا اگر نرخ ارسال فرستنده 9600bps و در گیرنده 38400bps تنظیم شده باشد، این بیت مداوم در گیرنده 1 شده و 1 باقی میماند. از این طریق میتوان وقوع خطا را متوجه شد.
بیت Data Over Run) DOR)
اگر در گیرنده یک بایت دریافت شود و برنامهنویس این بایت را نخواند و سپس یک بایت دیگر دریافت شود، در این صورت مقدار بایت قبلی از بین خواهد رفت و این بیت 1 میشود.
بیت Parity Error) PE)
اگر مشکل بیت توازن رخ دهد که بیت 1 میشود (این مشکل در اواسط آموزش مطرح شد).
بیت U2X
اگر این بیت 1 شود، نرخ ارسال داده 2 برابر خواهد شد. در مورد این بیت در قسمت رجیسترهای UBRR توضیح دادیم.
بیت Multi Processor Communication Mode) MPCM)
اگر این بیت را 1 کنیم، ارتباط سریال وارد مد چند پردازندهای میشود که در حوصله این مطلب نمیگنجد و سعی میکنیم در یک جلسه بصورت جداگانه عملکرد این مد را بررسی کنیم.
رجیستر Usart Control Status Register B) UCSRB)
این رجیستر بصورت شکل زیر است و جهت کنترل سخت افزاری رابط سریال به کار میرود.
بیت RXC Interrupt Enable) RXCIE)
اگر این بیت 1 شود، وقفه دریافت USART فعال شده و در صورت گرفتن یک بایت، وقفه رخ میدهد. شماره Vector این وقفه عدد 12 است.
بیت TXC Interrupt Enable) TXCIE)
اگر این بیت را 1 کنیم، در صورت کامل شدن ارسال یک بایت، وقفه رخ میدهد. شماره Vector این وقفه عدد 14 است.
بیت Usart Data Register Empty Interrupt Enable) UDRIE)
در صورت 1 کردن این بیت، اگر پس از ارسال بایت، رجیستر UDR نوشتنی خالی شده و آماده دریافت بایت بعدی شود، وقفه رخ میدهد. شماره Vector این وقفه عدد 13 است.
نکته: برای رخ دادن وقفه به غیر از فعال کردن هر کدام از این سه بیت، باید بیت وقفه عمومی هم 1 شود.
بیت RX Enable) RXEN)
برای حالت گیرندگی باید این بیت را 1 کرد. در غیر این صورت نمیتوانیم داده دریافت کنیم.
بیت TX Enable) TXEN)
برای حالت فرستندگی این بیت باید 1 شود. در غیر این صورت نمیتوان ارسال داده انجام داد.
بیت Usart Character Size 2) UCSZ2)
بیتهای UCSZ از 3 بیت تشکیل شده که بیت سوم آن یعنی UCSZ2 در این رجیستر قرار دارد و دو UCSZ0 و UCSZ1 در رجیستر UCSRC واقع شدهاند. گفتیم که طول داده دریافتی و ارسالی میتواند 5، 6، 7، 8 و یا 9 بیت باشد. این مقدار توسط این 3 بیت مطابق شکل زیر تنظیم میشود.
به عنوان مثال اگر بخواهیم طول دادههای ارسالی یا دریافتی 8 بیت باشد، وضعیت این 3 بیت باید “011” مقداردهی شود.
بیت RX Bit 8) RXB8)
در جدول بالا 👆 اگر طول داده بصورت 9 بیتی تنظیم شود، این بیت نهمین بیت دریافتی است و باید قبل از رجیستر UDR خوانده شود. به عنوان یادآوری باید گفت که بیتهای 0 تا 7 در رجیستر UDR فقط خواندنی قرار دارند.
بیت TX Bit 8) TXB8)
اگر طول داده را 9 بیت تنظیم کنیم، این بیت نهمین بیت ارسالی است و باید قبل از نوشتن بر روی UDR نوشتنی، مقداردهی شود. باز هم یادآور میشویم که بیتهای 0 تا 7 در رجیستر UDR نوشتنی قرار دارند.
رجیستر Usart Control Status Register C) UCSRC)
این رجیستر هم قسمتهای دیگر رابط سریال را کنترل میکند.
بیت Usart Register Select) URSEL)
اگر به رجیستر UBRRH دقت کرده باشید، آخرین بیت این رجیستر دقیقا بیت URSEL است. چون رجیستر UCSRC و UBRRH در حافظه از آدرس مشترکی استفاده میکنند، این بیت تمایز بین این دو رجیستر را انجام میدهد. به عبارتی دیگر اگر بخواهیم بر روی رجیستر UCSRC مقداری بنویسیم باید این بیت را بصورت 1 شده اعمال کنیم و از طرفی اگر بخواهیم مقداری را بر روی UBRRH بنویسیم باید این بیت را 0 شده در نظر بگیریم.
مثال: با نوشتن کد زیر رجیستر UCSRC مقداردهی میشود؛ چون بیت URSEL مقدار 1 داده شده است.
1 2 |
UCSRC = 0x86; |
اما در کد زیر برای مقداردهی رجیستر UBRRH باید بیت URSEL را 0 مقداردهی کنیم.
1 2 |
UBRRH = 0x02; |
بیت Usart Mode Select) UMSEL)
رابط USART در دو مد سنکرون و آسنکرون کار میکند.
- اگر UMSEL برابر 0 شود، مد آسنکرون انتخاب میشود.
- اگر UMSEL برابر 1 شود، مد سنکرون فعال خواهد شد.
در اکثر موارد مد آسنکرون استفاده میشود. اما اگر مد سنکرون انتخاب شود، کافی است در هر دو میکرو، پایههای XCK به یکدیگر وصل شوند. در این صورت پایه XCK یکی از میکروها بصورت خروجی و پایه XCK میکرو دیگر، باید بصورت ورودی تعریف شود.
بیتهای UPM0 و Usart Parity Mode) UPM1)
در مورد بیت Parity صحبت کردیم. بیت توازن یا غیر فعال است و یا فعال بوده و در حالت زوج یا فرد قرار دارد.
- اگر این دو بیت “00” شوند، بیت توازن غیرفعال بوده و در Frame ارسالی یا دریافتی وجود ندارد.
- حالت “01” غیر مجاز است.
- حالت “10” بیت توازن فعال شده و بصورت توازن زوج عمل میکند.
- حالت “11” بیت توازن فعال شده و بصورت توازن فرد عمل میکند.
بیت Usart Stop Bit Select) USBS)
تعداد بیتهای Stop را معلوم میکند.
- اگر 0 باشد، یک بیت Stop در آخر فریم میآید.
- اگر 1 باشد، دو بیت Stop استفاده میشود.
بیتهای UCSZ0 و Usart Character Size) UCSZ1)
این بیتها در توضیحات رجیستر UCSRB شرح داده شدند.
بیت Usart Clock Polarity) UCPOL)
این بیت زمانی کاربرد دارد که رابط USART در مد سنکرون عمل کند. توضیحات بیشتر در مورد این بیت فعلا جایز نیست و در مثالها به این بیت میپردازیم.
مثال 1
برنامهای بنویسید که بصورت مداوم هر 1 ثانیه حرف A را بر روی خروجی چاپ کند. بیت Parity غیرفعال، تعداد بیتهای Stop یک و طول داده 8 بیت است. همچنین نرخ ارسال هم 9600bps میباشد.
تذکر: در تمام مثالها فرکانس کاری میکروکنترلر بصورت 8MHz داخلی تنظیم شده است و مد عملکرد هم آسنکرون میباشد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <mega16.h> #include <delay.h> void main(void){ /////////////////////USART Initialization UBRRH = 0; UBRRL = 51; UCSRA = 0x00; UCSRB = 0x08; UCSRC = 0x86; while(1){ //UDR = 65; UDR = 'A'; delay_ms(1000); } } |
تنظیم UBRRH و UBRRL
اولین کار مشخص کردن نرخ ارسال یا Baud Rate است. چون در مد آسنکرون هستیم و بیت U2X واقع در رجیستر UCSRA صفر است، باید از فرمول اول استفاده کنیم.
با گذاشتن 8000000 بجای Fosc و 9600 بجای BAUD، مقدار UBRR برابر 51 میشود. پس UBRRH برابر 0 و UBRRL برابر 51 خواهد شد.
تنظیم UCSRA
گفتیم که این رجیستر بیشتر وضعیت رابط را نشان میدهد. اما دو بیت مهم آن یعنی MPCM و U2X باید 0 شوند. نوشتن 0 یا 1 بر روی U2X روی Baud Rate تاثیر میگذارد و اگر در این مثال آن را 1 کنیم، مقدار Baud Rate دو برابر خواهد شد.
تنظیم UCSRB
از هیچ وقفهای استفاده نمیکنیم. پس سه بیت TXCIE ،RXCIE و UDRIE باید 0 شوند. تنها میخواهیم ارسال داده انجام دهیم. پس بیت RXEN برابر 0 و بیت TXEN برابر 1 میشود. چون طول داده 8 بیتی است، وضعیت بیتهای UCSZ باید “011” شود. در نتیجه بیت UCSZ2 باید 0 شود. در آخر هم به دلیل 8 بیتی بودن طول داده، نیازی به دو بیت RXB8 و TXB8 نیست. بنابراین مقدار این رجیستر 0x08 خواهد شد.
تنظیم UCSRC
گفتیم که برای نوشتن روی UCSRC باید بیت URSEL را هنگام مقداردهی 1 کنیم. چون عملکرد در مد آسنکرون است، بیت UMSEL باید 0 شود. بیت توازن نداریم پس UPM0 و UPM1 هر دو باید 0 باشند. تعداد بیت Stop هم 1 بیت است. در نتیجه USBS باید 0 باشد. اما چون وضعیت بیتهای UCSZ برابر “011” شد، هر دو بیت UCSZ0 و UCSZ1 باید 1 شوند. در آخر هم UCPOL که در مثالهای بعدی در مورد آن توضیح میدهیم، باید 0 مقداردهی گردد.
با این تفاسیر مقدار رجیستر UCSRC برابر 0x86 میشود.
بدنه while
چون قرار است بصورت مداوم اطلاعات ارسال شود، دستورات را داخل (1)while مینویسیم. برای ارسال یک بایت، کافی است به رجیستر UDR مقدار دهیم. حرف A در داخل برنامه بصورت ‘A’ تفسیر میشود و این مقدار را به UDR میدهیم که بلافاصله پس از مقدارگیری ارسال اطلاعات آغاز شده و CPU به دستور delay میرسد و این چرخه ادامه دارد.
تذکر: به عنوان کامنت عبارت UDR = 65 را نوشتهایم. تمام حروف انگلیسی، اعداد و علامتها در جدولی به نام جدول اسکی ذکر شدهاند که هر حرف یا علامت از یک بایت منحصر به فرد برای نمایش استفاده میکند. این مقدار برای حرف A برابر 65 است.
مثال 2
برنامه مثال 1 را طوری ارتقا دهید که بجای حرف A عبارت Hello را چاپ کند. تمام تنظیمات یکسان است.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
#include <mega16.h> #include <delay.h> unsigned char Array[] = {'H', 'e', 'l', 'l', 'o'}; unsigned char i; void main(void){ /////////////////////USART Initialization UBRRH = 0; UBRRL = 51; UCSRA = 0x00; UCSRB = 0x08; UCSRC = 0x86; while(1){ for(i=0;i<5;i++){ UDR = Array[i]; while((UCSRA&0x20)==0x00); } delay_ms(1000); } } |
تعریف آرایه
چون عبارت Hello از 5 حرف تشکیل شده است، بنابراین یک آرایه با طول 5 بایت تعریف میکنیم که هر حرف یک بایت را اشغال کرده است.
حلقه For
در حلقه For مقدار i با هر بار اجرا یک واحد زیاد میشود. مقدار اولیه متغیر i برابر 0 است و تا عدد 5 ادامه پیدا میکند. اما مقدار رجیستر UDR از عناصر آرایه و به ترتیب از حرف H تا حرف آخر ادامه مییابد.
پس از اینکه به UDR مقدار داده شد، باید منتظر بمانیم تا داده ارسال شده و رجیستر UDR خالی و آماده ارسال بایت بعدی شود. اینکار با دستور (while((UCSRA&0x20)==0x00 انجام میشود. به عبارتی بیت UDRE واقع در رجیستر UCSRA وقتی رجیستر UDR خالی شود 1 خواهد شد. پس اگر مقدار UCSRA را با 0x20 بصورت منظقی & کنیم و حاصل را با 0x00 مقایسه کنیم، در حالت عادی شرط صحیح بوده و CPU در دستور while صبر میکند. اما به محض 1 شدن بیت UDRE، شرط نادرست شده و CPU از while خارج میشود. بنابراین حلقه به انتها رسیده و از اول حلقه، رجیستر UDR مقدار بعدی را گرفته و این روال ادامه خواهد یافت.
مثال 3
برنامهای بنویسید که در طرف فرستنده پورت A را هر 100 میلی ثانیه بخواند و ارسال کند. همینطور در طرف گیرنده، اطلاعات را دریافت کند و بر روی پورت A نشان دهد. تنظیمات سریال مشابه دو مثال قبلی است.
برنامه فرستنده
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <mega16.h> #include <delay.h> void main(void){ DDRA = 0x00; PORTA = 0xFF; /////////////////////USART Initialization UBRRH = 0x00; UBRRL = 51; UCSRA = 0x00; UCSRB = 0x08; UCSRC = 0x86; while(1){ UDR = ~PINA; while((UCSRA&0x20)==0x00); delay_ms(100); } } |
تنها نکتهای که این برنامه دارد این است که پورت A را ورودی کرده و مقاومتهای pullup آن فعال شده است. در حلقه while هم مقدار PINA خوانده شده و معکوس آن درون UDR ریخته میشود. چون در حالت عادی و بدون وصل کردن هیچ کدام از کلیدها، مقدار 0xFF خوانده میشود و باید معکوس آن که 0x00 است ارسال شود. بقیه خطوط مشابه مثال 1 و 2 است.
برنامه گیرنده
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){ DDRA = 0xFF; PORTA = 0x00; /////////////////////USART Initialization UBRRH = 0; UBRRL = 51; UCSRA = 0x00; UCSRB = 0x10; UCSRC = 0x86; while(1){ while((UCSRA&0x80)==0x00); PORTA = UDR; } } |
در این برنامه پورت A بصورت خروجی عمل میکند. اما چون تنها میخواهیم دریافت اطلاعات داشته باشیم، باید بیت RXEN در رجیستر UCSRB را 1 کنیم. پس مقدار UCSRB برابر 0x10 خواهد شد.
حلقه while
در بدنه این حلقه دستور (while((UCSRA&0x80)==0x00 نوشته شده و تا زمانی که بیت RXC در رجیستر UCSRA برابر 0 باشد، شرط آن صحیح بوده و CPU در این خط میماند. پس از دریافت یک بایت، شرط آن نادرست شده و CPU به خط بعدی میرود. در این خط مقدار UDR (خواندنی) خوانده شده و درون PORTA ریخته میشود.
مثال 4
برنامه گیرنده مثال 3 را طوری بازنویسی کنید که به وسیله وقفه اطلاعات را دریافت و بر روی پورت A نشان دهد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#include <mega16.h> #include <delay.h> void main(void){ DDRA = 0xFF; PORTA = 0x00; /////////////////////USART Initialization UBRRH = 0; UBRRL = 51; UCSRA = 0x00; UCSRB = 0x90; UCSRC = 0x86; #asm ("sei") while(1); } interrupt[12]void Receive(void){ PORTA = UDR; } |
در برنامه فوق مقدار رجیستر UCSRB از 0x10 به 0x90 تغییر کرد. چون میخواهیم از وقفه دریافت استفاده کنیم، باید بیت RXCIE را 1 کنیم. از طرفی هم باید بیت وقفه عمومی 1 شود که با دستور asm# این کار انجام شده است.
شماره Vector وقفه دریافت USART برابر 12 بوده که در interrupt مربوطه نوشته شده است. در دستورات وقفه، رجیستر UDR خوانده میشود و در PORTA قرار میگیرد.
کتابخانه stdio
در این کتابخانه توابعی وجود دارد که دریافت و ارسال اطلاعات بر روی رابط USART را انجام میدهند. البته در هر صورت باید تنظیمات رجیسترهای USART را انجام داد.
تابع putchar
این تابع یک کاراکتر را بر روی خروجی ارسال میکند.
1 2 |
putchar('یک کاراکتر'); |
تابع getchar
این تابع منتظر دریافت یک کاراکتر از ورودی میماند و به محض دریافت، آن را در خروجی تابع ارسال میکند.
1 2 3 |
unsigned char A; A = getchar(); |
تابع puts
این تابع یک آرایه را در خروجی ارسال میکند.
1 2 3 |
unsigned char Array[] = {'H', 'e', 'l', 'l', 'o'}; puts(Array); |
در کد بالا آرایه 5 بایتی است که دستور puts بصورت پشت سرهم هرکدام را ارسال میکند.
تابع putsf
این تابع مانند تابع puts است با این تفاوت که بجای آرایه یک رشته را در ورودی خود دریافت کرده و آن را ارسال میکند.
1 2 |
putsf("www.rasdino.ir"); |
نکته: این تابع بصورت اتوماتیک یک بایت با مقدار 0 به انتهای رشته اضافه میکند. پس در طرف گیرنده تعداد بایت دریافتی باید به اندازه 1 واحد بیشتر باشد.
تابع gets
این تابع برای دریافت n بایت از ورودی استفاده میشود. دو ورودی میگیرد که اولی آرایهای است که باید اطلاعات در آن ذخیره شود و دومی تعداد بایت دریافتی است.
1 2 3 |
unsigned char Array[8]; gets(Array, 8); |
در کد بالا یک آرایه 8 بایتی تعریف شده و در خط بعد 8 بایت دریافت میشود و در درون Array ذخیره میگردد.
مثال 5
برنامهای بنویسید که در فرستنده عبارات www.rasdino.ir و Hello ارسال شده و در گیرنده پس از دریافت، بر روی lcd کاراکتری نشان داده شوند.
برنامه فرستنده
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <mega16.h> #include <delay.h> #include <stdio.h> void main(void){ /////////////////////USART Initialization UBRRH = 0x00; UBRRL = 51; UCSRA = 0x00; UCSRB = 0x08; UCSRC = 0x86; while(1){ delay_ms(1000); putsf("www.rasdino.ir"); delay_ms(1000); putsf("Hello"); } } |
در کد بالا درون حلقه while عبارتهای گفته شده با دستور putsf با تاخیر 1 ثانیهای نسبت به هم ارسال میشوند.
برنامه گیرنده
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 |
#include <mega16.h> #include <delay.h> #include <stdio.h> #include <alcd.h> unsigned char Array[17]; void main(void){ lcd_init(16); lcd_clear(); /////////////////////USART Initialization UBRRH = 0; UBRRL = 51; UCSRA = 0x00; UCSRB = 0x10; UCSRC = 0x86; while(1){ ///////////////////www.rasdino.ir gets(Array,15); lcd_clear(); lcd_gotoxy(0,0); lcd_puts(Array); ///////////////////Hello gets(Array,6); lcd_clear(); lcd_gotoxy(0,0); lcd_puts(Array); } } |
دستورات ابتدایی برنامه مشابه برنامههای قبل است. اما در حلقه while با استفاده از تابع gets تعداد 15 بایت دریافت میشود. سپس lcd پاک شده و در خط و ستون اول، عبارت دریافت شده (Array) بر روی lcd چاپ میشود. در ادامه 6 بایت دریافت شده و همانند قبل نمایش داده میشود.
نکتهای که قبلا هم آن را ذکر کردیم این است که چون عبارت www.rasdino.ir از 14 بایت تشکیل شده است و تابع putsf در فرستنده یک بایت به انتهای این عبارت اضافه میکند، باید 15 بایت دریافت کنیم. همینطور برای عبارت Hello هم باید 6 بایت دریافت شود.
برای آموزشهای بیشتر با رزدینو همراه باشید👋
18 پاسخ
چرا از USART استفاده میکنیم؟
مزایای ارتباط سریال چیست؟
نوعی پرتکل ارتباطی است که به سبب سادگی آن زیاد مورد استفاده قرار میگیرد و از مزیت های مهم ان میتوان به پر کاربرد بودن ان اشاره داشت که اکثر ماژول ها و میکروها ان را ساپورت میکنند
آیا هر کدام از میکروکنترلها میتوانند جداگانه برنامه ای را اجرا کنند؟
با سلام. بله دوست عزیز هر میکرو میتواند برنامه ای منحصربه فرد را داشته باشد و در عین حال با یکدیگر نیز ارتباط داشته باشند
عالی بود ممنون
عالی.واقن عالی توضیح دادین .من خودم مشکل داشتم مشکلم حل شد .بی نظیر
خیلی خوب و کامل و مسلط توضیح دادین .عالی بود
سلام .من اگر بخواهم از دو پورت سریال در میکرو2560 استفاده کنم.جهت ارسال یا دریافت داده .در کدویژن بصورت پیش فرض از سریال صفر استفاده میکند ولی من میخواهم از سریالهای دیگر نیز استفاده کنم.باید چه کاری کنم؟
ببخشید من تمام توضیحات شما رو خوندم اما این قسمت while((UCSRA&0x20)==0x00); رو متوجه نشدم که چرا این دو عبارت با هم AND شدن؟؟ آیا نمیشد بجای AND کردن خود رجیستر رو مستقیم مقایسه کنیم؟؟؟
سوال دومم اینه که تفاوت تابع GETS و GETCHAR و UDR چیه؟؟ و چجوری بفهمیم که کجا باید از کدوم استفاده کنیم؟؟
سوال سومم اینه که چرا شما توی مثال ها از تابع GETS بجای UDR استفاده نکردین؟ چون اینجوری مجبور نیستید حروف رو بصوصرت یکی یکی دریافت کنید!!
خیلی ممنون میشم اگه به سوالاتم پاسخ بدین
سلام ممنون از این همه اطلاعات مفید مهندس من با ماژول cp2102 می خوام میکرو atmega16 وصل کنم به کامپیوتر از تمام دستورات ارسال و دریافتی که گفتید استفاده کردم اما اطلاعات نادرست میده بهم میکرو. با کد ویزارد کد ویژن کار می کنم و دیگه نمی دونم باید چیکار کنم میشه کمکم کنی؟
با سلام از طریق پشتیبانی واتس اپ اقدام نمایید
سلام خسته نباشیدو دمتون گرم.
بنده در مورد بیت پاریتی در فریم دیتا مشکل داشتم که به لطف شما که این قدر واضح توضیح دادین،مشکلم بر طرف شد. ممنون
بسیار مفید و عالی و به خصوص روان و ساده توضیح دادین واقعا خسته نباشید میگم خدمتتون 🧡❤💛💚💙
سلام، بسیار عالی و روان.
خیلی ممنون از آموزش شما، اگر یک مثال هم برای استفاده وقفه پورت سریال بیان کنید خیلی عالی میشه. من روی این قسمت مشکل دارم.
سلام
آیا این نوع ارتباط سرعت لازم را دارد ؟ برای سرعت بالا کدام نوع ارتباط بهتر است ؟
ممنون بابت زحمتی که میکشید خدا جبران کند
اون وقفه داخل حلقه بینهایت رو چرا خطا میده کدویژن؟؟
آیا برای فرستنده کدش میشه این؟؟. interrupt[14]void name (void)
سلام. بسیار ساده و کاربردی توضیح دادید. سپاس از زحمات شما