• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Pomiar częstotliwości przebiegu prostokątnego
#1
Witam
Mam zagwozdkę. W programie poniżej wykonuje między innymi pomiar częstotliwości przebiegu prostokątnego za pomocą funkcji PulseIn w zakresie 2kHz do około 12kHz. 

Ogólnie chce mierzyć częstotliwość na przepływomierzu masowym powietrza. W momencie kiedy nacisnę przycisk zostaje otwarty elektrozawór który poda powietrze przez 4 sekundy, a więc na przepływomierzu będzie wzrastać częstotliwość. Chce mierzyć częstotliwość i wyciągnąć największą wartość i wyświetlić ją na wyświetlaczu. 
Problem jest taki że gdy podaje różne częstotliwości w zakresie od 2 kHz do 12 kHz to na wyświetlaczu potrafi pojawić się wartość np.: 13625 Hz. Chciałbym zbliżyć się jak najbardziej do prawdziwych wartości. Poniżej zamieszczam kody.

Generator różnych częstotliwości:
Kod:
const int buttonPin = 4;
const int outputPin = 6;
int measurementCount = 0;
unsigned long startTime = 0; // Zmienna przechowująca czas rozpoczęcia generowania losowych wartości

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(outputPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // Sprawdź, czy przycisk został naciśnięty
  if (digitalRead(buttonPin) == LOW) {
    startTime = millis(); // Zapisz aktualny czas rozpoczęcia generowania

    // Generuj losowe wartości przez 4 sekundy
    while (millis() - startTime < 4100) {
     
      int frequency = random(2000, 12001); // Zakres od 2 kHz do 12 kHz
      //delay(60);

      // Zwiększ numer pomiaru przy każdej iteracji
      measurementCount++;

      // Ustawianie częstotliwości wyjściowej
      tone(outputPin, frequency);

      // Wypisywanie informacji do monitora szeregowego
      Serial.print("Pomiar ");
      Serial.print(measurementCount);
      Serial.print(": Częstotliwość: ");
      Serial.print(frequency);
      Serial.println(" Hz");
     
     
    }

    noTone(outputPin);
    measurementCount = 0;
  }
}

Kod pomiaru częstotliwości:
Kod:
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_WIDTH 128                              // Wysokośc ekranu w pikselach
#define OLED_HEIGHT 64                              // Szerokośc ekranu w pikselach
Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT);


float voltage = 0;
int maxAnalogValue = 0;
int maxFrequency = 0;
int stanPrzycisku=0;
int zmienna= 0;

int measurementCount = 0;


void setup()
  {
  Serial.begin(9600);
  pinMode (4,INPUT_PULLUP);
  pinMode (2,INPUT);
  pinMode(A1,INPUT);                                // Ustawnienia pinu A1 jako wejscie
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);        // Inicjalizacja wyświetlacza OLED
  setCustomTextSize(2);                             // Ustawienie rozmiaru czcionki
  display.setTextColor(WHITE);                      // Ustawienie koloru tekstu
  wyswietlanie_start();
  }


void setCustomTextSize(float size)                  //Ustawenie niestandardowego rozmiaru dla wyswietlacz
  {
  display.setTextSize(round(size));        
  }

void wyswietlanie_start ()
  {
    display.clearDisplay ();        // Czyszczenie ekranu
    display.setCursor(0, 0);        // Ustawienie pozycji kursora
    display.println("Nacisnij");    // Wyświetlenie napisu "U="
    display.setCursor(0, 24);       // Ustawienie pozycji kursora
    display.println("przycisk");    // Wyświetlenie napisu "U="
    display.setCursor(0, 48);       // Ustawienie pozycji kursora
    display.println("START");       // Wyświetlenie napisu "U="
    display.display();              // Wyświetlenie zawartości bufora na ekranie
  }

void wyswietlanie_trwa_pomiar()
  {
    display.clearDisplay ();
    display.setCursor(0, 0);                  // Ustawienie pozycji kursora
    display.println("Trwa pomiar");           // Wyświetlenie napisu "U="
    display.display(); 
  }

void wyswietlanie_wartosci()
{
    display.clearDisplay ();        // Czyszczenie ekranu
    display.setCursor(0, 0);        // Ustawienie pozycji kursora
    display.println("U=");          // Wyświetlenie napisu "U="
    display.setCursor(36, 0);       // Ustawienie pozycji kursora
    display.println(voltage);       // Wyświetlenie wartości napięcia
    display.setCursor(0, 34);       // Ustawienie pozycji kursora
    display.println("f=");          // Wyświetlenie napisu "f="
    display.setCursor(36, 36);      // Ustawienie pozycji kursora
    display.print(maxFrequency);          // Wyświetlenie wartości czestotliwosci
    display.display();              // Wyświetlenie zawartości bufora na ekranie
  }




void pomiar()
  { 
    maxFrequency = 0;
    voltage =0;

    unsigned long startTime = millis();

    while (millis() - startTime < 4000)                                 // Mierz czas przez 4 sekundy
    {
      unsigned long pulseWidthHigh = pulseIn(2, HIGH);          // Mierz czas trwania sygnału na pinie cyfrowym D2
      delayMicroseconds (5);
      unsigned long pulseWidthLow = pulseIn(2, LOW);
      delayMicroseconds (5);
      maxAnalogValue = analogRead(A1);
     
     
      int frequency = 1000000 / (pulseWidthHigh + pulseWidthLow );       // Oblicz częstotliwość na podstawie czasu trwania sygnału
      voltage = maxAnalogValue * (5.0 / 1023.0);                        // Obliczenie napięcia

      if (frequency > maxFrequency)                                     // Znajdź największą zmierzoną wartość
      {                                  
        maxFrequency = frequency;
      }
      measurementCount++;
      Serial.print(measurementCount);
      Serial.print(": Częstotliwość: ");
      Serial.println(frequency);
    }
    measurementCount = 0;
}

void loop()
  {
  stanPrzycisku= digitalRead(4);
  if (stanPrzycisku == LOW && zmienna==0)
    {
      wyswietlanie_trwa_pomiar();
      pomiar ();
      zmienna++;  
    }
    
  if (stanPrzycisku == LOW && zmienna==1)
    {
      wyswietlanie_wartosci();
      zmienna--;
    }
    delay (100);
  }
 
Odpowiedź
#2
(06-12-2023, 19:50)Fondix napisał(a): Witam
Mam zagwozdkę. W programie poniżej wykonuje między innymi pomiar częstotliwości przebiegu prostokątnego za pomocą funkcji PulseIn w zakresie 2kHz do około 12kHz. 


Musisz bardziej sprecyzować problem - np. jak dokładnie chcesz mierzyć tą częstotliwość, jak często. Od tego zależy czy lepiej będzie liczyć impulsy w ustalonym czasie czy mierzyć długość 1 okresu przebiegu. Wydaje mi się, że jeśli pomiar ma być możliwie najlepszy musisz "wyjść poza Arduino" tzn. zakładająć że wykorzystujesz Atmegę wykorzystać jakiś wolny TIMER, wykonać kilka połączeń (impulsy na TIMER a nie na GPIO). Oczywiście trzeba dać generator kwarcowy. Kiedyś zbudowałem układ po pomiary częstotliwości na procku ATXmega (ma więcej układów peryferujnych) i korzystając z impulsów 1PPS z odbiornika GPS zmierzyłem częstotliwość zegarową. Wyszło 24MHz z wahaniami po kilkaset Hz (przy taktowaniu kwarcowym) lub po kilkanaście kHz (przy taktowaniu z wbudowanego generatora RC). W ATXmega można zegar przełączać programowo w trakcie pracy...
Dokumentacja Arduino podaje, że na ATmedze core wykorzystuje 1 timer, reszta jest wolna.
Może jest jakaś gotowa biblioteka...
 
Odpowiedź
#3
(06-12-2023, 20:46)oscarX napisał(a):
(06-12-2023, 19:50)Fondix napisał(a): Witam
Mam zagwozdkę. W programie poniżej wykonuje między innymi pomiar częstotliwości przebiegu prostokątnego za pomocą funkcji PulseIn w zakresie 2kHz do około 12kHz. 


Musisz bardziej sprecyzować problem - np. jak dokładnie chcesz mierzyć tą częstotliwość, jak często. Od tego zależy czy lepiej będzie liczyć impulsy w ustalonym czasie czy mierzyć długość 1 okresu przebiegu. Wydaje mi się, że jeśli pomiar ma być możliwie najlepszy musisz "wyjść poza Arduino" tzn. zakładająć że wykorzystujesz Atmegę wykorzystać jakiś wolny TIMER, wykonać kilka połączeń (impulsy na TIMER a nie na GPIO). Oczywiście trzeba dać generator kwarcowy. Kiedyś zbudowałem układ po pomiary częstotliwości na procku ATXmega (ma więcej układów peryferujnych) i korzystając z impulsów 1PPS z odbiornika GPS zmierzyłem częstotliwość zegarową. Wyszło 24MHz z wahaniami po kilkaset Hz (przy taktowaniu kwarcowym) lub po kilkanaście kHz (przy taktowaniu z wbudowanego generatora RC). W ATXmega można zegar przełączać programowo w trakcie pracy...
Dokumentacja Arduino podaje, że na ATmedze core wykorzystuje 1 timer, reszta jest wolna.
Może jest jakaś gotowa biblioteka...

Nie mam narzuconego jak często musze mierzyć gdyż przepływomierz ustabilizuje się bardzo szybko. Przykładowo przez 0,1 sekundy będzie częstotliwość wzrastać, a następnie przez te powiedzmy 3,9 sekundy będzie oscylować wokół np 12 khz. Pomiar ma być że tak powiem zgrubny. Układ będzie działał tak: klikam przycisk -> otwiera się elektrozawór, a wiec podaje powietrze do przepływomierza, po np 0,1 s rozpoczyna się pomiar -> elektrozawór się zamyka, pomiar się zakańcza -> wyświetla sie max wartość zmierzona
 
Odpowiedź
#4
Żeby były bardziej prawdziwe wartości to czeka się nauka o timerach i ICP. Innego lepszego sposobu nie ma, uno ma timer sprzętowy ICP włąsnie do tego.
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#5
(07-12-2023, 06:52)Jarewa0606 napisał(a): Żeby były bardziej prawdziwe wartości to czeka się nauka o timerach i ICP.  Innego lepszego sposobu nie ma,  uno ma timer sprzętowy ICP włąsnie do tego.

Właśnie - jeszcze potrzebne jest info jaki moduł/procek kolega Fondix używa. Jeśli ATMEGA to prosto. Jeśli jakiś inny to niekoniecznie.
Zasadniczo przy niskich częstotliwościach stosuje lepiej jest zastosować pomiar okresu sygnału. Timer liczący takty zegara (w miarę najszybszego) a na wejście ICP timera badany sygnał.
ICP to takie wejście, które może zgłosić przerwanie, ale dodatkowo w momencie wykrycia zbocza zapamiętywany jest w specjalnym rejestrze aktualny stan licznika. Można go _POTEM_ odczytać, bez specjalnego starania się o czas obsługi przerwania. Na kolejnych przerwaniach można obliczyć różnicę pomiędzy aktualną wartością rejestru a wartością odczytaną (i gdzieś zapamiętaną) w poprzednim przerwaniu. W ten sposób otrzymujemu okres sygnału. Na bierząco można wyznaczać jego minimalną wartość, odpowiadającą maksymalnej częstotliwości (f = 1/t). Taki sposób liczenia długości sygnały jest dużo dokładniejszy od pomiaru polegającego na odczytywaniu pinu IO. Ale trzeba użyć wejście ICP, których jest mało.
 
Odpowiedź
#6
Wogole program jest bardzo źle przemyślany, pulseint może zwrócić "0" a przez zero się nie dzieli. I te cyrki wcale nie muszą być powodem złego odczyty czasu
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#7
Przepraszam nie napisałem że działam na arduino nano v3  Rolleyes. Zmodyfikowałem program, przeszedłem z pulseIN na <FreqCount>. Odczyty piękne, tylko zastanawia mnie jedna rzecz. W poniższym kodzie FreqCount (1000) decyduje o między innymi o czasie odczytu. Tylko gdy wartość zmienie na np 10 to jak najbardziej pomiary są szybsze ale też zaokrąglone np.: dla FreqCount (1000) odczyt wynosi 12211 hz a dla FreqCount (10) odczyt wynosi 12200 hz. Pytanie czemu tak się dzieje ?

Kod:
#include <FreqCount.h>

void setup() {
  Serial.begin(57600);
  FreqCount.begin(1000);
}

void loop() {
  if (FreqCount.available()) {
    unsigned long count = FreqCount.read();
    Serial.println(count);
  }
}
 
Odpowiedź
#8
Dobra doprowadziłem program do stanu który mnie zadowala. Błąd odczytu to max 50 hz przy 12 kHz. Poniżej zamieszczam kody:

Generator liczb losowych :

Kod:
const int buttonPin = 4;
const int outputPin = 2;
int measurementCount = 0;
unsigned long startTime = 0;
int frequency = 0;


void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(outputPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
 
  if (digitalRead(buttonPin) == LOW) {
    startTime = millis();
    measurementCount = 0; /

  
    while (millis() - startTime < 4000) {
      if (millis() - startTime < 1000) {
       
        frequency = random(0, 10000); // Zakres od 0 do 12 kHz
        delay(30);
      } else {
       
        frequency = random(11501, 12001);
        delay (30);
      }

      tone(outputPin, frequency);

      Serial.print("Line: ");
      Serial.print(measurementCount++);
      Serial.print(", Frequency: ");
      Serial.println(frequency);
    }

    noTone(outputPin);
  }

  measurementCount=0;
  delay (30);
}

Pomiar:
Kod:
#include <Wire.h>
#include <Adafruit_SSD1306.h>
#include <FreqCount.h> 
#define OLED_WIDTH 128                              // Wysokośc ekranu w pikselach
#define OLED_HEIGHT 64                              // Szerokośc ekranu w pikselach
Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT);


float voltage = 0;
float maxVoltage = 0;

int count=0;
int maxCount = 0;
int frequency=0;

int buttonState=0;
int buttonValue= 0;




void setup()
  {
  Serial.begin(9600);
  pinMode (4,INPUT_PULLUP);
  pinMode(A1,INPUT);                               // Ustawnienia pinu A1 jako wejscie
  FreqCount.begin(20);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);        // Inicjalizacja wyświetlacza OLED
  display.clearDisplay();                             //enough basking
  setCustomTextSize(2);                             // Ustawienie rozmiaru czcionki
  display.setTextColor(WHITE);                      // Ustawienie koloru tekstu
  display_start();
  }

void setCustomTextSize(float size)                  //Ustawenie niestandardowego rozmiaru dla wyswietlacz
  {
  display.setTextSize(round(size));        
  }

void display_start ()
  {
    display.clearDisplay ();        // Czyszczenie ekranu
    display.setCursor(0, 0);        // Ustawienie pozycji kursora
    display.println("Nacisnij");    // Wyświetlenie napisu "U="
    display.setCursor(0, 24);       // Ustawienie pozycji kursora
    display.println("przycisk");    // Wyświetlenie napisu "U="
    display.setCursor(0, 48);       // Ustawienie pozycji kursora
    display.println("START");       // Wyświetlenie napisu "U="
    display.display();              // Wyświetlenie zawartości bufora na ekranie
  }

void display_progress()
  {
    display.clearDisplay ();
    display.setCursor(0, 0);                  // Ustawienie pozycji kursora
    display.println("Trwa pomiar");           // Wyświetlenie napisu "U="
    display.display(); 
  }

void display_value()
{
    display.clearDisplay ();        // Czyszczenie ekranu
    display.setCursor(0, 0);        // Ustawienie pozycji kursora
    display.println("U=");          // Wyświetlenie napisu "U="
    display.setCursor(36, 0);       // Ustawienie pozycji kursora
    display.println(voltage);       // Wyświetlenie wartości napięcia
    display.setCursor(0, 34);       // Ustawienie pozycji kursora
    display.println("f=");          // Wyświetlenie napisu "f="
    display.setCursor(36, 36);      // Ustawienie pozycji kursora
    display.print(frequency);          // Wyświetlenie wartości czestotliwosci
    display.display();              // Wyświetlenie zawartości bufora na ekranie
  }

void measurement()
  { 
    voltage =0; 
    count=0;
    maxCount=0;
    frequency=0;
    unsigned long startTime = millis();

    while (millis() - startTime < 3900)                                 // Mierz czas przez 4 sekundy
    {
      maxVoltage = analogRead(A1);
      voltage = maxVoltage * (5.0 / 1023.0);                        // Obliczenie napięcia

    if (FreqCount.available())
      {                                                               //if the code if working
      count = FreqCount.read();                           //create float var called count and populate it with current frequency count

    if (count > maxCount)                                     // Znajdź największą zmierzoną wartość
      {                                  
        maxCount = count;
      }
      frequency= (maxCount*50);
      }
    }
  }

void loop()
  {
  buttonState= digitalRead(4);
  if (buttonState == LOW && buttonValue==0)
    {
      display_progress();
      measurement ();
      buttonValue++;  
    }
    
  if (buttonState == LOW && buttonValue==1)
    {
      display_value();
      buttonValue--;
    }
    delay (100);
  }



// Przycisk D4
//Wejscie sygnału D5
// SCL A5
// SDA A4
 
Odpowiedź
#9
(07-12-2023, 22:27)Fondix napisał(a): Przepraszam nie napisałem że działam na arduino nano v3  Rolleyes. Zmodyfikowałem program, przeszedłem z pulseIN na <FreqCount>. Odczyty piękne, tylko zastanawia mnie jedna rzecz. W poniższym kodzie FreqCount (1000) decyduje o między innymi o czasie odczytu. Tylko gdy wartość zmienie na np 10 to jak najbardziej pomiary są szybsze ale też zaokrąglone np.: dla FreqCount (1000) odczyt wynosi 12211 hz a dla FreqCount (10) odczyt wynosi 12200 hz. Pytanie czemu tak się dzieje ?

Ze sposobu użycia można wnioskować, że biblioteka ta działa na zasadzie liczenia impulsów w zadanym odcinku czasu. Wtedy pojawia się dość typowa zależność - dłuższy pomiar => większa dokładność, krótszy => mniejsza. Wynika to z tego, że licznik po prostu liczy pełne zdarzenia - daje liczbę naturalną, jak czas wynosi 1sekundę to zmiana 1 Hz wystarczy by wystąpił o jeden cykl sygnału wejściowego więcej lub mniej. Rodzielczość pomiaru (najmniejsza różnica, którą daje się wykryć) wynosi 1Hz. Dla jednej setnej sekundy rozdzielczość wynosi już 100Hz.
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości