Forum: Mikrocontroller und Digitale Elektronik Drehzahlmessung mittels AVR


von c.beginner (Gast)


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:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3
4
#define true 1 
5
#define false 0
6
#define F_CPU 8000000UL
7
#define periodendauer_timer1 (1000000 / (F_CPU/1024))  //in us
8
  
9
volatile uint16_t ueberlauf;
10
volatile uint16_t icr1_wert_1;
11
volatile uint16_t icr1_wert_2;
12
volatile uint16_t uerberlauf_gesp;
13
volatile uint8_t messung_beendet;
14
uint16_t drehzahl;
15
16
void timer_1_init(void)       
17
{
18
  TCCR1A |= (1<<WGM10) | (1<<WGM11) | (1<<COM0A1);  //WGM10, WGM11, WGM12 = 1; 10-Bit Fast PWM Mode, OC1A(PB1) = PWM output
19
  TCCR1B |= (1<<WGM12) | (1<<ICNC1) | (1<<ICES1);    //ICNC1, ICES1 = 1; Input Capture am ICP1 Pin (PB0) aktiviert mit Filter
20
  TCCR1B |= (1<<CS10);                //CPU clk / 1 (Kein Teiler)
21
  TIMSK1 |= (1<<TOIE1) | (1<<ICIE1);      //Interrupt bei: Input Capture und TimerÜberlauf
22
}
23
24
25
int main(void)
26
{
27
   DDRD |= (1<<PD0) | (1<<PD1) | (1<<PD6); 
28
   DDRB |= (1<<PB1);
29
30
   timer_1_init();
31
  
32
   sei ();        //Glogale Interrupts aktivieren
33
  
34
   while (1) 
35
   {    
36
     if (messung_beendet == true)
37
     {
38
           messung_beendet = false;
39
           uint32_t x = uerberlauf_gesp * periodendauer_timer1;
40
           uint16_t y = (1024-icr1_wert_1 + icr1_wert_2) * (periodendauer_timer1 / 1024.0);
41
    
42
           drehzahl = (60000000 / (x + y));
43
      }
44
    }
45
}
46
47
ISR (TIMER1_OVF_vect)
48
{
49
   OCR1A = pwm_duty_ocr1a;  //Übernahme des PWM Tastgrads von der Regelung
50
   ueberlauf ++;
51
}
52
53
ISR (TIMER1_CAPT_vect)
54
{
55
  static uint8_t durchlauf;
56
  
57
  if (durchlauf == 0)
58
  {
59
    ueberlauf = 0;
60
    icr1_wert_1 = ICR1;
61
    durchlauf ++;
62
  }
63
  else if (durchlauf == 1)
64
  {
65
    durchlauf = 0;
66
    icr1_wert_2 = ICR1;
67
    uerberlauf_gesp = ueberlauf;
68
    messung_beendet = true;
69
  }
70
}
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)


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)


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)


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)


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)


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.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.