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

łańcuchy w C w pamięci Flash i przekazywanie ich do funkcji

slawek55 06 Lis 2007 14:58 1976 15
REKLAMA
  • #1 4452182
    slawek55
    Poziom 23  
    Posty: 788
    Pomógł: 3
    Ocena: 61
    Cześć.
    Piszę sobie funkcję która ma przyjmować tekst w postaci łańcuch i wyświetlic go na LCD.
    Niby prymitywne, ale napotakałem na kilka przykrych niespodzianek.
    Funkcja wygląda tak:
    void lcd_p(prog_char *ptr) 
    { 
    char znak; 
    while( 0 != (znak = pgm_read_byte(ptr++)) ) 
    lcd_data(znak); 
    } 
    


    No i jak piszę wywołanie funkcji np
    lcd_p("tekst"); 


    to tak nie mogę bo pojawia mi sie to zarówno w RAM jak i we Flash. Pisząc jeszcze inaczej
    lcd_p(PSTR("tekst");

    to koppia nie zostaje umieszczona w RAM ale pojawia się komunikat:
    warning: passing arg 1 of `lcd_p' discards qualifiers from pointer target type

    Dlaczego tak jest. DLaczego jak zmienię funkcję a dokładniej parametr funkcji i napiszę
    void lcd_p(const prog_char *ptr) 
    { ...


    To już nie pojawia sie ostrzeżenie. Co powoduje że dodając const to się zmieia.


    Mam nadzieję że jakoś wyraźnie to opisałem i nie namieszałem. A oczywiście kompilator ARG-GCC
  • REKLAMA
  • #2 4452231
    markosik20
    Poziom 33  
    Posty: 2261
    Pomógł: 208
    Ocena: 147
    Cytat:
    Co powoduje że dodając const to się zmieia


    Bo wyraźnie określasz wtedy że wskaźnik użyty w tej funkcji ma "wskazywać" na obszar pamięci stałej (flash). Bez tego wskaźnik domyślnie jest ustawiony na RAM. Dlaczego taki zapis a nie inny? To wynika ze specyfikacji GCC.
  • #3 4453034
    slawek55
    Poziom 23  
    Posty: 788
    Pomógł: 3
    Ocena: 61
    Czytałem że const użyte w ten sposób ma określać tylko że wskaznik jest stały, czyli niemodyfikowalny.
    A z drugiej strony dlaczego zamiast używać połaczenia const większośc stosuje rzutowanie tzn. (prog_char *)PSTR("tekst") podczas wywołania funkcji. Dlaczego taka kompilikacja?
  • #4 4453309
    markosik20
    Poziom 33  
    Posty: 2261
    Pomógł: 208
    Ocena: 147
    slawek55 napisał:
    Czytałem że const użyte w ten sposób ma określać tylko że wskaznik jest stały, czyli niemodyfikowalny.


    W Twoim przypadku wzkaźnik jest modyfikowalny a wyrażenie const informuje kompilator że dotyczy on danych "stałych", niezmiennych.

    Cytat:
    A z drugiej strony dlaczego zamiast używać połaczenia const większośc stosuje rzutowanie tzn. (prog_char *)PSTR("tekst") podczas wywołania funkcji. Dlaczego taka kompilikacja?


    Bo taka jest specyfika tego kompilatora :wink:. Zamierzony efekt można osiągnąć różną drogą.
  • REKLAMA
  • #5 4453414
    adamusx
    Poziom 27  
    Posty: 977
    Pomógł: 94
    Ocena: 28
    Dlatego ze AVRy maja architekture Harwardzka, a co za tym idzie rozdzieloną pamiec danych i programu, czyli pamiec RAM jest w innej przestrzeni adresowej niz pamiec FLASH. Dlatego do odczytu i zapisu flash kompilator GCC wykorzystuje inne funkcje (pgm_read_byte, prog_char itp)
  • #6 4461284
    slawek55
    Poziom 23  
    Posty: 788
    Pomógł: 3
    Ocena: 61
    Powiedzmy że mam funkcję napisaną w AVR-GCC wyświetlającą na LCD tekst.

    deklaruję sobie np cos takiego
    void lcd_p(const prog_char *ptr) 
    { 
    char a; 
    while((a=pgm_read_byte(ptr++)))LCD_Data(a); 
    }

    wywołuje ją poprzez
    lcd_p(PSTR("jakiś tekst na lcd"));

    dlaczego nie ma róznicy w definicji funkcji a dokładniej w jej parapetrze ptr jak napiszę zamiast powyższczego takie coś
    void lcd_p(const char *ptr) 
    { 
    char a; 
    .......... 
    


    Czyli zamiast prog_char napiszę char.
    W ogóle tego nie rozumiem. raz jest tak a raz tak, czy jest jakaś reguła na to?
  • REKLAMA
  • #7 4461364
    shg
    Poziom 35  
    Posty: 2289
    Pomógł: 339
    Ocena: 135
    Nie ma różnicy, bo te wskaźniki są takie same, tzn. elementy na które wskazują mają ten sam rozmiar, znak i kwalifikator (const), to w zasadzie jest warunkiem wystarczającym do poprawnej kompilacji. Arytmetyka na tych wskaźnikach wygląda dokładnie tak samo. Różnią się tylko atrybutem, ale nie typem.
  • #8 4461398
    slawek55
    Poziom 23  
    Posty: 788
    Pomógł: 3
    Ocena: 61
    A czy atrybut nie wskazuje na to iż dana zmienna ma zostać utworzona w pamięci programu. Tzn czy taka deklaracje nie mówi o tym że musi to zostać utworzone w pamięci Flash a nie RAM? Właśnie tego nie rozumiem.
  • #9 4461426
    szelus
    Poziom 34  
    Posty: 1508
    Pomógł: 315
    Ocena: 53
    GCC nie radzi sobie z architekturą harwardzką. Taka jego specyfika i już. Zauważ, że odwołanie do dpowiedniej pamięci nie jest automatyczne - trzeba ręcznie wołać odpowiednią funkcję - pgm_read_byte() itd.
    Z punktu widzenia składni C, typy danych różniące się tylko atrybutem (który jest z kolei rozszerzeniem, a nie częścią języka) są równoważne.
  • REKLAMA
  • #10 4461475
    markosik20
    Poziom 33  
    Posty: 2261
    Pomógł: 208
    Ocena: 147
    slawek55 napisał:
    A czy atrybut nie wskazuje na to iż dana zmienna ma zostać utworzona w pamięci programu.


    Zmienna nie może być utworzona w pamięci programu. U Ciebie wskaźnik MUSI być w RAM bo jest modyfikowany. Atrybut Twojego wskaźnika służy do określenia na jaką pamięć on wskazuje i kompilator wtedy wie że adres na jaki wskazuje wskaźnik to adres stałej (danej) w pamięci programu.

    Tak jak napisał szelus
    Cytat:
    GCC nie radzi sobie z architekturą harwardzką
    i trzeba parę "sztuczek" żeby osiagnać pożądany cel przy działaniu na pamięci programu.
  • #11 4461705
    slawek55
    Poziom 23  
    Posty: 788
    Pomógł: 3
    Ocena: 61
    No tak ale jeśli tworzę wskażnik char *ptr, czyli powienien wskazywać na daną w pamięci RAM a przekazuję do niego wskazanie na dana która ma sie znajdować w pamięci Flash. CZyżby to znaczyło że nie ma znaczenia czy do wskażnika zapiszę adres pamięci flash a RAM. Przecież prog_char *ptr to nie jest to samo co char *ptr. Jeden wskażnik powienien wskazywać na RAM a drugi na flash, natomiast Wy mówicie że nie ma różnicy.
    Inna sprawa to co co za "sztuczki" o których wspomina markosik20 Można bliższe szczególy o tych sztuczkach?
    I co to są atrybuty. Wiem ze może to laickie pytanie, ale naprawdę nie wiem o co chodzi. Czy atrybut nakazuje kompilatorowi jakoś np inaczej traktować zmienną, jeśli tak to powinien ją utworzyć w pamięci Flash, po jak tworzę tablicę, którą chcę umieścić właśnie we flash to przecież piszemy np
    prog_char tablica[3]="1,2,3,4";

    Czyż nie?
  • #12 4461767
    szelus
    Poziom 34  
    Posty: 1508
    Pomógł: 315
    Ocena: 53
    markosik20 napisał:
    Atrybut Twojego wskaźnika służy do określenia na jaką pamięć on wskazuje i kompilator wtedy wie że adres na jaki wskazuje wskaźnik to adres stałej (danej) w pamięci programu.

    Żeby to uściślić. Atrybut, o którym mówimy, to 'section'. Pozwala on umieścić dane np. w różnych klasach pamięci, ale dla kompilatora jest to (w zasadzie) przezroczyste. Tą informację kompilator przekazuje do linkera, który dopiero rozdziela dane pomiędzy różne klasy pamieci. Dlatego włśnie GCC słabo sobie radzi z architekturą harwardzką - kompilator nie jest w stanie np. użyć specjalnych rozkazów dostępu do pamięci flash i stąd specjalne funkcje (w asm).

    Teraz jeśli chodzi o wskaźniki, to informacja o klasie pamięci na jaką wskaźnik wskazuje jest dla kompilatora zupełnie przezroczysta i z jego punktu widzenia, nadmiarowa. Rozróżnienie to jest przydatne jedynie dla programisty, aby łatwiej było się zorientować, że należy np. użyć specjalnych funkcji dostępu.
  • #13 4462593
    romario4
    Poziom 16  
    Posty: 138
    Pomógł: 17
    Ocena: 3
    slawek55 napisał:
    Przecież prog_char *ptr to nie jest to samo co char *ptr. Jeden wskażnik powienien wskazywać na RAM a drugi na flash, natomiast Wy mówicie że nie ma różnicy.

    Witam!
    A mnie się wydaję że prog_char *ptr nie oznacza że wskaźnik ma wskazywać na zmienną w flash tylko sam ma się tam znajdywać a wskazywać może na flash lub na ram. Tak samo zapis char *ptr alokuje wskaźnik ptr w ram a na co będzie wskazywać to już nasza sprawa. Osobiście jeśli chcę przekazać do funkcj zmienną zapisaną w flash to używam zapisu np. lcdstr_P(const char*).
  • #14 4463228
    shg
    Poziom 35  
    Posty: 2289
    Pomógł: 339
    Ocena: 135
    romario4 napisał:
    A mnie się wydaję że prog_char *ptr nie oznacza że wskaźnik ma wskazywać na zmienną w flash tylko sam ma się tam znajdywać a wskazywać może na flash lub na ram.

    Żeby było jak piszesz, trzeba by zrobić tak:
    typ_na_ktory_wskazuje_wskaznik *  PROGMEM wskaznik

    Kwalifikatory i atrybuty obiektu wskaźnika znajdują się przed jego nazwą, ale za '*'.
    Podobnie:
    const int *ptr

    Wskazuje na obiekt typu int posiadający kwalifikator const.

    int * const ptr

    Wskaźnik posiadający kwalifikator const, wskazujący na obiekt typu int. Wbrew pozorom wcale nie bez sensu i też czasem przydatne ;]
  • #15 4464545
    slawek55
    Poziom 23  
    Posty: 788
    Pomógł: 3
    Ocena: 61
    CZeść.
    Wiem że dobrze znasz się na tym czy możesz mi wyjaśnić co się dzieje.
    Przeprowadziłem sobie próby z użyciem zmiennej prog_char (chodzi o typ prog) i tego już nie rozumiem w ogóle.
    Jeśli zadeklarowałem zmienną
    prog_char data=10;
    wewnatrz funkcji main to sledząc tą zmienną okazało się że został jej przydzielony rejestr R24.
    Natomiast jak to samo napisałem przed main to pojawił się błąd (zrozumiały)
    error: assignment of read-only variable `data'

    Nie rozumiem dlaczego tak się dzieje przy komilacji?
  • #16 4464569
    shg
    Poziom 35  
    Posty: 2289
    Pomógł: 339
    Ocena: 135
    slawek55 napisał:
    Jeśli zadeklarowałem zmienną
    prog_char data=10;
    wewnatrz funkcji main to sledząc tą zmienną okazało się że został jej przydzielony rejestr R24.

    Wyłącz optymalizację to też dostaniesz błąd. Kompilator "stwierdził", że zmienna nie będzie "widziana" spoza main(), ma wolny rejestr i w niczym nie przeszkadza używanie tej zmiennej jako rejestru (być moze było to konsekwencja innych optymalizacji), więc można usprawnić działanie przypisując zmienną do rejestru.
    slawek55 napisał:
    Natomiast jak to samo napisałem przed main to pojawił się błąd (zrozumiały)
    error: assignment of read-only variable `data'

    Nie rozumiem dlaczego tak się dzieje przy komilacji?

    W drugim wypadku, oczywiście słusznie, kompilator nie przydzielił rejestru (rejestry nie są przydzielane automatycznie dla zmiennych globalnych, bo było by to zbyt duże marnotrawstwo), przydzielone zostało miejsce dla "zmiennej" w pamięci flash, po czym "stwierdził", że na etapie inicjalizacji zmiennych (który to wykonuje się w początkowej fazie działania programu) nie będzie można zapisać tam jej wartości (zadeklarowałeś ją jak zmienną, więc będzie inicalizowana jak inne zmienne). Zresztą taka konstrukcja jest zupełnie bezsensowna, bo używając tej wartości (10) jako części wyrażenia dostanie się ten sam efekt - wartość zostanie zapisana w pamięci flash, ale jako jeden z operandów. Nawet gdybyś się uparł i chciał jej wartość odczytywać z pamięci flash, to efekt będzie ze wszech miar niezadowalający, po będziesz musiał odwołać siędo pamięci programu przez wskaźnik i wykonać LPM, a gdyby wartość ta była użyta jako stała, to w najgorszym przypadku konieczne jest ładowanie jej do rejestrów przed operacją, a w najlepszym stanie się ona jednym z operandów instrukcji.

Podsumowanie tematu

✨ Dyskusja dotyczy problemów związanych z przechowywaniem łańcuchów znaków w pamięci Flash mikrokontrolerów AVR oraz przekazywaniem ich do funkcji wyświetlających tekst na LCD. Omówiono różnicę między wskaźnikami typu `prog_char *` a `const prog_char *` oraz wpływ kwalifikatora `const` na eliminację ostrzeżeń kompilatora GCC. Wyjaśniono, że architektura Harwardzka AVR rozdziela pamięć programu (Flash) i danych (RAM), co wymaga stosowania specjalnych funkcji, takich jak `pgm_read_byte()`, do odczytu danych z Flash. Wskaźniki w C nie rozróżniają automatycznie przestrzeni pamięci, dlatego konieczne są atrybuty i specjalne makra (np. `PSTR`) do umieszczania danych w Flash. Kwalifikatory i atrybuty, takie jak `const` i `PROGMEM`, informują kompilator i linker o lokalizacji danych, ale nie zmieniają typu wskaźnika, co powoduje konieczność stosowania rzutowań lub odpowiednich deklaracji funkcji. Omówiono także, że kompilator GCC nie obsługuje w pełni architektury Harwardzkiej, co wymusza ręczne zarządzanie dostępem do pamięci Flash. Dodatkowo wyjaśniono kwestie związane z deklaracją zmiennych typu `prog_char` i ich alokacją w rejestrach lub pamięci Flash oraz błędy wynikające z próby inicjalizacji zmiennych stałych w Flash jako zmiennych globalnych.
Podsumowanie wygenerowane przez AI na podstawie treści dyskusji.
REKLAMA