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

[C] Wysłanie stringu poprzez rs232.

Yoshi_80 12 Wrz 2008 14:46 2628 12
  • #1 12 Wrz 2008 14:46
    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 :

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


    wywołanie fukcji wykonuje w taki sposób :
    Code:


                     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]

    0 12
  • #2 12 Wrz 2008 16:52
    bobbyAIR
    Poziom 20  

    A jak wygląda funkcja USART_Transmit ?

    0
  • #3 12 Wrz 2008 19:37
    KonusiK
    Poziom 11  

    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.

    0
  • #4 12 Wrz 2008 21:18
    HIOB
    Poziom 17  

    Witam,

    Proponuję takie rozwiązanie:

    Code:

    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.

    0
  • #5 12 Wrz 2008 21:46
    bobbyAIR
    Poziom 20  

    Jak juz to lepiej ( bo mniej operacji) tak (bazując na hioba propozycji)

    Code:

    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++);
       }
    }

    0
  • #6 12 Wrz 2008 22:13
    HIOB
    Poziom 17  

    Z ciekawości sprawdziłem, która się szybciej wykonuje:

    Code:

    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.

    0
  • #7 12 Wrz 2008 23:13
    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

    Code:

    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

    0
  • #8 13 Wrz 2008 00:14
    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:

    Code:
    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.

    0
  • #9 13 Wrz 2008 09:23
    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 :

    Code:
    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 :

    Code:
    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 :).

    0
  • #10 13 Wrz 2008 15:25
    BoskiDialer
    Poziom 34  

    Code:
    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).

    0
  • #11 14 Wrz 2008 12:31
    KonusiK
    Poziom 11  

    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

    Code:
    #include <avr/interrupt.h>
    do includ'ów

    oraz:
    Code:

    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.

    0
  • #12 14 Wrz 2008 19:37
    bobbyAIR
    Poziom 20  

    AVRlibc generuje domyślnie zaślepkę dla nieobslużonych prerwań, Wystarczy użyć EMPTY_INTERRUPT

    0
  • #13 16 Wrz 2008 08:01
    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 :

    Code:

       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

    Code:

    #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 ;) )

    0