Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

Klasy, C++ a AVR

owca2002 04 Dec 2007 15:10 6090 41
  • #1
    owca2002
    Level 11  
    Witam,

    Od jakiegoś czasu bawię się AVRami, ale zawsze pisałem wszystko w C. Teraz niestety potrzebuję użyć klas. I moje pytanie czy da się to zrobić i czy działa to w pełni poprawnie. Prosta klasa jakoś się kompiluje i działa, jednak bardziej skomplikowane rzeczy niestety już nie. Mimo, że kompilują się bez problemów, to program zachowuje się zupełnie nielogicznie. Biorę pod uwagę, że to może być mój błąd (chociaż już wielokrotnie to sprawdzałem), ale wolę zapytać, bo może okazać się, że tego w ogóle nie da się zrobić :/

    Używam WinAVR, a dawniej CodeVision.
  • #2
    GreG$
    Level 13  
    owca2002 wrote:
    ...nie da się zrobić :/

    Używam WinAVR, a dawniej CodeVision.


    Da się zrobić :). Pisałem trochę softu obiektowo w gcc (czyli to co masz pod WinAvr - choć tego środowiska nie używam) nawet na tn2313.
    Faktycznie są problemy ale wynikają często z błędów programistycznych, które czasami uchodzą w C ;). Ponadto faktem jest, że czasami musiałem wyłączyć optymalizację całkiem - 0, i zwykle problemy kończyły się.
    Próbuj zmienić opcje optymalizacji.
    Polecam drogę właśnie w tym kierunku tzn. C++.
  • #3
    owca2002
    Level 11  
    A czy da się wykorzystać w pełni możliwości C++? Chodzi mi np. o szablony?

    A z optymalizacją spróbuję.
  • #4
    Klima
    Level 30  
    Może problemy wynikały ze zbyt małego obszaru pamięci przeznaczonego na stos?
  • #5
    owca2002
    Level 11  
    Klima wrote:
    Może problemy wynikały ze zbyt małego obszaru pamięci przeznaczonego na stos?


    A można to jakoś ręcznie regulować, bo nigdy się chyba tym nie bawiłem?
  • #6
    szelus
    Level 34  
    To zależy, co masz na myśli mówiąc regulować. Standardowo cała dostępna pamięć jest przeznaczona na stos, zmienne statyczne i zmienne dynamiczne. Tzn. na początku pamięci jest obszar zmiennych statycznych, potem ewentualnie obszar zmiennych dynamicznych, który rośnie "w górę". Na końcu jest stos, który rośnie od końca pamięci "w dół".
    Jeżeli pamieci jest mało, to stos i zmienne się szybko spotkają i efekty będą nieciekawe :wink:.
    Na AVR-ach jeszcze C++ nie próbowałem, ale C++ potrafi być pamięciożerne. A linker nie ostrzega, że na stos zostało np. 10 bajtów. :(
  • #7
    owca2002
    Level 11  
    Aha, to tyle to chyba wiedziałem. Myślałem, że da się to jakoś kontrolować ręcznie.

    W chwili obecnej nie wydaje mi się, żebym miał za mało pamięci, bo obecnie używam megi16, a program na prawdę jest ograniczony do minimum. Jedyne podejrzenie, to że źle alokuję pamięć, bo potrzebuję stringów, a inaczej to chyba się nie da.

    Code:
       char * tmp;
    
       if ( (tmp = (char*) malloc ( sizeof(char)*20 ) )== NULL)
       {
          Write_tekst ("error");
       }


    @GreG$ optymalizacja wyłączona, ale jak na razie to też nie pomogło. :/
  • #9
    fantom
    Level 31  
    szelus wrote:
    To akurat wygląda OK, ale osobiście nigdy z malloc() nie korzystałem.
    To oczywiście znasz ? http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_cplusplus


    Jak na C++ to zdecydowanie nie wyglada OK.

    Kolego owca2002: zdecyduj sie czy korzystasz z C czy z C++.Od zarzadzania pamiecia w C++ sa operatory new i delete a nie malloc ktora jest standardowa funkcja z bliblioteki C.Niestety wiekszosc ludzi kompletnie myli te dwa jednak znacznie rozniace sie jezyki. Musisz rowiez miec na uwadze ze zarzadzanie pamiecia w przypadku mikrokontrolerów jest duzo bardziej ubogie niz w PC-tach.Nie mozna sobie pozwolic na marnotrawstwo pamieci na struktury danych do zarzadzania sterta bo nie starczy miejsca na sama sterte.Dlatego w wiekszosci przypadkow zarzadzanie pamiecia w portach mikrokontrolerowych jest oparte na jednorazowym przydziale pamieci (tzn obszar raz przydzielony nie moze byc zwolniony).Wlasnie stad moga wynikac rozne problemy.
  • #10
    owca2002
    Level 11  
    fantom wrote:
    szelus wrote:
    To akurat wygląda OK, ale osobiście nigdy z malloc() nie korzystałem.
    To oczywiście znasz ? http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_cplusplus


    Jak na C++ to zdecydowanie nie wyglada OK.

    Kolego owca2002: zdecyduj sie czy korzystasz z C czy z C++.Od zarzadzania pamiecia w C++ sa operatory new i delete a nie malloc ktora jest standardowa funkcja z bliblioteki C.Niestety wiekszosc ludzi kompletnie myli te dwa jednak znacznie rozniace sie jezyki. Musisz rowiez miec na uwadze ze zarzadzanie pamiecia w przypadku mikrokontrolerów jest duzo bardziej ubogie niz w PC-tach.Nie mozna sobie pozwolic na marnotrawstwo pamieci na struktury danych do zarzadzania sterta bo nie starczy miejsca na sama sterte.Dlatego w wiekszosci przypadkow zarzadzanie pamiecia w portach mikrokontrolerowych jest oparte na jednorazowym przydziale pamieci (tzn obszar raz przydzielony nie moze byc zwolniony).Wlasnie stad moga wynikac rozne problemy.


    Zdaję sobie sprawę z tego, że to jest C, ale z tego co wyczytałem, to operatory new i delete po prostu tutaj nie działają (zresztą jak wiele innych standardowych funkcji C++). Dlatego też używam malloc.
    Jeśli masz gdzieś pod ręką jakiś tutorial na temat pisania w C++ pod AVR to chętnie poczytam, albo chociaż jakieś przykłady, tylko właśnie na mikrokontrolery, bo normalnie z pisaniem w C++ to problemów raczej nie miałem.
  • #11
    fantom
    Level 31  
    Nie mam nic takiego bo akurat w AVR-ach uwazam korzystanie z C++ jako przerost formy nad trescia.Niestety ten brak pewnych kluczowych operacji moze wiazac sie z ograniczona architektura tego procka.
  • #12
    upanie
    Level 22  
    Quote:
    Nie mam nic takiego bo akurat w AVR-ach uwazam korzystanie z C++ jako przerost formy nad trescia.

    Absolutnie się zgadzam.
    Program, którego kod wynikowy, może wynosić kilka kilobajtów nie może efektywnie korzystać z dobrodziejstwa C++. Ja już nawet nie wspominam o RAMie. Wspominałeś o wykorzystaniu wzorców. Otóż wzorce są cholernie pamięciożerne i generalnie C++ jest bardzo rozbudowanym językiem, który potrzebuje dużych zasobów zarówno pamięciowych jak i mocy obliczeniowej.
    Sam fakt korzystania z klas do grupowania danych i funkcji nie oznacza, że korzystamy z dobrodziejstw C++. Wystarczy używać struktur. Jeżeli nie wystarcza Ci samo C to znaczy, że również nie wystarcza Ci ten mikrokontroler.
  • #13
    owca2002
    Level 11  
    Sprawa jest o tyle skomplikowana, że raczej C++ będzie potrzebny, ponieważ staram się zaimplementować sieć probabilistyczną na uC. A cały projekt ma właśnie udowodnić, że nawet na czymś takim małym da się to zrobić. :)

    Tylko w chwili obecnej to podstawowe rzeczy nie działają tak jak powinny i cały czas staram się znaleźć błędy, bo wiem, że każdy je popełnia. :D
  • #14
    fantom
    Level 31  
    Jestem pewien ze da sie to zrobic spokojnie z uzyciem C.
  • #15
    szelus
    Level 34  
    Cóż, poczułem sie wywołany do tablicy, zwłaszcza przez kolegę fantom, ktory zasugerował, że nie rozróżniam C i C++, a anwet nie przeczytał linka, który wstawiłem. Cóż, tak krawiec kraje, jak mu materii staje...

    upanie wrote:
    Quote:
    Nie mam nic takiego bo akurat w AVR-ach uwazam korzystanie z C++ jako przerost formy nad trescia.

    Absolutnie się zgadzam.
    Program, którego kod wynikowy, może wynosić kilka kilobajtów nie może efektywnie korzystać z dobrodziejstwa C++. Ja już nawet nie wspominam o RAMie.

    A ja się absolutnie nie zgadzam. C++ ma pare zalet, które można wykorzystać, nawet w przypadku mikrokontrolerów. Wprawdzie do tej pory pisałem to w C, ale trochę cierpialem z powodu jego ograniczeń. Jeżeli programista rozumie, co robi, to program może być całkiem efektywny, vide przykład dalej.

    Quote:

    Wspominałeś o wykorzystaniu wzorców. Otóż wzorce są cholernie pamięciożerne i generalnie C++ jest bardzo rozbudowanym językiem, który potrzebuje dużych zasobów zarówno pamięciowych jak i mocy obliczeniowej.

    Kolega pisał o szablonach. Szablony i wzorce to dwie różne rzeczy, jakby kolega nie wiedział. Akurat rzeczywiście z wzorcami to mogła by być przesada. Ale szablony to bardzo silne narzędzie.

    Proszę, o to przykład. Postanowiłem sprawdzić, co da się zrobić w C++, bo podobny kod w C z użyciem długich makr zrobił sie trochę mało czytelny, a głównie ciężko poprawialny.

    Code:

    #include <inttypes.h>
    #include <avr/io.h>


    // definicja LED-a
    template <uint8_t portAddr, uint8_t bit, bool isNegativeLogic>
    struct LedDef {
        static void On()    //zapal
            {
                if (isNegativeLogic)
                {
                    _SFR_IO8(portAddr) &= ~_BV(bit);
                }
                else
                {
                    _SFR_IO8(portAddr) |= _BV(bit);
                }
            }

        static void Off()   //zgas
            {
                if (isNegativeLogic)
                {
                    _SFR_IO8(portAddr) |= _BV(bit);
                }
                else
                {
                    _SFR_IO8(portAddr) &= ~_BV(bit);
                }
            }

    };


    typedef LedDef <0x18, 2, false> LedGreen;
    typedef LedDef <0x1B, 7, true>  LedRed;


    enum LedMode {
        OFF = 0,
        ON,
        BLINK
    };

    // LED Processor ;-)
    template <class Led, typename CounterType, CounterType period, CounterType timeOff>
     class Blinker : public Led {

      private:
        CounterType counter;
        LedMode mode;

      public:
         void Process()  //do wolania w przerwaniu zegarowym
            {
                ++counter;
                if (counter >= period)
                {
                    counter = 0;
                }

                if (mode == ON)
                {
                    Led::On();
                }
                else if (mode == OFF)
                {
                    Led::Off();
                }
                else if (counter > timeOff)
                {
                    Led::On();
                }
                else
                {
                    Led::Off();
                }
            }

        void SetMode(LedMode m)
            {
                mode = m;
            }


    };


    static const uint8_t LED_PERIOD = 50; //przyklad


    Blinker <LedGreen, uint8_t, 50, 25>     greenBlinker;
    Blinker <LedRed, uint8_t, 100, 80>      redBlinker;


    void timerIrqProc()
    {
            greenBlinker.Process();
            redBlinker.Process();
    }



    int main()
    {

        greenBlinker.SetMode(ON);
        redBlinker.SetMode(BLINK);

        while(1)
        {
            timerIrqProc();
        }
    }


    I oto co wygenerował kompilator (z opcją -fshort-enums). Zgodnie z oczekiwaniami, kod tak efektywny, jak to możliwe:
    Code:

    0000005a <_Z12timerIrqProcv>:

      public:
         void Process()  //do wolania w przerwaniu zegarowym
            {
                ++counter;
      5a:   80 91 62 00    lds   r24, 0x0062
      5e:   8f 5f          subi   r24, 0xFF   ; 255
      60:   80 93 62 00    sts   0x0062, r24
                if (counter >= period)
      64:   82 33          cpi   r24, 0x32   ; 50
      66:   10 f0          brcs   .+4         ; 0x6c
                {
                    counter = 0;
      68:   10 92 62 00    sts   0x0062, r1
                }

                if (mode == ON)
      6c:   80 91 63 00    lds   r24, 0x0063
      70:   81 30          cpi   r24, 0x01   ; 1
      72:   31 f0          breq   .+12        ; 0x80
                {
                    Led::On();
                }
                else if (mode == OFF)
      74:   88 23          and   r24, r24
      76:   31 f0          breq   .+12        ; 0x84
                {
                    Led::Off();
                }
                else if (counter > timeOff)
      78:   80 91 62 00    lds   r24, 0x0062
      7c:   8a 31          cpi   r24, 0x1A   ; 26
      7e:   10 f0          brcs   .+4         ; 0x84
      80:   c2 9a          sbi   0x18, 2   ; 24
      82:   01 c0          rjmp   .+2         ; 0x86
      84:   c2 98          cbi   0x18, 2   ; 24
      86:   80 91 60 00    lds   r24, 0x0060
      8a:   8f 5f          subi   r24, 0xFF   ; 255
      8c:   80 93 60 00    sts   0x0060, r24
      90:   84 36          cpi   r24, 0x64   ; 100
      92:   10 f0          brcs   .+4         ; 0x98
      94:   10 92 60 00    sts   0x0060, r1
      98:   80 91 61 00    lds   r24, 0x0061
      9c:   81 30          cpi   r24, 0x01   ; 1
      9e:   31 f0          breq   .+12        ; 0xac
      a0:   88 23          and   r24, r24
      a2:   31 f0          breq   .+12        ; 0xb0
      a4:   80 91 60 00    lds   r24, 0x0060
      a8:   81 35          cpi   r24, 0x51   ; 81
      aa:   10 f0          brcs   .+4         ; 0xb0
      ac:   df 98          cbi   0x1b, 7   ; 27
      ae:   08 95          ret
      b0:   df 9a          sbi   0x1b, 7   ; 27
      b2:   08 95          ret

    000000b4 <main>:
                {
                    Led::On();
                }
                else
                {
                    Led::Off();
                }
            }

        void SetMode(LedMode m)
            {
                mode = m;
            }


    };


    static const uint8_t LED_PERIOD = 50; //przyklad


    Blinker <LedGreen, uint8_t, 50, 25>     greenBlinker;
    Blinker <LedRed, uint8_t, 100, 80>      redBlinker;

    void timerIrqProc()
    {
            greenBlinker.Process();
            redBlinker.Process();
    }

    int main()
    {
      b4:   cf e5          ldi   r28, 0x5F   ; 95
      b6:   d2 e0          ldi   r29, 0x02   ; 2
      b8:   de bf          out   0x3e, r29   ; 62
      ba:   cd bf          out   0x3d, r28   ; 61
      bc:   81 e0          ldi   r24, 0x01   ; 1
      be:   80 93 63 00    sts   0x0063, r24
      c2:   82 e0          ldi   r24, 0x02   ; 2
      c4:   80 93 61 00    sts   0x0061, r24

        greenBlinker.SetMode(ON);
        redBlinker.SetMode(BLINK);

        while(1)
        {
            timerIrqProc();
      c8:   c8 df          rcall   .-112       ; 0x5a
      ca:   fe cf          rjmp   .-4         ; 0xc8


    Jak widac są zalety:
    1. Precyzyjna przestrzeń nazw.
    2. Unikanie powielania kodu (źrodłowego)
    3. Scisłe sprawdzanie typów
    4. Bardziej precyzyjne komunikaty o błędach składniowych - znalezienie błędu w długich makrach "zastępujących" szablony nie jest czasem proste.

    Są też, jak widać, wady. Głównie ograniczone możliwości odwołania do symbolicznych nazw rejestrów i/o. Trzeba by stworzyć odpowiednie pliki nagłówkowe w C++. Np. powyżej musiałem przekazać adres rejestru szesnastkowo, bo odpowiedniej stałej nie ma w plikach <avr/io.h>. Makro _SFR_IO_ADDR nie działa, bo w C jest ten adres generowany na etapie optymalizacji, a nie kompilacji.

    upanie wrote:

    Sam fakt korzystania z klas do grupowania danych i funkcji nie oznacza, że korzystamy z dobrodziejstw C++. Wystarczy używać struktur. Jeżeli nie wystarcza Ci samo C to znaczy, że również nie wystarcza Ci ten mikrokontroler.

    fantom wrote:

    Jestem pewien ze da sie to zrobic spokojnie z uzyciem C.


    Oczywiście, że sie da. Wszystko się da, tylko po co sie męczyć, jeśli można prościej. Wszystko zależy od konkretnej sytuacji.


    :arrow:owca2002
    Może jakiś kod przykładowy, który nie działa? Jak widać, "u mnie działa". :wink:

    P.S. Używałem avr-gcc 3.4.3, ale nie sądzę, aby to miało istotny wpływ.
  • #16
    fantom
    Level 31  
    Troche sam sie wywolales do tablicy (zeby sie popisac ?) a przeciez nie o to chodzilo. Nikt nie powiedzial ze sie nie da tylko ze na tak skromna architekture jest to bez sensu. Przy czyms tak prostym kompilator nie ma problemu ale sprobuj pokorzystac troche z operatorow zarzadzania pamiecia i juz nie bedzie tak kolorowo. Podales listing kodu a ile wynosi w takim przypadku zajetosc RAM-u ? Wszystko musi miec rece i nogi. Armata tez mozesz zabic muche ale po co skoro mozna to zrobic packa na muchy.
  • #17
    szelus
    Level 34  
    fantom wrote:
    Troche sam sie wywolales do tablicy (zeby sie popisac ?) a przeciez nie o to chodzilo. Nikt nie powiedzial ze sie nie da tylko ze na tak skromna architekture jest to bez sensu.

    Raczej po to, aby oprzeć dyskusję na czymś konkretnym, a nie na ogólnym twierdzeniu "nie ma sensu".
    I wydaje mi się, że pokazałem sens. Może kolega zaproponuje, jak to zrobić równie elegancko w C. Wiem, że ogólnie się da - ten kod powstał na podstawie tego, co miałem w C z makrami, ale o wadach i zaletach pisałem.
    Quote:

    Przy czyms tak prostym kompilator nie ma problemu ale sprobuj pokorzystac troche z operatorow zarzadzania pamiecia i juz nie bedzie tak kolorowo. Podales listing kodu a ile wynosi w takim przypadku zajetosc RAM-u ?

    Przecież pisałem, że programista musi rozumieć, co robi. Korzystanie z wszystkich możliwości C++, w tym dynamicznej alokacji obiektów, oczywiście nie ma sensu.
    Ale w tym wypadku zajetość RAM jest dokładnie taka, jak w C:
    Code:

      0 .text         000000cc  00000000  00000000  00000094  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
      1 .data         00000000  00800060  000000cc  00000160  2**0
                      CONTENTS, ALLOC, LOAD, DATA
      2 .bss          00000004  00800060  00800060  00000160  2**0
                      ALLOC


    Quote:

    Wszystko musi miec rece i nogi. Armata tez mozesz zabic muche

    Z tym widzialbym pewien problem :wink: Przynajmniej z trafieniem w konkretną muchę :wink:
    Quote:

    ale po co skoro mozna to zrobic packa na muchy.

    Z drugiej strony, Jeżeli mamy przyzwoity środek owadobójczy, to po co latać z packą? :wink:
  • #18
    fantom
    Level 31  
    Na 94 bajty kodu masz 160 bajtow danych po to zeby mrugac dwiema diodami - to jest malo ?? No comments.
  • #19
    szelus
    Level 34  
    Ups.. Wyciąłem jedną linijkę za dużo i kolega źle zinterpretował. 4 (słownie: cztery) bajty.

    Code:

    test.elf:     file format elf32-avr

    Sections:
    Idx Name          Size      VMA       LMA       File off  Algn
      0 .text         000000cc  00000000  00000000  00000094  2**0
                      CONTENTS, ALLOC, LOAD, READONLY, CODE
      1 .data         00000000  00800060  000000cc  00000160  2**0
                      CONTENTS, ALLOC, LOAD, DATA
      2 .bss          00000004  00800060  00800060  00000160  2**0
                      ALLOC

  • #20
    fantom
    Level 31  
    No to juz brzmi rozsadniej bo az sie przestraszylem (w koncu to tylko dwa obiekty ktore maja pod dwa proste pola). Z drugiej strony pisac cos w C++ i nie miec mozliwosci skorzystania ze wszystkich jego dobrodziejstw jest sztuką dla sztuki.
  • #21
    owca2002
    Level 11  
    Bardzo mnie cieszy, że temat się tak rozwinął i są chętni do pomocy. Jak tylko będę miał chwilę, to wrzucę jakiś fragment kodu, żeby przekonać się czy to jest mój błąd, bo samemu często trudno jest wykryć własne błędy.
  • #22
    owca2002
    Level 11  
    Wrzuciłem na serwer minimalną wersję programu, która już mi nie działa http://7ds.pl/~owca/engine.zip

    Jeśli ktoś mógłby sprawdzić czy to jakiś mój błąd składniowy czy może coś innego.
    Część kodu jest usunięta, bo była to obsługa wyświetlacza.
  • #23
    UDMA
    Level 16  
    Moje rady na szybko:
    1. Kod pisz, kompiluj i testuj na pececie z użyciem GCC dla pentium. Gdy wszystko ruszy to dopiero migracja na 8-bit.
    2. Nie używaj konstrukcji:
    Code:

    typedef char *string;

    bo może kolidować z std::string.
    3. Używaj GDB gdy program się sypie i nie wiadomo o co chodzi.
  • #24
    szelus
    Level 34  
    owca2002 wrote:
    Wrzuciłem na serwer minimalną wersję programu, która już mi nie działa http://7ds.pl/~owca/engine.zip


    Hmm...
    Czy mógłbyś napisać, co właściwie znaczy "nie działa"? I jakiego masz AVR-a?

    Z uwag ogólnych:

    1. To, że napisałem, że moim zdaniem często warto skorzystać z C++ nie oznacza, że można szastać zasobami. malloc() to raczej ostateczność. Nie wiem, co chcesz osiagnąć, ale alokowanie łańcuchów stałej długości nie ma sensu - znacznie efektywniej byłoby po prostu zadeklarować tablice w obiekcie.

    2. Wskazane jest (dla mikrokontrolerów) korzystanie z obiektów statycznych (w miare możliwości) - przeważnie daje bardziej efektywny kod, porównywałny z tym w C (mogą byc wyjątki, ale baardzo specyficzne).

    3. Czy przestrzeń stanów masz zamknietą czy otwartą? Jeżeli zamkniętą, to użycie napisów do definiowania stanu jest mocno nieefektywne. Pamięć na AVR-ze zaraz się skończy.

    4. Ponieważ ten program nic nie robi, to nie rozumiem, co znaczy, że nie działa. Testujesz to na symulatorze?
  • #25
    owca2002
    Level 11  
    szelus wrote:

    1. To, że napisałem, że moim zdaniem często warto skorzystać z C++ nie oznacza, że można szastać zasobami. malloc() to raczej ostateczność. Nie wiem, co chcesz osiagnąć, ale alokowanie łańcuchów stałej długości nie ma sensu - znacznie efektywniej byłoby po prostu zadeklarować tablice w obiekcie.

    2. Wskazane jest (dla mikrokontrolerów) korzystanie z obiektów statycznych (w miare możliwości) - przeważnie daje bardziej efektywny kod, porównywałny z tym w C (mogą byc wyjątki, ale baardzo specyficzne).


    Wiem, że program nie jest napisany efektywnie, jednak jest to spowodowane tym, że jest to bardzo okrojona wersja o wiele większej partii programu. Jednak nie chciałem wrzucać całego, bo wtedy na pewno nikomu nie chciałoby się tego sprawdzać.

    szelus wrote:

    3. Czy przestrzeń stanów masz zamknietą czy otwartą? Jeżeli zamkniętą, to użycie napisów do definiowania stanu jest mocno nieefektywne. Pamięć na AVR-ze zaraz się skończy.

    Jeśli chodzi Ci o to, czy w czasie działania programu będą dodawane kolejne stany (węzły), to w wersji pierwotnej tak miało być jednak najpierw chcę zrobić tak, żeby wszystkie dane miał już wprowadzone ręcznie w kodzie programu.

    szelus wrote:

    4. Ponieważ ten program nic nie robi, to nie rozumiem, co znaczy, że nie działa. Testujesz to na symulatorze?


    Po "wgraniu sieci" wyświetlam nazwy poszczególnych pól na wyświetlaczu i w tym momencie wyświetla już mi jakieś śmieci. Tak jakby miał jakieś problemy z pamięcią i czytał z innego miejsca niż powinien.

    Używam tylko atmegi16 i żadnej pamięci zewnętrznej, poza tym sprawdzałem już też inny uC i jest to samo, a więc to raczej wina kodu, a nie sprzętu.

    Jeszcze mam podejrzenie, że to może być przez wyświetlacz, którego używam, dlatego też jestem w trakcie uruchamiania RSa, żeby wykluczyć tę możliwość.

    Sądziłem po prostu, że jest tutaj jakiś podstawowy błąd w programowaniu, którego ja nie widzę.
  • #26
    szelus
    Level 34  
    owca2002 wrote:

    Po "wgraniu sieci" wyświetlam nazwy poszczególnych pól na wyświetlaczu i w tym momencie wyświetla już mi jakieś śmieci. Tak jakby miał jakieś problemy z pamięcią i czytał z innego miejsca niż powinien.


    Wyświetlasz w którym miejscu w programie? W main przed pętla while?
    Zwróć uwagę, że obiekt wez zadeklarowany w CProbabilisticNetwork::Load() jest lokalny! Po wyjściu z tej metody obiekt przestaje istnieć. Więc nie tworzysz żadnych węzłów. Chyba coś innego miałeś na myśli.

    Quote:

    Używam tylko atmegi16 i żadnej pamięci zewnętrznej, poza tym sprawdzałem już też inny uC i jest to samo, a więc to raczej wina kodu, a nie sprzętu.

    Nie podejrzewałem sprzetu. Chodziło mi w zasadzie o to, ile masz pamięci. W 16-stce masz tylko 1kB RAMu, choć ten programik powinien się bez problemu zmieścić. Ale przy dynamicznej alokacji pamieci, a zwłaszcza przy tworzeniu obiektow na stosie trudno mieć pewność. Nie dostaniesz (jawnie) żadnego błędu gdy stos wjedzie na stertę.
  • #27
    owca2002
    Level 11  
    Wyświetlałem zaraz po tym jak wywołałem funkcję Load na obiekcie.

    Czyli w pliku probabilisticnetwork.cpp

    po lini
    Code:
    wez.Load(0,"Visit_to_Asia?","NoVisit","Visit", 8, 0);


    więc tutaj chyba powinno działać?

    W chwili obecnej to już chyba niczego nie jestem pewien jeśli chodzi o ten program :/
  • #28
    szelus
    Level 34  
    W zasadzie w tym miejscu powinno być ok, ale z RAMem możesz być na styk.
    Zobacz, obiekt typu network zajmuje (nie chciało mi sie liczyc dokładnie) ponad 256 bajtow! Stałe napisowe użyte w ten sposób też zużywaja RAM. Dodatkowo używasz pewnie ponad 70 bajtów pamięci dynamicznej na nazwę/stany na węzeł. Docelowo, na 15 węzłów, pamięci masz tak 2-3 razy za mało.

    Mógłbyś nie używać malloc-a i na stałe umieścić tablice w węzłach, ale wtedy obiekt typu network nie zmieści się w pamięci.

    Spróbuj przynajmniej zadeklarować obiekt network jako statyczny.
  • #29
    owca2002
    Level 11  
    Wychodzi na to, że całe to "dziwne" zachowanie uC jest przez małą ilość pamięci. Przez kilka godzin bawiłem się kodem i po prostu w pewnym momencie, dodanie kolejnego elementu powoduje wysypanie się programu.

    Także trzeba dokupić trochę RAMu i zobaczyć co wtedy. No i spróbować trochę zoptymalizować program, bo do tej pory raczej nie musiałem zwracać na to uwagi :|

    Dzięki wszystkim za dotychczasową pomoc i jakby ktoś miał jeszcze jakieś sugestie jak to dobrze zoptymalizować to byłbym bardzo wdzięczny.
  • #30
    szelus
    Level 34  
    owca2002 wrote:

    Także trzeba dokupić trochę RAMu i zobaczyć co wtedy. No i spróbować trochę zoptymalizować program, bo do tej pory raczej nie musiałem zwracać na to uwagi :|


    Pisanie programów na mikrokontrolery, to trochę jak kiedyś na Atarynki czy Spektrusie :) - zacząć trzeba od zaplanowania w jaki sposób najlepiej wykorzystać dostępne zasoby.

    Z konkretów - unikaj (najlepiej wyeliminuj wogóle) dynamiczną alokację pamięci i obiekty lokalne (chyba, że b. małe). Jeżeli potrzebujesz dynamicznych obiektów to znacznie lepiej sprawdzi się tablica statyczna i własny alokator -> np. statyczna metoda Create() w klasie (nawet wykorzystująca prostą flagę wolny/zajęty). Przeciążanie operatora new nie jest wskazane, bo nie da się zwrócić błędu, a wyjątki nie są zaimplementowane. Napisy stałej (maksymalnej, ale raczej krótkiej :wink:) długości, itp. Wtedy z góry wiesz ile różnych obiektów Ci się zmieści w pamięci i program może być kompletnie deterministyczny.

    Powodzenia!