Skocz do zawartości

Miernik LC na ESP32 i komparatorze LM311


Pomocna odpowiedź

Napisano (edytowany)

Witam, zrobiłem sobie taki miernik z tytułu do pomiaru pojemności kondensatorów i indukcyjności cewek, pomysł zaczerpnięty z elektrody, ale jest to bardzo popularny projekt, jedyne co musiałem zrobić to przenieść kod na C z assemblera, ale tym zajeła się sztuczna inteligencja, układ zbudowany trochę na zasadzie z tego co mam pod ręką, dlatego jest tutaj  przekażnik na 12v zasilany z modułu przetwornicy boost, która z kolei jest zasialana z pakietu 2S, trochę to przekombinowane ale działa.

poniżej schemat układu, oczywiście zapomniałem dać dzielnik do pomiaru napięcia aku, trzeba było drutować😐 finalnie wszystko działa👍

Sheet_1.thumb.png.e72eb11904070460820701d1c2b63b27.png

#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "driver/pcnt.h"

LiquidCrystal_I2C lcd(0x27, 16, 2);

// Definicje pinów sprzętowych
const int PIN_POMIARU = 34;      // D34 - wyjście z LM311 (rezonans)
const int GUZIK_KALIBRACJI = 19; // D19 - przycisk kalibracji i zerowania
const int PIN_PRZEKAZNIKA = 26;  // D26 - bramka MOSFETa przekaźnika
const int PIN_TRYBU = 14;        // D14 - przełącznik trybu SW2
const int PIN_AKU = 35;          // D35 - wejście dzielnika napięcia akumulatora
const int PIN_BUZZER = 27;       // D27 - wyjście na buzzer pasywny

// Parametry dzielnika napięcia akumulatora i kalibracja
const double R_GND = 9900.0;     // 9.9k do masy
const double R_VCC = 49600.0;    // 49.6k do zasilania VCC (akumulatora)
const double COEFF_DZIELNIKA = (R_GND + R_VCC) / R_GND;

// WSPÓŁCZYNNIK KALIBRACJI: Koryguje odczyt z 4.26V na rzeczywiste 8.13V
const double KALIBRACJA_ADC = 8.13 / 4.26;

// Dokładna wartość kondensatora kalibracyjnego ze schematu
const double C_CAL = 1000.0e-12;

double F1 = 0.0;
double F2 = 0.0;
double F3 = 0.0;

double offset_C = 0.0;
double offset_L = 0.0;

// Zmienne do nieblokującego odliczania czasu dla alarmu (3 sekundy przerwy)
unsigned long poprzedni_czas_alarmu = 0;
const unsigned long INTERWAL_ALARMU = 3000;

// Poprawione zmienne do precyzyjnej obsługi przycisku (Debouncing)
unsigned long czas_wcisniecia_guzika = 0;
bool guzik_byl_wcisniety = false;
bool kalibracja_wykonana_w_locie = false;

void setupPulseCounter() {
  pcnt_config_t pcnt_config = {
    .pulse_gpio_num = PIN_POMIARU,
    .ctrl_gpio_num = PCNT_PIN_NOT_USED,
    .lctrl_mode = PCNT_MODE_KEEP,
    .hctrl_mode = PCNT_MODE_KEEP,
    .pos_mode = PCNT_COUNT_INC,
    .neg_mode = PCNT_COUNT_DIS,
    .counter_h_lim = 32767,
    .counter_l_lim = -32768,
    .unit = PCNT_UNIT_0,
    .channel = PCNT_CHANNEL_0,
  };
  pcnt_unit_config(&pcnt_config);
  pcnt_set_filter_value(PCNT_UNIT_0, 20);
  pcnt_filter_enable(PCNT_UNIT_0);
  pcnt_counter_pause(PCNT_UNIT_0);
  pcnt_counter_clear(PCNT_UNIT_0);
}

// Średnia krocząca z 16 pomiarów po 10ms
double zmierzCzestotliwosc() {
  uint32_t suma_impulsow = 0;
  const int PROBKI = 16;

  for (int i = 0; i < PROBKI; i++) {
    int16_t count = 0;
    pcnt_counter_clear(PCNT_UNIT_0);
    pcnt_counter_resume(PCNT_UNIT_0);

    delayMicroseconds(10000);

    pcnt_counter_pause(PCNT_UNIT_0);
    pcnt_get_counter_value(PCNT_UNIT_0, &count);

    if (count > 0) {
      suma_impulsow += count;
    }
  }
  return ((double)suma_impulsow / (double)PROBKI) * 100.0;
}

// Funkcja mierząca rzeczywiste napięcie akumulatora z uwzględnieniem współczynnika kalibracji
double odczytajNapiecieAku() {
  uint32_t sum = 0;
  for (int i = 0; i < 20; i++) {
    sum += analogRead(PIN_AKU);
    delayMicroseconds(50);
  }
  double vPin = ((double)sum / 20.0) * 3.3 / 4095.0;
  return vPin * COEFF_DZIELNIKA * KALIBRACJA_ADC;
}

// Generowanie tonu na buzzerze pasywnym
void dzwiekBuzzera(int czas_ms) {
  tone(PIN_BUZZER, 2000);
  delay(czas_ms);
  noTone(PIN_BUZZER);
}

// NIEBLOKUJĄCA funkcja obsługi alarmów napięcia akumulatora
void sprawdzAlarmyNapiecia() {
  unsigned long obecny_czas = millis();

  if (obecny_czas - poprzedni_czas_alarmu >= INTERWAL_ALARMU) {
    double vAku = odczytajNapiecieAku();

    if (vAku < 6.2) {
      poprzedni_czas_alarmu = obecny_czas;
      lcd.setCursor(12, 0);
      lcd.print("!BAT");

      for (int i = 0; i < 3; i++) {
        dzwiekBuzzera(80);
        delay(80);
      }
      Serial.print("[ALARM] Niskie napiecie akumulatora: "); Serial.print(vAku); Serial.println(" V");
    }
    else if (vAku > 8.4) {
      poprzedni_czas_alarmu = obecny_czas;
      lcd.setCursor(12, 0);
      lcd.print("!VCC");

      for (int i = 0; i < 5; i++) {
        dzwiekBuzzera(50);
        delay(50);
      }
      Serial.print("[ALARM] Za wysokie napiecie akumulatora: "); Serial.print(vAku); Serial.println(" V");
    }
  }
}

void wykonajZerowanie() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Zerowanie...");

  offset_C = 0.0;
  offset_L = 0.0;

  F3 = zmierzCzestotliwosc();
  double mianownik = pow(F1 / F2, 2) - 1.0;

  if (mianownik > 0.0 && F3 > 1000.0) {
    bool trybCapacitor = (digitalRead(PIN_TRYBU) == HIGH);

    if (trybCapacitor) {
      double licznik = pow(F1 / F3, 2) - 1.0;
      offset_C = C_CAL * (licznik / mianownik);
    } else {
      double C_ind = C_CAL / mianownik;
      offset_L = (pow(F1 / F3, 2) - 1.0) / (4.0 * pow(PI, 2) * pow(F1, 2) * C_ind);
    }
  }

  lcd.clear();
  lcd.print("Wyzerowano!");
  delay(600); // Nieco skrócony czas, aby szybciej wracał do ekranu pomiaru
}

void wykonajKalibracje() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Kalibracja...");
  Serial.println("--- Start Kalibracji Ukladu LC ---");

  digitalWrite(PIN_PRZEKAZNIKA, LOW);
  delay(600);
  F1 = zmierzCzestotliwosc();

  digitalWrite(PIN_PRZEKAZNIKA, HIGH);
  delay(600);
  F2 = zmierzCzestotliwosc();

  digitalWrite(PIN_PRZEKAZNIKA, LOW);
  delay(400);

  offset_C = 0.0;
  offset_L = 0.0;

  if (F1 < 1000.0 || F2 < 1000.0 || F2 >= F1) {
    lcd.clear();
    lcd.print("Blad kalibracji!");
    F1 = 0.0; F2 = 0.0;
  } else {
    double mianownik = pow(F1 / F2, 2) - 1.0;
    double C_wewn = C_CAL / mianownik;
    double L_wewn = 1.0 / (4.0 * pow(PI, 2) * pow(F1, 2) * C_wewn);

    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Cc:"); lcd.print(C_wewn * 1e9, 3); lcd.print("nF");
    lcd.setCursor(0, 1);
    lcd.print("Lc:"); lcd.print(L_wewn * 1e6, 2); lcd.print("uH");
    delay(3000);
  }
}

void setup() {
  Serial.begin(115200);

  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.print("Miernik LC v3.4");

  pinMode(GUZIK_KALIBRACJI, INPUT_PULLUP);
  pinMode(PIN_TRYBU, INPUT_PULLUP);
  pinMode(PIN_BUZZER, OUTPUT);

  pinMode(PIN_PRZEKAZNIKA, OUTPUT);
  digitalWrite(PIN_PRZEKAZNIKA, LOW);

  setupPulseCounter();

  delay(1500);
  wykonajKalibracje();

  if (F1 > 0.0 && F2 > 0.0) {
    double vStart = odczytajNapiecieAku();
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Napiecie aku:");
    lcd.setCursor(0, 1);
    lcd.print("U = "); lcd.print(vStart, 2); lcd.print(" V");
    delay(3000);
  }
}

void loop() {
  sprawdzAlarmyNapiecia();

  // --- POPRAWIONA, FILTROWANA OBSŁUGA PRZYCISKU (DEBOUNCING) ---
  bool guzik_wcisniety = (digitalRead(GUZIK_KALIBRACJI) == LOW);
  unsigned long obecny_czas = millis();

  if (guzik_wcisniety) {
    if (!guzik_byl_wcisniety) {
      czas_wcisniecia_guzika = obecny_czas;
      guzik_byl_wcisniety = true;
      kalibracja_wykonana_w_locie = false;
    }
    else if (!kalibracja_wykonana_w_locie && (obecny_czas - czas_wcisniecia_guzika > 1800)) {
      // Próg kalibracji lekko skrócony do 1.8s dla lepszego wyczucia
      dzwiekBuzzera(100);
      wykonajKalibracje();
      kalibracja_wykonana_w_locie = true;
    }
  }
  else {
    if (guzik_byl_wcisniety) {
      unsigned long czas_trzymania = obecny_czas - czas_wcisniecia_guzika;

      // FILTR: wykonaj zerowanie tylko, jeśli kliknięcie trwało od 50 ms do 1000 ms
      // Chroni to przed przypadkowymi szumami styków (drganiem) oraz błędami w locie
      if (!kalibracja_wykonana_w_locie && (czas_trzymania > 50) && (czas_trzymania < 1000)) {
        dzwiekBuzzera(40); // Bardzo krótkie piknięcie potwierdzające rejestrację kliknięcia
        wykonajZerowanie();
      }
      guzik_byl_wcisniety = false;
      kalibracja_wykonana_w_locie = false;
    }
  }

  digitalWrite(PIN_PRZEKAZNIKA, LOW);
  F3 = zmierzCzestotliwosc();

  if (F1 == 0.0 || F2 == 0.0) {
    lcd.setCursor(0, 0);
    lcd.print("Nieskalibrowany ");
    lcd.setCursor(0, 1);
    lcd.print("Wcisnij guzik   ");
    return;
  }

  if (F3 < 1000.0) {
    lcd.setCursor(0, 0);
    lcd.print("Brak oscylacji  ");
    lcd.setCursor(0, 1);
    lcd.print("Podlacz element ");
    return;
  }

  bool trybCapacitor = (digitalRead(PIN_TRYBU) == HIGH);
  double mianownik = pow(F1 / F2, 2) - 1.0;

  lcd.setCursor(0, 0);
  lcd.print("F: ");
  lcd.print(F3 / 1000.0, 3);
  lcd.print(" kHz  ");

  lcd.setCursor(0, 1);

  if (mianownik > 0.0) {
    if (trybCapacitor) {
      double licznik = pow(F1 / F3, 2) - 1.0;
      double C_x = C_CAL * (licznik / mianownik) - offset_C;

      lcd.print("C: ");
      if (C_x < 0.0 || F3 >= F1) {
        lcd.print("0.000 pF       ");
      } else if (C_x < 1e-9) {
        lcd.print(C_x * 1e12, 2);
        lcd.print(" pF        ");
      } else {
        lcd.print(C_x * 1e9, 3);
        lcd.print(" nF        ");
      }
    }
    else {
      double C_ind = C_CAL / mianownik;
      double L_x = (pow(F1 / F3, 2) - 1.0) / (4.0 * pow(PI, 2) * pow(F1, 2) * C_ind) - offset_L;

      lcd.print("L: ");
      if (L_x < 0.0 || F3 >= F1) {
        lcd.print("0.000 uH       ");
      } else if (L_x < 1e-3) {
        lcd.print(L_x * 1e6, 3);
        lcd.print(" uH        ");
      } else {
        lcd.print(L_x * 1e3, 3);
        lcd.print(" mH        ");
      }
    }
  }
  delay(20); // Zmniejszone opóźnienie, aby loop kręcił się szybciej i precyzyjniej sprawdzał guzik
}

 

 

IMG_20261102_120643059_HDR.thumb.jpg.661250b517f34e5dd5678e7e026b00ea.jpg

 

 

Edytowano przez farmaceuta
  • Lubię! 2
1 godzinę temu, orb777 napisał:

Dobra robota, podoba mi się.

Dzięki 👍 

Teraz żałuję że nie wziąłem kilku cewek przykładowych żeby mieć świadomość czy to mniej więcej poprawnie działa...

(edytowany)

@orb777 nie widziałem tego kalkulatora, mi by pasowało kilka sztuk w dużym zakresie żeby sprawdzić czy jako tako to mierzy...takie po kilkadziesiąt uH to mam w przetwornicach to mogę do testów wydrukować i sprawdzić, a pasowało by coś jeszcze w mH 

Edytowano przez farmaceuta
  • Lubię! 1

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...