Strony

środa, 20 listopada 2013

Tablica wskaźników we flash ( PROGMEM ) Atmel toolchain

Witam,

Taka mała krótka porada dla wszystkich zjadaczy nowych właściwości atmel toolchain (w tym także przypomnienie dla mnie) ;) w porównaniu do starego AVR GCC. Jest to porada z cyklu jak płynnie i bezboleśnie przejść ze starego toolchaina WinAVR na najnowszy Atmel toolchain. Jak wiecie jeśli w starym GCC chcieliśmy zdefiniować w pamięci Flash jakieś napisy (stringi, cokolwiek także stałe liczbowe) np na potrzeby MENU, czy też jakieś fonty np dla wyświetlaczy różnej maści ;), a następnie utworzyć tablicę wskaźników do tychże napisów/fontów w pamięci Flash, to wystarczyło zrobić to tak (podam przykład w oparciu o stringi ok?) ...


.


czyli krótko mówiąc, najpierw musieliśmy zdefiniować każdy napis jako tablicę w pamięci programu, a następnie można było utworzyć tablicę wskaźników tych napisów w pamięci programu. Niestety :( .... atmel toolchain grozi nam swoim paluchem że tak nie można. 



Wiecie już, że przede wszystkim należy wprowadzić podstawową zmianę opisaną przeze mnie w tym artykule:



czyli bezwzględnie należy zaopatrzyć każdą definicję zmiennej w pamięci flash w specyfikator const. Więc próbujemy to zrobić o tak:



O! już jest lepiej ;) ale cóż to - eclipse krzyczy nam że jest kolejny błąd:


co to oznacza ? .... ano to, że kompilator wciska nam, że sama zmienna tablicowa MenuItemPointers[] również powinna być typu const. Hmmm na pierwszy rzut oka patrzymy na to, patrzymy no i kombinujemy ale jak to - przecież mamy const ... więc ile tego ma być i jak? Czy to jakiś psikus ? ..... Nie nie nie - to nie psikus - trzeba po prostu uspokoić kompilator podając mu środki uspokajające w postaci kolejnego specyfikatora const tuż przed nazwą tej zmiennej ;) o tak:



uuuuuf ;) i dopiero teraz jak widzicie kompilator daje nam spokój, zgadza się z nami - nie płacze ;) wszystko jest OK a my się cieszymy, że poznaliśmy coś nowego, oraz że dalej możemy się spokojnie zająć programowaniem pomimo paru nieprzespanych nocy czy też wyrwanej nie jednej garści włosów z głowy - że nie chciało nam to działać.

Może jeszcze na koniec przykład jak dobierać się do takich stringów, np wyświetlać na LCD itp:

const char MenuItem1[] PROGMEM = "Mirek";
const char MenuItem2[] PROGMEM = "Tomek";
const char MenuItem3[] PROGMEM = "Atnel";
 
const char * const MenuItemPointers[] PROGMEM = {
  MenuItem1,
  MenuItem2,
  MenuItem3
};
 
int main( void ) {
 
 lcd_init();
 uint8_t i;
 
 while(1) {
  for(i=0; i<3; i++) {
   lcd_locate(0,0);
   lcd_str_P( (char*)pgm_read_word( &MenuItemPointers[i] ) );
   _delay_ms(1000);
   }
 }
 
}

Możemy też zdefiniować sobie makro:

#define PGM_GETSTR( str, idx ) (char*)pgm_read_word( &str[ idx ] )

i użyć go tak:

#define PGM_GETSTR( str, idx ) (char*)pgm_read_word( &str[ idx ] )

const char MenuItem1[] PROGMEM = "Mirek";
const char MenuItem2[] PROGMEM = "Tomek";
const char MenuItem3[] PROGMEM = "Atnel";

const char * const MenuItemPointers[] PROGMEM = {
  MenuItem1,
  MenuItem2,
  MenuItem3
};

int main( void ) {

 lcd_init();
 uint8_t i;

 while(1) {
  for(i=0; i<3; i++) {
   lcd_locate(0,0);
   lcd_str_P( PGM_GETSTR( MenuItemPointers, i ) );
   _delay_ms(1000);
   }
 }

}

Powodzenia ;)

24 komentarze:

  1. Jakie zalety ma ATMEL toolchain nad GCC ? Bo jak narazie pracuję na GCC i w książce oraz poradnikach dla eclipse przestrzegał Pan przez toolchainem Atmela. Że to dla bardziej wtajemniczonych, że to dla zaawansowanych itd. A ja (tak przy okazji tego poradnika) chciałbym się dowiedzieć w czym jest lepszy ? Oprócz tego że jest on specjalistycznym dodatkiem dla AVR'ów. Proszę nie odebrać mnie źle, proszę tylko Pana o wyjaśnienie mi różnicy po między tymi dwoma toolchainami. :)

    OdpowiedzUsuń
    Odpowiedzi
    1. Kolega chyba coś MEGA ŹLE zrozumiał z moich wypowiedzi. NIGDY i NIGDZIE nie przestrzegałem nikogo przed toolchainem atmela. To już niestety wytwór wyobraźni kolegi ;)

      Ja jedynie mówiłem, że początkującemu, który zaczyna z typowymi prockami AVR jak najbardziej wystarczy stary pakiet WinAVR i wszystko będzie działać w najlepszym porządku. I jeśli się nie ma doświadczenia to warto nawet jeszcze na razie od tego zacząć bo będzie mniej kłopotów.

      Ale jeśli ktoś chce od razu przejść na Atmel toolchain to proszę bardzo są nawet poradniki na moim blogu jak tego dokonać a także na co zwrócić uwagę gdy się przechodzi bo zmienia się co najmniej kilka zasad (wymogów) kompilatora języka C

      Tymczasem tak myślę, że kolega czytając sobie zbyt pobieżnie moje wypowiedzi w różnych miejscach - skojarzył atmel toolachain z kocim ATMEL STUDIO (które rzeczywiście z pełną świadomością odradzam początkującym swoich sił w języku C dla AVR) .... ale to są panie kochany DWIE CAŁKIEM RÓŻNE RZECZY ... a ty je ładnie pomieszałeś ;)

      Usuń
    2. No dobrze, trochę pomieszałem. Ale wziąłem się za ponowną lekturę i teraz to widzę tak...
      ATMEL STUDIO = ble ble fuj. Nie dotykać, lepiej eclipse.
      ATMEL TOOLCHAIN = patch do at studio i eclipse wspierający uC firmy ATMEL
      AVR GCC = patch do eclipse i at studio wspierający uC firmy ATMEL

      Zgadza się ?
      Wolę tu się tego dowiedzieć ( i przy okazji narazić się na hańbę :P ) niż krzyczeć na lewo i prawo co to tam kiedyś usłyszałem. Mam nadzieję że się rozumiemy :)

      Usuń
  2. Zapewne również chodzi o to, że GCC ostatnie z 2010r. nie obsługuje nowszych procków Atmela. Toolchain oficjalny wspiera wszystkie procesory tego producenta.

    OdpowiedzUsuń
  3. Witam, u mnie nie działą poniższy kod,
    const char MenuItem1[] PROGMEM = "Mirek";
    const char MenuItem2[] PROGMEM = "Tomek";
    const char MenuItem3[] PROGMEM = "Atnel";

    wszystko jest ok dopiero po gdy jest bez PROGMEM w definicji

    const char MenuItem1[] = "Mirek";
    const char MenuItem2[] = "Tomek";
    const char MenuItem3[] = "Atnel";

    const char * const MenuItemPointers[] PROGMEM = {
    MenuItem1,
    MenuItem2,
    MenuItem3
    };

    OdpowiedzUsuń
    Odpowiedzi
    1. Sorki ale to nie za bardzo wiesz co robisz, bez PROGMEM tworzysz zmienne w RAM a nie we Flash to po pierwsze, a z takim opisem problemu

      "u mnie nie działa"

      sorki ale pomóc ci może tylko jasnowidz :(

      zamiast pisać że coś ci nie działa to opisz jakie masz błędy, warningi itp - to dopiero wtedy będzie można coś pomóc

      Usuń
    2. A to ja miałem jeszcze inaczej
      miałem wszystko źle gdy miałem:

      unsigned char tab0[3] PROGMEM =
      {
      0x00, 0x01, 0x02
      };

      unsigned char tab1[3] PROGMEM =
      {
      0x02, 0x01, 0x00
      };

      unsigned char* tab[2] PROGMEM =
      {
      tab0, tab1
      };

      i to nie chodzi o ten const, jak to ten atmel toolchain wymaga, ale chodziło o to, że jak odczytywałem bajty z flasha za pomocą pgm_read_byte to podawałem adres tab[0] czyli tak w zasadzie tab0
      ale problem był taki, że jak miałem przy tej tablicy tab słowo PROGMEM to mi odczytywał w ogóle jakieś bajty z palca wyssane. Podejrzewam, że ta tablice tab wrzucił do flasha, a nie miał jej wrzucać i odczytywał bajty z tej tablicy i może jakbym dwa razy użył pgm_read_byte to wtedy by to działało. A tak to wywaliłem z tablicy tab PROGMEM i wszystko śmiga. Ta druga tablica tab ma przechowywać adresy innych tablic w pamięci FLASH i ona ma żyć w programie a nie we flashu. atrybut PROGMEM ma iść tylko do tych tablic tab0 i tab1

      Usuń
    3. Bez urazy, aleś kolego teraz "namiąchał" tym opisem ! ;) masakra .... no masakra ....

      Zrób albo działaj zgodnie z tym co opisałem i będzie w porządku. A CAŁKIEM innym zagadnieniem jest jak dobierać się do danych .... o czym akurat w tym poradniku nie pisałem ;) a to też trzeba dobrze robić i to tyle.

      Usuń
    4. Ja tam Panu Mirkowi w programowaniu nie dorastam do pięt... ale na tyle co już wiem. Skoro błędy zaczynają ci się sypać przy PROGMEM to czy zainkludowałeś bibliotekę ""?

      Usuń
  4. No dobrze, a jak mamy w taki sam sposób zdefiniowane ciągi liczb zamiast znaków?Chodzi o kilka tablic z liczbami, do których pointery też są w tablicy. Jak to potem przeczytać, adresując tablicę, oraz czytać liczbę po liczbie? Właśnie nigdzie nie mogę tego znaleźć a wszędzie są przykłady dla stringów.

    OdpowiedzUsuń
  5. z tego co widzę , napisy są we flashu ale tablica wskaźników do nich też jest we flashu, trzeba dwa razy użyć "pgm_read_word" (tutaj chyba drugie użycie ukryte w "lcd_str_P "?)
    lepiej jest trzymać wskaźniki w RAM odczyt flasha jest dużo dłuższy niż ram (jedna lub dwie instrukcje)

    OdpowiedzUsuń
    Odpowiedzi
    1. Kolega widać nie za bardzo zrozumiał o co chodzi w tym artykule ... a chodzi własnie o to aby pokazać jak trzymać wskaźniki we Flash - nie ważne czy szybciej z RAM czy z FLASH - czasem bywa że i tak potrzebne są we FLASH a warto wiedzieć JAK to zrobić - a nie co będzie szybsze. Bo chyba sprawą dość oczywistą jest że z pamięci RAM będzie szybciej ;)

      Usuń
  6. Witam!
    Generalnie to C (c++) jest jezykiem do maszyn von neumana a większosć mikrokontroleów to maszyny o architekturze Harvard. I ta druga zakłada oddzielne przestrzenie na program i dane. Tak że żeby to pogodzić to trzeba cos kompilatorowi powiedziec. I tyle. Taka szybla konwersja z Harvard na Von Neuman albo vice versa

    OdpowiedzUsuń
  7. "- trzeba po prostu uspokoić kompilator podając mu środki uspokajające w postaci kolejnego specyfikatora const tuż przed nazwą tej zmiennej ;) o tak:"

    :D GCC zjedz snickersa tj. consta bo zaczynasz gwiazdorzyć

    OdpowiedzUsuń
  8. Ten komentarz został usunięty przez autora.

    OdpowiedzUsuń
    Odpowiedzi
    1. Powycinało mi treść stringów! Oczywiście w zmiennych gpx_N są jakieś teksty.

      Usuń
    2. Powycinało ci mówisz .... - bo takie pytania to się panie kochany zadaje na forum a nie na blogu - tu nie ma prawidłowego formatowania kodu w odpowiedziach.

      Poza tym wyżej (pod koniec artykułu pokazuję jak sobie z tym radzić a ty robisz odwrotnie i to kompletnie inaczej i dziwisz się, że ci nie dział :( ... Ja się nie dziwię, że ci nie działa ............ cóż mogę ci poradzić ??? hmmm przeczytaj proszę ten artykuł pod którym zrobiłeś swój wpis ... znajdziesz odpowiedź na swój problem

      Usuń
    3. Panie Mirku, próbowałem każdej możliwości z tej strony. Tej na końcu strony również (jak wyświetlić na ekranie LCD), wszystko mam dokładnie tak samo. Zawsze dostaje krzaki (tylko inne, przy każdej możliwości). Mam wersje Eclipse z pierwszego wydania bluebooka, Atmega32, programuje AVRUSBmkII. Założę odpowiedni post na forum, żeby składnia była ok.

      Usuń
    4. No dobrze ale powiedz mi co ja mam tobie powiedzieć ? No sorki jasnowidzem nie jestem i nie widzę ani twojego kodu ani błędów jakie masz. Zastanów się proszę - jak można tak zadawać pytania w ogóle ? ... tracimy czas - nie sądzisz ?

      Co za problem - jeszcze raz powtarzam opisać wszystko dokładnie ale na forum, pokazać co robisz i jakie masz błędy ... wtedy można coś pomóc - a ty chcesz żeby zgadywać ?

      Usuń
  9. Panie Mirku, przecież napisałem, że stworzę post na forum i tak też uczyniłem. Po co ta złośliwa tyrada? Ma mi być teraz głupio, że zadałem pytanie? Litości...

    OdpowiedzUsuń
    Odpowiedzi
    1. Nie zauważyłem ? .... tylko kolega ma prawo się pomylić ?

      Usuń
  10. W nowszym gcc można to uprościć:

    __flash const char * __flash const MenuItemPointers[] = {
    (__flash const char[]){"Mirek"},
    (__flash const char[]){"Tomek"},
    (__flash const char[]){"Atnel"}
    };

    Przy okazji pozbywamy się makr pgm_read_word

    OdpowiedzUsuń