Hallo,
Bei einer Schaltung ist jetzt durch ein Update der Speicher des
verwendeten Attiny2313 zu klein geworden. Bei der Suche nach Code der
viel Speicher benötigt bin ich bei folgenden zwei Codeausschnitten
fündig geworden.
1
#define DDS_CLK 125000000UL
2
...
3
uint64_ttuningword;
4
tuningword=(frequency*4294967296)/DDS_CLK;
5
...
Hier wird das 32-bit Tuning-Word für einen 9850 DDS-Chip berechnet.
1
uint32_tGetFreqency(uint8_tvalues[])
2
{
3
uint32_tretval;
4
5
retval=values[0];
6
retval+=values[1]*10;
7
retval+=values[2]*100;
8
retval+=values[3]*1000;
9
retval+=values[4]*10000;
10
retval+=values[5]*100000;
11
retval+=values[6]*1000000;
12
retval+=values[7]*10000000;
13
14
returnretval;
15
}
Hier wird der Wert der an der 8-stelligen 7-Segment-Anzeige angezeigt
wird berechnet.
Ich zerbrich mir jetzt schon länger den Kopf darüber aber ich finde
keinen Weg diese Code-Ausschnitte effizienter zu schreiben.
MN schrieb:> uint32_t GetFreqency(uint8_t values[])
wo wird denn die Funktion aufgerufen? Wo wird das Array gefüllt?
Kannst du den Kompletten Code zeigen, meist lässt sich da besser
erkennen wo man etwas optimieren kann. Es macht wenig sinn eine Funktion
zu verbessern wenn der Ansatz schon merkwürdig ist.
Hier kommt man da Array merkwürdig vor.
MN schrieb:> Hier wird das 32-bit Tuning-Word für einen 9850 DDS-Chip berechnet.>
Horner Regel
1
uint32_tGetFreqency(uint8_tvalues[])
2
{
3
uint32_tretval=0;
4
uint8_ti;
5
6
for(i=0;i<8;i++)
7
retval=retval*10+values[7-i];
8
9
returnretval;
10
}
Die Richtung der Schleife könnte man noch umdrehen, dann fällt der
'unschöne' Ausdruck 7-i in der Inidzierung weg.
Aber ob das dein Platzproblem signifikant entlastet - da habe ich so
meine Zweifel.
Auf der anderen Seite: wenn du nur ein paar Bytes mehr brauchst ...
als pseudoschleife:
i=7
Ergebnis=0
{
Ergebnis=zahl[i]+Ergebnis*10
i=i-1
}solange bis i=0
und wenn möglich könnte man sogar den Input direkt in der schleife
abholen.
das würde dann sogar das speichern der einzelstellen sparen.
i=7
Ergebnis=0
{
Ergebnis=lies_stelle_n_ein(i)+Ergebnis*10
i=i-1
}solange bis i=0
sg
Du übergibst der Routine die auf das 7Seg. ausgibt also einen 32-Bit
Wert. Was macht die Funktion die das ganze auf dem 7Seg. ausgibt mit dem
Wert...? Was ich damit sagen will Du wandelst einzelne Ziffern eines
numerischen Wertes in einen solchen um. Zur Ausgabe auf einer 7Seg.
Anzeige muss das ganze meist wieder Rückgängig gemacht werden. Da lässt
sich vielleicht was optimieren. Wieviele Bytes musst Du denn sparen?
Meist hilft es teile des Programs in Assembler zu programmieren, wenn es
nicht um Speicher im KB Bereich geht.
Welcher Speicher ist zu knapp? RAM bei der Ausführung oder Flash für den
Programmspeicher?
Hast du schon mal mit den Optimier-Optionen des Compilers gespielt? Ich
vermute mal der ist eher in der Lage das zu reduzieren als irgendwelche
Programmiertricks.
Wobei mir ehrlich gesagt nicht wirklich klar ist, weshalb gerade diese
Funktion und das Array viel Platz wegnehmen sollten, da steht doch kaum
etwas da? Womit hast du herausgefunden, dass gerade diese Code-Teile
viel Platz benötigen?
Jorge schrieb:> Wobei mir ehrlich gesagt nicht wirklich klar ist, weshalb gerade diese> Funktion und das Array viel Platz wegnehmen sollten, da steht doch kaum> etwas da?
32-bit-Multiplikation und -Division.
Welche Compilerversion ist das? Meiner Erinnerung nach hat sich da
in den aktuellen Versionen einiges getan.
Einen ATtiny2313 hätte ich allerdings für diesen 32-bit-Kram wohl
auch eher nicht benutzt. Ggf. kannst du ja noch einen „Upgrade“ auf
einen ATtiny4313 in Erwägung ziehen, um paar Reserven zu bekommen.
Jorge schrieb:> da steht doch kaum etwas da?
Ich kenne den verwendeten Controller nicht, aber wenn dieser keinen
Hardwaremultiplizierer besitzt und die ganzen verschiedenen
Multiplikationen in Software durchgeführt werden müssen, kann ich mir
schon vorstellen, dass der ROM knapp wird.
be s. schrieb:> wenn dieser keinen Hardwaremultiplizierer besitzt und die ganzen> verschiedenen Multiplikationen in Software durchgeführt werden müssen
So ist es.
Ich würde für sowas (bzw. nehme) wenigstens einen ATmega, der hat
einen Hardware-Multiplizierer, was deutlich Code spart.
be s. schrieb:> Ich kenne den verwendeten Controller nicht, aber wenn dieser keinen> Hardwaremultiplizierer besitzt und die ganzen verschiedenen 32 Bit> Multiplikationen in Software durchgeführt werden müssen, kann ich mir> schon vorstellen, dass der ROM knapp wird.
Na ja. So wild ist das dann auch wieder nicht.
Die 32 * 32 Bit Multiplikation wird ja nicht direkt an dieser Stelle
gemacht, sondern dafür wird eine Funktion aufgerufen.
D.h. in der ursprünglichen Fassung werden da 8 Funktionsaufrufe stehen
sowie entsprechende Register Lade Anweisungen um die verschiedenen
Konstanten auf den Weg zu bringen.
Das kann man natürlich etwas zusammenstreichen, aber um die
Multiplikationsfunktion kommt man nicht herum. D.h. im wesentlichen kann
man 7 Funktionsaufrufe samt Registervorbelegung einsparen. Ist natürlich
eine Einsparung, aber die Welt wird es auch nicht sein.
be s. schrieb:> Ich kenne den verwendeten Controller nicht, aber wenn dieser keinen> Hardwaremultiplizierer besitzt und die ganzen verschiedenen> Multiplikationen in Software durchgeführt werden müssen, kann ich mir> schon vorstellen, dass der ROM knapp wird.
wobei man sagen muss, das die Multiplikationen für jeden Datentype nur
einmal im ROM ist. Wenn er an einer andere stelle noch eine
Multiplikationen verwendet, bringt es überhaupt nichts diese Funktion zu
optimieren.
Jörg W. schrieb:> Ich würde für sowas (bzw. nehme) wenigstens einen ATmega, der hat> einen Hardware-Multiplizierer, was deutlich Code spart.
oder das Problem gleich anders Lösen. Kann mir nicht so richtig
vorstellen für was man so ein Array braucht. Wenn dort wenigsten ASCII
drin stehen würde, könnte ich es verstehen. Aber wo werden denn die
Ziffern ein Zahl binär übertragen?
Peter II schrieb:> Kann mir nicht so richtig vorstellen für was man so ein Array braucht.
Ist ja nicht nur das Array, sondern eben auch die Berechnung des
tuning words für die DDS.
Jörg W. schrieb:> Ist ja nicht nur das Array, sondern eben auch die Berechnung des> tuning words für die DDS.
aber wieso braucht man dort das Dezimalsystem? (habe noch nie mit einem
DDS gearbeitet)
MN schrieb:> #define DDS_CLK 125000000UL> ...> uint64_t tuningword;> tuningword = (frequency * 4294967296) / DDS_CLK;> ...
Wenn man den konstanten ausdruck 4294967296) / DDS_CLK so umrechnet,
dass eine 2er Potenz im Nenner steht, könnte man sich den Platz für die
Divisionsroutine im Flash sparen.
Peter II schrieb:> Jörg W. schrieb:>> Ist ja nicht nur das Array, sondern eben auch die Berechnung des>> tuning words für die DDS.>> aber wieso braucht man dort das Dezimalsystem? (habe noch nie mit einem> DDS gearbeitet)
Keine Ahnung.
Ich schätze mal das wird ein Überbleibsel aus der Benutzersteuerung
sein. 8 Drehgeber - für jede Stelle eine. Oder irgend so etwas.
Hallo vielen Dank schon mal an alle. Ich komme mit dem Lesen fast nicht
mehr hinterher. Der Code passt jetzt gerade so in den Controller ist
aber noch nicht ganz fertig.
Peter II schrieb:> Kannst du den Kompletten Code zeigen, meist lässt sich da besser> erkennen wo man etwas optimieren kann. Es macht wenig sinn eine Funktion> zu verbessern wenn der Ansatz schon merkwürdig ist.
Ja kann ich selbstverständlich. In der Zip-Datei im Anhang sollten alle
Code-Dateien enthalten sein. Der Code ist noch etwas wirr und
unformatiert da ich gerade ständig irgendwo etwas ändere. Ich hoffe man
kann ihn trotzdem einigermaßen entziffern.
Karl H. schrieb:> Horner Regel
Vielen Dank. Das hat schon mal viel gebracht! Wenn ich jetzt den Code
sehe könnte ich mir in den Hintern beißen. Darauf hätte ich auch selber
kommen können.
Thomas H. schrieb:> Du übergibst der Routine die auf das 7Seg. ausgibt also einen 32-Bit> Wert. Was macht die Funktion die das ganze auf dem 7Seg. ausgibt mit dem> Wert...? Was ich damit sagen will Du wandelst einzelne Ziffern eines> numerischen Wertes in einen solchen um. Zur Ausgabe auf einer 7Seg.> Anzeige muss das ganze meist wieder Rückgängig gemacht werden. Da lässt> sich vielleicht was optimieren.
Nein, ich übergebe der Funktion die auf die 7Segment-Anzeige schreibt
die Stellen schon einzeln. Ich verwende einen MAX7219 als
Display-Treiber. Den 32bit-Wert brauche ich zur Berechnung des
Tuning-Wortes für den AD9850.
Mit der Hilfe von Karl H. passt der Code jetzt gerade so in den Chip
(text=1908). Ich muss allerdings noch eine Wertebereich-Abfrage
einfügen.
Jorge schrieb:> Hast du schon mal mit den Optimier-Optionen des Compilers gespielt? Ich> vermute mal der ist eher in der Lage das zu reduzieren als irgendwelche> Programmiertricks.
Optimierer-Option ist leider bereits auf -Os.
MN schrieb:> Hallo vielen Dank schon mal an alle.
Fällt mir gerade so nebenbei ein:
Der DDS AD9850 braucht die Bits "umgekehrt" hineingeschaufelt
gegenüber der Wertigkeitsfolge wie sie die üblichen AVR
SPI Maschinen erzeugen.
Dies, falls jemand darauf hereinfallen sollte ....
Man braucht einen Bit Swapper.
die Variable temp wird dir der Compiler sowieso rauswerfen. In
Wirklichkeit braucht die keiner. Du könntest die entsprechende
Auswertung auch hier
1
if(tmp>0)
ganz einfach einsetzen. Worum gehts an dieser Stelle tatsächlich? Es
geht darum, ob 1 Bit auf 1 oder auf 0 stand. Ob dieses temp größer als 0
ist, interessiert eigentlich nicht. ungleich 0 reicht und dann kann man
das so schreiben
1
if(data&(1<<i))
2
PORT_DISPLAY|=(1<<DISP_DATA);
3
else
4
PORT_DISPLAY&=~(1<<DISP_DATA);
aber eigentlich willst du auf einem AVR einen Ausdruck der Form '1<<i'
nicht haben, denn den muss der Compiler in Form einer Schleife auflösen.
eine adequate Umsetzung, die ohne so einen variablen Shift auskommt,
lautet
1
voidSend(uint16_tdata)
2
{
3
uint16_tmask=0x8000;
4
5
PORT_DISPLAY&=~(1<<DISP_CS);
6
7
for(uint8_ti=0;i<16;i++)
8
{
9
PORT_DISPLAY&=~(1<<DISP_CLK);
10
11
if(data&mask)
12
PORT_DISPLAY|=(1<<DISP_DATA);
13
else
14
PORT_DISPLAY&=~(1<<DISP_DATA);
15
16
PORT_DISPLAY|=(1<<DISP_CLK);
17
18
mask>>=1;
19
}
20
21
PORT_DISPLAY&=~(1<<DISP_CLK);
22
23
// CS pulse
24
PORT_DISPLAY|=(1<<DISP_CS);
25
PORT_DISPLAY&=~(1<<DISP_CS);
26
}
Achte auch auf Datentypen! Du schmeisst da zb bei deinem i mit einem int
um dich, obwohl du den Wertebereich überhaupt nicht brauchst. Das alles
kostet. Nicht viel, aber ein paar Bytes sind es auch.
Man könnte auch darüber nachdenken, sich eine raustakt-Funktion für
einen 8 Bit Wert zu schreiben und die dann 2 mal aufzurufen. Das würde
die ganzen 16 Bit OPerationen runterbrechen. Müsste man ausprobieren,
was dann tatsächlich kürzer ist.
(Das war jetzt nur die allererste Funktion, auf die ich nach öffnen des
Zip Files im ersten C-File gestossen bin)
Profi Frickler schrieb:> Der DDS AD9850 braucht die Bits "umgekehrt" hineingeschaufelt> gegenüber der Wertigkeitsfolge wie sie die üblichen AVR> SPI Maschinen erzeugen.
So?
Ich habe das hier:
1
staticvoid
2
fq_ud(uint32_tfq)
3
{
4
union
5
{
6
uint32_tl;
7
uint8_tb[5];
8
}
9
u;
10
uint8_ti;
11
12
u.l=fq;
13
/*
14
* W32..W39: no phase angle, no power-down, control bits = 0
15
*/
16
u.b[4]=0;
17
18
for(i=0;i<5;i++)
19
{
20
SPDR=u.b[i];
21
while((SPSR&_BV(SPIF))==0)
22
/* wait */;
23
}
24
25
/* Update now. */
26
PORTC|=_BV(1);
27
PORTC&=~_BV(1);
28
}
… und das funktioniert. Nachgemessen. ;-)
Hier ist die Initialisierungsroutine:
1
staticvoid
2
init_ad9850(void)
3
{
4
/*
5
* Disable SPI, and reset the DDS circuitry.
6
*/
7
SPCR=0;
8
PORTC|=_BV(0);
9
PORTC&=~_BV(0);
10
11
/*
12
* After reset, initiate one W_CLK pulse followed by one FQ_UD pulse
13
* to activate the serial configuration control word.
14
*/
15
PORTB|=_BV(7);
16
PORTB&=~_BV(7);
17
PORTC|=_BV(1);
18
PORTC&=~_BV(1);
19
20
/*
21
* Now, we're ready to hand over the data transmission to SPI.
22
*/
23
SPCR=_BV(SPE)|_BV(DORD)|_BV(MSTR);
24
SPSR=_BV(SPI2X);
25
}
Kann es sein, dass dir das DORD-Bit entgangen ist?
Profi Frickler schrieb:> Man braucht einen Bit Swapper.
Nein braucht man doch nicht, man kann die SPI auch umkonfigurieren.
Ich war nur damals nicht willens meinen Treiber zu modifzieren.
Profi Frickler schrieb:> Ich war nur damals nicht willens meinen Treiber zu modifzieren.
Wenn man dann auch noch ein SPI LCD mit dem gleichen Treiber
ansteuert dann muss man auch noch dauernd die Wertigkeit umdrehen .....
das willst du nicht tun!
Du willst nicht aus einer ISR heraus eine Funktion aufrufen!
Damit zwingst du den Compiler zu einer Registersicherorgie. Die kostet
Laufzeit (was noch verschmerzbar wäre) aber sie kostet natürlich auch
Code!
Zieh die ISR runter zur Funktion und bette den Funktionsinhalt direkt in
die ISR ein.
Peter II schrieb:> wobei man sagen muss, das die Multiplikationen für jeden Datentype nur> einmal im ROM ist.
Korrekt. Ich sehe hier jedoch zwei Multiplikationsroutinen:
16 Bit signed:
1
values[1]*10;
2
values[2]*100;
3
values[3]*1000;
4
values[4]*10000;
32 Bit signed:
1
values[5]*100000;
2
values[6]*1000000;
3
values[7]*10000000;
Insbesondere diese Zeile
1
values[4]*10000;
kann aufgrund des Wertebereichs zum Problem werden. Diese Multiplikation
müsste mit 32 Bit ausgeführt werden.
Nachtrag: Ich nehme an, dass im Array nur Werte von 0 bis 9 gespeichert
sind, ansonsten müssten andere Multiplikationen auch als 32 Bit
durchgeführt werden.
dasselbe Spielchen noch einmal.
Ich denke, es würde auch etwas bringen, wenn man sich eine eigene 8 Bit
Ausgabefunktion schreibt und die dann direkt mit dem jeweils
zurechtgeschobenen Byte aufruft. Das Array braucht man in Wirklichkeit
nämlich gar nicht
1
voiddddsSetFrequencyByte(uint8_tbyte)
2
{
3
uint8_ti;
4
5
for(i=0;i<8;i++)
6
{
7
PORTD&=~(1<<PIN_WCLK);
8
9
if(byte&0x01)
10
PORTD|=1<<PIN_DATA;
11
else
12
PORTD&=~(1<<PIN_DATA);
13
14
PORTD|=(1<<PIN_WCLK);
15
16
byte>>=1;
17
}
18
}
19
20
voidddsSetFrequency(uint32_tfrequency)
21
{
22
uint64_ttuningword;
23
24
tuningword=(frequency*4294967296)/DDS_CLK;
25
26
dddsSetFrequencyByte(tuningword&0xFF);
27
dddsSetFrequencyByte((tuningword>>8)&0xFF);
28
dddsSetFrequencyByte((tuningword>>16)&0xFF);
29
dddsSetFrequencyByte((tuningword>>24)&0xFF);
30
dddsSetFrequencyByte(0x00);
31
32
PORTD&=~(1<<PIN_WCLK);
33
PORTD|=(1<<PIN_FQUP);
34
PORTD&=~(1<<PIN_FQUP);
35
}
aber der Löwenanteil wird wahrscheinlich tatsächlich für die 64Bit bzw.
32Bit Arithmetik drauf gehen.
Der Code passt mittlerweile in den Controller und macht auch schon das,
was er soll. Vielen Dank an dieser Stelle noch einmal für die Hilfe.
Karl H. schrieb:> ISR(TIM_COMP_vect)> {> buttonDebounceISR();> }>> das willst du nicht tun!>> Du willst nicht aus einer ISR heraus eine Funktion aufrufen!> Damit zwingst du den Compiler zu einer Registersicherorgie. Die kostet> Laufzeit (was noch verschmerzbar wäre) aber sie kostet natürlich auch> Code!>> Zieh die ISR runter zur Funktion und bette den Funktionsinhalt direkt in> die ISR ein.
Okey, das sehe ich ein.
Dieses komische Konstrukt kommt daher, dass man in der Timer-ISR neben
dem entprellen oft andere Aufgaben mit erledigen kann. Dann habe ich
zwei Möglichkeiten:
1) die ISR() steht in der button.c und enthält Code, der eigentlich gar
nicht zum Taster gehört. Die Datei button.c würde dann in jedem Projekt
anders aussehen was ja nicht der Sinn der Sache ist.
2) die ISR() steht z.B. in main.c und der Code der jetzt in
buttonDebounceISR() steht muss kopiert werden. Auch irgendwie eine
unschöne Arbeitsweise.
Könnte man denn die angesprochene "Registersicherorgie" nicht vermeiden,
wenn man die Funktion buttonDebounceISR() mit "inline" definieren würde?
MN schrieb:> Könnte man denn die angesprochene "Registersicherorgie" nicht vermeiden,> wenn man die Funktion buttonDebounceISR() mit "inline" definieren würde?
Im Zweifelsfall: ausprobieren und im Listing File nachsehen ob und was
sich verändert hat. In deinem Fall könnte man auch sagen: Wird das
Hex-File kleiner, dann hat es was gebracht - auch wenn die nackten
Zahlen interpretiert werden wollen.
Karl H. schrieb:> Im Zweifelsfall: ausprobieren und im Listing File nachsehen ob und was> sich verändert hat. In deinem Fall könnte man auch sagen: Wird das> Hex-File kleiner, dann hat es was gebracht - auch wenn die nackten> Zahlen interpretiert werden wollen.
Das werde ich bei Gelegenheit machen. Heute bin ich schon mal sehr
zufrieden, dass die neue Codeversion für den DDS läuft.
danke
PS: Ich bin mal wieder entsetzt auf wie viele Dinge ich beim
programmieren zu wenig achte. Das kommt wohl daher das ich die µC
ansonsten eher überdimensioniere.
Hallo,
hier ist noch ein anderer Ansatz für die Berechnung des FTW eines AD8950
mit FCLK = 125MHz konstant !.
FTW = (34 +1/2 -1/7 +1/385 -1/528.613) * f ; f in Hz.
D.h. man benötigt 1 Multiplikation, 4 Divisionen, 2 Additionen und 2
Subtraktionen
Frickelfritze,
dann bist Du kein Mathematiker und hast den Vorteil dieser
Näherungsformel, für FTW = f * 2^32 /125MHz, nicht verstanden.
Mann muss sich nur mal die verschiedenen Wertebereiche ansehen..
Uwe S. schrieb:> dann bist Du kein Mathematiker
Für soviel reicht es dass ich sagen kann dass der Ausdruck
in der Klammer
Uwe S. schrieb:> (34 +1/2 -1/7 +1/385 -1/528.613)
konstant ist und damit nur einmal berechnet werden muss.
Du würdest es aber jedesmal neu berechnen wenn du nicht Glück
hast und der Compiler das sowieso wegoptimiert.
In deiner Formel kommt noch "erschwerend " hinzu dass eine
Floating Point Zahl vorkommt die den Compiler zwingt
in FP zu rechnen. Sehr ungünstig ..... für die Erzeugung
von minimalem Code in einem AVR .....
Hallo,
Um zu erfahren mit wie vielen Bits man rechnen muss, macht man eine
Abschätzung.
Es gilt mit FCLK = 125MHz:
a) k = 2^32 /FCLK ~ 34,360 < 35
b) 0 <= f <= FCLK/3 ~ 41,667MHz
Anm.: Mit FCLK/3 hat man noch 3 Stützstellen bei der Sinusgenerierung.
Wir wählen für unsere Rechnung als obere Grenzfrequenz f_max = 40MHz.
Es folgt:
FTW = f *2^32 /FCLK = f *(k -Fehlerterm) = f *k - f *Fehlerterm <= f *k
mit k = 35 aus a) und f_max = 40MHz können wir die maximale Anzahl Bits
für unsere Rechnung herleiten:
max. Bit = log2(40 *10⁶ *35) = log2(1.4 *10³ *10⁶)
max. Bit = log2(1.4 *10⁹) = 30,38 Bit < 31 Bit
Also können wir bei alle Rechnungen mit ganzen 32 Bit Zahlen (0<=N<2³²)
durchführen ohne einen Überlauf erwarten zu müssen.
Frickelfritze schrieb:
Sorry, für das Missverständnis:
528.613 = 528613 und es wurde ein Tausendertrennzeichen eingefügt.
> Uwe S. schrieb:>> dann bist Du kein Mathematiker>> Für soviel reicht es dass ich sagen kann dass der Ausdruck> in der Klammer>> Uwe S. schrieb:>> (34 +1/2 -1/7 +1/385 -1/528.613)>> konstant ist und damit nur einmal berechnet werden muss.> Du würdest es aber jedesmal neu berechnen wenn du nicht Glück> hast und der Compiler das sowieso wegoptimiert.>> In deiner Formel kommt noch "erschwerend " hinzu dass eine> Floating Point Zahl vorkommt die den Compiler zwingt> in FP zu rechnen. Sehr ungünstig ..... für die Erzeugung> von minimalem Code in einem AVR .....
Uwe S. schrieb:> Also können wir bei alle Rechnungen mit ganzen 32 Bit Zahlen (0<=N<2³²)> durchführen ohne einen Überlauf erwarten zu müssen.
In deiner Formel hast du das aber nicht verinnerlicht, das
mit den ganzen Zahlen ... und der Compiler weiss auch nichts davon.
Hallo Chris,
ich sehe schon, diese Art von Berechnung ist nicht vielen geläufig.
Natürlich löst man die Klammer auf.
Es gilt ja weiterhin laut Datenblatt des AD9850:
FTW = f * k mit k = 2³²/125MHz
und k ~ 34,3597 ~ (34 +1/2 -1/7 +1/385 -1/528613)
FTW ~ 34*f +f/2 -f/7 +f/385 -f/528613
chris schrieb:> Uwe S. schrieb:>> FTW = (34 +1/2 -1/7 +1/385 -1/528.613) * f ; f in Hz.>> 1/2 = 0> 1/7 = 0> 1/385 = 0>> integer Division...
Ich kann auch behaupten und beweisen, schon für die DDS AD9912, AD9851,
AD9851/AD9850 und AD9834 Funktionen für die Berechnung des FTW
geschrieben zu haben.
Karl H. schrieb:> ISR(TIM_COMP_vect)> {> buttonDebounceISR();> }>> das willst du nicht tun!>> Du willst nicht aus einer ISR heraus eine Funktion aufrufen!
Sorge dafür, dass die Funktion inlined wird (eventuell per LTO), dann
ist das kein Problem. Den Ansatz, in der ISR mehrere Funktionen
aufzurufen, ist jedenfalls grundsätzlich nicht schlecht; das Ganze
hässlich machen kann der Compiler selbst.
Uwe S. schrieb:> hier ist noch ein anderer Ansatz für die Berechnung des FTW eines AD8950> mit FCLK = 125MHz konstant !.>> FTW = (34 +1/2 -1/7 +1/385 -1/528.613) * f ; f in Hz.>> D.h. man benötigt 1 Multiplikation, 4 Divisionen, 2 Additionen und 2> Subtraktionen
Das ist großer Unfug. Du ersetzt jetzt nämlich eine Laufzeitoperation
("Konstante einsetzen") durch 9x Arithmetik, plus Registerbenutzung und
so weiter. Die Division alleine wird so viel Code erzeugen, dass der
Flash wieder platzt; die Rechenzeit ignoriere ich dabei mal gepflegt.
Zumal ich nicht verstehe, warum du 2^32/FCLK unbedingt zur Laufzeit
ausrechnen willst. Das kann der Compiler auch selbst. Dann bleibt da
nämlich "FTW = f * k" stehen, und das ist eine Laufzeitmultiplikation.
S. R. schrieb:> Zumal ich nicht verstehe, warum du 2^32/FCLK unbedingt zur Laufzeit> ausrechnen willst. Das kann der Compiler auch selbst. Dann bleibt da> nämlich "FTW = f * k" stehen, und das ist eine Laufzeitmultiplikation.
Weil
1
(frequency*4294967296)/125000000UL;
nicht immer das gleiche Ergebnis wie
1
frequency*(4294967296/125000000UL);
liefert.
Die Idee von Uwe ist, die 64 Bit Operationen durch 32 Bit Operationen zu
ersetzen.
Hallo S. R.,
an wen ist der Kommentar gerichtet ?
S. R. schrieb:> Uwe S. schrieb:>> hier ist noch ein anderer Ansatz für die Berechnung des FTW eines AD8950>> mit FCLK = 125MHz konstant !.>>>> FTW = (34 +1/2 -1/7 +1/385 -1/528.613) * f ; f in Hz.>>>> D.h. man benötigt 1 Multiplikation, 4 Divisionen, 2 Additionen und 2>> Subtraktionen>> Das ist großer Unfug. Du ersetzt jetzt nämlich eine Laufzeitoperation> ("Konstante einsetzen") durch 9x Arithmetik, plus Registerbenutzung und> so weiter. Die Division alleine wird so viel Code erzeugen, dass der> Flash wieder platzt; die Rechenzeit ignoriere ich dabei mal gepflegt.>> Zumal ich nicht verstehe, warum du 2^32/FCLK unbedingt zur Laufzeit> ausrechnen willst. Das kann der Compiler auch selbst. Dann bleibt da> nämlich "FTW = f * k" stehen, und das ist eine Laufzeitmultiplikation.
Wenn ich gemeint bin, dann zeige bitte genau, wie Du die Berechnung des
FTW ohne Nutzung der 64Bit Arithmetik machen möchtest.
Bei mir belegt die Funktion uint32_t ftw_ad9850(uint32_t freq); 380Byte
Code im Flash.
Uwe S. schrieb:> Bei mir belegt die Funktion uint32_t ftw_ad9850(uint32_t freq); 380Byte> Code im Flash.
Ich habe es nachvollzogen. Die Rechnung mit Approximation und mehrfacher
Division in uint32_t ist deutlich kompakter als die Rechnung der
Multiplikation und Division mit long long (uint64_t). Grund ist
hauptsächlich dass bei der 64 bit Rechnung grössere Divisionsroutinen
aus der AVR Lib (ich nenne es mal so) mit eingebunden werden müssen.
Frickelfritze schrieb:> dass bei der 64 bit Rechnung grössere Divisionsroutinen> aus der AVR Lib (ich nenne es mal so) mit eingebunden werden müssen.
Wenn allerdings bei grösseren Programmen der (64-bit-)Code aus der
Lib sowieso vorhanden ist, ist der Grössen-Vorteil dahin.
Frickelfritze schrieb:> Ich habe es nachvollzogen.
Mit welcher Compilerversion? (Hatte ich oben den TE schon mal gefragt.)
Ich habe so in Erinnerung, dass Johann da in letzter Zeit mal was
getan hatte an den Bibliotheksroutinen.
Ggf. stellst du ja einfach deinen (compilierfähigen) Code mal allen
zur Verfügung, damit das jeder selbst testen kann?
Frickelfritze schrieb:> GCC vX.xx --> AVR Studio 4.18
Ach du Sch***e! Der ist ja älter als die Braunkohle. ;-)
Das taugt keineswegs als Beweis, inzwischen hat sich die Welt mächtig
weitergedreht.
Hallo Miteinander,
hier ist noch ein weiterer Ansatz, aber von der Codegröße schlechter,
für die Berechnung des FTW für einen AD9850 mit Fclk = 125MHz.
Diesen "findet" man unter Betrachtung der Wertigkeiten der
Nachkommastellen von: k = 2³²/125MHz ~ 34,359738368
Jörg W. schrieb:> Das taugt keineswegs als Beweis, .....
Ich hatte nicht den Anspruch, einen Beweis anzutreten, das wird mir
von dir einfach so angedichtet. Ich wollte lediglich wissen was los
ist und habe die Breechnung nachvollzogen.
Jörg W. schrieb:> Ggf. stellst du ja einfach deinen (compilierfähigen) Code mal allen> zur Verfügung, damit das jeder selbst testen kann?
Hier mal meine Verifikation ..... your mileage may vary.
Vielleicht findet jemand noch Fehler oder Verbesserungsmöglichkeiten.
Einfach wechselweise die beiden #if 0 und #if 1 togglen um
beide Codeversionen zu erzeugen. Bin gespannt was bei neueren
Compiler-Versionen rauskommt.
Interessant wäre noch (für Binärcode-Kenner) warum die 64 Bit Version
zusätzlich viel RAM braucht.
Frickelfritze schrieb:> Ich hatte nicht den Anspruch, einen Beweis anzutreten, das wird mir> von dir einfach so angedichtet.
Sorry, ich wollte dir nichts "andichten".
Danke für den Code. Hab's mal in #ifdef USE_64 / #else umgeschrieben,
damit kann man das direkt auf der Kommandozeile testen, ohne die Datei
nochmal zu editieren.
Hier das Ergebnis für einen GCC 4.8.3 (auch schon nicht mehr der
Jüngste):
Hier ist die 64-bit-Rechnung also deutlich platzsparender als der
Workaround.
Frickelfritze schrieb:> Interessant wäre noch (für Binärcode-Kenner) warum die 64 Bit Version> zusätzlich viel RAM braucht.
Das müsstest du an deinem Compilat untersuchen. Ich erinnere mich
dunkel, dass da mal irgendwas war, wo die libgcc ein 256 Byte großes
Array angelegt hat, um irgendwelche Bitsucherei zu beschleunigen.
Möglicherweise ist das ja bei dir der Fall.
.... hmmmm ... schon interessant, aber ich habe nicht die Kenntnis
und Erfahrung um aus dem Binary schlau zu werden.
Ich poste hiermit mal die für die obige Source erzeugten *.lss und *.map
files für die 64 bit Version. Vielleicht kann sich jemand einen Reim
daraus machen. Es fällt auf dass viel Code dazugelinkt wird, warum auch
immer. Und es scheinen "pauschal" 256 Byte RAM allokiert zu werden.
Aus der Menge an Variablen nicht zu erklären ....
Wie bereits angedeuted ist das mit Studio 4.18 erzeugt worden. Ist
ja aber in den Files auch ersichtlich.
Frank M. schrieb:> Nimmt der TO eine aktuellere Toolchain, wird sich sein Problem nach ein> paar Handgriffen wahrscheinlich schon in Luft auflösen.
Also ein klassischer Fall von "Problem an der falschen Stelle gelöst"?
Frickelfritze schrieb:> Wie bereits angedeuted ist das mit Studio 4.18 erzeugt worden.
Nachtrag: und für einen Mega88 übersetzt ... ob das eine Rolle spielt?
Posten des ELF-Files wäre hilfreicher gewesen: Disassemblieren kann es
sich jeder selbt, aber man könnte dann auch schnell einen Blick auf die
Symboltabelle werfen.
Ja, der „Störenfried“ ist dieser da:
Wenn du mal nach AVR-GCC __clz_tab gugelst, findest du das Problem
seinerzeit sicher hinreichend diskutiert.
Aber vielleicht wären ja solche Artefakte (zusammen mit der ja offenbar
insgesamt deutlich mieseren Optimierung) endlich mal ein Anlass für
einen Upgrade der Toolchain? Du kannst ja auch eine aktuelle Toolchain
unter das olle Studio schieben, allerdings musst du dann dem Compiler
mit -gstrict-dwarf sagen, dass er keine erweiterten Debug-Infos erzeugt,
denn diese lassen den handgefrickelten DWARF-Parser dieses alten Studios
abstürzen (nicht, weil der Compiler buggy ist, sondern weil der Parser
nicht nach dem DWARF-Standard geschrieben worden war, sondern offenbar
per trial&error auf das maßgeschneidert, was der damalige Compiler so
rausgeworfen hat).
> Nachtrag: und für einen Mega88 übersetzt ... ob das eine Rolle spielt?
Ja, denn der hat einen Hardwaremultiplizierer, den der vom TE
genutzte ATtiny2313 nicht hat. Damit würde das Compilat also dann
nochmal größer werden.
Danke für die Hinweise.
Jörg W. schrieb:> Aber vielleicht wären ja solche Artefakte (zusammen mit der ja offenbar> insgesamt deutlich mieseren Optimierung) endlich mal ein Anlass für> einen Upgrade der Toolchain?
Ja, damit hadere ich schon die ganze Zeit, die Lösung mit aktuellerem
GCC und Studio 4.18 wäre eine gute Alternative um das überladene,
resourcenfressende Studio 7.xx zu umgehen. Zumal die Durchsichtigkeit,
Übersichtlichkeit eines Projekts in der neuen Umgebung nicht besser
wird. Mal sehen was ich mir dabei noch für Nebeneffekte einhandele.
Übrigens der Unterschied zwischen Mega88 und Tiny2313 in der Grösse
des Codes ist nur minimal.... abgesehen davon dass die 4K nicht in
einen Tiny2313 passen würden ....