Problemy z C++ (GNU ARM)

May 15, 2008 26 Replies

Witam!



Mam mały problem... który wynika z mojej nieznajomości tego języka ;).. Otóż chciałbym w ARMach robić sobie takie proste opóźnienia w stylu: for (i=0;i<10000;i++); Tylko zamiast podawać wartość maksymalną wprost chciałbym używać stałych ms i us i definiować je w zależności od szybkości zegara ;)... No to robię tak: const int ms = 3025; (...) for (i=0;i<10*ms;i++);



No i działa :)... gorzej z mikrosekundami :/... Niestety, jak wpiszę tak: const float us = 3.025 (...) for (i=0;i<10*us;i++);



To wyskakują błędy: undefined reference to `__floatsisf' lpc-2378stk main.c line 61 undefined reference to `__ltsf2' lpc-2378stk main.c line 61 undefined reference to `__mulsf3' lpc-2378stk main.c line 61



No i domyślam się o co chodzi... nie mam bibliotek do mnożenia liczb zmiennoprzecinkowych itp... Tylko mi nie o to chodzi!! :)... ja bym chciał, zeby kompilator pomnożył mi 10*3.025, zaokrąglił i de facto wpisał i<30 i tyle... no ale niestey, stała jest w tym języku niezmienną zmienną i nic nie idzie zrobić :(... I stąd dwa pytania:



1) jaką dodać bibliotekę, żeby to ruszyło ;)...
2) jak to zrobić, żeby to działało tak, jak ja chcę (bo wiem, że mój zapis jest błędny, ale lepszego wymyślić nie umiem :()

Pozdrawiam Konoppo


Spróbuj może: const int us = 3.025 * 10; for (i=0;i<us;i++); Wtedy mnożenie wykonane typu float zostanie na etapie kompilacji. Sprawdzić trzeba. K.

John Smith pisze:

No tak, jakbym chciał zdefiniować jedną stałą, to bym nie kombinował z mnożeniem... ale chodzi o to, ze chciałem mieć możliwość wrzucania for'ów z róznymi opóźnieniami: for (i=0;i<1*us;i++); for (i=0;i<5*us;i++); for (i=0;i<100*us;i++);

Chyba nie ma sensu definiować stałych _1us, _5us, _100us i wszystkich innych możliwych ;)...

Pozdrawiam Konop

A tak:

const float us = 3025

for (i=0;i<(10*us)/1000;i++);

sorry ...

const int us = 3025

Zrobiłem tak:

#define ms 3750 #define us ms/1000

i chyba działa ;).. chyba, tzn. jak używam us i zmieniam wartość ms, to opóźnienie się zmienia ;)... niestety, nie mam porządnego częstościomierza, ten w multimetrze widze, że jest szajski... no ale tak czy siak:

-bibliotek do floata to nie wymaga ;)...

-nie zaokrągla us do 3 albo 4, bo zmiana ms z 3600 na 3750 powoduje zmianę czasu generowanego przez us ;)...

no ale dalej nie wiem, czy to obliczył kompilator, czy to ARM liczy w każdej pętli ;)... no ale de-asemblera chyba nie mam ;P...

Dzięki wielkie za sugestię, żeby użyć /

Pozdrawiam Konop

@arm-elf-objdump -h -S program.elf >program.lst

Się było kiedyś koderem na MC68000 :)

Konop pisze:

A nie, jednak coś zaokrągla ;(... i moją i Twoją metodą - zmiana ms z

3750 na 3740 powoduję zmianę częstotliwości z 2.044kHz na 1.6xxkHz... aż tak tragicznej rozdzielczości to ten miernik chyba nie ma ;)... mogę sprawdzić na oscyloskopie (analogowym) jakby co ;)... tę częstotliwość to generują 2 fory 250*us albo (250*ms)/1000

Konop przemówił ludzkim głosem:

Po pierwsze, nie używaj do opóźnień pętli w c bo w zależności gdzie to wstawisz to możesz otrzymywać różny kod w assemblerze (i różny czas wykonania). Lepiej zrobić funkcję inline ze wstawką asemblerową i jako parametr przekazywać liczbę obiegów pętli. Oczywiście i tak się wszystko będzie rozjeżdżało jeśli masz włączone przerwania, MAM, cache, albo kilka obszarów pamięci z różnymi wait-state'ami. Po drugie napisz sobie makro, które wyliczy ilość obiegów pętli. Poniższy kod nie jest dla ARMa, ale bez problemów powinieneś to sobie przerobić.

static __inline void Wait(unsigned long Delay){ unsigned long Result;

asm volatile ("L_%=:\n" "\tdec.l #1,%0 \n" "\tbne L_%= \n":"=r"(Result): "0"(Delay)); }

#define WaitMs(a) Wait( (unsigned long)((a) * 18000000.0 / F_CPU) ) #define WaitUs(a) Wait( (unsigned long)((a) * 18000.0 / F_CPU) )

Współczynniki musisz sobie wyliczyć, albo dobrać przy pomocy oscyloskopu. Wersja full-wypas może dodatkowo sprawdzać czy wartość parametru "a" da się wyliczyć na etapie kompilacji i w razie czego generować błąd ( poszukaj opisu funkcji __builtin_constant_p )

No niestety... na oscyloskopie czas dla 3740 ma 2 działki, a dla 3750 -

3 dzialki (tak w przybliżeniu ;P)... no nic, postaram się wrzucić to jednak do asemblera ;)... Ale dzięki, za uwagi :)..

Pozdrawiam Konop

Konop pisze:

Uważaj na poziom optymalizacji. Ja bym się nie zdziwił, gdy powyższa linia zostanie zastąpiona jedynie podstawieniem i=10000 (albo i tego brak gdy zmienna "i" nie jest potem używana).

Do robienia opóźnień polecam skorzystanie z dobrodziejstw peryferiów (zależy jakiego dokładnie masz ARMa), np. licznika RTT albo chociaż swobodnie tykającego timera (np. co 1 us).

Dzięki za uwagi!! Znalazłem w książce przykład delay'a napisany w asm, przerobiłem nieco (choć nie rozumiem tych wszytkich znaczków ;) ), wygląda tak i działa:

static __inline void delay (unsigned long d_time) { asm volatile ( "petla%=:" "subs %[d_time],%[d_time],#1\t\n" "bne petla%=\t\n" : :[d_time]"r"(d_time) ); }

No i do tego dwa makra:

#define Waitms(a) \

(__builtin_constant_p (a) ? (delay((unsigned long) ((a)*12733))) : delay (0xFFFFFFFF)) #define Waitus(a) \ (__builtin_constant_p (a) ? (delay((unsigned long) (((a)*12733)/1000))) : delay (0xFFFFFFFF))

I wszystko śmiga ;)... Tę wartość 12733 trzeba tylko uzależnić jakoś od kwarcu, no ale to nie problem :)... gorzej z możliwością generowania błędu przez kompilator w przypadku, gdy wartości parametru a nie da się określić w chwili kompilacji - tego, to już nie wiem jak zrobić :(... dlatego jest tam wpisany po prostu duży delay ;)... jak zamiast tego wpisałem #error "BLAD", to wyskoczyło:

'#' is not followed by a macro parameter lpc-2378stk main.c line 46

1210892429093 228

Pozdrawiam Konop

PS Raz jeszcze - dzieki :)

Adam Dybkowski pisze:

Fakt, o optymalizacji nie pomyslałem, ale już jest w ASM, volatile, to chyba mi tego nie ruszy ;)..

Co do opóźniej - masz rację, najlepszy jest timer, ale nie zawsze super-dokładność jest potrzebna, a ja jednocześnie uczę się C, C++ i ARMów, dlatego chciałem zacząć od czegoś prostego, do timerów jeszcze nie doszedłem ;)... Z drugiej strony - chciałem, żeby z tej "nauki" powstało coś przydatnego - stąd pomysł stworzenia uniwersalnej funkcji opóźniającej ;)....

Pozdrawiam Konop

Adam Dybkowski pisze:

A może wiesz, skąd się wzięło takie coś?

Mam wait'a zdefiniowanego tak, jak pisałem wcześniej:

I potem w main mam coś takiego:

// endless loop to toggle the red LED P0.0 while (1) { IOSET0 = 1; Waitms (500); IOCLR0 = 1; Waitms (500); }

Bo zrobieniu pliku main.lst z pliku main.out uzyskuje:

// endless loop to toggle the red LED P0.0 while (1) { IOSET0 = 1; 998: e3a0324e mov r3, #-536870908 ; 0xe0000004 99c: e283390a add r3, r3, #163840 ; 0x28000 9a0: e3a02001 mov r2, #1 ; 0x1 9a4: e5832000 str r2, [r3] Waitms (500); 9a8: e3a00861 mov r0, #6356992 ; 0x610000 9ac: e2800c25 add r0, r0, #9472 ; 0x2500 9b0: e2800024 add r0, r0, #36 ; 0x24 9b4: eb000008 bl 9dc <delay>

IOCLR0 = 1; 9b8: e3a032ce mov r3, #-536870900 ; 0xe000000c 9bc: e283390a add r3, r3, #163840 ; 0x28000 9c0: e3a02001 mov r2, #1 ; 0x1 9c4: e5832000 str r2, [r3] Waitms (500); 9c8: e3a00861 mov r0, #6356992 ; 0x610000 9cc: e2800c25 add r0, r0, #9472 ; 0x2500 9d0: e2800024 add r0, r0, #36 ; 0x24 9d4: eb000000 bl 9dc <delay>

} 9d8: eaffffee b 998 <main+0xa8>

000009dc <delay>: 9dc: e1a0c00d mov ip, sp 9e0: e92dd800 stmdb sp!, {fp, ip, lr, pc} 9e4: e24cb004 sub fp, ip, #4 ; 0x4 9e8: e24dd004 sub sp, sp, #4 ; 0x4 9ec: e50b0010 str r0, [fp, #-16] 9f0: e51b3010 ldr r3, [fp, #-16]

000009f4 <petla100>: 9f4: e2533001 subs r3, r3, #1 ; 0x1 9f8: 1afffffd bne 9f4 <petla100>

9fc: e89da808 ldmia sp, {r3, fp, sp, pc}

O co chodzi? Po pierwsze, instrukcja IOSET składa się z przypisania do r3 wartości E0000004h i potem dodaniu 28000h, jakby od razu nie mógł przypisać E0028004h :/:/:/... Po drugie - delay nie jest wykonywany jako __inline, tylko normalna funkcja.. nie wspominajac, ze nie mam pojęcia co on tam oblicza na początku tego delaya :(...

Może ktoś coś poradzić?? Bo już nie mogę... to jest dla mnie za duży szok... pierwszy raz stykam się z ARMami, z narzędziami tego typu (pliki makefile i inne cuda, to nie to samo, co przycisk COMPILE czy BULID w AVT Studio itp ;P), praktycznie pierwsze zetkniecie z C i C++... za duzo tego ;P...

Pozdrawiam Konop

zastanawia³e¶ siê jak mozna wykonaæ pêtlê 30,25 razy ?

zmiennna decydujaca o ilo¶ci cykli w instrukcji for musi byæ ca³kowita

pzdr AK

Jesli chodzi o mov'a to gdzies widzialem w jakims FAQ'u, podejrzewam ze chodzi o to by wartosc ladowana mov'em byla 8-bitowa, wg

formatting link
0x4E jest 8-bitowe i rotowane w prawo, potem dodawana jest druga czesc.

Jesli chodzi o inline to:

- gcc ignoruje zupelnie inline gdy nie ma wlaczonych optymalizacji (stwierdzone empirycznie - sam sie zdziwilem). Gdy ma wlaczone optymalizacje najczesciej uwzglednia, ale tez czesc normalnych funkcji sam sobie rozwija w miejsce wywolania

- inline jest tylko SUGESTIA dla kompilatora - ma pelne prawo ja zignorowac jesli uzna za stosowne z jakiegos powodu

U¿ytkownik "Konop" snipped-for-privacy@gazeta.pl napisa³ w wiadomo¶ci news:g0iflf$hlj$ snipped-for-privacy@inews.gazeta.pl...

ton±cy delaya siê chwyta

;)

e.

To jest akurat C i instrukcja jest jak najbardziej poprawna

- warunkek kontynuacji petli dobry jak kazdy inny i petla wykona sie

31 razy :-)

Niestety od dobrej woli kompilatora zalezy jak to skompiluje .. ale jesli i jest volatile, to nie ma wiekszego wyboru jak zamienic na float/double i porownac do .. 30.25 ?

Bo na oko to wywoluje konwersje a nie mnozenie.

J.

Uzyj #define cos w rodzaju #define us(t) ((3025*t)/100)

i miej nadzieje ze kompilator to dobrze obliczy.

bezpieczniej by chyba bylo dalej skrocic i wpisac ((605*t)/25) czy ((121*t)/5)

J.

jasne - dokona zaokraglenia - ale powinien krzyczeæ ze to float

AK

Join the Discussion

Have something to add? Share your thoughts — no account required.

Didn't find your answer?

Ask the community — no account required