Ads_700x200

piątek, 24 sierpnia 2012

Eclipse - kłopot z PSTR() ? - przeczytaj ;)

Witam,

Ostatnio odkąd udało mi się po raz pierwszy użyć najnowszego toolchaina Atmela pod Eclipse, (a to dzięki kolegom SunRiver i Krauser z www.forum.atnel.pl), okazało się, że w kodach które dotąd pisałem pojawiło się mnóstwo ostrzeżeń (Warnings!) .... związanych nie tylko z użyciem zmiennych zdefiniowanych w pamięci FLASH, ale co ciekawe także w liniach mojego kodu gdzie stosowałem swoje własne funkcje z końcówką _P, co ma oznaczać, że jej argumentem będzie właśnie zmienna czy wskaźnik do zmiennej znajdującej się w pamięci FLASH. Okazuje się bowiem, że w nowym toolchainie języka C, postanowiono zrobić porządek, którego wcześniej nie było a widać to nawet po tym co wyżej napisałem. No bo jakim prawem użyłem określenia "zmienna w pamięci FLASH" ??? skoro to w zasadzie nie żadna zmienna tylko STAŁA z punktu widzenia programu w C. W końcu pamięć FLSAH jest tylko do odczytu - zgodzisz się chyba ze mną na tym etapie? prawda ? - no i trudno nie przyznać racji, że postanowiono zrobić z tym porządek. Na czym polega ten porządek ?




Otóż wprowadzono nieodwołalnie zasadę, że od momentu użycia najnowszej wersji toolchaina Atmela nie można już zdefiniować "zmiennej" w pamięci FLASH, tak jak robiliśmy to beztrosko do tej pory, czyli:


char napis[] PROGMEM = "jakis napis";


Ba! dodatkowo kompilator już nawet zgłosi tu BŁĄD! a nie tylko warning ;) o takiej treści:


variable 'napis' must be const in order to be put into read-only section by means of


co oznacza ni mniej ni więcej niż tylko to, że w końcu trzeba pójść po rozum do głowy, i skoro definiujemy zmienną TYLKO do ODCZYTU, to wtedy ma ona charakter stałej czyli TRZEBA użyć operatora const ! w ten sposób jak niżej:


const char napis[] PROGMEM = "jakis napis";


chyba się zgodzicie, że to wydaje się być bardziej logiczne prawda ? no ale przez to musi być zgodne ze standardem języka C ! .... bo jak inaczej. Więc nie ma co załamywać rąk i pozmieniać w swoich wszystkich poprzednich kodach źródłowych takie definicje dodając wszędzie ten przedrostek const.

No i wszyscy byliby szczęśliwi, ale to nie koniec kłopotów ;) .... niestety standard C jest nieubłagany i bardzo dobrze! .... skoro zmienna opatrzona jest przedrostkiem const, to nie można po drodze zmieniać jej charakteru gdy np przekazujemy ją do funkcji jako argument ! Dlatego zauważymy od razu warningi w naszych wcześniejszych funkcjach z końcówką _P, które miały używać takich argumentów, a były zdefiniowane w ten sposób:


void lcd_str_P( char * str );


wcześniej nie bacząc na nic, ot tak po prostu przekazywaliśmy te argumenty a przecież tu nastąpić by miała niejawna zamiana const, na zmienną bez tego przedrostka ! .... hola hola !!! tak nie można język C na to nie pozwala w standardzie. Jak coś jest const, to nie można sobie po drodze takich numerów robić, tzn jak ktoś chce i jest świadom konsekwencji to jak zwykle może .... no ale zgodnie z zasadą aby doprowadzać każdy swój kod do ilości Warnigs = 0 !!!! musimy się tego pozbyć. Czy ciężko tego dokonać ? No pewnie, że nie, wystarczy tylko we własnym kodzie stosować, (a w starszych kodach niestety żmudnie poprawić), także przedrostek const dla argumentów - tak jak żąda tego standard języka C, a zatem powyższą deklarację i definicję funkcji wystarczy zmienić na:

void lcd_str_Pconst char * str );

TRUDNE ? chyba nie ;) .... niezrozumiałe ? chyba nie ..... czy to koniec kłopotów ????

Niestety też nie ;) ponieważ warningi ukażą nam się jeszcze w innych miejscach. W jakich ?

Pamiętacie ? W pierwszej książce opisywałem takie makro o nazwie PSTR(). No właśnie jak nie pamiętacie to proszę sięgnąć do książki pt: "Mikrokontrolery AVR Język C Podstawy programowania". W skrócie przypomnę tylko, że jeśli chcieliśmy tak na szybko użyć swojej funkcji np do przesłania znaków z tablicy (stringa) zdefiniowanego w pamięci programu czyli FLASH, to można było zrobić to w uproszczony sposób tak:


lcd_str_P( PSTR("jakis napis") );


zamiast np tak:

char napis[] PROGMEM = "jakis napis";

lcd_str_P(  napis  );

(naturalnie taki sposób jest ZAWSZE poprawny)

Pamiętacie to chyba albo kojarzycie już to PSTR() ..... Kłopot w tym, że niestety - skoro mamy mieć standard - to okazuje się, że nowsza wersja makra PSTR w nowym toolchainie także korzysta ze specyfikatora const, aby umieścić nasz napis (tablicę) w pamięci FLASH. I teraz w liniach tego typu:

lcd_str_P( PSTR("jakis napis") );

dostajemy wciąż Warning:


passing argument 1 of 'buf_str_P' discards 'const' qualifier from pointer target type [enabled by default]


O Matko! .... tu już niektórzy załamują ręce! - to co teraz z nami będzie - jak sobie z tym poradzić ? Przecież to jakaś masakra? .... spokojnie ja też tak na początku pomyślałem - więc nie robię tu z siebie alfy i omegi, tylko pokazuję wam, że zamiast się załamywać zacząłem żmudnie szukać rozwiązania bo musi jakieś być - w końcu twórcy GCC mają jak to się mówi "ŁEB NA KARKU", i nie wymyślili by dla nas jakiejś głupoty, żeby nas katować.... postanowiłem się douczyć ... czyli poszukać rozwiązania.

I co ? okazało się, że jest kurka wodna, PROSTE JAK DRUT rozwiązanie ;) .... stąd ten cały post i chęć podzielenia się z Wami tą informacją ;)

wystarczy poprawić deklaracje i definicje swoich własnych funkcji w taki sposób jak pisałem wyżej, czyli:

void lcd_str_Pconst char * str );

i teraz już spokojnie możesz w kodzie użyć:

lcd_str_P( PSTR("jakis napis") );

a nagle w czarodziejski sposób znikną Warningi ;) które przerażały nas na początku przy tym PSTR.

Owszem można narzekać, że to ktoś wymyślił jakieś bzdurne utrudnienie, że to głupi pomysł, że teraz będzie trzeba więcej pisać, że teraz będzie trzeba przerabiać swoje programy itd....

Mam nadzieję, że ten krótki artykuł pomoże wielu osobom, które wcześniej tak jak ja - same poszukiwały rozwiązania ale wiadomo to zajmuje czas.... Skoro udało mi się więc dzielę się informacją ;)

SŁOWO NA ZAKOŃCZENIE:

Gdyby ktoś przypadkiem stosował najnowsze Eclipse Juno to pewnie się zdziwi, że pomimo tu opisanych przeze mnie rzeczy, nadal występują jakieś dziwne podkreślenia - jakby warningi przy PSTR(). Ale o dziwo w warnigach kompilatora tego nie widać. To o co tu chodzi ? czy można coś z tym zrobić ?

Można można ;) .... proszę tylko przeczytać to:

http://forum.atnel.pl/post13899.html#p13899

a wszystko się wyjaśni ;) i skończą się także te kłopoty.

UPDATE! 3 grudzień 2012 ! *********** NOWY KŁOPOT ZWALCZONY ;)

proszę poczytać opis na dole artykułu - video poradnika:


AVR-EEPROM Struktury - poradnik do książek



http://mirekk36.blogspot.com/2012/11/avr-eeprom-struktury-poradnik-do-ksiazek.html

11 komentarzy:

  1. Jak zwykle wszystko zwięźle i merytorycznie a na dodatek wyręcza innych w poszukiwaniu rozwiązania (w tym mnie). Stary już chyba jestem i widzę jednak pewną zagadkę w tym tekście dotyczącą PSTR, choć nie mam pewności czy dobrze to rozumiem:

    "... a zatem powyższą deklarację i definicję funkcji wystarczy zmienić na:

    void lcd_str_P( const char * str );

    ... czy to koniec kłopotów???? Niestety też nie ;) ponieważ warningi ukażą nam się jeszcze w innych miejscach. W jakich ? PSTR"

    a dalej:

    ".... postanowiłem się douczyć ... czyli poszukać rozwiązania....wystarczy poprawić deklaracje i definicje swoich własnych funkcji w taki sposób jak pisałem wyżej, czyli:

    void lcd_str_P( const char * str );

    a nagle w czarodziejski sposób znikną Warningi ;) które przerażały nas na początku"

    Jak dla mnie jedna i druga, sugerowana zmiana deklaracji jest taka sama a jedna nie rozwiązuje problemu z PSTR a druga niby rozwiązuje...może to jednak jakiś skrót myślowy i dlatego trudno załapać...robiw

    OdpowiedzUsuń
    Odpowiedzi
    1. to nie żaden skrót myślowy - bo najpierw piszę o kłopotach z funkcjami własnymi _P i zmiennymi bez const, a drugi przypadek związany jest dopiero z PSTR() a to całkiem coś innego.

      Dokładne wyjaśnienie na forum:

      http://forum.atnel.pl/post13875.html#p13875

      Usuń
  2. A co w przypadku tego toolchaina można zrobić ze wskaźnikami do pamięci FLASH np.
    const prog_char *ptr;
    niestety w obecnej wersji nie jest już możliwe takie zadeklarowanie wskaźnika. Rozwiązaniem wedle tej zasady było by zadeklarowanie wskaźnika
    const char *const ptr PROGMEM;
    niestety nie pozwala to na operacje na tym wskaźniku np. ptr++ (zgodnie zresztą z deklaracją)

    OdpowiedzUsuń
    Odpowiedzi
    1. Tylko że można spokojnie się bez tego obejść. Podaj przykład do czego ci potrzebny taki wskaźnik a podam ci przykład jak to rozwiązać inaczej.

      Usuń
    2. jak można zastąpić:
      void LCD_pisz_ciag_flash(const prog_char * s)
      {
      // pomocnicza zmienna
      char c;
      // dopóki wskazywany przez s jest różny od zera
      while((c = pgm_read_byte(s++)))
      // zapis znaku
      LCD_pisz_znak(c);
      }

      Usuń
  3. Panowie z innej beczki - nie jestem zawodowym programistą - ale możliwe jest że po zastosowaniu PROGMEM - program spowolnił i po pewnym czasie wiesza się? W PROGMEMach zawarłem statyczny szablon strony www - zdecydowanie szybciej działa. Zmienne (wyniki) prezentuje już standardowo na www.

    OdpowiedzUsuń
  4. a jak będzie w przypadku tej deklaracji extern TATCMD polecenia_at[] PROGMEM; ?

    const przed EXTERN czy za ?

    OdpowiedzUsuń
    Odpowiedzi
    1. EXTERN panie kochany jest ZAWSZE na początku

      Usuń
    2. teraz wygląda tak
      "extern const TATCMD polecenia_at[] PROGMEM;",
      ale nadal błąd "expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘PROGMEM’"
      W C jestem słaby i nie wiem co dalej

      Usuń
    3. dzieki :) działa

      Usuń