Măsurare frecvență cu Arduino

 Autor:   Publicat pe:   Actualizat pe:  2018-09-03T20:56:42Z

O metodă corectă de măsurare a frecvenței unui semnal folosind plăcile de dezvoltare Arduino. Se pot măsura semnale de până la 6 MHz cu ajutorul timer-elor microcontroller-ului. Adăugând un afișaj, se poate construi un frecvențmetru.

Măsurarea frecvenței unui semnal folosind o placă de dezvoltare Arduino pare un lucru simplu. Dar,  mediul de dezvoltare Arduino nu prevede o funcție simplă care analizează un semnal de pe un pin și întoarce frecvența acestuia. Există funcția pulseIn pe care mulți utilizatori Arduino o folosesc pentru a măsura frecvențe, dar utilitatea ei este în a măsura durata unui semnal. Măsurarea unui singur ciclu al unui semnal oscilant nu este o metodă corectă pentru determinarea frecvenței acelui semnal. Mai mult de atât, „frecvența” maximă ce poate fi determinată astfel este de doar 50 kHz.

Măsurarea corectă a frecvenței unui semnal folosind un microcontroller se face numărând pulsațiile semnalului necunoscut într-un interval de timp bine determinat. Pentru o apreciere corectă, în acel interval de timp, semnalul ar trebui să treacă prin sute sau mii de tranziții (oscilații). Acest mod de măsurare presupune configurarea unor timer-e hardware. Timer-ul este un registru special care își incrementează valoarea sincron cu oscilatorul principal al sistemului sau în funcție de oscilațiile unui semnal extern aplicat pe un pin al microcontroller-ului.

Măsurare frecvență cu Arduino
Folosirea timer-ului hardware pentru numărarea oscilațiilor semnalului de măsurat impune aplicarea acestui semnal pe un pin specific. Acesta ar fi singurul dezavantaj. Dacă se aplică un semnal oscilant cu un factor de umplere de 50%, frecvența maximă teoretică ce poate fi măsurată este 8 MHz! O îmbunătățire semnificativă față de capacitatea funcției pulseIn. Dar, dacă se adaugă un circuit integrat divizor de frecvență (prescaler), se pot măsura frecvențe și mai mari (sute de MHz), evident cu scăderea rezoluției.

Pentru dezvoltarea și testarea codului am folosit o placă de dezvoltare compatibilă cu Arduino Nano, așezată pe un breadboard pe care am construit un oscilator simplu cu cristal de cuarț și integrat 74LS04. Codul este bazat pe biblioteca scrisă de Martin Nawrath și schița lui Nick Gammon.

Software-ul se folosește de două timer-e. Primul timer este de fapt numărătorul tranzițiilor semnalului de măsurat. Al doilea timer generează o întrerupere la intervale precise de timp. La această întrerupere se calculează frecvența semnalului, utilizând valoarea din primul timer. Conform fișei tehnice a microcontroller-ului ATmega328 (utilizat de plăcile Arduino Uno, Nano, Micro), frecvența maximă ce poate fi aplicată timer-ului 1 este de jumătate din frecvența procesorului, dacă semnalul aplicat are un factor de umplere de 50%. Dar, practic, această frecvență nu poate fi mai mare de 40% din frecvența procesorului. Așadar 16 MHz x 0,4 = 6,4 MHz.

Timer-ul 1 are o rezoluție de 16 biți, deci poate număra de la 0 la 65535. Dacă îi aplicăm o frecvență de, să alegem maximul, 8 MHz, termină de numărat în 8,192 milisecunde. Suficient pentru semnale de ordinul megahertzilor, acest interval este prea mic pentru semnale de câțiva kHz, limitând astfel gama de măsurare. De aceea, se va îmbunătăți rezoluția timer-ului 1 cu un contor software. Acesta este o variabilă incrementată la fiecare overflow al timer-ului 1.

Timer-ul 2 este acționat de oscilatorul principal al sistemului. Pentru a declanșa întreruperea la intervale precise de timp, se va folosi un divizor de frecvență și se va seta un prag de resetare. Frecvența oscilatorului plăcilor Arduino este de 16 MHz. Aceasta va fi divizată cu 128 înainte de a fi aplicată timer-ului. Astfel timer-ul 2 se incrementează cu o frecvență de 16000/128 = 125 kHz. Rezoluția lui este de 8 biți, deci poate număra până la 255. Dar, va fi limitat la 124. Astfel, durata necesară pentru a număra de la 0 la 124 cu o frecvență de 125 kHz este fix 1 milisecundă. La fel ca în cazul timer-ului 1, și aici se va folosi un contor de overflow, dar unul care poate fi modificat de utilizator. Astfel, timpul de măsurare poate fi ajustat în pași de 1 ms. Valoarea implicită a acestui timp este de 200 ms, setată prin variabila samplingPeriod. Cu această setare am putut măsura inclusiv frecvența de 50 Hz a rețelei electrice (în 200 ms, semnalul a efectuat doar 10 tranziții).

Codul este următorul (disponibil și pe GitHub):
// setare interval masurare (in miliescunde)
unsigned int samplingPeriod = 200;

// Contor overflow timer 1
volatile unsigned long overflow1;

void init_Timer1() {
  overflow1 = 0; // resetare contor

  // Setare registri pentru timer (cf. datasheet)
  TCCR1A = 0; // functionare normala
  TCCR1B = bit(CS12) | bit(CS11) | bit(CS10); // cu sursa de ceas externa

  TCNT1 = 0; // valoare curenta timer

  TIMSK1 = bit(TOIE1); // activare intererupere la overflow
}

ISR(TIMER1_OVF_vect) {
  overflow1++; // incrementare contor overflow
}

// Contor overflow pentru timer 2
volatile unsigned int overflow2;

void init_Timer2() {
  overflow2 = 0; // resetare contor

  GTCCR = bit(PSRASY); // resetare si sincronizare prescaler

  // Setare registri pentru timer (cf. datasheet)
  TCCR2A = bit(WGM21); // mod CTC (resetare la comparare)
  TCCR2B = bit(CS22) | bit(CS20); // prescaler setat la 1/128, adica 125 kHz
  OCR2A = 124; // numara de la 0 la 124, apoi genereaza intrerupere si se reseteaza

  TCNT2 = 0; // valoare curenta timer

  TIMSK2 = bit(OCIE2A); // activare intrerupere
}

// intreruperea are loc dupa 125 de cicluri cu frecventa 125 kHz, deci dureaza 0.001 s = 1 ms
ISR(TIMER2_COMPA_vect) {
  if (++overflow2 < samplingPeriod) // incrementare contor overflow si verificare daca e timpul sa se afiseze
    return; // inca se numara

  unsigned long totalSamples = (overflow1 << 16) + TCNT1;  
    
  float freqHz = totalSamples * 1000.0 / samplingPeriod;

  Serial.print("Frecventa: ");
  Serial.print((unsigned long)freqHz);
  Serial.println("Hz");

  // resetare timere si contoare overflow
  TCNT1 = 0; overflow1 = 0;
  TCNT2 = 0; overflow2 = 0;
}

void setup() {
  // activare port serial
  Serial.begin(115200);
  Serial.println("Frecventmetru Arduino");
  Serial.println();

  // Dezactivare Timer0; millis() nu poate fi folosit in aceasta schita
  TCCR0A = 0; TCCR0B = 0;

  // pornire timere
  init_Timer1();
  init_Timer2();
}

void loop() {
  // nimic de facut aici; totul este realizat prin intreruperi
}

Spre deosebire de schițele după care m-am inspirat, codul scris de mine apreciază continuu frecvența la pinul de intrare D5. Timpul de măsurare trebuie ajustat în funcție de frecvența semnalului aplicat. Se poate adăuga cod care să modifice acest timp automat, după fiecare măsurătoare, în funcție de numărul de oscilații din măsurarea anterioară.

Codul prezentat aici a fost dezvoltate și testat pe platforma ATmega328. Alte microcontroller-e pot avea regiștri de configurare a timer-elor diferiți. Nick Gammon prezintă o schiță adaptată pentru ATmega2560.

Construirea unui frecvențmetru bazat pe Arduino devine foarte ușoară acum. Este necesar un afișaj și un amplificator de semnal (cu tranzistor, amplificator operațional sau trigger Schmitt). Acuratețea și rezoluția acestui frecvențmetru depind de acuratețea oscilatorului principal și de rezoluția timer-ului 2 (redusă prin prescaling).

Niciun comentariu :

Trimiteți un comentariu

Vă recomandăm să citiți regulamentul comentariilor înainte de a scrie un comentariu.