wahrscheinlich 'ne Frage, bei der die Antwort "nur" von akademischem Interesse ist... Bisher hab ich immer, nach kritiklos übernommener Lehrmeinung, meine main mit einer Endlosschleife beendet. Diese Tage stöber ich in der FAT32 Bibliothek herum, die von Daniel R. hier auf mikrocontroller.net eingestellt wurde. Am Ende der main vom Beispielprogramm (das Ende wird im Ablauf tatsächlich erreicht) steht nix Endlosschleife, stattdessen ganz frech ein return. Und trotzdem passiert nichts schlimmes: das Raum-Zeit-Kontinuum gerät nicht aus den Fugen, das Programm startet nicht pausenlos neu, der Atmel verglüht nicht. Hat mich jetzt ehrlich gesagt sehr überrascht. Ist die Endlosschleife am Ende der main Funktion Aberglaube, oder gibts eine sinnhaltige Begründung a la "guter Programmierstil"?
1 | #include <stdio.h> |
2 | |
3 | int main() |
4 | {
|
5 | printf("Hallo Welt\n"); |
6 | return 0; |
7 | }
|
Damit wird die Endlichkeit des Universums bewiesen. ;)
>Am Ende der main vom Beispielprogramm ...
Und welches Beispielprogramm meinst Du?
Bis dahin: main wird in mikrocontroller-Programmen niemals verlassen.
Abgesehen davon, könntest Du ja mal einfaches Programm nehmen und im Simulator laden und im Einzelschrittbetrieb ausführen. Dann siehst Du, was geschieht.
Viel zu lernen Du noch hast
Was stört dich daran, explizit hinzuschreiben, das das Programm zum Schluss warten soll? Sich darauf verlassen, dass der Compiler das gewollt umsetzt ohne es hinzuschreiben fände ich gewagt.
@ Floh (Gast) >Sich darauf verlassen, dass der Compiler das gewollt umsetzt ohne es >hinzuschreiben fände ich gewagt. Darauf muss man sich nicht blind verlassen, das kann man im Handbuch des Compilers nachlesen. Und hier reden wir ja wahrscheinlich vom avr gcc. Der geht NACH dem Ende von main() in eine Endlosschleife mit gesperrten Interrupts.
Dann kann man sich das tatsächlich sparen. Seit welcher Version ist das eigentlich so?
Schon ziemlich lange, ich schätze mal locker 10 Jahre.
Ist es nicht sinnvoller in den absoluten power down mode zu gehen?!
Ungläubiger schrieb: > Ist es nicht sinnvoller in den absoluten power down mode zu gehen?! Sinnvoller ist es eher, das Programm selbst als Endlosschleife zu schreiben, so daß es gar nicht erst dazu kommt, daß man sich Gedanken darüber machen muß, was man am Ende von main() macht. µC-Programme gehen normalerweise eigentlich nicht einfach irgendwann zu Ende, sondern laufen bis zum Ende des Universums - oder so lange, bis der Depp von Benutzer den Saft abdreht, je nachdem, was zuerst kommt.
Naja. Es hat jedenfalls (in der Regel) keinen Sinn, ein Mikrocontroller-Programm mit return aus main enden zu lassen, selbst wenn der GCC da als Gimmick noch selbst 'ne Schleife einbaut. Soweit ist das wohl klar, oder?
Nur der Vollständigkeitshalber: Der gcc macht hier kein Gimmick. Die Endlosschleife ist Teil des Startup-Codes und dieser ist Teil der C-Laufzeitumgebung, also meißtens der avr libc. Sollte jemand den Drang verspüren ein eigenes Startup-File zu verwenden so muss er selber irgendwo für die Endlosschleife sorgen. Andernfalls kommt es wirklich zu undefiniertem Verhalten. Ich würd zB nicht die Hand ins Feuer legen dass dieses "Feature" beim arm-none-eabi-gcc auch enthalten ist. Wie gesagt, Sache des Startup-Codes.
Ben schrieb: > Ich würd zB nicht die Hand ins Feuer legen dass dieses "Feature" beim > arm-none-eabi-gcc auch enthalten ist. Der enthält gar keinen Startup Code, bzw. nur einen Dummy den keiner verwendet. > Wie gesagt, Sache des Startup-Codes. Ja, und der wird vom Mikrocontroller-Hersteller geliefert. Der von ST für die STM32 enthält keine Endlosschleife, aber ein "bx lr", also return. lr wird 0xFFFFFFFF enthalten, und daher: "A reset sets this register[=LR] to 0xFFFFFFFF. The reset value causes a fault condition if the processor uses it when attempting a subroutine return." d.h. der Controller stürzt ab wenn man aus der main() zurückkehrt.
Micha schrieb: > Bisher hab ich immer, nach kritiklos übernommener Lehrmeinung, meine > main mit einer Endlosschleife beendet. Ein MC-Programm läuft bei mir immer solange, bis ihm der Saft abgedreht wird. Die Mainloop tut also ständig ihren Aufgaben und wird nie beendet. Ich schreibe also weder eine leere Loop noch ein Return ans Ende des Main. Vieleicht ganz am Anfang einer Entwicklung könnte es mal vorgekommen sein, daß die Main-Loop noch leer ist und ich erstmal nur die Initialisierung teste. Theoretisch ist ein rekursives Main möglich, aber ich sehe auch darin keinen Sinn. Das Return benötigt außerdem mehr Code, da Main vom Typ int ist, also der Returnwert geladen werden muß.
95% aller Programme verwenden eine Main loop. Meistens sieht das so aus:
1 | void main() |
2 | {
|
3 | |
4 | // initialisierungen
|
5 | |
6 | while(1) |
7 | {
|
8 | // etwas machen
|
9 | }
|
10 | }
|
Genauso wie die meisten Autos an der Tankstelle aufgetankt werden. Musst du zwar nicht machen, ist aber allgemein so ueblich. Ein typischer Fall von "Das ist halt so" Sollte es wohl mal ein bayrisches Programmierhandbuch geben. http://de.wikipedia.org/wiki/Bairische_Dialekte
Peter Dannegger schrieb: > Ich schreibe also weder eine leere Loop Keine leere Loop? Und was wenn man komplett Interrupt-basiert programmiert um verschiedene Dinge gleichzeitig tun zu können? Dann ist die mainloop leer, es sei denn man zählt ein asm("WFI") oder asm("SLEEP") als "nicht-leer"...
Dr. Sommer schrieb: > Peter Dannegger schrieb: >> Ich schreibe also weder eine leere Loop > Keine leere Loop? Und was wenn man komplett Interrupt-basiert > programmiert um verschiedene Dinge gleichzeitig tun zu können? Dann ist > die mainloop leer, es sei denn man zählt ein asm("WFI") oder > asm("SLEEP") als "nicht-leer"... Programmcode gehoert nicht in die Interruptroutine. Also sofort wieder verlassen. Es geht zwar, wenn es nur einen Interrupt level gibt, jedoch moderne Prozessoren haben u.U. mehrere. Um verschiedene Dinge gleichzeitig tun zu koennen, wird oft ein "Round Robin" Konstrukt in der main loop verwendent. Komplett Interrupt-basiert? Entweder verwendest du Interrupts, und dann wird die Programausfuehrung wohl durch einen Timer kontrolliert (Zeitscheiben Verfahren). Oder du verwendest keine Interrupts. Die meisten professionellen Programme gehen so vor, also es wird ein Zeitscheiben Verfahren verwendet. z.B. 1mSec. als Grundtakt, Interrupt, und dann einfach Zaehler verwenden, und Unterprogramme ausfuehren.
Takao K. schrieb: > Programmcode gehoert nicht in die Interruptroutine. Also sofort wieder > verlassen. Warum? Sind Interrupts irgendwie böse? > Es geht zwar, wenn es nur einen Interrupt level gibt, jedoch > moderne Prozessoren haben u.U. mehrere. Das ist doch gut, kann man zur Priorisierung nehmen. > Um verschiedene Dinge gleichzeitig tun zu koennen, wird oft ein "Round > Robin" Konstrukt in der main loop verwendent. > > Komplett Interrupt-basiert? Entweder verwendest du Interrupts, und dann > wird die Programausfuehrung wohl durch einen Timer kontrolliert > (Zeitscheiben Verfahren). Oder du verwendest keine Interrupts. Timer nur wenn man Delays braucht. Ansonsten halt bei Interrupts auf das geschehene Ereignis reagieren, eine Aktion ausführen und auf den nächsten Interrupt warten. > Die meisten professionellen Programme gehen so vor, also es wird ein > Zeitscheiben Verfahren verwendet. Asynchrone, also event-basierte Programmierung gilt schon länger als schick, das heißt in Event-Handlern - also ISR's - auf das Ereignis reagieren und dort etwas tun. Die mainloop tut dann nichts als warten.
Dr. Sommer schrieb: > Takao K. schrieb: >> Programmcode gehoert nicht in die Interruptroutine. Also sofort wieder >> verlassen. > Warum? Sind Interrupts irgendwie böse? Ja, diese vebrauchen sehr viel Zeit, insbesondere, wenn die Granulierung zu fein ist. >> Es geht zwar, wenn es nur einen Interrupt level gibt, jedoch >> moderne Prozessoren haben u.U. mehrere. > Das ist doch gut, kann man zur Priorisierung nehmen. > Es gibt schon Anwendungen, die Interrupts unbedingt erfordern, z.B. IR Daten auslesen wenn sich ein Pin aendert. Oder um auf USB zu reagieren. Dies ist dann oft schon vorprogrammiert. PIC32 kann z.B. auch DMA >> Um verschiedene Dinge gleichzeitig tun zu koennen, wird oft ein "Round >> Robin" Konstrukt in der main loop verwendent. >> >> Komplett Interrupt-basiert? Entweder verwendest du Interrupts, und dann >> wird die Programausfuehrung wohl durch einen Timer kontrolliert >> (Zeitscheiben Verfahren). Oder du verwendest keine Interrupts. > Timer nur wenn man Delays braucht. Ansonsten halt bei Interrupts auf das > geschehene Ereignis reagieren, eine Aktion ausführen und auf den > nächsten Interrupt warten. Eine blinkende LED ist schon sinnvoll um zu sehen ob das Programm noch einwandfrei abgearbeitet wird. Also vewende ich immer einen Timer interrupt, meistens zwischen 1 KHz und 10 KHz. >> Die meisten professionellen Programme gehen so vor, also es wird ein >> Zeitscheiben Verfahren verwendet. > Asynchrone, also event-basierte Programmierung gilt schon länger als > schick, das heißt in Event-Handlern - also ISR's - auf das Ereignis > reagieren und dort etwas tun. Die mainloop tut dann nichts als warten. Ja schon richtig aber es ist besser, nur ein Flag zu setzen, oder eine Nachricht zu generieren, und den Interrupt sofort wieder zu verlassen. Insbesondere wenn mehrere Interrupts moeglich sind, wird die Programmierung sonst langsam und kompliziert. Also nur die interrupt Flags abfragen, und falls eins gesetzt ist, sofort verlassen. Damit kann auch die Prioritaet geloest werden: Zum einen durch die Reihenfolge der Abarbeitung der Nachrichten. Und zum anderen kann ein kritischer Interrupt eigentlich immer sofort stattfinden. So einen oder zwei kann man schon direkt abarbeiten, also in der ISR, wenn z.B. nur Daten fuer die serielle Schnittstelle nachgeladen werden sollen.
:
Bearbeitet durch User
Takao K. schrieb: > Dr. Sommer schrieb: >> Takao K. schrieb: >>> Programmcode gehoert nicht in die Interruptroutine. Also sofort wieder >>> verlassen. >> Warum? Sind Interrupts irgendwie böse? > > Ja, diese vebrauchen sehr viel Zeit, insbesondere, wenn die Granulierung > zu fein ist. Du bist du mir mit deinen Aussagen etwas zu sehr auf der ultra-Konservativen Seite. Lies lieber erst mal eine zeitlang mit, dann weißt du auch gegen wen du argumentierst. Die Leute hier, die haben nämlich Ahnung von dem was sie tun. Die machen das schon 'ein paar' Jährchen lang. Beruflich. > Ja schon richtig aber es ist besser, nur ein Flag zu setzen, oder eine > Nachricht zu generieren, und den Interrupt sofort wieder zu verlassen. In einer ISR wird getan, was zu tun ist. Und ja, das kann auch bedeuten eine (kurze) Bearbeitung eines Sachverhaltes in die ISR zu setzen. Wenn es länger dauert, dann wird ein Jobflag bemüht. Die Aussage "alles wird immer und unbedingt immer in einer ISR gemacht" ist genauso falsch wie "man darf gar nichts in einer ISR machen ausser ein Flag zu setzen". Man entscheidet im Einzelfall, welche Strategie implementiert wird. > Insbesondere wenn mehrere Interrupts moeglich sind, wird die > Programmierung sonst langsam und kompliziert. Quatsch. Überhaupt nichts wird deswegen komplizierter, wenn man einen Multiplex in eine Timer-ISR setzt oder in einer Timer-ISR eine Software Uhr hochzählt. Auch wird nichts kompliziert, wenn man in der Receive ISR des UART das Zeichen holt und in einen Ringbuffer stellt. Genauso wie umgekehrt nichts komplliziert wird, wenn man die Ausgabe auf die UART erst mal in einen Buffer stellt und dann Uart Transmit Interrupt getrieben die Zeichen über die UART rausbläst. Und es gibt noch Millionen anderer Beispiele, in denen die Berabeitung eines Ereignisses sinnvollerweise komplett in der ISR abgehandelt wird. Wenn man diese Dinge anders macht, dann wird es kompliziert.
:
Bearbeitet durch User
@ Dr. Sommer > Keine leere Loop? Und was wenn man komplett Interrupt-basiert > programmiert um verschiedene Dinge gleichzeitig tun zu können? Dann ist > die mainloop leer, es sei denn man zählt ein asm("WFI") oder > asm("SLEEP") als "nicht-leer"... Also nur weil man Interrupts verwendet, kann man doch trotzdem alles in das Loop im Main hineinschreiben? Was spricht da dagegen? Ich mach das immer so: ich setze mir in der ISR nur ein Flag und arbeite im loop in der Main alle Flags "ab". Ist keins mehr übrig, wird idle() gemacht. Dazu gibts einen Timer, das alle 100ms oder so den Prozessor aus dem Idle holt, für den kleinen Rest der gepollt werden will. Ich dachte immer, das loop im main wäre genau dazu gut? Oder macht man das so nicht? Wäre echt interessant, wie das andere lösen! Bei mir funktionerts bisher ganz gut, auch mit 10 "Tasks". Besser wäre natürlich ein RTOS mit echtem Multitasking. Zu den Interrupts: Die verbrauchen schon Zeit, diese steht aber im Datenblatt: "Interrupt Latency". Das liegt so im Bereich von 10-20Takten glaube ich, je nach Ziel. Relevant ist es nur dann, wenn man die Interrupts in hoher Frequenz hat, z.B. alle 10µs. Die verlorene Zeit kann man leicht woanders einsparen, z.B. wenn man den DMA verwendet. Das spart auch eine Menge Interrutps.
Humpfdidumpf schrieb: > ich setze mir in der ISR nur ein Flag und arbeite im loop in der Main > alle Flags "ab". Dann brauchst du eigentlich garkeine Interrupts sondern kannst die Interruptflags direkt in der main loop abarbeiten. MfG Klaus
Dr. Sommer schrieb: > Asynchrone, also event-basierte Programmierung gilt schon länger als > schick, das heißt in Event-Handlern - also ISR's - auf das Ereignis > reagieren und dort etwas tun. Die mainloop tut dann nichts als warten. Besser und kürzer kann man den aktuellen Schwachsinn nicht formulieren. "Gilt als schick". Allein diese Aussage sagt schon alles. Normaler Verstand wird durch Herdentrieb ersetzt. Nichts gegen JavaScript z.B. im Browser. Wenn aber mittlerweile so eine Halbsprache in Form von nodejs, und dazu noch eventgetrieben, auf dem Server zum Einsatz kommt und gefeiert wird, dann kann man PHP und Konsorten als Hochsicherheitstrakt bezeichnen. Interrupts auf einem Controller sollte man schon mit Bedacht einsetzen und auch auf das nötige Beschränken. Ich stelle mir gerade vor wie jemand im Interrupt auf ein eingehendes Ethernetpacket reagiert und eine Webseite ausliefern will.... Wenn dann noch ein paar zeitkritische Interrupts nötig sind um z.B. nur einen popeligen DS18S20 abzufragen geht der Eiertanz los. Soll das dann auch aus dem Ethernethandler bedient werden? Mann oh Mann, hoffentlich kriege ich sowas nicht mit dem nächsten Auto angedreht.
Takao K. schrieb: > 95% aller Programme verwenden eine Main loop. Und was machen die anderen 5%? Mir ist noch nie ein µC-Programm über den Weg gelaufen, das sich beendet. Ich wüßte auch keinen Anwendungsfall dafür. Dr. Sommer schrieb: > Takao K. schrieb: >> Programmcode gehoert nicht in die Interruptroutine. Also sofort wieder >> verlassen. > Warum? Sind Interrupts irgendwie böse? Man sollte sie zumindest möglichst kurz halten und am besten keine non-inline-Funktionen aufrufen. In einer UART-Rx-ISR das empfangene Zeichen in einen Puffer eintragen oder in einer Timer-ISR eine Uhr hochzählen ist noch ok, aber viel mehr würde ich da nicht machen. Die eigentliche Arbeit gehört ins Hauptprogramm oder bei einem RTOS in Tasks. >> Komplett Interrupt-basiert? Entweder verwendest du Interrupts, und dann >> wird die Programausfuehrung wohl durch einen Timer kontrolliert >> (Zeitscheiben Verfahren). Oder du verwendest keine Interrupts. > Timer nur wenn man Delays braucht. Ansonsten halt bei Interrupts auf das > geschehene Ereignis reagieren, eine Aktion ausführen und auf den > nächsten Interrupt warten. Bei sehr einfachen Programmen könnte man sich das vorstellen. > Asynchrone, also event-basierte Programmierung gilt schon länger als > schick, das heißt in Event-Handlern - also ISR's - auf das Ereignis > reagieren und dort etwas tun. Die mainloop tut dann nichts als warten. Die ISRs "handlen" keine Events, sondern generieren sie. In der Main-Loop laufen dann alle Events zusammen, und da werden sie dann "gehanlet". Das ist genau das selbe Konzept wie bei GUI-Anwendungen auf dem PC.
@Klaus das habe ich auch schon versucht, das geht auch. Es hat aber so sein Tücken, hauptsächlich wegen der Stromsparmodi und dem Echtzeitzeugs. Wenn man das so macht, kann man Idle oder Sleep nicht gut verwenden, weil eine zeitnahe Abarbeitung der Flags nicht funktioniert- man hat immer das Delay bis zum nächsten "Systemtick". Alles reagiert im Endeffekt mit dem Systemtick verzögert. Man kann natürlich den Tick kürzer machen, das kostet aber Strom. Und dann sind da noch die zeitkritischen Sachen, z.B. Buffer ausleeren und ähnliches, das in der ISR gemacht wird. Hat man keine ISR, muss das main loop schnell genug durchlaufen. Verwendet man jetzt Interrupts, reagiert das System schnell und braucht wenig Energie. Die Kombination hats für mich gebracht. Sicher gibt es aber elegantere Lösungen. Allgemeingültige Lösungen gibt es ja sowieso nie... Kann jemand dazu ein gutes Buch empfehlen?
Dr. Sommer schrieb: > Keine leere Loop? Und was wenn man komplett Interrupt-basiert > programmiert um verschiedene Dinge gleichzeitig tun zu können? Dann ist > die mainloop leer Dann machst Du Dir den Vorteil der Interrupts ja wieder kaputt. Interrupts können das Main unterbrechen, aber wenn das Main leer ist, muß wieder jeder auf den anderen warten, bis der fertig ist. D.h. Du kannst Dir die Interrupts gleich schenken und im Main nur die Flags pollen. Und das ist sogar schneller, da Du keine Registerrettungs und Restore Arie veranstalten mußt. Jede Mainroutine kann die Scratchpadregister einfach benutzen, ohne Push/Pop. Der Interrupt weiß aber nicht, daß das Main leer ist, er muß alles erst sichern. In der Praxis sieht man eine leere Mainloop daher nur bei Anfängern, die nur einfache Aufgaben programmieren. Bei komplexeren Anwendungen hat der Verzicht auf einen Ausführungslevel nur Nachteile.
Rolf Magnus schrieb: > Die ISRs "handlen" keine Events, sondern generieren sie. In der > Main-Loop laufen dann alle Events zusammen, und da werden sie dann > "gehanlet". Das ist genau das selbe Konzept wie bei GUI-Anwendungen auf > dem PC. Hallo, also ISRs generieren keine events, sie setzen bestenfalls Flags. Eventhandler warten auf Events ohne die CPU zu benühen, also ohne Polling. Gruß, Michael
Michael Appelt schrieb: > Rolf Magnus schrieb: >> Die ISRs "handlen" keine Events, sondern generieren sie. In der >> Main-Loop laufen dann alle Events zusammen, und da werden sie dann >> "gehanlet". Das ist genau das selbe Konzept wie bei GUI-Anwendungen auf >> dem PC. > > Hallo, > > also ISRs generieren keine events, sie setzen bestenfalls Flags. Das kommt sicher drauf an, wie man "Event" definiert. Ein von der ISR gesetztes Flag ist letztendlich auch nur eine sehr einfache Form eines Events. In komplexeren Fällen sind es dann eigene Objekte, die in einer Queue zwischengespeichert werden und ggf. noch zusätzliche Informationen enthalten. > Eventhandler warten auf Events ohne die CPU zu benühen, also ohne > Polling. Eventhandler können blockierend sein oder auch nicht, je nach Anwendungsfall. Und auch die Mainloop auf dem AVR kann ohne CPU-Last warten, wenn man den sleep-Mode benutzt.
Karl Heinz schrieb: > Lies lieber erst mal eine zeitlang mit, dann weißt du auch gegen wen du > argumentierst. Die Leute hier, die haben nämlich Ahnung von dem was sie > tun. Die machen das schon 'ein paar' Jährchen lang. Beruflich. > Ja mach ich. > In einer ISR wird getan, was zu tun ist. Und ja, das kann auch bedeuten > eine (kurze) Bearbeitung eines Sachverhaltes in die ISR zu setzen. > > Wenn es länger dauert, dann wird ein Jobflag bemüht. > Die Aussage "alles wird immer und unbedingt immer in einer ISR gemacht" > ist genauso falsch wie "man darf gar nichts in einer ISR machen ausser > ein Flag zu setzen". Man entscheidet im Einzelfall, welche Strategie > implementiert wird. > Ist schon richtig. Hab ich ja nicht gesagt "Man darf nichts in einer ISR machen". Nach einiger Zeit bewerte ich Programme als besser wartbar, wenn die ISR/ ISRs klein gehalten werden. So ca. 200 Programme. >> Insbesondere wenn mehrere Interrupts moeglich sind, wird die >> Programmierung sonst langsam und kompliziert. > > Quatsch. > Überhaupt nichts wird deswegen komplizierter, wenn man einen Multiplex > in eine Timer-ISR setzt oder in einer Timer-ISR eine Software Uhr > hochzählt. Wenn der Programmierer qualifiziert ist, kann es schon in Ordnung sein. Hatte ich aber bereits in der ISR und seit einiger Zeit mache ich es nicht mehr. Zugegebenermassen habe ich mich auch veraendert, also z.B. Assembler aufgegeben, und den Sprung von 8bit zu 32bit gemacht. Wenn ich Assemblerprogrammierung miteinbeziehe programmiere ich seit Ende 1990er Jahre. Allzu dramatisch ist es garnicht mit ISR, also in den meisten Faellen, insofern der Programmierer einigermassen qualifiziert ist, kann es wohl weitgehend egal sein, vielleicht nur eine Frage des Programmierstils. Ohne den Programmcode zu sehen, moechte ich es nicht bewerten im Sinne "Darf man nicht oder sollte man nicht machen".
Michael Appelt schrieb: > also ISRs generieren keine events, sie setzen bestenfalls Flags. Mit dieser Verallgemeinerung bin ich nicht einverstanden. So ist das Quatsch. Denn ich werde beispielsweise die Timer-ISR, die eine LED-Matrix multiplext ganz sicher nicht mittels Jobflag und Behandlung in der Main-Loop machen. Die Antwort lautet immer noch: Es hängt davon ab. Wenn die notwendige Behandlung kurz ist und nicht viel Zeit verbraucht oder wenn sie unbedingt schnellstmöglich erfolgen muss, dann mache ich die Behandlung des Ereignisses direkt in der ISR. Genauso, wenn ich die Zeit habe. Es ist nämlich recht sinnlos eine Timergetriebene blinkende LED mittels Job-Flag in der Mainloop behandeln zu lassen. Selbst wenn die Behandlung etwas länger dauert (ich rede immer noch von Sekundenbruchteilen), es aber im Gesamtsystem absolut nicht zeitkritisch ist, dann gebe ich dem einfacheren Code ebenfalls den Vorzug, als einer vollgestopften Main-Loop.
Takao K. schrieb: >> Quatsch. >> Überhaupt nichts wird deswegen komplizierter, wenn man einen Multiplex >> in eine Timer-ISR setzt oder in einer Timer-ISR eine Software Uhr >> hochzählt. > > Wenn der Programmierer qualifiziert ist, kann es schon in Ordnung sein. Nein, nicht wenn der Programmierer "qualifiziert" ist. Ein einer Led-Matrix ist es wichtig, dass das Multiplexing regelmässig und zuverlässig erfolgt. Bis auf absolute Notfälle (Kernkrfatwerk wegen Kernschmelze runterfahren), hat das Priorität vor allem anderen. Das wird komplett in der ISR durchgezogen, denn der Multiplex kann nicht darauf warten, dass dann endlich mal der Messwert vom ADC umgerechnet, aufbereitet und per UART auf den Weg gebracht wurde. In so einem Fall fangen die Probleme genau dann an, wenn du die komplette Multiplex-Funktionalität NICHT in der ISR hast. Während es anders rum (also Multiplexing komplett in der ISR) trivial ist. Selbiges mit einer Software-Uhr. Es ist wichtig, dass das Weiterzählen einer Uhr nicht unterbrochen wird und zuverlässig erfolgt. Sonst verliert man in der Uhr zeit und die Uhrzeit stimmt nicht mehr. Selbiges bei der Generierung von Servo-Signalen. Die Puls müssen zuverlässig rausgehen. Wenn sie sich mal um ein paar µs verzögern (weil vorher noch die Multiplex ISR fertig abgearbeitet wird), dann ist das wesentlich weniger schlimm, als wenn die Servos 30° Ruckler machen, weil per UART unbedingt eine Statusmeldung ausgegeben werden muss oder gerade eine komplexe Bahnberechnung läuft und deshalb die Pulsgenerierung steht, weil die Main-Loop damit beschäftigt ist und erst eine halbe Sekunde später wieder zur Abfrage des Jobflags kommt.
:
Bearbeitet durch User
Rolf Magnus schrieb: > Mir ist noch nie ein µC-Programm über den Weg gelaufen, das sich > beendet. In dem Fall ging es doch um ein Beispielprogramm. Das Ding hat nach dem Ende des Beispiels einfach nichts mehr, was es noch tun könnte. Hab' ich auch schon gemacht. Neustart halt nur mit dem Reset-Knopf. Ist natürlich zugegebenermaßen selten (ich würde 0,05 % statt 5 % dafür ansetzen).
Bei dem was hier abgeht darf man Nachts gar nicht schlafen gehen... Ich versteh euch alle nicht. Ob ein "langer Task" jetzt seine Zeit in der main() oder in der ISR verbraucht, wo ist der Unterschied? Richtige™ Controller können nicht umsonst verschachtelte und priorisierte Interrupts, da kann man die timingkritische Abhandlung vom LED-Blinker oder AKW-Abschalter oder wasauchimmer in einen Hoch-Prioritären Timer-Interrupt etc stecken. Wenn man in ISR's nur Flags setzt kann man sich die ISR auch gleich sparen und in der main() einfach das entsprechende Interrupt-Flag der Peripherie-Einheit auslesen - wozu das Flag "in Software" nochmal duplizieren? Peter Dannegger schrieb: > Jede Mainroutine kann die Scratchpadregister einfach benutzen, ohne > Push/Pop. Der Interrupt weiß aber nicht, daß das Main leer ist, er muß > alles erst sichern. Beim AVR sind ISR-Entries's vielleicht langsam, aber beim Cortex-M z.B. sinds 12 Takte inkl. automatischem Push... Rolf Magnus schrieb: > Die ISRs "handlen" keine Events, sondern generieren sie. Man kann auch den Interrupt an sich als Event betrachten, und die ISR als Event-Handler... Solche asynchrone/Event/Callback-gesteuerte Programmierung ist sehr beliebt da sie jederzeit sofortige Reaktionen auf Evens ermöglicht (da die Behandlung im Callback geschieht) und nicht erst auf ein Freiwerden der mainloop gewartet werden muss (falls die grad mit was langem beschäftigt ist). old man schrieb: > Besser und kürzer kann man den aktuellen Schwachsinn nicht formulieren. Dieser aktuelle Schwachsinn wird seit Dekaden so gemacht. z.B. am PC - keiner mag Programme, die bei einer I/O-Operation (wie read) blockieren und nicht bedienbar sind, wenn das Programm auch asynchron auf die Fertigstellung warten und währenddessen weiterlaufen könnte. > Interrupts auf einem Controller sollte man schon mit Bedacht einsetzen > und auch auf das nötige Beschränken. Ich stelle mir gerade vor wie > jemand im Interrupt auf ein eingehendes Ethernetpacket reagiert und eine > Webseite ausliefern will.... Wenn dann noch ein paar zeitkritische > Interrupts nötig sind um z.B. nur einen popeligen DS18S20 abzufragen > geht der Eiertanz los. Soll das dann auch aus dem Ethernethandler > bedient werden? Wer Ethernet an nem AVR ohne Interrupt-Prios macht ist halt selber schuld. Ansonsten die Verarztung vom DS18S20 in einen Höher-prioritäten Interrupt stecken. old man schrieb: > Mann oh Mann, hoffentlich kriege ich sowas nicht mit dem > nächsten Auto angedreht. Keine Sorge, da ist nur klicki-bunti-grafische Programmierung verbaut...
Dr. Sommer schrieb: >> Wie gesagt, Sache des Startup-Codes. > Ja, und der wird vom Mikrocontroller-Hersteller geliefert. Der von ST Um nochmal zum Thema zurückzukommen. Diese Aussage ist absoluter Schwachsinn. Der Startupcode gehört zu den Teilen um den ich mich als Programmierer schon noch selbst kümmern sollte. Und wenn ich ein Beispiel oder Default vom MC-Hersteller oder dem Hersteller der IDE oder sonstwas verwende, was da passiert sollte man schon wissen. Wenn man das gemacht hat erübrigt sich die Frage von ganz oben eigentlich von selbst. Dr. Sommer schrieb: > Dieser aktuelle Schwachsinn wird seit Dekaden so gemacht. z.B. am PC - > keiner mag Programme, die bei einer I/O-Operation (wie read) blockieren > und nicht bedienbar sind, wenn das Programm auch asynchron auf die > Fertigstellung warten und währenddessen weiterlaufen könnte. Was hat das eine mit dem anderen zu tun? Es gibt auch Möglichkeiten mit synchronen IOs zu arbeiten und trotzdem nicht zu blockieren. Dass eine heutige GUI eventgetrieben ist brauchst du sicherlich keinen mehr erläutern. Darum ging es aber nicht. Der Einsatz von RTOS-Systemen auf einem MC oder der Multithreadprogrammierung auf dem PC ist unter anderem auch entstanden weil große Projekte die extensiv asynchron arbeiten irgendwann unwartbar werden. Für den entsprechenden Zweck das richtige Modell zu wählen ist die Kunst des Softwarearchitekten. Sich danach zu richten was gerade schick ist zeugt nicht von Intelligenz und darum ging es mir. Leider wird gerade mit nodejs wieder so eine neue Kuh durchs Dorf getrieben die wiedermal verspricht das allein selig machende zu sein. Dr. Sommer schrieb: > Man kann auch den Interrupt an sich als Event betrachten, und die ISR > als Event-Handler... Solche asynchrone/Event/Callback-gesteuerte > Programmierung ist sehr beliebt | Womit wir wieder beim Herdentrieb wären...
Y2 schrieb: > Der Startupcode gehört zu den Teilen um den ich mich als Programmierer > schon noch selbst kümmern sollte. Ansichtssache. Es gibt Umgebungen (AVR-GCC / avr-libc bspw.), bei denen ist der mitgelieferte Startup-Code ausreichend gut ausgefeilt, dass er problemlos 99,99 % aller Anwendungen abdeckt. Wenn du eine PC-Applikation schreibst (egal welches OS), führst du dir dann auch erst den Startup-Code des Compiler-Herstellers zu Gemüte?
Y2 schrieb: > Um nochmal zum Thema zurückzukommen. Diese Aussage ist absoluter > Schwachsinn. Der Startupcode gehört zu den Teilen um den ich mich als > Programmierer schon noch selbst kümmern sollte. Ah. Wenn das so ist. Ich würde mal behaupten die meisten C-Programmierer hier im Forum haben noch nie was von Startupcode oder Linkerscript gehört... Und die Leute die bei uns an der HS aus den ETechnik oder Informatik-Studiengängen rauskommen auch nicht. Die freuen sich einfach wenn alle globalen Variablen initialisiert sind und die main() aufgerufen wird. Was will man da auch selber noch machen? Erstmal die ganzen Compiler&Linker Interna lernen, nur um globale Variablen mit seinem eigenen Code initialisieren zu können? Das kann man sich auch sparen. > Was hat das eine mit dem anderen zu tun? Es gibt auch Möglichkeiten mit > synchronen IOs zu arbeiten und trotzdem nicht zu blockieren. Interessant. Wie? Multithreading? Dann hat man die Asynchronität nur auf die Thread-Interkommunikation verschoben. > Der Einsatz von RTOS-Systemen auf > einem MC oder der Multithreadprogrammierung auf dem PC ist unter anderem > auch entstanden weil große Projekte die extensiv asynchron arbeiten > irgendwann unwartbar werden. Kommt drauf an, wenn man ordentlich strukturiert und richtige Statemachines plant geht das. > Für den entsprechenden Zweck das richtige > Modell zu wählen ist die Kunst des Softwarearchitekten. Sich danach zu > richten was gerade schick ist zeugt nicht von Intelligenz und darum ging > es mir. Kategorisch das abzulehnen was scheinbar großen Erfolg hat ist auch nicht unbedingt geschickt. Mikrocontroller-Programmierer haben ja auch eine grundsätzliche Aversion gegen OOP und C++ (weil das im ET-Studiengang nicht gelehrt wird) obwohl das in anderen Bereichen riesigen Erfolg hat - auch nicht besonders schlau. Mit Intelligenz hat das aber alles nichts zu tun - aber lieber erstmal beleidigen um sich seiner eigenen zu versichern, gell? > Leider wird gerade mit nodejs wieder so eine neue Kuh durchs > Dorf getrieben die wiedermal verspricht das allein selig machende zu > sein. Was hat das alles mit nodejs zu tun? Klar gibt es auch sinnlose IT-Moden, aber es gibt auch sinnvolle, sonst wärn wir alle noch bei Binärcodes und Lochstreifen. > Womit wir wieder beim Herdentrieb wären... Wir bewundern dich, intelligenter individueller Event-Verweigerer.
Y2 schrieb: > Der Startupcode gehört zu den Teilen um den ich mich als > Programmierer schon noch selbst kümmern sollte. Warum sollte ich? Ich gehe davon aus, daß der Compiler schon alles richtig macht und hatte noch nie Probleme damit. Ich wähle einfach das Target aus und gut is. Ich hab noch nen alten Keil C51 von 1995, da mußte man in der startup.asm die Größe des externen RAM eintragen, wenn man ihn genullt haben wollte. Heutige Versionen dürften das automatisch machen.
Dr. Sommer schrieb: >> Der Einsatz von RTOS-Systemen auf >> einem MC oder der Multithreadprogrammierung auf dem PC ist unter anderem >> auch entstanden weil große Projekte die extensiv asynchron arbeiten >> irgendwann unwartbar werden. > Kommt drauf an, wenn man ordentlich strukturiert und richtige > Statemachines plant geht das. ... > Mikrocontroller-Programmierer haben ja auch > eine grundsätzliche Aversion gegen OOP und C++ (weil das im > ET-Studiengang nicht gelehrt wird) obwohl das in anderen Bereichen > riesigen Erfolg hat - auch nicht besonders schlau. Irgendwie passt diese Logik für mich nicht zusammen. Auf den Einsatz von RTOS auf MC verzichten, aber OOP favorisieren. Für mich bringt ein RTOS mehr Ordnung in eine MC-System als OOP, weil funktionelle Zusammenhänge und Abläufe beeinander bleiben. Ich erkenne die Vorteile von OOP an, aber nicht unbedingt im Zusammenhang mit MCs.
:
Bearbeitet durch User
Klaus Falser schrieb: > Für mich bringt ein RTOS mehr Ordnung in eine MC-System als OOP, weil > funktionelle Zusammenhänge und Abläufe beeinander bleiben. In die Abläufe, ja, kann sein. Aber eigentlich ging es im ganzen Thread gar nicht um RTOS. Wenn man fragt ob man komplett in Interrupts oder auch in der main() arbeitet ist "RTOS" nicht die einzige Antwort. > Ich erkenne die Vorteile von OOP an, aber nicht unbedingt im > Zusammenhang mit MCs. Gerade da passt OOP doch wunderbar, man hat sogar real existierende Hardware-Objekte die man als OOP-Objekte auffassen kann. zB ein Pin, ein ADC, ein UART. Warum die nicht als Klasse implementieren?
1 | void doIt (Pin p, Uart u) { |
2 | p.setHigh (); |
3 | u.send ("blubb"); |
4 | p.setLow (); |
5 | }
|
6 | |
7 | int main () { |
8 | doIt (PA7, UART0); |
9 | doIt (PA2, UART1); |
10 | }
|
ist doch sehr praktisch und intuitiv.
Jörg Wunsch schrieb: > Ansichtssache. Es gibt Umgebungen (AVR-GCC / avr-libc bspw.), bei > denen ist der mitgelieferte Startup-Code ausreichend gut ausgefeilt, > dass er problemlos 99,99 % aller Anwendungen abdeckt. > > Wenn du eine PC-Applikation schreibst (egal welches OS), führst du dir > dann auch erst den Startup-Code des Compiler-Herstellers zu Gemüte? Jörg, du schreibst Mist und willst provozieren. Mag sein, daß die AVR-Startupcodes gut genug sind, aber die Startupcodes, die üblicherweise bei allen bekannten ARM- und Cortex- Toolchaines dabei sind und/oder von den Herstellern geliefert werden, sind Bockmist. Warum? Weil sie z.B. auf unabgedeckte Interrupts oder Faults mit einem B. enden. Darum! Oberdrauf kommt noch, daß main zumeist per BX aufgerufen wird. Was also passiert bei einem vor Ort werkelnden µC, wenn da mal ne Störung reinrauscht? Er rennt in den Stopp und das auch noch im privilegierten Modus, wo es außer dem Reset keinen Ausweg gibt. Solch einen blödsinnigen Startupcode MUSS man durch was eigenes ersetzen, wenn man Scherereien aus dem Weg gehen will. Ich mache das schon seit langem so, daß solche Faults einfach ein globales Fehlerflag setzen und einen Warmstart auslösen. Dann kann der µC wenigstens reagieren und seine Arbeit wieder aufnehmen. In vielen Situationen ist das lebenswichtig. Und deine Ammerkung zum PC-Geschäft paßt nun mal garnicht. Jeder weiß, daß eine App auf'm PC eben irgendwann beendet wird und allenfalls einen Returncode zurückgibt - deswegen das int main(...), was auf einem µC sinnlos ist, da es kein Zurück zum BS gibt. Du weißt das doch alles, warum schreibst du dann sowas? W.S.
Dr. Sommer schrieb: > int main () { > doIt (PA7, UART0); > doIt (PA2, UART1); > } Und jetzt erklärst du bitte noch, wo das Objektorientierte bei deinem hier zitierten Beispiel ist, gelle? Ich geb dir mal nen Tip: main(...) ... PA7.doTheBlubb; W.S.
W.S. schrieb: > Warum? > Weil sie z.B. auf unabgedeckte Interrupts oder Faults mit einem B. > enden. Was ist ein unabgedeckter Interrupt? Der Startupcode von ST für STM32 z.B. wird vermutlich von 99% der STM32-User hier im Forum verwendet, und scheint auch zu funktionieren. Was machst du falsch, dass der bei dir immer abstürzt? Verkehrtes Linkerscript verwendet? > Darum! Oberdrauf kommt noch, daß main zumeist per BX aufgerufen > wird. Ja und? Wenn der Linker nicht total verwirrt ist, hat er das unterste Bit des "main"-Symbols entsprechend gesetzt s.d. "BX" ganz wunderbar funktioniert. Wo ist das Problem? W.S. schrieb: > Was also passiert bei einem vor Ort werkelnden µC, wenn da mal ne > Störung reinrauscht? Was für eine Störung? Was genau soll die bewirken? > Er rennt in den Stopp und das auch noch im > privilegierten Modus, wo es außer dem Reset keinen Ausweg gibt. Was soll das für ein mystischer Stopp sein? W.S. schrieb: > Solch einen blödsinnigen Startupcode MUSS man durch was eigenes > ersetzen, wenn man Scherereien aus dem Weg gehen will. Oder wenn man als Guru angesehen werden will, weil man Schlangenöl "verkauft". > Ich mache das > schon seit langem so, daß solche Faults einfach ein globales Fehlerflag > setzen und einen Warmstart auslösen. Wie schaffst du das denn im Startup-Code? Meinst du die Fault-Interrupts - die gehören doch nicht in den Startup, sondern in den normalen Code. Und da kann man machen was man will. Dazu braucht man überhaupt keinen angepassten Startup-Code. > Dann kann der µC wenigstens > reagieren und seine Arbeit wieder aufnehmen. In vielen Situationen ist > das lebenswichtig. Ja, wenn man so schlecht programmiert hat dass man ständig Faults produziert! Zum Faults abfangen muss man am Startup-Code gar nichts machen. Kann das sein dass du einfach nur gerne Orakel spielst und irgendwelche haaresträubenden hanebüchenen Behauptungen aufstellst, und damit obskure Programmier"techniken"/praktiken rechtfertigst und dann klugscheißt dass du das so toll kannst? Naja, jeder braucht seine Bestätigung. W.S. schrieb: > Und jetzt erklärst du bitte noch, wo das Objektorientierte bei deinem > hier zitierten Beispiel ist, gelle? Gerne. Die "UART" oder "Pin"-Peripherie-einheit wird durch eine UART/Pin-Klasse repräsentiert und gekapselt. Um einer Funktion mitzuteilen, welche UART-Peripherieeinheit man will, muss man nur die entsprechende Instanz der UART-Klasse übergeben. Bei diversen unobjektorientierten Hardware-API's müsste man gleich eine ganze Reihe an Pointern/Konstanten übergeben, da nur die alle zusammen die Peripherieeinheit repräsentieren; bei ordentlicher Kapselung entspricht eine Peripherieeinheit ("Uart Nr. 0") genau einer "Uart"-Instanz ("UART0"). > Ich geb dir mal nen Tip: > > main(...) > ... > PA7.doTheBlubb; Ja, das passiert wenn man keine Ahnung von OOP hat aber mal was mit Klassen machen will. Wie unschwer zu erkennen war sollte mein Mikro-Beispielcode den Pin solange auf 1 ziehen wie die Übertragung stattfand. Das ist sozusagen ein "Algorithmus", der sowohl Pin als auch Uart benutzt. Der hat weder speziell was mit dem UART noch speziell was mit dem Pin zutun - daher wäre es äußerst komisch, wenn die Funktion doIt eine Funktion der Pin-Klasse wäre (wie bei deinem Code), oder wenn sie Funktion der Uart-Klasse wäre. Was hat die Pin-Klasse auch mit dem Uart zu tun? Was hat die Pin-Klasse mit dem Blubb zu tun? Nichts. Sauberes OOP-Design besagt hier, dass die Pin-Klasse nur die Funktionalität zur Verfügung stellt, die ein Hardware-Pin kann: Auf 0 setzen, auf 1 setzen, Eingang abfragen, vielleicht noch elektrische Parameter wie Pull-Up/Down setzen. Aber nichts mit Blubb über UART senden. Bei OOP kann man durchaus globale ("freie") Funktionen verwenden; aber in meinem Fall sind die Parameter das interessante.
W.S. schrieb: > Jörg, du schreibst Mist und willst provozieren. Mensch, du solltest doch nicht immer in den Spiegel gucken, wenn du postest. :-)
kopfkratz Also "obligatorisch" ist die main loop keineswegs, man kann sie z.B. mit dem Leerlaufprozeß von Betriebssystemen vergleichen. Aber üblicherweise läuft bei einer µC Applikation in der main die Ablaufsteuerung, ob nun als Statemachine mittels switch case oder indem auf einen Tastendruck, Datenaufkommen o.ä. gewartet/reagiert wird. Es gibt sicherlich Abläufe die ausschießlich via IRQ erledigt werden können, z.B. im ADC IRQ einen Poti automatisch abfragen und dann via globaler volatile Variable den gemessenen Wert immer wieder an eine PWM im Timer-IRQ zu übergeben. Nur kostet eine Endlosschleife nix, denn wenn der Compiler merkt das da nix passiert und ansonsten alles Paletti ist kann der das dann auch wegoptimieren. Man gewinnt also nichts kann aber viele Probleme erzeugen wenn man die "obligatorische" Endlosschleife wegläßt.
kopfkratzer schrieb: > Nur kostet eine Endlosschleife nix, denn wenn der Compiler merkt das da > nix passiert und ansonsten alles Paletti ist kann der das dann auch > wegoptimieren. Na die Endlosschleife wegoptimieren wäre nicht so geschickt, weil dann doch wieder die main() zurückkehrt... Eine Endlosscheife ist aber sonst recht billig, ja, typischerweise 1 Instruktion...
kopfkratzer schrieb: > Nur kostet eine Endlosschleife nix, denn wenn der Compiler merkt das da > nix passiert und ansonsten alles Paletti ist kann der das dann auch > wegoptimieren. Nein, kann er nicht, denn dadurch erhält man (aus C-Sicht) ein anderes Programm.
Jörg Wunsch schrieb: > Mensch, du solltest doch nicht immer in den Spiegel gucken, wenn du > postest. :-) Ha, der war gut.. Aber was soll ich machen bei den heutigen Hochglanz-Displays? Ich werde aber beim nächsten Mal intensiver an DICH denken. Dr. Sommer schrieb: > Was ist ein unabgedeckter Interrupt? Ah, du wolltest dich mal wieder hervortun. Aber ich bin ja nett und erklär's dir: Sowas ist ein Interrupt, der vom Nutzprogramm nicht benutzt wird. Dazu zählen auch die meisten CPU-Faults - oder kennst du jemanden, der einen Handler für nen Prefetch-Fault oder nen Datafault in sein Programm schreibt? Und echte Störungen von außen gibt's auch öfter als du denkst. Man kann sowas ignorieren - meistens. Aber wer seinen Job gründlich macht, denkt auch darüber nach und schreibt sich einen besseren Startupcode als den Defaultcode vom Hersteller. Ach ja, den Unterschied zwischen einem BX und einem BLX kennst du offenbar auch nicht. Wir waren ja hier beim Thema Endlosschleife am End von main. Rate mal, wohin die Reise geht, wenn jemand main beendet und main per BX angesprungen wurde. W.S.
W.S. schrieb: > Ah, du wolltest dich mal wieder hervortun. Du bist das beste Vorbild dafür. > Aber ich bin ja nett und erklär's dir: Sowas ist ein Interrupt, der vom > Nutzprogramm nicht benutzt wird. Also ein ganz ordinärer Interrupt+ISR, nur dass der Default-Handler-Code verwendet wird. Und dass dieser Default-Handler typischerweise gar nichts macht und einfach nur sofort zurück kehrt. > Dazu zählen auch die meisten CPU-Faults > - oder kennst du jemanden, der einen Handler für nen Prefetch-Fault oder > nen Datafault in sein Programm schreibt? Du scheinbar, um in so einem Fall einen Reset auszulösen. Ist ja schön, den zu überschreiben - aber das hat immer noch NICHTS mit dem Startup-Code zu tun. Nur weil man eine Fault-Handler-ISR schreibt muss man noch lange keinen eigenen Startup Code schreiben. > Und echte Störungen von außen > gibt's auch öfter als du denkst. Man kann sowas ignorieren - meistens. > Aber wer seinen Job gründlich macht, denkt auch darüber nach und > schreibt sich einen besseren Startupcode als den Defaultcode vom > Hersteller. Was haben die Störungen mit dem Startup-Code zu tun? Wenn du damit meinst, die Fault-Handler zu behandeln - siehe oben. Und woher weißt du dass eine Störung so freundlich ist einen Faulthandler auszulösen, und nicht einfach ein Bit im Register kippt? Wenn du mit "Störsicherheit" kommst musst du noch ganz andere Maßnahmen ergreifen als nur eigene Faulthandler zu schreiben. z.B. einen Prozessor verwenden der für sowas gemacht ist. Wenn ich einen Code sehe der in seinen Fault-Handlern einfach einen Reset auslöst würde ich mal sagen der Autor hat seinen Job nicht ordentlich gemacht, da er wohl Fehler in seinem Code hat die er aber zu finden nicht in der Lage war, und daher einfach im Fault-Handler einen Reset auslöst. Ich würde im Handler, wenns denn sein muss, ein
1 | asm volatile ("bkpt"); while (1); |
schreiben, damit ich im Fehlerfall JTAG anklemmen und debuggen kann. Abstürze sind nämlich meistens, wer hätte es gedacht, Resultat von Programmierfehlern. > Ach ja, den Unterschied zwischen einem BX und einem BLX kennst du > offenbar auch nicht. Doch. Ich hatte nur darauf getippt dass du "BX" vs. "B" meintest. Weiß man ja nicht so genau was in deinem Kopf vorgeht. > Wir waren ja hier beim Thema Endlosschleife am End > von main. Rate mal, wohin die Reise geht, wenn jemand main beendet und > main per BX angesprungen wurde. Wie ich hier ( Beitrag "Re: Ist die obligatorische Endlosschleife am Ende der main Funktion nur Aberglaube?" ) schon erläutert habe, stürzt der Controller ab - bei Verwendung von "BX" statt "BLX" halt nicht nach Rückkehr aus der main(), sondern bei der Rückkehr aus der main - ein großer Unterschied?! Wenn du natürlich unbedingt aus der main() zurückkehren willst, musst du wohl im Startupcode nach dem "BLX main" irgendwas einfügen. Man kann aber auch einfach ein while(1); ans Ende der main() packen und mit "BX" in die main() springen - davon wirds auch nicht weniger "stabil". Ganz davon abgesehen verwendet z.B. der Startuptcode von ST tatsächlich das:
1 | bl main |
2 | bx lr |
Recht sinnlos, das ist das selbe wie "b main". Bis jetzt kam jedenfalls kein sinnvoller Grund von dir, eigenen Startup Code zu schreiben. Fault-Handler kann man, oh wunder, auch in den "normalen" Anwendungscode packen. Wenn du Spaß daran hast auf vage unbegründbare Vermutungen hin irgendwelche "Stabilitätsmaßnahmen" an einem Code vorzunehmen und deinen eigenen Startup-Code zu produzieren um zu demonstrieren dass du es "besser" als die Hersteller kannst, ist das ja schön - aber verwirr nicht die armen Mitleser hier die die Interna des ARM nicht so gut kennen um deinen Unfug als solchen nicht sofort erkennen zu können.
Es geht ohne die obligatorische Endlosschleife und auch ohne Compiler Ergänzung: Beitrag "Re: MSP430 F2013 Problem bei Interrupt von P2"
low power schrieb: > Es geht ohne die obligatorische Endlosschleife und auch ohne Compiler > Ergänzung: Dann zeig mal bitte wie das zB am ARM-GCC für Cortex-M3 geht. Und woher weißt du dass bei dem dort verwendeten MSP430-Compiler keine Endlosschleife im Startupcode ist?
Dr. Sommer schrieb: > Und woher weißt du dass bei dem dort verwendeten MSP430-Compiler > keine Endlosschleife im Startupcode ist? Vielleicht weil das Programm in main ankommt? :o)
Johann L. schrieb: > Vielleicht weil das Programm in main ankommt? :o) NACH Aufruf&Rückkehr der main() natürlich, Nasendreher :-P
Dr. Sommer schrieb: > Und woher > weißt du dass bei dem dort verwendeten MSP430-Compiler keine > Endlosschleife im Startupcode ist? Der MSP430 restauriert mit verlassen der ISR das SR. Dort ist der LPM verzeichnet. Der LPM wird wieder eingenommen ohne das Code aus dem Hauptprogramm ausgeführt wird. Da brauchts keine Endlosschleife.
Dr. Sommer schrieb: > Johann L. schrieb: >> Vielleicht weil das Programm in main ankommt? :o) > NACH Aufruf&Rückkehr der main() natürlich, Nasendreher :-P Das Prinzip hab ich schon verstanden; ist wie bei Windows: Zum Beenden muss man auf "Start" gehen".
low power schrieb: > Der MSP430 restauriert mit verlassen der ISR das SR. Dort ist der LPM > verzeichnet. Der LPM wird wieder eingenommen ohne das Code aus dem > Hauptprogramm ausgeführt wird. Da brauchts keine Endlosschleife. Ach das, ja, geschickt das dafür zu nutzen. Cortex-M3 kann das prinzipiell auch, aber durch diverse Events kann der Core "versehentlich" doch mal aus dem WFI zurückkehren - also braucht man doch eine Schleife...
Die ganze Diskussion ist für ... das Tier das die Mäuse fängt. Auf einem µC gibt es schlicht nichts, wohin main() zurückkehren könnte. Deswegen ist eine main() Funktion, die beendet werden kann, auch voll- kommen sinnfrei. Jedes sinnvolle µC-Programm hat also die Endlosschleife in main() auf die eine oder andere Weise schon drin. Abgesehen von irgendwelchen Test-Codeschnipseln vielleicht, wo man dann eine Endlosschleife explizit hinschreiben würde. XL
Also eine Sache interessiert mich (als STM32-Newbie) jetzt aber doch: W.S. schrieb: > Was also passiert bei einem vor Ort werkelnden µC, wenn da mal ne > Störung reinrauscht? Er rennt in den Stopp und das auch noch im > privilegierten Modus, wo es außer dem Reset keinen Ausweg gibt. Aber der Watchdog würde schon noch zuschlagen, oder?
Axel Schwenke schrieb: > Die ganze Diskussion ist für ... das Tier das die Mäuse fängt. > > Auf einem µC gibt es schlicht nichts, wohin main() zurückkehren könnte. Wie jetzt schon länger diskutiert - doch, es gibt den Startupcode. > Deswegen ist eine main() Funktion, die beendet werden kann, auch voll- > kommen sinnfrei. Das hängt vom Startupcode ab. > Jedes sinnvolle µC-Programm hat also die Endlosschleife in main() auf > die eine oder andere Weise schon drin. Abgesehen von irgendwelchen > Test-Codeschnipseln vielleicht, wo man dann eine Endlosschleife explizit > hinschreiben würde. Oder komplett Interrupt-basierten Programmen. Bronco schrieb: > W.S. schrieb: >> Was also passiert bei einem vor Ort werkelnden µC, wenn da mal ne >> Störung reinrauscht? Er rennt in den Stopp und das auch noch im >> privilegierten Modus, wo es außer dem Reset keinen Ausweg gibt. > > Aber der Watchdog würde schon noch zuschlagen, oder? W.S. blubbert einfach irgendeinen Unfug von dem er keine Ahnung hat. Das sieht man schon daran, dass er jedes Mal, wenn er wiederlegt wurde, nicht mehr antwortet. Es gibt keinen "Stopp". Es gibt wenn schon Fault-Handler. Und die kann man im normalen Anwendungsprogramm überschreiben. Die Fault-Handler "gegen Störungen" zu verwenden ist so wie eine Sirene in der Bank zu installieren und die mit einem Knopf zu versehen und den mit "Bankräuber bitte hier drücken" zu beschriften.
Dr. Sommer schrieb: > Die Fault-Handler "gegen Störungen" zu verwenden ist so wie eine Sirene > in der Bank zu installieren und die mit einem Knopf zu versehen und den > mit "Bankräuber bitte hier drücken" zu beschriften. Mach's mal halblang. Du bist doch einer von denjenigen hier die immer wieder gegen die intervenieren die tiefer in die Materie einsteigen und sich nicht blind auf das verlassen was "man so macht". Wenn du heute einen größeren Cortex M3 oder M4 etwas auslastest, dann hast du in der Regel einen Sack voll fremder Libs dabei. Ob Ethernet, Filesysteme, RTOS oder was weiß ich. Damit ist es auch ziemlich ausgeschlossen fehlerfreie Programme zu schreiben. Keines dieser Programme kann jemals in allen erdenklichen Formen getestet werden. In diesem Fall ist es aber sehr häufig sinnvoller, der Controller macht einen Reset als in einer Endlosschleife fest zu hängen. Frag doch mal bei Arm nach warum sie so einen "Blödsinn" erfunden haben, wenn man ihn doch nicht benutzen darf. Du bist Verfechter von C++ und der Verwendung der libstdc++. Bist du dir sicher damit immer die volle Kontrolle über Stack und Heap zu haben? Unter allen denkbaren Umständen? Dr. Sommer schrieb: > W.S. blubbert einfach irgendeinen Unfug von dem er keine Ahnung hat. Das > sieht man schon daran, dass er jedes Mal, wenn er wiederlegt wurde, > nicht mehr antwortet. Nö, ist ganz einfach: Der Klügere gibt nach! Es gibt Leute die müssen ihre Meinung mit allen Mitteln als die einzig richtige verbreiten. Lass mich raten. Angestellt an einer UNI? Spätestens wenn man das schnallt ist die Zeit gekommen die Diskussion zu beenden.
Dr. Winter schrieb: > In > diesem Fall ist es aber sehr häufig sinnvoller, der Controller macht > einen Reset als in einer Endlosschleife fest zu hängen. Aha. Das ist aber was ganz anderes. Das ist keine "elektrische Störung" oder "kosmische Strahlung", sondern ganz einfach ein Programmierfehler. Ja, in so einem Fall kann man einen Reset auslösen. > Frag doch mal > bei Arm nach warum sie so einen "Blödsinn" erfunden haben, wenn man ihn > doch nicht benutzen darf. Hauptsächlich zum Debuggen, um zu sehen was schief gelaufen ist. Ich habe auch nie gesagt dass man die FaultHandler nicht benutzen darf; lediglich dass sie keinen sinnvollen Schutz gehen elektrische Störungen bieten (Programmfehler würde ich nicht als "störung" rechnen) und eigentlich hauptsächlich, dass man den Startupcode dafür nicht anfassen muss. > Du bist Verfechter von C++ und der Verwendung der libstdc++. Bist du dir > sicher damit immer die volle Kontrolle über Stack und Heap zu haben? Über den Stack hat man in C(++) grundsätzlich keine Kontrolle, die hat der Compiler. Und was hat die C++ stdlib mit dem Heap zu tun? Und warum entzieht die einem die "kontrolle über den Heap", was auch immer das heißen soll? > > Dr. Sommer schrieb: >> W.S. blubbert einfach irgendeinen Unfug von dem er keine Ahnung hat. Das >> sieht man schon daran, dass er jedes Mal, wenn er wiederlegt wurde, >> nicht mehr antwortet. > > Nö, ist ganz einfach: Der Klügere gibt nach! "Der Himmel ist grün-gelb gepunktet! Und jetzt gebe ich nach weil ich der Klügere bin." > Es gibt Leute die müssen > ihre Meinung mit allen Mitteln als die einzig richtige verbreiten. ja, und W.S. ist ein Paradebeispiel. Bei jeder (un)denkbaren Gelegenheit hält er den Leuten seine unglaublich vermurkste "Lernbetty" unter die Nase mit "guckt mal was ich tolles gemacht hab macht es mir genau so nach"! > Lass > mich raten. Angestellt an einer UNI? Spätestens wenn man das schnallt > ist die Zeit gekommen die Diskussion zu beenden. Weil man als einziger Entwickler in einem Unternehmen nicht hinterfragt wird und da beliebigen Unsinn verzapfen kann, oder wie kommst du darauf?
Dr. Sommer schrieb: > Axel Schwenke schrieb: >> Die ganze Diskussion ist für ... das Tier das die Mäuse fängt. >> >> Auf einem µC gibt es schlicht nichts, wohin main() zurückkehren könnte. > Wie jetzt schon länger diskutiert - doch, es gibt den Startupcode. Albern. Der Startup-Code ist da aus rein technischen Gründen. Weil jemand oder etwas z.B. den Stack initialisieren muß oder die statischen Variablen. Aber es gibt genau gar keinen Grund, warum der Startup-Code main() als Funktion aufrufen sollte (was dann zumindest technisch eine Rückkehr erlauben würde) statt main() einfach anzuspringen oder vom Linker direkt hinter das Ende des Startup-Codes linken zu lassen. Auch sonst tut auf einem µC der Startup-Code nichts von all dem was man in einem hosted environment voraussetzen würde, wie z.B. Argumente zu übergeben (argc, argv) oder den Returnwert von main() auszuwerten (wie auch?). Das Verhalten des C-Laufzeitsystems auf einem µC ist definiert von "der Programmablauf startet mit der main() Funktion" bis zum Ende von main(). Alles was davor oder danach passiert, ist schlicht undefiniert. Und wenn nach dem Ende von main() undefiniertes Verhalten folgt, dann darf main() eben nicht enden. XL
Natürlich gibt es Anwendungen, die den return-Wert von main auswerten, z.B. wenn man Tests laugen lässt. Typisches Beispiel ist, die Toolchain zu testen und Code in einem Simulator laufen zu lassen (zumindest die Laufzeit-Tests, für die reinen Compile-Tests braucht's natürlich keinen Simulator). In dem Falle tut man gut daran, wenn die Tools sich möglichst standardkonform verhalten. Den Fall man man als esotherisch ansehen, aber wer von euch will denn gerne eine ungesestete Toolchain verwenden? Bzw. eine Toolchain, bei deren Testergebnissen man sagt: Ok, 3-5% der ca. 100000 Tests (z.B. C-Tests für gcc) gehen durch, der Rest macht irgendwas und ist und wurscht?
Rolf Magnus schrieb: > Und was machen die anderen 5%? Mir ist noch nie ein µC-Programm über den > Weg gelaufen, das sich beendet. Ich wüßte auch keinen Anwendungsfall > dafür. Ich hab sowas schon mal geschrieben. In dieser Anwendung hat sich die Schaltung zum Stromsparen zum Schluß einfach selbst den Saft abgedreht. Der letzte Befehl war einfach ein sich auf low schaltender Pin der ein Relais ausschaltet. Zum einschalten muss dann ein Taster gedrückt werden, der die Stromzufuhr wieder herstellt ;)
mec schrieb: > Der letzte Befehl war einfach ein > sich auf low schaltender Pin der ein Relais ausschaltet. Zum einschalten > muss dann ein Taster gedrückt werden, der die Stromzufuhr wieder > herstellt ;) Und das Relais braucht zur An-Zeiten weniger Strom als der Mikrocontroller dauerhaft im tiefsten Sleep-Mode? Und was passiert in der Zeit zwischen Pin-Abschalten und Strom aus? Relais sind langsam...
Dr. Sommer schrieb: > Und das Relais braucht zur An-Zeiten weniger Strom als der > Mikrocontroller dauerhaft im tiefsten Sleep-Mode? Was hat das jetzt mit der Endlosschleife zu tun? Kaum nennt dir jemand ein tatsächliches Beispiel, musst du lieber gleich das Prinzip in Frage stellen, statt es so zu akzeptieren? Seltsame Logik. Axel Schwenke schrieb: > Jedes sinnvolle µC-Programm hat also die Endlosschleife in main() Wenn du jetzt zurück auf "Los!" gehst, wirst du aber feststellen, dass der Thread mit einer Referenz auf ein einfaches Demo-Programm begonnen hat. Das hat an seinem Ende weiter nichts, was es noch hätte tun können, eben daher hat es sich regulär beendet. Dass man das in einer realen Applikation nicht machen würde, darüber besteht wohl allenthalben völlige Einigkeit.
Jörg Wunsch schrieb: > Was hat das jetzt mit der Endlosschleife zu tun? mec schrieb: > Der letzte Befehl war einfach ein sich auf low schaltender Pin der ein > Relais ausschaltet. Das klingt ein bisschen so als sei dort keine Endlosschleife und als würde die main() zuruckkehren
Herr Doktor (falls man dich überhaupt so anreden darf ...), du redest komplett dran vorbei. Du hast nicht mehr über die (bei ihm nicht vorhandene) Endlosschleife geredet, sondern angefangen, sein grundsätzliches Konzept in Frage zu stellen ("Ist das Relais denn überhaupt sinnvoll?"). Das nur, weil dir einer ein reales Beispiel genannt hat, bei dem in einer praktisch realisierten Applikation am Ende von main() eben mal keine Endlosschleife steht — weil sie sowieso nicht mehr erreicht worden wäre. Du kannst nicht ruhig schlafen, wenn du mal nicht recht hast, oder wie ist das?
Jörg Wunsch schrieb: > Herr Doktor (falls man dich überhaupt so anreden darf ...) Rechtlich leider nicht, aber daher bin ich ja im Internet, da weiß das keiner. > Du hast nicht mehr über die (bei ihm nicht vorhandene) Endlosschleife > geredet, sondern angefangen, sein grundsätzliches Konzept in Frage zu > stellen ("Ist das Relais denn überhaupt sinnvoll?"). Ich fragte mich lediglich was passiert zwischen dem Befehl, der den Relais-Pin abschaltet, und der Rückkehr aus der main(). Ist der µC tatsächlich sofort aus bevor die main() zurückkehrt? Oder ist da noch eine mechanisch und vielleicht kapazititv bedingte Latenz die den µC noch etwas länger laufen lässt? Weil in dem Fall könnte - in starker Abhängigkeit vom Startupcode - der Controller vor dem Strom-Aus noch irgendeinen undefinierten Unsinn machen, wie Flash-Speicher löschen oder was auch immer. > Du kannst nicht ruhig schlafen, wenn du mal nicht recht hast, oder wie > ist das? Nein. Sonst glaubt mir ja auch keiner, daher versuch ich es hier.
Micha schrieb: > wahrscheinlich 'ne Frage, bei der die Antwort "nur" von > akademischem > Interesse ist... > > Bisher hab ich immer, nach kritiklos übernommener Lehrmeinung, meine > main mit einer Endlosschleife beendet. > Das hängt ganz allein vom Startup Code ab. Was auf einem uC passiert, wenn man aus main() zurückspringt ist unterschiedlich. Die Möglichkeiten wären unter anderem: Watchdog-Reset, Soft-Reset, Endlosschleife, Halt... Der Startup Code den ich in der Vergangenheit selbst geschrieben habe, geht beispielsweise folgendermaßen vor: a) Aufruf der Destruktoren statischer Objekte b) Aufruf der registrierten atexit() Funktionen c) Deaktivieren aller Interrupts und nebenläufiger Ereignisse d) Wachdog Enable und Start zum Triggern eines Resets e) Endlosschleife bis Reset erfolgt Allerdings, in eine reine Endlosschleife zu laufen (Implementierung der Faulpelze) wenn man aus main() zurückspringt ist streng genommen nicht Standard-konform: Es werden weder statische Objekte zerstört noch atexit() Funktionen aufgerufen.
Dr. Sommer schrieb: > Unsinn machen, wie Flash-Speicher löschen oder was auch immer. Wenn du dir schon mal so angesehen hast, welchen Aufwand man treiben muss, um den Flash zu programmieren, dann gewinnst du eher im Lotto, als dass sich da versehentlich ein Byte umprogrammieren würde. ;-) Aber gut, prinzipiell ist der Einwand natürlich berechtigt: man sollte sich wenigstens Gedanken gemacht haben, was mit der in den Stützkondensatoren gespeicherten Energie noch passieren kann, nachdem die eigentliche Versorgung abgeschaltet ist. Aber wer macht sich diese Gedanken eigentlich für den normalen Ausschaltvorgang mit einem mechanischen Schalter? Ob wirklich alle Leute den Brownout-Reset aktivieren, damit der Controller in einen definierten Reset fährt, sobald die Spannung an den Stützkondensatoren unter den Wert für einen definierten Betrieb abrutscht? ;-)
Jörg Wunsch schrieb: > Wenn du dir schon mal so angesehen hast, welchen Aufwand man treiben > muss, um den Flash zu programmieren, dann gewinnst du eher im Lotto, > als dass sich da versehentlich ein Byte umprogrammieren würde. ;-) Hoffentlich gilt das auch für alle anderen angeschlossenen Speicherbausteine, FET's, etc... Mir persönlich wäre etwas unwohl dabei wenn mein Controller regelmäßig (beim Ausschalten) etwas undefiniertes macht, und das nur um die eine Instruktion für die Endlosschleife zu sparen.
Dr. Sommer schrieb: > Mir persönlich wäre etwas unwohl dabei wenn mein Controller regelmäßig > (beim Ausschalten) etwas undefiniertes macht Etwas undefiniertes wird er nur bei sehr schlechten Frameworks machen. Wie oben bereits geschrieben, falls es ein AVR war und die avr-libc, dann ist die Endlosschleife garantiert, denn dort wird ganz C-standardgemäß danach nach main() noch exit() gerufen. Man könnte auch argumentieren, dass dort die explizite (leere) Endlosschleife pure Platzverschwendung ist, so wie der Test bei:
1 | if (p != 0) free(p); |
Aber siehe oben: hast du dann auch den Brownout-Reset aktiviert? Denn beim Runterfahren der Spannung macht unterhalb der garantierten Grenze der Kern dann irgendwann tatsächlich etwas undefiniertes, da beispielsweise der Flash nicht mehr korrekt ausgelesen werden kann, die Gatter der CPU aber nach wie vor noch ein wenig weiterwerkeln.
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.