Hallo zusammen,
ich will Daten zwischen zwei ISRs austauschen. Bislang war es mir immer
gelungen, die Daten so zu erzeugen, daß ich einen atomaren Zugriff
sicherstellen konnte, oder erlaubt, die ISRs lange genug
zurückzustellen.
Jetzt muß ich zum ersten Mal eine "echte" Mutex implementieren.
Mein naiver Ansatz bislang:
Daß die langsame Funktion in ihrem Verlauf mit total veralteten Daten
der schnellen Funktion konfrontiert wird, ist normal und ich kann damit
leben. Nur die Tupel s, v, a müssen immer konsistent zusammenpassen,
ansonsten passieren schlimme Dinge.
Funktioniert das so, oder habe ich irgendeine mögliche race condition
nicht bedacht?
Viele Grüße
W.T.
> Funktioniert das so
Ich denke, das wird funktionieren. Ich würde aber (wenn es die CPU
Performance erlaubt) trotz der Prioritäten in beiden ISR prüfen, ob der
Lock schon gesetzt ist.
Wenn dein Code unabhängig von der Rangfolge der Prio ist, wird es viel
leichter, als Kopiervorlage für neue Projekte zu dienen.
ist nicht zwangsweise atomar. Sofern du in reinem C bleiben willst
empfehle ich dir eine Mutex Implementierung für deine spezifische
Platform zu suchen!
Ansonsten bietet sich eventuell C++ an, dass je nach Platform bereits
ein Mutex hat. Und wenn nicht, dann mit hoher Wahrscheinlichkeit
zumindest std::atomic, womit man sich recht einfach selbst ein Mutex
basteln kann.
Vincent H. schrieb:> ist nicht zwangsweise atomar. Sofern du in reinem C bleiben willst> empfehle ich dir eine Mutex Implementierung für deine spezifische> Platform zu suchen!
Klar. Ne Mutex im Interrupt-Handler. Atomic ist ok, hier aber unnötig.
Wichtig ist nur, dass die Reihenfolge der Operationen nicht umoptimiert
wird. Aber dafür sollten die volatiles sorgen.
Vincent H. schrieb:> ist nicht zwangsweise atomar.
In C11 wohl schon. Und Plattformen, auf denen es vorher nicht atomar
war, sind mittlerweile arg selten.
MaWin schrieb:> Warum sperrst du die interrupts nicht, statt dieses handgestrickte Mutex> zu verwenden?
In einem niedrig priorisierten Interrupt während einer Funktion mit dem
bezeichnenden Namen doSomeComplicatedStuff einen 1MHz Interrupt sperren?
A. K. schrieb:> In einem niedrig priorisierten Interrupt während einer Funktion mit dem> bezeichnenden Namen doSomeComplicatedStuff einen 1MHz Interrupt sperren?
Nein. Nur während des Schreibens und Lesens der 3 Werte.
A. K. schrieb:>> Warum sperrst du die interrupts nicht, statt dieses handgestrickte Mutex>> zu verwenden?>> In einem niedrig priorisierten Interrupt während einer Funktion mit dem> bezeichnenden Namen doSomeComplicatedStuff einen 1MHz Interrupt sperren?
Der hier gezeigte Code ist ja offensichtlich nicht der echte Code. Wenn
im 1MHz Interrupt wirklich nichts sonst passiert, dann wärs natürlich
ein Weg.
Walter T. schrieb:> Bei einer globalen Variable? Woher? Gibt es dazu Material?
Die erste Generation der DEC Alphas konnte nicht direkt auf Bytes
zugreifen. Wenn da "bool" ein Byte sein sollte, dann läufts technisch
auf getrennte
load
merge
store
Befehle raus, und das ist tatsächlich nicht atomar. Ist aber
mittlerweile recht exotisch. Vielleicht gibts DSPs mit dieser
Eigenschaft, aber da wird dann "bool" wohl kein Byte sein.
A. K. schrieb:> Vincent H. schrieb:>> ist nicht zwangsweise atomar. Sofern du in reinem C bleiben willst>> empfehle ich dir eine Mutex Implementierung für deine spezifische>> Platform zu suchen!>> Klar. Ne Mutex im Interrupt-Handler. Atomic ist ok, hier aber unnötig.> Wichtig ist nur, dass die Reihenfolge der Operationen nicht umoptimiert> wird. Aber dafür sollten die volatiles sorgen.
Sagt ja keiner was von blockend...
Wenn man
- lock in changed umbenennt,
- der Leser dieses vor dem Lesen auf false setzt (1Byte;μC, Single
Core),
- er dann seine Kopie zieht,
- danach das changed-Flag kontrolliert und ggf. nochmal kopiert,
- der Schreiber (höher Prio ISR, Leser sieht Schreiber's Zugriffe
atomar)
setzt das changed-Flag, sobald er geschrieben hat.
Nennt sich "lock-free", behindert den Schnelleren nicht und macht dem
Langsameren nur minimalen Zusatzaufwand.
Walter T. schrieb:> Vincent H. schrieb:>> Act.lock = true;>>>> ist nicht zwangsweise atomar.>> Bei einer globalen Variable? Woher? Gibt es dazu Material?
Die Variable wird im RAM des Controller abgelegt und von beiden ISRs
verwendet. Selbst wenn der Zugriff sofort passiert (volatile), so dauert
die Prozedur je nach prozessor mehrere Takte und umfasst verschiedene
Befehle.
Diese Mutex Implementierung sollte funktionieren, da hier nur von einer
Seite aus geschrieben wird. Das volatile stellt sicher, dass die
Variable im RAM sicher geschrieben ist, bevor der kritische code
ausgeführt wird.
Bei einem Lock, auf das von mehreren stellen aus zugegriffen wird, ist
das Ganze schwieriger und meist sehr architekturabhängig zu lösen.
Die Namen "ISR_1MHz_Prio0" und "ISR_10_kHz_Prio1" sind nicht komplett
zufällig gewählt.
Normalerweise bin ich niemand, dem es auf den letzten Taktzyklus
ankommt, aber diesemal will ich den Overhead insbesondere in der ersten
Funktion minimal halten. Jeden der theoretisch möglichen 72 Taktzyklen,
den diese ISR nicht benötigt, kann ich an anderer Stelle gut gebrauchen.
(Andererseits ist die Last momentan noch niedrig genug, die ISR nicht in
Assembler schreiben zu müssen.)
Was mir auffällt: Die Diskussion dreht sich nur um den lock-Mechanismus
in "Act". Heißt das, daß im Doppelpuffer-Mechanismus in "Set" kein
Problem auftreten kann?
Carl D. schrieb:> Nennt sich "lock-free", behindert den Schnelleren nicht und macht dem> Langsameren nur minimalen Zusatzaufwand.
Sehe ich das richtig, daß man damit einen langsamen Task, der nur sehr
spärlich Zyklen abbekommt, komplett in den Stillstand bremsen kann?
Walter T. schrieb:> Heißt das, daß im Doppelpuffer-Mechanismus in "Set" kein> Problem auftreten kann?
Nö. Sondern dass du nicht deutlich genug gemacht hast, dass es dir auch
um diesen Aspekt geht. ;-)
Walter T. schrieb:> Normalerweise bin ich niemand, dem es auf den letzten Taktzyklus> ankommt, aber diesemal will ich den Overhead insbesondere in der ersten> Funktion minimal halten. Jeden der theoretisch möglichen 72 Taktzyklen,
Eine ganz normale Interruptsperre verbraucht vermutlich nicht mehr
Taktzyklen als das lesen des Locks und der bedingte Sprung.
Walter T. schrieb:> Carl D. schrieb:>> Nennt sich "lock-free", behindert den Schnelleren nicht und macht dem>> Langsameren nur minimalen Zusatzaufwand.>> Sehe ich das richtig, daß man damit einen langsamen Task, der nur sehr> spärlich Zyklen abbekommt, komplett in den Stillstand bremsen kann?
Ich gehe mal davon aus, daß du gerade Grundlagenforschung betreibst und
nicht wirklich jede μs neue Daten per ISR in den Speicher legen willst
und erwartest, daß da noch viel Zeit für andere ISR's übrig bleibt. Mein
Vorschlag ist eher geeignet die schnelle ISR nicht sperren zu müssen, um
in der Langsamen/Mainloop konsistente Daten zu lesen.
Walter T. schrieb:> Die Namen "ISR_1MHz_Prio0" und "ISR_10_kHz_Prio1" sind nicht> komplett> zufällig gewählt.>
...
>> Sehe ich das richtig, daß man damit einen langsamen Task, der nur sehr> spärlich Zyklen abbekommt, komplett in den Stillstand bremsen kann?
Deine Namensgebung ist verwirrend. Gegenseitiger Ausschluss in ISRs ist
etwas komplett Anderes als zwischen Tasks, in sofern ist der Begriff
Mutex i.Z. mit ISRs unglücklich, weil der Begriff Mutex überall so
verwendet wird, dass die beteiligten Handlungsstränge (= Tasks)
suspendiert werden können. Das ist bei ISRs selbstverständlich ein
absolutes Nono.
Ich gehe jetzt mal davon aus, dass es bei Dir nur um ISRs geht (deinen
letzten Posts nach zu urteilen hast Du kein OS). Dann tritt ein
asymmetrischer Mechanismus in Kraft: Der höher priorisierte ISR ist per
Definition nicht durch einen nieder priorisierten ISR unterbrechbar,
d.h. alle Operationen, die während des höher priorisierten ISR ohne
Verlassen nacheinander ausgeführt werden, sind immer "atomisch"
(jedenfalls relativ zum niederer priorisierten).
Umgekehrt ist die einzige Möglichkeit für den nieder priorisierten ISR,
eine Sequenz "atomisch" gegenüber dem höher priorisierten auszuführen,
den ISR zu sperren (ausser Du kannst Dich darauf verlassen, dass die
Instruktionen controllerseitig ununterbrechbar sind. Sich auf sowas wie
den Exclusive Access Monitor zu verlassen, ist bei ISRs übrigens eine
schlechte Idee).
Was genau macht doSomeSimpleStuff(&s, &v, &a) mit den Parametern? Nicht
etwas busy waiting oder andere in ISRs verbotenen Operationen?
MaWin schrieb:> Eine ganz normale Interruptsperre verbraucht vermutlich nicht mehr> Taktzyklen als das lesen des Locks und der bedingte Sprung.
Der schnelle Interrupt darf nie gesperrt werden in diesem
Anwendungsfall und der langsame Interrupt braucht nicht gesperrt zu
werden, weil er die schnelle ISR nicht unterbricht.
> Die Diskussion dreht sich nur um den lock-Mechanismus in "Act".
Weil das die Einzige Stelle ist, wo du überhaupt so etwas ähnliches wie
ein Locking eingebaut hast. Dein "Set" Teil braucht sicher auch noch
eine Anpassung. Aber eins nach dem anderen ist besser.
> Sehe ich das richtig, daß man damit einen langsamen Task, der nur sehr> spärlich Zyklen abbekommt, komplett in den Stillstand bremsen kann?
Ja.
1Mhz Interruptrate ist schon extrem hoch, das riecht schon nach einem
falschen Lösungsansatz. Aber darüber können nichts sagen, weil dein
Anwendungsfall ja völlig unbekannt ist.
Walter T. schrieb:> Der schnelle Interrupt darf nie gesperrt werden in diesem
Und warum nicht? Es ist ja offensichtlich auch Ok, wenn Daten verworfen
werden, solange dein Mutex gelockt ist.
MaWin schrieb:> A. K. schrieb:>> In einem niedrig priorisierten Interrupt während einer Funktion mit dem>> bezeichnenden Namen doSomeComplicatedStuff einen 1MHz Interrupt sperren?>> Nein. Nur während des Schreibens und Lesens der 3 Werte.
So ist es. Das Sperren erfolgt ja nur für die 3 LOAD-Befehle, ist also
komplett zu vernachlässigen. Dein doSomeComplicatedStuff() wird bestimmt
deutlich länger als 3 CPU-Takte brauchen.
Nur mal so nebenbei...
Sind die Taktquellen für die zwei Interrupts verschieden? (Ändert sich
der Zeitpunkt des Aufrufs dadurch geringfügig/ zufällig?) -> die
10kHz-ISR nur ein Flag setzen lassen und die Berechnungen in der
1MHz-ISR ausführen.
RAc schrieb:> oder andere in ISRs verbotenen Operationen?
Ich mache keine verbotenen Sachen (weder in der ISR noch sonst), und die
Taktrate der schnellen ISR ist ungewöhnlich hoch, aber das mit sehr
gutem Grund. Die ISR ersetzt mir eine externe Logik. Dafür ist es
notwendig, den Jitter in engen Grenzen zu halten.
MaWin schrieb:> Walter T. schrieb:>> Der schnelle Interrupt darf nie gesperrt werden in diesem>> Und warum nicht?
Ist so. Unverhandelbar. Word of God.
Ralf G. schrieb:> Sind die Taktquellen für die zwei Interrupts verschieden? (Ändert sich> der Zeitpunkt des Aufrufs dadurch geringfügig/ zufällig?) -> die> 10kHz-ISR nur ein Flag setzen lassen und die Berechnungen in der> 1MHz-ISR ausführen.
Nein, die Interruptquellen sind nicht 100% getrennt. Die 1-MHz-ISR ruft
die 10-kHz-ISR alle 100 Aufrufe auf, die dann aufgrund der IRQ-Priorität
hintenangestellt wird. Die 10-kHz-ISR macht dann alle Berechnungen, die
für die 1-MHz-ISR zu lange dauern. Die 10-kHz-ISR macht nur einfachen
Kram.
A. K. schrieb:> Nö. Sondern dass du nicht deutlich genug gemacht hast, dass es dir auch> um diesen Aspekt geht. ;-)
Also, noch einmal zur Klarstellung:
Ich will zwischen der schnellen und der langsamen ISR insgesamt jeweils
12 Bytes in beide Richtungen austauschen, die zusammenpassen und nicht
den Stand unterschiedlicher Zeitpunkte widerspiegeln. Mehr nicht.
Erwartet hätte ich Antworten der Art: "Das kann schiefgehen, wenn ...".
Davon habe ich nur eine bekommen, nämlich daß evtl. eine boolesches
Variable nicht unbedingt atomar gelesen und geschrieben werden kann. Das
werde ich überprüfen. Der ARM bietet für genau den Zweck Bitbanding an,
aber bislang schien mir das nur nötig, wenn ich aus einem Bitfeld
einzelne Bits setzen oder löschen will. Außerdem wurden ja auch schon
die entsprechenden Funktionen des GCC vorgeschlagen. Momentan gehe ich
davon aus, daß eine bool klammheimlich ein uint32 ist. Das kann ich
später problemlos mit dem Debugger überprüfen.
Die anderen Bedenken gingen in die Richtung "das geht anders aber
schöner".
Daraus schließe ich erst einmal, daß meine "unschöne" Lösung
grundsätzlich funktioniert.
Danke für die Diskussion.
MaWin schrieb:> Gehst du mit deinen Kollegen auch so um? Dann bist du wohl sehr beliebt.
Nur mit denen, die am Ende einer Diskussion, bei der sie nichts
mitbekommen haben, aber viel zu sagen haben, noch stundenlang
nachquengeln wollen. Macht mich bei den anderen Kollegen durchaus nicht
unbeliebter.
Walter T. schrieb:> Nur mit denen, die am Ende einer Diskussion, bei der sie nichts> mitbekommen haben, aber viel zu sagen haben, noch stundenlang> nachquengeln wollen.
Und was hat das jetzt mit diesem Thread zu tun?
Walter T. schrieb:> Nein, die Interruptquellen sind nicht 100% getrennt. Die 1-MHz-ISR ruft> die 10-kHz-ISR alle 100 Aufrufe auf, die dann aufgrund der IRQ-Priorität> hintenangestellt wird.
Nun, dann sind sie doch synchron und Du brauchst garnichts zu sperren.
Die 1MHz-ISR muß nur soviel Luft haben, daß die ersten 3 Load-Befehle
der 10kHz-Routine erfolgen, bevor die 1MHz-ISR erneut triggert.
Walter T. schrieb:> Ist so. Unverhandelbar. Word of God.
Jeder, der schon etwas länger programmiert weiß, daß absolut garnichts
in Stein gemeißelt ist. Es gibt immer mehrere Lösungen für eine Aufgabe.
Und oft erreicht man viel effektivere Lösungen, wenn man mal über den
eigenen Tellerrand hinausschaut.
Peter D. schrieb:> Jeder, der schon etwas länger programmiert weiß, daß absolut garnichts> in Stein gemeißelt ist. Es gibt immer mehrere Lösungen für eine Aufgabe.> Und oft erreicht man viel effektivere Lösungen, wenn man mal über den> eigenen Tellerrand hinausschaut.
Erkenntnis ist hier nicht gewünscht.
Es ist nur gewünscht sich das "OK" für die existierende Lösung zu holen.
Alternative Vorschläge werden belächelt oder schnippisch beantwortet.
Ich bin froh, nicht der Kollege des OP zu sein.
Hi
Wenn die 1MHz-ISR die 10kHz-ISR laufend stört, dann wird vom
Haupt-Programm wohl auch nicht wirklich was abgearbeitet.
Eigentlich sollte die 10kHz-ISR mit der Arbeit fertig sein, bevor der
neue Start-Befehl kommt - somit wäre am Anfang der 10kHz-Routiene 'genug
Zeit', um die Variablen abzuspeichern, bevor der nächste 1MHz-Hammer
geschlagen wird.
Vll. bekommt man so nur die Daten der letzten Berechnung gespeichert,
aber vll. reicht Das ja schon.
Die Daten der jetzigen Berechnung gibt's dann beim nächsten Aufruf.
MfG
Peter D. schrieb:> Nun, dann sind sie doch synchron und Du brauchst garnichts zu sperren.> Die 1MHz-ISR muß nur soviel Luft haben, daß die ersten 3 Load-Befehle> der 10kHz-Routine erfolgen, bevor die 1MHz-ISR erneut triggert.
Also eine memory barrier einziehen, daß die volatile-Variablen auch
wirklich am Anfang ausgelesen werden, und nicht vom Compiler das
Einlesen lazy nach hinten geschoben wird?
Irgendwie widerstrebt mir das ein wenig, mich auf das Timing zu
verlassen, eben auch in Anbetracht der Tatsache, daß die Triggerung der
ISRs momentan noch nicht in Stein gemeißelt ist.
Aber es ist ein guter Denkanstoß.
Peter D. schrieb:> Jeder, der schon etwas länger programmiert weiß, daß absolut garnichts> in Stein gemeißelt ist. Es gibt immer mehrere Lösungen für eine Aufgabe.
Genau. Und wenn ein Teil dieser Lösung (Sperren der Interrupts und
Variablen so schachteln, daß sie in einem atomaren Zugriff geschrieben
oder gelesen werden können) ausreichend genau betrachtet wurde, muß man
auch mal dazu übergehen, die anderen Lösungen zu betrachtet, und nicht
immer wieder dieses alte Thema aus der Versenkung holen.
Patrick J. schrieb:> Eigentlich sollte die 10kHz-ISR mit der Arbeit fertig sein, bevor der> neue Start-Befehl kommt
Das ist kein Problem. Die 10-kHz-ISR ist laaaaaaaange fertig, bevor sie
das nächste Mal wieder aufgerufen wird. Sie braucht knapp 4 µs.
Walter T. schrieb:> Peter D. schrieb:>> Nun, dann sind sie doch synchron und Du brauchst garnichts zu sperren.>> Die 1MHz-ISR muß nur soviel Luft haben, daß die ersten 3 Load-Befehle>> der 10kHz-Routine erfolgen, bevor die 1MHz-ISR erneut triggert.>> Also eine memory barrier einziehen, daß die volatile-Variablen auch> wirklich am Anfang ausgelesen werden, und nicht vom Compiler das> Einlesen lazy nach hinten geschoben wird?>
Vorsicht, das Einziehen einer Barriere (ich vermute mal, dass wir hier
mit der Nomenklatur des Cortex arbeiten, also das Einfügen einer DMB
oder DSB Instruktion) ändern m.W. nach nichts am vom Compiler
generierten Code, sie bewirken nur zur Laufzeit einen Abschluss aller
asynchronen Transfers vor dem nächsten execution Zyklus (was natürlich
im Kontext der Diskussion auch wichtig ist).
Oder was genau meinst Du mit Memory Barrier?
Ruediger A. schrieb:> Oder was genau meinst Du mit Memory Barrier?
die ISRs sehen natürlich tatsächlich nicht exakt wie in im obigen
Beispiel aus. Um genau zu sein: Sehen sie schon, nur die Funktion
doSomeComplicatedStuff ist keine Funktion, sondern ein längeres Stück
Quelltext (das ich nicht posten werde, weil es noch nicht fertig ist).
Näherungsweise reicht aber das hier:
1
voidISR_10_kHz_Prio1(void)
2
{
3
4
uint32_ts=Act.s;
5
int32_tv=Act.v;
6
int32_ta=Act.a;
7
8
9
f1(&s);// dauert
10
f2(&s,&v);// dauert auch
11
12
f3(&a);// <<< Bis hierhin kann der Compiler das Lesen von a verzögern.
13
14
uint_fast8_ti=Set.idx;
15
i=1-i;
16
Set.s[i]=s;
17
Set.v[i]=v;
18
Set.a[i]=a;
19
Set.idx=i;
20
}
Wäre die ISR so einfach, könnte ich natürlich f3(&a) an den Anfang
setzen. Dann müßten aufgrund volatile alle s,v,a gelesen sein.
Aber das sieht nach einer Lösung aus, die mir irgendwann auch wieder auf
die Füße fallen kann, wenn irgendetwas geändert wurde.
Walter T. schrieb:> Die 10-kHz-ISR ist laaaaaaaange fertig, bevor sie> das nächste Mal wieder aufgerufen wird. Sie braucht knapp 4 µs.
Netto oder Brutto ;-) Denn:
Wird die 10kHz-ISR jetzt 4x unterbrochen oder wird die 1MHz-ISR 4x
verpasst?
Ralf G. schrieb:> Netto oder Brutto
Wie auch immer das definiert ist.
Es sind knapp 4 µs, d.h. die langsame ISR wird dreimal unterbrochen.
An der Stelle ist es eine wirklich gemütliche Angelegenheit - aber die
Last wird vermutlich sogar noch etwas sinken.
> Es sind knapp 4 µs, d.h. die langsame ISR wird dreimal unterbrochen.
Was natuerlich Bullshit ist. Voellig falsches Konzept. Man splittert die
10kHz ISR in mehrere Bloecke, die man mit einer Zustandsmaschine
abspult.
Sapperlot W. schrieb:> Man splittert die 10kHz ISR
Kann man das nicht als ganz normale Funktion in der 'main' alle 100µs
anschubsen? Dann sind auch alle anderen Berechnungen ringsherum in einem
definierten Zustand.
Sapperlot W. schrieb:> Was natuerlich Bullshit ist. Voellig falsches Konzept. Man splittert die> 10kHz ISR in mehrere Bloecke, die man mit einer Zustandsmaschine> abspult.
OK, dann zeig mal.
Die Haupt-Zeit geht in diesen paar Zeilen verloren:
1
assert(i>=1);
2
if(i<4096){
3
// Fix 22:10, davon nur signifikant 6:10
4
//uint32_t fac = (sqrt(i<<20UL)-sqrt((i-1)<<20UL));
Also.
const uint8 reloadloop =100;
volatile uint8 : loopcount = reloadloop;
volatile uint8 : zsm =0xff;
interrupt 1MHz {
.. mach was ...
loopcount--;
if (loopcount ==0) {
loopcount = reloadloop;
zsm =0;
}
}
main () {
loop forever {
if zsm !=0xff {
switch zsm {
0: //hier zustand 0 einfuellen !!
zsm =1;
break;
1: //hier zustand 1 einfuellen !!
zsm =2;
break;
2: //hier zustand 2 einfuellen !!
zsm =3;
break;
3: //hier zustand 3 einfuellen !!
zsm =4;
break;
4: //hier zustand 4 einfuellen !!
zsm =5;
break;
5: //hier zustand 5 einfuellen !!
zsm =0xff;
break;
} // switch
}
.. weiter mit main ..
..
..
} // loop forever
}
Dann schaut man sich die Mathematik etwas an. Was denn wirklich noetig
ist.
uint32_t fac = (sqrt32(i<<20UL)-sqrt32((i-1)<<20UL));
Also i <= 1 < 4096 , dann haenge ich noch 20 Nullen an und ziehe die
Wurzel. Das bedeutet ich kann mir den Assert auf 65536 danach sparen.
.. man muesste mehr davon wissen.
Der assert() ist ohnehin nur Füllmaterial fürs Debugging.
Und wie teilst Du jetzt die Wurzel auf, deren Berechnung allein mehr als
1 µs kostet?
Ich mache es kurz: Ich halte es für deutlich praktischer, die
Fähigkeiten einer Nested-Interrupt-MCU zu nutzen, als krampfhaft
irgendwelche Rechnungen in Stücke zu brechen, wobei ich bei jedem Stück
einzeln gucken muß, ob es die Zeitbeschränkung reißt.
Ich habe das Gefuehl, die Wurzel ist vereinfachbar. Stark vereinfachbar.
Weil der Faktor 2^20, resp 2^16 abtrennbar ist. Da kann man sich einige
Zyklen sparen.
Wenn man so eine Berechnung im Main macht, darf sie auch ohne drueber
nachzudenken unterbrochen werden.
Was soll die Berechnung denn machen ? Allenfalls kann man sie ersetzen
durch etwas einfacheres
Sapperlot W. schrieb:> Was soll die Berechnung denn machen ?
Ich orientiere mich jetzt einfach mal ein Deinem Nutzernamen: "Jetzt
nicht." Wenn mein Projekt so weit ist, werde ich es, wenn es nicht
irgendwo scheitert, wahrscheinlich Open Source machen.
Das Skalieren der Wurzel hat den Grund, daß ich ekligerweise an einer
Stelle in der Rechnung sqrt(i)-sqrt(i-1) machen muß, d.h. ich will und
muß Rundungsfehler minimieren.
Andererseits ist die Sache auch noch nicht so weit gediehen, daß es
schon sinnvoll wäre, viel Zeit in die Optimierung (Reihenentwicklung,
lookuptable etc.) zu stecken. Unter Umständen wird die Wurzel-Funktion
wieder für immer verschwinden.
Meine Lösung also: Das, was schnell sein muß, in eine schnelle ISR, das,
was langsam sein darf, in eine langsame ISR. Die MCU kümmert sich um den
ganzen Rest, ich kann mir "sinnvolle Aufteilung" und Optimierungen erst
einmal sparen und mich mit den Kernproblemen beschäftigen. Die sind
kompliziert genug.
Einzige Mehrkosten für mich: Eine ISR und ich muß dann eben 12 Byte
konsistent zwischen den beiden Routinen austauschen. Den Preis bin ich
problemlos bereit zu zahlen.