Hallo liebe Leute, ich hänge an einem Problem mit sehr großen Zahlen und weiß einfach nicht, wie ich am besten damit umgehen kann. Folgendes Problem: Ich will einem DDS Baustein eine 48bit (6 Byte) tiefe Zahl übergeben, die von einem Benutzer eingegeben werden soll. Der Benutzer soll aber nicht die Zahl direkt eingeben, sondern eine Frequenz. Den wert, den der DDS bekommt, muß erst berechnent werden. Und darin liegt mein Problem. Wie kann ich folgendes gut lösen ? Der Benutzer gibt eine Zahl mit 16 Stellen ein zB. 104.000 000 080 000 0 Mhz ein. Das ist soweit kein Problem. Ich habe für jeweils einen 3er Block (104)(000)... eine int Variable i,j,k,l,m,n deklariert und habe dann die Zahl in einer (long long) Variablen. tuning_word =((i*1000000ULL+j*1000ULL+k+l/1000.0+m/1000000.0+n/1000000000.0) * X) X ist constant und in meinem Fall zB. (2815315.315) Gibt man nun 1.000 000 000 000 0 ein, dann ist mein tuning_word = 2815315315000 Mein Problem ist jetzt, dass ich eigentlich nur ganzzahlige Zahlen eingebe (i,j,k...) und auch nur eine ganzzahlige Zahl am Ende brauche, um dort hin zu kommen aber floats habe. Kann man diese Rechnung nicht irgendwie aufteilen und wieder zusammenfügen, um so nicht mit floats zu rechnen. Das man nicht jede beliebige Frequenz einstellen kann, ist schon klar, da das tuning_word nur ganzzahlig sein kann. Es wird dann halt die nächst mögliche Freqeunz erzeugt. Es geht mir nur darum, die Rechnung anders durchzuführen. Danke schon im voraus. Mattias
Loes die Klammer auf und berechne die Werte fuer X/1000, X/1000000, X/1000000000 per Taschenrechner als ganze Zahlen und setzte sie ein. Ich wuerde im uebrigen ueberall mindestens einen Faktor 100 mit dazunehmen, sonst ergibt X/1000000000 naemlich 0 (bei deinem X) Also alles mal 100 oder 1000 (Ueberchlagsmaessig muesste sich das noch ausgehen. Du benutzt bis jetzt 6 Bytes, d.h du hast noch 2 Bytes Reserve in einem long long) =( (i * ( X * 1000000000ULL ) + j * ( X * 1000000ULL ) + k * ( X * 1000ULL ) + l * ( X / 1 ) + m * ( X / 1000 ) + n * ( X / 1000000 ) ) / 1000 Die vielen Multiplikationen bzw. Divisionen mit X sind kein Problem. Da alles konstant ist, kannst Du mal davon ausgehen, das das der Compiler zur Compilezeit erledigt.
Hallo Mattias, hallo Karl Heinz, Klammer auflösen ist klar, muß man machen. Zur Skalierung ('Ich wuerde im uebrigen ueberall mindestens einen Faktor 100 mit dazunehmen, sonst ergibt X/1000000000 naemlich 0'): der größte Wert ist i*1000000*X, was, wie Mattias richtig sagt, ja mindestens 48Bit (naja, close) entspricht. Bei ner 64 Bit Rechengenauigkeit haste dann noch ca. 16 Bit Spielraum, den Du zur Beibehaltung der Genauigkeit ausnutzen solltest, also maximaler Faktor könnte ca. 2^16 sein, ist im folgenden snipppet SSH genannt. Naja, Dynamik muß man nochmal genau ausprobieren, die 6 Additionen kommen noch dazu, macht auch nochmal 6 Bit, im Beispiel war i allerdings nicht 104 sondern 1, das spart auch 7 BIT pi mal Daumen. Ach ja, im code nix float, das sollte der Compiler alles vorher ausrechnen. Danke für die nette Knobelei. Cheers Detlef #define X (2815315.315) #define SSH 16 #define FIX (SINT64)((1<<SSH)*X*1e6) #define FJX (SINT64)((1<<SSH)*X*1e3) #define FKX (SINT64)((1<<SSH)*X*1e0) #define FLX (SINT64)((1<<SSH)*X*1e-3) #define FMX (SINT64)((1<<SSH)*X*1e-6) #define FNX (SINT64)((1<<SSH)*X*1e-9) tuning_word=(((i*FIX)>>SSH)+((j*FJX)>>SSH)+((k*FKX)>>SSH) +((l*FLX)>>SSH)+((m*FMX)>>SSH)+((n*FNX)>>SSH));
Ich würde die Rechnungen alle in Assembler machen. Anbei ein Beispielcode für 56 Bit. Der ist auch einfach auf beliebige Bitzahlen erweiterbar. Peter
Hallo Detlef, das mit der Klammerauflösung werd ich machen. Was ist denn bitte SINT64 ??? Das kennt mein Compiler nicht. Ich denke mal das ist (long long) ? Mattias
> Was ist denn bitte SINT64?
Ein Alias für den <stdint.h>-Namen int64_t?
Hallo, ja, 'long long' für den AVR. Dachte, das wäre selbsterklärend (wie 'Bratkartofffeln', da ist der Name ja auch Programm): SINT64 = signed integer, 64 Bit lang. 'unsigned' wäre besser gewesen, aber Hauptsache 64 Bit. Die Lösung von Karl Heinz ist im Prinzip dasgleiche, nur gefällt dem Prozessor ne integer-Division mit ner Potenz von 2 besser. Mit 'long long' ist immer solche Sache, das kennt mein Compiler sowenig wie 'ULL' als suffix. Cheers Detlef
so, habe mal den Code von Detlef ausprobiert. Mit dem SSH = 16 wird der Code sehr klein und es funktioniert aber nicht. In t_word steht dann 0 drin. Der Compiler sagt auch: warning: left shift count >= width of type Nun, das kann eigentlich nur in der ersten Zeile passieren, wo noch mit 1e6 multipliziert wird. Nun habe ich mal in der 1. Zeile mit SSH = 8 und sonst mit 16 probiert. Es sind nur die Werte mit SSH = 8 richtig. Und warum ist der Code wenn SSH = 16 ist so viel kleiner als bei 8 ??? das versteh ich nicht..... wenn ich das so schreibe, wie Karl Heiz vorgeschlagen hat, dann funktioniert es schon, der Code wird nur viel größer. Wahrscheinlich weil der Teiler keine Potenz von 2 ist. Ich werd mal weiter forschen. Mattias
"104.000 000 080 000 0 Mhz ein." Und Du glaubst echt im Ernst, daß Du 100nHz Genauigkeit erreichst ??? Und Deine Anwendung ist auch wirklich so empfindlich, daß 100nHz eine Rolle spielen ??? Peter
Hallo, die '8' von 104.000 000 080 000 0Mhz sind 0.08Hz, das kann ne DDS mit 48Bit und das muß sie auch können, um z.B. ne PLL mit dem Ding aufzubauen, du willst ja phasenstarr koppeln, da darf nix weglaufen (obwohl: 104Mhz scheint ja FM Radio zu werden, das geht dann auch nicht so genau !!??). Mattias, diese Warning hat vielleicht was mit dem Präprozessor zu tun, wäre dann vielleicht gut, die defines mit nem Taschenrechner per Hand zu berechnen und dann direkt einzusetzen !? (die 1 von '1<<SSH' hält Compiler ja für 'int', das sind beim AVR 16Bit, 16 nach links geht also nich, mal mit 1ULL probiern?) Cheers Detlef
Hallo Leute Kommt mal wieder runter von Wolke Nr. 7, zurück auf den Boden der Realität! Ich arbeite mit DDS und numerischen Loopfiltern für extrem tiefe PLL-Bandbreiten! 32 Bit Auflösung sind für einen 100 MHz DDS in der Regel mehr als genug, dies ergibt immerhin eine Auflösung von ca. 0.05Hz! 0.05 Hz / 100 MHz => 0.0005ppm Ich beneide jeden, der einen Referenz-Oszillator genauer als 1ppm sein Eigen nennt!!! 32 Bit Integer bzw. 10-Digit Dezimalzahlen reichen also völlig aus, mit dieser Auflösung kannst man "straight forward" programmieren, ohne irgend welche Zahlenakrobatik! Eine höhere Auflösung ist Nonsense! Gruss Peter
Hallo, ok, das mit dem 1ULL hat er jetzt gefressen. Allerdings passt der Code jetzt nicht mehr in mein flash. Ich werde also die defines für jede Stelle optimal ausrechnen. Eigentlich ist ja nur die 1. und die 2. Zahl so groß. Den Rest kann ich anders errechnen. Ich werd berichten, wie ich am wenigsten Platz verbrauche. Übrigens, ich will weder ein Radio noch ne PLL aufbauen. Die Zahlen sind willkürlich gewählt. Ich brauche den DDS als präzisen Frequenzteiler. Peter, ich erzeuge meine Referenz mit einem Femtosekunden-Laser und einer Rep-Rate von mehreren hundert Gigahertz. Damit wird ein Frequenzkamm erzeugt und die Differenz von Mischprodukten zu einem zweiten Laser ermittelt. Wer sich dafür interessiert, kann hier nachlesen http://www.ptb.de/de/org/4/43/sfb/b10/freque.htm Wenn es einen DDS mit mehr als 48 Bit gäbe, würde ich eher den nehmen. Mattias
so, folgende Schreibweise erzeugt den kleinsten Code: program(temp_cursor, temp_menu_index, (( i * ( X 1000000ULL (1ULL<<SSH)) + j * ( X 1000ULL (1ULL<<SSH)) + k * ( X 1ULL (1ULL<<SSH)) + l * ( X / 1000ULL * (1ULL<<SSH)) + m * ( X / 1000000ULL * (1ULL<<SSH)) + n * ( X / 1000000000ULL * (1ULL<<SSH)) )) / (1ULL<<SSH), ( o * ( Y ) + p * ( Y / 1000ULL) ) ); mit X und Y jeweils fest definiert. Wenn noch jemand einen Fehler sieht, bitte ich um Nachricht. Danke für die professionelle Hilfe. Mattias
Hallo Mattias, hast Du nicht letztens für Deinen Frequenzkamm den Nobelpreis in Physik gekriegt? http://www.mpg.de/bilderBerichteDokumente/dokumentation/pressemitteilungen/2005/pressemitteilung20051004/ Ach nee, das war ja der Theodor! Peter, 5e-10 als absolute Genauigkeit der Frequenz ist schon sehr gut, ist das nicht die Größenordnung der Genauigkeit von DLF77? Wenn Du mit einem Empfänger aber kohärent zu einem Sender sein muß, hast Du bei 0.05Hz Frequnzabweichung in 20sec. eine Welle verloren, da kann schon garnix mehr gehn! Cheers Detlef
"hast Du nicht letztens für Deinen Frequenzkamm den Nobelpreis in Physik gekriegt?" Na mit dem Code bestimmt nicht. Wenn der Compiler gut ist, dann optimiert er X * (1ULL<<SSH) / (1ULL<<SSH) weg zu X. Und den Ausdruck mit n komplett weg, da ja 2815315.315 / 1000000000ULL immer 0 ist. Peter
Okydoky, ich nahem das mit dem "Nonsens" kleinlaut zurück! ;o) Ich würde die Frequenz in Herz mit Dezimalpunkt eingeben. z.B. "104000000.0800000" [Hz] Den String dann in zwei Teile zerlegen, in einen Hz-Sring und in ein nHz-String. (Der nHz-String könnte mittels Stringmanipulation auf die entsprechende Länge erweitert werden, falls man nicht immer alle Nullen eintippen will.) Mit der atol() Funktion können die Strings in zwei 32-Bit Integers konvertiert werden, die dann zu einem 64-Bit Integer zusammengefügt werden. Freq_64 = Freq_Hz*1000000000ULL + Freq_nHz; Da der GCC 64Bit Arithemetik locker beherrscht, müsste dies und die ganze restliche register Berechnung in 2..3 kByte passen! Anbei habe ich den Code um meinen 32-Bit DDS zu setzen, diese Funktion sollte mit kleinen Änderungen auch auf nen 48 Bit DDS anpassbar sein. typedef signed char s08; typedef signed int s16; typedef signed long s32; typedef signed long long s64; typedef unsigned char u08; typedef unsigned int u16; typedef unsigned long u32; typedef unsigned long long u64; void SPI_set_dds(u32 f_dds) //--------------------------------------------------- // Set DDS (AD9851) frequency to [f_dds] Hz // f_dds = 0 will set the DDS into PowerDown Mode // f_dds = 1..40'000'000 Hz set DDS output frequency //--------------------------------------------------- #define DDS_SYS_CLK 100000000UL // 100 MHz (as unsigned long costant) #define DDS_MPL 0x0100000000000000ULL // 2^56 as unsigned long long const // multiplicator to ensure enough bit accuracy { union {u64 reg; u08 byte[8];} freq; // for easy byte access to u64 freq.reg = (f_dds * (DDS_MPL/DDS_SYS_CLK)); // 32 x 32 Bit multiplication PORTB &= ~(1<<CS_DDS); // set DDS-Select low for (u08 i=3; i<7; i++) { SPDR=freq.byte[i]; // send freq.byte[3..6] over SPI to DDS while(!(SPSR & (1<<SPIF))); // wait until SPI transfer is complete } if (f_dds) // if frequency > 0 Hz then { SPDR=0x00; // DDS-PhaseControlWord (Phase=0; PowerDown=0; 6xREFCLK=0) } else // else enable PowerDown for the DDS { SPDR=0x04; // DDS-PhaseControlWord (Phase=0; PowerDown=1; 6xREFCLK=0) } while(!(SPSR & (1<<SPIF))); // wait until SPI transfer is complete PORTB |= (1<<CS_DDS); // set DDS-Select high } MfG Peter
Nochmals der selbe Code, will bloss ausprobieren ob es nun mit Formatierung "schöner" aussieht.
1 | typedef signed long long s64; |
2 | typedef unsigned char u08; |
3 | typedef unsigned int u16; |
4 | typedef unsigned long u32; |
5 | typedef unsigned long long u64; |
6 | |
7 | |
8 | void SPI_set_dds(u32 f_dds) |
9 | //---------------------------------------------------
|
10 | // Set DDS (AD9851) frequency to [f_dds] Hz
|
11 | // f_dds = 0 will set the DDS into PowerDown Mode
|
12 | // f_dds = 1..40'000'000 Hz set DDS output frequency
|
13 | //---------------------------------------------------
|
14 | #define DDS_SYS_CLK 100000000UL // 100 MHz (as unsigned
|
15 | long costant) |
16 | #define DDS_MPL 0x0100000000000000ULL // 2^56 as unsigned long
|
17 | long const |
18 | // multiplicator to
|
19 | ensure enough bit accuracy |
20 | {
|
21 | union {u64 reg; u08 byte[8];} freq; // for easy u64 byte |
22 | access
|
23 | freq.reg = (f_dds * (DDS_MPL/DDS_SYS_CLK)); // 32 x 32 Bit => 64 Bit |
24 | multiplication
|
25 | PORTB &= ~(1<<CS_DDS); // set DDS-Select low |
26 | for (u08 i=3; i<7; i++) |
27 | {
|
28 | SPDR=freq.byte[i]; // send freq.byte[3..6] |
29 | over SPI to DDS |
30 | while(!(SPSR & (1<<SPIF))); // wait until SPI |
31 | transfer is complete |
32 | }
|
33 | if (f_dds) // if frequency > 0 Hz |
34 | then
|
35 | {
|
36 | SPDR=0x00; // DDS-PhaseControlWord |
37 | (Phase=0; PowerDown=0; 6xREFCLK=0) |
38 | }
|
39 | else // else enable PowerDown |
40 | for the DDS |
41 | {
|
42 | SPDR=0x04; // DDS-PhaseControlWord |
43 | (Phase=0; PowerDown=1; 6xREFCLK=0) |
44 | }
|
45 | while(!(SPSR & (1<<SPIF))); // wait until SPI |
46 | transfer is complete |
47 | PORTB |= (1<<CS_DDS); // set DDS-Select high |
48 | }
|
p.s. Wenn der Funktion als Frequenzparameter ein 64-Bitwert in nHz (statt 32 Bit in Hz) übergibt, brauchts vermutlich den DDS_MPL Multiplikator nicht mehr.
> Wenn der Compiler gut ist, dann optimiert er X * (1ULL<<SSH) / > (1ULL<<SSH) weg zu X. Nein, das macht er nur, wenn er fehlerhaft ist, außer wenn SSH den Wert 0 hat.
@Rolf, wo ist denn da der Fehler ? X == X * Y / Y Und selbst wenn SSH = 0 ist, dann stimmt es immer noch: X == X * 1 / 1 Nur anders herum: X / Y * Y darf natürlich nicht optimiert werden, da eine solche Funktion sinnvoll ist, z.B. zum Runden. Peter
Hallo Leute, so, ich habe noch sehr viel experimentiert. ich habe die Eingabe über mein Display auf die Koeffizienten i,j,k,l und m beschränkt und habe beschlossen die Werte für nano Herz nur im remote Betrieb zu setzen (über rs232 direkt in das Register zu schreiben). Ich kenne mich mit dem gcc intern wirklich nicht gut aus, folgender Code erzeugt aber den kleinsten Code und funktioniert bis µhz runter. i,j,k,l und m können Werte von 0-999 annehmen. X habe ich fest definiert und den 48bit Wert so berechnet: #define X (2814749767107) unsigned (long long) t_word; unsigned int i,j,k,l,m; t_word = (i * X) + (j * (X / 1000)) + (unsigned long) ( (k * (X / 1000000)) + (l * (X / 1000000000)) + (m * (X / 1000000000000))); Wenn mir jemand sagen könnte, wie ich diese Rechnung noch kleiner hinbekomme (zB in asm) währe ich sehr dankbar. Allerdings habe ich von asm fast überhaupt keine Ahnung. Mein Mega8 ist aber jetzt schon fast voll. Mattias
"X == X * Y / Y" In der üblichen Mathematik schon. Freilich arbeiten Computer mit einer speziellen Form der Arithmetik, dank begrenzter Bitzahl. Und da kann in (X*Y)/Y das Produkt überlaufen, somit hat der Ausdruck nicht die exakt gleiche Wirkung wie X und der Compiler darf das nicht wegoptimieren.
Hallo Mattias, sieht gut aus, der code. X hat 38Bit, 0..999 hat 10Bit, zusammen 48. Bißchen Genauigkeit verschenkst Du noch ohne Grund: Du rechnest mit 64Bit, also könntest Du X noch Faktor, naja, 2^14 oder so, aufskalieren und nachher 14 Bit wieder runterschieben. Codesize: Du mußt 16Bit * 64 Bit multiplizieren, keine Ahnung, was der gcc da aus den libs zubindet, könnte aber viel sein, mal im map-file kucken. Alternative: Du bastelst eine Routine, die Dir 16Bit*64Bit mit schieben und addieren rechnet, dauert dannn aber vermutlich länger. Cheers Detlef
Hallo! Da ich in C-programmierung mich nicht auskenne und nur ab und zu in Assembler programmiere(und auch da nicht perfekt,ich entwickle eigentlich nur leiterplatten usw.),wer kann mir bitte ein Programm für den ATmega103/128)für die Programmierung eines AD9852 Tuning Words (aber hier nur 32Bit,ist ja vollkommen ausreichend)zumailen? Mir geht es nur um die Berechnung des FTW(fout=2Hoch32/Ftakt). fout max.100MHz,Schrittweite 10Hz.Und dann die Umrechnung diese dezimalen Wertes in hex. Danke Dieter
Gucke mal etwas weiter "oben", da habe ich meine Funktion für den AD9851 gepostet! (Inkl. SPI-Bus Ansteuerung) Ich nutze 1 Hz Auflösung und 32 bit, müsste genauso auchfür den AD9852 tun! Peter
Danke Peter! Du meinst doch bestimmt die "DIVMUL56.ASM" Datei? Dieter
Nee, das ist von einem anderen Peter...! ;o) Ich meine mein Post mit der Funktion: void SPI_set_dds(u32 f_dds) MfG Peter
Danke Peter! Da ich mich mit C noch nicht beschäftigt habe nützt mir diese Datei nichts.Analog Device hat ein Tool speziell für den AD9852.Da gebe ich die Taktfrequenz und eine Ausgangsfrequenz vor,übetrage die Hex-Werte in den Assembler und brauche dann nur noch ADD oder SUB auszuführen.Die Multiplikation mit 2^32/Takt kann ich mir sparen.Ich muß nur(!!!) 32Bit rechnen.32Bit reichen allemal,die LSB-Bit setze ich auf Null.Aber die 32Bit ADD und SUB machen mir schon Probleme.Ich bin halt kein Softwarespezi,sondern entwickle Schaltungen und Leiterplatten,Analog und Digital.Ich habe auch die Möglichkeit die Platinen industriell ätzen zu lassen.Vielleicht ist es zuviel verlangt,aber hast Du evtl.eine 32+32Bit Assemblerroutine für ATmega128?Soll auch nicht umsonst sein. Dieter
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.