- fchao-Sinus-Wechselrichter AliExpress         
Seite 3 von 3 ErsteErste 123
Ergebnis 21 bis 28 von 28

Thema: TTL-Signal am ATmega8

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

    E-Bike
    Das stimmt, die eine-Sekunde-Vergleichsmessung wird wohl (vorerst) nicht benötigt
    Doch würde mich das wirklich interessieren, wie man gleichzeitig eine Sekunde und beispielsweise 5ms-Takte gleichzeitig mit einem Timer zu zählen vermag. Im Moment brauche ich das nicht besonders, doch die Anzahl der Timer ist immer zu wenig und man lernt immer mehr dazu
    In kürzester Zeit spiele ich den Code drauf und melde mich wieder zu Wort. Versuchen werde ich das wohl erst einmal mit Messung einer Periode - nur im Notfall ändere ich das in mehrere.

    Anbei poste ich den Code für die Nachfolger und würde natürlich dankbar sein, wenn ihr auch Optimierungstipps für mich hättet - so ein Microcontroller hat nicht allzuviel Toleranz (sprich:Speicher), was nicht-optimierte Programmierung betrifft

    MfG Nik
    Code:
    /*****************************************************************
    
    Frequenzzähler, im Bereich von 1Hz bis mind. benötigte 1400Hz. 
    
    Unter Zuhilfenahme eines externen Quarzes (16MHz) wird das an 
    PPB0(ICP1) des ATmega8 anliegende TTL-Signal ausgewertet und anschließend
    auf einer vierstelligen 7-Segment-Anzeige im Multiplexbetrieb
    dargestellt.
    
    Für etwaige kleinere Abweichungen wurde der Korrekturfaktor eingeführt,
    welcher durch Abgleich die Ungenauigkeit der Messung arithmetisch "glättet".
    
    ******************************************************************/
    
    #define F_CPU 16000000UL // 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;		// Zählvariable des Timer1
    volatile unsigned short UpdateDisplay;   // Verzögerung
    volatile unsigned long Zeitdifferenz = 0;    
    
    volatile uint8_t tausender, hunderter, zehner, einer;	// Speicher für die eizelnen Digits
    volatile uint8_t position = 0;
    
    unsigned int zaehler1_ovf = 0;		// Anzahl der Überläufe des Timer1
    unsigned long zaehlschritte = 0;
    
    uint8_t bitmaske = 0b00010000; // Funktion: Keinen Strom auf PortD4 geben, da TTL
    
    uint16_t freq = 0;
    
    
    
    
    float korrekturfaktor = 1; // für Abgleich anpassen (z.B. 0.9997)
    
    const int8_t numbers[11] =		// Array zur Darstellung der Zeichen auf der 7-Seg-Anz.
    {
    
    	0b01101111,		// 0
    	0b00000110,		// 1
    	0b10101011,		// 2
    	0b10001111,		// 3
    	0b11000110,		// 4
    	0b11001101,		// 5
    	0b11101101,		// 6
    	0b00000111,		// 7
    	0b11101111,		// 8
    	0b11001111,		// 9
    	0b00000000		// leere Anzeige
    	
    };
    
    
    ISR(TIMER1_OVF_vect) 
    {
    	z++;
    	TIFR = (1<<TOV1); //Beim Überlauf von Timer1 und dem anschließenden Interrupt erfolgt eine Zurücksetzung auf Null
    	//Braucht nicht gelöscht zu werden. Jeder Interrupt löscht automatisch sein eigenes Flag beim Aufruf der ISR.
    }
    
    
    ISR(TIMER1_CAPT_vect)
    {
    	// static bedeutet, dass auf die Funktion/Variable nur in der Datei, in der sie steht, zugegriffen werden kann.
    
      static unsigned short ErsteFlanke = TRUE;
      static unsigned long Startzeit = 0;
      static unsigned int LowByte = 0;
      static unsigned int HighByte = 0;
    
    
      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
    	
    	
    	LowByte = ICR1L;         // LowByte zuerst, HighByte später gepuffert
    	HighByte = ICR1H;  
    	
    	// Overflow verpasst, wenn ICR1H klein und wartender Overflow Interrupt
    	
    	if ((HighByte < 128) && (TIFR & (1<<TOV1)))
    		{   
    			// wartenden Timer Overflow Interrupt vorziehen
    			++z;         
    			TIFR = (1<<TOV1);    // Timer Overflow int. löschen, da schon hier ausgeführt
    		}
    
      
      // 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
      {
        Zeitdifferenz = ICR1 - Startzeit;
        UpdateDisplay = TRUE;      // Eine vollständige Messung. Sie kann ausgewertet werden
        ErsteFlanke = TRUE;        // Bei der nächsten Flanke beginnt der nächste Messzyklus
      }	
    
    }
    
    
    ISR (TIMER0_OVF_vect)  // timer0 overflow interrupt
    {
        TCNT0 += 6; // WorkAround, CTC-Mode-"Simulation"
        
        PORTC = 0b00000000;		// alle Digits aus
    	
    	/*****************************************************************
    	Die Prozedur ist gesondert zu schildern. Die Funktion switch(position)
    	enthält nicht nur den eigentlichen Multiplex-Vorgang zum 
    	timer1-abhängigen Wechsel der jeweiligen 7-Segment-Anzeige, sondern
    	entfernt, mithilfe verknüpfter case-if-Abfragen auch die unnötigen
    	Nullen, die vor der eigentlichen Frequenzanzeige angezeigt werden würden.
    
    	******************************************************************/
    	
    	
    	switch (position)		// aktuelle Stelle ausgeben
    	{
    		case 0 : if (tausender==0) 
    					{digit(10, PC0);} 
    				 else
    					{digit(tausender, PC0);}			
    				 break;
    		
    		case 1 : if ((tausender==0)&&(hunderter==0))
    					{digit(10, PC1);}
    				 else
    					{digit(hunderter, PC1);}
    				 break;
    				 
    		case 2 : if ((tausender==0)&&(hunderter==0)&&(zehner==0))
    					{digit(10, PC2);}
    				 else
    					{digit(zehner, PC2);}
    				 break;
    				 
    		case 3 : 	{digit(einer, PC3);} 
    				 break;
    	}
    	
    	position++;			// beim nächsten Mal nächstes Digit
    	if(position == 4)	// wenn alle Digits durch
    	{
    		position = 0;	// von vorne beginnen
    	}
    }
    
    
    /*********************************************************************
    Die Funktion digit() sorgt für die eigentliche Darstellung der Zahlen
    an der jeweiligen Stelle. Sie filtert unter Zuhilfenahme der Bitmaske
    den für das TTL-Signal freigehaltenen Pin (T0) und holt sich aus dem
    numbers[]-Array die Portzuweisungen.
    
    *********************************************************************/
    
    void digit(uint8_t wert, uint8_t pin)
    {
    
    	PORTD  = (PORTD & bitmaske) | numbers[wert];     
        PORTC |= (1 << pin);    // entsprechendes Digit an
    	
    }
    
    void zahl_ausgeben(uint16_t zahl)
    {
    	tausender =  zahl/1000;	// Tausender Dezimalstelle
    	zahl = zahl % 1000;
    	
    	hunderter = zahl/100;	// Hunderter Dezimalstelle
    	zahl = zahl % 100;
    	
    	zehner = zahl/10;		// Zehner Dezimalstelle
    	zahl = zahl % 10;
    	
    	einer = zahl;			// Einer Dezimalstelle
    }
    
    
    
    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
    	
    	TIMSK |= (1 << TOIE0);				   // Interrupt aktivieren, Overflow
               TCCR0 |= (1 << CS01) | (1 << CS00);  // Vorteiler auf 64 
    	
    
    	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 = (unsigned long)(0.5+(65536.0*zaehler1_ovf) + Zeitdifferenz);
    				
    			
    				if (zaehlschritte==0)
    					{
    						freq = 0;
    					}
    				else
    					{
    						// Die abschließende Berechnung der Schritte pro Sekunde
    						freq = (uint16_t) (F_CPU/(zaehlschritte)); 
    					}
    			
    				zahl_ausgeben(freq);
    				
    				UpdateDisplay = FALSE;
    			}
    			
    			
    	}
    
    return 0;
    }

  2. #22
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Der Overflow Zähler muss in der ICP ISR gesichert werden - der Fehler ist immer noch drin.

    Auch das unnötige zurücksetzen des Overflow interrupt bits ist immer noch drin - das sollte raus.

    Das Multiplexing zur Darstellung geht auch ohne switch, einfach nur über ein Array. Den Test auf Führende Nullen macht man dann halt einmal beim Schreiben der Zahl in den Puffer. Die Laufzeit der ISR hat auch einen Einfluss auf die maximale Frequenz die per ICP noch gemessen werden kann - für maximal 10 KHz reicht es auch so noch locker, nur das Array wäre aber deutlich schneller.
    Die Frequenz für die Darstellung ist auch recht hoch. Das würde vor allem die Messung mit der festen Torzeit von 1s stören.

    Es ist nicht gut in einer ISR ein Unterprogramm aufzurufen - dafür müssen unnötig viel Register gerettet werden. Die Minimallösung wäre es die routine digit als INLINE zu kennzeichnen - mit etwas Glück macht das der Compiler auch von alleine, muss aber nicht.

  3. #23
    Benutzer Stammmitglied
    Registriert seit
    16.04.2011
    Beiträge
    78
    Den Overflow Zähler sichern... Meinst du damit, in zaehler1_ofv soll am Ende von ISR(TIMER1_CAPT_vect) aus z kopiert werden?

    Overflow bit ist entfernt. Für switch finde ich keinen anderen Ansatz, doch werde ich mir darüber Gedanken machen. Die ICP ISR kann ich nicht kleiner gestalten, da dort alle Überprüfungen stattfinden...
    Der Multiplex-Betrieb läuft mit 1 ms. (anhand von Experimenten werden zur sauberen Anzeige 4-5 ms benötigt) Auf eine Millisekunde habe ich den umgestellt, um damit später evtl. auch gleich eine Sekunde messen zu können

    INLINE wäre mir in diesem Kontext neu, ist das eine Art von Makro? Oder reicht es folgendermaßen zur Kennzeichnung:
    Code:
    inline void digit(uint8_t wert, uint8_t pin)
    {
        PORTD  = (PORTD & bitmaske) | numbers[wert];     
        PORTC |= (1 << pin);   
    }
    MfG Nik
    Geändert von Liquidator (22.03.2013 um 14:20 Uhr)

  4. #24
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Das Kopieren von Z nach zaehler1_ofv wäre eine Möglichkeit den Stand in der ISP ISR zu sichern.

    Der Zusatz INLINE sorgt dafür das der Code direkt eingesetzt wird, und kein echtes Unterprogramm Erzeugt wird. Je nach Einstellung des Compilers wird das zur Optimierung ggf. auch automatisch gemacht. Zumindest die älteren GCC Versionen erzeugen noch zusätzlich ein nie benutztes Unterprogramm, denn der Compiler unterscheidet nicht nach einem kompletten Programm und einem Teil, der ggf. noch eingebunden wird. Um das zu verhindern wäre STATIC INLINE die richtige Kennzeichnung - damit weiss der Compiler, dass das Unterprogramm nicht noch extern gebraucht wird.
    Das Gleiche könnte man auch erreichen indem an einen Makro (mit #Define ) nutzt.

    Die ISR zum Multiplexing kriegt man kürzer, indem man ein Puffer direkt für die Werte anlegt, die ausgegeben werden sollen. Also hier numbers[einer],numbers[zener],... .Es müssen dann nur noch die Werte aus diesem Array ausgegeben werden. Die Überprüfung auf führende Nullen und die Umrechnung von Ziffern in die Segmente erfolgt dann im nicht zeitkritischen Hauptprogramm.

    Die ISR sähe dann etwa so aus, gleich ohne die extra Funktion Digit:

    PORTD = (PORTD & bitmaske) | puffer[position];

    position++; // beim nächsten Mal nächstes Digit
    if(position == 4) // wenn alle Digits durch
    {
    position = 0; // von vorne beginnen
    }

  5. #25
    Benutzer Stammmitglied
    Registriert seit
    16.04.2011
    Beiträge
    78
    Guten Tag Besserwessi,

    alles klar, soweit verstanden, werde es umsetzen.
    Code:
    STATIC INLINE void digit(uint8_t wert, uint8_t pin)
    {
        PORTD  = (PORTD & bitmaske) | numbers[wert];     
        PORTC |= (1 << pin);   
    }
    Zur Optimierungdes Multiplex werde ich mir das Ganze nochmal anschauen, aber noch versteh ich nicht so ganz, wieso die z-variable nicht in main(), sondern am Ende der CAPT_ISR in zahler1_ovf kopiert werden soll? Dann müsste auch z zum Ende der ISR hin annulliert werden, nehm' ich an...

    MfG Nik

  6. #26
    Erfahrener Benutzer Robotik Visionär
    Registriert seit
    26.11.2005
    Ort
    bei Uelzen (Niedersachsen)
    Beiträge
    7.942
    Das hochzählen von z in der Overflow ISR geht auch nach dem ICP Interrupt weiter. Bei der Startzeit ist das kein Problem, weil da z zum richtigen Zeitpunkt in der ISR auf 0 gesetzt wird. Aber beim Ende der Periode vergeht zwischen dem ICP Interrupt und der Ausgabe im Hauptprogramm noch etwas Zeit, und in der Zeit kann noch 1 Timer Überlauf stattfinden. Das ist nicht viel Zeit, aber das reicht damit mit einer gewissen Wahrscheinlichkeit (etwa 0,1-1%) ein Überlauf zu viel angezeigt wird.

  7. #27
    Benutzer Stammmitglied
    Registriert seit
    16.04.2011
    Beiträge
    78
    Klingt plausibel, danke für die Erklärung
    Anmerkung zu den static inline Funktionen: Je nach Linker/Compiler müssen sie auch extra oben deklariert werden, sonst meckern die Ersteren.
    Code:
    static inline void digit(uint8_t wert, uint8_t pin);
    MfG Nik

    EDIT: Eine andere Möglichkeit in der Optimierung der Multiplex-Routine wäre wohl ein Multiplexer-Baustein, ähnlich dem MAX7219 - spart auch einiges an Ports.

    EDIT 2: Heute die Möglichkeit gehabt das Ganze am Funktionsgenerator zu testen - nach langem Suchen habe ich auf die inline-Deklarierung verzichtet und zumindest wurde die Anzeige aktiviert.
    Zur Messgenauigkeit: sie ist bei einer ganzen Zahl wirklich gut - schwankt +-1 Hz, lässt sich einfach korrigieren. Aktuell Suche ich nach einem Codefehler, der die Anzeige flimmern lässt und zwar ganz unregelmäßig - mal eine Anzeige, mal alle.

    EDIT3: Lasse die Platine mit ISP-Buchse fertigen und werde dann nach dem Fehler suchen, ich melde mich
    Geändert von Liquidator (28.03.2013 um 16:40 Uhr)

  8. #28
    Benutzer Stammmitglied
    Registriert seit
    16.04.2011
    Beiträge
    78
    Hallo nochmal,

    die Platine wurde angefertigt, doch flackert die Anzeige, als ob ein falsches Multiplexing vorliegt. Zwischendurch sieht man die völlig korrekt angezeigte Frequenz.
    Leider finde ich auf Anhieb keine Fehler im Quelltext...
    Ob jemand einen Tipp hätte, mir gehen die Möglichkeiten aus

    Bedanke mich im Voraus,
    MfG Nik

Seite 3 von 3 ErsteErste 123

Ä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
  •  

LiFePO4 Speicher Test