Forum: Mikrocontroller und Digitale Elektronik ATmega328P - 50HZ Signal auswerten


von Dominik W. (dominik009)


Angehängte Dateien:

Lesenswert?

Hallo Zusammen,

ich habe ein Arduino Pro Mini (3,3V 8MHz). Am Analogeingang habe ich ein 
Signal mit einer Frequenz von 50Hz (Sinus) angelegt (siehe Bild).

Ich versuche aktuell den "Nullpunkt" (570mV) zu erkennen und 
auszuwerten.

Habe schon vieles Versucht, aber schaffe es einfach nicht.

Hier der Code:
1
#include <Wire.h> 
2
#include <LiquidCrystal_I2C.h> 
3
LiquidCrystal_I2C lcd(0x27, 20, 4);
4
5
6
int analogPin = A0; //Analogsignal am Pin A0 
7
8
int in1 = 0;
9
float Uin1  = 0;
10
float U1 = 0;
11
const float zero = 0.57;
12
unsigned long Zeit = 0;
13
14
int i = 0;
15
16
17
void setup() {
18
  Serial.begin(500000); 
19
  analogReference(INTERNAL);
20
  lcd.init();
21
  lcd.backlight(); 
22
}
23
24
25
void loop() {
26
  in1 = analogRead(analogPin); // Pin einlesen
27
  Uin1 = (((1.1 / 1023.0) * in1)); // Umwandeln in 0V bis 1,1V
28
  U1 = (Uin1-zero)*(671989.0/989.0); //Umwandeln in -350V bis + 350V
29
30
    lcd.setCursor(0, 2);
31
    lcd.print(i);
32
  
33
  if (U1==0) i=i+1;
34
  
35
36
  if (i==10000){
37
    lcd.setCursor(0, 1);
38
    lcd.print("TEST");
39
    i=0;
40
    delay (2000);
41
    lcd.clear();}
42
    
43
}

Ist der AVR oder ADC im AVR zu langsam um den Nullpunkt bei 50HZ 
Freqeuenz zu erkennen? Habe das ganze mit einem Zähler versucht. Was 
mache ich falsch?

Gruß Dominik

: Bearbeitet durch User
von Manfred (Gast)


Lesenswert?

Ohne Deinen Code vollständig nachzuvollziehen bin ich mir ziemlich 
sicher, dass
>
1
if (U1==0)
niemals zutreffen wird, prüfe auf 'kleiner null komma null null 
sonstwas' - passend zu Deinem erwarteten Wertebereich.

von STK500-Besitzer (Gast)


Lesenswert?

Dominik W. schrieb:
> Ist der AVR oder ADC im AVR zu langsam um den Nullpunkt bei 50HZ
> Freqeuenz zu erkennen? Habe das ganze mit einem Zähler versucht. Was
> mache ich falsch?

Der sollte das schaffen.
Deine LCD-Ausgabe verlangsamt die Messung.

Manfred schrieb:
> Ohne Deinen Code vollständig nachzuvollziehen bin ich mir ziemlich
> sicher, dass
>>if (U1==0)niemals zutreffen wird, prüfe auf 'kleiner null komma null null
> sonstwas' - passend zu Deinem erwarteten Wertebereich.

Richtig.

von Roland F. (roland_f451)


Lesenswert?

Hallo Dominik,

wie meine Vorredner schon richtig geschrieben haben sollte man keinen 
"== "Vergleich bei float vornehmen.
Weiterhin würde ich das Suchkriterium für den Nullpunkt nicht bei jedem 
Schleifendurchlauf berechnen.
Da Du den genauen Wert abhängig von der Beschaltung sowieso nicht genau 
berechnen kannst, würde ich die 570 mV konstant anlegen und den AD Wert 
notieren. Dieser Wer ist dann in der Schleife Dein Suchkriterium.

Später kannst Du den Wert natürlich im Setupteil auch für andere 
Bedingungen berechnen.

Als letzte Änderung schlage ich vor nicht bei jedem Schleifendurchgang 
eine LCD Ausgabe zu machen.

Grüße
Roland

von PittyJ (Gast)


Lesenswert?

Bei Floats niemals auf == vergleichen.
Am besten den letzen Wert merken, und wenn
letzer <= 0.0 && aktuell >0.0
Dann hat ein Nulldurchgang in positiver Richtung stattgefunden.

von Einer K. (Gast)


Lesenswert?

Dominik W. schrieb:
> Ich versuche aktuell den "Nullpunkt" (570mV) zu erkennen und
> auszuwerten.

Der ATMega328P hat einen eingebauten Komparator.
Was spricht gegen dessen Verwendung?

von Roland F. (roland_f451)


Lesenswert?

Roland F. schrieb:
> Als letzte Änderung schlage ich vor nicht bei jedem Schleifendurchgang
> eine LCD Ausgabe zu machen.

Auch hier kannst Du Dich langsam vortasten:
Zunächst mal nur bei erreichen des Suchkriteriums eine Ausgabe machen, 
wenn das klappt sehen wir weiter...

von Roland F. (roland_f451)


Lesenswert?

Arduino Fanboy D. schrieb:
> Der ATMega328P hat einen eingebauten Komparator.

Super das wusste ich auch nicht, wieder etwas gelernt, da hat sich der 
Tag ja schon gelohnt. :-)
Ich wollte Dominik schon vorschlagen einen externen Komparator 
einzusetzen aber Dein Vorschlag ist natürlich der bessere.

Grüße
Roland

von Wolfgang (Gast)


Lesenswert?

Roland F. schrieb:
> wie meine Vorredner schon richtig geschrieben haben sollte man keinen
> "== "Vergleich bei float vornehmen.

Float in Zusammenhang mit einem AD-Wandler, der von Natur aus nur 
Ganzzahlwerte liefern kann, ist für effiziente Erkennung vom 
Nulldurchgang sowieso Unfug.

Man rechnet sich seinen Schwellwert mit der Skalierung einmal auf 
Ganzzahl um und vergleicht das ADC-Ergebnis nur mit diesem ganzzahligen 
Wert. Das spart die ganze unselige Float-Rechnerei in der Schleife.

von Hansel (Gast)


Lesenswert?

Der eingebaute Komparator funktioniert sehr gut zur Erkennung der 
Nulldurchgangs. Da gibt es auch eine Appnote zu.

von Dominik W. (dominik009)


Lesenswert?

Hallo Zusammen,

erstmal vielen Dank für die ganzen Antworten und die verschiedenen 
Lösungsideen!

Ich habe den Code mal etwas angepasst und erkenne jetzt die 
Nulldurchgänge (vor der positiven Halbwelle).
1
#include <Wire.h> // Wire Bibliothek einbinden
2
#include <LiquidCrystal_I2C.h> // Vorher hinzugefügte LiquidCrystal_I2C Bibliothek einbinden
3
LiquidCrystal_I2C lcd(0x27, 20, 4); //Hier wird festgelegt um was für einen Display es sich handelt. In diesem Fall eines mit 16 Zeichen in 2 Zeilen und der HEX-Adresse 0x27. Für ein vierzeiliges I2C-LCD verwendet man den Code "LiquidCrystal_I2C lcd(0x27, 20, 4)" 
4
5
6
int analogPin = A0; // Pin, der gelesen werden soll: Pin A0
7
8
int in1 = 0; // Variable, die den gelesenen Wert speichert
9
float Uin1  = 0;
10
float U1 = 0;
11
float U1alt = 0;
12
const float zero = 0.57;
13
float Zeit = 0;
14
unsigned long Startzeit = 0;
15
int i = 0;
16
17
18
19
void setup() {
20
  Serial.begin(9600); // Setup der seriellen Verbindung
21
  analogReference(INTERNAL);
22
  lcd.init(); //Im Setup wird der LCD gestartet 
23
  lcd.backlight(); //Hintergrundbeleuchtung einschalten (lcd.noBacklight(); schaltet die Beleuchtung aus). 
24
}
25
26
27
void loop() {
28
  //Spannung berechnen//
29
  U1alt = U1;
30
  in1 = analogRead(analogPin); // Pin einlesen
31
  Uin1 = (((1.1 / 1023.0) * in1)); // Umwandeln in 0V bis 1,1V
32
  U1 = (Uin1-zero)*(671989.0/989.0); //Umwandeln in -350V bis + 350V
33
34
  //Nulldurchgang erkennen//
35
  if (U1 > 0.0 && U1alt <= 0.0) i=i+1;
36
37
  //Periodendauer ermitteln//
38
  if (i==1)Startzeit = micros();
39
  
40
  if (i==501){
41
  Zeit = (micros()-Startzeit);
42
  
43
  lcd.setCursor(0, 3);
44
  lcd.print(Zeit/500000.0, 4);
45
  lcd.setCursor(0, 2);
46
  lcd.print(1/(Zeit/500000000.0), 2);
47
  Serial.println(Zeit/500000.0, 4);
48
  
49
  i=0;
50
  }
51
    
52
}

Leider ist das Ganze noch etwas ungenau. Die Periodendauer sollte bei 
50Hz  ja bei 20ms liegen.

Ich komme erst in Richtung 20ms, sobald ich mehrere 100 Perioden zähle 
und dann den Mittelwert errechne. Bei einer, 10 oder 20 liege ich extrem 
stark daneben und bekomme eine zu kurze Periodendauer (z.b. 16ms bei 10 
Perioden).

Da die Periodendauer zu kurz ist, ist der AVR ja nicht zu langsam.
Kann es am micros() liegen, oder ist meine Erkennung zu ungenau?

Wollte das ganze gerne im AVR ohne noch mehr externe Beschaltung lösen.
Der interne Komparator ist auch eine Idee, dafür müsste ich mir jedoch 
meine 570mV nochmal erzeugen und auf einen anderen AI geben.

Gruß Dominik

von Stefan F. (Gast)


Lesenswert?

Mach Dir mal Gedanken, wie lange wohl die Display Ausgabe dauern wird.

Lösungsvorschlag: Messe in regelmäßigen Abständen mit einem 
freilaufenden ADC (unterstützt Arduino vermutlich nicht direkt).

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Ich würde gerne wissen, wozu der Nulldurchgang dann verwendet wird. Wenn 
es nur um Triggern geht, dann gibt es dazu viel einfachere Methoden, die 
auch deutlich pegelunabhängiger sind, als das Jonglieren mit float 
Werten.

Dominik W. schrieb:
> Der interne Komparator ist auch eine Idee, dafür müsste ich mir jedoch
> meine 570mV nochmal erzeugen und auf einen anderen AI geben.

Nö, musst du nicht. Du kannst den Sinus mit einem RC Tiefpass glätten 
und auf den anderen Komparatoreingang geben. Das ist dann auch gleich 
eine Autolevel Erkennung, weil sich nach dem Tiefpass die ganze Nummer 
sowieso auf die Nulllinie einpendelt.

von Beo Bachta (Gast)


Lesenswert?

Dominik W. schrieb:
> Ich habe den Code mal etwas angepasst und erkenne jetzt die
> Nulldurchgänge (vor der positiven Halbwelle).

Aber den wertvollsten / effektivsten Rat hast du nicht befolgt:

Wolfgang schrieb:
> Man rechnet sich seinen Schwellwert mit der Skalierung einmal auf
> Ganzzahl um und vergleicht das ADC-Ergebnis nur mit diesem ganzzahligen
> Wert. Das spart die ganze unselige Float-Rechnerei in der Schleife.

Dieses unsägliche Rechnen mit float ist ein Wahnsinn, und die
Ausgabe in float ist auch nicht der Renner. Siehe auch Stefans
Hinweis.

von Dominik W. (dominik009)


Lesenswert?

Hallo Zusammen,

die Ausgabe auf dem Display erfolgt ja erst nach dem ermitteln der Zeit 
und vor dem Zurücksetzen meiner Zählvariable i.

Habe den Code nochmal angepasst und arbeite jetzt nur noch mit dem 
Int-Wert vom Analogeingang ohne Umrechnungen.
1
#include <Wire.h> // Wire Bibliothek einbinden
2
#include <LiquidCrystal_I2C.h> // Vorher hinzugefügte LiquidCrystal_I2C Bibliothek einbinden
3
LiquidCrystal_I2C lcd(0x27, 20, 4); //Hier wird festgelegt um was für einen Display es sich handelt. In diesem Fall eines mit 16 Zeichen in 2 Zeilen und der HEX-Adresse 0x27. Für ein vierzeiliges I2C-LCD verwendet man den Code "LiquidCrystal_I2C lcd(0x27, 20, 4)" 
4
5
6
int analogPin = A0; // Pin, der gelesen werden soll: Pin A0
7
8
int in1 = 0; // Variable, die den gelesenen Wert speichert
9
int U1alt = 0;
10
const float zero = 0.57;
11
float Zeit = 0;
12
unsigned long Startzeit = 0;
13
int i = 0;
14
15
16
17
void setup() {
18
  Serial.begin(9600); // Setup der seriellen Verbindung
19
  analogReference(INTERNAL);
20
  lcd.init(); //Im Setup wird der LCD gestartet 
21
  lcd.backlight(); //Hintergrundbeleuchtung einschalten (lcd.noBacklight(); schaltet die Beleuchtung aus). 
22
}
23
24
25
void loop() {
26
27
  U1alt = in1;
28
  in1 = analogRead(analogPin); // Pin einlesen
29
30
  //Nulldurchgang erkennen//
31
  if (in1 > 522 && U1alt <= 522) i=i+1;
32
33
  //Periodendauer ermitteln//
34
  if (i==1)Startzeit = micros();
35
  
36
  if (i==110){
37
  Zeit = (micros()-Startzeit);
38
  
39
  lcd.setCursor(0, 3);
40
  lcd.print(Zeit/110000.0, 4);
41
  lcd.setCursor(0, 2);
42
  lcd.print(1/(Zeit/110000000.0), 2);
43
  Serial.println(Zeit/110000.0, 4);
44
  
45
  i=0;
46
  }
47
    
48
}

Ich möchte gerne die Periodendauer von einem (später mehreren) 50Hz 
Sinussignalen messen und die Nulldurchgänge erkennen können.

RC-Tiefpass mit niedriger Grenzfrequenz ist natürlich auch noch eine 
gute Idee, danke  Matthias!

Gruß Dominik

Beitrag #6032949 wurde vom Autor gelöscht.
von Stefan F. (Gast)


Lesenswert?

Dominik W. schrieb:
> später mehreren

Dann ist dein aktueller Ansatz so gar nicht mehr geeignet.

Lies mal im Datenblatt des Mikrocontrollers nach, was "freilaufender 
ADC" bedeutet. In Kombination mit einer ISR kannst du dafür sorgen, dass 
deine Messwerte in schön regelmäßigen Intervallen in Arrays eingetragen 
werden. Die kannst du dann in der loop() nacheinander auswerten.

von Veit D. (devil-elec)


Lesenswert?

> Uin1 = (((1.1 / 1023.0) * in1)); // Umwandeln in 0V bis 1,1V

richtiger:
Uin1 = (1.1 / 1024 * in1); // Umwandeln in 0V bis 1,1V


vielleicht hilfreich:
Beitrag "Re: Comparator Attiny841"

von Einer K. (Gast)


Lesenswert?

Dominik W. schrieb:
> Ich möchte gerne die Periodendauer von einem (später mehreren) 50Hz
> Sinussignalen messen und die Nulldurchgänge erkennen können.

Die Salami, wie ich sie liebe!

von Jens M. (schuchkleisser)


Lesenswert?

Wo der problem?

- sample mit relativ hoher Rate alle Eingänge die du haben willst
- merke dir für jeden Eingang den jeweils höchsten und niedrigsten Wert 
(gern auch als Mittel über die letzten x Werte, dann wandert das auch 
bei Schwankungen mit)
- rechne "(low + high)/2" als "zero"
- aktueller Wert über/unter "zero"? (Oder "<zero-3 or >zero+3" oder so)
????
- profit

Kein float, schnell, einfach, Pegelunabhängig.

von Einer K. (Gast)


Lesenswert?

Jens M. schrieb:
> Wo der problem?

Die Periodendauer eines 50Hz Signals braucht man nicht zu messen, die 
berechnet man.
Da stecken noch Geheimnisse im Hintergrund.
(die wir offensichtlich nicht erfahren dürfen)

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.