Seri Bağlantı (RS232)

Bu projede bilgisayarın seri bağlantı portuyla bağlantı kurup, bilgisayardan veri alma ve bilgisayara veri gönderme konusu işlenmektedir. Bilgisayarın seri portu RS232 standardıdır ve bu standart hakkında yeterli bilgi sahibi olmadan böyle bir projeyi denerseniz, Atmega işlemcinizi, daha da kötüsü bilgisayarınızın seri portunu yakabilirsiniz. Öncelikle, RS232 elektrik seviyeleri Atmega işlemcinizin elektrik seviyesinden çok farklıdır ve birbirlerine doğrudan bağlanamazlar. Bu yüzden bu ve bundan sonraki devrelerinizde kullanmak için kendinize bir seviye ayarlayıcısı yapmanız yerinde olur. Bu çok basit devreyi piyasada bolca bulabileceğiniz herhangi bir 232 entegresi ve bir kaç kondansatörle bu yapabilirsiniz.

Bu programda C dilinin kuvvetli noktalarından bazılarını da yakından incelemiş oluyoruz. FILE tipi değişken ile bir stream değişkeni yaratılmakta ve standart girdi ve çıktı bu stream değişkenine yönlendirilerek printf ve scanf gibi güçlü fonksiyonların kullanımı çok kolaylaşmaktadır. Bu konu C bilgisi az olanlar için zor olabilir ancak her bir satırı anlamasanız da neler olup bittiğini anlamanız önemlidir.

Burada aslında önemli iki fonksiyon var: UartPutChar ve UartGetChar. İsimlerinden de anlaşılacağı üzere bunlar seri porta bir karakterlik bilgi gönderen ve alan basit iki fonksiyondur. Bu fonksiyonları doğrudan çağırıp pekala çok daha basit bir program yazabilirdik. Ancak bu durumda, ekrana yada seri porta yazacağımız verileri belli bir formatta yazmak için kafa yormamız gerekecekti. Kayan-noktalı (floating point) sayılar buna iyi bir örnektir, elinizdeki ekrana sadece bir karakter basan fonksiyonla oda sıcaklığını 29.05 formatında yazmak için biraz çaba harcamanız gerekir. Oysa printf ("Mevcut Sıcaklık: %2.2f", temp) komutu temp değişkenindeki sıcaklık değerini ##.## formatında standart çıktıya gönderir, yani formatla siz uğraşmazsınız. Aynı şekilde scanf fonksiyonu da standart girdiden formatlanmış veri alır. C dilinde standart girdi (stdin), standart çıktı (stdout) ve standart hata (stderr) isimli stream (karakter katarı) nesneleri vardır ve bunları istediğiniz çıktıya (ekran, seri port, yazıcı, network) yönlendirebilirsiniz.

FILE uartstr = FDEV_SETUP_STREAM(UartPutChar, UartGetChar, _FDEV_SETUP_RW);

komutuyla uartstr adında bir steam değişkeni yarattık ve FDEV_SETUP_STREAM macrosuyla da çıktı fonksiyonunun UartPutChar, girdi fonksiyonun UartGetChar olduğunu belirledik, _FDEV_SETUP_RW parametresi ise yazma/okuma fonksiyonlarının her ikisinin de kullanılacağını ifade etmektedir (Avr-libc el kitabına bakınız). Ana fonksiyon içindeki, stdout=stdin=&uartstr; komutuyla stdout ve stdin olarak uartstr değişkenin adresinin kullanılacağını belirttik, bu noktadan sonra artık standart girdi ve çıktıyı kullanan tüm fonksiyonlar, uartstr değişkenini kullanacaklar, bir başka deyişle girdi ve çıktı için UartPutChar ve UartGetChar fonksiyonları çağrılacaktır. LCD projemizde seri portdan okunan verilerin LCD'ye nasıl yazılacağını göreceğiz. Böyle bir örnekte girdi fonksiyonumuzun yine UartGutChar gibi seri porttan karakter okuyan bir fonksiyon olacağını, çıktı fonksiyonumuzun ise LCDPutChar gibi LCD'ye bir karakter gönderen bir fonksiyon olacağını tahmin ettiyseniz, konuyu tam olarak kavradınız demektir.

#include <avr/io.h>
#include <util/delay.h>
#include <stdio.h> //standart giris cikis
int UartPutChar(char c,FILE* stream);
int UartGetChar(FILE* stream); //uartstr adli degiskene yazilan her deger RS232'ye
//gider, ve RS232'den gelen deger bu degiskene yazilir
FILE uartstr = FDEV_SETUP_STREAM(UartPutChar, UartGetChar, _FDEV_SETUP_RW);

int UartPutChar(char c,FILE* stream) { //RS232 ye bir karakter gonderir
loop_until_bit_is_set(UCSRA, UDRE);
UDR = c;
return 0;
}

int UartGetChar(FILE* stream) { //RS232'den bir karakter okur
char c;
loop_until_bit_is_set(UCSRA,RXC);
c = UDR;
return c;
}

void mcu_init(void){ // USART kurulumu
// 8 Data, 1 Stop, No Parity
// Baud rate: 4800 UCSRA=0x00;
UCSRB=0x18;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x0C;
}

//Ana Program Fonksiyonu
int main(void){
char ad[12];
mcu_init(); //islemciyi hazirla
stdout=stdin=&uartstr; //standart girdi ve ciktiyi uartstr degiskenine yonlendir
printf("Bana ismini yazar misin?\r\n");

while(1){ //sonsuz dongu
scanf("%s",ad);
printf("\r\nMerhaba %s",ad);
}
}

Kesmelerle İlgili Önemli Noktalar

Hayatta her kazanç bir kaybın karşılığıdır ve bu maalesef printf için de geçerli, programı derlediğinizde Atmega8'in program hafızasının yarısını yediğini göreceksiniz. Evet, printf oldukça pahalı bir fonksiyondur. Atmega128 gibi yüksek hafızalı işlemcilerde pek sorun olmasa da Atmega8 için çok gerekmedikçe printf ve scanf fonksiyonlarını kullanmamanız tavsiye edilir.

Dikkat etmeniz gereken bir başka konu da, Make dosyasındaki printf ve scanf ile ilgili ayarlardır. Make dosyanızı incelediğinizde printf ve scanf ile ilgili bir bölümün olduğunu göreceksiniz:

Make dosyalarında cümle başındaki # işaretinin o cümleyi pasif hale getirdiğini unutmayın

#---------------- Library Options ----------------
# Minimalistic printf version
PRINTF_LIB_MIN = -Wl,-u,vfprintf -lprintf_min

# Floating point printf version (requires MATH_LIB = -lm below)
PRINTF_LIB_FLOAT = -Wl,-u,vfprintf -lprintf_flt

# If this is left blank, then it will use the Standard printf version.
PRINTF_LIB =
#PRINTF_LIB = $(PRINTF_LIB_MIN)
#PRINTF_LIB = $(PRINTF_LIB_FLOAT)

# Minimalistic scanf version
SCANF_LIB_MIN = -Wl,-u,vfscanf -lscanf_min

# Floating point + %[ scanf version (requires MATH_LIB = -lm below)
SCANF_LIB_FLOAT = -Wl,-u,vfscanf -lscanf_flt

# If this is left blank, then it will use the Standard scanf version.
SCANF_LIB =
#SCANF_LIB = $(SCANF_LIB_MIN)
#SCANF_LIB = $(SCANF_LIB_FLOAT)

MATH_LIB = -lm
#-------------------------------------------------

Burada printf ve scanf fonksiyonları için üç seçenek vardır: standart, minimum ve kayan-nokta. Eğer kayan-nokta formatında çıktı alacaksanız PRINTF_LIB_FLOAT parametresini aktif hale getirmelisiniz, aksi halde ekranda anlamsız karakterler görürsünüz. Bu bölüm avr-libc elkitabında ayrıntılı olarak anlatılmıştır.

Konuyu pekiştirmek için printf ile çeşitli formatlarda çıktı almayı deneyin; örneğin ondalık 12.4567 ondalık sayısını içeren bir x değişkeninin

printf("X değişkeni=%2.2f", x);

ifadesi ile nasıl bir çıktı verdiğinin inceleyin (bu ifadenin doğru sonuç üretmesi için Make dosyasında PRINTF_LIB = $(PRINTF_LIB_FLOAT) ifadesinin aktif hale getirilmesi gerekir).

Benzer bir programın, printf fonksiyonu kullanılmadan (daha az program hafızası harcanarak) nasıl yazılacağını görmek için Sıkça Sorulan Sorular kısmına bakınız.

Proje Dosyaları

Proje Dosyaları