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
volatileuint16_tmessung=0;
18
volatileuint16_tzeit=0;
19
volatileuint16_thilf=0;
20
volatileuint16_thilf2=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
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?
Zweimal auf das Maximum abfragen, Steuerung über eine boolsche
Zustandsvariable, Messung_laeuft oder so ähnlich.
Aber auch das Argument mit dem Rauschen beachten.
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?
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.
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
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
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.
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.
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.
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.
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.
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
volatileuint16_tmessung=0;
18
volatileuint32_tperiodenzeit;
19
volatileuint16_tstart;
20
volatileuint16_tende;
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
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...