Frekvencia

Processing/Wiring (illetve C) nyelvű programozási fogások, tippek. (AVR-Duino, Arduino, EthDuino, Diecimila, Severino, Nano, LilyPad)
Válasz küldése
glitslik
Újonc
Újonc
Hozzászólások: 2
Csatlakozott: 2016. október 9. vasárnap, 17:41

Frekvencia

Hozzászólás Szerző: glitslik »

Sziasztok.
Egy egyetemi projekt keretében probálkozok egy Arduino alapu mérőeszköz megépítésével ill. programozásával. Több problémám is van, de most csak a frekvencia mérésének programozásáról kérném a segítségeteket. Írtam egy kis programot ami úgy-ahogy működik, de túl magas értékeket mér hálózati frekvenciaként, néha túl alacsonyat (43-63 Hz). Multiméterrel ellenörzöm a valós érték ~ 49.95 - 50.05 Hz közöt ingadozik. Én három esetleges okot találtam:

- túl érzékeny a program, tehát érzékeli a feszültségingadozásokat, interferenciát, felhangokat...
- a mérési tartomány nincs jól megállapítva
- az órajel túl alacsony a méréshez

észrevételeket, javaslatokat, kritikát szívesen várok.
Köszönettel:
Gergely

a kód:


int U2 = A1;
int millis1 = 0;
float frequency = 0;

void setup() {
Serial.begin(9600);
analogReference(DEFAULT);
Serial.println("Set up ... ");

}

void loop() {
millis1 = millis(); // start value for calculating average time
while(millis() < (millis1+1000)){
if (analogRead(U2) > 0){ // count impuls
int fs;
frequency = fs++;
}
while (analogRead(U2) > 0){
delayMicroseconds(1); // Wait untill U2 = 0
}
}
Serial.print("Frequency: ");
Serial.println(frequency);
}
Avatar
csegebiga
Chipgyilok
Hozzászólások: 288
Csatlakozott: 2015. március 27. péntek, 21:27

Re: Frekvencia

Hozzászólás Szerző: csegebiga »

kétféleképpen lehet egy viszonylag alacsony frekit mérni:
- megméred a periódusidejét, és veszed a reciprokát
- egy másodpercnél hosszabb ideig számolod a periódusokat, és utána visszaosztod 1s-re

ha zajos a mérésed akkor gyorsan mérsz sokat, és átlagolsz

a programod eléggé bizonytalan, ami a mérés időalapját illeti
kiszámoltad-e az egyéb dolgok gépidejét?
minden ciklusod egy picit több idő alatt fut le, mint 1 uS, és ez mérési hibát okoz
(csinálja, amit csinál, aztán vár 1 mikrosec-et.)

jó lenne látni a kapcsolást, hogy állítod elő a mért jelet?
mit is mérsz voltaképpen? milyen feszültséget mérsz?
dióda végén félperiódust?
graetz végén dupla felest?
beleszámolod valahol a diódán eső feszültséget? (kétszer egy graetz esetén)

én nagyon csodálkoznék, ha a hálózat 50Hz-nél magasabb lenne. az az 50.05Hz az utópisztikus érték
ingadozni csak lefelé szokott, amikor nagyon megterhelik a hálózatot a fogyasztók.
glitslik
Újonc
Újonc
Hozzászólások: 2
Csatlakozott: 2016. október 9. vasárnap, 17:41

Re: Frekvencia

Hozzászólás Szerző: glitslik »

Szia Csegebiga!

A jel: feszültségosztóval és diódával csinálok 12VAC-ból kb 4,1 V csúcsértékű szinusjelet (természetesen csak a pozitív félhullám potin mérve).

A freki: a jelet ausztriában mérem. Tapasztalatok alapján a hálózati frekvencia meglehetősen stabil, nagyon ritkán tér el 0,05 Hz-nél nagyobb mértékben. A positív kilengést, talán az infrastrukturában lévő különbség okozza. Itt nagyon magas a szél- és naperőművek aránya. Ezek gyakran okoznak időszakos, többletfrekvenciás kilengéseket.

Intervallum: ami az időt illeti igazad van a millis() túl "durva mérés" ez valóban lehet komoly eltérések forrása. Pont ma javasolta egy ismerős, hogy microsekundum-ban mérjek. Az "egyéb dolgok gépidejét" nem számoltam ki, mert nem tudom hogyan kell... :( Bár valóban megfordult a fejemben, hogy ez is okozhat pontatlanságot. Hogyan tudhatom meg, hogy egy-egy parancs, funkció meddig fut?

Feszültség a diódán: nem értem, hogy a diódán esett feszültség hogyan befolyásolja a frekvenciát?

Az új progi:


float U2 = A1;
int micro1 = 0;
int micro2 = 0;
float frequency = 0;

void setup() {
Serial.begin(9600);
analogReference(DEFAULT);
Serial.println("Set up ... ");

}

void loop() {
micro1 = micros();
while(micros() < ( micro1+1000000)){ // 1 másodpercig mérj
if (analogRead(U2) > 0){ // számold a positív jelet körönként egyszer
int fs;
frequency = fs++;
}
while (analogRead(U2) > 0){
delayMicroseconds(5); // amíg van jel várj
}
}
micro2 = micros();
Serial.print("Frequency: ");
Serial.println(frequency = 1000000*frequency /(micro2-micro1)); // számolj átlagot és jelenítsd meg
}

<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
az eredmény:

Set up ...
Frequency: 1530.72
Frequency: 0.00
Frequency: 0.00
Frequency: 0.00
Frequency: 0.00
Frequency: 0.00
Frequency: 0.00
...

ez rosszabb mint volt.
Úgy látom, hogy a mircos() túl gyors.
Avatar
csegebiga
Chipgyilok
Hozzászólások: 288
Csatlakozott: 2015. március 27. péntek, 21:27

Re: Frekvencia

Hozzászólás Szerző: csegebiga »

nézd meg, milyen értékkészlete van egy int változónak, és belefér-e egy micros() visszatérő értéke?
https://www.arduino.cc/en/Reference/Int

szerintem mérj úgy, hogy a mérésed közben minél kevesebb más dolgot csináljon a rendszer
pl így:

Kód: Egész kijelölése

csinálsz egy 50 elemes int tömböt
veszel egy 50 szer futó ciklust, és
  vársz, amíg elindul egy félperiódus(üres ciklus, amíg a bemenet nem nulla, majd üres ciklus, amíg a bemenet nulla )
  //itt kezdődik a mérés
    rögzíted a micros() értékét v1 változóba
    vársz, amíg tart a félperiódus (üres ciklus amíg a bemenet nem nulla)
    rögzíted a micros() értékét v2 változóba
   //itt végződik a mérés   
  kivonod a v1 értékéből a  v1-et   
  az eredményt beírod a tömbbe a helyére
mikor véget ért az 50 mérés, 
csinálsz az 50 tömbelemből egy átlagot, (ez egy félperiódus átlagideje mikrosecben)
szorzod kettővel (teljes periódusidő uS)
csinálsz egy nagyságrend korrekciót (osztod a 10 megfelelő hatványával)			
veszed a reciprokát (frekvencia Hz)
kiíratod a soros porra
ez kb másodpercenként ad egy mérést

a dióda kérdése:
a diódák nyitóirányú karakterisztikájából adódik, hogy k 0.6 V-nál nyit ki, tehát a félperiódusod elején-végén az 0,6V eltűnik a dióda után.
a hozzátartozó fel-és lefutási idővel együtt.
mondjuk, ha 1Vos jelet vizsgálnál, ott ez már számottevő jel- (és idő-)veszteség lenne.

a hálózati frekvenciáról:
látom, most már a szabványban az 50Hz+-20mHz van megadva
legutóbb, mikor Pakson jártam, még 49,9valamennyin járt az atomerőmű, az ottani mérnökök mondták, aránytalanul nagy energiát igényelne az 50Hz tartása (ezért késtek az olcsóbb órák, mivel azok by default 50Hz-re voltak tervezve)
itt egy jegyzet, amiben a terhelés és a frekvencia összefüggést is szépen bemutatják
https://vet.bme.hu/sites/default/files/ ... tml#d6e143
és itt egy cikk, a,iről te is szóltál, hogy lehet az 50Hz nha több is
http://www.mnnsz.hu/502-hertz-problema- ... lgaltatok/

és végül egy kérés:
légyszíves a program kódját tedd a code gomb által létrehozott tag-ek közé, mert úgy jobban olvasható thx!
bagizoltan
Bitmanipulátor
Hozzászólások: 128
Csatlakozott: 2015. december 8. kedd, 19:52

Re: Frekvencia

Hozzászólás Szerző: bagizoltan »

Sziasztok!

Esetleg lehetne úgy is, hogy egy kb. 5V-os trafó kimenetére ráteszel egy diódán keresztül egy ellenállást, mondjuk 1kohm-ot. Ekkor az ellenálláson egy félperiódust kapsz, aminek a felfutó életét lenne célszerű felhasználni a pillanatnyi micros() kiolvasására. Igaz, hogy a jel felfutásának a véges meredeksége miatt nem a nullátmenetet fogod "detektálni", de mivel két felfutás közötti időt méred, nem számít, hogy hol is érzékeljük HIGH-nak a jelet, hiszen a mérés indítása és leállítása ugyanolyan komparálási körülmények között fog lezajlani. Gondot okozhat a zaj, amit azzal lehet kiküszöbölni, ha csak akkor tekintjük felfutó élnek, ha már legalább 2-5 bemenet kiolvasásakor HIGH értéket tapasztalunk a bemeneten!

A két felfutó él közötti időméréshez írnék egy függvényt, ami várakozik a felfutó élig, és visszatér a micros() aktuális értékével. Mivel minden egyes felfutó él detektálásához ugyanazt a függvényt hívnám, itt sem lenne különböző "időveszteség", tehát a mérési hiba minden egyes felfutó élen ugyanakkora. Vagyis mindegy az utasítások sorrendje és száma, ciklusideje (mondjuk ne legyen 100 soros a program :-) )

Kérdés, hogy felhasználható-e a micros() függvény az időmérésre? Hálózati frekvencia mérése esetén mondjuk 2 tizedesjegyre történő pontosságnál (pl. 50.01Hz -> 0,019996sec, 49,99Hz -> 0,020004sec) legalább 4mikrosec kell legyen a mérés pontossága. A micros() függvény erre éppen megfelelő felbontású, mivel 4mikresec a felbontása. Ha ez a felbontás elegendő, akkor jó lehet a program. Ha nem elegendő, akkor nem két felfutó él közötti időt kell megmérni, hanem mondjuk 10 vagy 100 felfutó él közötti különbséget. Nyilván a mért időt majd a végén 100-al kell osztani. Ez egyébként a mérési hibát is csökkenti (átlagolás). Viszont ekkor egyre nő a mérési idő. Mondjuk ha 100 periódust mérünk, akkor a mérés ideje már 2 másodperc.

Figyelembe kell azt is venni, hogy emlékeim szerint a micros() függvény időnként túlcsordul, illetve 0-ról folytatódik az értéke. Így a mért idő néha "marhanagyra" adódik, Ezt a nagy értéket ki kell zárni, illetve a mérést ismételni kell! Bár ez csak 70 percenként fordulhat elő, és még ekkor sem túl nagy a valószínűsége!

Úgy tűnik a végén nem is annyira egyszerű program!!! De ezért néhány óra alatt meg tudnám írni. Kivéve, ha nincs valami elvi probléma abban amit leírtam. Az meg mindig menet közben derül ki :-). Legalkább is nekem, mivel még eléggé kezdő vagyok!

Remélem érthető voltam.

Bagi
Avatar
csabeszq
Bitfaragó
Hozzászólások: 678
Csatlakozott: 2012. szeptember 5. szerda, 6:00

Re: Frekvencia

Hozzászólás Szerző: csabeszq »

Nem akaralak elkeseríteni titeket, de egy Arduino-val 8MHz-es jelet is lehet nagyobb erőfeszítés nélkül mérni. Természetesen nem a micros() függvénnyel.

A trükk neve: timer
- beállítod, hogy a Timer 1 a PD5 láb felfutó éleit mérje (Atmega328P)
- elindítod a Timer 2-t és megnézed, hogy 1ms alatt hányat váltott a Timer 1. Szimplán kiolvasod TCNT1-et.

Magyarul az órajel forrása a külső PD5-ön felfutó él és időközönként kiolvasod, hogy hányat váltott. 8 MHz jel esetén pontosan 8000-et fog milliszekundumonként menni a TCNT1.
Avatar
csabeszq
Bitfaragó
Hozzászólások: 678
Csatlakozott: 2012. szeptember 5. szerda, 6:00

Re: Frekvencia

Hozzászólás Szerző: csabeszq »

Csatolom az őrült bonyolult programot, ami megméri az arduino saját PWM jelének frekvenciáját.
Annyit kell tenni, hogy az 5-ös és 6-os PIN-eket Arduino UNO-n/Nano-n összekötni vezetékkel.

Kód: Egész kijelölése

// kösd össze a D5 (PD5) és a D6 (PWM, PD6) lábakat vezetékkel

void setup() {
  Serial.begin(9600);
  Serial.println("Frekvencia mérés");

  pinMode(5, INPUT); // az 5-ösön mérsz frekvenciát
  pinMode(6, OUTPUT); // a hatoson megy ki a PWM jel
  analogWrite(6, 127); // 50%-os PWM jel 6-os PIN-re
}

void loop() {
  TCCR1A = 0; // Normál 0-FFFF mód
  TCCR1B = _BV(CS12) | _BV(CS11) | _BV(CS10); // a felfutó élet méred T1-en (5-ös pin)

  TCNT1 = 0; // timer nullázás
  delay(1000); // várj 1s-t, hogy könnyű legyen számolni

  uint16_t tcnt = TCNT1;
  Serial.print("Frekvencia:");
  Serial.print(tcnt);
  Serial.println(" Hz");

  delay(2000);
}
Avatar
csegebiga
Chipgyilok
Hozzászólások: 288
Csatlakozott: 2015. március 27. péntek, 21:27

Re: Frekvencia

Hozzászólás Szerző: csegebiga »

ez nagyon jó progi, de ezzel hogy mérsz 49.98Hz-et? :)
Avatar
csabeszq
Bitfaragó
Hozzászólások: 678
Csatlakozott: 2012. szeptember 5. szerda, 6:00

Re: Frekvencia

Hozzászólás Szerző: csabeszq »

Akkor impulzushosszt kell mérni (bocs). Majd küldök kódot, ez is megy simán hardveresen timerrel. Semmiképp nem kell szórakozni micros-sal.
Avatar
csabeszq
Bitfaragó
Hozzászólások: 678
Csatlakozott: 2012. szeptember 5. szerda, 6:00

Re: Frekvencia

Hozzászólás Szerző: csabeszq »

Íme a kód, ami impulzus hossz alapján mér frekvenciát. A 8-as és 6-os pineket össze kell kötni. A belső PWM-et impulzushossz alapján fogjuk mérni.
- 50Hz-nél a timer1 túlcsordulna, ezért le kell osztani 8-as prescalerrel (320000 órajel egy periódus).

A lényeg, hogy amikor PB0-n felfutó él történik (8-as pin), kiváltódik egy interrupt. Itt vesszük a timer időpontot, amikor a felfutó él történt (ICR1) és kivonjuk az előzőből. Frekvenciát bárhogyan is szeretnél mérni, mindig hardveresen illik megcsinálni.

Kód: Egész kijelölése

// kösd össze a D8 (PB0) és a D6 (PWM, PD6) lábakat vezetékkel

uint16_t lastICR1;
volatile uint32_t periodLength;

ISR(TIMER1_CAPT_vect)
{
  uint16_t icr1 = ICR1;
  periodLength = (uint32_t)( icr1 - lastICR1 ) << 3; // 8-as prescaler miatt szorzol
  lastICR1 = icr1;
}


void setup() {
  Serial.begin(9600);
  Serial.println("Frekvencia mérés");

  pinMode(8, INPUT); // az 8-ason mérsz frekvenciát
  pinMode(6, OUTPUT); // a hatoson megy ki a PWM jel
  analogWrite(6, 127); // 50%-os PWM jel 6-os PIN-re

  TCCR1A = 0; // Normál 0-FFFF mód
  TCCR1B = _BV(CS11)  // prescaler 8
        |  _BV(ICES1) // a felfutó élet méred PB0-n (8-as pin)
  ;
  TIMSK1 |= _BV(ICIE1); // input capture interrupt engedélyezés
}

void loop() {
  float freki = (float)F_CPU/(float)periodLength;
  Serial.print("Frekvencia:");
  Serial.print(freki);
  Serial.println(" Hz");

  delay(2000);
}
Avatar
csabeszq
Bitfaragó
Hozzászólások: 678
Csatlakozott: 2012. szeptember 5. szerda, 6:00

Re: Frekvencia

Hozzászólás Szerző: csabeszq »

Zajos jelre ICNC1-gyel bekapcsolhatsz szűrést.
TCCR1B |= _BV(ICNC1)
Válasz küldése