Skocz do zawartości

Miernik długości przewodów


Pomocna odpowiedź

Spoko - my tu z kolegą @atMegaTona gaworzymy o jakichś duperelach, ale tym się nie przejmuj. I on, i ja chcemy żeby to wyszło - tyle że on chce szybko a ja na spokojnie 🙂

Co do mechaniki będę miał pytanie, ale poczekajmy na działający program.

Na razie czekam na lepszy kod.

Link do komentarza
Share on other sites

(edytowany)

To coś tam poprawiłem wg Twoich wskazówek ale nie za bardzo wiem czy poprawiłem to co sugerowałeś jeżeli chodzi o zliczanie.

Wstępne testy pokazały  że jest lepiej ale nie idealnie więc chęć do pracy jest 🙂

@ethanak 


#include <Wire.h>              // standardowa biblioteka Arduino
#include <LiquidCrystal_I2C.h> // dolaczenie pobranej biblioteki I2C dla LCD

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Ustawienie adresu ukladu na 0x27

#define czujnik 2
#define przyciskReset 4
int licznik = 0;
int czy_otwarte = 0;
int ostatni_stan = 0;
int poprzedni_licznik;

void setup()
{
  pinMode(4, INPUT_PULLUP);
  pinMode(2, INPUT);

  Serial.begin(9600);
  lcd.begin(16, 2);
}

void loop()

{

  {

    {
      // zlicza ile razy czujnik wykrył magnes
      if (ostatni_stan == 0 && (digitalRead(czujnik) == 1))
      {
        licznik++;
        ostatni_stan = 1;
        czy_otwarte = 1;
      }
      else if (ostatni_stan == 1 && (digitalRead(czujnik) == 0))
      {
        ostatni_stan = 0;
        czy_otwarte = 0;
      }
    }

    {
      //naciśnięcie przycisku resetuje licznik
      if (digitalRead(przyciskReset) == LOW)
      {
        licznik = 0;
      }
    }
    //wyświetla na LCD ile razy czujnik wykrył magnes
    if (poprzedni_licznik != licznik)
    {
      poprzedni_licznik = licznik;
      Serial.println(licznik);
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print(licznik);
    }
  }
}

 

Edytowano przez marboz
Link do komentarza
Share on other sites

Nic więcej z tym się zrobić nie da bez przerwań, najlepszym wyjściem będzie użyć timera do zliczania zboczy i aktualizować lcd co sekundę. Nic lepszego się nie da wymyślić. Ale być może @ethanak ma jakiś własny plan edukacyjny więc nie będę się tu wciskał między wódkę a zakąskę 😉

Link do komentarza
Share on other sites

14 godzin temu, marboz napisał:

nie za bardzo wiem czy poprawiłem to co sugerowałeś

Nie poprawiłeś - zamiast jednego masz wciąż dwa odczyty z pinu, a zamiast użyć zmiennej pozbyłeś się jej w innym miejscu.

No - ale załóżmy, że to możesz poprawić. Tyle, że - jak sam zauważyłeś - nie działa to jeszcze tak jak powinno. Po prostu - sterowanie wyświetlaczem nie odbywa się za pomocą magii w zerowym czasie, ale wymaga jakiejś tam operacji na pinach, która to operacja zabiera czas. I dlatego każde wypisanie czegoś na wyświetlaczu spowoduje utratę impulsu czy kilku.

Można to ominąć, rezygnując ze zmian wyświetlania kiedy rolka jest w ruchu - ale to też niezbyt wygodne...

No - a gdyby udało się jakimś sposobem zmusić Arduino, aby robiło dwie rzeczy na raz?

Piny 2 i 3 mają taką ciekawą możliwość, że zmiana ich stanu może spowodować natychmiastowe wykonanie jakiegoś fragmentu programu, przerywając program główny. I takie właśnie przerwanie możemy wykorzystać.

 

volatile int licznik;

void licz(void)
{
  licznik++;
}

void setup() {
	/* tu różne pinMode, inicjalizacja lcd
     * i co w ogóle potrzebne */
  attachInterrupt(digitalPinToInterrupt(2), licz, RISING);
}

    

volatile oznacza, że zmienna może w każdej chwili zmienić swój stan niezależnie od tego, co robi program i nie można oszczędzać na pojedynczym jej odczycie z pamięci; attachInterrupt podłącza funkcję licz() do przerwania na pinie 2 w chwili, kiedy sygnał zmieni się z niskiego na wysoki, a funkcja digitalPinToInterrupt tłumaczy numer pinu na numer przerwania (które w różnych typach Arduino są różne).

Teoretycznie można by było teraz użyć owej zmiennej w analogicznym jak Twój programie:

int poprzedni_licznik;

void loop()
{
  if (digitalRead(przyciskReset) == LOW)
  {
    licznik = 0;
  }
  //wyświetla na LCD ile razy czujnik wykrył magnes
  if (poprzedni_licznik != licznik)
  {
    poprzedni_licznik = licznik;
    Serial.println(licznik);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(licznik);
  }
}

Ale życie nie jest takie proste; cztery razy odczytujemy zmienną licznik, i jej wartość może zmienić się między odczytami; do tego dochodzi fakt, że zmenne typu int zajmują dwie komórki pamięci, a zmiana wartości może nastąpić równie dobrze pomiędzy odczytem jednej i drugiej.

Na szczęście wykonanie przerwań można w Arduino wyłączyć na czas odczytu, i poprzez wprowadzenie zmiennej roboczej wyeliminować konieczność wielokrotnych odczytów:

int poprzedni_licznik;

void loop()
{
  int roboczy licznik;
  
  if (digitalRead(przyciskReset) == LOW)
  {
    cli(); // wyłączamy przerwania
    licznik = 0;
    sei(); // włączamy przerwania z powrotem
  }
  
  cli();
  roboczy_licznik = licznik;
  sei();
  
  //wyświetla na LCD ile razy czujnik wykrył magnes
  if (poprzedni_licznik != roboczy_licznik)
  {
    poprzedni_licznik = roboczy_licznik;
    Serial.println(roboczy_licznik);
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(roboczy_licznik);
  }
}

Co jednak, jeśli magnes wlezie pod hallotron akurat gdy przerwania będą zablokowane? Czy program nie zgubi impulsu?

Otóż nie. Arduino zapamiętuje w takiej chwili, że przerwanie powinno być wykonane i wykonuje je jak najszybciej po wykonaniu sei(). W ten sposób o ile nie będziemy ciągnąć przewodu z naddźwiękową prędkością żaden impuls się nie zgubi.

A teraz kolego @atMegaTona - po jakiego grzyba tu jakiś timer liczący zbocza i wyświetlanie w regularnych odstępach czasu? Zauważyłeś, że nie mierzymy prędkości przeciągania kabla, interesuje nas wyłącznie ilość obrotów rolki.

Ale wróćmy do programu.

Oczywiście - w programie trzeba dodać przeliczanie ilości obrotów na centymetry, ale to raczej nie powinno stanowić problemu.

A teraz pytanie od strony mechanicznej: czy zabezpieczyłeś rolkę przed cofnięciem (łożyska jednokierunkowe lub nawet prosty mechanizm zapadkowy)? Bo cofnięcie rolki w tym przypadku nie zmniejszy stanu licznika tylko go zwiększy, niszcząc w ten sposób wynik pomiaru.

Oczywiście można zrobić tak, że cofnięcie rolki będzie zmniejszało licznik - ale czy jest to konieczne? Bo wymaga nie tylko zmiany w programie, ale również dołożenia drugiego hallotronu.

 

 

  • Lubię! 2
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

@ethanak Dziękuje Ci bardzo za takie dokładne wyśnienie, znowu wieczorem przysiądę żeby to sobie przyswoić bo przerwania to dla mnie jeszcze czarna magia 🙂

Tak, mam zabezpieczenie na szpuli na którą nawijam przewód ale byc moze zrobię jeszcze przy rolkach. Póki co nie ma tam łożysk, jak uporam się z programem to wtedy pobawię się z tym.

Link do komentarza
Share on other sites

12 minut temu, marboz napisał:

przerwania to dla mnie jeszcze czarna magia

No już lepiej nie potrafię wyjaśnić 😞

Przy okazji: operowanie parą cli/sei może być czasem niewygodne i mało czytelne, ale w bibliotece avr jest makro ATOMIC_BLOCK. Czyli:

/* na początku */

#include <util/atomic.h>
  
/* a potem gdzieś w loop */
  
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
  roboczy_licznik = licznik;
}

Więcej na ten temat: https://www.nongnu.org/avr-libc/user-manual/group__util__atomic.html

  • Lubię! 1
Link do komentarza
Share on other sites

Nie, nie.@ethanak  Wyjaśniłeś bardzo fajnie ale żeby zrozumieć i popróbować muszę do tego dziś usiąść 🙂  Czarna magia w sensie że takie słowo jak przerwanie słyszałem ale nie stosowałem 🙂

Link do komentarza
Share on other sites

4 godziny temu, ethanak napisał:

A teraz kolego @atMegaTona - po jakiego grzyba tu jakiś timer liczący zbocza i wyświetlanie w regularnych odstępach czasu? Zauważyłeś, że nie mierzymy prędkości przeciągania kabla, interesuje nas wyłącznie ilość obrotów rolki.

Jeśli mamy do dyspozycji sprzęt to lepiej jest go wykorzystać niż emulować jego działanie programowo. Nie będzie konieczne wtedy obsługiwać przerwania przy każdym impulsie a jedynie odczytać licznik timera w razie potrzeby i zaktualizować zmienną lokalną. Przerwanie w tym wypadku będzie konieczne jedynie od przepełnienia licznika. Nie ma też sensu aktualizować stanu wyświetlacza częściej niż kilka razy na sekundę więc takie rozwiązanie jest bardzo wydajne. Ale zapewne w trakcie rozwoju wątku i do tego dojdziemy więc nie będę już wybiegał przed Twój program edukacyjny :)

Kiedy się uczyłem avr bawiłem się prostym wykrywaczem metalu na generatorze Colpitsa gdzie częstotliwość generatora zmieniała się pod wpływem obecności metalu w polu cewki. Wykorzystałem wtedy timer do zliczania impulsów z generatora w odniesieniu do podstawy czasu a odchylenie od średniej ilości sygnalizowało wykrycie metalu.

1 godzinę temu, marboz napisał:

w sensie że takie słowo jak przerwanie słyszałem ale nie stosowałem

Czasem lepiej przerwać niż przegiąć  :D

Link do komentarza
Share on other sites

18 minut temu, atMegaTona napisał:

Jeśli mamy do dyspozycji sprzęt to lepiej jest go wykorzystać niż emulować jego działanie programowo. Nie będzie konieczne wtedy obsługiwać przerwania przy każdym impulsie a jedynie odczytać licznik timera w razie potrzeby i zaktualizować zmienną lokalną. Przerwanie w tym wypadku będzie konieczne jedynie od przepełnienia licznika.

Przecież to się kupy nie trzyma! Program ma zliczać ilość obrotów rolki i tylko tyle, kiedy ma jakieś timery sczytywać i po co? Poza tym przecież wykorzystujemy sprzęt i nic programowo nie emulujemy! Coś Ci się nie pomyliło przypadkiem?

Zarzuć kawałkiem kodu bo inaczej uznam że bzdury gadasz.

Link do komentarza
Share on other sites

@ethanak jeszcze raz bardzo dziękuje za wytłumaczenie , zaczynam rozumieć o co chodzi, scalę ten kod i przetestuje ale chcę być pewny najpierw jak to dokładnie działa. Lubię wiedzieć a po moim początkowym kodzie widzę że jestem jeszcze w lesie je żeli chodzi o uporządkowanie wiedzy i przede wszystkim wiem teraz dlaczego tak istotne jest szybkie wykonywanie programu.

@atMegaTona  dzięki za zaangażowanie i Twoje rozwiązanie,  jest dla mnie trudniejsze do zrozumienia, tz jeszcze bo mam zamiar wiedzę zgłebiać bo temat jest cholernie ciakawy.

 

  • Lubię! 1
Link do komentarza
Share on other sites

44 minuty temu, atMegaTona napisał:

Można oczywiście włączyć generowanie co impuls ale nie widzę w tym sensu w tym konkretnym przypadku jeśli można rzadziej.

 

Nie bardzo rozumiem tok Twojego rozumowania, ale tutaj 1 impuls to ok. 1.5 cm, dlaczego chcesz mierzyć coś z dokładnością do kilometra... możesz to jakoś przybliżyć?

 

47 minut temu, atMegaTona napisał:

używanie funkcji arduino jest zazwyczaj łatwiejsze i wygodniejsze ale też najczęściej dłużej trwa i więcej pamięci zużywa.

W przypadku małego Arduino owszem... spróbuj to samo zrobić na ESP32 😉 A po to wymyślili rodzinę fast żeby się kompilowała na jedną czy dwie instrukcje. Poza tym pamiętaj, że rozmawiasz z kimś kto dopiero raczkuje w temacie i nie musisz mu pokazywać ile to Ty nie umiesz. Cały pic w tym, żeby przy minimalnej wiedzy chłop mógł se złożyć ustrojstwo i nie rozpoczynać od budowy mikrokontrolerów. Tu masz pole do popisu.

31 minut temu, marboz napisał:

chcę być pewny najpierw jak to dokładnie działa.

I bardzo dobrze - jak zauważyłeś sam przepisywanie czegoś bez zrozumienia daje fatalne wyniki 🙂

  • Lubię! 1
Link do komentarza
Share on other sites

To nie jest kwestia samego programowania jako takiego ale i znajomości sprzętu który się programuje.

Mikrokontroler ma wbudowane różne funkcje wybierane przełączaniem odpowiednich bitów w odpowiednich rejestrach jak np.  kanały tv zmienia się odpowiednimi przyciskami na pilocie. Różnica polega na sposobie ich wybierania. Framework arduino ma do tego funkcje, avr libc na którym arduino bazuje ma do tego makra i symbole. O ile nie używa się będących ze sobą w konflikcie "opcji" można równolegle używać obu rozwiązań przy czym stosowanie makr i symboli avr libc jest na ogół wielokrotnie wydajniejsze ale i bardziej skomplikowane choć nie zawsze.

Myślę nawet, że łatwiej jest zrozumieć bezpośrednie odwołanie do wektora przerwań niż działanie funkcji attachInterrupt.

W Twoim konkretnie przypadku różnica polega na tym, że po prostu odczytujesz licznik który sam się zwiększa. Nie trzeba niczego sprawdzać więc nie potrzeba żadnych ifów, zwiększa się on nawet podczas trwania delay() niezależnie od pętli głównej za każdym razem kiedy pojawi się impuls na wejściu.

To co przedstawiłem to taki przykład demonstracyjny choć mimo to funkcjonalny. Można uprościć i zrezygnować nawet z przerwania od przepełnienia bo faktycznie @ethanak ma rację, że to urządzenie nie służy do mierzenia kilometrów przewodu.

Zamiast:

licznik_impulsow = (0xFFFF * licznik_przerwan) + ICR1; 

zrobić:

licznik_impulsow += ICR1;
ICR1 = 0;

i nie włączać przerwań wcale:


// TIFR1 =  _BV(TOV1);				// ustaw przerwanie od przepełnienia 
// TIMSK1 = _BV(TOIE1);				// włącz generowanie przerwań


// volatile uint8_t licznik_przerwan;
// ISR(TIMER1_OVF_vect) {
//  licznik_przerwan++; 
// }

co upraszcza program do:

#include <avr/interrupt.h>
#include <Wire.h>              // standardowa biblioteka Arduino
#include <LiquidCrystal_I2C.h> // dolaczenie pobranej biblioteki I2C dla LCD

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Ustawienie adresu ukladu na 0x27

void reset(void){
   ICR1 = 0; 
}

void setup() {
	
    Serial.begin(9600);
    lcd.begin(16, 2);               // Inicjalizacja LCD 2x16
    pinMode(4, INPUT_PULLUP);       // reset pin

    TCCR1B |= _BV(ICES1) | _BV(CS10);   //włącz timer 1 w trybie ic i licz zbocza narastające na pinie 8

	pinMode(8, INPUT);                  // icp jako wejście
	digitalWrite(8, 0);	

    initTimer();
    attachInterrupt(digitalPinToInterrupt(4), reset, FALLING); // ;)

    
}

void loop(){

uint32_t licznik_impulsow = 0;

/// jakiś kod

delay(300); // wyświetlaj wynik co 300ms

licznik_impulsow += ICR1; // ICR1 jest 16-bitowy
ICR1 = 0;
Serial.println(licznik_impulsow);
lcd.clear();                  
lcd.setCursor(1, 0);          
//lcd.print((licznik_impulsow *15) / 10); //wyswietla dane
lcd.print(licznik_impulsow);
    

}

i niech @ethanak  nie mówi, że kod jest dłuższy i niech @ethanak nie pisze, że to nie ma sensu bo ic do tego właśnie zaprojektowano.

Link do komentarza
Share on other sites

Uzupełniłem kod, tz mało co było do zrobienia 🙂  Wszytko działa wyśmienicie więc jeszcze raz dziekuję.

Wciąż rozkminiam co jest czym ale powoli światełko w tunelu. Najważniejsze jest i działa. teraz pozostaje dopracowac idealnie mechanikę tz łozyska itp a potem złożyć wszytko na czymś innym niż płytka stykowa 🙂 Zastanawiam się tez czy będzie to działało na Arduino nano?

Mam nadzieję że nie dodałem nic za dużo  😂

 


#include <Wire.h>              // standardowa biblioteka Arduino
#include <LiquidCrystal_I2C.h> // dolaczenie pobranej biblioteki I2C dla LCD

LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Ustawienie adresu ukladu na 0x27

volatile int licznik;

void licz(void)
{
    licznik++;
}

void setup()
{

#define przyciskReset 4

    pinMode(4, INPUT_PULLUP);
    pinMode(2, INPUT);

    Serial.begin(9600);
    lcd.begin(16, 2);

    attachInterrupt(digitalPinToInterrupt(2), licz, RISING);
}

int poprzedni_licznik;

void loop()
{
    int roboczy_licznik;

    if (digitalRead(przyciskReset) == LOW)
    {
        cli(); // wyłączamy przerwania
        licznik = 0;
        sei(); // włączamy przerwania z powrotem
    }

    cli();
    roboczy_licznik = licznik;
    sei();

    //wyświetla na LCD ile razy czujnik wykrył magnes
    if (poprzedni_licznik != roboczy_licznik)
    {
        poprzedni_licznik = roboczy_licznik;
        Serial.println(roboczy_licznik);
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print((roboczy_licznik)*15/10);
    }
}

 

Link do komentarza
Share on other sites

Jeśli masz możliwość przełączyć ten czujnik na pin 8 to sprawdź w imię nauki rozwiązanie ze sprzętowym licznikiem bez ifów 😉 W przyszłości może docenisz taką możliwość.

Link do komentarza
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!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   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...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.