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

[C] Wysłanie stringu poprzez rs232.

Yoshi_80 12 Wrz 2008 14:46 3066 12
  • #1 5528525
    Yoshi_80
    Poziom 21  
    Zaczynam troche zabawe z C i napotkałem problem z funkcją wysyłającą stringa na port szeregowy. Funkcja wyglada na pewno wiekszości z was znajomo :

    	void send_string(char *tekst)
    	{
    		while(*tekst) {
    		USART_Transmit(*tekst);
    		tekst++;
    		}
    	}


    wywołanie fukcji wykonuje w taki sposób :
    
    
                     send_string("program testowy");
    

    No i teraz niech mi ktoś wytłumaczy dlaczego wysyłanie stringu się zapętla i leci bez końca. Tak jakby nie było znacznika końca stringu. Jeżeli tak to jak go "dołożyć". Problem czuje jest trywialny ale nie moge sobie z nim poradzić :D


    Poprawiłem tytuł:
    https://www.elektroda.pl/rtvforum/topic1015361.html
    Proszę umieszczać listingi programów w znacznikach "Code".
    [c_p]
  • #2 5528884
    bobbyAIR
    Poziom 20  
    A jak wygląda funkcja USART_Transmit ?
  • #3 5529405
    KonusiK
    Poziom 12  
    Faktycznie dziwnie to wygląda - a próbowałeś sobie zrobić podgląd rejestrów w symulacji, czy na pewno masz tam NULLa na końcu?

    A co Ci sie wyświetla w terminalu? Odpalałeś to na uC czy tylko w symulatorze?

    Jeśli odpalałeś, to może sprawdź sobie ten string w ten sposób,że co sekunde wyświetlaj sobie na ledach (jeśli możesz) wartość *tekst, albo zrób sobie porównanie stringu - chyba jest standardowa funkcja strcmp która zwraca odpowiednio wartość w zależności od wyniku porównania stringu - to Ci pokarze co dokładnie masz w rejestrach. Wiem,że to jest perfidnie lamerska próba znalezienia błędu, ale zawsze warto spróbować. Od czegoś trzeba zacząć, a względem tego jakie podałeś dane odnośnie powstałego problemu to tylko tyle przychodzi mi na myśl.

    Nio chyba,ze bobbyAIR słusznie podejrzewa bład w USART_Transmit(); ale to musiałbyś naprawde cos tam schrzanić, bo przeważnie ta funkcja sprowadza sie do wpisania danej do UDR.
  • #4 5529766
    HIOB
    Poziom 17  
    Witam,

    Proponuję takie rozwiązanie:

    
    static void Wyslij_USART(unsigned int dana)
    {
    	while(!(UCSRA & (1 << UDRE)));
    	
    	UDR = dana;
    }
    
    void Tekst_USART(char *tekst)
    {
    	char znak;
    	
    	while((znak = *(tekst++)) != 0)
    	{
    		Wyslij_USART(znak);
    	}
    }
    


    Pozdrawiam,
    Hiob.
  • #5 5529882
    bobbyAIR
    Poziom 20  
    Jak juz to lepiej ( bo mniej operacji) tak (bazując na hioba propozycji)
    
    void Wyslij_USART(unsigned int dana)// zadne static, to nie C++ ani funkcja inline
    {
       while(!(UCSRA & (1 << UDRE)));
       
       UDR = dana;
    }
    
    void Tekst_USART(char *tekst)
    {
       while(*tekst)
       {
          Wyslij_USART(*text++);
       }
    } 
    
  • #6 5529982
    HIOB
    Poziom 17  
    Z ciekawości sprawdziłem, która się szybciej wykonuje:

    
    void Tekst_USART(char *wskaznik)
    {
    	char znak;
    	
    	while((znak = *(wskaznik++)) != 0)
     2f0:	fc 01       	movw	r30, r24
     2f2:	21 91       	ld	r18, Z+
     2f4:	80 e6       	ldi	r24, 0x60	; 96
     2f6:	90 e0       	ldi	r25, 0x00	; 0
     2f8:	22 23       	and	r18, r18
     2fa:	d1 f3       	breq	.-12     	; 0x2f0 <main+0xe>
     2fc:	f5 cf       	rjmp	.-22     	; 0x2e8 <main+0x6>
    
    
    
    
    
    void Tekst_USART(char *wskaznik) 
    {
       while(*wskaznik) 
     2f4:	40 81       	ld	r20, Z
     2f6:	9f 01       	movw	r18, r30
     2f8:	e0 e6       	ldi	r30, 0x60	; 96
     2fa:	f0 e0       	ldi	r31, 0x00	; 0
     2fc:	44 23       	and	r20, r20
     2fe:	d1 f3       	breq	.-12     	; 0x2f4 <main+0x12>
     300:	f3 cf       	rjmp	.-26     	; 0x2e8 <main+0x6>
    


    Jak widać nie ma różnicy :)

    Pozdrawiam,
    Hiob.
  • #7 5530240
    bobbyAIR
    Poziom 20  
    Nie chodziło mi tu o operacje procesora (choć o to zazwyczaj postuluję ) ale o ilośc operacji w kodzie a więc jego czytalność. Druga rzecz zmienna znak pożera bajt pamięci na stosie a stosu mało w AVRkach.

    Z ciekawości to mozna porównywać operacje typu

    
    unsigned char zm1;
    unsigned short zm2;
    
    zm1 = (zm2 >>8);
    zm1 = (unsigned char)(zm2 >>8);
    zm1 = ((unsigned char)zm2) >>8;
    

    w zależności od tego co jest w tych zmiennych ( na ile kompilator to określi) i jaki jest poziom optymalizacji
  • #8 5530372
    BoskiDialer
    Poziom 34  
    Yoshi_80: Wytłumacz może co dokładnie rozumiesz pod pojęciem "zapętlić" - czy wysyła cały czas tylko pierwszy znak (restartowanie się procesora przy wychodzeniu z funkcji USART_Transmit spowodowane istnieniem stosu w obszarze niezapisywalnym - najczęściej ustawiony fusebit związany z kompatybilnością z innym prockiem) czy powtarza całość (może jawnie wrzuciłeś kod do pętli, która za każdym razem wysyła ten sam ciąg). Wklej może wszystkie funkcje związane z transmisją przez usart (init, transmit) oraz funkcję, w której wywołujesz funkcję send_string.

    Najprostrzy i najbardziej poprawny (warto używać "const") kod jest taki:
    void send_string(const char *tekst) 
    {
      while(*tekst)
        USART_Transmit(*tekst++); 
    }


    bobbyAIR: Jeszcze istnieją rejestry, których na potrzeby wywołania jednej funkcji jest co najmniej 13.
  • #9 5530649
    Yoshi_80
    Poziom 21  
    Witam

    Program był testowany fizycznie na kicie uruchomieniowym stk500.

    Program pisze w celu zbudowania rejestratora danych, moduł pomiarowy będzie połaczony z kitem. Pomiary wysyłane będą do programu rejestrujacego na pc lub po prostu do pliku txt do dalszej obróbki.
    Początkowo program słuzył jako "odbijacz" danych rs232 , jak coś dostał to od razu odsyłał. Działał bardzo dobrze. Zdecydowałem sie dołożyć "powitanie" tak aby rozpoczecie transmisji danych po rs było poprzedzone nagłowkiem np data, czas itp itd.

    "Zapętlanie" wykonywania wyglada tak ze napis "program testowy" jest w calosci transmitowany bez końca : "program testowyprogram testowyprogram testowyprogram .... "

    Kod funkcji transmitujacej wyglada tak :

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


    Dołozyłem tam mruganie diodą na porcie B0.

    Kod maina :

    int main (void)
    {
    	prog_init(); //ustawienie portów etc
    	sei();
    	USART_Init ( MYUBRR ); //ustawienie predk. transm.
    	
    send_string("program testowy");
    _delay_ms(3000);
    
    while(1) {
    rbuf=USART_Receive(); //odbijacz
    USART_Transmit(rbuf);
    }   
    	
    return 0;
    }


    W moim załozeniu program powinien wyslać tekst , odczekać 3 sek i wpaść w pętle odbijacza. Niestety tak się nie dzieje, zapętla sie na wysyłaniu napisu.

    Niestety wasze sugestie bede mogł fizycznie sprawdzić w poniedziałek w robocie ;). Muszę sobie taki kit prywatnie sprawić bo jest fajny :).
  • #10 5531466
    BoskiDialer
    Poziom 34  
    PORTB^=(0<<PB0);
    Powyższy kod nie robi nic.

    Nie wkleiłeś kodu funkcji USART_Init, a to może być znaczące - jeśli załączasz tam przerwania od usartu, to procek będzie się mógł resetować(brak funkcji obsługi przerwania) np po opróżnieniu się buforu nadawczego (przerwanie od wolnego buforu nadawczego).
  • #11 5533961
    KonusiK
    Poziom 12  
    BoskiDialer ma dużo racji:
    1.linijka kodu jest zupełnie bez użyteczna (nie zmienia Ci stanu portu - powinno być 1<<PB0 )
    2. Każde nie obsłużone przerwanie (brak funkcji obsługi, albo pustej funkcji ISR(BADISR_vect) powoduje restart uC. Więc zapodaj inicjalizacją i obsługą (jeśli ją napisałeś) przerwania UDRE lub TXC.

    Tak dla pewności wstaw sobie
    #include <avr/interrupt.h>
    do includ'ów

    oraz:
    
    ISR(BADISR_vect)
    {
        // pozostaw pusty - żadne nieobsłużone przerwanie nic nie zrobi
    } 



    Dobrym pomysłem byłoby mrugnięcie diodką po starcie procka (2-3 szybkie). Wtedy bedziesz miał pewność czy i kiedy procek sie restartuje.
  • #12 5535367
    bobbyAIR
    Poziom 20  
    AVRlibc generuje domyślnie zaślepkę dla nieobslużonych prerwań, Wystarczy użyć EMPTY_INTERRUPT
  • #13 5539654
    Yoshi_80
    Poziom 21  
    Cześć

    Pokombinowałem wczoraj z rana i zaczął program działać wg oczekiwań :) .

    Pierwszym błedem jaki zauważyłem było skonfigurowanie timera ale brak procedury obsługi przerwania (pozostałości po poprzedniej wersji programu). Po usunięciu konfiguracji rejestrów timera program wyraźnie odzyskał stabilość. Aż się dziwie, ze C jest tak czułe na błedy, nie to co sielankowy bascom hehe.
    Naniosłem też do programu wasze sugestie.

    Druga sprawa to zrezygnowałem z poleceń tego typu :
    
       PORTB^=(0<<PB0); 
       PORTB^=(1<<PB0); 
    
    


    gdyż działały zupełnie niezrozumiale (np ustawiało cały port zamiast jednego bitu lub nie resetowało jednego bitu itp).

    Zamiast tego zdefiniowałem polecenia

    
    #define cbi(sfr, bit)   (_SFR_BYTE(sfr) &= ~_BV(bit)) 
    #define sbi(sfr, bit)   (_SFR_BYTE(sfr) |= _BV(bit)) 
    


    i ustawiam poszczególne bitów portu poleceniami cbi i sbi. Działa znakomicie.
    Dzięki za pomoc w razie czego znowu sie odezwe (pewnie wkrótce ;) )
REKLAMA