50 adet pic ile rs485 haberleşme nasıl yapılır

Başlatan sagamen, 11 Haziran 2007, 03:07:58

sagamen

arkadaşlar bir adet picden rs485 kullanarak 50 ve daha fazla pic e adresleme ile bilgi nasıl gonderecegim hakkında bılgısı olan arkadaşlardan yardımcı olmalarını beklıyorum  internette aradım ama kayda deger bişey bulamadım

not: bu arada i2c ile de bolum acılmıs ama uygun olmayacagını dusunerek burda bi başlık actım
herkEse kolay gelsin

Klein

Eğer seri haberleşme yapabiliyor ama nasıl adresleyeceğini bilmiyorsan:
Basitçe şöyle özetleyebiliriz.

Önce her terminaline bir sdres vermekle başlamalısın. Bu adresi dip-sw , jumper vs.. gibi elemanlarla yapabileceğin gibi , çipi programlarken doğrudan verebilir  ya da  eeprom'a yazabilirsin.  Eğer master-slave çalışacaksan master'i 0x00 adresli kabul edebilirsin.  tüm birimlere ortak mesaj geçmek için ise 0xFF adresini kullanabilirsin.

şöyle basit bir protokol kullanabilirsin.

<stx><gönderici adresi><alıcı adresi><işlem kodu><veri><etx><cs>

normalde bütün birimler dinlemededir. eğer master-slave çalışıyor isen ; master birim sırayla bütün birimleri sorgular. onlar da gelen verinin alıcı adresi bölümüne bakar ve kendi adresi ile karşılaştırır. eğer adres kendi adresi ise gereken cevabı verir. işlem böyle devam eder. Eğer alıcı adresi genel yayın (broadcast) adresi ise , bütün terminaller mesajı kendilerine gönderilmiş kabul ederler. Ancak burada dikkat edilmesi gereken nokta; gönderilen mesajın tek yönlü , yani alıcıya cevap hakkı vermeyecek bir mesaj olması gerkliliğidir. yok illa cevap hakkı verecem diyorsan: alıcı birimler adres sıralarına göre cevaplarını verirler.

Eğer multi-master çalışıyor isen ;  söyleyecek sözü olan birim gönderici adresi kısmına kendi adresini yazar , alıcı adresine de hangi birime göndermek istiyor ise onu yazar. ve gönderir.  İlgili adrese sahip birim de isteneni yapar , ya da gerekli cevabı gönderir. Bu esnada  başka bir birimin de gönderecek verisi varsa , iki birim arasındaki haberleşme bitene kadar bekler. haberleşme bittikten sonra verisini gönderir.

Eğer aynı anda iki birimin gönderecek verisi varsa ?
bu durumda bir hat çarpışması gerçekleşir. eğer RS485 çipin bu çarpışmayı algılıyorsa sorun yok. çarpışma bilgisi geldiğinde iki birim de susar. Adresleri ile orantılı bir süre beklerler. Örneğin adresi 1 ise 1mS , adres 2 ise 2mS gibi... Bu durumda adresi küçük olav verisini daha erken göndermeye başlayacağı için ikinci bir çarpışma olmaz.

Fakat çipin bu çarpışmayı algılayamıyor , ve sen böyle bir çarpışma dedektörü yapmıyor , yapamıyor veya gerek görmüyor isen : bu durumda mesajı alan birim bir <ACK> bilgisi göndermeli. Eğer hatta bir çarpışma gerçekleşir ise , ilgili birimler mesajı alamayacağı için <ACK> onayı gönderemez. gönderici birim <ACK> onayını bir süre bekler. gelmez ise bir çarpışma olma ihtimaline karşı yukarıda yazdığım gibi adresi ile orantılı bir zaman daha bekler. ve mesajını gönderir.

Yine multi-master çalışmada çarpışmaları daha da azaltmak için başka bir yöntem daha kullanabilirsin. Birimlerden biri zamanlayıcı olarak çalışır. belirli aralıklarla hatta zamanlama bilgisi gönderir. Bu bilgiyi dinleyen bütün birimler yukarıdaki adres-zaman gecikmesini kullanarak verisini gönderir.


Not: 50 birim için araya sürücü (line driver) girmelisin.

MURSEL

sagol üsteat eline saglık oldukça güzel anlatım   :)

CaFFeiNe

@Klein hocam çok güzel bilgiler vermişsin sağol
çarpışmayı algılayan bir RS485 çipi için örnek verebilirmisin mümkünse kolay bulunan birşey

teşekkürler

Analyzer

Alıntı yapılan: "Klein"Fakat çipin bu çarpışmayı algılayamıyor , ve sen böyle bir çarpışma dedektörü yapmıyor , yapamıyor veya gerek görmüyor isen : bu durumda mesajı alan birim bir <ACK> bilgisi göndermeli. Eğer hatta bir çarpışma gerçekleşir ise , ilgili birimler mesajı alamayacağı için <ACK> onayı gönderemez. gönderici birim <ACK> onayını bir süre bekler. gelmez ise bir çarpışma olma ihtimaline karşı yukarıda yazdığım gibi adresi ile orantılı bir zaman daha bekler. ve mesajını gönderir.
Selam,

Bilgiler gerçekten çok güzel, tebrikler. Benim bu kısımla ilgili aklıma takılan bir soru var. Acknowlegde bilgisini gönderen alıcı kısımda eğer bir problem olursa, ack gönderemeyeceği için gönderici polling'e girip bir daha çıkamayabilir. Ya da sistem örn. 5 kez gönderip ack alamayınca arızaya geçebilir. Bunu nasıl önlemek gerekir? Ya da hiç uğraşmadan çakışma dedektörlü bir sürücüye mi geçmek en iyisi? Sonuçta RS485 endüstri standartı ve makinaların mümkün olduğunca az arızaya geçmesi beklenir.

Analyzer
Üşeniyorum, öyleyse yarın!

sagamen

aklına sağlık  cok  guzel anlatım   ben master slave ile adresleme yapacagım ama bı ornek kod olursa cok guzel olur hemde forumdakı arkadaslarda ogrenır  ccs c için
herkEse kolay gelsin


Klein

Alıntı yapılan: "Analyzer"
Alıntı yapılan: "Klein"Fakat çipin bu çarpışmayı algılayamıyor , ve sen böyle bir çarpışma dedektörü yapmıyor , yapamıyor veya gerek görmüyor isen : bu durumda mesajı alan birim bir <ACK> bilgisi göndermeli. Eğer hatta bir çarpışma gerçekleşir ise , ilgili birimler mesajı alamayacağı için <ACK> onayı gönderemez. gönderici birim <ACK> onayını bir süre bekler. gelmez ise bir çarpışma olma ihtimaline karşı yukarıda yazdığım gibi adresi ile orantılı bir zaman daha bekler. ve mesajını gönderir.
Selam,

Bilgiler gerçekten çok güzel, tebrikler. Benim bu kısımla ilgili aklıma takılan bir soru var. Acknowlegde bilgisini gönderen alıcı kısımda eğer bir problem olursa, ack gönderemeyeceği için gönderici polling'e girip bir daha çıkamayabilir. Ya da sistem örn. 5 kez gönderip ack alamayınca arızaya geçebilir. Bunu nasıl önlemek gerekir? Ya da hiç uğraşmadan çakışma dedektörlü bir sürücüye mi geçmek en iyisi? Sonuçta RS485 endüstri standartı ve makinaların mümkün olduğunca az arızaya geçmesi beklenir.

Analyzer

Çarpışma dedektörü bile olsa , eğer karşı taraf veriyi alamıyor ya da , <ACK> gönderemiyor ise yapacak pek bişey yok. Eğer sorun yoksa , sadece çarpışma yüzünden veri alıp gönderemiyorsa , adres orantılı gecikme sebebiyle ikinci bir çarpışma olasılığı çok düşük. hatta neredeyse imkansız. 2. kez çarpışma olduğunu bile varsayarsak 5 kez yaşanması daha da zor.

Klein

Alıntı yapılan: "CaFFeiNe"@Klein hocam çok güzel bilgiler vermişsin sağol
çarpışmayı algılayan bir RS485 çipi için örnek verebilirmisin mümkünse kolay bulunan birşey

teşekkürler

örnek vermem zor. çünkü görmedim. teknik bu kadar ilerlemişken , böyle bir çipin olmaması imkansız die düşünerek salladım açıkçası  :D

CLR

Tek master'lı  RS485 sistemde , en fazla 32 adet slave olabilir dolayısıyla 50 adet pic arasında ağ kurabilmek için en az 2 tanesi master olmalı.
Knowledge and Experience are Power

sagamen

ccs c ile örnek kod yokmu  master slave  ilişkisi ile
herkEse kolay gelsin

sagamen

evet ben bi acık kod buldum dmx ile ilgili yanlız belki ilgilenen arkadaşlar olabilir



/************************************************************************
*                    FUSES                                             *
*  NOPROTECT=Code not protected from reading                           *
*  CCP2C1=CCP2 input/output multiplexed with RC1                       *
*  NODEBUG=No Debug mode for ICD                                       *
*  BORV20=Brownout reset at 2.0V                                       *
*  MCLR=Master Clear pin enabled                                       *
*        Habilitamos el pin de clear                                   *
*  NOWDT=No Watch Dog Timer                                            *
*  INTRC_IO=Internal RC Osc, no CLKOUT                                 *
*  NOIESO=Internal External Switch Over mode disabled                  *
*  NOFCMEN=Fail-safe clock monitor disabled                            *
*    Desabilitamos la opcion de deteccion de que cuando hay una perdida *
*    de clock externo el clock interno  proporciona los clock al sistema*
*  NOBROWNOUT=Reset when brownout detected                             *
************************************************************************/
#fuses NOPROTECT,CCP2C1,NODEBUG,MCLR,NOWDT,INTRC_IO,NOBROWNOUT
#include <16F767.h>
#use delay(clock=4000000)  //Indicamos al compilador que los retardos los calcule
                          //en funcion de una base de tiempos con un clock de 4Mhz
#include <string.h>
#include <stdio.h>
/************************************************************************
*                    Puertos I/O                                        *
************************************************************************/

struct PA_pin_map {
     boolean unusedRA0;    //bit 0
     boolean unusedRA1;
     boolean unusedRA2;
     boolean unusedRA3;
     boolean unusedRA4;
     boolean unusedRA5;      //bit 4
       } Puerto_A;
#byte Puerto_A = 5

struct PB_pin_map {
     boolean unusedRB0; //bit 0
     boolean unusedRB1;
     boolean unusedRB2;
     boolean unusedRB3;
     boolean unusedRB4;
     boolean unusedRB5;
     boolean unusedRB6;
     boolean unusedRB7; //bit8
       } Puerto_B;
#byte Puerto_B = 6

struct PC_pin_map {
     boolean unusedRC0; //bit 0
     boolean unusedRC1;
     boolean unusedRC2;
     boolean unusedRC3;
     boolean unusedRC4;
     boolean test_osc;
     boolean unusedRC6;
     boolean rx; //bit8
       } Puerto_C;
#byte Puerto_C = 7

#CASE

#byte porta=0x05
#byte portb=0x06
#byte portc=0x07

/************************************************************************
*                    Registros USART                                    *
************************************************************************/

#byte SPBRG = 0x99
#byte RCSTA = 0x18   //Registro de recepcion de la usart
#byte TXSTA = 0x98
#byte RCREG = 0x1a
#byte PIR1  = 0x0c
#byte PIE1  = 0x8c
#byte INTCON = 0x0b


#bit SPEN   = RCSTA.7   //Serial port enable bit
#bit RX9   = RCSTA.6    // 9-bit receive enable
#bit SREN   = RCSTA.5   //En modo asyncrono no se tiene en cuenta
#bit CREN   = RCSTA.4   // Continuos receive enable bit
#bit ADDEN   = RCSTA.3
#bit FERR   = RCSTA.2   //Frame error bit
#bit OERR   = RCSTA.1   //Overrun error bit
#bit RX9D   = RCSTA.0   //El noveno bit

#bit BRGH   = TXSTA.2
#bit SYNC    = TXSTA.4

#bit RCIF  = PIR1.5
#bit RCIE  = PIE1.5
#bit GIE    = INTCON.7
#bit PEIE   = INTCON.6

/************************************************************************
*                    Variables globales                                  *
************************************************************************/

#define MAX_PWMS 3 //antes 24

/*Rx Buffer for dmx stream */
int8 Rx_Buffer[MAX_PWMS];
/*Current levels -0 to 255 */
int8 DMX_Levels[MAX_PWMS];


int1 Check_levels=0;

/************************************************************************
*                    Declaracion de funciones                           *
************************************************************************/

void Interrupt_USART_Rx(void);

/************************************************************************
*                    Rutina de atencion a la interrupcion               *
************************************************************************/

#int_rda
void Interrupt_USART_Rx(void)
{
 #define WAIT_FOR_NEXT_BYTE 0
 #define WAIT_FOR_BREAK     1
 #define WAIT_FOR_START     2
 #define RECEIVE_DATA       3

/* Maquina de estados para determinar el inicio de la trama dmx*/
 static int8 Rx_State = WAIT_FOR_BREAK;

 int8 data;   //El dato que estamos recibiendo

/*Definimos una union que es como una struct pero con la diferencia de
que sus componentes se almacenan en la misma posicion de memoria.
Esto significa  que el byte rcsta.byte esta formado por 8 bits los cuales
hemos definido id鮴icos a los bits del registro de estado de la usart: RCSTA */
 union
 {
   unsigned char byte;
   struct {
       unsigned char RX9D:1; //bit0
       unsigned char OERR:1;
       unsigned char FERR:1;
       unsigned char ADDEN:1;
       unsigned char CREN:1;
       unsigned char SREN:1;
       unsigned char RX9:1;
       unsigned char SPEN:1; //bit7
   } bits ;
 }rcsta;

 /* ͎DICE  AL BUFFER DE RECEPCION */
 static int8 *ptr_Rx;


 while (RCIF) //Nos quedamos leyendo el dato tanto tiempo como este presente
 {
  /*Leemos el registro de estado de la usart y se lo asignamos
  al byte rcsta.byte. Leemos el dato */

   rcsta.byte = RCSTA;
   data = RCREG;

   if (rcsta.bits.OERR)   //Miramos si hay un buffer overrun
   {
     //Si hay Overrun entonces hay que resetear la l��a de recepcion
     CREN = 0;
     CREN = 1;
     /* Como hemos recibido un overrun hemos de esperar un byte correcto
     antes de buscar la se��de break */
     Rx_State = WAIT_FOR_NEXT_BYTE;
     return;
   }

   switch (Rx_State)
   {
     case WAIT_FOR_NEXT_BYTE:
       if (!rcsta.bits.FERR)
         Rx_State = WAIT_FOR_BREAK;
       break;
     case WAIT_FOR_BREAK:
       if (rcsta.bits.FERR) //Miramos si hay un error de trama
       {
      /*Si recibimos un error de trama, hay que asegurarse que el dato es 0.
   Esto significa que hemos recibido una se��de break de como m�mo 44us.
   Hay que acordarse que trabajamos a 250Kbits lo cual implica un tbit de 4us
   SI enviamos 11 bits( start+8bit datos+2stop)esto son 44us*/
         if (!data)
           Rx_State = WAIT_FOR_START;
       }
       break;
     case WAIT_FOR_START:
        /*Comprobamos si hay error en la trama. Si recibimos un error
         tenemos que esperar a recibir un byte correcto antes de que
         empezemos a buscar nuestra se��de break */
       if (rcsta.bits.FERR)
           Rx_State = WAIT_FOR_NEXT_BYTE;
       /* El byte de start de nuestro paquete siempre empieza por 0. */
       else
       {
         if (!data)
         {
           /*Inicializamos nuestro �ice a nuestro buffer de recepcion */
           ptr_Rx = Rx_Buffer;
           /*En este punto debermos determinar donde de la trama DMX queremos empezar
           a recibir datos basandonos en una direccion de offset. Este primer programa
           solo contempla recoger los tres primeros canales*/
             Rx_State = RECEIVE_DATA;
         }

       }
       break;

     case RECEIVE_DATA:
       /*Comprobamos si hay error de trama. Si nosotros recivimos un error de trama
       esto puede ser el inicio de un nuevo paquete o un verdadero error de trama */
       if (rcsta.bits.FERR)
       {
         /* Si es el inicio de un nuevo paquete el dato debe ser 0 sino es un error
         en la trama */
         if (!data)
           Rx_State = WAIT_FOR_START;
         else
           Rx_State = WAIT_FOR_NEXT_BYTE;
       }
       else
       {
         /* ALmacenamos el dato recibido en el buffer de recepcion */
              *ptr_Rx=data;
              ptr_Rx++;
          /*Comprobamos si hemos recibido todos nuestros datos */
              if(ptr_Rx > &Rx_Buffer[MAX_PWMS-1] )
              {
                 /* Hemos recibido todos los datos*/
                 Check_levels=1;
                 Rx_State= WAIT_FOR_BREAK;
              }

       }
       break;

   }
 }
 return;
}

/************************************************************************
*                    Programa Principal                                 *
************************************************************************/


void main (void) {

int8 i;

//////////////////Configuramos los puertos ////////////////////////
set_tris_a(0x00);
set_tris_b(0x00);
set_tris_c(0b10000000);

setup_oscillator(OSC_INTRC|OSC_4MHZ); //pone el oscilador interno a 4Mhz

//un 1 es como entrada y 0 como salida
/////////////////////////////////////////

SPBRG=0x00;       // SPBRG=0 implica que a 4Mhz el baudrate=250.000
BRGH=1;         // BRGH=1 implica High speed.
SYNC=0;           // enable aSYNChronous reception
SPEN=1;           // serial port enabled - rc7/rx/dt , rc6/tx/ck pins as serial port pins

/////////////////////////////////////////

RX9=1;            // 9 bit reception
CREN=1;           // enable reception
ADDEN=0;
FERR=1;
OERR=1;
/////////////////////////////////////////
delay_ms(3000);
///////////////////////////////////////////////////////////////////
////                 Configuramos los tres PWM                 ////
///////////////////////////////////////////////////////////////////

  setup_ccp1(CCP_PWM);   //Configure CCP1 as a PWM
           //El tiempo de ciclo sera (1/clock)*4*t2div*(period+1)
           //Si el clock es de 4Mhz y el periodo 255 (debajo)
           //   (1/4000000)*4*1*256=256us   freq=3.9KHz
           //   (1/4000000)*4*4*256=1024us   freq=975.56Hz
           //   (1/4000000)*4*16*256=4096us   freq=244.14Hz
           //Si el clock es de 16Mhz y el periodo 255 (debajo)
           // (1/16000000)*4*1*256=64us  freq=15.625Hz
           // (1/16000000)*4*4*256=256us freq=3.906Hz
           // (1/16000000)*4*16*256=1024us     freq=976.5625Hz

  setup_ccp2(CCP_PWM);   //Configure CCP2 as a PWM
  setup_ccp3(CCP_PWM);   //Configure CCP2 as a PWM

  setup_timer_2(T2_DIV_BY_16,255,1); //configuramos a 200Hz aprox

//// Inicializamos los tres pwm     ////
set_pwm1_duty(127);
set_pwm2_duty(127);
set_pwm3_duty(127);
delay_ms(1000);
/////////////////////////////////////////

GIE=1;            // enable all interrupts
PEIE=1;           // enable peripheral interrupts
RCIE=1;           // enable receive interrupt


while (1)
  {
     /*Despues de cada paquete, el receptor actualiza este flag para
     permitirnos detectar un cambio.*/
     if (Check_levels)
     {
        Check_levels=0;

        DMX_Levels[0]=Rx_Buffer[0];
        DMX_Levels[1]=Rx_Buffer[1];
        DMX_Levels[2]=Rx_Buffer[2];
        set_pwm1_duty(DMX_Levels[0]); //CANAL 1   ->CCP1 (PIN 13)  RC2
        set_pwm2_duty(DMX_Levels[1]); //CANAL 2   ->CCP2 (PIN 12)  RC1
        set_pwm3_duty(DMX_Levels[2]); //CANAL 3   ->CCP3 (PIN 26)  RC3

     }


  }

}
herkEse kolay gelsin

sagamen

master/slave ile ilgili RS485 ile ilgili c icin kod


//master
#include<18f452.h>

#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
#use delay(clock = 4000000)
#define RS485_ID  0x10                 // The device's RS485 master address
#define RS485_USE_EXT_INT TRUE        // Select asynchronous serial interrupt                            

#include<RS485.c>

int buffer[40];
int next_in  = 0;
int next_out = 0;

#INT_RDA
void serial_isr()
{
  int t;

  buffer[next_in] = fgetc(RS485);
  t = next_in;
  next_in = (next_in+1) % sizeof(buffer);
  if(next_in == next_out)
     next_in=t;        // Buffer full !!
}

void main(void)
{
  int j;

  int data_received[32];

  enable_interrupts(INT_RDA);
     enable_interrupts(GLOBAL);


  rs485_init();

  while(1)
  {

     rs485_wait_for_bus(FALSE);

                  //send address=0x11, length = 1, msg=3
                               //the led does blink here
     if(rs485_send_message(0x11, 1, 3))    
     {
         
        output_low(PIN_B1);
        delay_ms(100);
        output_high(PIN_B1);
        delay_ms(100);
        output_low(PIN_B1);
     }

     delay_ms(5);

   
     if(rs485_get_message(data_received, FALSE))
     {
        for(j=0;  j<3; ++j)
                 data_received[j] = buffer[j];


              if(data_received[2]==6)         //if slave receives the number 3 successfully
        {                    //slave will send back number 6
           output_low(PIN_B2);
           delay_ms(100);
           output_high(PIN_B2);
           delay_ms(100);
           output_low(PIN_B2);
        }
     }

        }
}















//slave
#include<18f452.h>

#fuses HS, NOWDT, NOLVP, NOBROWNOUT, NOPROTECT, PUT
#use delay(clock = 4000000)
#define RS485_ID  0x11                 // The device's RS485 slave address or ID
#define RS485_USE_EXT_INT TRUE        // Select asynchronous serial interrupt

                               
#include<RS485.c>

int buffer[40];
int next_in  = 0;
int next_out = 0;

#INT_RDA
void serial_isr()
{
  int t;

  buffer[next_in] = fgetc(RS485);
  t = next_in;
  next_in = (next_in+1) % sizeof(buffer);
  if(next_in == next_out)
     next_in=t;        // Buffer full !!
}

void main(void)
{
  int i=1;
  int j;

  int data_received[32];
  enable_interrupts(INT_RDA);
     enable_interrupts(GLOBAL);

  rs485_init();

  while(1)
  {

     if(rs485_get_message(data_received, TRUE))
     {

        for(j=0;  j<3; ++j)
                 data_received[j] = buffer[j];
     

        if(data_received[0] == 0x11)
        {
                                         
           output_low(PIN_B2);
           delay_ms(100);
           output_high(PIN_B2);
           delay_ms(100);
           output_low(PIN_B2);
         

           rs485_wait_for_bus(FALSE);

           
           //after receive successfully, respond to master by sending number 6
           if(rs485_send_message(0x10, 1, 6))
           {
         
              output_low(PIN_B1);
              delay_ms(100);
              output_high(PIN_B1);
              delay_ms(100);
              output_low(PIN_B1);
           }
        }
     }

        }
}
herkEse kolay gelsin

Göktuğ

Alıntı yapılan: "eemkutay"Tek master'lı  RS485 sistemde , en fazla 32 adet slave olabilir dolayısıyla 50 adet pic arasında ağ kurabilmek için en az 2 tanesi master olmalı.
Alıntı yapılan: "Klein"
Not: 50 birim için araya sürücü (line driver) girmelisin.

Veya max487 ile 128 adet surulebilir. Daha yuksek surme kaabiliyetli chiplerde var.

Ek.
Slavelerde mantık devreleri yada mcu kullanılırsa master program akısına göre farklı noktalara kaydırılabilir.

OG

FORUMU İLGİLENDİREN KONULARA ÖM İLE CEVAP VERİLMEZ.