AVR RS485 Kullanımı



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);
       }
 }

Hiç yorum yok:

Yorum Gönder