Forum: Mikrocontroller und Digitale Elektronik AVR Timer1 Zeit zwischen Maximalwert vom ADC messen


von Tim (Gast)


Angehängte Dateien:

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
}

von C. (Gast)


Lesenswert?

Dein Signal ist nicht monoton steigend bzw. fallend.
Es rauscht und hat deswegen locale maxi- und minima, deswegen stopt 
deine Messung zu früh.

von S. Landolt (Gast)


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?

von Tim (Gast)


Lesenswert?

Er soll beim Erreichen des Maximums gestartet werden. Wie kann ich das 
denn genau Erreichen?

von S. Landolt (Gast)


Lesenswert?

Zweimal auf das Maximum abfragen, Steuerung über eine boolsche 
Zustandsvariable, Messung_laeuft oder so ähnlich.
  Aber auch das Argument mit dem Rauschen beachten.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Tim schrieb:
> Er soll beim Erreichen des Maximums gestartet werden. Wie kann ich
> das
> denn genau Erreichen?

Brauchst du wirklich das Maximum? oder ist das Signal soweit periodisch, 
dass dir eine Art "Nulldurchgang" auch reicht?

von Tim (Gast)


Lesenswert?

Das Signal ist periodisch also immer gleichmäßig. Ich würde lieber das 
Maximum messen, da der Nulldurchgang nicht so "sauber" ist.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Tim schrieb:
> Das Signal ist periodisch also immer gleichmäßig. Ich würde lieber das
> Maximum messen, da der Nulldurchgang nicht so "sauber" ist.

Zumindest auf deinem ersten oszi-Bild sieht der "Nulldurchgang" (der 
eigentlich ein Mittelwert-Durchgang ist) signifikant sauberer als das 
Maximum aus.

von georg (Gast)


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

von Tim (Gast)


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

von C. (Gast)


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.

von Route_66 H. (route_66)


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.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Route 6. schrieb:
> Fertig

Rauschen?

von Route_66 H. (route_66)


Lesenswert?

Michael R. schrieb:
> Rauschen?

Wenn du das Rauschen des Signals meinst: Das geht bei der beabsichtigten 
Art der Periodendauermessung (nämlich Pollen des ADC) sowieso im 
Abfragejitter unter!
Oder muss über mehrere (viele) Perioden gemittelt werden.

: Bearbeitet durch User
von Tim (Gast)


Lesenswert?

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.

von georg (Gast)


Lesenswert?

Tim schrieb:
> Somit hätte ich dann die
> Periode gemessen.

Die halbe.

Georg

von Einer K. (Gast)


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.

von Tim (Gast)


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
}

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


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...

: Bearbeitet durch Moderator
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.