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

[AT90s2313][C++] Problem z pgm_read_byte()

pawel.l89 10 Wrz 2009 22:46 2760 13
  • #1 7004328
    pawel.l89
    Poziom 12  
    Witam!

    Zacząłem przerabiać kurs programowania procesorów AVR w języku C. Jednak wolałem pisać te programy w C++ i wszystko na początku było ok.

    Problem pojawia się wtedy gdy chcę odczytać dane z pamięci programu za pomocą pgm_read_byte().
    Otóż w pamięci programu w tablicy mam dane potrzebne do obsługi czterocyfrowego wyświetlacza LED. Kiedy skompiluje program w avr-gcc z rozszerzeniem .c to wszystko działa jak powinno. Jednak kiedy skompiluje z rozszerzeniem .cpp to na wyświetlaczu pojawiają się przypadkowe znaki, tak jakby by błędnie działała funkcja odczytująca dane z pamięci programu za pomocą pgm_read_byte().

    Czy wie ktoś może dlaczego tak się dzieję?Co jest przyczyną?Jak zrobić by bylo dobrze?
  • #3 7005126
    tmf
    VIP Zasłużony dla elektroda
    pgrm_read_byte dziala prawidlowo zarowno w c, jaki i w c++, tak wiec wina lezy po stronie twojego kodu. Pokaz przyklad to cos sie wymysli.
  • #4 7007757
    pawel.l89
    Poziom 12  
    No dobra więc zapisuję tablicę ze "wzorami" cyfr do pamięci programu i deklaruję globalną tablicę przechowującą dane wyświetlacza.
    Funkcja Disp() odpowiedzialna jest za wpisywanie do tablicy g_LED[] liczb odpowiadających danym cyfrom. POwiedzmy, że liczbą wyświetlaną jest zmienna counter którą zwiększam po naciśnięciu przycisku lub co sekundę - nieważne. Wyświetlacz obsługuję podczas przerwania od timer0 co ok 4ms

    
    #define F_CPU 4000000
    
    #include <avr/io.h>
    #include <inttypes.h>
    #include <avr/interrupt.h>
    #include <avr/pgmspace.h
    
    
    prog_uint8_t g_Cyfry[] ={192,249,164,176,153,146,130,248,128,144};
    uint8_t g_LED[4]={0};
    
    void Disp(uint16_t n)
    {
      uint8_t i;
      for(i=0;i<4;i++)
        {
          g_LED[i]=pgm_read_byte(&g_Cyfry[n%10]);
          n/=10;
        }
    }
    
    int main(void)
    {
      /////////////////////////////////////////////////////////////////////
      //inicjacja
      DDRD=0x7c;  
      PORTD=0x07;
      DDRB=0xff;
      PORTB=0xff;
      SREG|=1<<7; //globalne zezwolenie przerwań
      TCCR0|=(1<<CS01 | 1<<CS00);  //timer0 jako timer prescale=64
      TCCR1B|=(1<<CS11 | 1<<CS10);//timer1 jako timer prescale = 64
      TIMSK|=(1<<TOIE0 | 1<<TOIE1); //zezwolenie przerwan od timer0 i timer1
     //koniec inicjacji
      ///////////////////////////////////////////////////////////////////// 
      
      while(1)
        {
        Disp(1234); //Powiedzmy ze tylko cos takiego ma wyswietlac
        }
    
      return 0;
    }
    
    
    SIGNAL(SIG_OVERFLOW0) //przerwanie od timer0 co ok 4ms
    {
      static Which=0;
      PORTD|=(1<<6 | 1<<5 | 1<<4 | 1<<3);
      PORTB=g_LED[Which];
      PORTD&=~(1<<(3+Which));   // +3 bo wyswietlacze są od PORTD.3 do PORTD.6
      if(++Which>3) Which=0;
    }
    
    
    


    Nie jest to pełny program bo nie chcę tutaj wypisywać wszystkich moich przemyśleń..
    Problem polage na tym, że działa TYLKO w C a jak kompiluje w C++ to nie działa i pojawiają się jakieś przypadkowe "śmieci" na wyświetlaczach LED.
  • Pomocny post
    #5 7007842
    BoskiDialer
    Poziom 34  
    Kompilowałem podany kod u siebie, przy kompilacji jako c++ kompilator rzucał się do linii "static Which=0;", ale po drobnych poprawkach ruszył. W kodzie asemblera nie widzę znaczących różnic, pod symulatorem wszystko działa poprawnie.
    1/ Sprawdź, czy zastąpienie "pgm_read_byte(&g_Cyfry[n%10])" przez stałą (np 164) coś zmieni.
    2/ Sprawdź, czy przeniesienie g_Cyfry do ram oraz odczyt bez pgm_read_byte coś zmieni.
    3/ Sprawdź, czy po zmianie typu Which z domyślnego (int) na uint8_t coś zmieni.
    4/ Napisz jakąś prostą funkcję wyświetlającą, która nie korzysta z pgm oraz wyświetl adres tablicy g_Cyfry w obu wersjach (c oraz c++). Sprawdź w hexedytorze, czy w plikach binarnych pod podanymi adresami rzeczywiście znajduje się podana tablica.
    -- edit
    5/ Sprawdź, czy dodanie volatile przy g_LED coś zmieni.
  • #6 7007872
    pawel.l89
    Poziom 12  
    no tak powinno być:

    
    static uint8_t Which=0;
    


    Dodano po 4 [minuty]:

    ale posprawdzam to co napisałeś jutro bo teraz już nie mam sił, dzięki za pomoc jutro napiszę co sie działo
  • #7 7008649
    pawel.l89
    Poziom 12  
    AD1) Zastąpienie stałą 164 powoduje ze wyświetlane są same dwójki czyli tak jak powinno to wyglądać.
    AD2) Z ramu również odczytuje poprawnie i działa jak należy.
    AD3) Co do tego to ja miałem w programie deklaracje taką:
    static uint8_t Which=0;

    Ale przy przepisywaniu mi to umknęło. Jeżeli zmienię na:
    static Which=0;

    to kompilator wywala błąd:
    ISO C++ forbids declaration of ‘g_Which’ with no type

    AD4) Nigdy wczesniej tego nie robilem ale mysle ze dam rade. Napisałem program ktory robi konwersje za pomocą reinterpret_cast<> ze wskaźnika do uint8_t na int i przerobiłem ta liczbe na system hexadecymalny i wyswietlilem na wyswiewtlaczu. Mam dwie tablice o nazwie g_Cyfry jedna w pamieci programu a jedna w RAM'ie . Adres otrzymany na wyswietlaczu to 0x60.
    Nie wiem natomiast jak szukać tego adresu w pliku .hex dlatego skopiuję go tutaj:
    
    :100000000AC023C022C021C020C09CC0C6C01DC0E1
    :100010001CC01BC01AC011241FBECFEDCDBF10E005
    :10002000A0E6B0E0E2E5F2E003C0C89531960D929B
    :10003000A037B107D1F710E0A0E7B0E001C01D92F2
    :10004000A737B107E1F74AD002C1DACFDF93CF93E8
    :1000500000D0CDB7DEB79A838983ECE4F0E08981E4
    :100060008083EDE4F0E089819A81892F992780834C
    :100070000F900F90CF91DF910895DF93CF9300D031
    :1000800000D0CDB7DEB79C838B838B819C818A8324
    :100090001982198219C08981282F30E08A81882F1E
    :1000A00090E08F709070E82FF92FE05AFF4F808119
    :1000B000E22FF32FE059FF4F80838A8182958F7062
    :1000C0008A8389818F5F89838981843020F30F90AF
    :1000D0000F900F900F90CF91DF910895DF93CF9302
    :1000E000CDB7DEB7E1E3F0E088E78083E2E3F0E05C
    :1000F00083E08083E7E3F0E08FEF8083E8E3F0E0E4
    :100100008FEF8083AFE5B0E0EFE5F0E080818068BD
    :100110008C93A3E5B0E0E3E5F0E0808183608C930D
    :10012000AEE4B0E0EEE4F0E0808182608C93A9E57B
    :10013000B0E0E9E5F0E0808182688C9380E690E0B1
    :100140009CDFFCCF1F920F920FB60F9211242F93BA
    :100150003F934F935F936F937F938F939F93AF934F
    :10016000BF93EF93FF93DF93CF93CDB7DEB78FEAC3
    :100170009CE36CDFCF91DF91FF91EF91BF91AF9145
    :100180009F918F917F916F915F914F913F912F91AF
    :100190000F900FBE0F901F9018951F920F920FB6E1
    :1001A0000F9211242F933F934F935F938F939F93BD
    :1001B000AF93BF93EF93FF93DF93CF93CDB7DEB7AA
    :1001C000A2E3B0E0E2E3F0E0808188678C93A8E3EB
    :1001D000B0E080917600882F90E0E82FF92FE05969
    :1001E000FF4F80818C93A2E3B0E0E2E3F0E08081F6
    :1001F000482F50917600852F90E0282F392F2D5FC2
    :100200003F4F81E090E0022E02C0880F991F0A94B0
    :10021000E2F7809584238C93852F8F5F80937600FF
    :1002200080917600843010F010927600CF91DF91AB
    :10023000FF91EF91BF91AF919F918F915F914F91FE
    :100240003F912F910F900FBE0F901F901895F8942B
    :02025000FFCFDE
    :10025200C0F9A4B0999282F880908883A7A1868E73
    :00000001FF
    


    A oto część programu odpowiedzialna za wyswietlenie adresu na wysiwetlaczu:

    
    prog_uint8_t g_Cyfry[16]={192,249,164,176,153,146,130,248,128,144,136,131,167,161,134,142};
    uint8_t g_Cyfry1[16]={192,249,164,176,153,146,130,248,128,144,136,131,167,161,134,142};
    
    ....
    
    void DispHex(uint8_t * ptr)
    {
      uint8_t n;
      n=reinterpret_cast<int>(ptr);
      uint8_t i=0;
      for(i=0;i<=3;i++)
        {
          g_LED[i]=g_Cyfry1[n%16];
          n/=16;
        }
    }
    
    
    int main(void)
    {
      /////////////////////////////////////////////////////////////////////
      //inicjacja
     ..........
      while(1)
        {
          DispHex(g_Cyfry);
        }
    
      return 0;
    }
    
    



    AD5) Nie nic nie zmienia.
  • #8 7011276
    tmf
    VIP Zasłużony dla elektroda
    Zobacz wygenerowany plik .lss - tam bedziesz mial linie kodu c++ i to jak zostaly przetlumaczone na assembler. Znajdziesz tam takze adresy. Nie podoba mi sie tez zapis uint8_t g_LED[4]={0}. IMHO to deklaruje 4-elementowa tablice, gdzie prawidlowe indeksy to 0-3, i inicjalizuje element o indeksie 0 na 0. Czyli zupelnie nie to co chcesz osiagnac.
    Natomiast taki zapis - uint8_t g_LED[5]; g_LED[4]=0; bedzie ok. Mozesz tez g_LED zadeklarowac jako static, co zagwarantuje wyzerowanie wszystkich elementow.
  • #9 7011717
    pawel.l89
    Poziom 12  
    Nie rozumiem zbytnio o co Ci chodzi..Tablica g_LED[] jest przeznaczona do przechowywania aktualnych kodów liczbowych odpowiadających odpowiednim cyfrom. Wyświetlaczy mam 4 wiec stad cztero-elementowa tablica. Instrukcja g_LED[4]={0}; wyzeruje wszystkie elementy tablicy, natomiast wyzerowanie to chyba nawet nie jest potrzebne bo zmienne globalne są inicjalizowane zerami z tego co wiem, ale dla pewności wyzerowałem je sobie.

    A jak mam wygenerować ten plik .lss? Ja pisze w edytorze tekstowym pod linuxem i używam kompilatora avr-gcc i programator avrdude. Nie znam jednak na tyle tych dwóch narzędzi, może ktoś pomóc?
  • #10 7013176
    BoskiDialer
    Poziom 34  
    Wyniki do 1,2,3 i 5 dało się przewidzieć, ale test 4 dużo rozjaśnił: w hex'ie widać, że tablica znajduje się pod adresem 0x252 (ciąg "C0F9A4...") a kompilator wstawia adres 0x60 co może wskazywać, że tablica pomimo zastosowania prog_uint8_t ląduje w pamięci ram (inicjalizowana z pamięci flash)
    6/ Spróbuj dodatkowo dodać atrybut PROGMEM
    7/ Sprawdź, czy objęcie #include <avr/pgmspace.h> oraz deklaracji tablicy przez extern "C" coś zmieni.
    8/ Sprawdź, czy masz najnowszą wersję kompilatora etc.

    -- edit
    Teraz zauważyłem, że funkcja wyświetlająca adres nie jest poprawna - adres rzutujesz na uint8_t (typ zmiennej n) a więc tracisz górny bajt
  • #11 7013272
    pawel.l89
    Poziom 12  
    No tak rzeczywiśie to rzutowanie byłe złe ale po poprawce i tak adres jest równy 0x60.

    Gdy dodam atrybut PROGMEM to kompilator wyrzuca ostrzeżenie:
    warning: only initialized variables can be placed into program memory area


    ...ale działa. Więc jeden problem rozwiązany a pojawił się następny?Jak się pozbyć tego ostrzeżenia? No i co robię źle w uzyskiwaniu adresu skoro teraz wyświetla mi się 0x16 natomiast tablica znajduję się w tym miejscu:
    
    ...
    :1000100024C023C022C0C0F9A4B0999282F8809075
    ...
    
  • #12 7013353
    BoskiDialer
    Poziom 34  
    Na tej jednej linijce widać, że tablica zaczyna się od adresu 0x16, więc nie ma błędu. Linijka zawiera bajty od adresu 0x10, ciąg "C0F9A4..." zaczyna się dopiero od szóstego bajtu. Na końcu zazwyczaj znajdują się dane przenoszone do ram, z początku są stałe tablice.

    Zaktualizuj kompilator i wtedy sprawdź, czy dalej wyrzuca ten warning.
  • #13 7013547
    pawel.l89
    Poziom 12  
    Acha no tak faktycznie, nie wiedziałem poprostu jak to się odczytuje..

    Wydaję mi się że mam najnowsze wersje zarówno avr-gcc jak i biblioteki avr-libc...Mam tak:


    gcc-avr 4.3.2-1
    avr-libc 1.6.2.cvs20080610-2
  • #14 7013861
    BoskiDialer
    Poziom 34  
    Nie wiem jak to sprawdzałem, że na symulatorze działało - a też nie powinno. Na googlach znalazłem, że jest to znany błąd, nie doczytałem się o poprawkach, więc trzeba z tym komunikatem jakoś żyć - chyba, że jest inne rozwiązanie.
REKLAMA