Skocz do zawartości
Asica10

Arduino PCINT - błędne zliczanie impulsów

Pomocna odpowiedź

Witam wszystkich!

Mam problem z przerwaniami PCINT. Używam ich do zliczania impulsów ( do wyświetlania obrotów wentylatora komputerowego). Problem że wynik który otrzymuje jest oo ok 20% inny niż rzeczywiste obroty. Tak samo obroty bardzo skaczą przy ok 1.5k skoki są rzędu 200+/- wciągu 1 sekundy, gdzie wentylator kręci się praktycznie tak samo +/- 10rpm.

Prosiłbym o weryfikacje kodu i pomoc w znalezieniu błędu....

//read RPM PCINT by Asica10

//-----Define variable for measure RPM-----
volatile int rpm_counter_1 = 0;
volatile int rpm_counter_2 = 0;
volatile int rpm_counter_3 = 0;

volatile uint8_t interrupt_pin = 0xFF; // Variable used to check where an interrupt occurs

int rpm_1 = 0;
int rpm_2 = 0;
int rpm_3 = 0; 

unsigned long previous_millis = 0;

//Defines the structure for multiple fans and 
//their dividers 
typedef struct{
char fantype;
unsigned int fandiv; }fanspec;

//Fans definitions
//Varible used to select the fan and it's divider,

fanspec fanspace[3]={{0,1},{1,2},{2,8}}; char fan = 2; //set 1 for unipole hall effect sensor, 2 for bipole hall effect sensor


void setup() {
 PCICR |= (1<<PCIE2); // define interrupt on PCIE2 - PCINT[23:16]
 PCMSK2 |= (1<<PCINT18) | (1<<PCINT19) | (1<<PCINT20); // Permission to interrupt

 Serial.begin(9600);

 sei(); // Global Interrupt Enable
}

void loop() {

 if (millis() - previous_millis == 1000) //Update every one second
   {  

   cli(); // Global Interrupt Disable  - when calculeting

   rpm_1 = ((rpm_counter_1 * 60) / fanspace[fan].fandiv);  // Calculating rpm_1 using dividers
   rpm_2 = ((rpm_counter_2 * 60) / fanspace[fan].fandiv); // Calculating rpm_2 using dividers
   rpm_3 = ((rpm_counter_3 * 60) / fanspace[fan].fandiv); // Calculating rpm_3 using dividers


   Serial.print("RPM_1 =\t"); //print "RPM" and tab.
   Serial.print(rpm_1); // print the rpm value.
   Serial.print("\t Hz_1=\t"); //print "Hz".
   Serial.println(rpm_counter_1); // print frequency value

   Serial.print("RPM_2 =\t"); //print "RPM" and tab.
   Serial.print(rpm_2); // print the rpm value.
   Serial.print("\t Hz_2=\t"); //print "Hz".
   Serial.println(rpm_counter_2); // print frequency value

   Serial.print("RPM_3 =\t"); //print "RPM" and tab.
   Serial.print(rpm_3); // print the rpm value.
   Serial.print("\t Hz_3=\t"); //print "Hz".
   Serial.println(rpm_counter_3); // print frequency value

   rpm_counter_1 = 0; //Restart counter
   rpm_counter_2 = 0;
   rpm_counter_3 = 0;

   previous_millis = millis(); 
   sei(); // Global Interrupt Enable
   }
}


ISR(PCINT2_vect) { // interrupt vector

//-----Checking which PIN reported interrupt-----
   uint8_t changedbits = PIND ^ interrupt_pin;
   interrupt_pin = PIND;

   if(changedbits & (1 << PD2))
     {
     rpm_counter_1++;
     }
   if(changedbits & (1 << PD3))
     {
     rpm_counter_2++;
     }
   if(changedbits & (1 << PD4))
     {
     rpm_counter_3++;
     }

}





Z góry bardzo dziękuje

Udostępnij ten post


Link to post
Share on other sites

Zastanów się ile czasu upływa (i jak jest on zmienny) od chwili detekcji sekundy do chwili wyzerowania liczników i odblokowania pomiarów. W tym czasie robisz jakieś obliczenia a co gorsza wysyłasz wyniki przez port szeregowy. I przez cały ten czas liczniki są zablokowane. To ile trwa rzeczywisty pomiar? Co gorsza przez cały ten czas masz wyłączone liczenie czasu (millis), które Arduino robi przecież na przerwaniach od timera 0.

Udostępnij ten post


Link to post
Share on other sites

Wywaliłem wysyłanie danych poprzez port szeregowy poza obszar gdzie przerwania są wyłączone, poprawa wystąpiła w granicach błędu pomiarowego. Co zauważyłem że jeżeli zrobię pomiar tylko dla 1 went i wywalę detekcje gdzie to wystąpiło to obroty skaczą o ok 10% w góre (wygląda na to że kilka lini kodu wprowadza znaczne zaburzenia). Po wywaleniu wykrywania pinu wprowadziłem detekcje zbocza opadającego/narastającego np: if(!(PIND & (1<

Udostępnij ten post


Link to post
Share on other sites

1. Zacznij od upewnienia się, że sygnał który doprowadzasz do pinu procesora jest OK. Obejrzyj na oscyloskopie (masa sondy na masie blisko procesora) dokładnie oba zbocza. Być może ten wiatraczek robi coś głupiego, może zbocza są zbyt powolne lub z odbiciem w okolicach 0-1V a procesor widzi to jak dodatkowy impuls. Sprawdż, czy masy obu rzeczy (wiatraczki i ich zasilania i procesora i jego zasilania) są dobrze połączone. Jeśli wiatraczki wypuszczają sygnał typu Open Collector, musisz mieć włączone pullupy - nie wiedzieliśmy schematu. Jeśli nie masz dostępu do sprzętu, zrób sobie poprawny sygnał: weź 555, policz i dospawaj kilka oporników + kondesnator i testuj swój licznik na takim czystym przebiegu z generatora astabilnego. Tam przynajmniej częstotliwość będzie stała. Możesz też użyć jakiejś płyteczki Arduino lub nawet timera z tego procesora który mierzy impulsy 🙂 Zaprogramuj gdzieś na początku np. timer 2 na generację przebiegu o wypełnieniu np. 50% i żądanej częstotliwości tak, by program o tym nic nie wiedział. To wytnie wszystkie niepewności związane z samym wiatraczkiem i będziesz mógł się skupić na pomiarach.

2. Zrób kod tak, by wyłączanie przerwań było jak najkrótsze, np. tak:

if (millis() - previous_millis == 1000)
   { 
   previous_millis = millis();
   cli();
   stan_licznjika = rpm_counter_1;
   rpm_counter_1 = 0;
   sei();
   // a tu są obliczenia itd..
   }
}

Teraz nie blokujesz ciągłości zliczania czasu i zliczania impulsów (i pracy portu szeregowego!) na dłużej niż kilka us. Ważne, by szybko pobrać stan głównego licznika do jakiejś zmiennej pomocniczej i wyzerować licznik. Reszta (obliczenia i wypisywanie wyników) może przecież odbywać się z włączonymi przerwaniami.

3. Jeśli masz oscyloskop (bez tego to dzisiaj trudno robić nietrywialne rzeczy), ustawiaj jakiś pin w stan 1 w czasie wykonywania funkcji ISR tj. zapalaj go zaraz na jej początku i gaś na jej końcu. Jeżeli na jeden kanał podasz impulsy z wiatraczka a na drugi ten pin, zobaczysz ile razy i na których naprawdę zboczach procesor wykonuje tę funkcję. Bez sprzętu to raczej zgadywanie, domyślanie się i strata masy czasu na w sumie prościutkiej rzeczy.

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Dziękuje bardzo za wyczerpującą odpowiedź. Nie mam niestety oscyloskopu ale mam do niego dostęp.

Zrobiłem dziś test tak wygląda przebieg z tacho wentylatora:

Nie dziwie się dlaczego pojawiała się różnica pomiędzy zboczem narastającym i opadającym...

Zastosowałem w celu przetestowania kondensator 100nF (wpięty pomiędzy masą a tacho) i przebieg wygląda tak:

Taka sama zależność była dla różnych wentylatorów i różnych napięć. Po przetestowaniu tego rozwiązania z Arduino wszystko jest ok jeżeli chodzi o liczbę zboczy narastających i opadających.

Zmodyfikuje program według Twoich propozycji myśle że może pomóc ponieważ znacząco skórce czas gdzie przerwania są wyłączone.

Udostępnij ten post


Link to post
Share on other sites

Zmodyfikowałem troche kod, teraz mierze czas pomiędzy poszczególnymi sygnałami (wyniki powinny być dokładniejsze)

Prosiłbym o wskazówki co można wyżucić z przerwań by przyspieszyć ich wykonywanie?

//read RPM PCINT by Asica10

//-----Define variable for measure RPM-----

volatile unsigned long timeLast_1 = 0; 
volatile unsigned long timeLast_2 = 0;
volatile unsigned long timeLast_3 = 0;

volatile unsigned long deltaT_1 = 0; 
volatile unsigned long deltaT_2 = 0;
volatile unsigned long deltaT_3 = 0;

volatile unsigned long sum_of_time_1 = 0; 
volatile unsigned long sum_of_time_2 = 0;
volatile unsigned long sum_of_time_3 = 0;

volatile int counter_1 = 0; // Variable which measurement how many interrupts have occurred
volatile int counter_2 = 0;
volatile int counter_3 = 0;

volatile uint8_t interrupt_pin = 0xFF; // Variable used to check where an interrupt occurs

//Fans definition
const byte sensor = 2; // Quantity of fan sensors (tacho line)
const unsigned long RPMconversion = 60000000/sensor; //Used in converting pulses form sensors to RPM

int rpm_1 = 0;
int rpm_2 = 0;
int rpm_3 = 0;

unsigned long previous_millis = 0;


void setup() {

 PCICR |= (1<<PCIE2); // define interrupt on PCIE2 - PCINT[23:16]
 PCMSK2 |= (1<<PCINT18) | (1<<PCINT19) | (1<<PCINT20); // Permission to interrupt

 Serial.begin(9600);

 sei(); // Global Interrupt Enable
}

void loop() {

   if ((millis() - previous_millis == 1000)) //Update every one second
   {  
     previous_millis = millis();

  cli(); // Global Interrupt Disable  - when calculeting

  rpm_1 = RPMconversion / (sum_of_time_1 / counter_1); //calculating RPM for fan
  rpm_2 = RPMconversion / (sum_of_time_2 / counter_2);
  rpm_3 = RPMconversion / (sum_of_time_3 / counter_3);

  counter_1 = 0;
  counter_2 = 0;
  counter_3 = 0;

  sum_of_time_1 = 0;
  sum_of_time_2 = 0;
  sum_of_time_3 = 0;

      sei();

     Serial.print("RPM =\t"); //print the word "RPM" and tab.
     Serial.println(rpm_1); // print the rpm value.
     Serial.print("RPM =\t"); //print the word "RPM" and tab.
     Serial.println(rpm_2); // print the rpm value.
     Serial.print("RPM =\t"); //print the word "RPM" and tab.
     Serial.println(rpm_3); // print the rpm value.

   }
}


ISR(PCINT2_vect) {  // interrupt vector

 uint8_t changedbits = PIND ^ interrupt_pin;
 interrupt_pin = PIND;

 if((PIND & (1<<PD2))) //detect only rising edges
 {
   unsigned long timeNow = micros(); // Get current time

   if((changedbits & (1 << PD2))) //check where was interrupt
   {
     deltaT_1 = timeNow - timeLast_1; // calculate change in time since last crossing event
     timeLast_1 = timeNow; // store current time for next crossing event
     sum_of_time_1 = sum_of_time_1 + deltaT_1;   
     counter_1++;
   }

   if((changedbits & (1 << PD3)))
   {
     deltaT_2 = timeNow - timeLast_2; 
     timeLast_2 = timeNow;
     sum_of_time_2 = sum_of_time_2 + deltaT_2;   
     counter_2++;
   }

   if((changedbits & (1 << PD4)))
   {
     deltaT_3 = timeNow - timeLast_3; 
     timeLast_3 = timeNow;
     sum_of_time_3 = sum_of_time_3 + deltaT_3;   
     counter_3++;
   }

 }
}

Udostępnij ten post


Link to post
Share on other sites

No proszę, jak schludnie napisany kod.

Trochę się poczepiam, ale ogólnie podobasię 🙂

1. ---------------

Po jednej parze nawiasów przy if-ach za dużo.

Te wokół operatora przesuwania też można wywalić (operator << ma wyższy priorytet niż &), nie będę się upierać - kwestia czytelności.

       if (changedbits & 1 << PD2) //też jest ok

2. ---------------

Zmienne statyczne: previous_millis, interrupt_pin

Można te zmienne wynieść z globali i zadeklarować wewnątrz funkcji, w której wyłącznie są używane.

Na przykład tak:

   static auto previous_millis = millis();
   if (millis() - previous_millis == 1000)
   {
       previous_millis = millis();

Pierwszy operator '=' nie jest operatorem podstawienia, a inicjalizacją zmiennej i jest wykonywany wbrew pozorom dokładnie jeden raz.

Drugi operator '=' jest operatorem podstawienia, i jest wykonywany za każdym razem w pętli, w if-ie.

Jeszcze ciekawiej wygląda to w procedurze obsługi przerwań:

   static uint8_t interrupt_pin = 0xFF;
   uint8_t changedbits = PIND ^ interrupt_pin;
   interrupt_pin = PIND;

Pierwsze interrupt_pin = 0xFF; jest inicjalizacją (tylko raz), kolejne: interrupt_pin = PIND; za każdym razem.

Jak nie wierzysz - tu jest kawałek kodu który to ilustruje:

void loop() {
   static unsigned long previous_millis = 0;
   if (millis() - previous_millis >= 1000) //Update every one second
   {
       previous_millis = millis();
       Serial.println (previous_millis);
   }
}

A po co tak? Niech kompilator ma szansę użyć mniej pamięci - zarówno kodu, jak i programu.

3. ---------------

Deklaracje zmiennych rpm_N - używane wyłącznie w loop() - warto do tego loopa przenieść, a nie trzymać w globalach.

4. ---------------

To co w ISR można uprościć.

Kompilator powinien to sobie sam zoptymalizować (algorytmy optymalizacji w GCC są naprawdę potężne), ale nie chce mi się zaglądać do binariów (przepraszam). Może mu przeszkadzać w optymalizacji podstawienie do globalnej, nigdzie nie używanej zmiennej deltaT_n. Jeśli już, to niech to będzie zmienna lokalna, ale po zmianie kolejności można rzecz ciut uprościć.

   totalTime += timeNow - timeLast;
   timeLast = timeNow;
   ++counter;

BTW: operator ++ prefiksowy (czyli ++counter) jest lepiej widziany, niż postfiksowy (counter++). Tu akurat nie ma to absolutnie żadnego znaczenia, ale odruch warto sobie wyrabiać.

W trzech ifach jest w zasadzie taki sam kod, aż się prosi o procedurę.

void calcTime (unsigned long timeNow, volatile unsigned long& timeLast, volatile unsigned long& totalTime, volatile int& counter) {
   totalTime += timeNow - timeLast;
   timeLast = timeNow;
   ++counter;
}
...
       if (changedbits & (1 << PD2)) //check where was interrupt
       {
           calcTime (timeNow, timeLast_1, totalTime_1, counter_1);
       }
... // and so on

Jeśli jest obawa, że wołanie procedury, całe to odkładanie parametrów na stosie będzie kosztowne, to można użyć słowa kluczowego inline. A jeśli nadal jest obawa, że kompialtor nie użyje inline (wszak inline jest tylko niezobowiązującą sugestią), to można wymusić: __attribute__ ((always_inline))

I wtedy nagłówek funkcji będzie wyglądać tak:

inline void __attribute__ ((always_inline)) calcTime (unsigned long timeNow, volatile unsigned long& timeLast, volatile unsigned long& totalTime, volatile int& counter) { ...

To powyżej jest substytutem starożytnych makr.

5. !!!!!!!!!!!!!!!!!

W tym fragmencie:

if (millis() - previous_millis == 1000) //Update every one second

Jak choć raz się program zagapi, i loop() odpali się dla millis() == 999, a następny dla 1001 - to już nigdy nie będzie żadnych kalkulacji (to znaczy kiedyś będą - jak się licznik millis() przekręci w kółko)

Zamiast '==' należy bezwzględnie użyć '>='

6. ---------------

No i program po lekkim refactoringu:

// read RPM PCINT by Asica10
//-----Define variable for measure RPM-----

volatile unsigned long timeLast_1 = 0;
volatile unsigned long timeLast_2 = 0;
volatile unsigned long timeLast_3 = 0;

volatile unsigned long totalTime_1 = 0;
volatile unsigned long totalTime_2 = 0;
volatile unsigned long totalTime_3 = 0;

volatile unsigned int counter_1 = 0; // Variable which measurement how many interrupts have occurred
volatile unsigned int counter_2 = 0;
volatile unsigned int counter_3 = 0;
                                                      //Fans definition
const byte sensor = 2;                                 // Quantity of fan sensors (tacho line)
const unsigned long RPMconversion = 60000000 / sensor; //Used in converting pulses form sensors to RPM

void setup() {
   PCICR |= (1 << PCIE2); // define interrupt on PCIE2 - PCINT[23:16]
   PCMSK2 |= (1 << PCINT18) | (1 << PCINT19) | (1 << PCINT20); // Permission to interrupt

   Serial.begin (9600);

   sei(); // Global Interrupt Enable
}

void loop() {
   static auto previous_millis = millis();

   if (millis() - previous_millis >= 1000) //Update at least every one second
   {
       previous_millis = millis();

       cli(); // Global Interrupt Disable  - calculations

       unsigned int rpm_1 = RPMconversion / (totalTime_1 / counter_1); //calculating RPM for fan
       unsigned int rpm_2 = RPMconversion / (totalTime_2 / counter_2);
       unsigned int rpm_3 = RPMconversion / (totalTime_3 / counter_3);

       counter_1 = 0;
       counter_2 = 0;
       counter_3 = 0;

       totalTime_1 = 0;
       totalTime_2 = 0;
       totalTime_3 = 0;

       sei();

       Serial.print ("RPM =\t");
       Serial.println (rpm_1);
       Serial.print ("RPM =\t");
       Serial.println (rpm_2);
       Serial.print ("RPM =\t");
       Serial.println (rpm_3);
   }
}

inline void __attribute__ ((always_inline)) calcTime (const unsigned long timeNow, volatile unsigned long& timeLast, volatile unsigned long& totalTime, volatile unsigned int& counter) {
   totalTime += timeNow - timeLast;
   timeLast = timeNow;
   ++counter;
}

ISR (PCINT2_vect) {  // interrupt vector
   static uint8_t interrupt_pin = 0xFF;
   uint8_t changedbits = PIND ^ interrupt_pin;
   interrupt_pin = PIND;

   if (PIND & (1 << PD2)) //detect only rising edges
   {
       auto timeNow = micros();

       if (changedbits & (1 << PD2)) //check where was interrupt
       {
           calcTime (timeNow, timeLast_1, totalTime_1, counter_1);
       }

       if (changedbits & (1 << PD3)) {
           calcTime (timeNow, timeLast_2, totalTime_2, counter_2);
       }

       if (changedbits & (1 << PD4)) {
           calcTime (timeNow, timeLast_3, totalTime_3, counter_3);
       }
   }
}

7. !!!!!!!!!!!!!!!!!

Błędy?

Procedura monitorująca loop() brutalnie modyfikuje obiekt monitorowany. Wiatrak sobie skrupulatnie odlicza czas, zlicza przerwania, a Ty mu w środku liczenia czasu, absolutnie asynchronicznie, bo 1000 milisekund nijak nie jest zsynchronizowana z przerwaniami robisz bum - wszystko zerujemy, licz od nowa. Obciąża to pomiary błędem.

Może wolisz to zrobić w obsłudze przerwania - if (counter>JakaśStała) { totalTime = 0; counter = 0; } ? Nie jest to jakieś wyrafinowane podejście, ale całkiem nieźle będzie uśredniać wyniki.

Rozwiązań jest dużo - choćby bufor kołowy o długości JakaśStała - w przerwaniach prawie nie trzeba nic liczyć, tylko w kółko wpisywać do bufora kolejne odczyty millis() (obsługiwać licznik - inkrementować, jak większy niż, to zerować, i inkrementować wskaźnik). Procedura monitorująca też będzie krótka - od bieżącego millis() odejmuje to co ma na najstarszej pozycji w buforze, dzieli przez JakaśStała - i jest wynik.

8. ---------------

Jak z loop wyrzucisz kasowanie stanu, to czas, kiedy przerwania są zablokowane będzie ciut mniejszy.

Możesz też dzielenie wynieśc poza cli()/sei().

Jakoś tak:

#include <util/atomic.h>
...
unsigned int calcRpm(volatile unsigned long& totalTime, volatile unsigned int& counter) {
   unsigned long totalTime_;
   unsigned int counter_;
   ATOMIC_BLOCK (ATOMIC_FORCEON) {
       totalTime_ = totalTime;
       counter_ = counter;
   }
   return RPMconversion / (totalTime_ / counter_);
}
... // cli() - wyrzucić
int rpm_1 = calcRpm (totalTime_1, counter_1);
... // sei() - wyrzucić

Best Regards

  • Lubię! 1

Udostępnij ten post


Link to post
Share on other sites

Dziękuje bardzo za wyczrupującą i niesamowicie przydatną wypowiedź.

No proszę, jak schludnie napisany kod.

Trochę się poczepiam, ale ogólnie podobasię

Dziękuje, miło to słyszeć. Dopiero wszystkiego powoli się uczę (ok 1 miesiąc), ale wiem że jeszcze długa droga przede mną.

Po jednej parze nawiasów przy if-ach za dużo.

A racja przeoczyłem to, zostało to po testowaniu jak zachowuje się program gdy wykrywam zbocze opadające if(!(PIND & (1<

Zmienne statyczne: previous_millis, interrupt_pin

Można te zmienne wynieść z globali i zadeklarować wewnątrz funkcji, w której wyłącznie są używane.

Muszę się przyznać, że nie wiedziałem o istnieniu takich zmiennych... Muszę sobie dokładnie o nich poczytać, bo patrząc pierwsze na kod myśle sobie że coś jest nie tak a dopiero potem doczytałem że:

Pierwszy operator '=' nie jest operatorem podstawienia, a inicjalizacją zmiennej i jest wykonywany wbrew pozorom dokładnie jeden raz.
W trzech ifach jest w zasadzie taki sam kod, aż się prosi o procedurę.

Zastanawiałem się na stworzeniem dodatkowej funkcji by to wykonywała, ale dlaczego tego nie zrobiłem sam nie wiem... W szczególności że docelowo prędkość będzie mierzona dla 5 wentylatorów, więc głupie było by powtarzać to samo 5 razy.

Procedura monitorująca loop() brutalnie modyfikuje obiekt monitorowany. Wiatrak sobie skrupulatnie odlicza czas, zlicza przerwania, a Ty mu w środku liczenia czasu, absolutnie asynchronicznie, bo 1000 milisekund nijak nie jest zsynchronizowana z przerwaniami robisz bum - wszystko zerujemy, licz od nowa. Obciąża to pomiary błędem.

Może wolisz to zrobić w obsłudze przerwania - if (counter>JakaśStała) { totalTime = 0; counter = 0; } ? Nie jest to jakieś wyrafinowane podejście, ale całkiem nieźle będzie uśredniać wyniki.

Wydaje się to lepszym rozwiązaniem (napewno dokładniejsze będą wyniki), ale jak odliczam 1s to wyniki pojawiają się co 1s, jak zacznę zliczać do pewnej ilości countera to dla każdego wentylatora w innym momencie osiągnięta zostanie wartość zadana.

Nie za bardzo rozumiem jak to zrobić w funkcji przerwana i potem obliczać to w loop().

Ja bym to zrobił tak że w głównym loop() sprawdzam warunki (tyle ile jest wentylatorów) czy counter osiągnął zadaną wartość jeżeli tak wchodzę w if wykonuje dzielenie i wyświetlam wynik.

  if (counter_1 >= 100)
 {
   cli(); // Global Interrupt Disable  - calculations 
   unsigned int rpm_1 = RPMconversion / (totalTime_1 / counter_1);
   counter_1 = 0;
   totalTime_1 = 0; 
   sei();
   Serial.print ("RPM =\t"); 
   Serial.println (rpm_1);     
 }

I tak dla każdego wentylatora...

Rozwiązań jest dużo - choćby bufor kołowy

Na tą chwile nie umiem tworzyć takich rzeczy, ale dopisze to sobie do listy czego muszę się w niedługim czasie nauczyć 🙂

No i program po lekkim refactoringu:

Jeszcze raz dziękuje bardzo za pomoc.

Pozdrawiam

Udostępnij ten post


Link to post
Share on other sites

A co Ty będziesz z tymi wynikami robić? Stabilizować obroty?

To może od razu poszukaj coś o regulatorach?

Udostępnij ten post


Link to post
Share on other sites

Buduje sytem zarządzania chłodzeniem wodnym w PC ( i to jest jedn z kilku części). Wyniki te będą wyświetlane tyljo na LCD 4x20 nic więcej (przynajmniej na chwile obecną, poza pojawieniem się alarmu jak obroty pompy będą za niskie )

Prosze powiedz jescze czy taki zapis jak zaproponowałem w loop() będzie ok okiem fachowca.

Udostępnij ten post


Link to post
Share on other sites

Wygląda nieźle, ale... niech się inni wypowiedzą, ja tu fachowcem nie jestem, w życiu nie skonstruowałem nic choćby tak zaawansowanego jak Twoje 🙂 Naprawdę 🙂

Udostępnij ten post


Link to post
Share on other sites

Jednak muszę wrócić do sposobu wyświetlania co jakiś czas. Jak zliczam impulsy pojawia się taki problem że jak wentylator się zatrzyma jego obroty się nie zaktualizują i zatrzymają się na jakiejś wartości. Przez co np nie zadziałają zabezpieczenia... wiem że można było by to rozwiązać jakimiś dodatkowymi warunkami, ale

Wiem że obarcza to wyniki błędem, ale ten błąd jest na tyle mały, że jest akceptowalny.

Udostępnij ten post


Link to post
Share on other sites

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Gość
Napisz odpowiedź...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.


×
×
  • Utwórz nowe...