-         

Seite 1 von 2 12 LetzteLetzte
Ergebnis 1 bis 10 von 12

Thema: 2 Frequenzen Messen Atmega8-16Mhz, bis 267Hz und 53Hz

  1. #1
    Neuer Benutzer Öfters hier
    Registriert seit
    21.10.2006
    Beiträge
    22

    2 Frequenzen Messen Atmega8-16Mhz, bis 267Hz und 53Hz

    Anzeige

    Ich wollte mit einem Atmega8 16Mhz zwei Sensoren einlesen. Und zwar ist das einmal ein Drehzahlsensor und ein Geschwindigkeitssensor. Beide geben 0V/5V raus pro Umdrehung. Die Drehzahl wird bis 267Hz gemessen und die Geschwindigkeit bis 53Hz.

    16000 U/min ~ 267 Hz
    270 km/h bei ca. 1,44m Radumfang ~ 53 Hz

    Wenn beide Frequenzen gemessen worden sind, soll die Übersetzung errechnet werden und mit der bekannten Getriebeübersetzung verglichen werden. Dann sollen 1-6 Leds leuchten für jeden Gang.

    Wie kann ich mit dem Atmega8 zwei Frequenzen gleichzeitig messen? Welchen Weg soll ich gehen:

    1. Einen Timer laufen lassen und Flanken zählen.
    2. Die Zeit zwischen zwei Flanken messen.
    3. Zwei LM 2907 an zwei AD-Wandler schalten.

    Welche Lösung ist genauer? Denn die Getriebeabstufung ist im 5. und 6. Gang ja nicht mehr so groß.

  2. #2
    Erfahrener Benutzer Robotik Einstein Avatar von wkrug
    Registriert seit
    17.08.2006
    Ort
    Dietfurt
    Beiträge
    1.892
    Bei so niedrigen Frequenzen dürfte wohl die Methode 2 die genaueste und vor allem schnellste sein.
    Und du erhältst bei jedem neien Impuls ein Ergebnis.
    Bei Methode 1 üblicherweise nur jede Sekunde.
    Du darfst halt nur die Teilerfaktoren für den verwendeten Timer bei Methode 2 nicht zu hoch einstellen.
    Je höher der Teilerfaktor um so höher der Messfehler.
    Allerdings produzieren kleinere Teilerfaktoren mehr Timer-Interrupts.

    Ich hab das mal mit einem ATMEGA8 bei 8MHz und einem Teilerfaktor von 8 gemacht.

    Erst bei ca. 15000 U/min war deine Methode 1 genauer (60 U/min Sprünge).

  3. #3
    Neuer Benutzer Öfters hier
    Registriert seit
    21.10.2006
    Beiträge
    22
    Um den Timer flankengesteuert zu starten und zu stoppen wird ja der ICP-Pin verwendet. Leider hat der Atmega8 nur einen davon. Wie starte ich dann gleichzeitig zwei Timer parallel?

  4. #4
    Erfahrener Benutzer Robotik Einstein Avatar von wkrug
    Registriert seit
    17.08.2006
    Ort
    Dietfurt
    Beiträge
    1.892
    Den ICP Pin kannst Du ja schon mal verwenden.
    Als zweiten Eingang nimmst Du einen Interrupt (INT 0).
    In diesen Interrupts wird nur der eine !!! Timer ausgelesen und in einem Hilfsregister (RAM) abgelegt und ein Flag gesetzt, das es einen neuen Wert gibt.
    Das Hauptprogramm fragt dann dieses Flag ab und berechnet die entsprechenden Drehzahlwerte.
    Da die Anzahl der Takte bis zur Abfrage des Timerstandes immer gleich ist, spielt es auch keine Rolle ob der Counter bis dahin weitergelaufen ist (beim INT 0).
    Nur wenn Du für den Timer Überlaufregister brauchst, weil dir der Zählumfang von 65536 nicht mehr reicht könnte es beim INT 0 zu Problemen kommen.
    Bei ICP ist das natürlich kein Problem.

    Ich hab meine Interruptroutine so aufgebaut, das in beiden Fällen nur der Zählerstand von TCNT1 abgefragt wird und dieser dann im RAM für die jeweilige Drehzahl abgelegt wird.
    Dieser Programmteil ist bei mir in Assembler.
    (Na gut meine Drehzahlmessung funktioniert aber auch bis 800.000 U/min fehlerfrei.)
    Der vorherige aktuelle Wert wird in eine "_OLD" Variable umgespeichert, weil Du die ja für die Berechnung der jeweiligen Drehzahl brauchst.
    Dann wird noch ein BIT Flag gesetzt (z.B. dr1_new=1; ), damit dein Hauptprogramm weiß, das es einen neuen Drehzahlwert für die Drehzahl 1 gibt und die Berechnung vornehmen kann.
    Dann musst Du nur noch im Hauptprogramm das Verhältnis der beiden Drehzahlen feststellen und daraus den Gang ermitteln und die LED's zum leuchten bringen.
    Probleme seh ich dann nur noch wenn ausgekuppelt ist

  5. #5
    Neuer Benutzer Öfters hier
    Registriert seit
    21.10.2006
    Beiträge
    22
    Ich habe das mal soweit versucht zu realisieren, wie ich konnte. Den Code habe ich angehängt. Leider habe ich noch einen schweren Fehler. Die Frequenz am INT0 stimmt (noch) nicht. Der Fehler liegt wohl in der Umrechnung. Am ICP1 stimmt die Messung. Dann habe ich noch das Problem, dass zwischendurch die Messung am INT0 einen Ausreißer hat.

    Code:
    //##################################################################
    //# MC: Atmega8 16Mhz                                              #
    //# Compiler: AVR-GCC 4.1.2 20061115                               #
    //# Version: 1.01                                                  #
    //# Ports: (TXD) PD1 (3); (ICP1) PB0 (14), (INT0) PD2 (4)          #
    //# Funktion: Frequenzzahler, Timer0 an INT0, Timer1 an ICP        #
    //##################################################################
    
    #define F_CPU 16000000
    
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <stdint.h>
    #include <util/delay.h>
    #include "rs232.c"
    
    
    #ifndef TRUE
    #define TRUE 1
    #define FALSE 0
    #endif
    
    volatile unsigned char NumberOverflow0 = 0;  // Anzahl Timer0 Overflows
    volatile unsigned char NumberOverflow1 = 0;  // Anzahl Timer1 Overflows 
    volatile unsigned int  StartTime0 = 0;        // TCNT0-Wert bei 1.High-Flanke speichern
    volatile unsigned int  StartTime1 = 0;        // ICR1-Wert bei 1.High-Flanke speichern
    volatile unsigned int  EndTime0 = 0;          // TCNT0-Wert bei 2.High-Flanke speichern
    volatile unsigned int  EndTime1 = 0;          // ICR1-Wert bei 2.High-Flanke speichern
    volatile unsigned char Update0;               // Flag
    volatile unsigned char Update1;               // Flag
    
    //----------------------------------------------------------------------------------
    // Flankenauswertung Timer0 INT0
    ISR(INT0_vect)
    {
     static unsigned char ErsteFlanke0 = TRUE;
    
     if( Update0 && Update1)
      return;
    
     if( ErsteFlanke0 )
     {
      StartTime0 = TCNT0;
      NumberOverflow0 = 0;
      ErsteFlanke0 = FALSE;       // Die naechste Flanke ist das Ende der Messung
      }
    
      else
      {
       EndTime0 = TCNT0;
       Update0 = TRUE;         // Eine vollstaendige Messung. Sie kann ausgewertet werden
       ErsteFlanke0 = TRUE;    // Bei der naechsten Flanke beginnt der naechste Messzyklus
      }
    }
    //---------------------------------------------------------------------------------
    // Flankenauswertung Timer1 ICP1
    ISR( TIMER1_CAPT_vect )
    {
     static unsigned char ErsteFlanke1 = TRUE;
    
     if( Update1 && Update0 )
      return;
    
     if( ErsteFlanke1 )
     {
      StartTime1 = ICR1;
      NumberOverflow1 = 0;
      ErsteFlanke1 = FALSE;       // Die naechste Flanke ist das Ende der Messung
      }
    
      else
      {
       EndTime1 = ICR1;
       Update1 = TRUE;         // Eine vollstaendige Messung. Sie kann ausgewertet werden
       ErsteFlanke1 = TRUE;    // Bei der naechsten Flanke beginnt der naechste Messzyklus
      }
    }
    //----------------------------------------------------------------------------------
    // Timer0 Overflows zählen
    ISR( TIMER0_OVF_vect )
    {
      NumberOverflow0++;
    }
    //----------------------------------------------------------------------------------
    // Timer1 Overflows zählen
    ISR( TIMER1_OVF_vect )
    {
      NumberOverflow1++;
    }
    //---------------------------------------------------------------------------
    int main(void)
    {
      double Erg0 = 0.0, Erg1 = 0.0;
      char Wert0[8], Wert1[8];
    
      initrs232();
    
    
      DDRB &= ~(1<<PB0); //PB0 als Eingang
      PORTB |= (1<<PB0); //PB0 Pullup nach Vcc
    
      DDRD &= ~(1<<PD2); //PD2 als Eingang
      PORTD |= (1<<PD2); //PD2 Pullup nach Vcc
    
      GICR |= (1<<INT0);  //INT0 aktivieren
      MCUCR |= (1<<ISC00) | (1<<ISC01); //pos.Flanke an INT0
    
      //Timer0 8bit
      TCCR0 |= (1<<CS00); // Kein Prescaling, 
      TIMSK |= (1<<TOIE0); //Overflow Interrupt aktiviert
    
      //Timer1 16bit, 
      TCCR1B |= (1<<CS10) | (1<<ICES1); //Kein Prescaling 
      TIMSK |= (1<<TICIE1) | (1<<TOIE1); //pos. Flanke, Overflowinterrupt aktivieren
    
      sei(); //globale Interrupts aktivieren
    
      while(1)
        {
    
    
    	if( Update0 && Update1) //Auf vollständige Messung prüfen
      	{
    
    		Erg0 = (NumberOverflow0 * 256) + EndTime0 - StartTime0;
    		Erg1 = (NumberOverflow1 * 65536) + EndTime1 - StartTime1;
    		Erg0 = F_CPU / Erg0;
    		Erg1 = F_CPU / Erg1;
    
    		dtostrf( Erg0, 5, 3, Wert0 );
    		dtostrf( Erg1, 5, 3, Wert1 );
    
    		uart_puts("Frequenz an INT0: ");
    		uart_puts( Wert0 );
    		uart_puts(" Hz ");
    		
    		uart_puts("Frequenz an ICP: ");
    		uart_puts( Wert1 );
    		uart_puts(" Hz\n");
    
    		Update0 = FALSE;
    		Update1 = FALSE;
    		
            }
        }
    }

  6. #6
    Erfahrener Benutzer Robotik Einstein Avatar von wkrug
    Registriert seit
    17.08.2006
    Ort
    Dietfurt
    Beiträge
    1.892
    Wo deine Aussreisser herkommen kann ich jetzt genau auch nicht sagen.
    Ich vermute das es sich dabei um einen Überholeffekt handelt.
    Der Timer0 Overflow Interrupt wurde ausgelöst kann aber den Überlaufzähler nicht inkrementieren, weil ein anderer Interrupt gerade läuft und das SEI Flag somit gelöscht ist.
    Der TCNT0 ist dann auf 0 ohne den Überlaufzähler NumberOverflow0 zu inkrementieren.
    Das Problem würde sich verbessern, wenn du für beide Routinen den Timer 1 verwenden würdest, da dieser 16 Bits hat und der Fehler dadurch wesentlich seltener auftritt.
    Ausserdem brauchst Du bei deiner Methode immer 2 interrupt Zyklen um die Drehzahl zu bestimmen.
    Wenn du, während der INT0 / ICP Interruptroutine, den Zählerstand der vorherigen Messung in den OLD Vektor schiebst und den neuen Zählerstand in den NEW Vektor einließt kannst Du bei jeder Flanke eine Drehzahlmessung vornehmen.
    Das heisst es geht doppelt so schnell.

    Ausserdem würde ich noch ein Timeout einbauen um den Leerlauf, sowie einen stehenden Motor zu detektieren.

    z.B. 2 sek kein Geschwindigkeitssignal aber Drehzahlsignal vorhanden = Leerlauf.
    2 sek gar kein Signal = Motor aus.

    Ausserdem würd ich das Programm mal mit dem Simulator vom AVR Studio austesten - sollte mit AVR GCC gehen.

    Ich benutz leider Codevision AVR und kann deshalb dein Prog nicht austesten.

  7. #7
    Neuer Benutzer Öfters hier
    Registriert seit
    21.10.2006
    Beiträge
    22
    Der ICP-Pin startet und stoppt ja den Timer1. Würde das dann nicht die zweite Messung stören, wenn plötzlich der Timer1 zurückgesetzt wird? Der erste Wert wird gespeichert und der zweite Wert ist dann vielleicht null. Ich habe es leider noch nicht ganz verstanden.

    Die Sache mit der Leerlauferkennung etc. kommt später dazu. Erstmal bin ich schon froh, wenn beide Frequenzen richtig gemessen werden.

  8. #8
    Erfahrener Benutzer Robotik Einstein Avatar von wkrug
    Registriert seit
    17.08.2006
    Ort
    Dietfurt
    Beiträge
    1.892
    Der ICP-Pin startet und stoppt ja den Timer1. Würde das dann nicht die zweite Messung stören, wenn plötzlich der Timer1 zurückgesetzt wird? Der erste Wert wird gespeichert und der zweite Wert ist dann vielleicht null. Ich habe es leider noch nicht ganz verstanden.
    Ich kann mich natürlich auch irren, aber soweit ich das weiß ist das nicht so.
    Der Zählerstand des TCNT1 wird lediglich in das ICP Register übertragen, wenn ein ICP Interrupt ausgelöst wird.
    Dieses Register sollte aber von Dir nur während der ICP Interrupt Routine ausgelesen werden.
    In deiner INT0 Routine solltest Du das TCNT1 Register auslesen und zur Berechnung heranziehen.
    Was Du meinst ist vermutlich das CTC Bit.
    Aber das brauchst Du ja bei der von mir vorgeschlagenen Meßmethode nicht zu verwenden.

    Kannst das ja mal mit dem Simulator vom AVR Studio austesten.

  9. #9
    Neuer Benutzer Öfters hier
    Registriert seit
    21.10.2006
    Beiträge
    22
    So, jetzt habe ich mal AVR Studio installiert und das Programm untersucht. Es klappt mit dem Timer1! Damit es einheitlicher ist verwende ich jetzt den INT0 und INT1. Auf jeden Fall werden jetzt beide Frequenzen gleich gemessen und das mit einen Timer.

    Endlich funktioniert es.

  10. #10
    Neuer Benutzer Öfters hier
    Registriert seit
    21.10.2006
    Beiträge
    22
    Das war leider ein Fall von "zu früh gefreut". Man kann jeweils eine Frequenz ausreichend genau messen, aber sobald beide anliegen, wird nur undefinierte Werte ausgegeben. Hat jemand eine Idee? Ich komme nicht mehr weiter...

Seite 1 von 2 12 LetzteLetzte

Berechtigungen

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