Hallo,
ich suche eine Lösung für mein Problem und hoffentlich kann mir jemand
auf die Sprünge helfen, der Mikrocontroller ist ein ATmega8.
Ich erwarte ein sinusförmiges Eingangssignal von 0 bis 5V, Frequenz ist
50Hz. Der Nullpunkt dieser Sinuskurve liegt bei 2,5V.
Nun will ich mich ein wenig tiefer in die AVR-GCC-Programmierung wagen
und möchte den höchsten Spannungswert mittels Software finden und
speichern.
Meine Überlegungen:
Da es sich um eine Frequenz von 50Hz handelt, ist eine Periode 20ms
lang.
Für die Abtastung einer Periodenlänge kommt daher ein Zeitfenster von
20ms in Frage. In diesen 20ms werden (sagen wir mal) 200 ADC-Messwerte
gelesen und überprüft, ob der aktuelle Messwert größer ist als der
zuletzt gemessene ADC-Wert. Nur, wenn das der Fall ist, wird der neue
Messwert als als "Höchstwert" gespeichert, der dann mit dem folgenden
ADC-Messwert verglichen wird.
Sind die 20ms abgelaufen, wird eine neue Messwertaufnahme gestartet, und
das insgesamt 10 Mal.
Die eingelesen Messwerte werden jeweils nach dem Ablauf des Zeitfensters
aufaddiert. Wenn 10 Messwerte aufgenommen wurden, wird daraus der
Mittelwert gebildet und ggfs. ausgegeben.
Kann man das so machen oder gibt es eine elegantere Lösung für dieses
Problem?
Besten Dank,
Olaf
MarioT schrieb:
> Warum gerade 10 Messwerte?
Damit ich dadurch den arithmetischen Mittelwert bekomme und so ein
Schwanken der Messwerte weitestgehende verhindern möchte.
Wenn grob gesagt alle 20ms ein Messwert ausgespuckt wird, schwankt die
Anzeige mehr, als wenn alle 200ms ein Mittelwert aus den 10
aufgenommenen Werten ausgegeben wird.
Armin schrieb:
> du kannst die Differenz zum letzten Messewert ausrechnen und immer dann,> wenn die das Vorzeichen wechselt, ist entweder eich Hoch- oder ein> Tiefpunkt.
Ich muss doch aber wissen, ob sich der Messwert in der Differenzmessung
wirklich um einen Hochpunkt handelt und es sich nicht um ein "fallendes"
Signal handelt. Ich stehe um diese Uhrzeit wohl aufm Schlauch und weiß
nicht, wie ich das genau zu verstehen habe.
aber wenn du den Zeitpunkt kennst, bis zu dem das Signal steigt und ab
dem das Signal fällt, dann ist das der Hochpunkt.
--> Morgen nochmal durchlesen.
mit der Mittelwertbildung erreichst du niemals den Spitzenwert, sondern
immer einen Wert, der leicht darunter liegt (Abtastrate kann nicht
unendlich hoch sein, d.h. den absoluten Spitzenwert wirst du nur selten
treffen. Normalerweise liegst du knapp davor oder dahinter, beides
liefert Messwerte, die tiefer liegen)
Glücklicherweiser ist die Steigung eines Sinussignals gering, so dass
die Fehler wahrscheinlich vernachlässigbar bleiben.
if Wert_neu - Wert_alt > 0 then Wert_alt = Wert_neu
Olaf schrieb:
> Damit ich dadurch den arithmetischen Mittelwert bekomme und so ein> Schwanken der Messwerte weitestgehende verhindern möchte.
Ich meinte warum müssen es unbedingt "10" Messungen sein?
Du willst es doch auf einem µC machen. Der "denkt" aber etwas anders als
Du. Du rechnest doch Dezimal, wenn man etwas durch 10 teilt verschiebst
Du doch nur das Komma. Für einen µC ist teilen durch 10 aber schon ein
größerer Akt.
Oh, das Thema passt mir gerade super. Werde bald was ähnliches machen.
Ich finde die Idee mit der Differenz der Messwerte auch gut. Nur sieht
es bei mir so aus, dass ich die PWM an einem Drehstrommotor zuerst über
einen Filter jage und dann die Amplitude und Frequenz messen will. Jetzt
hat mein Sinus, je nach Drehzahl des Motors, bis zu 300 Hz. Habe jetzt
mal in der Simulation einen Filter gebastelt. Bei hohen Frequenzen
bekomme ich dann auch einen wunderbaren Sinus, nur unten wirds schwer.
Der Sinus ist dort auch erkennbar, aber halt nicht ganz so sauber (siehe
Bild). Da kann ich diese Methode schon wieder vergessen, oder?
Benni Nori schrieb:
> Bild). Da kann ich diese Methode schon wieder vergessen, oder?
Ja.
Man sollte auch bedenken, dass für die Bestimmung der Differenz der
elektronische Aufbau schon sauber sein sollte. Ansonsten passiert es
nämlich, dass der ADC selbst bei absolut konstanter Eingangsspannung
immer ein wenig um den wahren Wert schwankt (+/- 1 oder 2 Digit)
Wenn ich aber sowieso weiß, dass die 0-Linie meiner Spannung bei 2.5V
liegt, dann würde ich das Maximum bzw. Minimum genau so ermitteln wie
der TO das hatte.
Sobald die Spannung einen Wert von 2.5V + Hysterese überschreitet wird
die nächste Maximumsuche scharf geschaltet und das letzte gefundene
Minimum als das Minimum im letzten Abschnitt ausgewiesen. Unterschreitet
die Spannung 2.5V - Hysterese, so wird die Minimum-Suche scharf
geschaltet und das zuletzt gefundene Maximum ausgegeben.
Hallo,
ich habe mich derweil in diesem genialen Forum weiter umgeschaut und bin
auf einen ähnlichen Beitrag gestoßen.
Da ich eine 4 stellige 7-Segment-Anzeige verwende, habe ich den Code
dieses Posts teilweise verwendet.
Ich kam nun zu folgendem Code (mich interessiert nur die positivste
Spannungsspitze):
1
volatileunsignedcharfertig=0;
2
volatileunsignedintresult;
3
volatileunsignedintoffset=511;
4
volatileunsignedintpeak=511;
5
6
voidmessung(void)
7
{
8
{
9
ADCSRA|=(1<<ADSC);
10
while(ADCSRA&(1<<ADSC)){
11
;
12
}
13
result=ADCW;
14
}
15
fertig=1;
16
}
17
18
intmain(void)
19
{
20
adcinit();
21
ioinit();
22
23
while(1)
24
{
25
if(fertig==0)
26
{
27
messung();
28
}
29
30
if(fertig==1&&result>peak)
31
{
32
peak=result;
33
fertig=0;
34
}
35
36
if(fertig==1&&result<offset)
37
{
38
Errechnet=(int)(peak/5.11);
39
peak=offset;
40
fertig=0;
41
}
42
43
if(!Taster_gedrueckt)
44
DisplayNumber(Errechnet);
45
elseDisplayDigit(peak);
46
}
47
}
Leider funktioniert das nicht wirklich. Der Atmega8 macht nur eine
Messung und läuft nicht ständig durch, obwohl ich doch das "fertig-Flag"
zurücksetze.
Ist wohl doch nicht so einfach, wie ich mir das gedacht habe?!
Gruß,
Olaf
zum einen fehlt da einiges vom Code,
zum anderen kannst du dir die Variable fertig komplett sparen (ist zwar
nicht der Fehler, macht das Programm aber übersichtlicher)
wofür brauchst du das "Fertig" überhaupt? das macht hier doch nur sinn,
wenn die messung() in einem parallel thread läuft, oder? ich kann grad
nicht folgen.
Ansonsten seh ich bloß, dass, wenn einmal genau 511 gemessen wird, das
ganze Ding stehen bleibt (fertig=1 dauerhaft)
Olaf schrieb:
> if (fertig == 1 && result > peak)
.
.
> if (fertig == 1 && result < offset)
wenn keine der beiden Bedingungen wahr ist, wird "fertig" nicht
zurückgesetzt. Vielleicht ist das das Problem.
Walter schrieb:
> zum einen fehlt da einiges vom Code,> zum anderen kannst du dir die Variable fertig komplett sparen (ist zwar> nicht der Fehler, macht das Programm aber übersichtlicher)
Der restliche Code ist nur die Ausgabe für die 7-Segment-Displays.
Mehr habe ich nicht im Code stehen, halt nur die Ansteuerung der
Displays (das funktioniert supi) und dann die ADC-Geschichte
(funktioniert nicht wirklich). Meine Stehlampe zieht 1,12A, das Display
hat nur eine Nachkommastelle und zeigt 1,1A an. Soweit ist das korrekt.
Ich habe das fertig-Flag definiert, damit die if-Anweisungen ausgeführt
werden.
Armin schrieb:
> wofür brauchst du das "Fertig" überhaupt?> das macht hier doch nur sinn,> wenn die messung() in einem parallel thread läuft, oder?
Antwort siehe oben... Parallel läuft es eigentlich nur mit der
7-Segment-Ausgabe.
dr.schmock schrieb:
> Olaf schrieb:>> if (fertig == 1 && result > peak)> .> .>> if (fertig == 1 && result < offset)>> wenn keine der beiden Bedingungen wahr ist, wird "fertig" nicht> zurückgesetzt. Vielleicht ist das das Problem.
Hhm, mein Offset beträgt 511, entspricht also 2,5V.
Und ich komme wirklich unter die 2,5V! Aber in diese Richtung muss ich
mal weitersuchen.
Wenn ich das "fertig-Flag" wegnehme, funktioniert die Anzeige wieder.
:-)
Nun habe ich aber das Problem, dass bei 0A, also 2,5V (511) die Anzeige
keine 0 mehr anzeigt, sondern beim letzten Spannungswert stehen bleibt.
:-(
Olaf schrieb:
> Wenn ich das "fertig-Flag" wegnehme, funktioniert die Anzeige wieder.> :-)
Eben.
Dieses 'fertig' ist ein Selbstläufer. Es bringt dir nichts und ist nur
ein Quell von Fehlern.
> Nun habe ich aber das Problem, dass bei 0A, also 2,5V (511) die Anzeige> keine 0 mehr anzeigt, sondern beim letzten Spannungswert stehen bleibt.> :-(
Klar. Weil deine gemessene Spannung niemals die Bedingung erfüllt
if ( result < offset)
Mach noch einen Zähler mit rein, der jedesmal nach einer Messung um 1
erhöht wird. Dann machst du noch
if ( result < offset || counter > 200 )
(200 oder was dir anmgemessen erscheint) so dass du nach spätestens 200
messungen eine Ausgabe erzwingst (und natürlich den Counter dann auch
wieder auf 0 zurück setzt).
Karl heinz Buchegger schrieb:
> Klar. Weil deine gemessene Spannung niemals die Bedingung erfüllt>> if ( result < offset)>> Mach noch einen Zähler mit rein, der jedesmal nach einer Messung um 1> erhöht wird. Dann machst du noch>> if ( result < offset || counter > 200 )>> (200 oder was dir anmgemessen erscheint) so dass du nach spätestens 200> messungen eine Ausgabe erzwingst (und natürlich den Counter dann auch> wieder auf 0 zurück setzt).
Danke, das hört sich vernünftig an.
Werde aber morgen damit weitermachen, für heute reicht es mir!
Muss dann wohl in der Nacht darüber weitergrübeln. grins
Eventuell werde ich erst einmal 16 Peaks sammeln und dann den artihm.
Mittelwert bilden, damit die Anzeige nicht zappelt.
Gute Nacht, vielen Dank für alle Antworten und bis morgen! :-)
Lieben Gruß, Olaf
Karl heinz Buchegger schrieb:
> Klar. Weil deine gemessene Spannung niemals die Bedingung erfüllt>> if ( result < offset)
Am Tage versteht man das besser und es stimmt, bei 0A (2,5V) komme ich
wirklich nie unter offset (2,5V).
Der Zähler ist bereits eingebaut und nach 20 Zählschritten wird der Wert
auf 0 gesetzt, wenn offset nicht mehr unterschritten wird.
Jetzt kann ich ohne Grübeln beruhigt in den Urlaub fahren!
Besten Dank, Olaf