AVR GCC mnozenie wielkich liczb

Oct 06, 2006 14 Replies

Hej mam taki problem z kompilatorem AVR GCC - na moj gust zglupial lub pewnei robie cos zle. jest takie powiedzenie, jesli twierdzisz ze kompilator jest glupi - idz spac. ale nie w tym rzecz. mam wynik pomiaru w postaci liczby 32 bit (wynik z ukladu TCD pomiaru czasu z rozdzielczoscia 7ps(sic!)) i potrzebuje go przemnozyc przez stala 7.629453 nie chce uzywac arytmetyki zmiennoprzecinkowej, zreszta z jakis powodow ona tez nie dziala.


wersja zmiennoprzecinkowa float calc_temp; uint32_t calc_temp1; calc_temp1 = TDC_Read(0); if (calc_temp1 != 0xFFFFFFFF) //overrange detection { calc_temp = (calc_temp1*7.629453); tlcd_write_U32((U32)calc_temp); tlcd_write_string("ps"); } else tlcd_write_string(" OVR ");



no i wynik nie dosc ze jest bez sensu to zawija sie gdzies przy 16 bitach, ale to musze dokaldneij sprawdzic


wersja integer:


uint64_t calc_temp; U32 calc_temp1; calc_temp1 = TDC_Read(0); if (calc_temp1 != 0xFFFFFFFF) //overrange detection { calc_temp = (calc_temp1*7629453UL)/1000000UL; //result in ps tlcd_write_U32((U32)calc_temp); tlcd_write_string("ps"); } else tlcd_write_string(" OVR ");



Wynik wydaje zawijac sie gdzies na 16 bitach lub wczesniej. nie pomaga zadeklarowanie liczb jako 1000000ULL, jedyny efekt uboczny to taki ze objetosc kodu zwieksza sie o 4KB - widac kompilator wlacza biblioteki 64bit?? o robie zle? pewnie znow sie okaze ze gdzies do zmiennej powinienem byl dodac jakas literke...



z innej beczki: czy spotkaliscie sie z bibliotekami w C konwerujacymi np U32 na BCD? napisalem sobie wlasne,ale na pewno nie sa optymalne jesli chodzi o kod, a pewnie istnieja gdzies eleganckie napisane w ASM...


Witam.

U¿ytkownik "Greg(G.Kasprowicz)" snipped-for-privacy@gmail.com napisa³ w wiadomo¶ci news:eg6er3$dk$ snipped-for-privacy@news.onet.pl...

A jeste¶ pewien, ¿e TDC daje Ci to czego oczekujesz ? Na którym TDC pracujesz ? Acam ? MSC ? jeszcze inne ? Ja czasami dostaje dziwne wyniki z TDC GP1

Pozdrawiam

Jarek Grolik

tak, TDC GP1 - ogldam w hexie i jest idealnie. Obok porownuje ze zgrubnym odczytem z oscyloskopu

ten sam problem mam z odczytem czasomierza zaimplementowanego w FPGA - chce przemnozyc liczbe 32bit przez 6.66666ns, i identyczny problem..

BTW: do czego uzywasz GP1?

- ja do pomiaru szeroksoci impulsow z akceleratora oraz dlugosci impulsow laserowych w dalmierzach.

Oj zdarza siê, ¿e kompilator robi psikusy. Kto mia³ do czynienia z MSVC6 ten wie :-)

W ostatniej linii prowadzisz obliczenia na liczbach 32 bitowych. Dopiero przy przypisaniu nastêpuje konwersja na liczbê 64 bitow±. To za pó¼no.

pzdr mk

Greg(G.Kasprowicz) napisał(a):

[...]

Musisz pamiętać, że sposób obliczania jest niejako narzucony przez pierwszą operację. Czyli w powyższym mnożeniu liczby 32-bitowej calc_temp1 przez 32-bitową stałą 7629453UL już masz problem - będzie przepełnienie i obcięcie. Potem dzielisz to przez kolejną 32-bitową stałą 1000000UL i nic sensownego z tego działania nie pozostanie.

Powinno zadziałać takie rozwiązanie (sprawdź): uint64_t a, b, c; a = TDC_Read(0); b = (a * 7629453ULL) / 1000000ULL;

Jeżeli a jest 32-bitowe to przed mnożeniem zrzutuj je na 64 bity: b = (uint64_t) a * 76294... I jak poszło? Pamiętaj, że stałe 64-bitowe w AVR wymagają dopisku LL / ULL.

tak tez podejrzewalem. dlatego probowalem z tym ULL, tyle ze troche widac za pozno.

tylko teraz, dlaczego dlugosc kodu wzrasta o te 4kB? az tyle zajmuja procedury mnozenia 64 bit? w asemblerze 8051 mnozylem nawet liczby 96bit i zajmowalo kilkaset Bajtow. Czy da sie jakos ograniczyc ilosc tego marnowanego kodu?

Greg(G.Kasprowicz) napisał(a):

Obejrzyj w mapie linkera (i ew. listingu po deasemblacji) jakie funkcje zajęły najwięcej. Mnożenie 64-bitowe bez problemu napiszesz na kolanie i zajmie mniej niż kilkaset bajtów. Albo ściągnij gotowe z Sieci.

No i jak chcesz przemnozyc przez 7,629453 to moze szybciej bedzie przemnozyc przez 32768251121 a potem "podzielic" przez 2^32

J.

Hi hi - a duzo masz czasu ?

Bo jesli na jedno wejscie sumatora podasz liczbe, a na drugie jego wyjscie podzielone przez 4, to po chwili wyjscie narosnie do we*1.33333(3)

przemnozmy to przez 2, otrzymamy 2.666666(6), pozostaje dodac 4*we.

Glowy nie dam czy wynik bedzie zawsze stabilny, no i przydaloby sie dla wiekszej dokladnosci najpierw wejscie "przemnozyc" np *8, a na koniec podzielic/4

Swoja droga byc moze to ma calkiem ladna postac funkcji zminimalizowanej .. tylko czym ja wygenerowac :-)

J.

U¿ytkownik "Greg(G.Kasprowicz)" snipped-for-privacy@gmail.com napisa³ w wiadomo¶ci news:eg6gsm$5t4$ snipped-for-privacy@news.onet.pl...

Ja staram siê zrobiæ na nim precyzyjn± i szybk± hercmiarkê, ale niestety nie uda³o mi siê z nim dogadaæ zbyt dok³adnie poprzez LPT i teraz bêdê go wpierw podpina³ pod uC i dopiero do kompa. U¿ywam trybu MB2 i dostaje stabiln± czê¶æ ca³kowit± , natomiast FineCount gania w te i we w te. Uprzedzê , ¿e czêstotliwo¶æ odniesienia to wzorzec rubidowy :-) Robiê to do pomiarów QCM.

Pozdrawiam

Jarek Grolik

PS: Jak odczytujesz z niego kolejne wyniki z rejestrów 16 bitowych , to CS jest ca³y czas aktywny i tylko podajesz kolejne stroby READ , czy te¿ CS równie¿ podnosisz ? Móg³by¶ podrzuciæ kawa³ek kodu odpowiedzialnego za inicjalizacjê i odczyt ?

PS2 : Jest te¿ ciekawy TDC innej firmy. TDC502 nawet chyba lepiej dopracowany

hi hi - jeszce jedno podobne: - nie dodawac, tylko odejmowac polowe. Wynik sie ustali na 0.666666(6) .. czyli najpierw przemnozyc przez 10 [jeden sumator].

Hm, z sumowaniem chyba bedzie, ten z odemowaniem to nie dam glowy :-)

J.

tez o tym pomyslalem, bedzie chyba szybciej,skoro i tak juz mnoze, to odpadnie dzielenie, bo potem tylko wezme starsza czesc

#include "TDC.h"

U32 TDC_RES_0; U32 TDC_RES_1; U32 TDC_RES_2; U32 TDC_RES_3; U32 TDC_STAT; U32 TDC_REG_1;

void TDC_setup(void) {

TDC_Write(addr_TDC_REG_0,TDC_REG0_default); TDC_Write(addr_TDC_REG_1,TDC_REG1_default); TDC_Write(addr_TDC_REG_2,TDC_REG2_default); //TDC_Write(addr_TDC_REG_3,TDC_REG3_default); //TDC_Write(addr_TDC_REG_4,TDC_REG4_default); //TDC_Write(addr_TDC_REG_5,TDC_REG5_default);

}

void TDC_command(U8 command) { SPCR |= (1<<CPHA); //set SPI in falling edge mode PORT_TDC |= (1<< SPI_src); // SPI input = TDC PORT_TDC &= ~(1<<TDC_CSn); SPDR = command; /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} PORT_TDC |= (1<<TDC_CSn); SPCR &= ~(1<<CPHA); //set SPI in rising edge mode }

void TDC_Write(U8 reg,U32 val) { SPCR |= (1<<CPHA); //set SPI in falling edge mode PORT_TDC |= (1<< SPI_src); // SPI input = TDC PORT_TDC &= ~(1<<TDC_CSn); SPDR = (reg&0x07)|Write_into_address_ADR; /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} SPDR = ((val>>16)&0xff); /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} SPDR = ((val>>8)&0xff); /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} SPDR = (val&0xff); /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} PORT_TDC |= (1<<TDC_CSn); SPCR &= ~(1<<CPHA); //set SPI in rising edge mode }

U32 TDC_Read(U8 reg) { SPCR |= (1<<CPHA); //set SPI in falling edge mode U32 temp,temp1; PORT_TDC |= (1<< SPI_src); // SPI input = TDC PORT_TDC &= ~(1<<TDC_CSn); SPDR = (reg&0x07)|Read_from_address_ADR; /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} SPDR = (0x00); /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} temp1 = SPDR; temp = temp1<<24; SPDR = (0x00); /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} temp1 = SPDR; temp|= temp1<<16; SPDR = (0x00); /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} temp1 = SPDR; temp|= temp1<<8;

SPDR = (0x00); /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} temp|= SPDR; PORT_TDC |= (1<<TDC_CSn); SPCR &= ~(1<<CPHA); //set SPI in rising edge mode return temp; }

U16 TDC_STAT_Read(void) {

SPCR |= (1<<CPHA); //set SPI in falling edge mode U16 temp; PORT_TDC |= (1<< SPI_src); // SPI input = TDC PORT_TDC &= ~(1<<TDC_CSn); SPDR = (addr_TDC_STAT)|Read_from_address_ADR; /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;}

SPDR = (0x00); /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} temp= SPDR<<8;

SPDR = (0x00); /* Wait for transmission complete */ while(!(SPSR & (1<<SPIF))) {;} temp|= SPDR; PORT_TDC |= (1<<TDC_CSn); SPCR &= ~(1<<CPHA); //set SPI in rising edge mode return temp; }

TDC.h

//TDC register 0 default content #define NEG_START 0 #define NEG_STOP1 0 #define NEG_STOP2 0 #define MRange2 1 #define DisAutoCal 0 #define Calibrate 1 #define SelClkT 1 #define No_FAKE 0 #define TCycle 0 #define PORT 1 // 2 bit #define START_ClkHS 1 #define ClkHSDiv 0 #define CALRES 0 //4 bit #define DIV_FIRE 0 #define FIRE 0

//TDC register 0 position

#define pos_NEG_START 0 #define pos_NEG_STOP1 1 #define pos_NEG_STOP2 2 #define pos_MRange2 3 #define pos_DisAutoCal 4 #define pos_Calibrate 5 #define pos_SelClkT 6 #define pos_No_FAKE 7 #define pos_TCycle 8 #define pos_PORT 9 // 2 bit #define pos_START_ClkHS 10 #define pos_ClkHSDiv 12 #define pos_CALRES 14 //4 bit #define pos_DIV_FIRE 16 #define pos_FIRE 20

//TDC register definition //OPCODES #define Write_into_address_ADR 0b10000000 #define Read_from_address_ADR 0b10110000 #define Init 0b01110000 #define Power_On_Reset 0b01010000 #define Start_Cycle 0b00000001 #define Start_Temp 0b00000010 #define Start_Cal_Resonator 0b00000011 #define Start_Cal_TDC 0b00000100 // 20 16 12 8 4 0 // | | | | | | #define TDC_REG0_default 0b000000010011011001100010 #define TDC_REG1_default 0b000000010100000100000000 // 20 16 12 8 4 0 // | | | | | | #define TDC_REG2_default 0b010000000000000000000000 #define TDC_REG3_default 0b001000000000000000000000 // 20 16 12 8 4 0 // | | | | | | #define TDC_REG4_default 0b001100000000000000000000 #define TDC_REG5_default 0b000000000000000000000000

#define addr_TDC_RES_0 0 #define addr_TDC_RES_1 1 #define addr_TDC_RES_2 2 #define addr_TDC_RES_3 3 #define addr_TDC_STAT 4

#define addr_TDC_REG_0 0 #define addr_TDC_REG_1 1 #define addr_TDC_REG_2 2 #define addr_TDC_REG_3 3 #define addr_TDC_REG_4 4 #define addr_TDC_REG_5 5

void TDC_setup(void); void TDC_command(U8 command); void TDC_Write(U8 reg,U32 val); U32 TDC_Read(U8 reg); U32 TDC_Read(U8 reg); U16 TDC_STAT_Read(void);

ja dla odroznienia uzywam trybu 1-szego interesuje mnei zakres 2ns...1us kody podalem w innym watku

dzieki, zadaialalo jednakze ostatecznie podzielilem wynik pomiaru na 3 zakresy co 1024 :) i od razu mi wyswietla w ns, us, ms co jest bardziej intuicyjne niz rzad 9 cyfr, z czego i tak 2 ostatnie mrugaj na najwyzszym zakresie (1s range/6ns bin)

Join the Discussion

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

Didn't find your answer?

Ask the community — no account required