-         

Ergebnis 1 bis 6 von 6

Thema: Zeit zweischen zwei Flanken (Drehzahlmesser)

  1. #1
    Benutzer Stammmitglied
    Registriert seit
    16.10.2005
    Ort
    Adendorf
    Alter
    35
    Beiträge
    46

    Zeit zweischen zwei Flanken (Drehzahlmesser)

    Anzeige

    Hi

    Ich möchte die Drehzahl von zwei Motoren (RB40) mit hilfe eines Mega16 ermitteln.
    An jedem Motor ist eine Lichtschranke als Taktgeber befestigt, die mit
    jeder Umdrehung des Motor 8 Flanken (vier schwarze vier weize Flachen) erzeugt.

    Um daraus die Drehzahl zu ermitteln gibt es ja zwei Methoden.
    Zum einen Zählt man die Flagen während einer Festen Zeitspanne und
    zum anderen misst man die Zeit, die zwischen zwei Flanken vergeht.

    Die erste Variante habe ich bereits erfolgreich ausprobiert. Timer1 Zählte die Flankenwechsel
    und timer0 rief diesen Werte alle 500ms ab. Die Werte wurden dann Über UART asugegeben.

    Da ich aber timer1 für die Ansteuerung der Motoren benötige(PWM) und timer0
    eh für Zeitliche Aufgaben gedacht ist, wollte ich die Variante zwei einmal Programieren.
    Dazu habe ich mir Vorgestellt, dass ich mit jeder Flanke auf eine Uhr(timer0) schaue und
    mir die Zeit Merke. Bei der nächsten Flanke bilde ich die differenz aus der neuen und
    der alten Uhrzeit und erhalte so die Zeit zwischen den Flanken. Wie genau ich das machen
    möchte hängt von der Einstellung der Uhr ab. Der timer0 macht 256 Schritte bis er einmal
    rum ist, dabei dauer jeder Schritt 5,8us (11,059Mhz, und Prescaler von 64).
    Also ist die Uhr nach 1,48ms einmal rum. Natürlich kann es vorkommen das die Uhr bereits
    einmal rum ist, eh die zweite Flanke kommt. Dafür habe ich mir einem 16bit Merker erstellt,
    der die Umrundungen der Uhr zwischen zwei Flanken zählt. Da ich die Uhr nicht stoppe, kann
    ich auch zwei drehzahlen Gleichzeitig erfassen(allerdings zwei Merker).
    So weit so gut..

    Nun habe ich mir die Werte mal alle 50ms über UART ausgeben lassen und bekomme Regelmäßig
    keine richtigen Ergebnisse( siehe Abbildung).

    Jetzt die Frage an euch: Woran könnte dies liegen und wie kann ich es besser machen.



    Erklärung zur abbildung:
    die X-Achse zeigt die Messdauer in Sekunden
    die Y-Achse die Zeit zwischen zwei Impulsen in usec

    der Motor drehte ohne Lasst mit 12V, also mit etwa 5000U/min
    daraus sollte eine Umdreung 12ms dauern und die impulsdauer 1,5ms

    Code:
    /* Interrupt Service Rountine bei Überlauf des Timer0 */
    ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW1) */
    {
        /* Interrupt Code */
    	timer0_counter++;			// Merker für Überläufe des Timers
    	timer0_counter_D1++;		// Merker für Überläufe des Timers, für die Drehzahl1
    	
    	if(timer0_counter % 3){	// alle 4,4ms
    	}else{
    		//tasten(); 			// Aufruf zur tasten entprellung	
    	}
    
    	if(timer0_counter % 30){ 	// alle 44ms 
    	}else {
    		auswerten();			// den Wert über UART ausgeben
    	}	
    
    	if( (timer0_counter >= 255)){	// alle 380ms 
    		PORTB ^=(1<<PB3);			// eine LED getoggelt
    	}	
    }
    
    /*Interrupt Service Routine bei Flanke an int0(extern) */
    ISR(INT0_vect)
    {
    	PORTB ^=(1<<PB2);								// Toggel Led, wildes blinken ist immer gut
    	counter_neu = (timer0_counter_D1*256) + TCNT0;	// Merke mir die Uhrzeit
    	timer0_counter_D1=0;							// sezte den Umrundungsmerker zurück
    	if(counter_neu > counter_alt){
    		time_delta = counter_neu - counter_alt;}	// ermittel die Zeitdifferenz
    	else{
    		time_delta = counter_alt - counter_neu;}	// ermittel die Zeitdifferenz
    	counter_alt = counter_neu;						// Merke mir die Uhrzeit für die nächste Messung
    }
    Miniaturansichten angehängter Grafiken Miniaturansichten angehängter Grafiken image001.gif  
    http://wsgs.blogspot.com

    Wenn deine Freundin denkt,du bist bei deiner Frau,und deine Frau denkt,du bist bei deiner Freundin, dann hast du endlich Zeit ins Labor zu gehen

  2. #2
    Erfahrener Benutzer Robotik Einstein Avatar von SprinterSB
    Registriert seit
    09.06.2005
    Ort
    An der Saar
    Beiträge
    2.801
    time_delta kann bei dieser Rechnung negativ sein.

    Welche Datentypen verwendest du? An so einem Schnippsel kann man nix sagen. Ich vermite mal 16 Bit. Greifst du atomar drauf zu?
    Disclaimer: none. Sue me.

  3. #3
    Erfahrener Benutzer Fleißiges Mitglied
    Registriert seit
    07.05.2006
    Beiträge
    183
    Hi,
    Kommt es eventuell zu einem Counteroverflow? Oh hast du schon bedacht.
    Vielleicht ist das Signal "Unsauber" -> Oberwellen, Spikes

  4. #4
    Benutzer Stammmitglied
    Registriert seit
    16.10.2005
    Ort
    Adendorf
    Alter
    35
    Beiträge
    46
    also
    8Bit
    timer0_counter_D1
    TCNT0
    timer0_counter_D1 habe ich als volatile definiert, glaube es heißt das der Wert sofort geschrieben werden soll.
    TCNT0 ist der Direkte TimerWert.

    16Bit
    counter_neu
    counter_alt
    time_delta

    das mit dem negative Time_delta habe ich nun auch ausprobiert.
    habe eine If-Bedingung eingefügt, die die differenz janachdem ob der alte oder neue Wert größer ist, bildet.
    Code:
    	if(counter_neu > counter_alt){
    		time_delta = counter_neu - counter_alt;}	// ermittel die Zeitdifferenz
    	else{
    		time_delta = counter_alt - counter_neu;}	// ermittel die Zeitdifferenz
    http://wsgs.blogspot.com

    Wenn deine Freundin denkt,du bist bei deiner Frau,und deine Frau denkt,du bist bei deiner Freundin, dann hast du endlich Zeit ins Labor zu gehen

  5. #5
    Benutzer Stammmitglied
    Registriert seit
    16.10.2005
    Ort
    Adendorf
    Alter
    35
    Beiträge
    46
    Ihr hattet recht. Der Fehler lag an der falschen Berechnung der Zeitdifferens.
    So ist es richtig.
    Code:
     
    	if(counter_neu > counter_alt){
    		time_delta = counter_neu - counter_alt;}	// ermittel die Zeitdifferenz
    	else{
    		time_delta = (65535-counter_alt) + counter_neu;}	// ermittel die Zeitdifferenz
    Damit andere auch etwas davon haben hier der gesammte Code für einen Mega16. Ich verwende das ATmega16 Testboard v2.0 von kreatives-chaos.com
    main.c
    Code:
    /**        
    * Mit hilfe dieses Programmes soll die Drehzal 
    * über ein UART interfase am PC ansgegeben werden.
    * Dazu wird die Drehzal wie folgt ermittelt:
    *
    * Zeit(Timer0) zwischen zwei Impulsen
    * 
    * 
    * Hierzu wird bei einen Externen Interrupt(INT0), 
    * die bisher vergangene Zeit des Timers0 über UART ausgegeben
    *
    **/
    
    #include <avr/interrupt.h>
    #include <stdint.h> // integerwerte wie uint8_t, uint16_t
    #include <stdlib.h> // itoa
    #include "timer0.h"	// einbinden der Header datei für Timer0
    #include "uart.h"	// einbinden der Header datei für UART
    //nicht vergessen die Passenden C Dateien in der Makefile zu schreiben
    
    #define STATUS_LED1 PB3		// PIN der on-board Status-Led
    #define STATUS_LED2 PB2		// Pin der on-board Status_Led
    
    
    void init(void);			// allgemeine Initialisierung
    void auswerten(void);		// Status Ausgabe über UART
    
    volatile uint16_t counter_alt;		// alter Timerstand
    volatile uint16_t counter_neu;		// neuer Timerstand
    volatile uint16_t time_delta;		// Zeitdifferens
    
    volatile uint16_t m1,m2,m3,m4,m5,m6,m7,m8,m9,m0; //Mittelwertbildung
    volatile uint8_t sm;
    
    int main(void){	
    	
    	init();			// Allgemeine Port initialisierung
    	timer0_init();	// Timer0 initialisieren
    	uart_init();	// UART initialisieren
    	
    	sei();	//global Interrupts Aktivieren
    	
    	uart_putc('*');			//senden eines einzelenen Zeichen
    	uart_puts(" UART inizialisierung ... OK \n\r\n\r");	//sendet einen string
    /*uart_puts("* Drehzahl1\r\n* Mit hilfe dieses Programmes soll die Drehzal \r\n");
    uart_puts("* ueber ein UART interfase am PC ansgegeben werden.\r\n* Hierfuer wird die Drehzal wie folgt ermittelt. \r\n");
    uart_puts("* Zeit(Timer0) zwischen zwei Impulsen\r\n");
    uart_puts("* Hierzu wird alle durch einen Externen Interrupt(INT0) \r\n");
    uart_puts("* Die Bisher vergangene Zeit des Timers0 über UART ausgegeben\r\n");
    */
    
    	while(1)
    	{	}
    	//never reached   i hope ;)
    }
    
    
    void init(void)
    {
    	/* STATUS_LED1 und 2 als Ausgang  */
    	DDRB |= (1 << STATUS_LED1) | (1 << STATUS_LED2);
    	PORTB &= ~((1 << STATUS_LED1) | (1 << STATUS_LED2));
    
    	/* INT0 init */	
    	DDRD &= ~(1<<PD2);		//PORTD.2 als Eingang
    	PORTD |= (1<<PD2);		//PORTD.2 mit Pullup
    	GICR |= (1<<INT0);		// External Interrupt Request 0 Enable
    	MCUCR |= (1<<ISC01)|(1<<ISC01);	// Die steigende Flanke an INT0 erzeugt einen Interrupt. 
    	
    	/* globale Variablen vorbelegen */
    	counter_alt = 0; 		
    	counter_neu = 0;
    	m1=m2=m3=m4=m5=m6=m7=m8=m9=m0=0;
    }
    
    void auswerten(void)
    {
    	/* gibt Zählerstand über UART aus */
    	unsigned char s[7]; 		// max -65536 entspricht 7 Zeichen
    	
    	uart_puts("Drehzahl: ");	// Textausgabe
    	utoa(time_delta, s, 10);	// Uint_16 in Asci-String wandeln
    	uart_puts(s);				// Textausgabe 
    	uart_puts("      \r");		// Textausgabe
    }
    
    
    // Interrupt Service Routine bei Flanke an int0(extern) */
    ISR(INT0_vect)
    {
    	PORTB ^=(1<<PB2);								// Toggel Led, wildes blinken ist immer gut
    	counter_neu = (timer0_counter_D1*256) + TCNT0;	// Merke mir die Uhrzeit
    	//timer0_counter_D1=0;							// sezte den Umrundungsmerker zurück
    	if(counter_neu > counter_alt){
    		time_delta = counter_neu - counter_alt;}	// ermittel die Zeitdifferenz
    	else{
    		time_delta = (65535-counter_alt) + counter_neu;}	// ermittel die Zeitdifferenz
    	
    	/* gleitender Mittelwert über 10 Messungen */	
    	//m9=m8;m8=m7;m7=m6;m6=m5;m5=m4;m4=m3;m3=m2;m2=m1;m1=m0;m0=time_delta;
    	//time_delta=(m1+m2+m3+m4+m5+m6+m7+m8+m9+m0)/10;	
    	
    	counter_alt = counter_neu;						// Merke mir die Uhrzeit für die nächste Messung
    }
    timer0.c
    Code:
    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include "timer0.h"
    
    /* Timer0 initialisieren */
    void timer0_init(void)
    {
    	/* alle 1,48ms Überlauf.Einstellen der Frequenz auf 675Hz ( Prescaler = 64  * 256 bis Überlauf ) */
    	TCCR0 |= ((1<<CS01) | (1<<CS00));// Prescaler 64, entspricht 5,8usec und 1,48ms bis überlauf
    	
    	/* Interrupts für Timer0 aktivieren */	
    	TIMSK |= (1<<TOIE0);
    	
    	DDRB |= (1<<PB3);		// toggel led
    	
    	timer0_counter_D1=0;		// sezte den Anfangswert
    	timer0_counter=0;		// sezte den Anfangswert
    
    }	
    
    /* Interrupt Service Rountine bei Überlauf des Timer0 */
    ISR(TIMER0_OVF_vect) /* veraltet: SIGNAL(SIG_OVERFLOW1) */
    {
        /* Interrupt Code */
    	timer0_counter++;			// Merker für Überläufe des Timers
    	timer0_counter_D1++;		// Merker für Überläufe des Timers, für die Drehzahl1
    	
    	if(timer0_counter % 30){ 	// alle 44ms 
    	}else {
    		auswerten();			// den Wert über UART ausgeben
    	}	
    
    	if( (timer0_counter >= 255)){	// alle 380ms 
    		PORTB ^=(1<<PB3);			// eine LED getoggelt
    	}
    	
    }
    http://wsgs.blogspot.com

    Wenn deine Freundin denkt,du bist bei deiner Frau,und deine Frau denkt,du bist bei deiner Freundin, dann hast du endlich Zeit ins Labor zu gehen

  6. #6
    Erfahrener Benutzer Robotik Einstein Avatar von SprinterSB
    Registriert seit
    09.06.2005
    Ort
    An der Saar
    Beiträge
    2.801
    Wenn du in deiner Anwendung auf die 16-Bit Variablen zugreifst, dann ist das "volatile" nur die halbe Miete.

    Lies mal
    http://www.roboternetz.de/wissen/ind...-atomarer_Code
    Disclaimer: none. Sue me.

Berechtigungen

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