• 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
"Miarka" na enkoderze inkrementalnym z dodatykowymi funkcjami.
#1
Question 
Witam.


Tworzę hobbistycznie miarkę elektroniczną na enkoderze inkrementalnym z obsługą silnika oraz sterowania elektrozaworem powietrznym z wynikiem który wyświetla się w centymetrach na ekranie.


Mam bardzo dużo ogrodzenia w które chciałbym wpleść taśmy ochronne. Na jedno przęsło przychodzi 6 pasków po 255cm. Z racji tego że cięcie tego i mierzenie ręczne zajmie bardzo dużo czasu postanowiłem stworzyć sobie pomoc.
Wydukałem po ciężkich próbach kod, który działa lecz nie tak jak bym sobie tego życzył.


Moim pomysłem jest po prostu enkoder na którym osadzona jest rolka(kółko miernicze) która kręci się wraz z pociąganiem taśmy. Po osiągnięciu 255cm siłowniki podłączone do elektrozaworu opadną i przytrzymają materiał żebym mógł go równo uciąć i do tej pory wszystko działało jak należy.
Lecz wpadłem na pomysł żeby "maszyna" nawijała sobie bezpośrednio na silnik pasek 255cm siłownik opada, jest cięcie i tak sześć razy.


Po naciśnięciu przycisku "licznik" czeka aż wartość osiągnie 255cm po czym wyłączy przekaźnik sterujący pracą silnika, po zakończonej pracy naciskamy przycisk i cykl zaczyna się od nowa.
Początkowo gdy nie myślałem o zwijaniu tego na silnik to wystarczyło przeciągnąć po stole taśmę, naliczyło 255cm i siłownik opada. Po naciśnięciu przycisku wskazanie licznika się zeruje wraz z tym przekaźnik sterujący cewką elektrozaworu powietrznego rozwiera styki.


Poniżej wkleję funkcję którą wyklepałem, lecz nie spełnia to moich oczekiwań. po zaimplementowaniu tego w kod enkoder zachowuje się jak manetka od motocykla. Czyli kręcąc "do przodu" przekaźnik załącza się z coraz większą szybkością aż do osiągnięcia 255. Dodam, że przy czym kręcąc "do przodu" zwiększa mi się szybkość odświeżania pomiaru a po naciśnięciu i przytrzymaniu przycisku "praca" licznik działa normalnie bez tej funkcji, lecz gdy distance=0 jest straszny lag.
Oczywiście podczas trzymania przycisku gdy zjadę równo na 0.00cm i puszczę przycisk przekaźnik i wynik na ekranie pracuje z częstotliwością ~3s i narasta wraz z wynikiem (nie jestem w stanie tego zrozumieć).
Oto kawałek kodu o który mi chodzi:



Kod:
 
 if (digitalRead(praca) == LOW) {  // jeśli przycisk jest naciśnięty
    distance = 0;                      // zerowanie zmiennej distance
    digitalWrite(silnik, HIGH);      // włączenie przekaźnika
    while (distance < 255) {           // pętla działająca dopóki distance < 255
      distance++;                      // inkrementacja zmiennej distance
      delay(10);                       // opóźnienie
    }
    digitalWrite(silnik, LOW);       // wyłączenie przekaźnika
    delay(100);                       // opóźnienie przed kolejnym naciśnięciem przycisku
  }
Dla jasności całego mojego przekazu wkładam całość kodu:
Kod:
#include <LiquidCrystal_I2C.h> // Biblioteka do magistrali i2c
LiquidCrystal_I2C lcd(0x27, 16, 2);  //inicjalizacja LCD
 
#define outputA 2  //ZEGAR pin

#define outputB 3  //DATA pin

#define rstbtn 8  //RESET WYNIKU

#define silowniki 7    //SIŁOWNIKI pin

#define silnik 9  //SILNIK pin

#define praca 10 // PRACA maszyny

int wartoscprogowa = 255;
int counter = 0;
const float pi = 3.14; // Wartość PI  
const float R = 3.245;  //wymiar kółka od srodka do krawędzi
const int N = 600; //ilość impulsów na jeden obrót enkodera
float distance = 0;
 
void setup() {
  pinMode(silowniki, OUTPUT);
  pinMode(outputA, INPUT_PULLUP);
  pinMode(outputB, INPUT_PULLUP);
  pinMode(rstbtn, INPUT_PULLUP);
  pinMode(silnik, OUTPUT);
  pinMode(praca, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(outputA), readEncoder, FALLING);
  digitalWrite(silnik, LOW);
  digitalWrite(silowniki, LOW);
}

void readEncoder() {      
  int bValue = digitalRead(outputB);
  if (bValue == HIGH) {
    counter++; // enkoder w przod
  }
  if (bValue == LOW) {
    counter--; // enkoder w tyl
  }
}
 
int getCounter() {  
  int result;
  noInterrupts();
  result = counter;
  interrupts();
  return result;
  }
 
void resetCounter() {
  noInterrupts();
  counter = 0;
  interrupts();
}



void loop() {  //wzór i obliczenie impulsy>cm wyplucie wartosci na LCD

 
  distance = ((2*pi*R)/N) * getCounter();
  lcd.setCursor(0, 0);
  lcd.print("RozpocznijPomiar");
  lcd.setCursor(0, 1);
  lcd.print(distance);
  lcd.print("         ");
  lcd.setCursor(5,1);
  lcd.print("CM");

 
 
 
 if (digitalRead(praca) == LOW) {  // jeśli przycisk jest naciśnięty
    distance = 0;                      // zerowanie zmiennej distance
    digitalWrite(silnik, HIGH);      // włączenie przekaźnika
    while (distance < 255) {           // pętla działająca dopóki distance < 255
      distance++;                      // inkrementacja zmiennej distance
      delay(10);                       // opóźnienie
    }
    digitalWrite(silnik, LOW);       // wyłączenie przekaźnika
    delay(100);                       // opóźnienie przed kolejnym naciśnięciem przycisku
  }
  if (distance >= wartoscprogowa)  // Oblsuga siłowników
  {
    digitalWrite(silowniki, LOW);
    lcd.setCursor(8,1);
    lcd.print("Trzymam");
  }
  else
  {
    digitalWrite(silowniki, HIGH);
  }
 
    if (digitalRead(rstbtn) == LOW) {
    resetCounter();
  }
 
 delay(10);
  }



Pozdrawiam serdecznie wszystkich.
 
Odpowiedź
#2
To ci w ogóle działa?? wszystko jest nie tak jak powinno być.
Arduino zostało wymyślone po to, by robić dobrze jedną prostą rzecz – migać diodą. 
 
Odpowiedź
#3
(09-05-2023, 20:15)Jarewa0606 napisał(a): To ci w ogóle działa??  wszystko jest nie tak jak powinno być.
Bez obsługi silnika tak. Enkoder mierzy, uC przelicza i wypluwa na ekranie wszystko. Przekaźnik steruje elektrozaworem na zadane distance=255.
Może podpowiesz coś z tym co opisałem w poście wyżej ?
Pozdrawiam!
 
Odpowiedź
#4
Zmienna ma być volatile, a nie wyłączasz przerwania.
Jak włączasz silnik elektryczny to on się rozpędza przez jakiś czas, jak odcinek jest krótki to pewnie przez cały czas. Może trzeba zamienić na silnik krokowy, ale ten też ma okres rozpędzania, utrzymywania prędkości i hamowania, choć tu można wyliczyć ile kroków ma do zrobienia nawet bez encodera i przewidzieć cały ruch, a co najwyżej przy hamowaniu zrobić kilka więcej/mniej kroków aż się osiągnie zadany odcinek ruchu.
Oczywiście lepiej z encoderem, bo czasami silnik krokowy też zgubi kroki.
Nie wiem jak to ma być powtarzalne ze zwykłym silnikiem AC czy DC.
A tak ogólnie to raczej sobie dorobiłeś roboty niż jej ubyło. Wg mnie prościej byłoby zrobić jakiś zderzak, wyciągasz taśmę, dobijasz do zderzaka i następuje ucięcie. Jak taśma przewodzi prąd to może zamykać obwód, albo w zderzaku naciska na krańcówkę i uruchamia ten proces.
Kolejna sprawa to delay i WDT. W czymś co ma taką energię i jeszcze coś ucina używanie delay nie jest dobrym pomysłem, ewentualnie decyzja o zatrzymaniu powinna być od razu podjęta w przerwaniu od PCI, a nie czekać na wyliczenie w loop, gdzie będzie opóźniona o delay, czas komunikacji po I2C (która jest wolna i działa jak delay).
WDT powinien cały czas sprawdzać, czy program gdzieś nie utknął, np. bo odłączył się kabelek od I2C i program tu stoi.
Jak na tym etapie umiejętności programowania będziesz pisał programy do maszyn z nożami to będziesz się witał łokciem jak w covidzie Big Grin.
 
Odpowiedź
#5
Najprościej mówiąc położyłeś program przy pomocy delay().

Ta funkcja działa w taki sposób, że zatrzymuje działanie mikrokontrolera (pętla loop przestaje być wykonywana) przez odpowiednią ilość czasu, wskazaną w nawiasach. Przerwania w tym czasie działają, więc oberwujesz to w postaci zwiększania się licznika skokowo zamiast płynnie.

Poczytaj o zastosowaniu millis() - dużo jest tutoriali na ten temat, osobiście polecam kurs Forbot. Jest też w Arduino IDE przykład BlinkWithoutDelay.

Przeanalizujmy działanie twojego programu:
Kod:
distance = ((2*pi*R)/N) * getCounter();
tutaj zmiennej distance przypisujesz wartość na podstawie zmiennej zapisanej w counter, następne wiersze wypluwają przeliczoną wartość na ekran i wszystko działa cacy. Wg. mnie nie powinieneś tego zapisywać w ten sposób chociażby dlatego, że:
Kod:
(2*pi*R)/N
jest wartością stałą, można ją policzyć raz np. w setup() i operować pojedynczą stałą, a nie kazać programowi przy każdym przejściu loop() wyliczać jej wartość na nowo.

Problemy pojawiają się tutaj:
Kod:
if (digitalRead(praca) == LOW) {  // jeśli przycisk jest naciśnięty
    distance = 0;                      // zerowanie zmiennej distance
    digitalWrite(silnik, HIGH);      // włączenie przekaźnika
Na początku warunek if - nie powinieneś obsługiwać przycisku w taki sposób. Podczas wciskania styk drga i conajmniej kilkakrotnie zmienia stan z HIGH na LOW i odwrotnie. Nazywa się to drganiem styków i wykorzystanie biblioteki Button pomaga w prosty sposób poradzić sobie z tym problemem (można to zrobić w inny sposób, ale po co na nowo wymyślać coś, co już jest wymyślone).

Jeżeli zapomnimy o nie do końca dobrym warunku w pętli to jak już faktycznie pin od przycisku będzie miał stan LOW i pętla if zacznie się wykonywać to na start zmienna distance zostaje wyzerowana - czyli to, co dotychczas wyliczone zostało na podstawie ruchów enkodera dostaje wartość 0 - tutaj pojawiają się zakłamania wartości wyświetlanej na ekranie.

Po tym zabiegu silnik startuje i rozpoczyna się pętla while:
Kod:
    while (distance < 255) {           // pętla działająca dopóki distance < 255
      distance++;                      // inkrementacja zmiennej distance
      delay(10);                       // opóźnienie
    }
Program mieli pętlę 255 razy, przy każdym jej przejściu czekając w międzyczasie 10ms, a potem po wyłączeniu silnika czeka jeszcze 100ms. 255x10ms + 100ms = ~3s, o których napisałeś. Pętla ta nie ma sensu - po co zwiększać zmienną distance, skoro jej wartość jest ustalana w programie na podstawie ruchów enkodera? Po co pętla while, w której zwiększasz zmienną distance? Dla wyliczenia odległości?

Potem dwie raczej nieszkodliwe funkcje i delay(10) na dokładkę na końcu pętli loop().

W tym czasie przerwania działają sobie niczym się nie przejmując, zmieniając wartość zmiennej counter.

Co w tym wypadku powinieneś zrobić?
1. Wykorzystać bibliotekę button w swoim projekcie
2. Wyeliminować delay()
3. Przemyśleć w jaki sposób postępować ze zmienną distance

W razie gdybyś nie wiedział jak to dalej ugryźć pisz - chętnie pomożemy.
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości