Guten Abend zusammen, ich habe eine ISR - SysTick_Handler(), die ich vor dem ersten Interrupt ein paar (hundert) mal aus main() aufrufen will, damit sich beim ersten Interrupt die internen Zustände soweit stabilisiert haben. Auf den ersten Blick sieht die ISR im Listfile nicht anders als andere void-void-Funktionen aus. Würde die Funktion sich selbst per IRQ unterbrechen, wäre das extrem ungewollt - deswegen werden alle Interrupts erst später freigegeben. Gibt es noch andere Nebenwirkungen, die ich berücksichtigen müßte?
Klingt für mich nach einer fragwürdigen Vorgehensweise - so nach dem Motto: Bevor ich mir jetzt überlege, wie die internen Zustände am Anfang aussehen, rufe ich lieber 'ne Funktion ein paar hundert mal auf... Was spricht dagegen, die internen Zustände über eine separate Funktion ordentlich zu initialisieren?
... oder den systick ein paar hundert mal ticken zu lassen, bevor die einheitliche Funktion freigegeben wird? Aber egal, das Ganze klingt tatsächlich sehr fragwürdig. Oliver
Gerhard schrieb: > Was spricht dagegen, die internen Zustände über eine separate Funktion > ordentlich zu initialisieren? Daß die internen Zustände intern sind und nirgendwo sonst benötigt werden. Gerhard schrieb: > Klingt für mich nach einer fragwürdigen Vorgehensweise Ich halte inkrementell-iterative Lösungsverfahren für eine valide Vorgehensweise. Tatsächlich ist mein Weg gerade der aus der Gegenrichtung: Ich hatte bis heute nachmittag eine Initialisierungfunktion mit grob zwei Bildschirmseiten Quelltext mit etlichen Fallunterscheidungen, die sich bei einem inkrementell-iterativen Lösungsverfahren in wenige Zeilen Wohlgefallen auflösten. Und netterweise ist die neue Initialisierung inhaltlich zu 100% schon im SysTick-Handler enthalten. Also gibt es momentan eine Funktion, die sowohl von main() als auch vom SysTick_Handler() aufgerufen wird. Geht auch. Aber es wirft trotzdem die Frage auf, ob man nicht einfach SysTick_Handler() direkt aufrufen kann, oder ob wichtige Gründe dagegen sprechen.
:
Bearbeitet durch User
Walter T. schrieb: > Also gibt es momentan eine Funktion, die sowohl von main() als auch vom > SysTick_Handler() aufgerufen wird. Geht auch. Kann aber auf dem AVR die ISR ziemlich ausbremsen. > Aber es wirft trotzdem die Frage auf, ob man nicht einfach > SysTick_Handler() direkt aufrufen kann, oder ob wichtige Gründe dagegen > sprechen. Eine ISR wird mit reti beendet, welches die Interrupts einschaltet.
Der Unterschied von normalen Routinen und ISR Routine ist Dir bekannt ?
Rolf M. schrieb: > Kann aber auf dem AVR die ISR ziemlich ausbremsen. Genau, und auf dem STM32 ist es auch nicht kostenlos. Rolf M. schrieb: > Eine ISR wird mit reti beendet, welches die Interrupts einschaltet. Genau diesen Unterschied scheint es hier nicht zu geben. Die ISRs benötigen keinerlei Attribute oder Makros. Wobei ich mich zu erinnern meine, dass es kein generelles Problem gab, Funktionen mit reti auch "normal" aufzurufen.
Walter T. schrieb: > Rolf M. schrieb: >> Kann aber auf dem AVR die ISR ziemlich ausbremsen. > > Genau, und auf dem STM32 ist es auch nicht kostenlos. Oh, ich hatte irgendwie AVR gelesen statt ARM. Hätte mir eigentlich spätestens bei SysTick_Handler() auffallen müssen, weil das STM32-typisch ist.
Walter T. schrieb: > Gibt es noch andere Nebenwirkungen, die ich berücksichtigen müßte? Nein, Du solltest Du den Systick nicht vorher aktivieren. Dazu mußt Du insbesondere schauen, daß der nicht schon im Startup vor main() eingerichtet wird. Aber ansonsten ist die ISR auf ARM eine völlig normale C-Funktion. Daß das vom Konzept her grober Pfusch ist und man das deswegen ganz grundsätzlich nicht so macht, wurde ja bereits erwähnt.
Generell fielen mir drei Wege ein, den Aufruf der ISR durch main() für den vorgesehenen Fall zu vermeiden. Zu denen habe ich aber keinerlei Fragen. Mich interessiert hauptsächlich, welche Gründe dagegensprechen (könnten), eine ISR aus main() aufzurufen. Rolf M. schrieb: > Oh, ich hatte irgendwie AVR gelesen statt ARM. Passiert ;-)
Nop schrieb: > Walter T. schrieb: > >> Gibt es noch andere Nebenwirkungen, die ich berücksichtigen müßte? > > Nein, Du solltest Du den Systick nicht vorher aktivieren. Dazu mußt Du > insbesondere schauen, daß der nicht schon im Startup vor main() > eingerichtet wird. Aber ansonsten ist die ISR auf ARM eine völlig > normale C-Funktion. Und wen interessiert, warum das funktioniert, kann das hier nachlesen: http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Babefdjc.html
Walter T. schrieb: > ich habe eine ISR - SysTick_Handler(), die ich vor dem ersten Interrupt > ein paar (hundert) mal aus main() aufrufen will, damit sich beim ersten > Interrupt die internen Zustände soweit stabilisiert haben. Das klingt wie eine blöde Idee <tm> Was soll sich da "stabilisieren"? Initialisiere deine static Variablen (das meinst du wohl mit "interne Zustände") einfach korrekt. Und fertig. Walter T. schrieb: > Ich halte inkrementell-iterative Lösungsverfahren für eine valide > Vorgehensweise. Ich nicht. Insbesondere bezweifle ich, daß das überhaupt eine Lösung darstellt. Woher nimmst du die Gewißheit, daß die "internen Zustände" nach 100 oder 1000 Aufrufen "stabilisiert" sind? Woher die Gewißheit, daß sie es vorher nicht sind? Als Programmierer mußt Du doch wissen, welche Zustände "stabil" sind. Also initialisiere das System einfach in einem solchen Zustand.
Jetzt mal unabhängig davon, ob das ein sinnvolles Verfahren ist. Stellt euch einfach vor, es gibt eine ganz normale winzige ISR und zweitens ein ganz normales Unterprogramm mit diesem speziellen Verfahren. Natürlich kann das Unterprogramm von main() aufgerufen werden. Es kann aber auch von der ISR aufgerufen werden. Das funktioniert doch auf jeden Fall? Wenn dann die ISR nur noch aus diesem Aufruf besteht, sollte der Compiler das bis auf Null Overhead optimieren. Evt. kann man ihm einen Tipp geben, mit inline oder so.
Guten Morgen, da waren jetzt einige hilfreiche Antworten, und einige Antworten, die es noch werden können. Nop schrieb: > Aber ansonsten ist die ISR auf ARM eine völlig > normale C-Funktion. John Doe schrieb: > > Und wen interessiert, warum das funktioniert, kann das hier nachlesen: > http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0552a/Babefdjc.html Danke. Axel S. schrieb: > Woher nimmst du die Gewißheit, daß die "internen Zustände" > nach 100 oder 1000 Aufrufen "stabilisiert" sind? Aus dem gleichen Grund, aus dem ich die Gewißheit nehme, daß sie es später im Betrieb auch sein werden. Stabilitäts- und Konvergenzkriterien sind doch keine Raketenwissenschaft. Vor allem bei FIR-Systemen. Axel S. schrieb: > Woher die Gewißheit, daß sie es vorher nicht sind? Das weiß ich tatsächlich nicht. Schlimmstenfalls vergeude ich also etliche hundert Aufrufe, obwohl das System schon im zweiten Schritt konvergiert ist. Es gibt aber schlimmeres. Das System startet so immer noch tausende Male schneller, als wenn ich erst etliche Male den SysTick-Interrupt abwarten müßte. Nop schrieb: > Daß das vom Konzept her grober Pfusch ist und man das deswegen ganz > grundsätzlich nicht so macht, wurde ja bereits erwähnt. Erwähnt ja, aber nicht begründet. Warum ist es Pfusch?
Walter T. schrieb: > Auf den ersten Blick sieht die ISR im Listfile nicht anders als andere > void-void-Funktionen aus. > ... > Gibt es noch andere Nebenwirkungen, die ich berücksichtigen müßte? Bei einem Interrupt wird zusätzlich zur Rücksprungadresse automatisch das Statusregister gesichert und mit der Rückkehr aus der Interrupt Routine wieder zurück gelesen. Das sollte auch aus dem Listfile hervor gehen - k.a. wie der bei dir aussieht.
Wolfgang schrieb: > sollte auch aus dem Listfile hervor gehen Nein, denn das macht der Prozessor automatisch, und dafür gibt es in der Software keine Instruktionen. Kannst du nicht die Werte, welche die internen Zustände nach dem "einschwingen" annehmen sollen, einfach direkt in die Initialisierung der jeweiligen Variablen schreiben? Dann wird das vom Startup-Code super effizient direkt aus dem Flash kopiert.
Walter T. schrieb: > Mich interessiert hauptsächlich, welche Gründe dagegensprechen > (könnten), eine ISR aus main() aufzurufen. Du doktorst damit an Symptomen herum, anstatt die Problemursache zu beheben. Mikrocontroller sind doch keine Motoren, die warm laufen müssen, bevor man sie belasten darf! Wenn du vor der Programmausführung einen bestimmten Zustand brauchst, dann stelle ihn gezielt her. Das ganze RAM und alle I/O Register sind direkt beschreibbar.
Walter T. schrieb: > Es gibt aber schlimmeres. Das System startet so immer > noch tausende Male schneller, als wenn ich erst etliche Male den > SysTick-Interrupt abwarten müßte. Eine ISR isr eine normale Funktion, die auch direkt aufgerufen werden kann. Allein auf Seiteneffekte sollte man aufpassen. Man kann auch den Systick auf 10 µs einstellen, damit die Funktion häufiger aufgerufen wird. Such Dir etwas aus.
Wolfgang schrieb: > Walter T. schrieb: >> Auf den ersten Blick sieht die ISR im Listfile nicht anders als andere >> void-void-Funktionen aus. >> ... >> Gibt es noch andere Nebenwirkungen, die ich berücksichtigen müßte? > > Bei einem Interrupt wird zusätzlich zur Rücksprungadresse automatisch > das Statusregister gesichert und mit der Rückkehr aus der Interrupt > Routine wieder zurück gelesen. > Das sollte auch aus dem Listfile hervor gehen - k.a. wie der bei dir > aussieht. Warum liest Du nicht einfach bei ARM - den Link habe ich oben gepostet - nach, wie es funktioniert? Dann würdest Du nicht so einen Unsinn schreiben.
Ich habe ein FIR-Filter n-ter Ordnung, das auf einen Sensorwert angewendet werden soll. Nehmen wir der Einfachheit an, es wäre der ADC. Wann ist das Filter eingeschwungen? Wenn in allen z bis z^{-n} der korrekte Startwert steckt. Dazu fallen mir spontan mehrere Lösungen ein: a) Mir das Filter genau anschauen, die n Werte global machen, eine Initialisierungsfunktion schreiben, die die n Startwerte korrekt für den ADC-Wert zum Initialisierungszeitpunkt ausrechnet und einträgt, und dann SysTick aktivieren. Bei FIR ist das kein Problem, nur Schreibarbeit. Bei IIR etwas mehr Schreibarbeit, aber auch kein generelles Problem. Allerdings ist hier schon allgemeinen Fall das Lösen eines Gleichungssystems nötig. b) SysTick_Handler() einfach n mal mit dem ADC-Wert der Initialisierung durchlaufen lassen und wissen, daß das Filter eingeschwungen sein wird. Bei IIR abweisend m mal, bis die Fehlerschranke unterschritten ist (m läßt sich vorberechnen). c...f) Andere Wege finden, daß die Funktion in SysTick_Handler() für n Schritte schneller ausgeführt wird als üblich. Zähler verkürzen, IRQs manuell setzen, Funktion indirekt aufrufen, indem sie einmal inline und einmal direkt aufrufbar etc. Welchen konkreten Vorteil haben jetzt a), c...f) gegenüber b), das letztes als "Pfusch" oder "herumdoktorn an den Symptomen" bezeichnet werden muß, während die anderen Lösung als "Behebung des Problems" gelten?
:
Bearbeitet durch User
Danke. Jetzt haben wir eine plausible Erklärung, warum du die ISR wiederholt aufrufen willst. Das es technisch geht, war eh schon klar (hoffe ich).
Ist es nicht beim FIR-Filter so, dass der sich lediglich die n letzten Eingangswerte merkt? In deinem initialen Lauf des Systick wirst du wohl davon ausgehen, dass die anfangs alle 0 oder auf einem fixen Wert sind. Kannst du nicht ganz schlicht das Array vorbelegen:
1 | int X [N] = {42, 42, 42, 42, ... }; |
Wenn "X" eine "static"-Variable es geht das genau so. Wenn es C++ und ein Klassen-Member ist entweder auch so, oder eine Meta-funktion schreiben die das in Abhängigkeit von "n" automatisch macht. Eine Initialisierungsfunktion ist hier eh unnötig. Beim Hochfahren die ISR wiederholt aufrufen ist hauptsächlich langsam und unelegant. Wenn du später in der ISR noch mehr Dinge tun möchtest gibt's ein Chaos...
Dr. Sommer schrieb: > Kannst du nicht ganz schlicht das Array vorbelegen: Klar. Das entspricht Variante a) aus meinem obigen Post. Dafür muß ich nur das Array global machen, damit es neben der ISR von einer Initialisierungs-Funktion beschrieben werden kann. Dr. Sommer schrieb: > Wenn du später in der ISR noch mehr Dinge tun möchtest > gibt's ein Chaos... Nur dann, wenn ich Dinge darin tun will, die vom exakten Zeitstempel abhängen. Aber stimmt: Ab diesem Punkt würde es unelegant. Meine Frage ist: Was ist an a) jetzt so viel besser als b), das diesen Mehraufwand rechtfertigt?
Walter T. schrieb: > Was ist an a) jetzt so viel besser als b), das diesen > Mehraufwand rechtfertigt? Es sieht ordentlicher aus, hat weniger den Character eines billigen Workarounds. Wenn dir die Lösung a besser gefällt, dann mach das halt. Nur darfst du dich dann auch nicht beklagen, wenn später jemand fragt "was hast du dir denn dabei gedacht?". Uns hast du es nachvollziehbar erklärt.
Walter T. schrieb: > Klar. Das entspricht Variante a) aus meinem obigen Post. Dafür muß ich > nur das Array global machen Ist es momentan "static" innerhalb der ISR? Auch dann kannst du es genau so in der ISR initialisieren. Der "Aufwand", es global zu machen, ist gar nicht nötig. Walter T. schrieb: > Meine Frage ist: Was ist an a) jetzt so viel besser als b), das diesen > Mehraufwand rechtfertigt? Es startet schneller, es ist eine korrekte Initialisierung im Sinne der Sprache, es entfällt für den Leser die Überraschung dass die ISR ohne Interrupt aufgerufen wird... Falls es sich um C++ handelt, kannst du folgendermaßen ein Array mit dem gleichen Wert vorbelegen:
1 | #include <utility> |
2 | #include <array> |
3 | #include <cstddef> |
4 | |
5 | template <typename T, std::size_t N, std::size_t... I> |
6 | constexpr std::array<T, N> genArray (const T& init, std::index_sequence<I...>) { |
7 | return {{ (I, init)... }}; |
8 | }
|
9 | |
10 | template <typename T, std::size_t N> |
11 | constexpr std::array<T, N> genArray (const T& init) { |
12 | return genArray<T, N> (init, std::make_index_sequence<N> {}); |
13 | }
|
14 | |
15 | std::array<int,20> X = genArray<int, 20> (42); |
So kannst du globale Variablen, Member-Variablen oder auch "static"-Variablen einer ISR initialisieren:
1 | class MyFirFilter { |
2 | public:
|
3 | constexpr MyFirFilter () : X (genArray<int,20> (42)) {} |
4 | std::array<int,20> X; |
5 | };
|
6 | |
7 | MyFirFilter myFirFilter; |
8 | |
9 | void ISR () { |
10 | static std::array<int,20> X = genArray<int, 20> (42); |
11 | }
|
Es muss nichts global sein, es gibt keine Initialisierungs-Funktion, der Compiler berechnet das Speicherlayout und der Startup-Code muss es nur noch in den RAM kopieren. Das "genArray" kann man auch etwas abwandeln um unterschiedliche Werte für die Einträge zu berechnen. Auch dann muss das Array nicht global sein. In C geht das dann nicht ganz so praktisch; aber auch hier kannst du die static-Variable direkt initialisieren:
1 | void ISR () { |
2 | static int X [5] = { 42, 42, 42, 42, 42 }; |
3 | }
|
Hat letztendlich den gleichen Effekt, es ist keine Initialisierungsfunktion und keine globale Variable nötig.
Walter T. schrieb: > Ich habe ein FIR-Filter n-ter Ordnung, das auf einen Sensorwert > angewendet werden soll. Nehmen wir der Einfachheit an, es wäre der ADC. > Wann ist das Filter eingeschwungen? Wenn in allen z bis z^{-n} der > korrekte Startwert steckt. Was ist denn der korrekte Startwert? Was konkret macht denn die Initialisierung? Warum nicht nur zyklische Aufrufe? Ich verstehe dein konkretes Problem hier nicht, insbesondere Walter T. schrieb: > . Das System startet so immer noch tausende Male schneller, als wenn ich > erst etliche Male den SysTick-Interrupt abwarten müßte. bei äquidistanten Abtastwerten.
Walter T. schrieb: > Welchen konkreten Vorteil haben jetzt a), c...f) gegenüber b) Also, du hast irgend eine Quelle für Daten (z.B. ADC), und diese Daten müssen gefiltert werden. Soweit ja in Ordnung. Prinzipiell kannst du nicht wissen, welchen numerischen Wert diese Daten haben, denn sie können ja in ihrem Wertebereich beliebig herumschwanken, was nicht von deinem µC-System, sondern von der Umwelt bestimmt ist. Wäre das nicht so, dann könntest du dir das Erfassen dieser Daten ja einsparen. Allerdings meine ich, daß es für diese Daten durchaus einen mittleren Erwartungswert geben dürfte - und den wirst du sicherlich kennen (wenigstens so einigermaßen). Deshalb wäre es schlichtweg der korrekte Weg, zum Initialisieren das Feld z[n], also die gespeicherten Daten mit dem Erwartungswert zu füllen und dann schlichtweg deinen Timertick einzuschalten - ohne die ISR jemals aus dem Grundprogramm aufrufen zu wollen. Walter T. schrieb: > die n Werte global machen Das mußt du sowieso, denn diese Variablen können nicht lokal zur ISR sein. Walter T. schrieb: > Wann ist das Filter eingeschwungen? Wenn in allen z bis z^{-n} der > korrekte Startwert steckt. Es gibt keinen korrekten Startwert, bestenfalls gibt es einen Erwartungswert. Siehe oben. Und ein FIR-Filter ist niemals eingeschwungen, sondern die Daten sind "eingeschwungen". Das ist ein prinzipieller Unterschied zum IIR Filter und zu allen analogen Filtern, die in ihrer Wesensart immer IIR Filter sind und rein theoretisch eine unendlich lange Erinnerungszeit haben. Also fülle dein z[n] einfach in der Init-Routine mit deinem Erwartungswert und fertig. Das Filter hat ja nur eine Erinnerungszeit von n und läuft schon beim allerersten echten Interrupt von da aus stracks in die richtige Richtung. W.S.
W.S. schrieb: > Das mußt du sowieso, denn diese Variablen können nicht lokal zur ISR > sein. Warum nicht?
Walter T. schrieb: > W.S. schrieb: >> Das mußt du sowieso, denn diese Variablen können nicht lokal zur ISR >> sein. > > Warum nicht? Vielleicht sind hier static Variablen innerhalb der Funktion gemeint.
Walter T. schrieb: > ich habe eine ISR - SysTick_Handler(), die ich vor dem ersten Interrupt > ein paar (hundert) mal aus main() aufrufen will, damit sich beim ersten > Interrupt die internen Zustände soweit stabilisiert haben Mache es anders: Schreibe deinen Filter in eine eigene Funktion, die du sowohl vom SysTick_Handler() als auch von main() aus aufrufst. Unterschiede zwischen normalen Funktionen und ISRs fallen dann nicht ins Gewicht. Der Kontext, in dem die Funktion aufgerufen wird (z.B. welcher Stack aktiv ist o.ä.) ist dann zwar unterschiedlich, aber das ist in der Regel kein Problem.
Okay, wir sind wieder auf dem Stand "ISR aus main() aufrufen ist böse und muß unbedingt vermieden werden - egal wie aufwendig der Workaround ist!" Woher kommt diese heftige Ablehnung? Normal entsteht sie ja nicht grundlos. Was ist daran prinzipiell unschön, furchbar, abstoßend, gefährlich, eklig oder verwerflich? Warum ist es Pfusch? Bitte nicht "Das ist Pfusch, weil <Synonym für Pfusch>", sondern die Gründe interessieren mich tatsächlich.
:
Bearbeitet durch User
Das hast du schon mehrmals gefragt und mehrere Antworten erhalten. lass gut sein, das Thema ist zu ende diskutiert. Aber jetzt kann es nur noch schlecht werden.
Stefanus F. schrieb: > [...] und mehrere Antworten erhalten Ich habe neue Synonyme für "Pfusch" gelernt. Das ist nicht nichts, aber auch nicht das, was ich mir erhofft habe. Es gab drei echte Ansätze der Erklärung: Rolf M. schrieb: > Eine ISR wird mit reti beendet, Das stimmt, trifft aber auf STM32 nicht zu. Die anderen beiden (die zweite ist von mir umschrieben): Dr. Sommer schrieb: > [...] langsam und unelegant. Dr. Sommer schrieb: > [wenn die ISR komplizierter wird, wird das kompliziert] Sind beides echte Begründungen, aber nicht unbedingt ausreichend, um eine allergische Reaktion dieses Ausmaßes zu verstehen. Es muß also noch stärkere Gründe geben.
Du hast meine Begründung übersehen: > Es sieht ordentlicher aus, hat weniger den Character > eines billigen Workarounds. Ist auch kein starker Grund.
Walter T. schrieb: > Sind beides echte Begründungen, aber nicht unbedingt ausreichend, um > eine allergische Reaktion dieses Ausmaßes zu verstehen. Beim Programmieren gibt es nicht für alles 100% klare knallharte Argumente. Viele Dinge macht man "nach Gefühl", welches sich mit Erfahrung bildet. Wenn das Programm weiter entwickelt wird und wächst, freut man sich über eine klare logische Struktur. Eine ISR einfach auf Verdacht 1000 mal aufzurufen (warum 1000 und nicht 999?) macht das Programm undurchsichtiger und für Leser, welche gängige Vorgehensweisen erwarten, schlechter lesbar. Man erwartet, dass eine Initialisierung über C-Variablen-Initialisierung, ggf. eine separate Funktion, oder Konstruktor gemacht wird. Dort sieht man dann auch klar und eindeutig, was die Startwerte sind. Die ISR 1000x aufzurufen verschleiert das - es ist unklar, was überhaupt der Ausgangszustand ist nach den 1000 Aufrufen. Es macht den Eindruck, dass du einfach 1000x drauf haust bis es "funktioniert". Ineffizient ist es sowieso. Allergische Reaktionen kommen immer, wenn man eine gewöhnliche Aufgabe (Initialisierung) auf eine völlig unerwartete Art macht, ohne einen ersichtlichen großen Vorteil (Sparen von 5 Zeilen ist keiner). Solchen Code möchte niemand warten! Daher fühlen sich viele fast schon beleidigt, wenn jemand knallharte Argumente für die normale gängige und von der Sprache vorgesehene Vorgehensweise verlangt und implizit damit droht, einen irgendwann später mit derart unerwartet strukturiertem Code zu konfrontieren.
Stefanus F. schrieb: > Du hast meine Begründung übersehen: > >> Es sieht ordentlicher aus, hat weniger den Character >> eines billigen Workarounds. Diese Sätze lesen sich für mich wie: "Das ist kein <freundlicheres Synonym für Pfusch>, weil es nicht den Charakter eines <anderes freundlicheres Synonym für Pfusch> hat". Das ist wahrscheinlich nicht böse gemeint, aber hilft nicht unbedingt beim Verständnis. Mein Fliesenleger begründet anders: "Wenn ich das uneben lege, ist das Pfusch, weil das Auge bei geraden Linien leider schon recht kleine Abweichungen erkennen kann." Oder: "Wenn ich das nicht in einer leichten Neigung zum Rand lege, ist das Pfusch, weil das Wasser nicht ablaufen kann, und sich Lachen auf dem Boden bilden. An den Stellen wächst dann sehr schnell Moos in den Fugen." Vielleicht sind Fliesenleger aber auch einfach von sich aus sehr geduldige Erklärer oder ich habe den besten Fliesenleger der Welt getroffen.
:
Bearbeitet durch User
PS: guter Code sollte so selbstdokumentierend sein, dass man kaum Kommentare braucht. Der Kommentar, der erläutert, dass genau 1000 ISR-Aufrufe das gewünschte Ergebnis haben (welches?), dürfte länger sein, als der gesparte Code. Wenn du ein Beispiel für die ja vorher vorhandene normale/explizite Initialisierung hast, könnte man dir ggf. helfen, das kompakter zu schreiben.
Walter T. schrieb: > Diese Sätze lesen sich für mich wie: "Das ist kein <freundlicheres > Synonym für Pfusch>, weil es nicht den Charakter eines <anderes > freundlicheres Synonym für Pfusch> hat". Das ist wahrscheinlich nicht > böse gemeint, aber hilft nicht unbedingt beim Verständnis. Ja stimmt. Der Dr. Sommer hat meine Gedanken dazu in Beitrag "Re: ARM-GCC: ISR aus main() aufrufen" besser auf den Punkt gebracht.
Dr. Sommer schrieb: > Eine ISR einfach auf > Verdacht 1000 mal aufzurufen (warum 1000 und nicht 999?) macht das > Programm undurchsichtiger und für Leser, welche gängige Vorgehensweisen > erwarten, schlechter lesbar. Man erwartet, dass eine Initialisierung > über C-Variablen-Initialisierung, ggf. eine separate Funktion, oder > Konstruktor gemacht wird. Dort sieht man dann auch klar und eindeutig, > was die Startwerte sind. Die ISR 1000x aufzurufen verschleiert das - es > ist unklar, was überhaupt der Ausgangszustand ist nach den 1000 > Aufrufen. Es macht den Eindruck, dass du einfach 1000x drauf haust bis > es "funktioniert". Ineffizient ist es sowieso. Dieses Argument verstünde ich, träfe das auf den vorliegenden Fall zu. Wenn die Begründung lautet: "Ich benötige beim Start N Aufrufe der Funktion a(), bis Variable b die Fehlerschranke epsilon unterschritten hat", sollte das aber doch ausreichen? Dr. Sommer schrieb: > an erwartet, dass eine Initialisierung > über C-Variablen-Initialisierung, ggf. eine separate Funktion, oder > Konstruktor gemacht wird. Bei einer klassischen Initialisierung von zur Compilezeit bekannten Variablen verstehe ich das ja. Aber bei einer Ermittlung von Startwerten, die erst zur Laufzeit berechnet werden können, hätte ich erwartet, daß da das Kriterium Nr. 1 lautet: "So wenig fehlerträchtig wie möglich. Dafür opfere ich sogar gerne leichten Herzens einmalig ein paar hundert Takte." Und wenn die am wenigsten fehlerträchtige Möglichkeit darin besteht, einfach die selbe Funktion zur Initialisierung wie zur Laufzeit zu nutzen, sollte ich freudestrahlend in die Hände klatschen und rufen: "Wieder eine Fehlermöglichkeit weniger!". Dr. Sommer schrieb: > Beim Programmieren gibt es nicht für alles 100% klare knallharte > Argumente. Viele Dinge macht man "nach Gefühl", welches sich mit > Erfahrung bildet. **seufz** Du kannst mit Gefühlen umgehen, weil Du die Erfahrung hast. Ich kann mich nur an Argumenten orientieren. Ich kann mir nur ein Blatt Papier nehmen und die für mich vorstellbaren Varianten skizzieren und ihre Vor- und Nachteile gegenüberstellen. Und ich denke alle Mitdiskutaten sind sich einig: "Mindestens vier Leute im Internet finden diese Lösung merkwürdig" ist nicht unbedingt ein Argument, das für sich ein valider Punkt ist. Der Grund, warum diese vier Leute das merkwürdig finden, kann es aber durchaus sein.
Walter, ich versuche mal, diese gefühlsmäßige Abneigung rational zu begründen. Ich entwickle beruflich sicherheitskritische Software (Luftfahrt). In den wenigen Fällen, wo wir noch in C "händisch" entwickeln, d.h. ein Mensch Source-Code schreibt, verfolgen wir gemäß den etablierten Standards einen ganz strengen Top-down-Ansatz. Das bedeutet, wir erhalten exakte Anforderungen, WAS das System tun soll (funktionale Anforderungen) und wie gut (d.h. präzise, schnell, etc.) es das tun soll (qualitative Anforderungen). Für diese Systemanforderungen (Requirements) überlegen wir uns dann vorläufig eine grobe Architektur (HW/SW). Mit diesen Annahmen teilen wir die Anforderungen dann auf in solche, die die Hardware betreffen und solche, die die Software betreffen. Das ist natürlich ein iteratives Vorgehen, welches auch den Kunden einbezieht und in seinem Verlauf genau dokumentiert wird. Für die Software (und auch Hardware, ist aber hier kein Thema) werden die Anforderungen dann in immer tiefere Ebenen weiterentwickelt. Dabei wird die Softwarearchitektur ebenfalls immer detaillierter beschrieben und die ganze Zeit gegen die Anforderungen sowie Kompatibilität mit der Hardware kontrolliert. Zum Schluß beschreibt die Softwarearchitektur neben einem generellen Ablaufdiagramm und globalen Erwägungen wie Speicherbedearf, Stacktiefe, I/O-Register, Bootloader, Initialisierung etc. jede einzelne Funktion. Die tiefste Ebene der Anforderungen (low-level Requirements) ist dann so detailliert, dass man daraus direkt den Quelltext schreiben kann. In unserer Firma darf es z.B. keine einzige Zeile Quellcode geben, die sich nicht direkt zu einem Requirement verfolgen lässt. Tatsächlich schreiben wir die Verweise zu den Requirements als Kommentar in den Quellcode. Für einfache Funktionen beschreiben die Requirements beispielsweise: Ein- und Ausgabewerte, Wertebereiche, Auflösung, maximale Ausführungszeit, Verhalten bei Grenz- und Sonderfällen, Rückgabe und Behandlung von Fehlern, Initialisierung etc. Die Low-Level-Requirements sind dabei so formuliert, dass ich dagegen ganz einfach Test-Cases schreiben kann, die sich meist auf eine bis drei Codezeilen beschränken. Der Vorteil an dem Vorgehen ist nun, dass ich auf jeder Ebene Verifikation durchführen kann - d.h. Quelltext gegen low-level-requirements, low-level-Requirements gegen high-level-Requirements und so weiter, bis ich wieder oben bei der Systemebene angekommen bin (V-Modell). Damit habe ich eine objektiv gerechtfertigte Zuversicht, dass der Code korrekt ist. Wenn man sich nicht an dieses Vorgehen hält, dann ist es dagegen sehr wahrscheinlich, dass man zahlreiche Fehler einbaut, weil man das Verhalten des Codes ausschließlich auf der Systemebene prüft (Gerät macht aus Sicht des Benutzers, was es soll), sich bei diesen Tests aber naturgemäß auf einen ganz begrenzten Bereich beschränken muss. Fehler wie das Aufhängen eines Gerätes beim Roll-Over der GPS-Woche werden durch dieses strukturierte Vorgehensweise vermieden, weil man sich zwangsläufig mit den Details der Datenstruktueren auseinandersetzen und alle Sonderfälle behandeln (und testen) muss. Diese Vorgehensweise ist mir mitterweile so in Fleisch und Blut übergegangen, dass ich allerdisch reagiere, wenn ein unstrukturierter Ansatz vergfolgt wird. Das Herumbasteln im Quellcode beispielsweise (ISR in einer Schleife ein paar hundert mal aufrufen), bis sich aus Benutzersicht das gewünschte Verhalten einstellt (Filter ist bei Gerätestart "eingeschwungen"), ist ein Beispiel dafür, wie wir es in der Luftfahrt nicht machen würden. Das Gegenbeispiel wäre das Betrachten der Frage, wie das Filter sich beim Einschalten verhalten soll und warum (welche System-Requirements beschreiben das? Wenn es sie nicht gibt, Meldung an den Kunden mit der Bitte um Ergänzung). Wenn man dieses Wissen hat, kann man sich beim Herunterbrechen der Requirements überlegen, an welcher Stelle man die Initialisierung durchführt. Das hängt dann unter anderem davon ab, ob die Initialisierung auch während der Laufzeit gebraucht wird (Watchdog-Reset). Meine Kritik richtet sich also weniger gegen die konkrete Implementierung (Aufrufen der ISR in einer Schleife), sondern mehr gegen die Vorgehensweise, die zu dieser Implementierung führt.
Walter T. schrieb: > "Ich benötige beim Start N Aufrufe der Funktion a(), bis Variable b die > Fehlerschranke epsilon unterschritten hat", sollte das aber doch > ausreichen? Ich würde da gerne eine Begründung sehen. Und natürlich, welche Werte da am Ende genau raus kommen. Also genau die, die man bei einer expliziten Initialisierung auch hinschreiben würde. Walter T. schrieb: > Aber bei einer Ermittlung von Startwerten, die erst zur Laufzeit > berechnet werden können, Wieso erst zur Laufzeit? Basieren die auf einer Eingabe? Wie kommt eine Eingabe zustande, wenn doch bis dato die Interrupts aus sind? Walter T. schrieb: > "Wieder eine Fehlermöglichkeit weniger!". Dafür eine Möglichkeit mehr: jemand schreibt noch mehr Code in die ISR. Bei einer Timer-ISR nicht besonders abwegig. Walter T. schrieb: > Mindestens vier Leute im Internet finden diese Lösung merkwürdig" ist > nicht unbedingt ein Argument, das für sich ein valider Punkt ist Doch, denn das bedeutet dass die Mehrheit deinen Code nicht so schnell durchblicken würde. Ich darf auf Arbeit leider mit sehr viel sehr schlechtem Code arbeiten, der auch viele Dinge auf kuriose Art und Weise macht. Leider kann ich die Entwickler davon nicht erreichen. Hier verschwende ich viel Zeit damit, zu ergründen, was die da machen.
Eine spannende Frage ist: Wie stellst du sicher, dass die N Aufrufe der Funktion in jedem möglichen Fall genügend sind?
Walter T. schrieb: > Mein Fliesenleger begründet anders: "Wenn ich das uneben lege, ist das > Pfusch, weil das Auge bei geraden Linien leider schon recht kleine > Abweichungen erkennen kann." Wenn ich etwas ISR nenne und das dann nicht nur ISR ist, wirkt das erstmal verwirrend und merkwürdig alleine von der Begrifflichkeit. Dr. Sommer schrieb: > Ich würde da gerne eine Begründung sehen. Ich auch. Unabhängig davon die ISR als Funktion aufrufen zu wollen
Dr. Sommer schrieb: > Wieso erst zur Laufzeit? Basieren die auf einer Eingabe? Wie kommt eine > Eingabe zustande, wenn doch bis dato die Interrupts aus sind? Das frage ich mich auch die Ganze Zeit. An den TE: 1.) Eine ISR ist eine Interrupt Service Routine. Diese wird durch einen Interrupt ausgelöst, nicht durch den User, das ist einfach ihr Sinn. So eine ISR einfach händisch aufzurufen ist Pfusch. Das ist nicht so schwer zu verstehen. 2.) Was genau soll sich denn da einschwingen? Du rufst eine Funktion 1000 mal auf OHNE externe Eingabe. Es kann unter diesen Umständen kein sinnvolles Szenario geben, bei dem die Werte nicht deterministisch sind, die du durch einen iterativen Aufruf in das Array schreibst.
Walter T. schrieb: > Okay, wir sind wieder auf dem Stand "ISR aus main() aufrufen > ist böse und muß unbedingt vermieden werden - egal wie > aufwendig der Workaround ist!" Eine ISR ist keine normale Subroutine und sollte daher nicht wie eine normale Subroutine behandelt werden. Wenn an einem Tankstutzen groß "DIESEL" dransteht, dann gehe ich in der Regel auch nicht davon aus, dass da Benzin (oder Gemisch) reingehört. Tu deinen Filter in eine eigene, normale Funktion. Die kannst du - wenn reentrant gemacht oder passend gesichert - aus normalem Code und ISR aufrufen. Das stinkt dann auch nicht so sehr nach "das hat ein Idiot programmiert". Explizit initialisieren ist trotzdem schöner. Kann man auch im ersten Durchlauf machen.
Walter T. schrieb: > Es muß also noch stärkere Gründe geben. Ja, natürlich! Also: deine ISR wird ja wohl nicht im stillen Kämmerlein wie ein Autist herumwerkeln, sondern sie wird filtern und dann ein Ergebnis dabei herausbekommen, was dann in irgend einer Weise weiter in der Firmware propagiert wird. So. Und nun läßt du diese ISR mutwillig auf die ersten n Samples los, die nach deiner Darstellung Mumpitz sind, weil nicht so, wie du dir das denkst. Also wird deine ISR aus den Mumpitz-Samples auch entsprechenden Mumpitz-Output erzeugen. Willst du das? Du könntest es einfacher haben: indem du einfach nur den Interrupt zu deiner ISR freigibst und ansonsten GARNICHTS tust. Nach n Samples wird sich dann alles von allein geregelt haben. Wozu willst du dann den Zirkus mit den "ein paar hundert mal aus main aufrufen" überhaupt durchführen? Das ist komplett überflüssig. Abgesehen davon ist es möglicherweise auch für die Hardware störend, weil diese normalerweise davon ausgeht, daß eine ISR nicht per Software, sondern nur vom zuständigen Interruptcontroller ausgelöst wird. Mag sein, daß das bei deiner HW keine Rolle spielt, aber woanders spielt es eine deutliche Rolle, auch dann, wenn eine ISR lediglich als void deklariert worden ist. Lies z.B. mal bei Arm nach, was dort für ein Zirkus abgeht, wenn der Gleitkommaprozessor sowohl im Grundprogramm als auch in der ISR benutzt werden soll. Was da in welchem Falle auf den Stack gerettet werden muß oder auch nicht - und so weiter. Oder lies bei Chips mit Privilegien nach, was da an Systemzuständen gerettet und restauriert werden muß oder ob manche Befehle in der ISR nur dann funktionieren, wenn sie per Interrupt tatsächlich aus dem User-level in einen höheren Level gehoben ist. Kurzum, bei einem AVR kannst du sowas ja gerne ausprobieren, wenngleich es dir auch sachlich nichts nützt, aber auf anderen Architekturen sollte man von so etwas Abstand nehmen. Man handelt sich gelegentlich mit sowas nur Bugs ein, die man ohne intime Kenntnis der HW garnicht finden kann. W.S.
S. R. schrieb: > Tu deinen Filter in eine eigene, normale Funktion. Die kannst du - wenn > reentrant gemacht oder passend gesichert - aus normalem Code und ISR > aufrufen. Was soll denn daran besser sein? Wenn die als ISR verwendete Routine die gleichen Vorgaben erfüllt, kann man diese auch direkt aufrufen. Ich finde diese Möglichkeit gut. Die vielen Warnungen hier scheinen mir alle auf verkalkte Köpfe hinzuweisen: "Haben wir noch nie gemacht" bzw. "Das machen wir immer so."
Stefanus F. schrieb: > Eine spannende Frage ist: Nein, diese Frage ist zumindest bei einem FIR Filter überhaupt nicht spannend, sondern bereits vor der ersten Zeile Quellcode geklärt. Ein solches Filter mit n Taps hat ein Gedächtnis von exakt n Samples. Wenn er also 101 Taps hat, dann reichen exakt 101 Aufrufe. Bei IIR Filtern sieht das anders aus, aber auch da reicht Obiges aus, denn wenn nicht, ist das Filter ohnehin instabil und liefert nur Murks. Kann bei IIR passieren, schließlich ist das ja quasi das Audion-Prinzip, also Versteilerung der Flanken durch Mitkopplung. Übertreibt man's, dann schwingt es. Und kurz drunter klingelt es. Ist also für Filterzwecke mit größter Vorsicht zu genießen. Aber da der Walter mit so einem Ansinnen kommt, schätze ich, daß er die Klasse der IIR-Filter ohnehin nicht wirklich handhaben kann. Mir wäre das auch zu heikel. FIR Filter sind da weitaus gutmütiger - und eben stabil und überschaubar im Verhalten. W.S.
m.n. schrieb: > Was soll denn daran besser sein? Wenn die als ISR verwendete > Routine die gleichen Vorgaben erfüllt, kann man diese auch > direkt aufrufen. Wenn man das kann, kann man das machen. Auf x86 und AVR kann man es definitiv nicht, weil dort eine ISR anders generiert werden muss. Ist halt im Zweifelsfall brüchiger Code (spätestens, wenn sich Compiler oder Architektur ändern). Einen normalen Funktionsaufruf - egal ob aus ISR oder nicht - kann der Compiler immer korrekt generieren. Einzig der Kontext ist unterschiedlich, aber dagegen kann man nichts tun. m.n. schrieb: > Ich finde diese Möglichkeit gut. Manche stehen auch auf Peitschenhiebe. Auch wenn es funktioniert, ist es meistens trotzdem keine gute Idee.
W.S. schrieb: > Mag sein, daß das bei deiner HW keine Rolle spielt, aber woanders spielt > es eine deutliche Rolle, auch dann, wenn eine ISR lediglich als void > deklariert worden ist. Lies z.B. mal bei Arm nach, was dort für ein > Zirkus abgeht, wenn der Gleitkommaprozessor sowohl im Grundprogramm als > auch in der ISR benutzt werden soll. Was da in welchem Falle auf den > Stack gerettet werden muß oder auch nicht - und so weiter. Beim Cortex M4F werden S0-S31 sowie FPSCR automatisch vom Prozessor auf den Stack gerettet. Was soll daran Zirkus sein?
Also ich muss Walter Recht geben. Wer würde denn ein riesiges Array voller "magic numbers" als wartungsfreundlicher ansehen? Das müsste irgendwie schon gerechtfertigt sein. Aber die Herren haben sich schon derartig vergallopiert, dass es ihnen offensichtlich ist, z.B. einen Tippfehler in der X. Stelle in so einem Array leichter finden zu können, als einen Initialisierungs-Loop zu verstehen. Und sie haben Recht! Denn den Tippfehler findet man rein Mechanisch... Mann!
Heiko L. schrieb: > Also ich muss Walter Recht geben. Wer würde denn ein riesiges Array > voller "magic numbers" als wartungsfreundlicher ansehen? Im Idealfall generiert man das mit einem C++ Constexpr Algorithmus... m.n. schrieb: > Die vielen Warnungen hier scheinen mir alle auf verkalkte Köpfe > hinzuweisen: > "Haben wir noch nie gemacht" bzw. "Das machen wir immer so." Manchmal haben verkalkte Köpfe auch mehr Erfahrung. Findest du die längere Hochfahr Zeit auch gut? Hier wird doch ständig über langsame Programme gelästert...
Dr. Sommer schrieb: > Im Idealfall generiert man das mit einem C++ Constexpr Algorithmus... Mhm. Und das ist dann? Mal überlegen, man hat einen loop mit konstanten bounds und ... ähm ... nein... Dr. Sommer schrieb: > Findest du die > längere Hochfahr Zeit auch gut? constexpr ist übrigens keine Garantie, dass eine Berechnung zur Compile-Time stattfindet.
Heiko L. schrieb: > ähm ... nein... Danke für die detaillierte Argumentation. Heiko L. schrieb: > constexpr ist übrigens keine Garantie, dass eine Berechnung zur > Compile-Time stattfindet. Doch, wenn das constexpr an der zu berechnenden Variable steht.
W.S. schrieb: > Ein > solches Filter mit n Taps hat ein Gedächtnis von exakt n Samples. Wenn > er also 101 Taps hat, dann reichen exakt 101 Aufrufe. Mit welchem Input? Zufallsdaten? Müll? Nullen?
Ich habe ein ähnlichen Problem so gelöst, daß ich im Timerinterrupt eine Variable runterzähle und erst, wenn sie 0 ist, wertet das Main die Ergebnisse aus.
Wenn schon, dann würde ich den Algorithmus 100x mit definiertem Input aufrufen, und nicht einfach Interrupts simulieren, die dann zufällige Werte aus dem ADC verwursten. Man möchte schließlich ein vorhersagbares Ergebnis.
Guten Morgen, über Nacht sind ein paar interessante Beiträge geschrieben worden. Da brauche ich ein besseres Eingabegerät und ein wenig Zeit, um die sinnvoll zu würdigen. Aber einen Fehlschluß will ich kurz zwischen Tür und Angel korrigieren: Stefanus F. schrieb: > Mit welchem Input? Zufallsdaten? Müll? Nullen? Der erste ADC-Wert (adc_0) ist alles andere als Müll. Es ist zu diesem Zeitpunkt das beste, was vorhanden ist. Und auch eigentlich gar nicht wirklich schlecht. Immerhin ist das in Hardware realisierte Anti-Aliasing Filter ja schon ein Weilchen vor dem Start der MCU aktiv. Damit ist in einem FIR-Filter für alle Eingangsverzögerungen u(z^(-i)) = adc_0 mit i in 0...n ein valider Startwert. Daß der Ausgang des Filters sich in den nächsten n Schritten immer weiter verbessern wird, ist natürlich auch klar. Aber ich wüßte keine bessere Schätzung für den Anfang.
:
Bearbeitet durch User
Dr. Sommer schrieb: > Heiko L. schrieb: >> ähm ... nein... > > Danke für die detaillierte Argumentation. > > Heiko L. schrieb: >> constexpr ist übrigens keine Garantie, dass eine Berechnung zur >> Compile-Time stattfindet. > > Doch, wenn das constexpr an der zu berechnenden Variable steht. Das liest man immer wieder, stimmt aber so nicht. Das gilt nur dann, wenn die constexpr in einem Kontext verwendet wird, in dem eine Compile-Zeit-Konstante vorliegen muss.
Heiko L. schrieb: > Das gilt nur dann, > wenn die constexpr in einem Kontext verwendet wird, in dem eine > Compile-Zeit-Konstante vorliegen muss. Und das ist der Fall, wenn eine globale oder statische Variable mit constexpr markiert wird. Wenn das nicht zur Compile-Time berechnet werden kann, gibt's einen Compiler-Error.
Walter T. schrieb: > Der erste ADC-Wert (adc_0) ist alles andere als Müll. Es ist zu diesem > Zeitpunkt das beste, was vorhanden ist. Und auch eigentlich gar nicht > wirklich schlecht. Immerhin ist das in Hardware realisierte > Anti-Aliasing Filter ja schon ein Weilchen vor dem Start der MCU aktiv. > Damit ist in einem FIR-Filter für alle Eingangsverzögerungen u(z^(-i)) = > adc_0 mit i in 0...n ein valider Startwert. Daß der Ausgang des Filters > sich in den nächsten n Schritten immer weiter verbessern wird, ist > natürlich auch klar. Aber ich wüßte keine bessere Schätzung für den > Anfang. Das ganze klingt irgendwie, als ob du versuchst, schneller auszuwerten als das Filter Daten am Ausgang liefert (Warum?). Ist denn sichergestellt, dass dein Nutzsignal schon beim anschalten anliegt? Vielleicht fehlt mir zum Verständnis auch ein konkreter Anwendungsfall. Aber wenn ich z.B. an Sprachsignale denke, macht das für mich keinen Sinn.
Dr. Sommer schrieb: > Und das ist der Fall, wenn eine globale oder statische Variable mit > constexpr markiert wird. Wenn das nicht zur Compile-Time berechnet > werden kann, gibt's einen Compiler-Error. Das ist ja auch, was du mit constexpr garantierst. "Kann zur Compile-Zeit berechnet werden."
Dr. Sommer schrieb: > Findest du die > längere Hochfahr Zeit auch gut? Hier wird doch ständig über langsame > Programme gelästert... Daß hier viel und auch grundlos gelästert wird, ist bekannt. Das ist kein Argument. Gerade beim Hochfahren hat ein Programm viel Zeit, da die Peripherie nicht sofort einsatzbereit ist. Programmier doch mal ein Gerät, das nach 1 ms Startup auf ein angeschlossenes (LC-/TFT-)Display schreibt. Viel Lesbares wird da nicht zu sehen sein. Walter T. schrieb: > ich habe eine ISR - SysTick_Handler(), die ich vor dem ersten Interrupt > ein paar (hundert) mal aus main() aufrufen will, damit sich beim ersten > Interrupt die internen Zustände soweit stabilisiert haben. > ... > - deswegen werden alle > Interrupts erst später freigegeben. Mache es einfach. Das wird doch in < 1 ms erledigt sein.
Ich mach sowas in der Regel ganz pragmatisch so daß ich die ganze Sache beim Einschalten normal loslaufen lasse, jedoch die Ergebnisse der Messung (oder was auch immer) erst nach einer gewissen Zeit verwende, das beinhaltet die Zeit bis alle Spannungen überall stabil anliegen und Meßwerte brauchbar sind. Langsame moving averages oder andere Filter initialisiere ich mit den ersten brauchbaren Werten direkt (musst den Filter halt so implementieren daß das geht) so daß ich ich nicht zu lange warten und auch nicht tausende sinnloser Runden drehen muß. Wie lange es nach dem Einschalten dauert bis die ersten brauchbaren Messwerte reinkommen liegt an der jeweiligen Schaltung und wird experimentell ermittelt.
Walter T. schrieb: > Der erste ADC-Wert (adc_0) ist alles andere als Müll. Ah, schön dass wir endlich erfahren dass die Eingabe doch erst zur Laufzeit bekannt ist. In dem Fall funktioniert eine normale Initialisierung natürlich nicht. Dann ist der Vorschlag am Besten, das Filter in eine eigene Funktion zu packen und die sowohl von ISR als auch von main() heraus aufzurufen. m.n. schrieb: > Programmier doch mal ein Gerät, das nach > 1 ms Startup auf ein angeschlossenes (LC-/TFT-)Display schreibt. Solche Dinge haben die Tendenz sich aufzusummieren. Daher achte ich schon gerne darauf, das Hochfahren auch zu beschleunigen. Sonst ist das am Ende wie beim Thermomix - nach dem Einschalten erstmal 1 Minute hochfahren bevor man loslegen kann...
Peter D. schrieb: > Ich habe ein ähnlichen Problem so gelöst, daß ich im Timerinterrupt eine > Variable runterzähle und erst, wenn sie 0 ist, wertet das Main die > Ergebnisse aus. Das ist das einzig sinnvolle. Ein FIR Filter mit N Stufen braucht halt N Eingabewerte, bevor der Ausgabewert nützlich ist (im Sinne von: er korreliert mit den Eingabedaten). Das Filter mit irgendwelchen Werten zu füllen, bringt wenig bis nichts. Vollkommen egal, ob man dazu einen C-Initializer verwendet oder im Code explizit initialisiert oder N Dummy-Datenpunkte reinfüttert. Der einzige 100% korrekte Weg besteht darin, N echte Messungen durchzuführen (also auch mit dem echten Timing) und so lange den Ausgabewert des Filters zu verwerfen. Es gibt noch eine Variante, die vielleicht "gut genug" ist und bei der man weniger lang warten muß: man füllt das leere Filter beim allerersten Aufruf einmal komplett mit dem ersten Meßwert. Das hat dann den gleichen Effekt, als hätte man N-mal den gleichen Wert gemessen. Aber für keinen dieser Lösungswege ist es notwendig, die ISR manuell aufzurufen.
S. R. schrieb: > Ist halt im Zweifelsfall brüchiger Code (spätestens, wenn sich Compiler > oder Architektur ändern). Das hielte ich übrigens am ehesten für ein Argument (von „haben wir ja noch nie so gemacht!“ abgesehen :). Compiler jetzt nicht so sehr (das ist weitgehend alles durch die ARM-Hardware so vorgegeben), aber Architektur natürlich schon. Wenngleich: einen SysTick_Handler gibt's halt ohnehin nur auf dieser Architektur (mit diesem Namen). Beim Übergang auf eine andere Architektur muss man da also sowieso reinfassen. Sauberer sieht es dann auf jeden Fall aus, eine separate Funktion zu schreiben, die N mal direkt und danach aus dem SysTick_Handler aufgerufen wird. Mit link time optimizations müssten Compiler und Linker das eigentlich auf der ARM-Architektur sogar wieder auf das gleiche optimieren können, wie Nicolas es hier gerade manuell erzwingen möchte. Rolf M. schrieb: > Hätte mir eigentlich spätestens bei SysTick_Handler() auffallen müssen, > weil das STM32-typisch ist. Kleine Korrektur: es ist Cortex-M-typisch, denn der SysTick ist von ARM standardisiert. Es ist eigentlich noch nicht ganz an der Zeit, dass man „STM32“ bereits als Synonym für „Cortex-M“ benutzen könnte. :)
Axel S. schrieb: > Es gibt noch eine Variante, die vielleicht "gut genug" ist.. Ja, sehe ich genau so. Das hatte ich ja mit dem "Erwartungswert" so angedeutet. Ich befürchte nur, daß auch deine Worte unverstanden verhallen. Aber wenn hier Leute mit Zeugs wie constexpr und Compile-Time herumdiskutieren und selbst ein Moderator sowas schreibt: Jörg W. schrieb: > Sauberer sieht es dann auf jeden Fall aus, eine separate Funktion zu > schreiben, die N mal direkt und danach aus dem SysTick_Handler > aufgerufen wird. ..dann graust es mich. W.S.
W.S. schrieb: > und selbst ein Moderator Könntest du bitte mal diesen unsinnigen Verweis unterlassen? Die Moderatorenfunktion heißt doch nicht, dass wir uns deshalb sämtlicher fachlicher Diskussionen enthalten müssten. Selbstverständlich vertreten wir dann aber, was den fachlichen Teil angeht, einfach nur eine persönliche Meinung wie jeder andere. Die kann diskussionswürdig sein, aber du ziehst es ja vor, statt fachlich über meine Meinungsäußerung zu diskutieren, einfach nur die Polemik-Keule zu ziehen. Was soll das denn?
Peter D. schrieb: > Ich habe ein ähnlichen Problem so gelöst, daß ich im Timerinterrupt eine > Variable runterzähle und erst, wenn sie 0 ist, wertet das Main die > Ergebnisse aus. Das ist schlecht, denn es kostet Rechenzeit - und das für immer. Die Herunterzählzeit mag ja schon längst vergangen sein, und dennoch muß jedesmal in der ISR abgetestet werden, ob die variable nun 0 ist oder nicht. Aber gerade bei digitaler Signalverarbeitung ist die Samplefrequenz mit Regelmäßigkeit verdammt hoch und man geizt deshalb mit jedem Systemtakt. Was meinst du, wieviel Takte man für die gesamte ISR hat, wenn man nen Stereo-Audio-Codec mit 192 kHz Samplerate bedienen und den Input filtern will? Und der erst: John Doe schrieb: > Beim Cortex M4F werden S0-S31 sowie FPSCR automatisch vom Prozessor auf > den Stack gerettet. Was soll daran Zirkus sein? Jahaha.. und nun willst du die ISR manuell von main aus aufrufen, ohne zu beachten, daß du ja nicht in Software diese Rettfunktion nachbilden kannst, die die Hardware da macht. W.S.
W.S. schrieb: > Das ist schlecht, denn es kostet Rechenzeit - und das für immer. Na übertreib mal nicht so. Ich hab dafür einen 1ms Timerinterrupt genommen, da ist genug Zeit. Ist 0 erreicht, kann der Interrupt auch sich selber sperren, die ARM haben ja in der Regel reichlich Timer. Je nach ARM könnte man auch einen One-Shot Timer aufsetzen, d.h. ganz ohne Interrupt auskommen.
W.S. schrieb: > Jahaha.. und nun willst du die ISR manuell von main aus aufrufen, ohne > zu beachten, daß du ja nicht in Software diese Rettfunktion nachbilden > kannst, die die Hardware da macht. Muss ja auch niemand. Wie Nicolas schon feststellte, unterscheidet sich sowas wie der SysTick_Handler in nichts von jeder anderen void/void-Funktion. Alles exception handling wird durch die Hardware erledigt. Joe hatte oben auf die ARM-Seite verwiesen, unter "Exception return" steht beschrieben, wie das genau erledigt wird.
Jörg W. schrieb: >> und selbst ein Moderator > Könntest du bitte mal diesen unsinnigen Verweis unterlassen? Freu' Dich doch, letztendlich zeigt es, dass er dich von unten aus bewundert.
Stefanus F. schrieb: > dass er dich von unten aus bewundert. Darauf kann ich gut und gern verzichten. Die Moderation ist Arbeit, die wir irgendwie nebenbei auf uns nehmen, aber eigentlich wollen wir Moderatoren diesen Teil allesamt nicht zum wesentlichen Anteil der Tätigkeit im Forum ausufern lassen. Wir sind hier genauso wie alle anderen der fachlichen Diskussionen wegen. Auch oder gerade bei Leuten wie W.S., bei denen ich nur selten eine fachliche Übereinstimmung finde *), lohnt sich die Diskussion allemal solange sie denn sachlich bleibt, denn es kann einen ja fachlich weiter bringen. Einen puren Verweis „sowas kommt von einem Moderator“ (wohlgemerkt bezogen auf eine fachliche Meinungsäußerung, nicht etwa auf Umgangston oder dergleichen, da wäre es natürlich völlig gerechtfertigt) empfinde ich jedoch als völlig unsachliche Diskussionskultur, die nur Frust erzeugt und keinen Weiterbildungseffekt hat. *) Ich bin allerdings wenig voreingenommen. W.S.' Lötstationsprojekt um den JBC-Kolben herum fand ich zum Beispiel sehr interessant.
:
Bearbeitet durch Moderator
Gerhard schrieb: > Ich entwickle beruflich sicherheitskritische Software (Luftfahrt). > [Danach kommt ein langer und lesenswerter Text über Softwareentwicklung > einem Bereich, der extrem stark reglementiert ist] Danke für Deine Beschreibung. Daß jemand, der es gewohnt ist, grundsätzlich mit einem Top-Down-Ansatz zu arbeiten, jeden Bottom-Up-Ansatz als "unstrukturiertes Vorgehen" zu bezeichnen, kann ich nachvollziehen. Insofern finde ich Deinen Beitrag sehr lesenswert. Im Alltag wirft man mir eigentlich selten "unstrukturiertes Vorgehen" vor, aber Softwareentwicklung ist ja auch definitiv nicht der Bereich, in dem ich mich zuhause fühle. S. R. schrieb: > [...] Das stinkt dann auch nicht so sehr nach "das hat ein Idiot > programmiert". Ich übersetze mal ins Hochdeutsche: Das ist <unfreundliches Synonym für "Pfusch">, weil <anderes unfreundliches Synonym für "Pfusch">. Dr. Sommer schrieb: > Manchmal haben verkalkte Köpfe auch mehr Erfahrung. Findest du die > längere Hochfahr Zeit auch gut? Wieso wird die Hochfahr-Zeit länger? Sie wird tatsächlich kürzer. Bei einer ISR, die im 10 Millisekunden-Raster aufgerufen wird und einem FIR-Filter mit n = 100 benötige ich im Normalfall 990 Millisekunden, bis der erste gültige Wert hinten herauspurzelt. Wenn ich das Filter 100 mal mit dem ersten Eingangswert füttere, ist vor der ersten Millisekunde der erste Ausgangswert vorhanden. Lukas schrieb: > Vielleicht fehlt mir zum Verständnis auch ein konkreter Anwendungsfall. > Aber wenn ich z.B. an Sprachsignale denke, macht das für mich keinen > Sinn. Bei Sprachsignalen wäre das auch komplett sinnlos. Sprachsignale werden Blockweise mit einer hohen Abtastraste ausgewertet. Bei einen hohen Abtastrate bringt es aber auch kaum Geschwindigkeitsgewinn, schon loszulegen, bevor die ersten n Samples anliegen. Sinn ergibt es erst, bei sehr langsam veränderlichen Signalen mit einer sehr niedrigen Abtastrate. Bernd K. schrieb: > Langsame moving averages oder andere Filter > initialisiere ich mit den ersten brauchbaren Werten direkt (musst den > Filter halt so implementieren daß das geht) so daß ich ich nicht zu > lange warten und auch nicht tausende sinnloser Runden drehen muß. Axel S. schrieb: > Es gibt noch eine Variante, die vielleicht "gut genug" ist und bei der > man weniger lang warten muß: man füllt das leere Filter beim allerersten > Aufruf einmal komplett mit dem ersten Meßwert. Das hat dann den gleichen > Effekt, als hätte man N-mal den gleichen Wert gemessen. W.S. schrieb: > Ja, sehe ich genau so. Das hatte ich ja mit dem "Erwartungswert" so > angedeutet. Ich befürchte nur, daß auch deine Worte unverstanden > verhallen. Walter T. schrieb: > Der erste ADC-Wert (adc_0) ist alles andere als Müll. Es ist zu diesem > Zeitpunkt das beste, was vorhanden ist. Und auch eigentlich gar nicht > wirklich schlecht. Immerhin ist das in Hardware realisierte > Anti-Aliasing Filter ja schon ein Weilchen vor dem Start der MCU aktiv. > Damit ist in einem FIR-Filter für alle Eingangsverzögerungen u(z^(-i)) = > adc_0 mit i in 0...n ein valider Startwert. Daß der Ausgang des Filters > sich in den nächsten n Schritten immer weiter verbessern wird, ist > natürlich auch klar. Aber ich wüßte keine bessere Schätzung für den > Anfang. Ihr vier hattet alle die gleiche gute Idee. Ich mache das auch so.
:
Bearbeitet durch User
Walter T. schrieb: > Wieso wird die Hochfahr-Zeit länger? Sie wird tatsächlich kürzer. Sie wird länger gegenüber einer direkten Initialisierung mit fixen Werten. Wie wir aber jetzt erst erfahren haben, geht das gar nicht weil die Initialisierung mit zur Laufzeit bekannten Werten geschieht. Bei einem FIR-Filter könnte man den 1. ADC-Wert einfach in die Schlange der Eingabewerte kopieren a la memset(). Die 1000 Aufrufe der ISR bewirken wahrscheinlich auch 1000 Berechnungen des Ausgabewerts, welcher dann gar nicht gebraucht wird.
Gerhard schrieb: > Meine Kritik richtet sich also weniger gegen die konkrete > Implementierung (Aufrufen der ISR in einer Schleife), sondern mehr gegen > die Vorgehensweise, die zu dieser Implementierung führt. Naja, meine Vorgehensweise bei der Implementierung kennst Du ja eigentlich gar nicht. Woher weißt Du, wie detailliert mein Mini-Lastenheft ist? Klar - ein Requirements Engineering wie in der Luftfahrt kann ich bei einem Hobbyprojekt nicht leisten. Sonst wäre meine Lebenszeit schon vor der ersten Quelltextzeile um. Und daß ich als Hobbyentwicker nur einen Bottom-Up-Ansatz wählen kann, weil 1. Ich für jede Komponente erst einmal einen Funktionsprototypen benötige, um zu wissen, was überhaupt funktioniert und 2. Sinnvolle Literatur über "Architektur einer Mikrocontroller-Firmware" Mangelware zu sein scheint, so daß jeder Entwickler jeden Fehler erst einmal selbst gemacht haben muß -oder zumindest jede Entwicklergruppe-, *) ist auch nicht unbedingt ein echtes Zeichen für eine "unstrukturierte" Vorgehensweise. Tatsächlich halte ich meine Vorgehensweise (1. Problem identifizieren -> 2. verschiedene Lösungsansätze skizzieren -> 3. Vor- und Nachteile ausarbeiten -> 4. bewerten -> 6. auswählen -> immer: dokumentieren) für sehr strukturiert. Aber diese Selbsteinschätzung hat wohl jeder Ingenieur, der etwas auf sich hält. Punkt 3 ist übrigens der Teil, der die meisten Foren-Fragen generiert und damit besonders schwierig ist. Gerhard schrieb: > Das Gegenbeispiel wäre das Betrachten der Frage, wie das Filter sich > beim Einschalten verhalten soll und warum (welche System-Requirements > beschreiben das? Die Frage habe ich in meinem Mini-Lastenheft tatsächlich bislang außen vor gelassen. Danke für die Ergänzung. *) Sollte jetzt jemand die entsprechende Literatur ("Archtitektur für Mikrocontroller-Firmware - das weiß man doch!") aus dem Hut zaubern: Darüber würde ich mich total ärgern. Versprochen!
:
Bearbeitet durch User
Walter T. schrieb: >> [...] Das stinkt dann auch nicht so sehr nach >> "das hat ein Idiot programmiert". > > Ich übersetze mal ins Hochdeutsche: > Das ist <unfreundliches Synonym für "Pfusch">, weil <anderes > unfreundliches Synonym für "Pfusch">. Nun, ich habe eine sinnvolle Lösung präsentiert. Mehrfach. Hattu ignoriat. Diese (oder eine der anderen vorgestellten sinnvollen Lösungen) nicht zu verwenden, ist nunmal Pfusch. Kann man machen, kann funktionieren, bleibt trotzdem Pfusch. Aber wer überall "Pfusch" hören will, der hört es auch überall.
W.S. schrieb: > Und der erst: > John Doe schrieb: >> Beim Cortex M4F werden S0-S31 sowie FPSCR automatisch vom Prozessor auf >> den Stack gerettet. Was soll daran Zirkus sein? > > Jahaha.. und nun willst du die ISR manuell von main aus aufrufen, ohne > zu beachten, daß du ja nicht in Software diese Rettfunktion nachbilden > kannst, die die Hardware da macht. > > W.S. Das ist bei den Cortext-M egal. Das Hardware-Stacking gibt es nur, wenn der "Call" auch durch die Hardware ausgeführt wurde. Wenn die Hardware Register gesichert hat, dann steht im Link-Register statt der Return-Adresse eine "Magic Number" drin (0xFFFFFFFirgendwas), was die Hardware-Aktion wieder rückgängig macht. Inklusive "Run-Mode"-Wechsel, etc. D.h. die Maschine weiß selbst, wann sie besser RETI machen sollte. Update: Gut versteckt in der ARM-Doku (Application Note 298) ist zu finden, daß auf Cortext-M mit FPU auch die C-Calling-Convention (S0..S16 müssen vom Caller gesichert werden) implementiert ist. Nur wird dafür zuerst nur Platz auf dem Stack reserviert und das eigentliche Sicher erst durchgeführt, wenn die ISR erstmals FP benutzt.
:
Bearbeitet durch User
Carl D. schrieb: > Update: > Gut versteckt in der ARM-Doku (Application Note 298) ist zu finden, daß > auf Cortext-M mit FPU auch die C-Calling-Convention (S0..S16 müssen vom > Caller gesichert werden) implementiert ist. Nur wird dafür zuerst nur > Platz auf dem Stack reserviert und das eigentliche Sicher erst > durchgeführt, wenn die ISR erstmals FP benutzt. Danke für den Hinweis! Ich mache zwar momentan alles in Integer, aber gibt doch ein wohliges Gefühl, daß auch hier keine Falle lauert.
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.