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

Źle odebrany bufor USART ATMEGA8

marnowak 16 Gru 2011 16:05 1954 11
REKLAMA
  • #1 10266801
    marnowak
    Poziom 15  
    Witam!
    Mam taki oto kod:
    W funkcji main:
    
    while(1)
    	{
    		ReadKeypad();
    		if(!strcmp((const char*)bufferin,"Wlacz"))
    		{
    			x=0;
    			device[1].dev_status=1;
    			while ( UCSRA & (1<<RXC) ) dummy = UDR;
    		}
    		if(!strcmp((const char*)bufferin,"ylacz"))
    		{
    			x=0;
    			device[1].dev_status=0;
    			while ( UCSRA & (1<<RXC) ) dummy = UDR;
    		}
    	}
    

    Funkcja przerwania:
    
    SIGNAL(USART_RXC_vect)
    {
    	bufferin[x]=UDR;
    	x++;
    	if(x==32) x=0;
    }
    

    Zmienne zadeklarowane globalnie:
    
    volatile char bufferin[32];
    volatile uint8_t x;
    


    Problem jest taki, że jak wysyłam RS-em komendę "Wlacz" to wchodzi do odpowiedniego warunku, jeśli jako następną wyślę komendę "Wylacz" to bufor jest równy "ylacz"(połyka gdzieś W). Jeśli pierwszym poleceniem jakie wyślę zaraz po podłączeniu zasilania układu będzie "Wylacz" to W nie jest połknięte. Nie rozumiem czemu przy drugim odbiorze mam w buforze połknięte W?
  • REKLAMA
  • #2 10266844
    tmf
    VIP Zasłużony dla elektroda
    Może dlatego, że niewiedzieć czemu po wysłaniu komendy czekasz na odebranie kolejnego znaku i go usuwasz odczytując UDR (dummy=UDR).
    Kolejna sprawa - te strcmp jest bez sensu, sprawdzać powinieneś dopiero po odebraniu danych. Ew. trzebaby zamazywać cały bufor, bo tak jak to masz zrobione przy odbiorze kolejnych komend bufor jest mixem nowych i starych (strcmp sprawdza aż do natrafienia różnego znaku, albo NULL.
  • #3 10266966
    Konto nie istnieje
    Konto nie istnieje  
  • #4 10267153
    marnowak
    Poziom 15  
    tmf napisał:
    Może dlatego, że niewiedzieć czemu po wysłaniu komendy czekasz na odebranie kolejnego znaku i go usuwasz odczytując UDR (dummy=UDR).
    Kolejna sprawa - te strcmp jest bez sensu, sprawdzać powinieneś dopiero po odebraniu danych. Ew. trzebaby zamazywać cały bufor, bo tak jak to masz zrobione przy odbiorze kolejnych komend bufor jest mixem nowych i starych (strcmp sprawdza aż do natrafienia różnego znaku, albo NULL.


    No właśnie myślałem, że jak wyzeruję x to zamażę bufor. Z dummy=UDR i bez- działa tak samo. W zasadzie to rozumiem, że strcmp działa w ten sposób, że za stringiem musi być \0, a ja przesyłam tylko i wyłącznie literki. Nie wiem jaki znak w tablicy ANSI odpowiada znakowi \0 stringa. Najlepiej by mi było go po prostu dodać do wysyłanej komendy, tylko pytanie który to?
  • REKLAMA
  • #5 10267314
    tmf
    VIP Zasłużony dla elektroda
    To zależy jak wysyłasz znaki, niektóre funkcje, np. sprintf same dodają null na końcu, zależy to także od zapisu literału. Oprócz niepotrzebnego odczytu UDR musisz też inaczej rozwiązać powiązanie bufora zapełnianego w przerwaniu z odczytem w głównej funkcji programu. Bo teraz jeśli porównanie wypada pomyślnie to zerujesz x mimo, że bufor może zawierać już kolejną komendę. Zakładając, że wysyłanie komendy kończy się znakiem null (0), to w proc. obsługi przerwania powinieneś wykrywać ten fakt, ustawiać odpowiednią flagę. Jej ustawienie powinno blokować odbieranie kolejnych znaków i być sygnałem dla programu głównego do sprawdzenia odebranej komendy (strcmp). Po takim sprawdzeniu należy wyzerować x i wyzerować flagę.
  • #6 10267320
    Konto nie istnieje
    Konto nie istnieje  
  • REKLAMA
  • #7 10267404
    marnowak
    Poziom 15  
    Zajrzałem, ale nie wiem jak mam porównywać bufor z czymkolwiek. Spróbuję dodać znak NULL do końca wysyłanego ciągu wedle podpowiedzi(zapomniałem, że \0 oznacza znak o wartości NULL). :)

    Dodano po 29 [minuty]:

    Zmodyfikowałem wedle podpowiedzi:
    W main:
    
    
    
    while(1)
    	{
    		ReadKeypad();
    		if(end_trans)
    		{
    			if(!strcmp((const char*)bufferin,"Wlacz"))
    			{
    				device[1].dev_status=1;
    				end_trans=0;
    			}
    			if(!strcmp((const char*)bufferin,"Wylacz"))
    			{
    				device[1].dev_status=0;
    				end_trans=0;
    			}
    		}
    	}


    W przerwaniu:
    
    SIGNAL(USART_RXC_vect)
    {
    	if(UDR!='\n')
    	{
    		bufferin[x]=UDR;
    		x++;
    		if(x==32) x=0;
    	}
    	else
    	{
    		end_trans=1;
    		x=0;
    	}
    }
    


    W sekwencji zmieniłem, aby nadawało HEX 57 6C 61 63 7A 0D ("wlacz\n") oraz HEX 57 79 6C 61 63 7A 0D (Wylacz\n). Teraz w ogóle nie czai warunków- nawet za pierwszym wysyłanym poleceniem.
  • #8 10268792
    Konto nie istnieje
    Konto nie istnieje  
  • REKLAMA
  • #9 10269417
    tmf
    VIP Zasłużony dla elektroda
    marnowak napisał:

    Zmodyfikowałem wedle podpowiedzi:
    W main:
    
    
    
    while(1)
    	{
    		ReadKeypad();
    		if(end_trans)
    		{
    			if(!strcmp((const char*)bufferin,"Wlacz"))
    			{
    				device[1].dev_status=1;
    				end_trans=0;
    			}
    			if(!strcmp((const char*)bufferin,"Wylacz"))
    			{
    				device[1].dev_status=0;
    				end_trans=0;
    			}
    		}
    	}


    W przerwaniu:
    
    SIGNAL(USART_RXC_vect)
    {
    	if(UDR!='\n')
    	{
    		bufferin[x]=UDR;
    		x++;
    		if(x==32) x=0;
    	}
    	else
    	{
    		end_trans=1;
    		x=0;
    	}
    }
    


    W sekwencji zmieniłem, aby nadawało HEX 57 6C 61 63 7A 0D ("wlacz\n") oraz HEX 57 79 6C 61 63 7A 0D (Wylacz\n). Teraz w ogóle nie czai warunków- nawet za pierwszym wysyłanym poleceniem.


    x masz na początku gdzieś zainicjowane? A nie czai bo odbierając ciąg musisz go kończyć znakiem NULL (0), u ciebie ciąg nie ma trerminacji i strcmp się sypie. Nie zeruj x w przerwaniu tylko w pętli głównej po odczytaniu flagi, wykorzystaj x jako licznik ilości bajtów do porównania przez np. memcmp, dzięki czemu zniknie problem terminatora.
  • #10 10269596
    marnowak
    Poziom 15  
    Dzięki!
    Twój kod działa!
    Ciekawi mnie czemu jak zadeklaruję zmienną "i" jako volatile globalnie to nie działa jej zerowanie po za przerwaniem- analogicznie jak miałem poprzednio?

    Docelowo chcę przesyłać słowa 5 znakowe z kontrolą CRC. Chciałem, aby zerowanie odbywało się zawsze po 5 znaku, ale widzę, że bez zdefiniowanego znaku końca, bądź początku ramki(tak jak tu '\n') będzie to działać kulawo(nawet teraz zdarza się, że połknie jakąś literkę i cała transmisja siada). Po za tym będzie to zbyt długi kod, aby wszystko umieszczać w przerwaniu.
  • #11 10269999
    Konto nie istnieje
    Konto nie istnieje  
  • #12 10270900
    marnowak
    Poziom 15  
    Właśnie mam już opracowany program pod Windows komunikujący się z modułami.
    Wykorzystałem następującą konstrukcję ramek:
    Źle odebrany bufor USART ATMEGA8


    Każde 8 bit jest jakby jednym znakiem RS-232. Ramka ma stałą długość, więc mam nadzieję, że nie będzie potrzeby wprowadzania dodatkowych znaków typu '\n', czy '\r'. Nie wiem, czy to się uda, ale chciałbym, aby przy pierwszym odebranym znaku startować timer. W przypadku, gdy w określonym czasie nie przyjdzie cała ramka(jeśli na przykład jakiś bajt z ramki się zagubi) to zostanie wyzerowana zmienna 'i', co nie zablokuje mi możliwości kolejnych transmisji do urządzenia. Należy zauważyć, że w tej chwili niepełne, bądź błędne polecenie rozwala bufor i nie reaguje już na kolejne transmisje do urządzenia.

    Dodano po 2 [godziny] 46 [minuty]:

    Dla zainteresowanych przesyłam kod Wlacz Wylacz z rozwiązanym problemem przyjścia zafałszowanych, bądź niepełnych słów(wprowadzone timeout-y transmisji).
    zmienne globalne
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Część kodu w main:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Przerwanie od RX-a:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Zmienna my_delay2 decrementowana w obsłudze przerwania od licznika Timer0:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    W moim układzie mam taktowanie sygnałem zewnętrznym o częstotliwości 3,684 MHz.
REKLAMA