AVR Timer1 Zeit zwischen Maximalwert vom ADC messen

Gast #5376912
Lesenswert?

Hallo alle zusammen,

ich habe ein Sensorsignal am Eingang des AD-Wandler am Microcontroller 
ATmega32 angeschlossen und möchte die Zeit zwischen jeweils den 
Maximalwert des AD-Wandlers messen. Das Signal ähnelt einem Sinussignal. 
Das heisst der Timer1 soll beim Erreichen des Maximalwertes anfangen zu 
starten und beim Erreichen des nächsten Maximalwertes stoppen. Um den 
Maximalwert zu erfassen, habe ich ein kleines Programm geschrieben. Aber 
es klappt noch nicht ganz. Der Timer1 stoppt auch mitten im Signal, 
obwohl das Maximum nicht erreicht hat. Kann mir jemand weiterhelfen? 
Wäre sehr nett.
1
//#define F_CPU  16000000
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include "lcd.h"
5
#include <avr/interrupt.h>
6

7

8
/*unsigned int read_ad(void)
9
{
10
  // Funktion zum Auslesen des A/D-Wandlers
11
    ADCSRA |= (1<<ADSC);  // Wandler starten
12
  while(!(ADCSRA & (1<<ADIF)));  //warten auf Wandler
13
  return ADCW;
14
}
15
*/
16

17
volatile uint16_t messung = 0;
18
volatile uint16_t zeit = 0;
19
volatile uint16_t hilf= 0;
20
volatile uint16_t hilf2 = 0;
21

22
ISR(ADC_vect)        // Interrupt zum Auslesen des Wandlers
23
{
24
  messung = ADCW;
25
  
26
  if (messung >= hilf)      // ADC-Wert mit Hilfsvariable vergleichen
27
                // um den Maximalwert herauszufinden
28
  {
29
    hilf = messung;
30
    TCCR1B = (1<<CS12)|(1<<CS10);      // Timer-Konfiguration mit 1024 Vorteiler
31
    PORTB |= (1<<PB5);
32
    zeit = TCNT1;
33
    
34
  }
35

36
  else {
37
      TCCR1B = (0<<CS12)|(0<<CS10);
38
      
39
       }
40
}
41

42

43

44
int main (void)
45
{
46
  DDRB = 0b11101110;
47

48
  ADMUX  = (0<<REFS1)|(1<<REFS0)|(1<<MUX0);  // Ref=AVCC, PA1
49
  ADCSRA = (1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);  // Wandler an
50
                    // Teiler 128 -> 125kHz Takt,
51
  
52
                      
53
 
54
  sei();                // Interruptverarbeitung AN
55
 
56
  
57
  lcd_init();
58
  lcd_string("TESTPROGRAMM ADC");
59
  lcd_setpos(1,0);
60
  lcd_string("Wert: ");
61

62
  
63
  /*unsigned int x;*/
64

65

66
  while(1)                // Hauptschleife
67
  {
68
    /*x = read_ad();*/
69
  lcd_setpos(1, 7);
70
  lcd_int(messung);
71
  lcd_char(' ');
72
  lcd_int(zeit);
73

74
  
75

76
  if( messung< 500)          // If Abfrage über 500 2 LED GRÜN
77
    {
78
      PORTB = (1<<PB7)|(1<<PB6);    // unter 500 3 LED ROT
79
    }
80

81
    else
82
    {
83
      PORTB &=~((1<<PB7)|(1<<PB6));
84
      PORTB = (1<<PB3)|(1<<PB2)|(1<<PB1);
85
    }
86

87
  };
88
  return 0;
89
}
Angehängte Dateien:
Gast #5376941
Lesenswert?

Ohne das Programm im Detail angeschaut zu haben - der Timer wird beim 
ersten ADC-Interrupt gestartet, wann bzw. wo innerhalb dieses 
sinusähnlichen Signals das ist, also rein zufällig, und stoppt bei 
Erreichen des Maximums. Wie sollte da ein brauchbares Resultat 
entstehen?
Gast #5377011
Lesenswert?

Tim schrieb:
> Ich würde lieber das
> Maximum messen, da der Nulldurchgang nicht so "sauber" ist.

Das dürfte ein schwerwiegender Irrtum sein:

1. Wenn das Signal verrauscht ist, dann nicht nur beim Nulldurchgang.

2. Das Maximum hat keinen festen Wert, prinzipiell weiss man erst 
nachher, das da ein Maximum war, wenn das Signal wieder gefallen ist.

3. Die Änderungsgeschwindigkeit des Signals ist am Maximum am 
geringesten (nämlich Null), daher ist das der ungeeignetste Zeitpunkt 
überhaupt zur Auswertung.

Auf falschen Annahmen kann das Vorhaben nicht gelingen.

Georg
Gast #5377024
Lesenswert?

Was meint ihr denn mit "Nulldurchgang". Ist damit der Punkt gemeint, wo 
das Signal der geringsten Wert hat? Ich brauch eigentlich nur die Zeit 
zwischen einer Periode. Ob vom Maximum zum Maximum oder Minimum zum 
Minimum wäre eigentlich egal?

Danke für die Antworten
Gast #5377052
Lesenswert?

Tim schrieb:
> Ist damit der Punkt gemeint, wo
> das Signal der geringsten Wert hat?

Nein, eher der Mittelwert

Bei einem richtigen Sinuns gibt es eine positive (obere) und eine 
negative (untere) Halbwelle. Denk dir bei demnem Signall eine Virtuelle 
Nullline.

Eine Schwinung hat zwei Nulldurchgänge.
#5377058
Lesenswert?

Tim schrieb:
> Was meint ihr denn mit "Nulldurchgang". Ist damit der Punkt gemeint, wo
> das Signal der geringsten Wert hat?

Nein. Da wo das Signal den Wert zwischen Maximum und minimum durchläuft, 
dort ist es am steilsten.

Also: Max ermitteln, Min ermitteln Differenz halbieren und auf diesen 
Wert triggern.
Bsp. Max=800, Min=200 Mitte=500
ADC messen bis 500 dann Timer starten, wenn wieder 500 ist, dann ist 
gerade die andere Flanke vorbeigesaust also warten, beim nächsten mal 
500 Zeit ablesen. Fertig.
Gast #5377241
Lesenswert?

Tim schrieb:
> Also kann ich die Mitte des Signals nehmen als Startwert und genau
> wieder den gleichen Wert zum stoppen nehmen. Somit hätte ich dann die
> Periode gemessen.

Und das ADC Gehampel brauchst du dann auch nicht.
Der Analog Comparator sollte für den Zweck reichen.
Gast #5383349
Lesenswert?

Nun ich habe das Programm geschrieben. Es funktioniert auch. Der Timer 
startet beim ADC Wert 500 und stoppt wenn er ihn wieder erreicht. 
Nachdem Erreichen des Wertes stoppt der Timer und wird wieder auf 0 
zurückgesetzt. Das funktioniert soweit. Ich hab dann beispielsweise 
gesagt: Falls die gemessene Zeit größer als 1000 ms ist. Soll die rote 
LED leuchten und falls die Zeit kleiner als 1000 ms ist, soll eine grüne 
LED leuchten. Das funktioniert aber leider nicht. Die rote LED leuchtet 
zwar aber obwohl die Zeit dann kleiner als 1000 ms ist, bleibt die rote 
LED immer noch an. Normalerweise müsste dann die grüne leuchten. Ich 
finde den Fehler nicht. Kann einer über den Code schauen. Vielleicht 
bemerkt jemand einen Fehler.
1
//#define F_CPU  16000000
2
#include <avr/io.h>
3
#include <util/delay.h>
4
#include "lcd.h"
5
#include <avr/interrupt.h>
6

7

8
/*unsigned int read_ad(void)
9
{
10
  // Funktion zum Auslesen des A/D-Wandlers
11
    ADCSRA |= (1<<ADSC);  // Wandler starten
12
  while(!(ADCSRA & (1<<ADIF)));  //warten auf Wandler
13
  return ADCW;
14
}
15
*/
16

17
volatile uint16_t messung = 0;
18
volatile uint32_t periodenzeit;
19
volatile uint16_t start;
20
volatile uint16_t ende;
21

22

23

24

25
ISR(ADC_vect)        // Interrupt zum Auslesen des Wandlers
26
{
27
  messung = ADCW;
28
  
29
  if (messung >= 500)      // ADC-Wert bei 500 Timer Starten
30
                
31
  {
32
    
33
    TCCR1B = (1<<CS12)|(1<<CS10);      // Timer-Konfiguration mit 1024 Vorteiler
34
    start=TCNT1;
35
    
36
    
37
    
38
  }
39

40
  else 
41
  {
42

43
      
44
    TCCR1B = (0<<CS12)|(0<<CS10);
45
    ende=TCNT1;
46

47
    periodenzeit=ende-start;
48

49
    TCNT1 = 0b00000000;          // Timer zurücksetzen
50
    
51

52
    if((periodenzeit*0.06425)>=3000)    // pro Timer-Schritt = 0.00006425 us
53
    {
54
      
55
    
56
      PORTB = (1<<PB3);
57
      PORTB &=~ (1<<PB6);  
58
       
59
    
60
    }
61
    else 
62
    {
63
      PORTB = (1<<PB6);
64
      PORTB &=~(1<<PB3);
65
    }
66

67
  
68
  }
69

70

71
}
72

73

74

75
int main (void)
76
{
77
  DDRB |=(1<<PB3)|(1<<PB6);
78

79

80
  ADMUX  = (0<<REFS1)|(1<<REFS0)|(1<<MUX0);  // Ref=AVCC, PA1
81
  ADCSRA = (1<<ADSC)|(1<<ADATE)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0)|(1<<ADEN);  // Wandler an
82
                    // Teiler 128 -> 125kHz Takt,
83
  
84
                      
85
  
86

87

88
 sei();                // Interruptverarbeitung AN
89
 
90
  
91
  lcd_init();
92
  //lcd_string("TESTPROGRAMM ADC");
93
  lcd_setpos(1,0);
94
  lcd_string("Wert: ");
95
  
96

97

98

99

100
  
101
  while(1)                // Hauptschleife
102
  {
103

104

105

106

107

108
  
109
  lcd_setpos(1,7);
110
  lcd_int(messung);
111
  lcd_char(' ');
112
  
113
  //lcd_string("Wert: ");
114
  
115
  
116
  lcd_setpos(0,0);
117
  lcd_string("Zeit: ");
118
  lcd_setpos(0,5);
119
  lcd_int((TCNT1*0.064));
120

121
  
122
  };
123
  return 0;
124
}
Moderator (Firma: Titel) Persönliche Seite #5383363
Lesenswert?

Tim schrieb:
> Er soll beim Erreichen des Maximums gestartet werden. Wie kann ich das
> denn genau Erreichen?
Du kannst es nur dann genau erreichen, wenn du für ein absolut 
ströungsfreies Signal sorgst. Weil du das zweitere nicht kannst, wirst 
du auch deinen Wunsch nicht erfüllt bekommen.

So, nachdem das geklärt ist, kommt der nächste Schritt: nimm mal ein 
Blatt Papier, mache einen ganz schmalen Schlitz rein und schiebe das so 
über dein Signal, dass du immer nur 1 Messwert siehst. Und jetzt suche 
die Maxima. Überlege dabei: findest du die gewünschten Maximalwerte? Wie 
sieht deine Suchstrategie aus? Musst du mit dem Schlitz vor- und 
zurückfahren? Merkst du dir alte Maximalwerte?

> Also kann ich die Mitte des Signals nehmen als Startwert und genau
> wieder den gleichen Wert zum stoppen nehmen. Somit hätte ich dann die
> Periode gemessen.
Damit hast du das Problem eigentlich nur in die Mitte der Kurve 
verschoben. Denk mal ausführlich drüber nach.

Sieht das Signal eigentlich immer genau so und nicht anders aus? Dann 
reicht tatsächlich der Komparator. Oder du schließt das Signal einfach 
an einen digitalen µC-Pin an...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren