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

Transmisja RS232 na ATmega32: Odczyt pliku znak po znaku, problem z pętlą

Goompas 17 Sie 2006 15:17 1907 9
REKLAMA
  • #1 2924716
    Goompas
    Poziom 13  
    Posty: 123
    Pomógł: 3
    Ocena: 2
    Witam
    Nie wiem jak to nazwac.
    Probuje napisac funkcje, ktora z wyslanego pliku przez rs232 bedzie odczytywala znak po znaku.
    Plik do odebrania wyglada np tak
    Cytat:

    r8f
    56
    rac
    79
    r67
    $

    Po znaku 'r' podana jest wartosc hexadecymalna, tzn procesor ma ja tak traktowac. Wszystko inne jak np 2 i 4 linijke ma pomijac. Robie to tak
    jezeli napotka znak 'r' ma pobrac kolejny znak i traktowac go jako 4 starsze bity zmiennej wynik, nastepnie pobrac kolejny znak i traktowac jako 4 mlodze bity zmiennej wynik, z czego po zlozeniu zmienna wynik jak w 1 linijce przykladowego pliku ma wartosc 8f
    Tyle w teori, w praktyce odczytuje mi z pliku tylko 1 linijke, reszte pomija i wychodzi z funkcji. Juz nie mam pomyslow dlaczego.
    Kod funkcji
    
    void USART_getfile (void)
    {
      u08 tmp;
      u08 wynik;
      register char ch=0;
      while (ch != '$')
      {
        //WDR();			// restart licznika Watchdoga
        ch=USART_Receive();	// pobieraj znak
    	if (ch=='$') break;	//koniec pliku
    	/*if (ch=='%') //pomin komentarze do konca linii
    		while (ch != '\n')
    				ch=USART_Receive();	*/
    	if (ch == 'r')
    			{
    			ch=USART_Receive();
    			if ((ch >= '0') && (ch <= '9'))
    				{
    				tmp = ch - '0';
    				wynik=tmp<<4;
    				LCD_clear();
    				LCD_putstr(" pkt 1 ");
    				KBD_keypls();
    				}
    			else if ((ch >= 'a') && (ch <= 'f'))
    				{
    				tmp = ch - 'a' + 10;
    				wynik=tmp<<4;
    				LCD_clear();
    				LCD_putstr(" pkt 2 ");
    				KBD_keypls();
    				}
    			else if ((ch >= 'A') && (ch <= 'F'))
    				{
    				tmp = ch - 'A' + 10;
    				wynik=tmp<<4;
    				LCD_clear();
    				LCD_putstr(" pkt 3 ");
    				KBD_keypls();
    				}
    				//druga polowa
    			ch=USART_Receive();
    			if ((ch >= '0') && (ch <= '9'))
    				{
    				tmp = ch - '0';
    				wynik=wynik+tmp;
    				LCD_clear();
    				LCD_putstr(" pkt 4 ");
    				KBD_keypls();
    				}
    			else if ((ch >= 'a') && (ch <= 'f'))
    				{
    				tmp = ch - 'a' + 10;
    				wynik=wynik+tmp;
    				LCD_clear();
    				LCD_putstr(" pkt 5 ");
    				KBD_keypls();
    				}
    			else if ((ch >= 'A') && (ch <= 'F'))
    				{
    				tmp = ch - 'A' + 10;
    				wynik=wynik+tmp;
    				LCD_clear();
    				LCD_putstr(" pkt 6 ");
    				KBD_keypls();
    				}
    			LCD_clear();
    			LCD_putstr("Rejestr: ");
    			LCD_puthex(wynik);
    			KBD_keypls();
    		}//if 'r'
      }//while '$'
    }//koniec funkcji

    Bede wdzieczny za kazda sugestie

    I takie pytanie dodatkowe: Jak wykryc znak konca linii? '\n' i '\r' nie dziala
  • REKLAMA
  • #2 2926052
    przemek20
    Poziom 21  
    Posty: 328
    Pomógł: 41
    Ocena: 25
    Wrzuc jeszcze jak u ciebie wygląda USART_receive i KBD_keyplus. Czy plik wysyłasz cały czy po kolei znaki w terminalu? Jesli nie uzywałeś tego drugiego sposobu to spróbuj.
    Pozdrawiam.
  • REKLAMA
  • #3 2926168
    Goompas
    Poziom 13  
    Posty: 123
    Pomógł: 3
    Ocena: 2
    Plik wysylam caly, jezeli odbieram plik znak po znaku i go odsylam spowrotem na terminal, wszystko jest ok, wszytko transmituje.
    Funkcja USART_Receive()
    //Odebranie znaku z rs232
    unsigned char USART_Receive( void ){
       while ( !(UCSRA & (1<<RXC)) );//czekaj na dane do odebrania
       return UDR;//odbierz i zwroc dane z bufora
    }


    Zmodyfikowalem troche funkcje i teraz z pliku wygladajacego tak:
    raf
    r56
    rac
    r79
    r67
    raa
    rbb

    przetwarza co 3 linijke
    Zmodyfikowana funkcja
    void USART_getfile(void)
    	{
    	u08 ch=0;
    	u08 tmp=0;
    	u08 wynik=0;
    	while (ch != '$')
    		{
    		wynik=0;
    		ch=USART_Receive();
    		if (ch == 'r')
    			{
    			ch=USART_Receive();
    			if ((ch >= '0') && (ch <= '9'))
    				{
    				tmp=ch - '0';
    				wynik=tmp<<4;
    				}
    			else if ((ch >= 'a') && (ch <= 'f'))
    				{
    				tmp=ch - 'a' + 10;
    				wynik=tmp<<4;
    				}
    			else if (( ch >= 'A') && (ch <= 'F'))
    				{
    				tmp=ch - 'A' + 10;
    				wynik=tmp<<4;
    				};
    			ch=USART_Receive();
    			if ((ch >= '0') && (ch <= '9'))
    				{
    				tmp=ch - '0';
    				wynik=wynik+tmp;
    				}
    			else if (( ch >= 'a') && (ch <= 'f'))
    				{
    				tmp=ch - 'a' + 10;
    				wynik=wynik+tmp;
    				}
    			else if ((ch >= 'A') && (ch <= 'F'))
    				{
    				tmp=ch - 'A' + 10;
    				wynik=wynik+tmp;
    				};
    			USART_putstr("Rejestr: ");
    			USART_putint(wynik,16);
    			}//if 'r'
    		}//while != '$'
    	}//koniec funkcji


    aha u08 to unsigned char
  • Pomocny post
    #4 2926195
    aster11
    Poziom 19  
    Posty: 211
    Pomógł: 36
    Ocena: 1
    Funkcja (w takiej ostatecznej formie jak przedstawiona przez Ciebie), wygląda na poprawną, tzn. powinna działać tak jak tego oczekujesz, chociaż sprawdzałem pobieżnie i głowy nie dam :D "Poprawność" rozumiem tu w sensie użytej sekwencji instrukcji.
    Przypuszczam, że kłopot może wynikać z zależności czasowych. Robisz dużo operacji na LCD. Być może użyte w funkcjach obsługi LCD kumulujące się opóźnienia są znacznie większe od czasu transmisji jednego znaku przez USART. Jeżeli Twoja funkcja USART_Receive() buforuje odbierane znaki, to nie powinno być kłopotu. Jeżeli jednak nie ma buforowania lub bufor jest bardzo krótki (np. w przerwaniu odbierany jest tylko ostatni znak i zapisywany tymczasowo do zmiennej), to rezultat może byc dokładnie taki jak opisujesz:

    ... nadchodzi znak 'r', uruchamia się dekodowanie liczby hex, niemal natychmiast odbierany jest kolejny znak, więc jest on poprawny. Teraz jednak zaczyna się "długotrwała" operacja wypisywania cyfr. Kolejny, czekający w buforze USART'a lub np. jednobajtowym buforze programowym znak również jest poprawny (stąd cała pierwsza liczba jest OK), natomiast kilka kolejnych znaków zostaje zaprzepaszczonych. Sam nie rozumiem dokładnie jak :|, znak końca '$' dostaje się do funkcji i kończy ją ...

    Ta sama przyczyna może powodować, że "przegapiasz" znaki '\r', '\n', bo istotnie każda linia w kodzie ASCII na pewno kończy się '\r''\n' lub '\n'.

    Jeżeli problem byłby tego typu co mówię, to musisz albo zapewnić dobre buforowanie strumienia znaków z USART, albo odbierane liczby "szybciutko" gdzieś zapisywać, a dopiero potem zająć się ich wyświetlaniem.

    Jeśli jednak porządnie buforujesz transmisję, to moje tłumaczenie wtedy nie bedzie miało sensu.:!:

    Pozdrawiam.


    PS. A jednak nie buforujesz ..., więc problem będzie prawdopodobnie taki jak opisałem.
  • REKLAMA
  • Pomocny post
    #5 2926220
    przemek20
    Poziom 21  
    Posty: 328
    Pomógł: 41
    Ocena: 25
    Oczywiście jest tak jak kolega mówi winne są
    USART_putstr("Rejestr: ");
    USART_putint(wynik,16);
    ponieważ nie masz zadnego buforowania i korzystasz z pollingu a nie przerwan znaki odbierane podcas wykonywania tych funkcji przepadają ( to jest 2 linie) musisz wrzucic te odebrane znaki do jakichś zmiennych a wysyłać/wyswietlac dopiero po zakonczeniu całej funkcji. Jezeli chcesz miec full duplex to tylko przerwania.
    Pozdrawiam
  • #6 2926557
    Goompas
    Poziom 13  
    Posty: 123
    Pomógł: 3
    Ocena: 2
    Dziekuje bardzo za rozjasnienie sprawy, pomogliscie wielce
    Zrobilem na przerwaniach z cyklicznym buforem na 50 znakow
    SIGNAL (SIG_USART_RECV)
    {
    	usartbufor[usartbufferlicznik]=UDR;
    	usartbufferlicznik++;
    	if (usartbufferlicznik > 50) usartbufferlicznik=0;
    }

    A koncowa funkcja wyglada tak
    void USART_getfile(void)
    	{
    	u08 liczniku=0;
    	u08 ch=0;
    	u08 tmp=0;
    	u08 wynik=0;
    	while (ch != '$')
    		{
    		if (ch == '%')//pomin komentarz do konca linijki
    			{
    			USART_putstr("\n\rZNALAZLEM KOMENTARZ\n\r");
    			while ( ch != '\n')
    				{
    				ch=usartbufor[liczniku];
    				liczniku++;
    				if (liczniku > 50) liczniku=0;
    				}
    			}//if '%'
    
    		wynik=0;
    		ch=usartbufor[liczniku];
    		liczniku++;
    		if (liczniku > 50) liczniku=0;
    		if (ch == 'r')
    			{
    			ch=usartbufor[liczniku];
    			liczniku++;
    			if (liczniku > 50) liczniku=0;
    			if ((ch >= '0') && (ch <= '9'))
    				{
    				tmp=ch - '0';
    				wynik=tmp<<4;
    				}
    			else if ((ch >= 'a') && (ch <= 'f'))
    				{
    				tmp=ch - 'a' + 10;
    				wynik=tmp<<4;
    				}
    			else if (( ch >= 'A') && (ch <= 'F'))
    				{
    				tmp=ch - 'A' + 10;
    				wynik=tmp<<4;
    				};
    			ch=usartbufor[liczniku];
    			liczniku++;
    			if (liczniku > 50) liczniku=0;
    			if ((ch >= '0') && (ch <= '9'))
    				{
    				tmp=ch - '0';
    				wynik=wynik+tmp;
    				}
    			else if (( ch >= 'a') && (ch <= 'f'))
    				{
    				tmp=ch - 'a' + 10;
    				wynik=wynik+tmp;
    				}
    			else if ((ch >= 'A') && (ch <= 'F'))
    				{
    				tmp=ch - 'A' + 10;
    				wynik=wynik+tmp;
    				};
    			USART_putstr("Rejestr: ");
    			USART_putint(wynik,16);
    			}//if 'r'
    		}//while != '$'
    	}//koniec funkcji

    Wrescie mi dziala rozpoznawanie i pomijanie komentarzy :P
    Jeszcze takie pytanko, czy moge wykryc jakos koniec otrzymanego pliku? Uzywanie tego znaku '$' nie jest najlepszym rozwiazaniem, ale 'eof' nie dzialalo.
  • #7 2926839
    aster11
    Poziom 19  
    Posty: 211
    Pomógł: 36
    Ocena: 1
    Trochę nawet dziwne, że działa dobrze :D, bo zapełniasz bufor cyklicznie, a nie odczytujesz go cyklicznie, tylko w każdym wywołaniu funkcji USART_getfile() czytasz od zerowego indeksu. Aby odczytywać bufor cyklicznie, zmienną u08 liczniku potrzeba zadeklarować jako static. Być może każdorazowo odczytywałeś dla testów tylko jedną serię danych, a potem restartowałeś układ i problem się nie ujawnił.

    W zapełnianiu bufora też chyba drobniutki błąd, bo jeżeli tablica bufora ma wymiar 50, to u Ciebie zapisany zostanie także element o indeksie 50, czyli element 51-wszy. Powinno być np.: if (usartbufferlicznik >= 50) usartbufferlicznik=0;, i podobnie w funkcji USART_getfile().
    Znów może Twoje dane nigdy nie przekroczyły 50 znaków i żaden problem się nie ujawnił.
    W programowaniu musimy być drobiazgowi, bo przez błędy tego typu raz na 1000 prób program zdziała niewłaściwie i przez tydzień rwiemy włosy z głowy - co sie dzieję? - chyba wszyscy tak mamy :D

    Jeżeli chodzi o znak końca pliku, to sam nie znam się dokładnie. Nie wiem czy standardowo ten znak jest wysyłany, czy nie. To zależy chyba też od tego co wysyła Ci te dane, czy to jakaś standardowa procedura, czy tworzona specjalnie do tego zadania. Może ktoś inny będzie potrafił wypowiedzieć się na ten temat.

    Pozdrawiam.
  • #8 2927000
    przemek20
    Poziom 21  
    Posty: 328
    Pomógł: 41
    Ocena: 25
    Ja bym kończył plik zerem. Jesli masz własny program do wysyłania plików oczywiście.
  • REKLAMA
  • #9 2927603
    Goompas
    Poziom 13  
    Posty: 123
    Pomógł: 3
    Ocena: 2
    poprawilem wedlug rad na ">="
    ale teraz mam pytanie czy lepiej zmienna liczniku zadeklarowac jako
    static u08 licznik=0;
    czy na poczatku wywolania funkcji USART_getfile przypisac ja do
    liczniku=usartbufferlicznik;
    przy czym usartbufferlicznik jest zmienna globalna?
    deklaracja
    u08 usartbufferlicznik=0;
  • Pomocny post
    #10 2928676
    aster11
    Poziom 19  
    Posty: 211
    Pomógł: 36
    Ocena: 1
    Charakterystyczną właściwością bufora cyklicznego (kolejki FIFO) jest to, że wskaźniki początku i końca (odczytu i zapełniania) przemieszczają się niezależnie - każdy w swoim tempie. Stąd w Twoim programie indeksy powinny być niezależnymi zmiennymi globalnymi/statycznymi.

    Przypisanie liczniku=usartbufferlicznik zakłóciłoby normalny tryb pracy bufora cyklicznego, grożąc utratą części odebranych danych. Może i wszystko działałoby tak samo dobrze (to zależy od momentu wywoływania funkcji USART_getfile()), ale nie koniecznie.

    Przy pełnej implementacji kolejki FIFO stosuje się jeszcze detekcję opróżnienia i zabezpieczenie przed przepełnieniem bufora. Od biedy u Ciebie można obejść się bez tego, jeżeli rozmiar bufora jest odpowiednio "na wyrost" względem przewidywanego rozmiaru danych oraz skutecznie działa detekcja znaczników końca serii danych.

Podsumowanie tematu

LABEL_AI_GENERATED
Dyskusja dotyczy problemu odczytu pliku przesyłanego przez interfejs RS232 na mikrokontrolerze ATmega32, gdzie plik zawiera linie z danymi w formacie znak 'r' oraz dwie kolejne cyfry heksadecymalne, które mają być odczytane i przetworzone na wartość 8-bitową. Problem polegał na tym, że funkcja odczytująca plik znak po znaku przetwarzała tylko pierwszą linię, a kolejne pomijała i wychodziła z pętli. Przyczyną były opóźnienia i brak buforowania podczas odbioru danych, zwłaszcza gdy w funkcji wykonywano operacje na LCD, co powodowało utratę znaków w transmisji. Rozwiązaniem było zastosowanie przerwań do odbioru danych oraz implementacja cyklicznego bufora FIFO o rozmiarze 50 znaków, do którego zapisywane są odebrane znaki w przerwaniu, a następnie odczytywane w funkcji przetwarzającej. Wskazano również na konieczność poprawnego zarządzania indeksami bufora (wskaźnikami początku i końca) jako zmiennymi niezależnymi i statycznymi lub globalnymi, aby uniknąć nadpisywania danych i utraty informacji. Dodatkowo zasugerowano zakończenie pliku znakiem '$' lub zerem, aby jednoznacznie określić koniec transmisji. Wskazano, że stosowanie pollingu bez buforowania w pełnodupleksowej transmisji RS232 jest niewystarczające i prowadzi do utraty danych.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA