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?
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 }
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
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.
Hi,
Kommt es eventuell zu einem Counteroverflow? Oh hast du schon bedacht.
Vielleicht ist das Signal "Unsauber" -> Oberwellen, Spikes
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
Ihr hattet recht. Der Fehler lag an der falschen Berechnung der Zeitdifferens.
So ist es richtig.
Damit andere auch etwas davon haben hier der gesammte Code für einen Mega16. Ich verwende das ATmega16 Testboard v2.0 von kreatives-chaos.comCode: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
main.c
timer0.cCode:/** * 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 }
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
Wenn du in deiner Anwendung auf die 16-Bit Variablen zugreifst, dann ist das "volatile" nur die halbe Miete.
Lies mal
https://www.roboternetz.de/wissen/in...-atomarer_Code
Disclaimer: none. Sue me.
Lesezeichen