ATmega328P - 50HZ Signal auswerten

OP #6032556
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
Angehängte Dateien:
Gast #6032565
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.
Gast #6032728
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.
#6032752
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
Gast #6032765
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.
OP #6032910
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
Gast #6032913
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).
(Firma: matzetronics) #6032920
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.
Gast #6032921
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.
OP #6032946
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 von einem Moderator gelöscht.
Gast #6032953
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.
#6033236
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.

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