-         

Seite 1 von 3 123 LetzteLetzte
Ergebnis 1 bis 10 von 28

Thema: TTL-Signal am ATmega8

  1. #1
    Benutzer Stammmitglied
    Registriert seit
    16.04.2011
    Beiträge
    78

    TTL-Signal am ATmega8

    Anzeige

    Gute Tageszeit liebe Gemeinde,

    nach einer erfolgreichen Konstruktion eines Frequenzzählers, würde ich gern versuchen das Offset von einer Sekunde zu umgehen,
    indem ich bei bekannter uC-Frequenz die Impulsanzahl zwischen zwei steigenden Flanken messe. So die Theorie.
    Praktisch bin ich mir nicht sicher und wende mich deshalb an euch.

    Code:
    TCCR0 |= (1<<CS02) | (1<<CS01) | (1<<CS00);
    Diese Zeile bewirkt das Starten des Timer0 bei steigender Flanke am T0 Eingang. Beim Überlauf wird eine Zählvariable inkrementiert usw, die übliche Geschichte.

    Meine Frage wäre folgende: Ich starte zwar den Timer, aber wie soll ich ihm sagen, dass er auch bei der nächsten Flanke aufhören sollte?
    Denn so scheint mir das Ganze noch nicht vollständig zu sein

    Anbei die relevanten Codestellen:

    Ich bedanke mich im Voraus,
    Nik
    Code:
    ISR(TIMER0_OVF_vect) 
    {
    	z++;
    	TIFR = (1<<TOV0); //Beim Überlauf von Timer0 und dem anschließenden Interrupt erfolgt eine Zurücksetzung auf Null
    }
    
    int main(void)
    {
    	// Definition von Ein- und Ausgängen des uC Boards
    	DDRC = 0xFF;	// PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
    	DDRD = 0xEF;	// außer dem PD4 für den TTL-Pegel sind alle Pins als Ausgänge definiert
    	PORTC = 0b00000000;
    	
    	
    	TCCR0 |= (1<<CS02) | (1<<CS01) | (1<<CS00); //  Extern anliegendes Signal an T0, Interrupt bei steigender Flanke, Starten des Timer0
    
    	TIMSK |= (1<<TOIE0); // Aktiviere den Interrupt von Timer0 beim Überlauf
    	
    	
    	sei();										// Interruptbehandlung ein
    	
    
    	
    	while(1)	// unendliche Schleife
    	{
    	
    			zaehler0_aktuell = TCNT0; // der aktuelle Timer0 Wert wird in count kopiert
    			zaehler0_ovf = z;		// Anzahl der Überläufe wird in zaehler0_ovf kopiert
    			z= 0;
    			TCNT0 = 0;
    			
    			if ((zaehler0_ovf==0)&&(zaehler0_aktuell==0))
    				{
    					freq = 0;
    				}
    			else
    				{
    					// Die abschließende Summierung aller Anteile samt Kompensation von Abweichungen
    					freq = (uint16_t) (F_CPU/(0.5+((256.0*zaehler0_ovf + zaehler0_aktuell)*korrekturfaktor))); 
    				}
    			
    			zahl_ausgeben(freq);
    				
    	}
    return 0;
    }

  2. #2
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    20.08.2008
    Ort
    Kandel
    Alter
    29
    Beiträge
    1.220
    Gar nicht. Diese Betriebsart ist für den Betrieb mit einer externen Taktquelle, zum Zählen von Ereignissen oder zum Messen von Frequenzen mit niedriger Wiederholrate gedacht. Wenn du sehr schnelle Frequenzmessungen durchführen möchtest, hast du im wesentlichen zwei Möglichkeiten:
    1. Input Capture. Diese Funktion größerer Timer ist genau für den von dir genannten Zweck gedacht, sie erfasst die Zeit zwischen zwei Ereignissen.
    2. Manuelles Capture: Du pollst den Pin oder verwendest einen der Externen Interrupts um den Zeitstempel eines kontinuierlich laufenden (wie bei 1) Timers einzulesen.

    mfG
    Markus
    Tiny ASURO Library: Thread und sf.net Seite

  3. #3
    Benutzer Stammmitglied
    Registriert seit
    16.04.2011
    Beiträge
    78
    Als Erstes vielen Dank für die Tipps markusj, eine Verständnisfrage hätte ich allerdings trotzdem. Den Code habe ich soweit auf den ICP1 abgestimmt (s.u.), doch verstehe ich nicht die Abfrage einzusetzen, wann denn
    nun die zweite Flanke eintraf.

    Code:
    TIMSK |= (1<<TICIE1);	// ICR1 aktiviert
    TCCR1B |= (1<<ICES1);	// Trigger bei steigender Flanke
    Der Codeausschnitt startet den Timer1 sobald eine steigende Flanke am ICR1-Pin eintrifft und ich kann wie gewöhnlich die Anzahl der Überläufe und aktuellen Zähler1-Stand kopieren und auswerten.
    Doch wann tritt die nächste Flanke ein?

    Code:
    if (ICF == 1) {};
    Das ist das Einzige, was ich als Option fand, doch steht in der Doku, dass das Bit bereits gesetzt ist, sobald das erste Ereignis,also die erste Flanke, stattfand.
    Ich hoffe, dass es verständlich war

    MfG Nik

  4. #4
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    20.08.2008
    Ort
    Kandel
    Alter
    29
    Beiträge
    1.220
    Gibt es einen speziellen Grund, warum du Interrupts vermeidest, Nik?

    Einfache Variante: Letzten Zählerstand in einer Variablen speichern, Interrupt anschalten und jedes Mal wenn der Input-Capture-Interrupt kommt, kannst du durch Neu-Alt die Periodendauer (in Zählerticks) ausrechnen.
    Umständliche Variante: Du pollst ICF (übrigens mit if (TIFR & (1 << ICF1), nicht mit deinem Codeschnipsel) und machst das was der Interrupt tun würde manuell. Und was das gesetzte Bit angeht:

    Zitat Zitat von ATMega8 Datasheet
    ICF1 is automatically cleared when the Input Capture Interrupt Vector is executed. Alternatively, ICF1 can be cleared by writing a logic one to its bit location.
    mfG
    Markus
    Tiny ASURO Library: Thread und sf.net Seite

  5. #5
    Benutzer Stammmitglied
    Registriert seit
    16.04.2011
    Beiträge
    78
    Peinlicherweise, weil mir ein Ansatz fehlt - wenn du den (TIMER1_CAPTURE_vect)-Ansatz meinst

    Code:
    ISR(TIMER1_OVF_vect) 
    {
    	z++;
    	TIFR = (1<<TOV1); //Beim Überlauf von Timer1 und dem anschließenden Interrupt erfolgt eine Zurücksetzung auf Null
    }
    
    ISR(TIMER1_CAPTURE_vect)
    {
    
    
    }
    
    
    
    int main(void)
    {
    	// Definition von Ein- und Ausgängen des uC Boards
    	DDRB = 0xFE;	// Mit Ausnahme des ICP1-Pins alles als Ausgang
    	DDRC = 0xFF;	// PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
    	DDRD = 0xFF;	// PORTD als Ausgang
    	PORTC = 0b00000000;
    	
    	
    	TIMSK = (1<<TICIE1) | (1<<TOIE1);	// ICR1 und Ovf aktiviert
    	TCCR1B = (1<<ICES1)  | (1<<CS10)	// Trigger bei steigender Flanke, Vorteiler = 1
    
    	
    
    	sei();					// Interruptbehandlung ein
    	
    
    	
    	while(1)	// unendliche Schleife
    	{
    	
    			zaehler1_aktuell = TCNT1; // der aktuelle Timer1 Wert wird in zaehler1_aktuell kopiert
    			zaehler1_ovf = z;		// Anzahl der Überläufe wird in zaehler1_ovf kopiert
    			z= 0;
    			TCNT1 = 0;
    			
    			if ((zaehler1_ovf==0)&&(zaehler1_aktuell==0))
    				{
    					freq = 0;
    				}
    			else
    				{
    					// Die abschließende Summierung aller Anteile samt Kompensation von Abweichungen
    					freq = (uint16_t) (F_CPU/(0.5+(65535.0*zaehler1_ovf + zaehler1_aktuell))); 
    				}
    			
    			zahl_ausgeben(freq);
    			
    			
    	}
    
    return 0;
    }
    Mit einem Interrupt sichere ich den Timer1 beim Überlauf.
    Wenn das TIMER1-Capture Ereignis eintritt,k nann ich doch nicht den Zählerstand auswerten, weil es möglicherwise erst die erste Flanke war?

    MfG Nik

    - - - Aktualisiert - - -

    So, habe hier mal was eingefügt - einfache Zählvariable, die inkrementiert wird, sobald ein Timer1-Capture Ereignis eintrifft. Erst beim zweiten Durchlauf (Zähvariable=2) werden die Timerwerte gesichert und angewendet.

    Code:
    #define F_CPU 16000000 // uC läuft mit 16MHz
    
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    
    volatile unsigned short z=0;
    volatile unsigned short c=0;
    
    int zaehler1_aktuell = 0;		// aktueller Timer0 Wert
    int zaehler1_ovf = 0;		// Anzahl der Überläufe des Timer0
    
    uint16_t freq = 0;
    
    
    ISR(TIMER1_OVF_vect) 
    {
    	z++;
    	TIFR = (1<<TOV1); //Beim Überlauf von Timer1 und dem anschließenden Interrupt erfolgt eine Zurücksetzung auf Null
    }
    
    ISR(TIMER1_CAPTURE_vect)
    {
    	//if(TIFR&(1<<ICF1))
    	c++;	
    
    
    }
    
    
    
    int main(void)
    {
    	// Definition von Ein- und Ausgängen des uC Boards
    	DDRB = 0xFE;	// Mit Ausnahme des ICP1-Pins alles als Ausgang
    	DDRC = 0xFF;	// PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
    	DDRD = 0xFF;	// PORTD als Ausgang
    	PORTC = 0b00000000;
    	
    	
    	TIMSK = (1<<TICIE1) | (1<<TOIE1); // Interrupts akivieren, Capture und Overflow
    	TCCR1B = (1<<ICES1)  | (1<<CS10); // Input Capture Edge, kein Prescaler
    	
    
    	sei();					// Interruptbehandlung ein
    	
    
    	
    	while(1)	// unendliche Schleife
    	{
    		if (C==2)
    			{
    				zaehler1_aktuell = TCNT1; // der aktuelle Timer1 Wert wird in count kopiert
    				zaehler1_ovf = z;		// Anzahl der Überläufe wird in zaehler1_ovf kopiert
    				z= 0;
    				TCNT1 = 0;
    			
    				if ((zaehler1_ovf==0)&&(zaehler1_aktuell==0))
    					{
    						freq = 0;
    					}
    				else
    					{
    						// Die abschließende Summierung aller Anteile samt Kompensation von Abweichungen
    						freq = (uint16_t) (F_CPU/(0.5+(65535.0*zaehler1_ovf + zaehler1_aktuell))); 
    					}
    			
    				zahl_ausgeben(freq);
    				
    				c=0;
    			}
    			
    			
    	}
    
    return 0;
    }
    Würde das eine optimale Lösung darbieten?
    MfG Nik
    Geändert von Liquidator (21.03.2013 um 13:04 Uhr)

  6. #6
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    20.08.2008
    Ort
    Kandel
    Alter
    29
    Beiträge
    1.220
    Nein. Hast du dir Mal die Erläuterung zum Input-Capture im Datenblatt durchgelesen? Da wird fast alles für dich automatisch gemacht!

    Kurzbeschreibung: Die Input-Capture-Einheit besteht im wesentlichen aus einem zusätzlichen Register, das ICR. Jedes Mal wenn ein Ereignis auftritt, kopiert die Input-Capture-Einheit den aktuellen Wert aus dem Zählerregister TCNT in das ICR.

    Was kannst du damit Anfangen? Du kannst die Zeit für eine komplette Periode (High -> Low -> High ->Low) sehr einfach messen. Die Dauer ist nämlich genau der alte Wert vom ICR (den du dir in eine Variable sicherst) Minus den neuen Wert. Damit bekommst du die Anahl der Zählertakte die der Periodendauer entsprechen. Anhand der von dir gewählten Timer-Konfiguration (Prescaler, CPU-Takt) kannst du ausrechnen, wie lange ein Zählertakt dauert. Und schon hast du die Periodendauer in Sekunden, und die Frequenz ist genau 1/Periodendauer.

    mfG
    Markus
    Tiny ASURO Library: Thread und sf.net Seite

  7. #7
    Benutzer Stammmitglied
    Registriert seit
    16.04.2011
    Beiträge
    78
    Gut, wenn es einen T0-Vektor auf Flanken gäbe, würde ich ohne auskommen...
    Nach zahlreichen Recherchen scheint mir langsam die Bedeutung klar zu werden - sorri for maj inglisch pliiz

    Das scheint wirklich die genauste Methode zu sein, deshalb hier noch einmal der Code Dürfte diesmal nicht viel falsch sein,
    da ich diesmal bisschen von anderen abgeschaut habe

    Code:
    #define F_CPU 16000000    // uC läuft mit 16MHz
    
    #ifndef TRUE
    #define TRUE 1
    #define FALSE 0
    #endif
    
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    
    
    volatile unsigned short z=0;
    volatile unsigned short UpdateDisplay;   // Verzögerung
    volatile unsigned int  Startzeit = 0;       // ICR-Wert bei 1. steigender Flanke sichern
    volatile unsigned int  Endzeit = 0;         // ICR-Wert bei 2. steigenden Flanke sichern
    
    int zaehler1_ovf = 0;		         // Anzahl der Überläufe des Timer1
    
    uint16_t freq = 0;
    uint16_t zaehlschritte = 0;
    
    
    ISR(TIMER1_OVF_vect) 
    {
    	z++;
    	TIFR = (1<<TOV1); //Beim Überlauf von Timer1 und dem anschließenden Interrupt erfolgt eine Zurücksetzung auf Null
    }
    
    ISR(TIMER1_CAPT_vect)
    {
    	
      static unsigned short ErsteFlanke = TRUE;
    
      if(UpdateDisplay)                                            // Das Display wurde mit den Ergebnissen der vorhergehenden
        {						 // Messung noch nicht aktualisiert. Die nächste Messung
    		return;				 // verzögern, bis die Start- und EndTime-Variablen wieder
    	}               		                       // gefahrlos beschrieben werden können
    
      
      // Bei der ersten Flanke beginnt die Messung, es wird der momentane
      // Timer beim Input Capture als Startwert gesichert
      
      if(ErsteFlanke)
      {
        Startzeit = ICR1;
        z = 0;
        ErsteFlanke = FALSE;       // Nach der nächsten Flanke beginnt die Ausgabe
      }
    
      
      // das ist die zweite Flanke im Messzyklus. Die Messung wird gestoppt
      
      else
      {
        Endzeit = ICR1;
        UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
        ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
      }	
    
    }
    
    
    
    int main(void)
    {
    	// Definition von Ein- und Ausgängen des uC Boards
    	DDRB = 0xFE;	// Mit Ausnahme des ICP1-Pins alles als Ausgang
    	DDRC = 0xFF;	// PORTC als Ausgang - über PORTC werden die jeweiligen Segmente einer 7-Seg. Anzeige gesteuert
    	DDRD = 0xFF;	// PORTD als Ausgang
    	PORTC = 0b00000000;
    	
    	
    	TIMSK = (1<<TICIE1) | (1<<TOIE1);    // Interrupts akivieren, Capture und Overflow
    	TCCR1B = (1<<ICES1)  | (1<<CS10);   // Input Capture Edge, kein Prescaler
    	
    
    	sei();					// Interruptbehandlung ein
    	
    
    	
    	while(1)	// unendliche Schleife
    	{
    		 if(UpdateDisplay)		// Erst nach zweiter Flanke ausführen
    			{
    				zaehler1_ovf = z;		// Anzahl der Überläufe wird in zaehler1_ovf kopiert
    				z= 0;
    				
    				
    				zaehlschritte = (uint16_t)(0.5+(65536.0*zaehler1_ovf) + Endzeit - Startzeit);
    				
    			
    				if (zaehlschritte==0)
    					{
    						freq = 0;
    					}
    				else
    					{	
    					         freq = (uint16_t) (F_CPU/(zaehlschritte)); 
    					}
    			
    				zahl_ausgeben(freq);
    				
    				UpdateDisplay = FALSE;
    			}
    			
    			
    	}
    
    return 0;
    }
    Ich möchte mich bei dir bedanken für deine geduldigen Erklärungen Markus
    MfG Nik
    Geändert von Liquidator (21.03.2013 um 16:53 Uhr)

  8. #8
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    So im Groben könnte es funktionieren, wenn auch noch relativ unübersichtlich. Bei den Überläufen gibt es aber noch ein paar Tücken - das wird so wie gezeigt nur meistens funktionieren, je nach Laufzeit für die Dispayroutinen ggf. auch weniger oft. Das erste Problem dabei ist es, das der Überlauf erst im Hauptprogramm ausgewertet wird - da sind also noch ein paar Verzögerungen möglich, so dass noch ein Überlauf dazu kommen kann bis das Hauptprogramm so weit ist.

    Dazu kommt hier noch, das in dem Code oben das Ergebnis in einer 16 Bit variable gespeichert wird. In dem Fall könnte man die Behandlung der Überläufe auch gleich ganz weglassen. Die Überläufe braucht man erst, wenn die Zeiten länger werden.

    Das 2. etwas schwierige Problem ist, dass der ICP-interrupt und der Overflow interrupt fast gleichzeitig kommen können - das kommt zwar nur selten vor (etwa alle 50000 Zyklen), das Problem lässt sich aber auch lösen. Im RN-Wissen Bereich unter Timer/Counter(AVR) (http://www.rn-wissen.de/index.php/Timer/Counter_(Avr)) steht wie es geht, incl. einem fertigen Programm dazu.

  9. #9
    Benutzer Stammmitglied
    Registriert seit
    16.04.2011
    Beiträge
    78
    Danke Besserwessi, dass du mich auf diesen Umstand aufmerksam gemacht hast. Die Variable zaehlschritte soll natürlich größer sein, werd' wohl 'nen unsigned long Datentyp deklarieren - bei F_CPU von 16Mio wird es schwer mit 16 bit
    Den zweiten von dir angesprochenen Punkt habe ich zwar befürchtet, dachte mir doch, dass der eh nie eintreten wird. Werde das aber doch berücksichtigen, danke für den Hinweis.

    Mal eine Frage als Anfänger - Quelltext unübersichtlich: Ist das Ästhetik oder nicht optimierter Code?

    MfG Nik

  10. #10
    Erfahrener Benutzer Roboter Genie
    Registriert seit
    20.08.2008
    Ort
    Kandel
    Alter
    29
    Beiträge
    1.220
    Hi Nik,

    ein paar Dinge:
    1. Du brauchst im Überlauf-Interrupt das Overflow-Flag nicht löschen. Jeder Interrupt löscht automatisch sein eigenes Flag bei Aufruf der ISR.
    2. Du wählst möglicherweise einen zu komplexen Ansatz für die Zeitmessung. Welche Frequenz willst du denn ermitteln? Wenn du den Prescaler entsprechend wählst, solltest du ohne den Zusatz-Zähler auskommen.
    3. Ich würde die Anzahl der vergangenen Ticks in der ISR ermitteln und direkt in eine Variable schreiben. Du brauchst also nur Startzeit und Ticks als Variable. Ticks errechnet sich durch ICR1 - Startzeit, fertig. Zusätzlich muss Startzeit dann auch nicht mehr volatile sein.
    4. Du solltest die Ergebnisse mehrere Messzyklen mitteln, um stabilere Werte zu bekommen. Das hängt natürlich auch ein Stück von den Eigenschaften des vermessenen Signals ab.
    5. Warum machst du deine Multiplikation mit Gleitkommazahlen?

    mfG
    Markus

    @Unübersichtlich: Beides. "Schöner Code" ist zumindest weniger Fehleranfällig und oft auch noch schneller.
    Tiny ASURO Library: Thread und sf.net Seite

Seite 1 von 3 123 LetzteLetzte

Ähnliche Themen

  1. TTL Signal Verarbeitung
    Von GDIViperM im Forum Elektronik
    Antworten: 14
    Letzter Beitrag: 03.05.2011, 11:29
  2. TTL-Signal auswerten
    Von mudi007 im Forum Elektronik
    Antworten: 10
    Letzter Beitrag: 05.05.2009, 23:52
  3. Drehzahl signal in TTL umwandeln
    Von EISMAN im Forum Elektronik
    Antworten: 6
    Letzter Beitrag: 19.10.2006, 12:35
  4. TTL-Signal Umschalter
    Von noxon im Forum Elektronik
    Antworten: 3
    Letzter Beitrag: 24.03.2006, 10:19
  5. 230V in TTL Signal umwandeln ( IC?)
    Von Lektor im Forum Elektronik
    Antworten: 26
    Letzter Beitrag: 19.11.2005, 13:55

Berechtigungen

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