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

[ATmega8][C] Problem z odbieraniem pojedynczego znaku

krizq1 27 Lut 2011 17:53 2427 28
REKLAMA
  • #1 9212331
    krizq1
    Poziom 10  
    Witam. Mam wydaje sie prosty problem z odbieraniem pojedyńczego znaku przez UART z wykorzystaniem RS232. Chodzi o to, że chcę, aby program odpowiadał odpowiedzią "OK" tylko w przypadku gdy wyślę pojedyńczy znak (w tym przypadku '1'). Problem polega na tym, że gdy wysyłam '11' to otrzymuje dwie odpowiedzi, gdy '111' to trzy odpowiedzi itp... Chcę aby odpowiadał tylko w przypadku wysłania z terminala pojedynczej 1.
    Oto fragment kodu:


    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    Z góry dziękuję za pomoc
  • REKLAMA
  • #3 9212436
    krizq1
    Poziom 10  
    Niestety to nic nie daje, wydaje mi się, że problem tkwi w nawiasie if'a, ale nie wiem jak do tego podejść...
  • #4 9212439
    krru
    Poziom 33  
    krizq1 napisał:
    Witam. Mam wydaje sie prosty problem z odbieraniem pojedyńczego znaku przez UART z wykorzystaniem RS232. Chodzi o to, że chcę, aby program odpowiadał odpowiedzią "OK" tylko w przypadku gdy wyślę pojedyńczy znak (w tym przypadku '1'). Problem polega na tym, że gdy wysyłam '11' to otrzymuje dwie odpowiedzi, gdy '111' to trzy odpowiedzi itp... Chcę aby odpowiadał tylko w przypadku wysłania z terminala pojedynczej

    Z góry dziękuję za pomoc


    A możesz sprecyzować co ma się dziać jak przyjdą dwie '1'? Jaka ma być reakcja na inne znaki?
  • #5 9212459
    Karol966
    Poziom 31  
    
    char znak;
    while(1)
    {
    znak=USART_Recieve();
    if(znak=='1') printf("OK\n");
    } 
    

    lub
    
    char znak;
    while(1)
    {
    znak=USART_Recieve();
    if(znak=='1')
    {
         printf("OK\n");
    }
    } 
    
    


    Dodano po 42 [sekundy]:

    Chociaż to opóźnienie po tym if'ie i tak bym wstawił.
  • #6 9212472
    krizq1
    Poziom 10  
    Nic nie ma się dziać, brak odpowiedzi... ewentualnie inna odpowiedz typu "unkown command",ale z tym to już sobie poradzę. Dodam jeszcze, że gdy wpisuje np. 21 badz jakakolwiek inną liczbę zawierającą w sobie 1 (np. 2231), to program odpowiada OK-ejem.
  • REKLAMA
  • #7 9212485
    tadzik85
    Poziom 38  
    po odebraniu 1 ustaw flage, gdy flaga jest ustawiona nie wysylaj ok gdy przyjdzie co innego wyzeruj flage
  • #8 9212502
    Karol966
    Poziom 31  
    Wg mnie program nie mając nic innego do roboty odbiera kolejno cyfry np 2,2,3,1 a nie całą liczbę - jeżeli chcesz całą liczbę odebrać to użyj bufora cyklicznego i w dodatku czekaj po każdym odebranym bajcie na ew kolejny bajt przez okres czasu nieco większy niż czas transmisji jednego pełnego bajtu danych.

    Dodano po 1 [minuty]:

    tadzik85 napisał:
    po odebraniu 1 ustaw flage, gdy flaga jest ustawiona nie wysylaj ok gdy przyjdzie co innego wyzeruj flage


    Chcąc wysyłać "ok" np co 5 sekund tym sposobem nie będzie to możliwe.
  • #9 9212526
    tadzik85
    Poziom 38  
    Karol966 napisał:

    tadzik85 napisał:
    po odebraniu 1 ustaw flage, gdy flaga jest ustawiona nie wysylaj ok gdy przyjdzie co innego wyzeruj flage


    Chcąc wysyłać "ok" np co 5 sekund tym sposobem nie będzie to możliwe.


    Nie rozumiesz problemu. W ciągu znaków masz wykryć tylko pojedyncze '1' a nie odpowiadać na każdą.
  • REKLAMA
  • #10 9212567
    Karol966
    Poziom 31  
    tadzik85 napisał:
    Karol966 napisał:

    tadzik85 napisał:
    po odebraniu 1 ustaw flage, gdy flaga jest ustawiona nie wysylaj ok gdy przyjdzie co innego wyzeruj flage


    Chcąc wysyłać "ok" np co 5 sekund tym sposobem nie będzie to możliwe.


    Nie rozumiesz problemu. W ciągu znaków masz wykryć tylko pojedyncze '1' a nie odpowiadać na każdą.


    Rozumiem problem. Jak rozróżnisz ciąg znaków od pojedyńczego znaku? No i druga sprawa to o czym już pisałem, sytuacja wgląda np tak, był ciąg znaków 231231 - efektem było wysłanie jednej odpowiedzi. Dalej przez 2 tygodnie :P nic się nie działo i użytkownik wysłał do procesora rozkaz "1" aby otrzymać odpowiedz na pytanie, czy np czy temperatura przekroczyła bezpieczną wartość a procesor spokojnie nic nie odpowie ;)
  • #11 9212570
    Andrzej__S
    Poziom 28  
    Procesor musi jakoś zdeterminować koniec transmisji. Ja to widzę tak:
    Ustalamy jakiś TIMEOUT.
    1. Odbieramy znaki do bufora (musi być odpowiednio duży w zależności od maksymalnej długości transmisji).
    2. Pomiędzy odbieranymi znakami mierzymy czas.
    3. Jeśli czas będzie większy od stałej TIMEOUT, uznajemy, że to koniec transmisji i analizujemy zawartość bufora, po czym go czyścimy.
    4. Wracamy do punktu 1.

    Innym, moim zdaniem lepszym sposobem jest wysyłanie jakiegoś delimitera, który by określał koniec transmisji, jak choćby '\0' lub '\n'. Takie rozwiązanie byłoby szybsze, bo pozwoliłoby natychmiast ocenić, czy transmisja jest już zakończona, czy PC ma zamiar wysłać jeszcze jakieś znaki należące to tego samego ciągu/zestawu.
  • REKLAMA
  • #12 9212588
    Karol966
    Poziom 31  
    Andrzej__S napisał:
    Procesor musi jakoś zdeterminować koniec transmisji. Ja to widzę tak:
    Ustalamy jakiś TIMEOUT.
    1. Odbieramy znaki do bufora (musi być odpowiednio duży w zależności od maksymalnej długości transmisji).
    2. Pomiędzy odbieranymi znakami mierzymy czas.
    3. Jeśli czas będzie większy od stałej TIMEOUT, uznajemy, że to koniec transmisji i analizujemy zawartość bufora, po czym go czyścimy.
    4. Wracamy do punktu 1.

    To samo miałem na myśli pisząc trochę wyżej;)
  • #13 9212592
    tadzik85
    Poziom 38  
    Ale dopowiadacie kto tu mówi o jakiejś transmisji czy paczkach danych?
    Ale autor nie się wypowie.

    Najlepiej niech użyje bufora 3 elementowego i sprawdza zawartość na tylko jedna jedynkę.
  • #14 9212651
    krizq1
    Poziom 10  
    Dzięki wszystkim za odpowiedzi, ale szczerze mówiąc jestem dość początkujący i jeszcze mam problemy z używaniem buforów. Łatwiej by mi było to zrozumieć, jeżeli pokazalibyście jak to zrobić w samym kodzie.. Będę wdzięczny jeśli ktoś się pofatyguje.
  • #15 9212653
    Andrzej__S
    Poziom 28  
    krizq1 napisał:

    Dodam jeszcze, że gdy wpisuje np. 21 badz jakakolwiek inną liczbę zawierającą w sobie 1 (np. 2231), to program odpowiada OK-ejem.

    Nie dopisałeś jednak, czy to zachowanie przez Ciebie oczekiwane, czy też nie. Sprecyzuj, kiedy procesor ma odpowiedzieć, a kiedy nie.

    krizq1 napisał:

    Problem polega na tym, że gdy wysyłam '11' to otrzymuje dwie odpowiedzi, gdy '111'....
    Chodzi o to, że chcę, aby program odpowiadał odpowiedzią "OK" tylko w przypadku gdy wyślę pojedyńczy znak...

    tadzik85 napisał:

    Ale dopowiadacie kto tu mówi o jakiejś transmisji czy paczkach danych?

    No na przykład trzy jedynki to już jakaś paczka. Jak ocenisz, czy to 3 "pojedyncze znaki", czy ciąg składający się z trzech jedynek?
  • #16 9212668
    krizq1
    Poziom 10  
    Andrzej, procesor ma odpowiadać tylko jak wpiszę 1 po czym nacisnę ENTER. Jak wpiszę 11 i nacisnę ENTER to ma nie odpowiadać.
  • #17 9212689
    tadzik85
    Poziom 38  
    obierasz dane do bufora 3 elementowego po odebraniu enter sprawdza czy 1element !=1 2 element =1. po sprawdzeniu zerujesz bufor
  • #18 9212777
    Andrzej__S
    Poziom 28  
    Cytat:

    Andrzej, procesor ma odpowiadać tylko jak wpiszę 1 po czym nacisnę ENTER. Jak wpiszę 11 i nacisnę ENTER to ma nie odpowiadać.


    Nie wiem, z jakiego terminala korzystasz. Przykładowo Bray Terminal pozwala na zaznaczenie opcji "CR" obok pola, do którego wpisujesz znaki do wysłania. Jeśli zaznaczysz tę opcję, po każdym wciśnięciu ENTER na końcu przesyłanego ciągu znaków dołączony zostaje znak 0x0D. Wtedy po stronie mikroprocesora odczytujesz znaki z USART'a i zapisujesz je do bufora, dopóki nie odbierzesz znaku 0x0D. Odebranie tego znaku oznacza koniec transmisji. Odczytujesz wtedy bufor, analizujesz jego zawartość, wykonujesz odpowiednie akcje w zależności od zawartości, zerujesz całą jego zawartość i czekasz na następną transmisję.

    tadzik85 napisał:

    ...po odebraniu enter...

    ...pod warunkiem, że zostanie wysłany "ENTER". Niekoniecznie znak końca linii musi być wysyłany przez terminal.
  • #19 9212826
    krizq1
    Poziom 10  
    tadzik, właśnie w ten sposób próbowałem to robić, tylko mam problem z zapisaniem tego... Rozumiem, że zmienną znak zapisuję: char znak[2]; tak? Nie wiem jak zapisać sprawdzanie tych elementów, napiszcie kawałek kodu jeśli to nie problem albo podajcie jakas www gdzie jest to dobrze wytłumaczone.. szukałem na googlach, ale na marne :( Walcze dalej...
  • #20 9212858
    tadzik85
    Poziom 38  
    Mniej więcej tak to widzę:
    temp=get_uart();
    znak[0]=znak[1];
    znak[1]=znak[2];
    znak[2]=temp;
    if(temp == 0x0d) //0x0d to enter
    {
       if(znak[1]=='1' & znak[0]!=1)
       {
              print(OK);
       }
        znak[0]=0;
        znak[1]=0;
        znak[2]=0;
    }
  • #21 9213070
    Andrzej__S
    Poziom 28  
    
    #include <avr/io.h>
    #include <string.h>
    
    #define MAX_LENGTH   10 // tu wpisujesz maksymalną długość ciągu
                           // jaki będziesz przesyłał
    
    int main(void)
    {
    char znak;
    char bufor[MAX LENGTH];
    unsigned char i=0;
    
       // inicjalizacja USART'a itp.
       ......
       while(1)
       {
          znak=USART_Recieve(); 
          if(znak==0x0D)
          {
          // akcję wykonujemy tylko, gdy przesłany został jeden znak
             if(strlen(bufor)==1)
             {
          // "OK" wysyłamy, jeśli odebrany, jedyny znak to '1'
                if(bufor[0]=='1') printf("OK\n");
             }
          // zerowanie bufora
             for(i=0;i<MAX_LENGTH;i++)
                bufor[i]=0x00;
             i = 0;
          } /* if(znak==0x0D) */
          else /* if(znak!=0x0D) */
             bufor[i++] = znak;
       }
    }
    

    Powinno zadziałać pod warunkiem, że przesyłany na końcu ciągu terminator transmisji (delimiter) to będzie jeden znak 0x0D. Czasami terminale mogą dołączać dwa znaki CR+LF, wtedy kod będzie wymagał modyfikacji.
    Jakie znaki wysyła terminal można sprawdzić przełączając się w tryb hex i stworzyć pętlę tzw. loopback, by terminal wyświetlił to co wysyła.
  • #22 9213186
    krizq1
    Poziom 10  
    Dzięki bardzo, ale niestety nie działa... Procesor w ogóle nie odpowiada. Tak to wygląda w moim kodzie:

    
    char znak[2];
    char temp;
    	
    while(1)
    {
            
    temp=USART_Recieve();
    		
    znak[0]=znak[1]; 
    znak[1]=znak[2]; 
    znak[2]=temp; 
    
    if(temp == 0x0d) //0x0d to enter 
    { 
    		
       if(znak[1]=='1' & znak[0]!=1) 
       { 
       printf("OK\n");
       _delay_ms(200);
       }
    
    znak[0]=0; 
    znak[1]=0; 
    znak[2]=0;
    }
    }	
    

    Próbowałem też z '1' (z apostrofami) w drugim członie drugiego if-a oraz z dwoma && ale nadal kompletny brak odpowiedzi...:(


    Andrzej, zaraz przetestuje Twój kod i dam znać!

    Niestety też brak jakiejkolwiek odpowiedzi... Dziś już chyba sobie odpuszcze, powalcze z tym jutro.. Dziękuje wszystkim za poświęcony czas.
    Pozdrawiam
  • #23 9213406
    Andrzej__S
    Poziom 28  
    Przede wszystkim sprawdź, tak jak wyżej napisałem, czy Twój terminal wysyła znak 0x0D na końcu. Bez tego kod nie będzie działał prawidłowo (mikrokontroler nie będzie odpowiadał).
    Najlepiej przełącz konsolę na wyświetlanie hex, zrób loopback, wpisz 1 i naciśnij ENTER lub jak to robisz inaczej i napisz co odebrałeś z powrotem. Napisz jakiego terminala używasz.
  • #24 9213688
    krizq1
    Poziom 10  
    Używam BrayTerminal v1.9b. Zaznaczanie CR+LF nic nie daje.
  • Pomocny post
    #25 9213756
    krru
    Poziom 33  
    
    char znak[2];
    char temp;
    	
    while(1)
    {
            
    temp=USART_Recieve();
    		
    znak[0]=znak[1]; 
    znak[1]=znak[2]; 
    znak[2]=temp; 
    
    
    
    ^^^^^^^^ Tutaj juz wychodzisz poza tablice.
    
    
    
    
    
    if(temp == 0x0d) //0x0d to enter 
    { 
    		
       if(znak[1]=='1' & znak[0]!=1) 
       { 
       printf("OK\n");
       _delay_ms(200);
    
    
    Delay jest niepotrzebny
    
    
       }
    
    znak[0]=0; 
    znak[1]=0; 
    znak[2]=0;
    }
    }	
    



    Ja bym napisal cos w stylu


    
    
    #define N 5      // maksymalna dlugosc polecenia
    
    char bufor[N];
    char temp;
    int blen;
    
    
    blen = 0;
    while(1)
    {
            
        temp=USART_Recieve();
        if (temp == 0xD)
        {
            // sprawdzenie zwartosci bufora - w ogolnosci jakis 'switch' albo seria ifow
            if ((blen == 1) && (bufor[0] == '1'))
                OK();
            
        }
        else if (blen < N)
        {
            bufor[blen++] = temp;   // dopisanie do bufora
        }
        else
        {
             // polecenie za dlugie - zignorowac
        }
    		
    }  /* while */
    
    
  • #26 9216549
    Andrzej__S
    Poziom 28  
    Cytat:

    Używam BrayTerminal v1.9b. Zaznaczanie CR+LF nic nie daje.

    Nie miałeś zaznaczać opcji "CR+LF", tylko "+CR" pomiędzy polem tekstowym do wpisywania znaków do wysłania a przyciskiem "-> Send". Za to opcja "CR=CR+LF" nie ma być zaznaczona. Przy takim ustawieniu mój kod powinien działać. Jeśli nie będzie, zrób screenshot'a okna terminala i pokaż, jak go skonfigurowałeś. Podaj też dokładny kod, jaki kompilujesz.

    EDIT:
    @krru:
    Twój kod też powinien działać. Zapomniałeś tylko o tym, że trzeba wyzerować w odpowiednim miejscu zmienną 'blen'. Jest nawet nieco prostszy od mojego, przy czym ja pisałem mój kod z myślą o późniejszej modyfikacji fragmentu:
    
          if(znak==0x0D) 
          { 
          // akcję wykonujemy tylko, gdy przesłany został jeden znak 
             if(strlen(bufor)==1) 
             { 
          // "OK" wysyłamy, jeśli odebrany, jedyny znak to '1' 
                if(bufor[0]=='1') printf("OK\n"); 
             } 
          // zerowanie bufora 
             for(i=0;i<MAX_LENGTH;i++) 
                bufor[i]=0x00; 
             i = 0; 
          } /* if(znak==0x0D) */
    

    na coś takiego:
    
          .........
          if(znak==0x0D) 
          { 
          // przekształcenie ciągu znaków na numer komendy
             int command = atoi(bufor);
             switch(command)
             {
                case 1:
                   printf("OK\n");
                   break;
                case 2:
                   // zrób coś innego
                   break;
                case 3:
                   // zrób jeszcze coś innego
                   break;
                case 15:
                   // to już komenda dwucyfrowa
                   break;
                default:
                   printf("unknown command");
             }
    
          // zerowanie bufora 
             for(i=0;i<MAX_LENGTH;i++) 
                bufor[i]=0x00; 
             i = 0; 
          } /* if(znak==0x0D) */
          // itd.
    

    W ten sposób można obsłużyć praktycznie dowolną liczbę komend, pod warunkiem, że przesyłane ciągi znaków będą zawierały tylko cyfry (żeby nie wdawać się w szczegóły działania funkcji atoi()) + znak końca transmisji (0x0D) i nie będzie ich więcej niż 4 (właściwie to nawet 5, ale to już ma swoje ograniczenia związane z pojemnością zmiennej typu int).

    Ja z kolei zapomniałem o zabezpieczeniu przed przesyłaniem zbyt długich ciągów znaków. W miejscu:
    
          else /* if(znak!=0x0D) */ 
             bufor[i++] = znak;
    

    powinno być:
    
          else if(i<MAX_LENGTH-1) /* if((znak!=0x0D) && (i<MAX_LENGTH-1)) */ 
             bufor[i++] = znak;
    

    żeby nie przekroczyć rozmiaru tablicy.
    Nie zmienia to faktu, że podczas przesyłania mniejszej ilości znaków kod powinien działać prawidłowo.
  • #27 9217868
    krizq1
    Poziom 10  
    Witam. Testowałem także kod od krru, ale też nie działa. Andrzej, w moim Bray'u nie ma nic pomiedzy polem do wpisywania tekstu, a ikonką Send. Być może chodzi Ci o CR=LF? (niżej zamieszczam linka do screena). Tak jak prosiłeś wklejam cały kod...
    
    #define F_CPU 1000000L
    #define BAUD 2400
    #define MYUBRR  F_CPU/BAUD/16-1
    #define MAX_LENGTH 10
    
    #include <stdio.h>
    #include <avr/io.h>
    #include <util/delay.h>
    
    
    	
    void USART_init(unsigned int myubrr)
    {
     
        UBRRH = (unsigned char)(myubrr>>8);
        UBRRL = (unsigned char)myubrr;
    	UCSRB = (1<<TXEN) | (1<<RXEN);
        UCSRC = (1<<URSEL)|(3<<UCSZ0); 
    }
    
    static int USART_Transmit(char data, FILE *stream)
    {
        while(!(UCSRA & (1<<UDRE)));
        UDR = data;
    }
    
    unsigned char USART_Recieve(void)
    {
        while(!(UCSRA & (1<<RXC)));
        return UDR; 
    }
    
    static FILE mystdout = FDEV_SETUP_STREAM(USART_Transmit, NULL, _FDEV_SETUP_WRITE);
    	
    
    int main(void)
    {
    char znak; 
    char bufor[MAX_LENGTH]; 
    unsigned char i=0; 
    
    USART_init(MYUBRR);
    stdout = &mystdout;
      
     while(1) 
       { 
          znak=USART_Recieve(); 
          if(znak==0x0D) 
          { 
           
             if(strlen(bufor)==1) 
             { 
         
                if(bufor[0]=='1') printf("OK\n"); 
             } 
         
             for(i=0;i<MAX_LENGTH;i++) 
                bufor[i]=0x00; 
             i = 0; 
          } 
          else if(i<MAX_LENGTH-1) 
             bufor[i++] = znak;
       } 
    } 
    



    [ATmega8][C] Problem z odbieraniem pojedynczego znaku
  • #29 9222440
    krizq1
    Poziom 10  
    Andrzej, miałeś rację - faktycznie działa! Dzięki bardzo za pomoc, pozdrawiam!
REKLAMA