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

Wskaźnik do struktury (WinAVR)

Qmexx 20 Paź 2009 13:10 2405 14
  • #1 7151772
    Qmexx
    Poziom 12  
    Mam taki (niezrozumiały dla mnie) przypadek:
    - wewnątrz procedury main() definiuję zmienną strukturalną beż żadnych modyfikatorów typu volatile, czy static;
    - programowo wypełniam jej pola wartościami;
    - z main() wywołuję inną procedurę zewnętrzną, do której przekazuję adres tej struktury, np. func(&struktura) - func() jest przystosowana do otrzymywania w wywołaniu wskaźnika do struktur tego typu;
    - jednak funkcja zewnętrzna func() otrzymuje jakiś bzdurny adres tej struktury, pod którym widzi same zera (adres około 4300(dec), czyli pod koniec wew. pamięci RAM uC).
    Jeśli jednak zdefiniuję tę strukturę jako globalną, czyli na zewnątrz main(), lub wewnątrz main() jako static, to wskaźnik jest prawidłowy i wszystkto działa jak należy.
    I tu nie rozumiem co robię źle...
    Czy nie można robić wskaźnika na strukturę lokalną? 8-O
    (jako IDE używam Eclipse, a kompilatorem jest WinAVR-20090313, piszę dla procka AT90CAN128)
  • #2 7151861
    tmf
    VIP Zasłużony dla elektroda
    Pokaz lepiej kod ilustrujacy twoj problem. Bo oczywisci emozna przekazywac adres do struktury lokalnej jesli z tej lokalnej funkcji jest wywolywana funkcja do ktorej ten adres przekazujesz.
    Ja bym obstawial, ze ta twoja struktura nie jest poprawnie inicjalizowana.
  • #3 7152141
    Qmexx
    Poziom 12  
    Oto fragmenty kodu ilustrujące wspomniany problem.
    Plik ee_dbase.h:
    struct st_rstat
    {
      unsigned rprob:1;
      unsigned tsync:1;
      unsigned rread:1;
      unsigned taval:1;
      unsigned haval:1;
      unsigned paval:1;
      unsigned eaval:1;
      unsigned probe:1;
    };
    typedef struct
    {
      unsigned char year;
      struct st_rstat rstat;
      unsigned char month;
      unsigned char day;
      unsigned char hour;
      unsigned char min;
      unsigned char sec;
      int temp;              
      unsigned humid;     
      unsigned press;      
    }t_st_dbrec;  // rekord o długości 13 bajtów
    
    extern unsigned char DB_write(t_st_dbrec *rec);
    Plik ee_dbase.c:
    unsigned char DB_write(t_st_dbrec * rec)
    {
      unsigned char buf[DBROW_LEN+1];
      unsigned char ret = 0;
      t_st_dbrec *wrec = (t_st_dbrec*)buf;
    
      if (ee_aval)
      	{    // tutaj, kiedy źle *rec wskazuje na bajty o wartości 0!!!
    	    memcpy(buf, (unsigned char *)rec, DBREC_LEN);
    	    wrec->rstat.rread = 0;
                   buf[DBROW_LEN-3] = DBUU1_VAL;
      	    buf[DBROW_LEN-2] = DBUU2_VAL;
      	    buf[DBROW_LEN-1] = DB_row_crc(buf);
      	    buf[DBROW_LEN] = 0xFF;
      	    EEPROM_write(empty_addr, buf, DBROW_LEN+1);
      	    EEPROM_read(empty_addr, buf, DBROW_LEN);
     	    if (buf[DBROW_LEN-1] == DB_row_crc(buf)) ret = 1;
      	      else db_fail |=EE_BADWRITE;
      	    newest_addr = empty_addr;
      	    empty_addr = (empty_addr + DBROW_LEN) % EEEPROM_SIZE;
      	    memcpy_P(buf, empty_rec_pattern, DBROW_LEN);
      	    EEPROM_write(empty_addr, buf, DBROW_LEN);
      	}
      return ret;
    }
    Plik main.c:
    #include "ee_dbase.h"
    
    //t_st_dbrec recq;  // dla tej struktury nie ma problemu ze wskaźnikiem
    
    int main(void)
    {
      t_st_dbrec recq;  // dla tej struktury jest problem ze wskaźnikiem
    //  static t_st_dbrec recq;  // dla tej struktury nie ma problemu ze wskaźnikiem
    
      while(1)
        {
           if (ru1rp != ru1wp)
            {
      	switch(ru1buf[ru1rp++])
    	  {
    	      case 'w': // zapis
    		       recq.year = 9;
    		       recq.rstat.eaval = 1;
    		       recq.rstat.haval = 1;
    		       recq.rstat.paval = 1;
    		       recq.rstat.probe = 0;
    		       recq.rstat.rprob = 0;
    		       recq.rstat.rread = 0;
    		       recq.rstat.taval = 1;
    		       recq.rstat.tsync = 0;
    		       recq.month = 10;
    		       recq.day = 19;
    		       recq.hour = 12;
    		       recq.min = 30;
    		       recq.sec = 01;
    		       recq.humid = 100;
    		       recq.press = 200;
    		       recq.temp = -5;
    		         // po tym struktura jest wypełniona jak należy!
    		       i = DB_write(&recq);  // <- procedura dostaje niedobry wskaźnik
                                   // po tym struktura nadal posiada wypełnione pola
    		       break;
    	}
           }
        }
    }
    Ja w tym specjalnie błędów nie widzę, ale problem ze wskaźnikiem jest.
  • #4 7152231
    tmf
    VIP Zasłużony dla elektroda
    Skad wiesz, ze przekazuje nieprawidlowy wskaznik?
    Nie podoba mi sie to:
    memcpy(buf, (unsigned char *)rec, DBREC_LEN); 

    Dlatego, ze buf definiujesz jako:
    unsigned char buf[DBROW_LEN+1]; 

    Pytanie brzmi, co to za stale, i czy czasem buf nie jest za maly? Dlaczego nie uzywasz sizeof do pobrania wielkosci struktury?
  • #5 7152292
    Qmexx
    Poziom 12  
    Nie no... nie jestem aż takim amatorem, żeby nie zdawać sobie sprawy z długości bufora. :)
    Używam sizeof :)
    W pliku ee_dbase.h mam takie definicje:
    #define DBREC_LEN sizeof(t_st_dbrec)    // długość bajtowa rekordu danych
    #define DBROW_LEN 16                    // długość wiersza na rekord w bazie

    A co do wskaźnika, to go sobie wyświetlam przez USART i stąd wiem.
    W nieprawidłowym przypadku wskaźnik ma wartość 4300(dec) i wskazuje na ciąg zer. W przypadku, gdy strukturę założę globalnie na zewnątrz main() lub jako static wewnątrz main(), to wskażnik ma wartość ok. 900(dec) i wskazuje na poprawne wartości, które wpisałem do struktury.
    Programuję w C od lat, napisałem wiele całkiem sporych softów i dopiero teraz natrafiłem na taki nieciekawy przypadek, kiedy nie mogę przekazać danych do procedury ze struktury zdefiniowanej wewnątrz main(). :)
    Podejrzewam błąd kompilatora lub jakąś małą lukę we własnej wiedzy na temat C. :)
    Ale dzięki za zainteresowanie! :)
    Problem jest właśnie w tym, że działanie programu zależy mi od sposobu lub/i miejsca zdefiniowania struktury.
  • #6 7152306
    piti___
    Poziom 23  
    Wydaje się że powinno działać, szczególnie że przy innych deklaracjach struktury działa. Przy okazji spytam do czego w deklaracji funkcji w pliku nagłówkowym służy extern ?
  • #7 7152319
    Qmexx
    Poziom 12  
    extern informuje inne moduły o istnieniu i budowie wywołania takiej funkcji. Kompilator nie daje wtedy komunikatów o "implicit declaration" :) Można też używać prototypów, ale jak tak nie lubię. :)

    Dodano po 30 [minuty]:

    I jeszcze jedno. W main() zakładam zmienną:
    unsigned testval;
    której wskaźnik przekazuję testowo do tej samej procedury
    extern unsigned char DB_write(t_st_dbrec *rec, unsigned *test);
    i = DB_write(&recq, &testval);
    i tu również procedura otrzymuje wskaźnik o wartości 4352(dec), a wartość widziana pod tym wskazaniem jest inna od wpisanej... Czyli sprawa nie dotyczy tylko struktur. Co robię źle? Jakieś opcje kompilacji trzeba zmienić, czy co?
    Ja już nie wiem co to ma być... :evil:
  • #8 7152516
    tmf
    VIP Zasłużony dla elektroda
    Sprobuje to potem skompilowac u siebie i zobacze co z tego wyjdzie.
    Ale jeszcze jedno glupie pytanie - jestes pewien, ze kompilujesz to na dobry procesor, ktorego uzywasz? W swoich programam praktycznie caly czas przekazuje wskazniki i nigdy nie mialem z tym problemow.
  • #9 7152615
    Freddie Chopin
    Specjalista - Mikrokontrolery
    Pewnie wybrałeś zły procesor, więc stos jest na adresach które w ogóle nie istnieją... Co zresztą widać, bo masz procek który ma 4kB pamięci, a adres - jak sam mówisz - jest już poza tym obszarem.

    Ewentualnie błąd w skryptach linkera dla tego układu.

    4\/3!!
  • #10 7159926
    Qmexx
    Poziom 12  
    Freddie Chopin napisał:
    Pewnie wybrałeś zły procesor, więc stos jest na adresach które w ogóle nie istnieją... Co zresztą widać, bo masz procek który ma 4kB pamięci, a adres - jak sam mówisz - jest już poza tym obszarem.
    Ten "nieprawidłowy" wskaźnik (jak go nazywam) jeszcze mieści się obszarze adresowym procka, który ma 4kB pamięci, ale zaczyna się od adresu 0x0100 (niżej są rejestry SFR) i rozciąga do 0x10FF, czyli dziesiętnie 4351. Również procesor jest odpowiednio wybrany.
    Napisałem krótki test w AVRStudio:
    - plik main.c:
    extern void func(unsigned *wsk1);
    
    int main(void)
    {
      unsigned zm = 0x1234;
    
      func(&zm);
    
      while(1);
    }
    - plik modul.c:
    void func(unsigned *wsk1)
    {
       volatile unsigned zm2 = *wsk1;
    }
    Z symulacji wynika, że kompilator zakłada zmienną zm w procedurze main() na stosie (stos rozpoczyna się od szczytu pamięci RAM mikrokontrolera i schodzi w dół), następnie przekazuje ten adres, normalnie przez R24:R25 do funkcji func(). Funkcja pobiera sobie wartość spod podanego adresu - de facto ze stosu - ale normalnie spod przekazanego wskazania. W tym programiku wartość 0x1234 została przekazana do funkcji poprawnie.
    Tu wyjaśniło mi się, dlaczego ów mój "nieprawidłowy" wskaźnik wskazuje gdzieś na koniec RAMu. Nie rozumiem jednak dlaczego w moim programie nie ma tam spodziewanej zawartości struktury, a jedynie zera... co czyści mi to miejsce? Nie wiem, dziwne...
    W poprzednim poście napisałem, że adres zmiennej testowej był 4352, a to już jest poza pamięcią.... 8-O Ale teraz jest adres 4097 i dalej jest źle...
    Wciąż nie wiem, czy to ja gdzieś robię błąd, czy też jest to wina kompilatora - pierwszy raz mam takie zjawisko, a zawsze bezboleśnie używałem różnych takich konstrukcji....
  • #11 7160682
    michalko12
    Specjalista - Mikrokontrolery
    Qmexx napisał:

    Wciąż nie wiem, czy to ja gdzieś robię błąd, czy też jest to wina kompilatora - pierwszy raz mam takie zjawisko, a zawsze bezboleśnie używałem różnych takich konstrukcji....


    Ponieważ jest to zmienna lokalna może być przechowywana tylko w rejestrach procesora lub cos w tym rodzaju. Spróbuj ja zrobić volatile.
  • #12 7164009
    Qmexx
    Poziom 12  
    Otóż muszę zdjąć wszystkie psy z kompilatora, które w ferworze walki na nim powiesiłem i przyznać się do własnego błędu. :oops:
    Błąd był mój. W zupełnie innym niż te, które zamieściłem fragmencie programu, gdzie czytam dane z samego końca zewnętrznej pamięci EEPROM, przez nieumiejętne użycie predekrementacji w złożonym logicznie warunku if, czytałem w tym jednym przypadku o jeden bajt za dużo, co wykraczało poza przydzielony bufor dla danych... A w takich sytuacjach, jak wiadomo dzieją się dziwne rzeczy... :)
    Zmyliło mnie to, że wstawiając w kod programu "halt" w postaci while(1); dużo niżej niż fragment, w którym wystąpił problem objawu nie było. Widocznie kompilator nie kompilował reszty i zmieniało się ustawienie zmiennych w pamięci i wówczas nadpisanie 1 bajtu nie powodowało widocznych problemów.
    Dzięki za zainteresowanie moim problemem! :)
  • #13 7164038
    loocasm
    Poziom 15  
    Qmexx napisał:
    Nie no... nie jestem aż takim amatorem, żeby nie zdawać sobie sprawy z długości bufora. :)


    :D Ale serdeczne gratulacje za rozwiązanie problemu :)
  • #14 7164129
    Qmexx
    Poziom 12  
    Cytat:
    Qmexx napisał:
    Nie no... nie jestem aż takim amatorem, żeby nie zdawać sobie sprawy z długości bufora. :)

    :D Ale serdeczne gratulacje za rozwiązanie problemu :)

    Wyrwał z kontekstu i mi przypiął... :lol: No cóż, trochę mi się należy, ale czy są programiści, którzy nie robią błędów? :sm37:
    Długości buforów, o które tam chodziło, a to co znalazłem, to dwie różne rzeczy. :)
  • #15 7164211
    loocasm
    Poziom 15  
    Qmexx napisał:
    przez nieumiejętne użycie predekrementacji w złożonym logicznie warunku if, czytałem w tym jednym przypadku o jeden bajt za dużo, co wykraczało poza przydzielony bufor dla danych...


    Wielkość bufora to wielkość bufora, obojętnie czy bufora A, czy bufora B.. ;)

    Jasne, że każdy programista popełnia błędy, po prostu czytając wątek, gdy natrafiłem na zarzekanie się kolegi, że "nie jest amatorem i umie sobie poradzić z wielkością bufora" czułem, że problem będzie dotyczył wielkości jakiegoś bufora...;)
    Po prostu nie ma się co zarzekać, a popełnianie błędów to ludzka rzecz :)
    Pozdrawiam
REKLAMA