RS485:
RS485
iki ya da daha fazla cihazın seri iletişimini sağlayan fiziksel altyapı
standardıdır. Seri iletişimde hat "1" ya da "0" olarak
veriyi iletir. ASCII "A" göndermek istediğimizde Tx (transmitter)
"0100 0001" şeklinde "1" ya da "0" olacaktır.
"1" ya da "0" olarak adlandırdığımız durum GND ile Tx
arasında ölçülen potansiyel farktır. (TTL seviyede 2,7v üstü ) Bu iki kablo ile
iki cihazı tek taraflı (simplex) haberleştirebiliriz. Mesafe uzayınca bu hat
üstünde oluşacak kayıplar ve gürültüler nedeniyle iletişim sağlıklı
olmayacaktır.
Uzun
mesafeler ve daha hızlı haberleşme ihtiyacı RS485 standardının geliştirilmesini
sağlamıştır. 1000m gibi bir mesafe ve 10Mbps gibi hızlarla iki ya da daha
fazla cihazın haberleşmesine imkan sağlar. Bu standardın RS422 ye göre hız ve
mesafeyi artıran özelliği, birbirine sarılı iki hattın gürültüleri en aza
indirmesidir. "1" ya da "0" olma durumu Gnd ye göre değil
iki kablo arasındaki farka göre bulunmaktadır. A ve B olarak adlandırılan bu
iki hat arasındaki fark 200mV tan büyükse "1" küçükse
"0" olarak belirlenir. Hattın karalı olması ve 200mV farkın
oluşabilmesi için sonlandırma ile pull up-pull down dirençler kullanılır.
Max485:
RS485
standardı ile iletişimin gerçekleşmesi için gerekli olan sürücüler vardır.
Bunlardan Max485'i inceleyelim. Datesheet buradan ulaşabilirsiniz. Aşağıda pin ve
bağlantı şeması bulunmaktadır. Sırasıyla bu pinleri açıklayayım.
RO:
Mikro denetleyicinin (Receive) RX pini bu pine bağlanır.
RE:
Alıcı (Receiver) aktifleştiren pindir. "0" ise veri okumaya hazırdır.
"1" olursa RO pininde "high z" yani ne "1" ne de
"0" durumu oluşur.
DE: Veri
gönderimi (Driver output) aktifleştiren pindir. "1" ise veri
gönderilebilir, "0" olursa A ve B "high z" durumundadır. DE
ve RE birbirine bağlanabilir, farklı modelerde (481-483) kapatma modu için
ayrıca kontrol edilebilir.
DI:
Mikro denetleyicinin (Transmitter) TX pini bu pine bağlanır.
Vcc-Gnd:
Pozitif +5V ve negatif besleme pinleridir. (12V a kadar besleme yapılabilir.)
A-B:
iletişimi sağlayan hattın bağlandığı pinlerdir. Burgulu tip kablo ile
diğer sürücünün aynı isimli pinlerine bağlanır.
Yukarıdaki tabloda RE ve DE pinlerine göre veri gönderme ve alma durumları
gösterilmiştir. DE pin "1" ve DI "1" olduğunda A
"1" - B"0" ve DI "0" olduğunda A"0" -
B"1" olarak gönderim yapılır. Gönderim modunda (DE"1") RE
nin durumu önemli değildir. Veri alırken DE ve RE pinleri "0"
yapılır. A - B <200mV ise "0", A - B >200mV "1"
olarak veri okunur. Verinin sağlıklı okunması ve gönderilmesi için kullanılacak
dirençlerin hesaplanması önemlidir. A ve B arasındaki fark iletişim yokken
200mV'tan fazla olmalıdır. Bazı kaynaklarda bu hesaplamayı 200mV eşit olarak
yapılması tavsiye edilmiş.
Dirençler:
Master
cihazda A hattı "+" ve B hattı "-" uçlara uygun direnç ile
bağlanmalıdır.(Bu bağlantı tek noktadan yapılmalıdır.) Hat sonu ve hat başı
genellikle 120 ohm direnç bağlanmaktadır. Ben 3 adet cihazın bağlı olduğu bir
sistem için hesap yapacağım. 3 Adet cihazın içinde bulunan 12 Kohm
dirençler ve 120 ohm sonlandırma dirençleri paralel bağlı dirençlerdir ve
paralel direnç değeri 59,113 ohm olur.
1/R(eşdeğer)=(1/R1)+(1/R2)+(1/R3)+......
1/R(eşdeğer)=
(1/12000)+(1/12000)+(1/12000)+(1/120)+(1/120)=59,113ohm
250mV
fark oluşturmak için 4,22 mA akıma ihtiyaç vardır.
V=I x R
0,250=Ix59,113
I=0,250/59,113=0,004229
A=4,22mA
Sistemin
5V ile beslenmesi durumunda 1182 ohm direnç bulunur.
V=I x R
5=0,004229
x R
R=5/0,004229=
1182ohm
Bunun 59
ohm kısmı paralel dirençlerden geliyordu bunu düştüğümüzde 561 ohm pull-up ve
pull-down dirençlere ihtiyacımız olur.
(https://320volt.com/rs485-balanced-data-transmission-hakkinda-bilgiler/)
Uygulama notlarında yer alan şema yukarıdaki
gibidir. RS485 altayapısıyla ilgili ihtiyacmız olacak bilgileri kabaca anlattım
artık AVR için C diliyle programın yazılması ve çalışmasına geçebiliriz.
C ile AVR RS485 Programlama
Uart kullanımı kısmında paylaştığım
programa ilave bir kaç komut ile bu işlemi yerine getirebiliriz. Öncelikle
sürücünün DE ve RE pinleri gönderme-alma ayrımını yapıyor. Ben cihazın veri
almaya hazır olmasını, veri göndereceğim zaman gönderme pozisyonuna geçmesini
istiyorum. Bu nedenle bu iki pin "0" olacak. Veri gönderme durumunda
DE pini "1" olacak. Bu işlemin kolay olması ve ilerde mikro
denetleyicide pin değişikliği yapmak isteyebileceğim için makro tanımlayacağım.
#define MAX_PIN PORTD2
#define MAX_OUT DDRD|=(1<<MAX_PIN)
#define MAX_AL PORTD&=~(1<<MAX_PIN)
#define MAX_VER PORTD|=(1<<MAX_PIN)
|
MAX_PIN
olarak PORTD PD2 pini seçildi burada yapılan tanımlama ile MAX_PIN görülen her
yere PORTD2 yazılacaktır, pin değiştirmek için bu tanımdaki kısmı
değiştireceğiz. MAX_OUT ile ilgili pini çıkış olarak tanımlıyoruz. MAX_AL ve
MAX_VER ile pin "0" ve "1" yapılarak sürücünün durumuna
karar veriyoruz. Veri gönderme sırasında işlem yapacağız, gönderme için
yazdığımız fonksiyona ekleme yapacağız.
void uart_gonder(uint8_t uData){
while(!(UCSR0A & (1<<UDRE0)));
UDR0=uData;
}
|
Fonksiyonun
ilk hali yukarıdadır. Eklemelerin yapılmış ve sürücü ile veri gönderimi
yapabilen hali ise aşağıdadır.
void uart_gonder(uint8_t uData){
MAX_VER;
while(!(UCSR0A & (1<<UDRE0)));
UDR0=uData;
_delay_us(200);
MAX_AL;
}
|
Fonksiyon
çağrıldığında MAX_VER ile DE pini "1" yapılıyor. Ardından uart
birimimin veri gönderimine hazır olması bekleniyor ve veri gönderiliyor. Burada
önemli olan bir nokta Baud Rate eğer düşük ise (9600) bekleme "
_delay_us(200); " süresi daha uzun olmalı. Sürücü veri gönderimi
tamamlanmadan alma pozisyonuna geçerse iletişim gerçekleşemez. İşlem
tamamlanınca sürücü MAX_AL ile veri alma durumuna geçiriliyor. Tx kesmesi kullanırsak bu beklemeye gerek kalmaz.
Umarım
faydalı bir yazı olmuştur.
/*
* RS485.c
*
* Created: 13.02.2019 22:06:14
* Author : haluk
*/
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#define MAX_PIN PORTD2
#define MAX_OUT DDRD|=(1<<MAX_PIN)
#define MAX_AL PORTD&=~(1<<MAX_PIN)
#define MAX_VER PORTD|=(1<<MAX_PIN)
char veri[25];
void uart_basla(uint32_t baud){
uint16_t baudRate=F_CPU/baud/16-1;
UBRR0H=(baudRate>>8);
UBRR0L=baudRate;
UCSR0B|=(1<<RXEN0)|(1<<TXEN0);
UCSR0C|=(1<<UCSZ01)|(1<<UCSZ00);
}
uint8_t uart_oku(){
while(!(UCSR0A & (1<<RXC0)));
return UDR0;
}
void uart_gonder(uint8_t uData){
MAX_VER;
while(!(UCSR0A & (1<<UDRE0)));
UDR0=uData;
_delay_us(200);
MAX_AL;
}
void uart_dizi(char*str){
uint8_t i=0;
while(str[i]) {
uart_gonder (str[i]);
i++;
}
}
int main(void){
MAX_OUT;
MAX_AL;
uart_basla(19200);
while (1) {
uart_gonder('A');
_delay_ms(1000);
}
}
|