logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[ATMega162|32][C]Komunikacja między uC

sevotharte 06 Lut 2011 21:15 2192 16
  • #1 9113860
    sevotharte
    Poziom 10  
    Witam.

    Mam taki problem. Próbuję skomunikować ze sobą dwa uC. Docelowo będą one skomunikowane po RS485, ale teraz w fazie testów próbuję je połączyć normalnie po UARCie. Skrosowałem TXD i RXD dwóch Atmeg. Wysyłam z Atmega162 znak 0x3A, natomiast na Atmedze32 włącza się przerwanie od odebrania (dioda niezależna od znaku odebranego się zapala ) ale nie odbiera tego znaku (dioda zależna od znaku się nie zapala). A oto kod do odebrania i do wysyłania:

    Wysyłanie:
    
    void USART_Wyslij(unsigned char dane) //procedury wysyłania danych
    {
    	UDR0 = dane; //umieść dane w buforze, wyślij dane
    }	
    
    void USART_Ramka(unsigned char adres, unsigned char dane) //Wysłanie ramki po RS232
    {
      //pakiety
      tx_buf[0]=0x3A; //znak startu
      tx_buf[1]=adres; 
      tx_buf[2]=0x02; //Funkcja - filtrowanie obrazów
      tx_buf[3]=dane; 
      tx_buf[4]=0x44; //znak stop
      //pętla wysyłająca dane zapisane w buforze poprzez uart 
       while (!(UCSR0A & (1 << UDRE0)));
      for(int i=0;i<5;i++)
       {
    	USART_Wyslij(tx_buf[i]); //umieść dane w buforze, wyślij dane
       }
    }
    


    Odbieranie:
    
    ISR(USART_RXC_vect) //Przerwanie od odebrania USART0
    {
    	byte=UDR; //pobranie wartości z rejestru UDR
       
       //sprawdzenie czy wystąpił znak startu ":" - czyli szesnastkowo 0x3A
       if (byte==0x3A)
       {
    		PORTB ^= _BV(1); //włączenie diody gdy odbierze się znak 0x3A
    		bitstart=1; 
    		bitstop=0;
    		licznik=0; //wyzeruj licznik   
       }
       licznik++;
       
       //sprawdzenie czy nie wystapil znak konca ramki: "D" czyli szesnastkowo 0x44
       if (byte==0x44)
       {
    		
    		bitstart=0; 
    		bitstop=1;
    		wykonaj(ramkaodbierz,licznik);  
       }
       
       //inkrementowanie tablicy char ramkaodbierz
       if(bitstart==1)
       {
    		ramkaodbierz[licznik]=byte;
       }
       PORTB ^= _BV(2); //Włączenie diody niezależnie od znaku
    }
    


    A Uarta w obydwu przypadkach deklaruje tak samo (zależnie od rejestrów)
    
    /załączenie UARTA
    void USART_Init(void)
    {
    	//ustaw prędkość transmisji
    	  /* Set baud rate */ 
      UBRRH = (BAUD_PRESCALE >> 8); 
      UBRRL = BAUD_PRESCALE; 
      /* Enable receiver and transmitter */ 
      UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE); 
      /* Set frame format: 8data, 1stop bit */ 
      UCSRC = (1<<URSEL)|(3<<UCSZ0);	
      
    }
    

    O co może chodzić?
  • #2 9115422
    Circuit Chaos
    Poziom 13  
    Funkcja USART_Wyslij() umieszcza znak w buforze i tyle, nie czeka aż on się wyśle.
  • #3 9116768
    sevotharte
    Poziom 10  
    zmienilem funkcje wysylajaca na:

    
    void USART_Wyslij(unsigned char dane) //procedury wysyłania danych 
    { 
      while (!(UCSR0A & (1 << UDRE0)));
       UDR0 = dane; //umieść dane w buforze, wyślij dane 
    }    
    
    void USART_Ramka(unsigned char adres, unsigned char dane) //Wysłanie ramki po RS232 
    { 
      //pakiety 
      tx_buf[0]=0x3A; //znak startu 
      tx_buf[1]=adres; 
      tx_buf[2]=0x02; //Funkcja - filtrowanie obrazów 
      tx_buf[3]=dane; 
      tx_buf[4]=0x44; //znak stop 
      //pętla wysyłająca dane zapisane w buforze poprzez uart  
      for(int i=0;i<5;i++) 
       { 
       USART_Wyslij(tx_buf[i]); //umieść dane w buforze, wyślij dane 
       } 
    }
    


    lecz znowu to nie działa.. delayem spróbować zrobić opóźnienie aż się wszystko powysyła?

    Pozdrawiam
  • #4 9117392
    Andrzej__S
    Poziom 28  
    Cytat:

    ...lecz znowu to nie działa...

    Niemniej uwaga sp5gof była słuszna.
    Cytat:

    delayem spróbować zrobić opóźnienie aż się wszystko powysyła?

    Nie ma takiej potrzeby, jeśli czekasz, aż rejestr UDR będzie pusty (while (!(UCSR0A & (1 << UDRE0)));). Poza tym bajt 0x3A wysyłasz jako pierwszy, więc to nie problem z odstępami czasowymi pomiędzy wysyłaniem poszczególnych bajtów, bo pierwszy powinien być odebrany prawidłowo.

    Nie napisałeś, jak taktujesz oba mikrokontrolery i jaką prędkość transmisji stosujesz.
  • #5 9117737
    sevotharte
    Poziom 10  
    Atmega 162 - 8MHz (bez dzielenia przez 8) prędkość 9600
    Atmega 32 - 8MHz (fuse przestawiony na 8) prędkość 9600

    Oba były sprawdzane za pomocą termianala na PC i przy tej predkości echo wracało takie jak zostało wysłane. Kable są skrosowane.
  • #6 9118007
    Fredy
    Poziom 27  
    Czy masz w odbiorniku odblokowane globalnie przerwania - funkcja Sei ???
  • Pomocny post
    #7 9118041
    Andrzej__S
    Poziom 28  
    Cytat:

    Oba były sprawdzane za pomocą termianala na PC i przy tej predkości echo wracało takie jak zostało wysłane.

    To jeszcze nie musi oznaczać, że wszystko jest OK. Powiedzmy, że w jednym z nich prędkość jest o 2% za niska, a w drugim o 2% za wysoka. Przy transmisji z PC obydwa mieszczą się w tolerancji (zgodnie z datasheet Atmela błąd nie powinien przekraczać 2%). Ale co będzie, kiedy połączysz je między sobą?

    Jeśli używasz wewnętrznego generatora RC, to problem może leżeć w kalibracji. ATmega162 jest fabrycznie skalibrowana na częstotliwość nominalną dla napięcia zasilającego 3V, a ATmega32 - dla napięcia 5V. A Ty, jak podejrzewam, zasilasz obydwa tym samym napięciem.
    Dodatkowo ATmega32 jest kalibrowana osobno dla każdej częstotliwości (1MHz,2MHz,4MHz,8MHz), a ATmega162 ma tylko jeden "Calibration byte" (zdaje się, że dla 1MHz), więc jeżeli ustawiłeś fusebity na częstotliwość inną niż fabryczna 1MHz, to tym bardziej może być problem.
    Proponuję przed szukaniem winy w kodzie spróbować użyć zewnętrznych rezonatorów kwarcowych (najlepiej takich samych), lub ewentualnie pokombinować z ustawieniami rejestru OSCCAL. Być może ta lektura będzie pomocna. Jednak moim zdaniem lepiej spróbować na zewnętrznych rezonatorach kwarcowych, żeby się upewnić, czy to nie wina różnicy częstotliwości wewnętrznych generatorów RC.
    Kod jest dosyć nieskomplikowany i wydaje mi się, że to raczej nie jego wina (oczywiście przy założeniu, że zrobiłeś poprawkę, o której była mowa wcześniej).
  • #8 9118576
    sevotharte
    Poziom 10  
    Chyba popróbuję z zewnętrznymi kwarcami. Bo ta metoda z kalibracją mnie trochę przeraża. Dzięki za wskazówki, jutro się okaże o co chodzi - ale na angielskojęzycznych forach też wskazywali na próbę z zewnętrznymi kwarcami. Pozdrawiam

    P.S.
    Funkcję sei() mam w pętli głównej programu.
    Zrobiłem poprawki ;)
  • #9 9121749
    sevotharte
    Poziom 10  
    Podłączyłem rezonator zewnętrzny pod atmegę 162. Rezonator 8MHz. Fuse zaprogramowałem następująco:

    LOW: 0xFD
    HIGH: 0xD9
    Extended: 0xFF

    Jednak teraz po przetestowaniu poprawności poprzez program echo miedzy PC a tym uC, wystepują krzaczki, tak jakby coś nie bardzo ten kwarc pasował. Ten kwarc jest w porządku czy mniejszy (wiekszy) dac i wtedy jeszcze raz sprobowac? sprawdzalem na 2 kwarcach 8MHz.

    Program echo działa na wewnetrznym oscylatorze, natomiast po podłączeniu kwarcu sa krzaczki.

    Parametry: 9600, data 8 bit, no parity, 1 stop bit
  • Pomocny post
    #10 9121833
    mirekk36
    Poziom 42  
    Zassaj sobie program MkAvrCalculator bo akurat z twoim prockiem będzie działał w pełni za FREE i porządnie ustaw fusebity.

    Wybierz w zakładce "Fusy Uproszczone" kwarc większy 8MHz jeśli podłączasz kwarc 8MHz

    a w tych ustawieniach co podałeś wybrałeś kwarc pomiędzy 3-8MHz
  • #11 9122406
    sevotharte
    Poziom 10  
    zrobilem tak jak mowiles. ustawilem w tym programie MkAvrCalculator na kwarc zewnętrzny większy niż 8. Ale dalej nie odbiera mi tak jakbym tego chciał (błędy są w transmisji). Spróbuję na większym kwarcu, zobaczę co wyjdzie.

    Dodano po 1 [godziny] 25 [minuty]:

    Zrobiłem komunikację, ale przy użyciu innego kwarcu. Przy kwarcu 8MHz nie chciało mi działać, natomiast przy kwarcu 11.0592Mhz śmignęło, aż miło ;). Jakbym miał jeszcze jakieś problemy z komunikacją po RS485 to będę pytał dalej.

    Dzięki wszystkim za odpowiedzi:)

    P.S. <kryptoreklama> Program od firmy Atnel działa cuda;P - </kryptoreklama>

    Dodano po 3 [godziny] 5 [minuty]:

    No to teraz mam pytanie odnośnie już stricte komunikacji po RS485. Podłączyłem atmegi z sobą za pomocą st485bn. Połączyłem wszystko zgodnie z dokumentacją, ale mam następujący problem.

    Steruję odpowiednio kierunkiem przepływu: gdy nóżki 2/3 (RE/DE) są 1 lub 0. Na 1 mierząc miernikiem wyszło mi blisko 5V, natomiast dla 0 wyszło 2V. W dokumentacji podali, że stan wysoki jest od min 2V, a stan niski do max 0.8V. Jak uzyskać mniejsze napięcie na nóżce? poprzez tranzystor? jak to wykonać?

    Pozdrawiam
  • #12 9124568
    mirekk36
    Poziom 42  
    Pokaż lepiej kod programu, bo być może nie ustawiasz pinu jako wyjście albo coś innego źle robisz ;) .... to byłaby jakaś masakra gdyby w celu wystawienia ZERA logicznego na wyjściu dowolnego pinu trzeba było jakiś tranzystor dawać - jak piszesz ;)

    Toż podanie zera i jedynki to podstawa. A u ciebie jest albo błąd w programi albo w połączeniach, że taki dziwoląg ci wychodzi. Może nawet jakieś zwarcie.
  • #13 9125454
    sevotharte
    Poziom 10  
    definiuje je tak:
    
    #define RS485_RE_DE 4
    #define RS485_RE_DE_PORT PORTB
    #define RS485_RE_DE_DDR DDRB
    #define RS485_ENABLE_RX() RS485_RE_DE_PORT &= ~(1 << RS485_RE_DE);
    #define RS485_ENABLE_TX() RS485_RE_DE_PORT |= (1 << RS485_RE_DE);
    


    a później odpowiednio w programie wywołuję je w funkcji do wysyłania lub odbierania:

    
    void USART_Ramka_RS485(unsigned char adres, unsigned char dane) //Wysłanie ramki po RS232
    {
    RS485_ENABLE_TX();
      //pakiety
      tx_buf[0]=0x3A; //znak startu
      tx_buf[1]=adres; 
      tx_buf[2]=0x02; //Funkcja - filtrowanie obrazów
      tx_buf[3]=dane; 
      tx_buf[4]=0x44; //znak stop
      //pętla wysyłająca dane zapisane w buforze poprzez uart 
      
      for(int i=0;i<5;i++)
       {
    	USART_Wyslij_RS485(tx_buf[i]); //umieść dane w buforze, wyślij dane
      }
      while (!(UCSR1A & (1 << UDRE1)));
    RS485_ENABLE_RX();
    }
    


    a funkcja USART_Wyslij to:
    
    void USART_Wyslij_RS485(unsigned char dane) //procedury wysyłania danych
    {
    	while (!(UCSR1A & (1 << UDRE1))); //oczekiwanie na zakończenie nadawania
    	UDR1 = dane; //umieść dane w buforze, wyślij dane
    }
    


    Oczywiscie Slave jest w petli głównej programu ustawiony na słuchanie czyli:
    RS485_ENABLE_RX();

    wydaje mi się, że jest to dobrze podłączone, bo układ reaguje na te zmiany wyjść (gdy przechodzi w stan wysyłania to na pinie RE,DE jest 5V).
    Więc co może być nie tak?
  • #15 9125565
    sevotharte
    Poziom 10  
    Pin RE i DE są zwarte z sobą i podane do jednej nóżki uC. Więc chyba ta definicja ustawia Pin 4 na porcie B na wyjście?;)

    P.S.
    chyba że lepiej sterować nimi osobno. Nie mam jeszcze doświadczenia, więc próbuję to co zaobserwowałem u innych osób:)
  • #16 9126380
    mirekk36
    Poziom 42  
    Oczywiście, że mogą być zwarte ze sobą i podłączone do jednej nogi (pinu) procka.

    Ale pokaż mi w kodzie, tym który wyżej przedstawiłeś gdzie wg ciebie jest ustawienie tego pinu procka jako wyjście ????

    Gdzie ty to widzisz??? no chyba, że ja taki ślepy jestem i patrzę i patrzę i zobaczyć nie mogę ;)
  • #17 9126699
    Andrzej__S
    Poziom 28  
    sevotharte napisał:

    Pin RE i DE są zwarte z sobą i podane do jednej nóżki uC. Więc chyba ta definicja ustawia Pin 4 na porcie B na wyjście?


    Mam wrażenie, ze nie rozumiesz. "Podanie" pinów RE i DE do jednej nóżki mikrokontrolera nie konfiguruje jej jako wyjście.

    Mirek36 usiłuje Ci powiedzieć, że na początku funkcji main() przed pętlą nieskończoną powinieneś mieć instrukcję ustawiającą pin 4 portu B jako wyjście, coś w stylu:
    
    RS485_RE_DE_DDR |= (1<<RS485_RE_DE);
    

    zgodnie z Twoimi definicjami, lub alternatywnie:
    
    DDRB |= (1<<4);
    

    Jeśli tego nie masz, pin 4 portu B jest skonfigurowany po "power on reset" jako wejście. W takiej sytuacji po ustawieniu:
    
    RS485_RE_DE_PORT |= (1 << RS485_RE_DE);
    

    włączasz w mikrokontrolerze wewnętrzny rezystor pull-up i wtedy na wejściu RE/DE MAX485 masz stan wysoki (przez ten wewnętrzny rezystor). Jednak ustawienie portu:
    
    RS485_RE_DE_PORT &= ~(1 << RS485_RE_DE);
    

    w sytuacji, kiedy pin 4 portu B jest ustawiony jako wejście, nie daje stanu niskiego, tylko ustawia pin w stan wysokiej impedancji. Stąd prawdopodobnie to "dziwne" napięcie 2V.

    sevotharte napisał:

    P.S.
    chyba że lepiej sterować nimi osobno

    To nie ma znaczenia. Tak czy inaczej musisz programowo ustawić piny jako wyjścia (tym razem musiałbyś ustawić 2 piny).
REKLAMA