-         
Seite 3 von 6 ErsteErste 12345 ... LetzteLetzte
Ergebnis 21 bis 30 von 59

Thema: Interrupt-Abfrage >>> Routine vereinfachen

  1. #21
    Erfahrener Benutzer Robotik Einstein Avatar von 021aet04
    Registriert seit
    17.01.2005
    Ort
    Niklasdorf
    Alter
    31
    Beiträge
    4.823
    Anzeige

    Wenn du mechanische Schaltkontakte nutzt (die Prellen) ist es besser wenn du einen Timer zum Auslesen verwendest.

    Ich hätte noch eine Verbesserung des Codes. Du prüfst mit einer If auf "0" und mit einer If auf "1". Bei einer If-Abfrage gibt es aber auch noch "else", das wird immer angesprungen wenn das If-Ergebnis nicht "1" ist.
    Ein Beispiel als Pseudocode:
    Code:
    if (PB3 == 1)
    {
       Led = 1
    }
    
    if (PB3 != 1)
    {
       Led = 0
    }
    ist das gleiche wie:
    Code:
    if (PB3 == 1)
    {
       Led = 1
    }
    else
    {
       Led = 0
    }
    das ist auch das gleiche:
    Code:
    if (PB3)
    {
       Led = 1
    }
    else
    {
       Led = 0
    }
    Das if wird immer ausgeführt wenn das Ergebnis wahr ist und das ist immer der Fall wenn es nicht 0 ist. Wenn du auf "0" abfragen willst musst du aber "if (Variable == 0)" schreiben. Bei der while Schleife ist es das gleiche, deswegen sind auch "while(1)" Schleifen endlos.

    MfG Hannes

  2. #22
    Benutzer Stammmitglied
    Registriert seit
    07.06.2019
    Beiträge
    82
    Zitat Zitat von Holomino Beitrag anzeigen
    löst das schon während der laufenden ISR den nächsten Interrupt aus - nicht sofort, da beim Betreten einer ISR implizit das "global interrupt enabled"-Flag zurückgesetzt wird. Das gesetzte Statusflag wirkt aber sofort nach Verlassen der ISR (hier wird implizit auch wieder das "global interrupt enabled" gesetzt) und Dein Programm springt direkt wieder in die gleiche ISR. Das merkst Du z.B., wenn Du in Deiner ISR einen Zähler inkrementierst. Der springt dann mal zwei oder mehr Schritte pro Tastendruck, je nachdem, wie lange das Prellen dauert und wie schnell Dein ISR-Code abläuft.
    Interessanter Aspekt, da ich davon ausging, dass erst nach dem Verlassen der ISR der nächste Interrupt durchgelassen wird. Das "speichern" des Flag innerhalb eines ISR ist mir neu und macht einiges unübersichtlicher...
    Daher wollte ich auch innerhalb des ISR die Tastenentpreller (später teils Tasten, Türkontakte, Bewegungsmelder, etc) einsetzen. Nach deiner Beschreibung, aber vollkommen unsinnig.

    Zitat Zitat von Holomino Beitrag anzeigen
    Wenn Du im ms-Bereich über einen Timer einfach nur den Port abfragst, hast Du dieses Problem nicht. Das Abtasten der Pin-Zustände im zeitlichen Raster wirkt wie ein digitaler Tiefpass, der das Prellen oberhalb der halben Abtastfrequenz (siehe Nyquist- oder Shannon-Theorem) herausfiltert.
    Hier habe ich immer noch "Bauchgrummeln", da auch ohne minuten-/stundenlange Kontaktbetätigung, im ms-Interval ISR-Routiniert wird - unnötige Energie und Rechenleistung!?

    Wie sieht es mit einem Kompromiss als Königsweg aus?
    Bsp:
    PortChange-Interrupt löst den Timer-Interrupt aus, der diesen wiederum ablöst und eine Tastenentpreller einleitet. Erst danach DARF der "global interrupt enabled" des PortChange wieder gesetzt werden.
    __________________________________________________ _
    | Sprache: C | Teensy 3.2 | Status: EwigerAnfaenger |

  3. #23
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    07.04.2015
    Beiträge
    540
    Zitat Zitat von frabe Beitrag anzeigen
    Hier habe ich immer noch "Bauchgrummeln", da auch ohne minuten-/stundenlange Kontaktbetätigung, im ms-Interval ISR-Routiniert wird - unnötige Energie und Rechenleistung!?
    Löse Dich von dieser Vorstellung. Ohne Sleep-Modi (die eigentlich nur im Batteriebetrieb interessant sind) läuft der Controller in der main-Loop immer in der Runde. Da spart man nix. Die Verluste von Netzteil und Regler bewegen sich üblicherweise weit oberhalb der vom Controller aufgenommenen Leistung.

    Der von mir gepostete ISR-Rahmen dürfte incl. Stack-Push/-Pop nicht mehr als 10..20µs bei 8MHz dauern. Im Zyklus von 1..8ms aufgerufen (so schnell muss man erst mal tippen können) und bezogen auf die Gesamtleistung des Controllers reden wir also über 0,125 .. 2% Rechenlast für die Timerroutine.

  4. #24
    Benutzer Stammmitglied
    Registriert seit
    07.06.2019
    Beiträge
    82
    Hallo - nochmal.

    Die Methoden die ihr mir mit dem Timer-ISR aufgezeigt habt ist toll und funktioniert. Soweit kann ich auch als Anfänger jede Code-Zeile nachvollziehen.
    Hier ist es so, das alle Kontakte des Register A kontinuierlich abgefragt werden. Sobald eine Veränderung statt findet, wird dieser Kontakt auf Prellung überprüft.
    Zum Entprellen wird einfach eine Variable hoch gezählt. Sollte 30ms keine Veränderung statt finden, ist dieser Kontakt entprellt!
    Danach kommt der Befehl "tu was".

    Aber ... eigentlich brauche ich eine Kontaktabfrage aus dem main() heraus.
    Innerhalb des Prg-ablauf gibt es verschiedene Bereiche. Mal ist es wichtig Kontakt PA4 dauernd zu überwachen, wärend dessen mir Eingang PA5 vollkommen egal ist.
    Erst wenn ich PA4 benötige, würde ich auf Veränderung und Entprellung abfragen.


    Oder ich sehe vor lauter Bäumen den Wald nicht mehr...
    __________________________________________________ _
    | Sprache: C | Teensy 3.2 | Status: EwigerAnfaenger |

  5. #25
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    07.04.2015
    Beiträge
    540
    Ich denke mal, es ist kein Problem, den Code so zu erweitern, dass Du auch aus der main() auf die Tasten reagieren kannst. Tatsächlich hatte ich die Änderungen hier schon reingeschrieben, dann allerdings wieder gelöscht - ganz einfach weil es jetzt gänzlich unterschiedliche Konzepte für unterschiedliche Anwendungen gibt (nix für Ungut, aber bei "ich bräuchte" von nem Youngster lohnt es sich manchmal zu hinterfragen, ob er es denn wirklich braucht).

    Eine tastengesteuertes Displaymenü z.B. würde ich mit Funktionspointern zu erschlagen suchen. Andere logische Zusammenhänge eher mit einer einfachen StateMachine. Ob das dann allerdings immer in der main() liegt oder ebenfalls in der Timer-ISR, hängt alleine davon ab, wie lange der Ablauf der Aktionen ist. Eigentlich präferiere ich die Timer-ISR, weil sie sich ideal für mehrere "asynchone" Aufgaben eignet (Das ist zwar nicht wirklich asynchron, aber mit Prescalern kann man fabelhaft passende Abfragezyklen bauen und so die Rechenleistung optimal aufteilen). Die main() hebe ich mir dann gerne für niederpriorisierte Aufgaben auf.

    Wir müssten jetzt also erst einmal darüber sprechen, was genau Du vor hast.

  6. #26
    Benutzer Stammmitglied
    Registriert seit
    07.06.2019
    Beiträge
    82
    Ein Reaktionsspiel; erst wenn Teil A erfolgt ist, läuft Teil B ab - usw.
    Zwichendurch werden immer wieder Reaktionszeiten ermittelt, die unterschiedliche Wege vorgeben.
    Bsp: Teil A, Reaktion langsam, spring in Teil B.
    Teil A, Reaktion schnell, spring in Teil C.

    Natürlich eine StateMachine!

    Innerhalb der einzelnen Teile (A, B) würde ich die jeweiligen Tastenabfrage über Interrupts erledigen.
    Somit kann mir der jeweilige Kontakt nicht mehr "durchrutschen" oder das Programm verlangsamen.
    Bsp: in Teil A wird die Taste PA4 und in Teil B wird die Taste PA5 auf Veränderung überwacht.

    Innerhalb der jeweiligen Blöche A, B etc. werden bestimmte elektro-mechanischen Kontakte (Tasten, Türkontakte, Bewegungsmelder etc) benötigt - daher Entprellung notwendig!
    Wenn das Prinzip einmal läuft, braucht man das Prinzip nur noch kaskadieren und/oder verschachteln.
    Zur Zeit baue ich einfach nur eine Versuchsplattform mit 2 Tastern und 2 Ausgängen (LED, Summer) auf.

    Der ATtiny84 hat nur 2 Timer-Interrupts. Einen lasse ich im 1ms-Takt als Zeitmesser laufen.
    Den habe ich auch als Eingangsabfrage nutzen wollen - ala "EierlegendeWollmilchSau" - bis ich jetzt nur noch "Bäume, aber keinen Wald" sehe...

    - - - Aktualisiert - - -

    Somit müsste nach meinem Verständnis eine Eingangsabfrage blockweise mit einem Timer-Interrupt erfolgen.
    Bsp:
    Teil A wird gestartet, PA4 wird dauerhaft überwacht, wären dessen laufen sonstige (niederpriorisierte) Dinge.
    Teil A wird beendet, PA4-Abfrage gestoppt.
    Teil B startet, PA5 und PB1 überwacht,
    bis PB1 nicht mehr gebraucht wird, aber PA5 weiter überwacht wird.
    usw.

    D.h. die einzelnen Eingangsüberwachungen (PA4, PA5, PB1) werden nach Bedarf ein- und ausgeschaltet.
    Königslösung?!
    __________________________________________________ _
    | Sprache: C | Teensy 3.2 | Status: EwigerAnfaenger |

  7. #27
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    07.04.2015
    Beiträge
    540
    Ein schönes Beispiel, klingt sehr einfach, hat es aber in sich. Mal so grob geplant mit 2 LEDs und 2 Tastern.

    Statusaufteilung:
    - Idle: Warte auf Key1Up, um das Spiel zu starten
    - Started: Blinke 3x mit LED1, wobei das Erlöschen des 3. Blinkzyklus den Spielstart signalisiert (das 3. Blinken kann man fieserweise noch mit einem leichten Random verzögern)
    - Measure: Messe die Zeit bis zum Key2Down und entscheide anhand der Reaktionszeit zwischen Success oder Fail
    - Success: Gebe Erfolgsmeldung LED1/2 blinken 10 mal im Wechseltakt.
    - Fail: Nur einmal blinken
    Success und Fail wechseln zurück zu Idle.
    Obacht bei "Started". Wer hier schon vor dem Startsignal wild auf Key2 herumtippt, wird mit Spielabbruch "bestraft".
    Ein Timeoverflow in "Measure" sollte das System nach 10s in den "Idle" zurücksetzen

    spinnerte Erweiterungsmöglichkeiten:
    - Highscore in EEPROM speichern
    - Tendenzen besser/schlechter erfassen (also mit vorhergehendem Ergebnis vergleichen)
    - Schwierigeres Startverhalten bei guten Ergebnissen (Startblinken wird schneller oder wilder mit mehr Random)

    Was uns in den einzelnen States interessiert:
    - KeyUp/KeyDown
    - Timerzyklus für statusinternen Zähler
    Diese Funktionen würde ich jedem Status optional zur Verfügung stellen, egal, ob sie gebraucht werden oder nicht. Zusätzlich bekommt jeder Status eine Init-Funktion, in der er sich entsprechend (erst einmal ganz abstrakt ausgedrückt) am System anmeldet und seine internen Variablen initialisiert.


    Aktoren:
    - LEDs
    - optionale statische Variablen für vorhergehende Ergebnisse
    - optional EEPROM für Highscore
    Diese "Aktoren" kommen in separate Module, quasi als "globale Funktionen".

    Aber bevor ich jetzt codemäßig anfange loszuwirbeln, erst einmal Dein Kommentar. Weiter geht es also nach der nächsten Maus.

  8. #28
    Benutzer Stammmitglied
    Registriert seit
    07.06.2019
    Beiträge
    82
    Moin.
    Bin gerade auf dem Sprung - konzentriert komme ich esrt zu Mittag dazu.

    Zitat Zitat von Holomino Beitrag anzeigen
    Ein schönes Beispiel, klingt sehr einfach, hat es aber in sich. Mal so grob geplant mit 2 LEDs und 2 Tastern.
    Mein Testaufbau sieht erst mal genau so aus. Das Spiel wird wesentlich komplexer. Mit Analog-Eingangs-Auswertungen, etc.
    Die Struktur steht auch soweit. Nur wollte ich endlich mal mit Interrupts arbeiten - NEULAND für mich - DAS macht es jetzt gerade etwas komplizierter, wird, wenn ich´s begreife und kontrolliere wesentlich schneller, flexibler und modularer!

    Zitat Zitat von Holomino Beitrag anzeigen
    (das 3. Blinken kann man fieserweise noch mit einem leichten Random verzögern)
    ...ohhhhh, welch B Ö S E R Gedanke ...
    __________________________________________________ _
    | Sprache: C | Teensy 3.2 | Status: EwigerAnfaenger |

  9. #29
    Benutzer Stammmitglied
    Registriert seit
    07.06.2019
    Beiträge
    82
    Zitat Zitat von Holomino Beitrag anzeigen
    se
    Aber bevor ich jetzt codemäßig anfange loszuwirbeln, erst einmal Dein Kommentar. Weiter geht es also nach der nächsten Maus.
    Danke für deine Gedanken und Mühe!

    Bis auf deine "spinnerte Erweiterungsmöglichkeiten:" ähneld deine Auflistung meinem Projekt.
    ABER, ich brauche keinen fertigen Code als Kopiervorlage. Viel mehr geht es mir ums Verstehen, Funktionen in universelle/autonome Module packen, um diese später in weitere Projekte mit gutem Gewissen einsetzen zu können.

    JETZT geht es mir um eine Interrupt-Eingangsabfrage, die ich nach Bedarf ein/ausschalten kann.
    Aber nicht generell, sondern jeder Eingang autonom.

    Bsp:
    Interrupt-Überwachung/Auslösung, inkl. (einfacher) Tastenentprellung
    # PA4, PA5 ein
    # PA4 aus
    # PB2 ein
    # PB2 aus
    # PA4 ein
    # PA4, PA5 aus
    __________________________________________________ _
    | Sprache: C | Teensy 3.2 | Status: EwigerAnfaenger |

  10. #30
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    07.04.2015
    Beiträge
    540
    Hmmm,

    wenn ich Dir die Zustandcodierung für "Idle" gebe:
    Code:
    #include <avr/io.h>
    #include "Statemanager.h"
    #include "LED.h"
    #include "Started.h"
    
    uint16_t IdleCounter;
    
    void Idle_KeyDown(uint8_t index)
    {
    	// NOT USED
    }
    
    void Idle_KeyUp(uint8_t index)
    {
    	if (index == 1)
    		Started_Init();
    }
    
    
    
    void Idle_TimerTick()
    {
    	IdleCounter++;
    	if (IdleCounter	> 2000)
    	{
    		IdleCounter	= 0;
    		ToggleLED1();
    		
    	}
    }
    
    void Idle_Init()
    {
    	IdleCounter = 0;
    	ClearLED1();
    	ClearLED2();
    	AttachState(Idle_KeyDown, Idle_KeyUp, Idle_TimerTick);
    }
    ...dazu noch die Beschreibung von "Started"...

    Code:
    #include <avr/io.h>
    #include <stdlib.h> //required for rand()
    #include "Statemanager.h"
    #include "LED.h"
    #include "Idle.h"
    
    
    uint16_t StartedCounter;
    uint8_t RandomDelay;
    void Started_KeyDown(uint8_t index)
    {
    	//Mogelvorbeugung: Abbruch, wenn in diesem Zustand die Reaktionstaste gedrückt wird
    	if (index == 2)
    		Idle_Init(); 
    }
    
    void Started_KeyUp(uint8_t index)
    {
    	//Mogelvorbeugung: Abbruch, wenn in diesem Zustand die Reaktionstaste gedrückt wird
    	if (index == 2)
    		Idle_Init();
    }
    
    void Started_TimerTick()
    {
    	//LED Startsequenz
    	switch(StartedCounter)
    	{
    		case 0:
    		case 1000:
    		case 2000:
    			SetLED2();
    			break;
    
    		case 300:
    		case 1300:
    			ClearLED2();	
    			break;
    	}
    	
    	if (StartedCounter	>= (2000 + RandomDelay))
    	{
    		//MeasureInit();
    	}
    	StartedCounter++;
    }
    
    void Started_Init()
    {
    	StartedCounter = 0;
    	ClearLED1();
    	ClearLED2();
    	RandomDelay = rand() % 500; //0..500 ticks delay
    	AttachState(Started_KeyDown, Started_KeyUp, Started_TimerTick);
    }
    ...mit folgenden Infos...
    - die Beiden ähneln sich doch sehr im Aufbau, wie man unschwer erkennt.
    - Jeder Status captured für sich die Eingangsdaten KeyDown/KeyUp/TimerTick durch die Anmeldung über AttachState.
    - Dieses "Capture" läuft über Funktionszeiger in einem Statemanager, den Du von weiter oben auch schon teilweise kennst

    (.h)
    Code:
    #ifndef STATEMANAGER_H_
    #define STATEMANAGER_H_
    
    
    // function prototypes
    typedef void ( *KeyDownCallback ) ( uint8_t );
    typedef void ( *KeyUpCallback ) ( uint8_t );
    typedef void ( *TimerTickCallback ) ( );
    
    void AttachState(KeyDownCallback keyDown, KeyUpCallback keyUp, TimerTickCallback timerTick);
    void Statemanager_Init();
    
    #endif /* STATEMANAGER_H_ */
    (.c)
    Code:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include "Statemanager.h"
    
    KeyDownCallback keyDownHandler;
    KeyUpCallback keyUpHandler;
    TimerTickCallback timerTickHandler;
    
    uint8_t prevPINA;
    
    void Statemanager_Init()
    {
    	//TODO: Init Timer to 1ms tick
    		
    	prevPINA = PINA;
    }
    
    
    
    
    ISR (TIM0_OVF_vect)
    {
    	//Emit Tick
    	if (timerTickHandler != 0)
    		timerTickHandler();
    		
    
    	uint8_t actPINA = PINA; //buffer actual state
    	uint8_t chPINA = prevPINA ^ actPINA;   //get changes with XOR
    	uint8_t setPINA = chPINA & actPINA;    // get new set pins
    	uint8_t clPINA = chPINA & ~actPINA;    // get new cleared pins
    
    	prevPINA = actPINA; //save actual PINA in prevPINA for next ISR call
    	
    	for (uint8_t i = 0; i<8; i++)
    	{
    		if ((setPINA >>i) & 0x01)
    			if (keyDownHandler != 0)
    				keyDownHandler(i); //Emit KeyDown
    		
    		if ((clPINA >>i) & 0x01)
    			if (keyUpHandler != 0)
    				keyUpHandler(i);  //Emit KeyUp
    	}
    }
    
    
    void AttachState(KeyDownCallback keyDown, KeyUpCallback keyUp, TimerTickCallback timerTick)
    {
    	keyDownHandler = keyDown;
    	keyUpHandler = keyUp;
    	timerTickHandler = timerTick;
    }
    ...erkennst Du vielleicht, worauf ich hinaus will:

    Man braucht die main nur zur Initialisierung,...
    Code:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include "Idle.h"
    #include "StateManager.h"
    
    int main(void)
    {
            //TODO: PORT INIT
    	Statemanager_Init();
    	Idle_Init();
    	sei(); //Enable interrupts
    	
        /* Replace with your application code */
        while (1) 
        {
        }
    }
    …, neue States lassen sich über eine Codevorlage als Modul per C&P und anschließendem Ersetzen von "State_" durch den Statusnamen sehr schnell hinzufügen,...
    Code:
    #include <avr/io.h>
    #include "Statemanager.h"
    #include "LED.h"
    void State_KeyDown(uint8_t index)
    {
    }
    
    void State_KeyUp(uint8_t index)
    {
    }
    
    void State_TimerTick()
    {
    
    }
    
    void State_Init()
    {
    	AttachState(State_KeyDown, State_KeyUp, State_TimerTick);
    }
    …, danach kümmert man sich nur noch um das Wesentliche (das Verhalten des States in den vorgegebenen Funktionsrahmen zu codieren).

    Damit man von einem State zum Nächsten springen kann, braucht man noch jeweils einen Minimalheader:
    Beispiel Started.h
    Code:
    #ifndef STARTED_H_
    #define STARTED_H_
    void Started_Init();
    #endif /* STARTED_H_ */
    ...mickrig!


    Ob das der Königsweg ist, vermag ich nicht zu sagen (Gib 100 Programmierern ein Problem und Du bekommst 100 Lösungen). Aber selbst wenn Du den Timer im 1ms-Takt laufen lässt, sind das bei 8MHz 8000 Takte. Der gesamte oben geschriebene Code nimmt als Kompilat aber nur 1kB Flash in Anspruch. Da ist also bei den berühmten 1..4 cycles per instruction der AVRs noch eine ganze Menge Luft nach oben.

    Bist Du immer noch der Meinung, Du brauchst eine Übergabe der Keys in die main()?
    Geändert von Holomino (08.08.2019 um 14:50 Uhr)

Seite 3 von 6 ErsteErste 12345 ... LetzteLetzte

Ähnliche Themen

  1. [ERLEDIGT] Interrupt Routine
    Von Saturas077 im Forum Assembler-Programmierung
    Antworten: 8
    Letzter Beitrag: 23.04.2014, 13:46
  2. Codebeispiel für Lesen von RC5 Code mit Interrupt-Routine
    Von -tomas- im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 19
    Letzter Beitrag: 25.05.2011, 13:54
  3. Interrupt Routine
    Von luvat im Forum Schaltungen und Boards der Projektseite Mikrocontroller-Elektronik.de
    Antworten: 4
    Letzter Beitrag: 16.03.2008, 21:54
  4. Interrupt in ISR-Routine freigeben
    Von dj5am im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 5
    Letzter Beitrag: 10.08.2007, 09:44
  5. uart interrupt routine
    Von Computerkora im Forum Basic-Programmierung (Bascom-Compiler)
    Antworten: 4
    Letzter Beitrag: 25.11.2006, 14:45

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •