Elektroda.pl
Elektroda.pl
X
Relpol przekaźniki
Proszę, dodaj wyjątek dla www.elektroda.pl do Adblock.
Dzięki temu, że oglądasz reklamy, wspierasz portal i użytkowników.

[ATmega][USART][C] Problem z transmisją RS232

28 Lip 2010 11:44 5711 15
  • Poziom 10  
    Witam,

    od kilku dni walczę z tą nieszczęsną transmisją szeregową. Uprościłem kod niemalże maksymalnie. Jedynie odbiór znaku z terminala i odesłanie go. Aczkolwiek wciąż coś jest nie tak. Mianowicie powracający znak do terminala nie pokrywa się z wysłanym. Z analizy binarnej wygląda mniej więcej na coś takiego, że podczas odbioru znaku przez mikrokontroler jego postać binarna zostaje zanegowana oraz przesunięta o jeden w prawo, a następnie podczas wysyłania ponownie zanegowana i przesunięta o jeden w prawo i zostaje dodana jedynka na LSB. Dla niektorych znaków zostaje dodana dwójka lub trójka. A czasami podczas przesyłu do terminala przesunięcie bitowe wynosi dwa lub trzy.

    Czy ktoś kiedykolwiek spotkał się z takim problemem i może wie w czym tkwi problem??? Bo ja zaraz osiwieje od rozgryzania tego :/

    Korzystam z:

    IC: ATmega32
    Boud: 9600
    F_CPU 16MHz
    przejściówka USB-RS Prolific TTL, bezpośrednio podpięta pod piny ATmegi32
    płytka testowa ZL3AVR

    Code:

    #define F_CPU 16000000UL

    #include <stdio.h>
    #include <avr/io.h>
    #include <util/delay.h>

    // zmienne globalne
    volatile uint8_t znak;
    int wyslano = 0;

    // inicjalizacja USART dla podanego BAUD
    void USART_Init(unsigned long int baud)
    {
       UBRRH = (unsigned char)((F_CPU/16UL/baud-1)>>8);
       UBRRL = (unsigned char)(F_CPU/16UL/baud-1);
       UCSRB |= _BV(RXEN) |_BV(TXEN);  //aktywacja odbiornika
       UCSRC |= _BV(URSEL)|_BV(UCSZ1)|_BV(UCSZ0); //transmisja 8 bitów i 1 bit stopu
    }

    /////////////// PROGRAM GŁÓWNY //////////////////
    void main(void)
    {
       USART_Init(9600); //ustawienie prędkości transmisji

       while(1)
       {
          if(bit_is_set(UCSRA,RXC))
           {
                znak=UDR; // wartość z RX do zmiennej a
               wyslano = 0;      
           }
                
          // Wait if a byte is being transmitted
          if(bit_is_set(UCSRA,UDRE))
          {
             if (wyslano == 0)
             {
                UDR = znak;
                wyslano = 1;
             }
          }
       }
    }
  • Relpol przekaźniki
  • Poziom 21  
    Chyba to połączenie którego użyłeś nie jest dobre, napisałeś że prosto z USB - RS232 Profilica podpiąłeś do portów procka. Pamiętaj że no może na takiej przejściówce i jest 5V ale spójrz jak jest to od strony RS232 jak są zakodowane sygnały. Procek Ci nie zdekoduje ujemnych napięć może tu leży twój problem. Jakiś max 232 by się przydał lub podobne rozwiązanie.

    Odsyłam do standardu RS232.

    Link

    Pozdrawiam
  • Poziom 35  
    Coś mi tu taj nie pasuje
    Code:

       UBRRH = (unsigned char)((F_CPU/16UL/baud-1)>>8);
       UBRRL = (unsigned char)(F_CPU/16UL/baud-1);


    raczej powinno być tak:
    Code:

       UBRRH = (unsigned char)(((F_CPU/(16*baud))-1)>>8);
       UBRRL = (unsigned char)((F_CPU/(16*baud))-1);


    a dla pewności ja robię to tak:

    Code:

    #define F_CPU 1000000UL  // 1 MHz
    #define PARITY_NONE 0
    #define PARITY_EVEN (1<<UPM1)
    #define PARITY_ODD ((1<<UPM1)|(1<<UPM0))
    #define STOP_BITS_1 0
    #define STOP_BITS_2 (1<<USBS)
    #define DATA_BITS_5 0
    #define DATA_BITS_6 (1<<UCSZ0)
    #define DATA_BITS_7 (1<<UCSZ1)
    #define DATA_BITS_8 ((1<<UCSZ1)|(1<<UCSZ0))
    #define DATA_BITS_9 ((1<<UCSZ2)|(1<<UCSZ1)|(1<<UCSZ0))
    #define UART_BAUD 4800UL
    #define _UBBR_ ((F_CPU/(16*UART_BAUD))-1)

    void USART_Init( unsigned int baud_reg )
    {
       UBRRH = (unsigned char)(baud_reg>>8);
       UBRRL = (unsigned char)baud_reg;
       UCSRB = (1 << RXCIE) | (1 << RXEN) | (1 << TXEN);
       UCSRC = (1 << URSEL) | DATA_BITS_8 | PARITY_NONE | STOP_BITS_1;
    }

    void USART_Transmit( unsigned char data )
    {
       while ( !( UCSRA & (1<<UDRE)) );
       UDR = data;
    }


    SIGNAL (SIG_UART_RECV)
    {
       unsigned char rxchar;

       rxchar = UDR;
       USART_Transmit(rxchar);
    }

    int main (void)
    {
       //konfiguracja RS'a 232
       USART_Init(_UBBR_);
       sei();

       while(1);
    }

  • Poziom 10  
    Samuraj napisał:
    Coś mi tu taj nie pasuje
    Code:

       UBRRH = (unsigned char)((F_CPU/16UL/baud-1)>>8);
       UBRRL = (unsigned char)(F_CPU/16UL/baud-1);


    raczej powinno być tak:
    Code:

       UBRRH = (unsigned char)(((F_CPU/(16*baud))-1)>>8);
       UBRRL = (unsigned char)((F_CPU/(16*baud))-1);



    wydawać by się mogło, że biorąc pod uwagę kolejność działań to to samo :):)

    aaaleee...wrzuciłem na żywca Twój kod i mam identyczny rezultat jak na swoim :/ ....u Ciebie na pewno dobrze działa? Jeśli tak to faktycznie coś nie tak może jest z moją przejściówką...

    co do max232 to przedtem miałem dwa takie układy bowiem korzystam z plytki testowej wyposażonej już w max'a więc do swojej przejściówki też dodałem, żeby podnieść z TTL na napięcia RS i takie same rezultaty były.
  • Poziom 35  
    To dla próby zewrzyj RX z TX na wyjściu przejściówki. Powinno powracać echo tego co wysłałeś - będziesz miał pewność co do przejściówki.
  • Relpol przekaźniki
  • Poziom 10  
    właśnie w tym sęk, że po zwarciu końcówek jest echo :D
    hmm...właśnie sprawdziłem na normalnym RS'ie w stancjonarnym kompie i tam faktycznie w terminalu wyswietlane sa te znaki, które zostały wciśnięte na klawiaturze. W związku z tym przez chwile myślałem, że źle podłączyłem jednego maxa, bo skoro one obracają napięcia z ujemnych na dodatnie to myślałem, że może dlatego negowały znaki. Jednak przecież podłączałem też bezpośrednio przejściówke do ATmegi i było to samo. Kilka dni tak nad tym siedze, i jakbym nie sprawdził na stacjonarnym kompie to dałej bym siedział. Chociaż dobrze by było, gdyby i na laptopie zaczęło działać..... może trzeba zainwestować w jakąś pożądniejszą przejściówkę????
  • Poziom 35  
    A zegar masz ustawiony prawidłowo ??
    W programie deklarujesz 16MHz tylko czy ATMega chodzi na takim zegarze. Może masz przestawione fusebity i chodzi na wewnętrznym generatorze.
  • Poziom 21  
    Witaj,

    skoro warstwę sprzętową masz ok to proponuję sie przyjrzeć samemu programowi.

    Zrobił bym to tak.

    1. Wyślij z PC-ta wartość 0x5A

    2. Odbierz tą wartość w Atmedze i zrób sobie instrukcję warunkową np.

    if(odebrany_bajt == 0x5A)
    ustaw_pin = 1; (jakiś wolny pin procka)
    else
    ustaw_pin = 0;

    Powyższa procedura pozwoli Ci stwierdzić czy bajt odbierasz poprawnie.

    3. Na sztywno wpisz samo wysyłanie z atmegi do terminala bajtu 0x5A.

    4. Sprawdź co odbiera terminal.

    Jeśli chcesz być bardziej pewnym co dokładnie do pc-ka przychodzi bo np. ten serial terminal jest taki sobie (na myśli mam software) spróbuj zainstalować sobie jakiegoś sinfferka - serial port monitor jakąś darmówkę.

    Zobaczymy po czymś taki co się będzie działo

    Pozdrawiam
  • Poziom 10  
    siedze, kombinuje, odpinam, przypinam kabelki...i ciągle jakieś cuda niewidy....przed chwilą sprawdziłem na kompie stacjonarnym i zaczęło śmigać elegancko, a zdaje się, że i wcześniej było identycznie połączone wszystko.

    Co najciekawsze....i tu popisałem się jako elektronik :P ....otóż moja płytka testowa wyposażona jest w MAX232 i cały czas byłem przekonany, że przejściówka ma wyjście TTL więc albo podpinałem ją bezpośrednio pod piny ATmegi albo dawałem jeszcze jednego max'a. Przed chwilą z braku pomysłów podpiąłem pod wejście RS na płytce i...zadziałało. Wynika ztego, że przejściówka jest niezła a wcześniejsze połączenia powodowały w obu przypadkach wystąpienie nieparzystej liczby maxów i występowała dodatkowa konwersja napięć z dodatnich na ujemne czyli ta nieszczęsna negacja bitów :/. Chociaż przez niemalże przezroczystą obudowę niedopatrzylem się w niej wcześniej max'a i przez to całe zamieszanie i stracony czas :/

    Dziękuję wszystkim za pomoc!!! :)
  • Poziom 10  
    Witam. Mam podobny problem i nie moge sobie z nim poradzic. Atmega ma wysylac dane jezeli dwa odpowiednie piny zostana zwarte do masy. Transmisja odbywa sie ale na terminalu odbieram rozne dziwne znaczki i nie mam pojecia o co chodzi. Do atmegi nie mam podpietego zadnego kwarcu. Oto moj program
    #include <avr/io.h>
    #include <util/delay.h>

    #define p0 0x01
    #define p1 0x02
    #define p2 0x04
    #define p3 0x08
    #define p4 0x10
    #define p5 0x20
    #define p6 0x30

    /*****************************************************************************************/

    #define F_CPU 1000000 // tu definiujesz wartość uzywanego kwarcu

    void InitUSART( unsigned long int baud ) // baud - czyli bitrate portu COM
    {

    UBRRH = (unsigned char)(((F_CPU/(16UL*baud))-1)>>8);
    UBRRL = (unsigned char)((F_CPU/(16UL*baud))-1);
    // Otwarty kanal odbioru i nadawania
    UCSRB = (1<<RXEN)|(1<<TXEN)|(1<<RXCIE);
    UCSRC = (1<<URSEL)|(1<<USBS)|(1<<UCSZ1)|(1<<UCSZ0); // niby jeden bit stopu
    }

    unsigned char OdbiorUSART( void ) // funkcja odbioru bajtu z RS232
    {
    while ( !(UCSRA & (1<<RXC)) );
    return UDR;
    }

    void WyslijUSART( unsigned char data ) // funkcja wysłania bajtu po RS232
    {
    while ( !( UCSRA & (1<<UDRE)) );
    UDR = data;
    }
    int main(void)
    {
    DDRC = p4+p5; // port c jako wwyjscie pinc_1 i pinc_2
    PORTC = 0x00; // portc ma zero na wyjsciu. PC_0 dioda zielona oznacza POLO, PC_1 dioda czerwona oznacza SKODA
    DDRB = 0x00 ; // PORT_B jako wejscie, czyli wszystkie piny portu B
    PORTB = p0+p1+p2; // port pb0 ,pb1,pb2 podciagniecie do vcc pb0 drukuj naklejke, pb1 polo, pb2 skoda, sterowanie masa
    InitUSART(9600);
    int i = 0;
    while (1)
    {

    if (!(PINB & p0 )) //jezeli pb0 bedzie rożne od 1 odbedzie sie transmisja czyli drukowanie naklejki
    {

    if (!(PINB & p1 )) // jezeli warunek spelniony czyli jest masa wysylamy dane o Polo
    {
    if (i==0 ) {
    PORTC |= p4; // zapalamy diode POLO czerwona
    WyslijUSART(0x50); //wysylamy napisik polo
    WyslijUSART(0x4f);
    WyslijUSART(0x4c);
    WyslijUSART(0x4f);
    i=1;}

    }
    if (!(PINB & p2))
    {
    if ( i==0 ) {
    PORTC |= p5; // zapalamy diode skoda zielona
    WyslijUSART(0x53); // wysylamy napis skoda
    WyslijUSART(0x4b);
    WyslijUSART(0x4f);
    WyslijUSART(0x44);
    WyslijUSART(0x41);
    i=1; }
    //dodac trzeba znak konca transmisji czyli 0x17
    }
    do
    {
    // ta petla czeka aż zniknie sygnal drukuj naklejke PINB p0 ma miec vcc
    _delay_ms(500);
    if (PINB & p0) {_delay_ms(100); if (PINB & p0 ) i=0; } // upewniamy sie ze PINB p0 ma vcc
    } while( i == 1);
    // koniec transmisji gasimy diody
    if (PINC & p4) PORTC &=~p4;
    if (PINC & p5) PORTC &=~p5;

    }


    }
    }

    A i uzywam przejsciowki rs232 com. po zwarciu rxd i txd w przejsciowce dosteje echo w terminalu. Prosze o ewentualne uwagi i pomoc.
  • Poziom 10  
    witam po czesci rozwiazalem problem, a mianowicie przy konfiguracji portu na 2400 czyili InitUsart(2400) i odpowiedniej konfiguracji terminala na 2400 transmisja odbywa sie prawidlowo. Ale jak trylko zwieksze predkosc na 9600 odbieram calkiem inne znaczki. Czy to wina ze nie mam podpietego zewnetrznego kwarcu ? Czy czegos innego ?
  • Poziom 38  
    że tak się zapytam, a na te 9600 zmieniasz w programie mikrokontrolera i w terminalu ?
  • Użytkownik usunął konto  
  • Poziom 27  
    _marek napisał:
    spirit1111 napisał:
    . Czy to wina ze nie mam podpietego zewnetrznego kwarcu ?


    Tak ale o odpowiedniej częstotliwości. Datasheet dla twojego procka zawiera tabelkę błędów z jakimi komunikujesz się po usart dla różnych prędkości zależnie od częstotliwości procka. Jednym z magicznych kwarców jest np 11.0592 który pozwoli tobie na pracę aż po 115200 z bodajże zerowym błędem .

    Nie przesadzaj kolego z tym magicznym kwarcem, na 16 MHz osiągałem poprawną transmisję z prędkością 115200 bodów PC-Atmega ale warunkiem jest poprawna konsiguracja procka i prawidłowe dopasowanie napięć pomimo ok 3 % niedopasowania.
  • Poziom 10  
    Witam. Dziekuje za pomoc transmisja na 9600 bodow rozwiazana. Okazalo sie ze procesor dziala na wewnetrznym rezonatorze 1mhz i po przestawieniu na 8MHz wszystko jest ok.
    Natomiast mam jeszcze jeden problem a mianowicie chodzi o to ze PB2 i PB1 i PB0 standardowo w programie sa wejsciem z wewnetrznym pull-up czyli maja napiecie. Stan tych pinow zmienia sie podajac na nie mase z zasilania atmegi. (Chyba ze zle sie nauczylem, ale bynajmniej tak dziala) i teraz chcialem wysylac przez rs232 do komputera napis gdy stany pinow pb0 i pb1 beda zmienione a inny napis gdy pb0 i pb2 sie zmienia. Wszystko dziala gdy podaje mase z ukladu atmegi natomiast ja potrzebuje użyć cos w stylu przekaźnika zwierno-rozwiernego 12V. Po podaniu zasilania do przekaznika przelacza on styk z pinu 30 na pin 87. Do pinu 30 mam podpiety PB0, mase atmegi podpieta jest do pinu 87. I aby program wyslal napis potrzeba zmienic stan pinu PB1 badz PB2 i podac zasilanie na przekaznik. Program poprawnie wysyla napis, ale co jakis czas sie resetuje atmega ( wiem ze sie resetuje bo napis ten jest rowniez licznikiem i gdy sie zresetuje zaczyna liczyc na nowo. Nie mialem tego problemu gdy nie mialem podpietego przekaznika. Prosze o pomoc jak mozna inaczej rozwiazac moj problem. Ponizej przedstawiam kod programu i schemat podlaczenia .
    Code:
    #include <avr/io.h> 
    
    #include <util/delay.h>   
    #include <stdlib.h>             

    #define p0 0x01
    #define p1 0x02
    #define p2 0x04
    #define p3 0x08
    #define p4 0x10
    #define p5 0x20
    #define p6 0x30
    #define F_CPU 8000000
       
    void InitUSART( unsigned long int baud ) // baud - czyli bitrate portu COM
    {
       
       UBRRH = (unsigned char)(((F_CPU/(16UL*baud))-1)>>8);
       UBRRL = (unsigned char)((F_CPU/(16UL*baud))-1);
       // Otwarty kanal odbioru i nadawania
       UCSRB = (1<<RXEN)|(1<<TXEN);
       UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0); // Set frame format: 8data, 2stop bit 
    }

    unsigned char OdbiorUSART( void )  // funkcja odbioru bajtu z RS232
    {
       while ( !(UCSRA & (1<<RXC)) );
       return UDR;
    }

    void WyslijUSART( unsigned char data )  // funkcja wysłania bajtu po RS232
    {
       while ( !( UCSRA & (1<<UDRE)) );
       UDR = data;
    }
    // Write usart 2 

    void TransmitSTRING(char *text) // funkcja wyslania tekstu
       {
       while(*text)
          {
          while (!( UCSRA & (1<<UDRE)));
             UDR = *text;
          text++;
          }
       }
       
    int main(void)
    {
       
       DDRC = p4+p5;      
       PORTC = 0x00; // podlaczane diody
       DDRB = 0x00   ;      // PORT_B jako wejscie,    PORTB = 0xFF;   // port pb0 ,pb1,pb2 podciagniecie do vcc  pb0    unsigned int i = 0;
       unsigned int licznik_polo=0;
       unsigned int licznik_skoda=0;
       char wynik_licznika[5]={'0','0','0','0'};
       InitUSART(9600);
                   
    while (1)
       {
       
          if (!(PINB & p0 )) //jezeli pb0 bedzie rożne od 1 odbedzie sie transmisja
          {
             
             if (!(PINB & p3 )) // jezeli warunek spelniony czyli jest masa wysylamy dane napis 1         {
                if (i==0 ) {
                PORTC |= p4; // zapalamy diode na znak ze to dziala
                licznik_polo++;
                itoa(licznik_polo,wynik_licznika,10);  // zaminia liczbe int na string
                WyslijUSART(0x50);  //wysylamy napisik
                WyslijUSART(0x4f);
                WyslijUSART(0x4c);
                WyslijUSART(0x4f);
                WyslijUSART(0x20);
                TransmitSTRING(wynik_licznika);
                WyslijUSART(0x20);
                i=1;}
                
             }
             if (!(PINB & p4))
             {
                if ( i==0 ) {
                PORTC |= p5; // zapalamy diode
                licznik_skoda++;
                itoa(licznik_skoda,wynik_licznika,10);  // zaminia liczbe int na string
                WyslijUSART(0x53);  // wysylamy napis
                WyslijUSART(0x4b);
                WyslijUSART(0x4f);
                WyslijUSART(0x44);
                WyslijUSART(0x41);
                WyslijUSART(0x20);
                TransmitSTRING(wynik_licznika);
                WyslijUSART(0x20);
                i=1;      }
             }
             do
             {
                // ta petla czeka aż zniknie sygnal PINB p0 ma miec vcc poniewaz dane maja byc wyslane tylko raz po zmianie stanu
                _delay_ms(500);
                if (PINB & p0) {_delay_ms(100); if (PINB & p0 ) i=0; } // upewniamy sie ze PINB p0 ma vcc
                } while( i == 1);
                // koniec transmisji gasimy diody
                if (PINC & p4) PORTC &=~p4;
                if (PINC & p5) PORTC &=~p5;
             
          }
          
       
       }
    }

    a oto schemat

    Dodano po 26 [minuty]:

    Przepraszam ze tak niedbale, ale nie mam zadnedo programu do malowania schematow oprocz painta.[img][ATmega][USART][C] Problem z transmisją RS232 [/img]
    [ATmega][USART][C] Problem z transmisją RS232
  • Poziom 10  
    ten KOD bez błędów raczej i kompiluje się
    Kod: c
    Zaloguj się, aby zobaczyć kod