راه اندازی USART در AVR موضوع مورد بحث این نوشته است. در این نوشته به راه اندازی واحد USART در AVR با وقفۀ دریافت و بدون آن می پردازیم. واحد USART میکروکنترلرهای AVR امکانات و ویژگی هایی دارد که برخی از آنها عبارتند از:
- عملکرد تمام دو طرفه؛
- عملکرد سنکرون و آسنکرون؛
- پشتیبانی از فریم دادۀ 5 تا 9 بیتی و تعداد 1 یا 2 بیت Stop قابل انتخاب؛
- تولید Parity زوج یا فرد و بررسی Parityبه صورت سخت افزاری؛
- آشکارساز Data over run؛
- آشکارساز خطای فریم؛
- سه وقفۀ جداگانه برای اتمام ارسال، اتمام دریافت و خالی شدن رجیستر ارسال؛
- مد ارتباطی مولتی پروسسور؛
اما در پروژه هایی که در این نوشته بررسی می کنیم، از تمام این قابلیت ها استفاده نمی کنیم. هدف ما در اینجا تنها راه اندازی ارتباط سریال AVR برای تبادل داده با کامپیوتر است. هر چند نمونه کدهایی که می آوریم، برای ارتباط سریال USART بین دو میکروکنترلر AVR نیز قابل استفاده اند. نمونه کد اول که بررسی می کنیم، در اتمل استودیو است و دریافت در آن به روش Polling است. نمونه کد دوم نیز در اتمل استودیو است و دریافت در آن با وقفۀ دریافت است. نمونه کد سوم نیز در CodeVisionAVR است. این نمونه کد را با CodeWizardAVR ایجاد کرده ایم و دریافت در آن با وقفۀ دریافت است. این نمونه کدها در پیوست قرار گرفته اند. نمونه کد اول را در نرم افزار CodeVisionAVR نیز نوشته ایم و در پیوست قرار دارد. لازم به ذکر است که این نمونه کدها برای میکروکنترلر ATmega64A و برای راه اندازی USART شمارۀ صفر آن هستند.
فیلم آموزش میکروکنترلرهای AVR مقدماتی
تصویر 1 – ارتباط سریال میکروکنترلر AVR با کامپیوتر
ابزارهای لازم برای ارتباط سریال AVR با کامپیوتر
برای راه اندازی USART در AVR و اتصال آن به پورت سریال کامپیوتر، نیاز به مبدل های سطح ولتاژ مثل MAX232 داریم. در صورتی که بخواهیم از پورت USB کامپیوتر استفاده کنیم، می توانیم از مبدل های USB به سریال مثل CP2102 استفاده کنیم. RX و TX این مبدل ها به ترتیب به TX و RX میکروکنترلر متصل می شوند. در اتصال پایه های میکروکنترلر به مبدل باید به ولتاژ سطح High توجه داشت. مثلاً اگر ولتاژ سطح High میکروکنترلر 3.3 ولت و ولتاژ سطح High ماژول 5 ولت باشد، باید به روشی، ولتاژ پایۀ TX ماژول به محدودۀ ولتاژ سطح High میکروکنترلر کاهش یابد. برای این مثال، تقسیم ولتاژ می تواند روش مناسبی باشد. در پروژه های این نوشته از برد توسعۀ ATmega64Pin یوبرد استفاده می کنیم. روی این برد توسعه آی سی CP2102 وجود دارد. برای استفاده از آن، کافی است پایه های 1 و 2 و همچنین 3 و 4 هدر نری روی برد را به هم متصل کنیم. کانکتور میکرو USB سمت چپ نیز مربوط به CP2102 است.
تصویر 2 – آی سی CP2102 روی برد توسعۀ ATmega64Pin یوبرد
در راه اندازی USART در AVR که در این نوشته بررسی می کنیم، می خواهیم با کامپیوتر ارتباط برقرار کنیم. در این صورت باید از یک ترمینال پورت سریال استفاده کنیم. نرم افزار Hercules دارای یک ترمینال پورت سریال است که می توانیم از آن استفاده کنیم. بعد از پروگرام کردن میکروکنترلر AVR، اتصالات بین میکروکنترلر و کامپیوتر را برقرار می کنیم. ترمینال پورت سریال را اجرا و ارتباط سریال را برقرار می کنیم. سپس به تبادل داده می پردازیم. همچنین تنظیمات پورت سریال در Device manager کامپیوتر باید به این صورت باشد:
- مقدار Bit per second برابر 115200 باشد؛
- مقدار Data bit برابر 8 باشد؛
- مقدار Parity برابر None باشد؛
- مقدار Stop bit برابر 1 باشد؛
- و مقدار Flow control برابر None باشد.
تصویر 3 – ترمینال پورت سریال نرم افزار Hercules
نمونه کد راه اندازی USART در AVR، روش Polling
برنامۀ زیر، نمونه کد USART در AVR با روش Polling در نرم افزار اتمل استودیو است. ابتدا تعیین کرده ایم که فرکانس کاری CPU برابر 8 مگاهرتز است. سپس کتابخانۀ io.h را فراخوانی کرده ایم. در تابع main متغیر data را از نوع char تعریف کرده ایم. در سطر بعد با یک کردن بیت های 3 و 4 رجیستر UCSR0B، فرستنده و گیرندۀ واحد USART را فعال کرده ایم. تعداد بیت های دادۀ فریم را با یک کردن بیت های 2 و 1 رجیستر UCSR0C برابر 8 قرار داده ایم. در مد آسنکرون وقتی بیت U2X0 صفر است و می خواهیم Baud rate برابر 9600 باشد، مقدار UBRR0 برابر است با:
UBRR0 = (F_CPU / (16 * BAUD)) - 1 = (8000000 / (16 * 9600)) - 1 = 51
این مقدار را در رجیستر های UBRR0 قرار داده ایم. چون مقدار 8 بیتی است، تنها به UBRR0L مقدار داده ایم. بقیۀ تنظیمات واحد USART در حالت پیش فرض هستند. یعنی مد آسنکرون، تعداد یک بیت Stop و Parity هم غیر فعال.
در ادامه در حلقۀ while، وضعیت بیت RXC0 را بررسی می کنیم. هر گاه بایتی دریافت شود، این بیت 1 می شود و دستورهای درون if اجرا می شوند. درون if، بایت دریافت شده را از رجیستر UDR0 می خوانیم و در متغیر data قرار می دهیم. سپس در یک حلقۀ while بدون دستور، بیت 5 رجیستر UCSR0A را بررسی می کنیم. هر گاه رجیستر داده خالی شود، این بیت 1 می شود و می توانیم در رجیستر داده بنویسیم. هرگاه رجیستر داده خالی شد، برنامه از حلقۀ while خارجی می شود. سپس مقدار دریافتی را که در متغیر data قرار داده بودیم، دوباره در UDR0 قرار می دهیم تا ارسال شود. بنابراین انتظار داریم هر کاراکتری که از کامپیوتر به میکروکنترلر ارسال می کنیم، میکروکنترلر همان را برای کامپیوتر ارسال کند.
#define F_CPU 8000000UL
#include <avr/io.h>
int main(void)
{
char data=0;
UCSR0B = (1<<RXEN0)|(1<<TXEN0); // Bit 4 and 3 of UCSR0B
UCSR0C = (1<<UCSZ01)|(1<<UCSZ00); // Bit 2 and 1 of UCSR0C
UBRR0L = 51;
while (1)
{
if ( (UCSR0A & (1<<RXC0)) == (1<<RXC0))
{
data = UDR0;
while((UCSR0A & (1<<UDRE0)) != (1<<UDRE0)); // Bit 5 of UCSR0A
UDR0 = data;
}
}
}
تصویر 4 – نتیجۀ راه اندازی USART در AVR، روش Polling
نمونه کد راه اندازی USART در AVR با وقفه، اتمل استودیو
برای این که میکروکنترلر بدون بررسی دائم بیت های پرچم USART، متوجه دریافت داده شود، باید وقفۀ دریافت USART را فعال کنیم. در این صورت هرگاه بایتی دریافت شود، وقفۀ دریافت اتفاق می افتد. و نیازی نیست که CPU دائماً درگیر بررسی بیت RXC باشد. برای فعال کردن وقفه USART در AVR برای دریافت، باید بیت 7 رجیستر UCSRnB را یک کنیم. که در نمونه کد زیر برای USART شمارۀ صفر، بیت 7 رجیستر UCSR0B را یک کرده ایم. بقیۀ تنظیمات USART0 همانند نمونه کد قبلی است. پس از پیکربندی USART0، با اجرای تابع sei، پرچم کلی وقفه ها را فعال کرده ایم. همچنین برای استفاده از وقفه ها در اتمل استودیو لازم است کتابخانۀ interrupt.h را فراخوانی کنیم. دستورهای حلقۀ while را نیز به روتین وقفۀ دریافت انتقال داده ایم. در روتین وقفۀ دریافت USART0، ابتدا مقدار رجیسترهای UCSR0A و UDR0 را در متغیرهای status و data قرار می دهیم. سپس بیت های مربوط به ارور فریم، ارور Parity و ارور Data over run را بررسی کرده ایم. هرگاه این بیت ها صفر باشند، در دریافت داده، خطایی به وجود نیامده است. و دستورهای درون if اجرا می شوند. در درون if، ابتدا خالی بودن رجیستر داده را بررسی کرده ایم. در صورتی که رجیستر داده خالی باشد، برنامه از حلقۀ while بدون دستور خارج می شود. در سطر بعد بایت دریافتی را در UDR0 قرار داده ایم تا ارسال شود. عملکرد این نمونه کد هم مثل نمونه کد قبلی است، اما این بار با فعال کردن وقفۀ دریافت.
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
int main(void)
{
UCSR0B = (1<<RXCIE0) | (1<<RXEN0) | (1<<TXEN0); // Bit 7, 4, 3 of UCSR0B
UCSR0C = (1<<UCSZ01) | (1<<UCSZ00); // Bit 2, 1 of UCSR0C
UBRR0L = 51;
sei();
while (1)
{
}
}
// USART0 RX interrupt
ISR(USART0_RX_vect)
{
char status,data;
status=UCSR0A;
data=UDR0;
if ((status & ((1<<FE0) | (1<<UPE0) | (1<<DOR0)))==0)
{
while((UCSR0A & (1<< UDRE0)) != (1<< UDRE0));
UDR0 = data;
}
}
تصویر 5 – نتیجۀ راه اندازی USART در AVR با فعال کردن وقفۀ دریافت
راه اندازی USART در AVR با وقفه، تنظیمات USART در کدویزارد
راه اندازی USART میکروکنترلرهای AVR در نرم افزار کدویژن با استفاده از CodeWizardAVR به راحتی انجام می شود. برای راه اندازی واحد سریال AVR تنظیمات USART در CodeWizardAVR را مطابق تصویر زیر انجام می دهیم. برای پیکربندی USART مثل آنچه در نمونه کد قبل داشتیم، فقط تیک Receiver و Transmitter و Rx Interrupt را فعال می کنیم. می توانیم اندازۀ بافر دریافت را نیز در قسمت Receiver buffer تعیین کنیم. پس از آن، کد را Generate می کنیم. کد تولید شده شامل تابع دریافت و بافر نرم افزاری با در نظر گرفتن شرط هایی برای اندازۀ بافر است. ما اندازۀ بافر را 100 قرار داده ایم. توابع ارسال رشته و کاراکتر را اضافه و قسمت های اضافی کد را نیز پاک کرده ایم.
تصویر 6 – تنظیمات واحد USART0 در کدویزارد برای راه اندازی UART در AVR
در وقفۀ دریافت مثل نمونه کد قبل عمل کرده ایم. اما در اینجا بایت دریافتی را در بافر قرار داده ایم. شمارندۀ بافر را یک واحد اضافه کرده ایم و شرط هایی برای متغیرهای مربوط به بافر قرار داده ایم تا در صورت پر شدن بافر، بایت بعدی، در مکان صفرم بافر ذخیره شود (متغیر rx_wr_index0). و همچنین تنها در صورت وجود دادۀ دریافتی، بافر خوانده شود (متغیر rx_counter0). تابع get_char_0 برای خواندن بایت دریافتی است. هر گاه این تابع را اجرا کنیم، یک بایت از بافر در خروجی تابع قرار می گیرد. تابع put_char_0 هم پارامتر ورودی را برای ارسال در رجیستر UDR0 قرار می دهد. تابع put_string_0 نیز کاراکترهای رشتۀ ورودی را به تابع put_char_0 ارسال می کند. در تابع main رشتۀ UBOARD.ir را به همراه Enter ارسال کرده ایم. سپس در حلقۀ while، با تابع get_char_0 کاراکتری را دریافت و آن را با تابع put_char_0 ارسال کرده ایم.
نکته: استفادۀ صحیح از بافر نرم افزاری در پروتکل های ارتباطی، نسبت به چیزی که در این پروژه می بینیم، نیازمند ملاحظات بیشتری است.
#include <mega64a.h>
#define DATA_REGISTER_EMPTY (1<<UDRE0)
#define RX_COMPLETE (1<<RXC0)
#define FRAMING_ERROR (1<<FE0)
#define PARITY_ERROR (1<<UPE0)
#define DATA_OVERRUN (1<<DOR0)
// USART0 Receiver buffer
#define RX_BUFFER_SIZE0 100
char rx_buffer0[RX_BUFFER_SIZE0];
unsigned int rx_wr_index0=0,rx_rd_index0=0;
unsigned int rx_counter0=0;
// USART0 Receiver interrupt service routine
interrupt [USART0_RXC] void usart0_rx_isr(void)
{
char status,data;
status=UCSR0A;
data=UDR0;
if ((status & (FRAMING_ERROR | PARITY_ERROR | DATA_OVERRUN))==0)
{
rx_buffer0[rx_wr_index0++]=data;
if (rx_wr_index0 >= RX_BUFFER_SIZE0) rx_wr_index0=0;
if (++rx_counter0 >= RX_BUFFER_SIZE0) rx_counter0=0;
}
}
char get_char_0(void)
{
char data;
while (rx_counter0==0);
data=rx_buffer0[rx_rd_index0++];
if (rx_rd_index0 >= RX_BUFFER_SIZE0) rx_rd_index0=0;
#asm("cli")
--rx_counter0;
#asm("sei")
return data;
}
void put_char_0(char c)
{
#asm("cli")
while ((UCSR0A & DATA_REGISTER_EMPTY)==0);
UDR0=c;
#asm("sei")
}
void put_string_0(char *s)
{
while(*s != 0)
{
put_char_0(*s++);
}
}
void main(void)
{
char data = 'a';
UCSR0B |= (1<<RXCIE0) | (1<<RXEN0) | (1<<TXEN0);
UCSR0C |= (1<<UCSZ01) | (1<<UCSZ00);
UBRR0H=0x00;
UBRR0L=0x33;
// Global enable interrupts
#asm("sei")
put_string_0("UBOARD.ir");
put_char_0('\n');
put_char_0('\r');
while (1)
{
data = get_char_0();
if(data != '\0')
{
put_char_0(data);
}
}
}
تصویر 7 – نتیجۀ راه اندازی USART در AVR با فعال کردن وقفۀ دریافت در CodeWizardAVR
نتایج راه اندازی USART در AVR
- واحد USART در میکروکنترلرهای AVR ویژگی ها و امکاناتی مثل عملکرد تمام دو طرفه، عملکرد سنکرون و آسنکرون، پشتیبانی از فریم دادۀ 5 تا 9 بیتی، تعداد 1 یا 2 بیت Stop قابل انتخاب، Parity زوج یا فرد، بررسی Parity به صورت سخت افزاری، آشکارساز Data over run، آشکارساز خطای فریم، وقفه های ارسال و دریافت جداگانه و … دارد.
- برای ارتباط سریال AVR با کامپیوتر با رابط USART هم می توان از روش Polling استفاده کرد و هم وقفه های USART را به کار گرفت.
- برای اتصال میکروکنترلر به پورت سریال کامپیوتر، به مبدل های سطح ولتاژ نیاز است.
- برای اتصال میکروکنترلر به پورت USB کامپیوتر، نیاز به مبدل های USB است.
- برای تبادل دادۀ بین میکروکنترلر و کامپیوتر، در کامپیوتر نیاز به نرم افزارهای ترمینال پروت سریال است.
- در ارتباط USART یا UART، باید پایه های RX و TX یک طرف، به ترتیب به پایه های TX و RX طرف دیگر متصل شود.
- در USART میکروکنترلرهای AVR برای ارسال باید ابتدا خالی بودن رجیستر ارسال را بررسی کرد، سپس در رجیستر UDR نوشت . در دریافت نیز باید ابتدا وضعیت دریافت شدن بایت خوانده شود، سپس بایت دریافتی را از رجیستر UDR خواند.
- بررسی خطاهای مربوط به فریم و Parity و Data over run، عملکرد دریافت را بهبود می بخشد.
- راه اندازی واحد USART در AVR با استفاده از CodeWizardAVR به راحتی انجام می شود.
- در پروتکل های ارتباطی برای تبادل داده می توان از بافرهای نرم افزاری استفاده کرد. در این صورت تبادل داده مطمئن تر و احتمال از دست رفتن داده ها کمتر و یا حتی صفر می شود.
سلام. ممنون از پست خوبتون. برای ارسال و دریافت با usart با بافر نرم افزاری هم مطلب بذارید ممنون
سلام. خواهش میکنم. بافر مطلب مهمیه. حتماً در آینده پروژه های USART با بافر نرم افزاری هم توضیح میدیم.
با درود فراوان خدمت استاد بزرگوار
بسیار بسیار ممنون و تشکر از زحمات فراوان شما .توضیح و تشریح کاربردی بود. این شیوه اموزش بسیار کارامد هست.
سلام دوست عزیز. خواهش میکنم. شما لطف دارید. ممنون از نظرتون
سلام. مهندس آبرومو خریدی فقط خدا هرچی نیت قلبی داری بخاطر این که دست منو گرفتی کمکت کنه. یک دنیا ممنون ارزش این کارتون خیلی بیشتر از یه تشکر حداقل برای من. گیر کرده بودم توی فیوز بیت ها که با کمک نمونه ای که گذاشته بودین رفع شد
سلام دوست عزیز. از این که تونستیم مشکلتون رو حل کنیم خیلی خوشحال هستیم. ممنون از دعای خوبتون. زنده باشید. امیدوارم موفق و پیروز باشید
سلام،من تازه کار هستم ولی اطلاعاتی که گذاشتید فوق العاده مفید بود.بسیار ممنون از زحمات شما بزرگوار
سلام. خیلی ممنون از نظرتون. خواهش میکنم. موفق باشید
با سلام
ممنون از آموزش بسیار خوبتون. من تمام کارها رو مانند مثال انجام دادم و روی برد دانلود کردم. ولی وقتی که میکرو یه کاراکتری مانند C رو ارسال میکنه، توی برنامه هرکول 'e نمایش داده میشه. برنامه توی پروتئوس درست کار میکنه ولی در عمل نه. ممنون میشم راهنمایی کنید.
با تشکر فراوان
سلام. خیلی ممنون. از صحیح انتخاب کردن Baud rate مطمئن بشید. همچنین توی کنترل پنل، تنظیمات پورت COM، باود ریت رو روی 115200 تنظیم کنید. دربارۀ Hercules توی وبلاگ مطلب داریم. اونا رو هم مطالعه کنید شاید تنظیماتی رو به اشتباه تغییر داده باشید. تنظیمات مربوط به Hercules. موفق باشید.
سلام
با تشکر بابت آموزش و پاسخ سوال قبلی
من یه تایمر یک ثانیه ای تعریف کردم که هر باری که اجرا میشه یه عدد رو از طریق uart ارسال میکنه. برای اینکه از صحت انجام مطمین بشم، یه ال ای دی هم تاگل میشه. برنامه الان درست کار میکنه و هر ثانیه یکبار داده ارسالی رو از طریق برنامه هرکول دریافت میکنم. تصمیم گرفتم که با متلب داده رو دریافت کنم.همه تنظیمات رو داخل متلب انجام دادم. ولی به محض اجرای fopen در متلب، ظاهرا میکرو استاپ میشه چون دیگه ال ای دی تاگل نمیکنه و هیچ داده ای دریافت نمیشه. هر وقت که fclose رو اجرا میکنم، میکرو نرمال میشه. به نظرتون مشکل چیه؟
device = serial('COM3');
set(device,'BaudRate',9600);
set(device,'DataBits',8);
set(device,'Stopbits',1);
set(device,'Parity','none');
set(device,'Terminator','')
fopen(device)
سلام. خواهش میکنم. نظر بنده اینه که توی پروتئوس تست کنید. اگه توی پروتئوس جواب گرفتید، برنامه ای که نوشتید درسته. شاید مشکل از سخت افزار باشه. حالا نمیدونم از چه مبدل USB به سریال یا مبدل COM به TTLی استفاده می کنید. اما توصیه میکنم از CP2102 استفاده کنید. همچنین با پورت های دیگۀ کامپیوتر هم تست کنید. این که قبلاً توی هرکولس داده ای دریافت می کردید نشون میده ارسالتون انجام میشده. راه ساده تر استفاده از همون هرکولسه. اگه مطابق نمونه کد این نوشته پیش رفتید تنها مشکل دیگه ای که به ذهنم میرسه اینه که تنظیمات فیوزبیت رو ممکنه اشتباه انجام داده باشید. موقع پروگرم کردن فیوزبیتها رو صحیح مقدار بدید.
من برد atmega64 شما رو تهیه کردم و استفاده میکنم. برای مبدل هم همون cp2102 برد رو استفاده میکنم. فیوز بیت ها چجوری میتونه تاثیر بزاره؟
تشکر فراوان بابت زمانی که برای پاسخ اختصاص دادین جناب مهندس
فیوزبیت ها از این جهت میتونه تاثیر بذاره که مثلا شما توی نرم افزار فرکانس کاری میکروکنترلر رو 8 مگاهرتز قرار دادید، اما فیوزبیت ها روی مثلا 1 مگاهرتز داخلی تنظیم شدن. دقت کنید که پروگرمر روی این برد USBasp هستش و برای انتخاب منبع کلاک خارجی، یعنی کریستال خارجی، با تنظیم فیوزبیت ها دچار مشکل میشید. البته روش هایی رو توی نوشتۀ «پروگرامر برد توسعه ATmega64Pin یوبرد» برای این کار ذکر کردیم.
شما توی نرم افزار ProgISP فیوزبیت ها رو بخونید و ببینید که فرکانس کلاک که توسط فیوزبیت ها تعیین شده با چیزی که توی نرم افزار انتخاب کردید یکسان هست یا نه. فقط مواظب باشید کلاک خارجی رو توی فیوزبیت ها انتخاب نکنید. چون بعد از اون دیگه برد به روش معمول پروگرم نمیشه
نکتۀ دیگه این که از جامپرهایی که به هدر نری روی برد متصل می کنید مطمئن بشید ببنید که اتصالات صحیح هست. باید پایه های 1 و 2 به هم و 3 و 4 به هم متصل بشن. مطابق مطالب دفترچۀ راهنما بخش سلکتورهای UART_S.
خواهش میکنم. زنده باشید
وقت بخیر و تشکر
جناب مهندس ما توی اتمل استودیو حتما برای ارسال و دریافت دیتا در پورت سریال حتما باید تابع بنویسیم؟!
منظورم اینه که نمیشه از توابعی مثل putchar , puts , printf , getchar ,…. استفاده کرد؟
باسپاس