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

wskaźniki i resetowanie się procesora.

lotnick 18 Lut 2010 17:10 1461 19
  • #1 7714122
    lotnick
    Poziom 15  
    Witam.
    Męczę się ze wskaźnikami. Myślałem że rozumiem o co chodzi ale chyba nie bardzo. Spójrzcie proszę na kody poniżej. Chciałem pobierać z UART kolejne znaki do ramki danych. Póki wpisuję je przez wskaźnik bez żadnej pętli to wszystko jest ok. Kiedy chciałem przesunąć wskaźnik na adres obok poprzez dodanie do niego wartości 'i' to procesor resetuje się nie wchodząc nawet do tej pętli. Po włączeniu zasilania jest reset, reset, reset , reset........
    Czy poniższe kody są OK.? Kompiluje się bez kłopotu.

    
    
    char ramka[67];  //ramka danych
    char* wsk_ramki = ramka;
    for (int i=0;i<50;i++)
    {
    *(wsk_ramki+i)=uart_getc();  //TU SIĘ RESETUJE
    }
    


    
    char ramka[67];  //ramka danych
    char* wsk_ramki = ramka;
    for (int i=0;i<50;i++)
    {
    *(wsk_ramki+1)=uart_getc();  //TU SIĘ nie RESETUJE
    }
    




    
    char ramka[67];  //ramka danych
    for (int i=0;i<50;i++)
    {
    ramka[i]=uart_getc(); '// TUTAJ SIE NIE RESETUJE
    }
    
  • #2 7714144
    arnoldziq
    VIP Zasłużony dla elektroda
    Czy byłby kolega łaskaw podzielić się z nami informacją, co to za procesor i co to za język programowania (kompilator) którego kolega raczy używać ?
  • #3 7714200
    lotnick
    Poziom 15  
    ATMEGA168
    WINAVR

    Dziękuję bardzo za informację.
    Przeniesiono z Programowanie Ogólne. - arnoldziq
  • #4 7716428
    Konto nie istnieje
    Konto nie istnieje  
  • #5 7716590
    nenpa8lo
    Poziom 17  
    *(wsk_ramki+i)=uart_getc();
    Jest bardzo podstawowym zapisem, często używanym przy operowaniu wskaźnikami, więc nie winił bym tutaj kompilatora.
    Proponował bym raczej dokładne sprawdzenie, przy jakim indeksie 'i' *(wsk_ramki + i) się wysypuje. Wygląda mi na to, że błąd jest zupełnie gdzie indziej. Jesteś pewien, że 'wsk_ramki' nie jest np 'static' i jakaś inna funkcja go nie modyfikuje w trakcie gdy czytasz z uartu?
  • #6 7717922
    lotnick
    Poziom 15  
    Jestem na etapie sprawdzania czy nie wysypuje się przez brak miejsca w procesorze.
    Jestem raczej baskomowcem i w C czuje sie niepewnie.
    Nie wiem jak sprawdzić ile waży kod wynikowy, ile stosu , ile ramki.....

    Generalnie program NIE wysypuje się w pętli for. Wystarczy wpisać pętlę for ze wskaźnikiem i zmienną 'i' do listingu. Program tam nie wchodzi nawet.... a resetuje.
  • #7 7718983
    nenpa8lo
    Poziom 17  
    Masz deguger czy sugerujesz się jakimiś diodami led?
    Twój kompilator powinien ci wyświelać informacje typu RO (readonly) ZI RW itd.
  • #8 7721972
    lotnick
    Poziom 15  
    sugeruję si diodami led
    Mam JTAGa ale nigdy sie nim nie bawiłem. Programuję przez ISP.
  • #9 7722108
    mirekk36
    Poziom 42  
    lotnick napisał:

    
    char ramka[67];  //ramka danych
    char* wsk_ramki = ramka;
    for (int i=0;i<50;i++)
    {
    *(wsk_ramki+i)=uart_getc();  //TU SIĘ RESETUJE
    }
    



    Ja się nie dziwię, że to się wysypuje czyli że mogą być różne efekty dziwolągowate.

    Po pierwsze, skoro iteracja jest od 0 do 50, to po jakiego grzyba deklarujesz zmienną i jako int (czyli typ dwubajtowy ze znakiem). ????

    Do tego deklarujesz że wskaźnik *wsk_ramki ma typ char

    Następnie robisz operację dodawania na wskaźniku i to jeszcze na dwóch tak różnych typach, w efekcie końcowym kompilator może uznaje (trzeba by sprawdzić) przez niejawne rzutowanie, że posługujesz się wskaźnikiem 16-bitowym - a to już jak łatwo się domyśleć będzie prowadzić po równi pochyłej do katastrofy i np nadpisania stosu i resetu procka.

    Nie wiem po co do tak prostych rzeczy sięgać zaraz po działa typu debugery, jtagi i tym podobne

    W dwóch pozostałych przypadkach, gdzie piszesz tak:

    *(wsk_ramki+1)=uart_getc();


    jak sam widzisz nie ma konfliktu przy obliczaniu wskaźnika i na pewno nie zostanie niejawnie zmieniony na 16-bitowy - więc będzie OK

    albo tak

    ramka[i]=uart_getc();


    tutaj posługujesz się zmienną i tylko jako numerem elementu tablicy więc kompilator też nie zrobi nieprzewidzianych rzeczy.

    Reasumując - kompilator jest OK - tylko programiści pomimo, że wskaźniki to super narzędzie, często zapominają jak się nimi posługiwać.

    Napisz to tak jak poniżej to zobaczysz, że ręką odjął:

    char ramka[67];  //ramka danych
    char* wsk_ramki = ramka;
    for (uint8_t i=0;i<50;i++)
    {
    *(wsk_ramki+i)=uart_getc();  
    }


    albo tak

    char ramka[67];  //ramka danych
    char* wsk_ramki = ramka;
    for (char i=0;i<50;i++)
    {
    *(wsk_ramki+i)=uart_getc();  
    }
  • #10 7723852
    VanThor
    Poziom 19  
    mirekk36 napisał:
    Po pierwsze, skoro iteracja jest od 0 do 50, to po jakiego grzyba deklarujesz zmienną i jako int (czyli typ dwubajtowy ze znakiem). ????

    Do tego deklarujesz że wskaźnik *wsk_ramki ma typ char

    Następnie robisz operację dodawania na wskaźniku i to jeszcze na dwóch tak różnych typach, w efekcie końcowym kompilator może uznaje (trzeba by sprawdzić) przez niejawne rzutowanie, że posługujesz się wskaźnikiem 16-bitowym - a to już jak łatwo się domyśleć będzie prowadzić po równi pochyłej do katastrofy i np nadpisania stosu i resetu procka.

    A co ma ten char do int-a?
    wsk_ramki nie jest typu char! wsk_ramki wskazuje na element typu char, a więc jest typu "wskaźnika na char"! Poza tym nie jest ważne, na jaki typ wskazuje dany wskaźnik - i tak to będzie liczba 16, 32, lub 64bitowa (adres elementu docelowego) w zależności od architektury i optymalizacji. Aby była możliwa zmiana sposobu przechowywania wskaźnika (czyli czy jest zapisany w pamięci jako liczba 8, 16, 32 czy 64 bitowa) kompilator musiałby mieć niestandardowe rozszerzenia przy deklaracji zmiennych - przykładem może być kompilator firmy Keil dla C51, który umożliwiał taką deklarację wskaźnika, że był on zapisywany w pamięci jako 8, 16 lub 24 bitowa liczba.
    Wracając do AVR i GCC - jaką szerokość mają rejestry X, Y, Z używane jako wskaźniki? 16 bitów! Jak duża liczba jest potrzebna, żeby adresować wszystkie dostępne w AVR obszary pamięci RAM i Flash? Na pewno nie 8 bitowa. Czasem nawet 16 bitów to za mało.

    
    char * wskc;
    long * wskl;
    


    Mimo, że oba powyższe wskaźniki wskazują na elementy różnych typów, to sposób przechowywania wskaźników w pamięci będzie ten sam - oba wskaźniki będą zapisane jako x-bitowa liczba, gdzie x=16,32,64.

    mirekk36 napisał:
    jak sam widzisz nie ma konfliktu przy obliczaniu wskaźnika i na pewno nie zostanie niejawnie zmieniony na 16-bitowy - więc będzie OK


    Nie zostanie, bo najpewniej on już jest zapisany jako 16bitowy adres :)

    mirekk36 napisał:

    Reasumując - kompilator jest OK - tylko programiści pomimo, że wskaźniki to super narzędzie, często zapominają jak się nimi posługiwać.


    Właśnie jak widać często ludzie mylą typ elementu na który wskaźnik ma wskazywać z samym wskaźnikiem i typem, który jest używany do przechowywania wartości samego wskaźnika ("underlying type"). Polecam lekturę dobrej książki o języku C lub standardu tego języka.
  • #12 7724516
    VanThor
    Poziom 19  
    lotnick napisał:
    Kiedy chciałem przesunąć wskaźnik na adres obok poprzez dodanie do niego wartości 'i' to procesor resetuje się nie wchodząc nawet do tej pętli. Po włączeniu zasilania jest reset, reset, reset , reset........


    Ja się tylko zastanawiam, jak to jest możliwe, że się resetuje przed wejściem do tej pętli skoro zakładamy, że błąd jest w pętli.

    Cztery pytania do autora:
    1. Jak stwierdziłeś, że resetuje się przed wejściem do pętli?
    2. Watchdog jest wyłączony?
    3. Jak jest podpięte wyprowadzenie RST mikrokontrolera?
    4. Jak wygląda kompletny kod programu?
  • #13 7724897
    mirekk36
    Poziom 42  
    AVRowiec --> mi się trochę pomyliło z typami a wskaźnikiem, ale to co ty tu opowiadasz to już na maxa pomieszanie ;)

    wsk_ramki+i gdzie i=49 nie wychodzi poza zakres skoro bufor ma 67 znaków. Z dodawaniem chyba nie powinno być problemów. Pierwszy element tablicy plus 49 daje nam miejsce które nadal mieści się w tej tablicy.

    A zwalanie tego na błedy kompilatora i inne architektury to już zgroza.
  • #14 7725311
    jarekgol
    Poziom 38  
    Kolego lotnick, w folderze projektu powinien być plik o podobnej nazwie co plik .c z rozszerzeniem .asm, wklej go oraz pełen kod c .
  • #16 7726181
    hilbercik
    Poziom 11  
    AVRowiec ma 100% racji. Wskaźniki to potęga C o ile umie się z nich korzystać. Musisz wiedzieć na co aktualnie wskazuje wskaźnik! Każde dodanie do niego czegokolwiek powoduje skok o ilość bitów pamięci związanej z typem wskaźnika.

    Jeśli masz:
     char *wsk;
    wsk = 0; //wskaźnik wskazuje na zerowy adres
    wsk = wsk+1; // wskaźnik wskazuje na 0x0001;
    //ale jak napiszesz teraz:
    wsk = wsk + 5; //to nie będzie wskazywał na komórkę pamięci 0x0005; ale na 0x0006; bo już wcześniej został zinkrementowany. 

    Jest 23. Programisto, na co teraz wskazuje Twój wskaźnik? ;) Po poprawie typów zmiennych powinno być ok. Co do równoważności zapisów, to biorąc pod uwagę, że tablica jest zapisywana liniowo w pamięci, to Twoje 2 i 3 są identyczne; 1 jest niepoprawny!
  • #17 7726238
    mirekk36
    Poziom 42  
    hilbercik napisał:
    AVRowiec ma 100% racji.


    A gdzie ty w tym pierwszym kodzie widzisz inkrementowanie wartosci wskaźnika hmmm???

    *(wsk_ramki+i)=uart_getc();  //TU SIĘ RESETUJE 


    w takiej operacji wartość wskaźnika nie jest zmieniana ani na milimetr, w przeciwieństwie do takiej

    *(wsk_ramki++)=uart_getc();  //TU SIĘ RESETUJE 


    albo takiej:

    wsk_ramki+=i;
    *(wsk_ramki)=uart_getc();  //TU SIĘ RESETUJE 


    Więc o jakiej ty racji piszesz ???
  • #18 7726345
    hilbercik
    Poziom 11  
    Oczywiście masz rację. Przeczytaj posty jeszcze raz, bo chyba się nie rozumiemy.
  • #19 7726527
    VanThor
    Poziom 19  
    hilbercik napisał:
    AVRowiec ma 100% racji. Wskaźniki to potęga C o ile umie się z nich korzystać. Musisz wiedzieć na co aktualnie wskazuje wskaźnik! Każde dodanie do niego czegokolwiek powoduje skok o ilość bitów pamięci związanej z typem wskaźnika.

    Jeśli masz:
     char *wsk;
    wsk = 0; //wskaźnik wskazuje na zerowy adres
    wsk = wsk+1; // wskaźnik wskazuje na 0x0001;
    //ale jak napiszesz teraz:
    wsk = wsk + 5; //to nie będzie wskazywał na komórkę pamięci 0x0005; ale na 0x0006; bo już wcześniej został zinkrementowany. 

    Jest 23. Programisto, na co teraz wskazuje Twój wskaźnik? ;)


    Mam wrażenie, że późna godzina się odcisnęła na sensowności tego postu albo nie używamy tego samego języka.

    To:
    wsk = wsk + 5;
    *wsk=0x66;

    nie jest równoważne temu:

    Wprawdzie przy identycznej wartości początkowej wsk zapiszemy liczbę 0x66 w to samo miejsce w pamięci, ale w pierwszym przypadku zmodyfikujemy wskaźnik a w drugim nie!

    char buf[100];
    char * const wsk = buf;
    
    *(wsk+5)=0x66;
    *(wsk+6)=0x67;
    

    Po wykonaniu powyższego kodu otrzymamy:
    buf[5] = 0x66;
    buf[6] = 0x67;
    wsk = buf; // albo &( buf[0] ) jak kto woli
    Zatem wskaźnik od takiego użycia się nie zmieni. Zresztą użycie const w jego deklaracji gwarantuje błąd kompilacji przy próbie zmiany jego (wskaźnika) wartości.

    hilbercik napisał:
    Po poprawie typów zmiennych powinno być ok. Co do równoważności zapisów, to biorąc pod uwagę, że tablica jest zapisywana liniowo w pamięci, to Twoje 2 i 3 są identyczne; 1 jest niepoprawny!


    Jakich typów? Co ma liniowość ułożenia tablicy w pamięci do poprawności kodu numer 1?
    A kody numer 2 i 3 nie są identyczne - kod numer 2 będzie wpisywał kolejne znaki w to samo miejsce pamięci (ramka[1]). Jakim cudem ramka[1] jest równoważna ramka[i] podczas iteracji po i?
  • #20 7731890
    lotnick
    Poziom 15  
    Dzięki za ożywioną dyskusję.
    Nie sądziłem że tyle osób się odezwie.
    Dłubię w projekcie RIEGEL'a dotyczącym zapisu na kartę SD.
    Zmiana licznika na typ char nie daje wiele.
    JAk widzę są osoby które znają się na wskaźnikach, ale widzę też że jest to niejasne nie tylko dla mnie. Postaram się przeanalizować spokojnie to co napisaliście i odezwę się za kilka dni.
    Pozdrawiam
REKLAMA