Forum: Mikrocontroller und Digitale Elektronik Drehzahlmessung mittels AVR


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von c.beginner (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Guten Tag,

ich möchte gerne mit einem ATmega88 die Drehzahl eines Motors erfassen. 
Dazu nutze ich einen Hall Sensor, der pro Motorumdrehung einen Impuls 
erzeugt. Die Impulse erfasse ich mithilfe der Input Capture Einheit des 
Controllers.
Da ich den Timer 1 schon zum generieren eines PWM Signales nutze, läuft 
dieser mit einer Frequenz von ca. 20 kHz (prescaler von 1). Gleichzeitig 
möchte ich über diesen Timer die Dauer einer Motorumdrehung messen, um 
daraus die Drehzahl zu berechnen. Der Code dazu sieht folgendermaßen 
aus:
#include <avr/io.h>
#include <avr/interrupt.h>

#define true 1 
#define false 0
#define F_CPU 8000000UL
#define periodendauer_timer1 (1000000 / (F_CPU/1024))  //in us
  
volatile uint16_t ueberlauf;
volatile uint16_t icr1_wert_1;
volatile uint16_t icr1_wert_2;
volatile uint16_t uerberlauf_gesp;
volatile uint8_t messung_beendet;
uint16_t drehzahl;

void timer_1_init(void)       
{
  TCCR1A |= (1<<WGM10) | (1<<WGM11) | (1<<COM0A1);  //WGM10, WGM11, WGM12 = 1; 10-Bit Fast PWM Mode, OC1A(PB1) = PWM output
  TCCR1B |= (1<<WGM12) | (1<<ICNC1) | (1<<ICES1);    //ICNC1, ICES1 = 1; Input Capture am ICP1 Pin (PB0) aktiviert mit Filter
  TCCR1B |= (1<<CS10);                //CPU clk / 1 (Kein Teiler)
  TIMSK1 |= (1<<TOIE1) | (1<<ICIE1);      //Interrupt bei: Input Capture und TimerÜberlauf
}


int main(void)
{
   DDRD |= (1<<PD0) | (1<<PD1) | (1<<PD6); 
   DDRB |= (1<<PB1);

   timer_1_init();
  
   sei ();        //Glogale Interrupts aktivieren
  
   while (1) 
   {    
     if (messung_beendet == true)
     {
           messung_beendet = false;
           uint32_t x = uerberlauf_gesp * periodendauer_timer1;
           uint16_t y = (1024-icr1_wert_1 + icr1_wert_2) * (periodendauer_timer1 / 1024.0);
    
           drehzahl = (60000000 / (x + y));
      }
    }
}

ISR (TIMER1_OVF_vect)
{
   OCR1A = pwm_duty_ocr1a;  //Übernahme des PWM Tastgrads von der Regelung
   ueberlauf ++;
}

ISR (TIMER1_CAPT_vect)
{
  static uint8_t durchlauf;
  
  if (durchlauf == 0)
  {
    ueberlauf = 0;
    icr1_wert_1 = ICR1;
    durchlauf ++;
  }
  else if (durchlauf == 1)
  {
    durchlauf = 0;
    icr1_wert_2 = ICR1;
    uerberlauf_gesp = ueberlauf;
    messung_beendet = true;
  }
}
Da ich noch Anfänger bin, wollte ich Euch fragen, ob Ihr das anders 
lösen würdet oder ob Ihr Verbesserungsvorschläge zum Code habt.

von foobar (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Was mir auffällt: TIMER1_Capt aktualisiert ständig wert_1, wert_2 und 
ueberlauf_gesp, die main sieht also nicht immer konsistente Werte. Evtl 
in dem Interrupt die Werte nur ändern, wenn messung_beendet==false ist 
(in main das entspr auch erst setzen, wenn die Auswertung beendet ist.

von c-hater (Gast)


Bewertung
0 lesenswert
nicht lesenswert
c.beginner schrieb:

> Da ich den Timer 1 schon zum generieren eines PWM Signales nutze, läuft
> dieser mit einer Frequenz von ca. 20 kHz (prescaler von 1).

Bei einem Prescaler von 1 wären 20kHz für den Timer eher ungewöhnlich...
Du meintest 20MHz, nicht wahr?

> Gleichzeitig
> möchte ich über diesen Timer die Dauer einer Motorumdrehung messen, um
> daraus die Drehzahl zu berechnen.

Soweit OK. Du musst halt nur beachten, dass der Timer in dieser 
Konfiguration an der Grenze zum 11. Bit überläuft und nicht erst beim 
Overflow eines freilaufenden Timers (also beim Wackeln im virtuellen 17. 
Bit).

Die Auswertung muss auf diesen Sachverhalt angepasst werden. C&P-Code 
hilft dir hier nicht weiter.

von c.beginner (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
foobar schrieb:
> Was mir auffällt: TIMER1_Capt aktualisiert ständig wert_1, wert_2 und
> ueberlauf_gesp, die main sieht also nicht immer konsistente Werte.

Stimmt. Wenn die main Routine nicht schnell genug abgearbeitet wird, 
kann das passieren.

c-hater schrieb:
> Bei einem Prescaler von 1 wären 20kHz für den Timer eher ungewöhnlich...
> Du meintest 20MHz, nicht wahr?

Ich meinte mit den 20kHz die Anzahl der Überläufe pro Sekunde. Bei einem 
Prescaler von 1 und TOP Wert von 1024 läuft der Timer alle 51us über, 
was bei 20MHz CPU Takt etwa 19531Hz wären.

von JK (Gast)


Bewertung
0 lesenswert
nicht lesenswert
> #define F_CPU 8000000UL
> #define periodendauer_timer1 (1000000 / (F_CPU/1024))  //in us

Wo sind da 20 MHz, oder etwa 20 kHz?

von c.beginner (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
JK schrieb:
>> #define F_CPU 8000000UL
>> #define periodendauer_timer1 (1000000 / (F_CPU/1024))  //in us
>
> Wo sind da 20 MHz, oder etwa 20 kHz?

Momentan läuft der ATmega mit dem internen Oszillator. Später soll er 
mit einem externen 20MHz Quarz betrieben werden.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.