- 3D-Druck Einstieg und Tipps         
Seite 1 von 2 12 LetzteLetzte
Ergebnis 1 bis 10 von 14

Thema: Zeitmessung mit dem Atmega8 - geht das?

  1. #1

    Zeitmessung mit dem Atmega8 - geht das?

    Anzeige

    Powerstation Test
    Hallo Leute,
    ich muss erst einmal erwähnen, ich bin absoluter Neuling im AVR-Gebiet.
    Was nicht heißen soll, dass das mich davon abhalten soll^^

    Mir geht es um Folgendes:
    Ich habe 2 Taster, mein Ziel wäre es , je nachdem ob 1 oder beide taster gedrückt sind und auch va. wie lange diese jeweils gedrückt sind, diverse Aktionen zu provozieren.

    Mir wäre es am Liebsten das ganze in C++/Basic zu programmieren,
    nur wie kann man mit dem AVR 2 Verschiede Zeiten gleichzeitig messen?
    was mir wichtig ist, ist dass es dann so funktioniert:
    Taster gedrückt -> Zähler startet; Taster losgelassen -> Zähler stoppt.
    Und davon halt zwei gleichzeitig.

    ist soetwas machbar?
    Bitte erklärt es so genau wie möglich, ich bin wirklich absoluter anfänger.
    Sollte ich weitere Bauteile benötigen wäre das auch kein Problem, ich bestelle eh erst danach.

    Schon mal Danke

  2. #2
    Erfahrener Benutzer Roboter-Spezialist
    Registriert seit
    15.01.2008
    Ort
    Siegen, Germany, Germany
    Beiträge
    441
    Hallo,
    schau dir mal diesen Artikel im RN-Wissen Bereich an:

    https://www.roboternetz.de/wissen/in...scom_und_Timer

    Sollte das sein, was du suchst

  3. #3
    Ich denke du hast es zumindest mit diesem Artikel ganz gut getroffen.
    Allerdings tun sich mir nach dem Lesen der Artikels wieder 2 Fragen auf:

    1. All die Sachen die in den Beispielen oben zu Beginn des Programms aufgerufen werden, kann man die auch beliebig im Programm aufrufen?
    Wie zB müsste ich den Code umschreiben, um timer0 bei tastendruck zu starten und wieder zu beenden?

    2. Heißt das es ist ohne weiteres möglich, beide 8-Bit Timer des mega8
    unabhängig voneinander, aber zugleich durchlaufen zu lassen?[/list]

  4. #4
    Erfahrener Benutzer Robotik Einstein Avatar von Felix G
    Registriert seit
    29.06.2004
    Ort
    49°32'N 8°40'E
    Alter
    41
    Beiträge
    1.780
    Also zunächst mal würde ich einen der Timer so konfigurieren, daß er eine art globalen Takt zur Verfügung stellt, er sollte also im wesentlichen nur eine Variable z.B. 1x pro Millisekunde um eins inkrementieren. Je nach Bedarf muss das natürlich eventuell auf mehrere Variablen erweitert werden, ich verwende eigentlich immer mehrere Variablen (z.B. 1/1000s, 1/10s, 1s, 1min ... je nachdem was gerade gebraucht wird)


    Auf diese Art kann man leicht an beliebiger Stelle die Zeit, welche zwischen zwei Ereignissen vergangen ist feststellen, denn man muss ja nur die Startzeit von der Endzeit abziehen.


    So, und damit das mit deinen beiden Tastern bequem funktioniert, schließt du sie an die beiden zur Verfügung stehenden externen Interrupts an.

    Der Ablauf ist dann ganz simpel:
    - Einstellen beider Interrupts auf fallende Flanke (wenn die Taster gegen Masse schalten)

    - Wenn ein Interrupt durch eine fallende Flanke ausgelöst wird: die aktuelle Zeit (= Startzeit) merken, und den Interrupt auf steigende Flanke umschalten

    - Wenn ein Interrupt durch eine steigende Flanke ausgelöst wurde: Startzeit von aktueller Zeit abziehen (= Dauer des Tastendrucks), und den Interrupt wieder auf fallende Flanke umschalten.
    So viele Treppen und so wenig Zeit!

  5. #5
    Hallo Felix G,
    ich finde deine Idee eigentlich klasse, nur will mir mein fehlendes AVR-Wissen mal wieder einen Strich durch die Rechnung ziehen.

    Daher hab ich nochmal ein paar Fragen:

    Kannst du mir vlt kurz erklären was es mit den Flanken auf sich hat ( Der Begriff "Flanke" sagt mir leider im Zusammenhang mit dem IC nichts)
    Ein Link zu einer Erklärung reicht natürlich auch.

    Wie würde sich das mit globalen Variable auf Dauer verhalten.
    Bei mir wäre es nämlich so, dass der atmega die ganze zeit laufen würde, da ich damit unter anderem auch meinen Computer anschalten will.

    Wie verhält sich das mit Pufferüberläufen bzw wie könnte ich das umgehen?

    Schon mal Danke für die Mühe

  6. #6
    Erfahrener Benutzer Robotik Einstein Avatar von Felix G
    Registriert seit
    29.06.2004
    Ort
    49°32'N 8°40'E
    Alter
    41
    Beiträge
    1.780
    Also bei einem Digitalsignal sind die Flanken im Prinzip die Übergänge von einem Pegel auf den anderen.

    Eine fallende Flanke ist dabei ein Übergang von High auf Low (wie er z.B. beim Drücken des Tasters entsteht, falls dieser gegen GND schaltet), und eine steigende Flanke dementsprechend ein Übergang von Low auf High.

    Die externen Interrupts eines ATmega kann man nun so konfigurieren, daß sie in unterschiedlichen Situationen ausgelöst werden, z.B. eben bei einer steigenden oder einer fallenden Flanke.


    Da ein (gegen GND schaltender) Taster bei Betätigung eine fallende Flanke erzeugt, und beim Loslassen eine steigende, kann man mit Hilfe eines Interrupts leicht feststellen wann ein Taster gedrückt, und wann er wieder losgelassen wurde.


    edit:
    wichtig dabei ist übrigens, daß die Taster hardwaremäßig entprellt wurden.
    sie benötigen also jeweils einen nachgeschalteten RC-Tiefpass, und idealerweise zusätzlich noch einen sogenannten "Schmitt-Trigger" (notfalls reicht aber auch der Tiefpass, wenn er etwas großzügiger dimensioniert ist).


    Was die Variable betrifft, ist die Lösung eigentlich recht simpel...

    Entweder du verwendest mehrere Variablen, und realisierst damit eine Art interne "Uhr". Wenn du also eine 8-Bit Variable für ms hast, und möchtest gerne weiter zählen als bis 256ms, dann nimm noch eine zweite für z.B. 1/100s oder 1/10s, und zähle diese jeweils eins hoch wenn die ms-Variable den entsprechenden Wert erreicht hat (die ms Variable wird dann natürlich wieder auf 0 gesetzt). Nach diesem System kannst du deine "Uhr" bis auf Sekunden, Minuten, Stunden, Tage, Jahre oder gar Jahrhunderte erweitern, je nachdem wieviel du benötigst.

    Eine andere Variante wäre es, bei der Subtraktion entsprechend zu berücksichtigen ob in der Zwischenzeit ein Überlauf stattgefunden hat (das merkt man ja leicht, wenn die Endzeit kleiner ist als die Startzeit). Die Einzige Beschränkung liegt dann natürlich darin, daß nur maximal ein Überlauf im gemessenen Zeitraum auftreten darf.

    ich denke meist ist eine Kombination aus beiden Varianten am sinnvollsten, also z.B. Zählvariablen die bis in den Sekundenbereich gehen, und dann überprüfen ob in der gemessenen Zeit die Sekundenvariable einen Überlauf hatte (so könntest du dann Zeiten bis 256s messen).
    So viele Treppen und so wenig Zeit!

  7. #7
    Ich hab mir überlegt und denke mir reicht es wenn ich eine "Uhr" erstelle die im 50 ms-takt tickt. Würde dann ja heißen, dass die Variable 20 mal die Sekunde erhöht wird. Ich weiß jetzt nicht genau wo die grenze Liegt (vermute ja mal was im bereich um die 65000 für eine Int Zahl), aber
    65000/20 reicht mir vollkommen, wenn man bedenkt, dass so ein Tastendruck maximal 5 sek. dauern wird ^^

    bedeutet Interrupt dass dieser Interrupt quasi überwacht, ob sich eine Flanke geändert hat oder müsste ich diese interrupt Funktion in eine iterative/rekursive Schleife packen? (Also wie beim erstellen eines GUIs: Entweder man prüft andauernd ob ein Button geklickt wurde und Ruft entsprechend die Funktion auf oder man verknüpft den Button mit einer Funktion, damit dieser sie beim Klicken aufruft.)
    Ich hoffe nur ich fange nicht langsam an dich zu nerven,aber...
    Kennst du vielleicht ein gutes Tutorial bezüglich Interrupts?
    Ich finde es klingt zwar schon logisch, aber mir ist es immer am liebsten wenn ich es mir iwie bildlich vorstellen kann.

  8. #8
    Erfahrener Benutzer Robotik Einstein Avatar von Felix G
    Registriert seit
    29.06.2004
    Ort
    49°32'N 8°40'E
    Alter
    41
    Beiträge
    1.780
    Interrupts sind dafür da, daß man eben nicht dauernd nachprüfen muss ob etwas passiert ist...

    Du schreibst nur eine spezielle Funktion, die sogenannte ISR (Interrupt Service Routine), diese wird dann vom AVR vollautomatisch ausgeführt, sobald das entsprechende Ereignis eintritt (also z.B. ein Pegelwechsel von Low nach High).


    Für jeden Interrupt den du verwendest benötigst du eine ISR, in deinem Fall wären das also insgesamt 3, nämlich eine für den Timer und nochmal je eine für die beiden externen Interrupts.


    Bei einer ISR ist darauf zu achten, daß sie möglichst kurz sein sollte, sehr aufwendige Berechnungen haben darin nichts zu suchen.



    Ich kann dir, wenn ich später ein bischen Zeit habe, gerne mal ein kleines Beispielprogramm (in C) schreiben, dann lässt sich der ganze Zusammenhang und die Funktionsweise auch besser erklären.
    So viele Treppen und so wenig Zeit!

  9. #9
    Das wäre klasse wenn du das machen könntest.
    ich hatte mir zwar den Timer Artikel aus dem RN-Wissen angeschaut, aber erst jetzt als du "ISR" erwähnt hast, ist mir der Zusammenhang aufgefallen.

  10. #10
    Erfahrener Benutzer Robotik Einstein Avatar von Felix G
    Registriert seit
    29.06.2004
    Ort
    49°32'N 8°40'E
    Alter
    41
    Beiträge
    1.780
    Soooo....


    Ich habe jetzt hier ein (ungeprüftes) Beispielprogramm das, wenn es funktioniert, zwei Taster darauf überprüfen soll ob sie kurz (<3s) oder lang (>3s) gedrückt wurden, und jeweils eine von 4 zugehörigen Funktionen aufruft.

    Der Code ist etwas lang, und normalerweise würde ich ein derartiges Programm auf mehrere .c und .h Dateien verteilen, aber es ist ja nur ein einfaches Beispiel.
    Code:
    #include <avr/io.h>
    #include <avr/signal.h>
    #include <avr/interrupt.h>
    
    /* globale Variablen */
    volatile unsigned char timer_10ms, timer_1s;
    volatile unsigned char flags;
    
    #define FLAG_INT0_SHORT		0
    #define FLAG_INT0_LONG		0
    #define FLAG_INT1_SHORT		0
    #define FLAG_INT1_LONG		0
    
    
    /* Hardware initialisieren */
    void avr_init(void)
    {
    	//Ports
    
    	//Port-D
    	DDRD = 0xF3;	//Pins PD2(Int0) und PD3(Int1) als Eingang
    	PORTD = 0x0C;	//Pull-Up Widerstände von PD2 und PD3 aktiviert
    
    
    	//Timer
    
    	//Timer0 (10ms Takt)
    	//ATmega8 Datenblatt, Seite 72
    	TCCR0 = 0x05;	//Prescaler: 1/1024
    	TCNT0 = 0x64;	//Vorgabewert: 100
    
    	//Timer Interrupt Register
    	//ATmega8 Datenblatt, Seite 72-73
    	TIMSK = 0x01;	//Overflow-Interrupt für Timer0
    	TIFR = 0x00;	//Interrupt-Flag für Timer0 löschen
    
    
    	//Externe Interrupts
    	//ATmega8 Datenblatt, Seite 66-68
    	MCUCR = 0x0A;	//Externe Interrupts für fallende Flanke konfigurieren
    	GICR = 0xC0;	//Externe Interrupts aktivieren
    	GIFR = 0x00;	//Interrupt-Flags löschen
    
    
    	sei();			//Interrupts global aktivieren
    }
    
    
    /* Timer0 Interrupt Service Routine */
    SIGNAL(SIG_OVERFLOW0)
    {
    	TCNT0 = 0x64;	//Vorgabewert neu einstellen
    
    	timer_10ms++;	//10ms Variable inkrementieren
    
    	//nach einer Sekunde
    	if(Timer_10ms % 100 == 0)
    	{
    		Timer_10ms = 0;	//10ms Variable = 0
    		Timer_1s++;		//1s Variable inkrementieren
    	}
    }
    
    
    /* INT0 Interrupt Service Routine */
    SIGNAL(SIG_INTERRUPT0)
    {
    	static char int0_start, int0_duration;
    	char tmp;
    
    	//fallende oder steigende Flanke?
    	if((MCUCR & 0x03) == 0x02)
    	{
    		//fallende Flanke
    		
    		//Startzeit merken
    		int0_start = timer_1s;
    		
    		//Interrupt auf steigende Flanke einstellen
    		tmp = MCUCR;
    		tmp &= 0xFC;	//Konfigurationsbits für INT0 löschen
    		tmp |= 0x03;	//einstellen auf steigende Flanke
    		MCUCR = tmp;
    	}
    	else if((MCUCR & 0x03) == 0x03)
    	{
    		//steigende Flanke
    		
    		//aktuelle Zeit zwischenspeichern, falls ISR durch Timer-Interrupt unterbrochen wird
    		tmp = timer_1s;
    		
    		if(tmp <= int0_start)
    			int0_duration = tmp + (255 - int0_start);
    		else
    			int0_duration = tmp - int0_start;
    		
    		//kurz oder lang?
    		if(int0_duration < 3)
    			flags |= (1 << FLAG_INT0_SHORT);
    		else
    			flags |= (1 << FLAG_INT0_LONG);
    
    		//Interrupt auf fallende Flanke einstellen
    		tmp = MCUCR;
    		tmp &= 0xFC;	//Konfigurationsbits für INT0 löschen
    		tmp |= 0x02;	//einstellen auf fallende Flanke
    		MCUCR = tmp;
    	}
    	else
    	{
    		//Interrupt falsch eingestellt, rücksetzen auf fallende Flanke
    		tmp = MCUCR;
    		tmp &= 0xFC;	//Konfigurationsbits für INT0 löschen
    		tmp |= 0x02;	//einstellen auf fallende Flanke
    		MCUCR = tmp;
    	}
    }
    
    
    /* INT1 Interrupt Service Routine */
    SIGNAL(SIG_INTERRUPT1)
    {
    	static char int1_start, int1_duration;
    	char tmp;
    
    	//fallende oder steigende Flanke?
    	if((MCUCR & 0x0C) == 0x08)
    	{
    		//fallende Flanke
    		
    		//Startzeit merken
    		int0_start = timer_1s;
    		
    		//Interrupt auf steigende Flanke einstellen
    		tmp = MCUCR;
    		tmp &= 0xF3;	//Konfigurationsbits für INT1 löschen
    		tmp |= 0x0C;	//einstellen auf steigende Flanke
    		MCUCR = tmp;
    	}
    	else if((MCUCR & 0x0C) == 0x0C)
    	{
    		//steigende Flanke
    		
    		//aktuelle Zeit zwischenspeichern, falls ISR durch Timer-Interrupt unterbrochen wird
    		tmp = timer_1s;
    		
    		if(tmp <= int1_start)
    			int1_duration = tmp + (255 - int1_start);
    		else
    			int1_duration = tmp - int1_start;
    		
    		//kurz oder lang?
    		if(int0_duration < 3)
    			flags |= (1 << FLAG_INT1_SHORT);
    		else
    			flags |= (1 << FLAG_INT1_LONG);
    
    		//Interrupt auf fallende Flanke einstellen
    		tmp = MCUCR;
    		tmp &= 0xF3;	//Konfigurationsbits für INT1 löschen
    		tmp |= 0x08;	//einstellen auf fallende Flanke
    		MCUCR = tmp;
    	}
    	else
    	{
    		//Interrupt falsch eingestellt, rücksetzen auf fallende Flanke
    		tmp = MCUCR;
    		tmp &= 0xF3;	//Konfigurationsbits für INT1 löschen
    		tmp |= 0x08;	//einstellen auf fallende Flanke
    		MCUCR = tmp;
    	}
    }
    
    
    /* Hier kommen die Funktionen */
    void action_int0_short(void)
    {
    	//eine Funktion muss tun, was eine Funktion tun muss
    }
    
    void action_int0_long(void)
    {
    	//eine Funktion muss tun, was eine Funktion tun muss
    }
    
    void action_int1_short(void)
    {
    	//eine Funktion muss tun, was eine Funktion tun muss
    }
    
    void action_int1_long(void)
    {
    	//eine Funktion muss tun, was eine Funktion tun muss
    }
    
    
    /* last but not least: main */
    int main(void)
    {
    	//Variablen initialisieren
    	timer_10ms = 0;
    	timer_1s = 0;
    	flags = 0x00;
    
    	//Hardware initialisieren
    	avr_init();
    
    	while(1)
    	{
    		//Taster an INT0 wurde kurz betätigt
    		if(flags & (1 << FLAG_INT0_SHORT))
    		{
    			action_int0_short();				//zugehörige Funktion aufrufen
    			flags &= ~(1 << FLAG_INT0_SHORT);	//flag löschen
    		}
    		
    		//Taster an INT0 wurde lang betätigt
    		if(flags & (1 << FLAG_INT0_LONG))
    		{
    			action_int0_long();					//zugehörige Funktion aufrufen
    			flags &= ~(1 << FLAG_INT0_LONG);	//flag löschen
    		}
    		
    		//Taster an INT1 wurde kurz betätigt
    		if(flags & (1 << FLAG_INT1_SHORT))
    		{
    			action_int1_short();				//zugehörige Funktion aufrufen
    			flags &= ~(1 << FLAG_INT1_SHORT);	//flag löschen
    		}
    		
    		//Taster an INT1 wurde lang betätigt
    		if(flags & (1 << FLAG_INT1_LONG))
    		{
    			action_int1_long();					//zugehörige Funktion aufrufen
    			flags &= ~(1 << FLAG_INT1_LONG);	//flag löschen
    		}
    	}
    }
    Es beginnt mit der Definition einiger Variablen, dann kommt eine funktion die die Initialisierung des Mega8 übernimmt, gefolgt von den drei ISRs und den 4 Funktionen die aufgerufen werden sollen. Das eigentliche "Hauptprogramm", also main, kommt gaaanz am Schluss.


    Die Funktionen werden übrigens aus einem bestimmten Grund in main() aufgerufen: jede Funktion die man innerhalb einer ISR aufruft, verlängert diese unnötig, daher ist es sinnvoll sich nur zu merken welche Funktion aufgerufen werden soll, und dieses dann außerhalb der ISR zu tun.
    So viele Treppen und so wenig Zeit!

Seite 1 von 2 12 LetzteLetzte

Berechtigungen

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

MultiPlus Wechselrichter Insel und Nulleinspeisung Conrad