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.

Wskaźnik do struktury (WinAVR)

Qmexx 20 Paź 2009 13:10 2144 14
  • #1 20 Paź 2009 13:10
    Qmexx
    Poziom 11  

    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)

    0 14
  • #2 20 Paź 2009 13:46
    tmf
    Moderator Mikrokontrolery Projektowanie

    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.

    0
  • #3 20 Paź 2009 15:02
    Qmexx
    Poziom 11  

    Oto fragmenty kodu ilustrujące wspomniany problem.
    Plik ee_dbase.h:

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

    0
  • #4 20 Paź 2009 15:19
    tmf
    Moderator Mikrokontrolery Projektowanie

    Skad wiesz, ze przekazuje nieprawidlowy wskaznik?
    Nie podoba mi sie to:

    Code:
    memcpy(buf, (unsigned char *)rec, DBREC_LEN); 

    Dlatego, ze buf definiujesz jako:
    Code:
    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?

    0
  • #5 20 Paź 2009 15:37
    Qmexx
    Poziom 11  

    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:

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

    0
  • #6 20 Paź 2009 15:39
    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 ?

    0
  • #7 20 Paź 2009 16:12
    Qmexx
    Poziom 11  

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

    Code:
    unsigned testval;
    której wskaźnik przekazuję testowo do tej samej procedury
    Code:
    extern unsigned char DB_write(t_st_dbrec *rec, unsigned *test);
    Code:
    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:

    0
  • #8 20 Paź 2009 16:28
    tmf
    Moderator Mikrokontrolery Projektowanie

    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.

    0
  • #9 20 Paź 2009 16:51
    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!!

    0
  • #10 22 Paź 2009 10:22
    Qmexx
    Poziom 11  

    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:
    Code:
    extern void func(unsigned *wsk1);
    

    int main(void)
    {
      unsigned zm = 0x1234;

      func(&zm);

      while(1);
    }
    - plik modul.c:
    Code:
    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....

    0
  • #11 22 Paź 2009 14:44
    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.

    0
  • #12 23 Paź 2009 09:17
    Qmexx
    Poziom 11  

    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! :)

    0
  • #13 23 Paź 2009 09:31
    loocasm
    Poziom 14  

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

    0
  • #14 23 Paź 2009 10:08
    Qmexx
    Poziom 11  

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

    0
  • #15 23 Paź 2009 10:44
    loocasm
    Poziom 14  

    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

    0