www.elektronik.si
Arduino UNO in encoder
Pojdi na stran Prejšnja  1, 2, 3  Naslednja  :||:
www.elektronik.si -> Arduino sekcija

Avtor: igo PrispevekObjavljeno: Pet Mar 08, 2019 10:53 pm    Naslov sporočila:  
----------------------------------------------------------------------------
Znotraj loop() dodaj
Koda:

 noInterrupts(); // prepreči spremembo obrat in count
 obrat4lcd = obrat; // shrani njuni trenutni vrednosti
 count4lcd = count;
 interrupts(); // dovoli spreminjanje


Tako se bosta obrat za lcd in count za lcd spremenila samo enkrat v zanki in bo lahko lcd v miru izpisal njuni trenutni vrednosti.
Če bi izpisoval direktno count in obrat in bi se med izpisovanjem count parkrat spremenil, nato pa še obrat, bi nastala velika zmeda. Sploh, če bi vmes še kaj računal ali primerjal z obrat in count.
Novi spremenljivki morata biti enakega tipa kot originalni.

Ukaz za pisanje na lcd je odvisen od tega, katero knjižnico imaš.
Običajno je nekaj v stilu
Pisi2lcd(spremenljivka, vrstica, stoplec); //Pisi2lcd je izmišljeno ime
Dobro pa je imeti še število cifer, leva/desna poravnava in pa polnilo za vodilno ničlo.
Potem izpišeš približno takole:
Pisi2lcdTXT("OBRATI:", 0, 0);
Pisi2lcdNUM(obrat4lcd, 0, 8, 4, desno, " "); // tisti " " presledek je polnilo spredaj. 4 pozicije.
Pisi2lcdTXT(".", 0, 12);
Pisi2lcdNUM(count4lcd, 0, 13, 3, desno, "0"); // "0" je polnilo med . in prvo od 0 različno cifro. 3 pozicije

Preveri svojo knjižnico za izpis na lcd. Kako se imenuje, kako izpišeš besedilo in kako številko.

Avtor: marko11Kraj: Dobrepolje PrispevekObjavljeno: Sob Maj 04, 2019 3:56 pm    Naslov sporočila:  
----------------------------------------------------------------------------
Hi

Nabavil sem od Kitajca nov encoder, ima 10 razdelkov na obrat, da bo lažje za računat! Čakal zelo dolgo.
Spodaj je slika encoderja.

S priloženo kodo pa je nekaj narobe v interrupt funkciji, tam kjer šteje dol (CCW),
pa prosim za pomoč.

Opis napake:

- ko sem na poziciji 0.0 in zavrtim v nasprotni smeri ure CCW, bi moral izpisati -0.9 izpiše pa -1.9

Koda:


volatile byte count = 0; // zadostuje 8-bitno 0 ... 255
volatile int obrat = 0; // od -32768 do 0 in naprej do 32767
//volatile unsigned int obrat = 0; // 0 do 65535

void setup()
{
    Serial.begin(115200);
    pinMode(2, INPUT_PULLUP); // A_PHASE
    //pinMode(3, INPUT_PULLUP);  // Z_PHASE
    pinMode(4, INPUT_PULLUP);  // B_PHASE
     
    attachInterrupt(digitalPinToInterrupt(2), pulse, RISING); // Interrupt 0 se zgodi na Pin 2
    //attachInterrupt(0, pulse, FALLING);
}

void loop()
{
   Serial.print(obrat);  // ko naredi encoder EN obrat (360 stop.)
   Serial.print(".");
   Serial.println(count); // encoder ima 10 razdelkov
   delay(50);       
}

void pulse()    // Interrupt funkcija
{
    if(digitalRead(4) == HIGH) // steje GOR ( CW )
    {
        count++;
        if(count > 9)
        {
            count=0;
            obrat++;
        }
    }
    else          // steje DOL ( CCW )
    {
        count--;
        if(count > 9)
        {
            count=9;
            obrat--;
        }
    }
}

Avtor: dejko1Kraj: Ljubljana PrispevekObjavljeno: Sob Maj 04, 2019 4:27 pm    Naslov sporočila:  
----------------------------------------------------------------------------
Verjetno te moti, da se obrat postavi na -1 že pri prvem koraku v nasprotno smer urinega kazalca.

Na začetku je count = 0, torej pri prvem CCW koraku preide v count = 255 ter takoj v count = 9 in obrat = -1.

Zakaj raje ne šteješ le korakov in obrate izračunaš kot:
Koda:
obrat = count / 10

(nisem sicer siguren če ti ne bo bolj všeč prikaz kot ga imaš sedaj)

Ena od pomanjkljivosti tvoje kode je tudi, da v primeru poskakovanja na pinu 2 ali premikanja na mestu za del periode koda šteje narobe.

Standardni tipi kot so uint8_t in int8_t so lažji za direktno ugotovit njihov obseg vrednosti.

Avtor: marko11Kraj: Dobrepolje PrispevekObjavljeno: Sob Maj 04, 2019 9:50 pm    Naslov sporočila:  
----------------------------------------------------------------------------
dejko1@ (nisem sicer siguren če ti ne bo bolj všeč prikaz kot ga imaš sedaj)

Ena od pomanjkljivosti tvoje kode je tudi, da v primeru poskakovanja na pinu 2 ali premikanja na mestu za del periode koda šteje narobe.

Standardni tipi kot so uint8_t in int8_t so lažji za direktno ugotovit njihov obseg vrednosti.

Ja, najbolj mi odgovarja ,da izpisuje v obliki naprimer 0.9.
Ja, opazil sem tudi, da premikanje na mestu levo desno ni več točno.
Kaj misliš stem unit8_t...?

Kako naj popravim kodo, da se bo točno izvajala glede na zahteve?

Že na začetku posta sem napisal, da se z arduino in C++ prvič seznanjam in da mi je to vse velika neznanka, zato prosim za nasvete.

lp,marko

Avtor: igo PrispevekObjavljeno: Sob Maj 04, 2019 10:27 pm    Naslov sporočila:  
----------------------------------------------------------------------------
V bistvu mora šteti
0,2
0,1
0,0
-0,1
-0,2
...

Najlažje je dodati pomožno spremenljivko countX.
Koda:

volatile byte countX = 10; // zadostuje 8-bitno 0 ... 255 
volatile int obrat = 0; // od -32768 do 0 in naprej do 32767 
volatile int count = 0; //

void setup() 

    Serial.begin(115200); 
    pinMode(2, INPUT_PULLUP); // A_PHASE 
    //pinMode(3, INPUT_PULLUP);  // Z_PHASE 
    pinMode(4, INPUT_PULLUP);  // B_PHASE 
      
    attachInterrupt(digitalPinToInterrupt(2), pulse, RISING); // Interrupt 0 se zgodi na Pin 2 
    //attachInterrupt(0, pulse, FALLING); 


void loop() 

   Serial.print(obrat);  // ko naredi encoder EN obrat (360 stop.) 
   Serial.print("."); 
   Serial.println(count); // encoder ima 10 razdelkov 
   delay(50);        


void pulse()    // Interrupt funkcija 

  if(digitalRead(4) == HIGH) // steje GOR ( CW ) 
    { 
        countX++; 
        if(countX > 19) 
        { 
            countX=10; 
            obrat++; 
        } 
    } 
    else          // steje DOL ( CCW ) 
    { 
        countX--; 
        if(countX < 1) 
        { 
            countX=10; 
            obrat--; 
        } 
    }
  count = countX - 10;
}

Avtor: igo PrispevekObjavljeno: Ned Maj 05, 2019 8:18 am    Naslov sporočila:  
----------------------------------------------------------------------------
Oh, problem bo s predznakom med 0.0 in -0.9 .


Bo treba imeti samo
volatile int count;
volatile int countCEL;
volatile byte countDEC;

znotraj if stavkov v prekinitvi samo count++ in count--,
pred izhodom iz prekinitve pa
countCEL = count / 10; // celi del pri deljenju z 10
countDEC = abs(count % 10); // absolutna vrednost ostanka pri deljenju z 10.

Pri izpisu v loop() pa vmes med countCEL in countDEC vriniti vejico.

Avtor: marko11Kraj: Dobrepolje PrispevekObjavljeno: Ned Maj 05, 2019 11:53 am    Naslov sporočila:  
----------------------------------------------------------------------------
dejko1 je napisal/a:


Ena od pomanjkljivosti tvoje kode je tudi, da v primeru poskakovanja na pinu 2 ali premikanja na mestu za del periode koda šteje narobe.

Standardni tipi kot so uint8_t in int8_t so lažji za direktno ugotovit njihov obseg vrednosti.


Kaj si mislil s tem poskakovanjem, oziroma kako popraviti kodo?

lp,

Avtor: dejko1Kraj: Ljubljana PrispevekObjavljeno: Pon Maj 06, 2019 5:21 pm    Naslov sporočila:  
----------------------------------------------------------------------------
Enkoder ki ga imaš verjetno deluje na optičnem ali magnetnem principu, torej ni bouncinga, ki bi ga pričakoval pri mehanskem. Še vedno obstaja možnost, npr. da je ena linija na 0, ter da se enkoder večkrat levo-desno premakne preko meje na drugi liniji, ki ti proži interrupt (posledično tvoja koda šteje premike v eno smer).

Mislim, da moraš za debouncing uporabiti obe fronti signala (ali fronti obeh signalov) v interruptu ter upoštevati zdajšnje in prejšnje stanje signalov.

Npr. pri vrednosti vhodov AB 00 pričakuješ prehod v 10 v eno smer in 01 v drugo smer.

Primer z interrupti (nisem preizkusil, ampak mislim, da je ok):
https://www.instructables.com/id/Improved-Arduino-Rotary-Encoder-Reading/

Zadevo se da izvest tudi s pollingom (periodičnim branjem stanja vhodov):
https://www.best-microcontroller-projects.com/rotary-encoder.html

Glede tipov:
uint8_t je tip, ki je ekvivalenten byte v tvoji kodi, le da se direkto iz definicije vidi, da ima 8 bitov, je unsigned in integer.

uint8_t = 6;

Z uprabo opisnih tipov kot sta char in byte ni nič narobe, dokler veš kaj predstavljata na dotičnem sistemu Smile

Avtor: ElGrigonKraj: okolica Lenarta PrispevekObjavljeno: Pon Maj 06, 2019 5:30 pm    Naslov sporočila:  
----------------------------------------------------------------------------
Če pogledaš datasheet enkoderja, se proži phaseZ vsako celo periodo ali pahseA ali phaseB. Razlika je le v smeri kar določiš ali B zaostaja ali A zaostaja.

Avtor: Jaka57Kraj: Grosuplje PrispevekObjavljeno: Pon Maj 06, 2019 6:21 pm    Naslov sporočila:  
----------------------------------------------------------------------------
Da ne boste o poskakovanju, je tukaj pdf.
Očitno ga je edino ElGrigon pogledal.

Avtor: marko11Kraj: Dobrepolje PrispevekObjavljeno: Tor Maj 07, 2019 5:29 pm    Naslov sporočila:  
----------------------------------------------------------------------------
Jaka57 je napisal/a:
Da ne boste o poskakovanju, je tukaj pdf.
Očitno ga je edino ElGrigon pogledal.


Zdravo generacijo, kako kaj?

Ta encoder na sliki, je verjetno optični, zato mislim, da tukaj ni nekega poskakovanja.

Z brskanjem po spletu sem našel tole spletno stran: https://playground.arduino.cc/Main/RotaryEncoders/
kjer je veliko različnih programov, preveril sem vse programe, ta spodaj pa se mi zdi, da je najbolj točen, če enkoder ročno premikam naprej nazaj in je izhodiščna točka vedno pravilna.

Ne vem pa če se da program preurediti tako, ker sedaj našteje za en obrat 40 razdelkov, rad bi da ko napravi en obrat pretvori teh 40 v 1.0 obratov in tako naprej v tej obliki:
-0.2
-0.1
0.0
1.1
1.2
1.3

Koda iz predlagane povezave, ki je nekje zelo točna, uporablja dve interrupt linije:


Koda:


#define encoder0PinA  2
#define encoder0PinB  3

volatile int encoder0Pos = 0;

void setup() {
  pinMode(encoder0PinA, INPUT);
  pinMode(encoder0PinB, INPUT);

  // encoder pin on interrupt 0 (pin 2)
  attachInterrupt(0, doEncoderA, CHANGE);

  // encoder pin on interrupt 1 (pin 3)
  attachInterrupt(1, doEncoderB, CHANGE);

  Serial.begin (9600);
}

void loop() {
  //Do stuff here
}

void doEncoderA() {
  // look for a low-to-high on channel A
  if (digitalRead(encoder0PinA) == HIGH) {

    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinB) == LOW) {
      encoder0Pos = encoder0Pos + 1;         // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }

  else   // must be a high-to-low edge on channel A
  {
    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinB) == HIGH) {
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
  Serial.println (encoder0Pos, DEC);
  // use for debugging - remember to comment out
}

void doEncoderB() {
  // look for a low-to-high on channel B
  if (digitalRead(encoder0PinB) == HIGH) {

    // check channel A to see which way encoder is turning
    if (digitalRead(encoder0PinA) == HIGH) {
      encoder0Pos = encoder0Pos + 1;         // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;         // CCW
    }
  }

  // Look for a high-to-low on channel B

  else {
    // check channel B to see which way encoder is turning
    if (digitalRead(encoder0PinA) == LOW) {
      encoder0Pos = encoder0Pos + 1;          // CW
    }
    else {
      encoder0Pos = encoder0Pos - 1;          // CCW
    }
  }
}


Jaka57, mislim, da si na temu področju pravi mačo, pa te prosim, če malo pokomentiraš oziroma svetuješ kaj in kako naprej, pa tudi kdo drug je dobrodošel z nasveti. Hvala.

LP,marko

Avtor: Jaka57Kraj: Grosuplje PrispevekObjavljeno: Tor Maj 07, 2019 11:23 pm    Naslov sporočila:  
----------------------------------------------------------------------------
Marko, nisem ravno taka perla, kot misliš Very Happy .
Me pa motijo rezultati, ki jih dobiš iz tvojega programa.
Prvo, od kje decimalne vrednosti?
Drugo pa, tvoj program šteje vse prehode iz 0 na 1 in obratno, kajti do prekinitve pride ob vsaki spremembi stanja.
Tvoj enkoder ima resolucijo 10impulzov/obrat tvoj program pa jih vrne 40?

Naslednja stvar, ki me moti je, da imaš v interupt rutini A Serial.println.
Interupt rutina naj bi bila, kar se da kratka!
Če želiš gledat štetje impulzov lahko to narediš void loop() saj je spremenljivka encoder0pos globalna in je dosegljiva od kjerkoli.

Kar se pa tiče 40->1 lahko to narediš tudi v prekinitveni rutini z dodatno globalno spremenljivko (ni pa nujno, kot lahko vidiš iz spodaj omenjenega primera), ki jo povečuješ ali zmanjšuješ za 1, ko pride do 40 ali -40.
Pri tipih spremenljivk se moraš tudi zavedati, da gre int od -32768 do 32767.

Kakorkoli, že nekaj časa je, kar sem se hecal s klasičnim enkoderjem.
Uporabil sem knjižnjico, ki je v prilogi.
Uporabil sem jo tako, kot je to narejeno v Interuptrotator.ino.
V tem primeru lahko vidiš na koncu programa, ko pride do >66 čaka 6.6 sekunde, vendar impulze šteje naprej.
Tukaj lahko enostavno postaviš pozicijo na 0, ko prešteje do 40 in za eno povečaš vrednost spremenljivke, recimo obrati, ali zmanjšaš, če pride na -40.

Zakaj pa misliš uporabiti ta enkoder, kajti prav hitro ga ne moreš vrteti (recimo za kakšno hitro štetje obratov).
V končni fazi, bi lahko prekinitev uporabil na Z izhodu in ko pride do prekinitve program pogleda kaj je na A in B na podlagi tega, pa določi ali gre CW ali CCW, šteje pa samo Z impulze.


Nazadnje urejal/a Jaka57 Pet Maj 10, 2019 1:21 am; skupaj popravljeno 2 krat

Avtor: marko11Kraj: Dobrepolje PrispevekObjavljeno: Čet Maj 09, 2019 4:27 pm    Naslov sporočila:  
----------------------------------------------------------------------------
Jaka57 je napisal/a:


Drugo pa, tvoj program šteje vse prehode iz 0 na 1 in obratno, kajti do prekinitve pride ob vsaki spremembi stanja.
Tvoj enkoder ima resolucijo 10impulzov/obrat tvoj program pa jih vrne 40?

Zakaj pa misliš uporabiti ta enkoder, kajti prav hitro ga ne moreš vrteti (recimo za kakšno hitro štetje obratov).
V končni fazi, ti lahko prekinitev uporabil na Z izhodu in ko pride do prekinitve program pogleda kaj je na A in B na podlagi tega, pa določi ali gre CW ali CCW, šteje pa samo Z impulze.


Ko sem v programu popravil ta del iz CHANGE na Falling pa šteje pravilno 10 pulzov/obrat


Koda:
attachInterrupt(0, doEncoder, FALLING);  //  CHANGE


Ta enkoder bi rabil za štetje ovojev, max 1500 obratov/minuto. Zato rabim decimalke, da veš kje se nahajaš.

Igo je napisal
Citiram:


Bo treba imeti samo
volatile int count;
volatile int countCEL;
volatile byte countDEC;

znotraj if stavkov v prekinitvi samo count++ in count--,
pred izhodom iz prekinitve pa
countCEL = count / 10; // celi del pri deljenju z 10
countDEC = abs(count % 10); // absolutna vrednost ostanka pri deljenju z 10.

Pri izpisu v loop() pa vmes med countCEL in countDEC vriniti vejico.


Sem ta del kode vpisal v spodnji program.

Štetje GOR (CW) deluje in pravilno izpisuje.

Štetje DOL (CCW) pa mi NE deluje, javi napako, ko vpišem vrstico z countDEC.
Brez te vrstice pe celi del izpisuje pravilno.

Koda:

#define encoder0PinA  2
#define encoder0PinB  3

volatile int count = 0; 
volatile int countCEL = 0;
volatile int countDEC = 0;

void setup() {
  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH);       // turn on pull-up resistor
  pinMode(encoder0PinB, INPUT);
  digitalWrite(encoder0PinB, HIGH);       // turn on pull-up resistor

  attachInterrupt(0, doEncoder, FALLING);  // FALLING, RISING, CHANGE
  Serial.begin (9600);
  Serial.println("start");                // a personal quirk
}

void loop() {
   Serial.print(countCEL);  // ko naredi encoder EN obrat (360 stop.)
   Serial.print(".");
 Serial.println (countDEC);
 delay(300);
 
}

void doEncoder()
{
  if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB))
    {
    count++;                // štej gor CW
    countCEL = count/10;
    countDEC = abs(count % 10);
       
    }
  else
    {
    count--;                //štej dol CCW
    countCEL = count/10;
    //countDEC = abs(count % 10);  //tukaj javi napako

    }
}


void doEncoder_Expanded() {
  if (digitalRead(encoder0PinA) == HIGH) {   // found a low-to-high on channel A
    if (digitalRead(encoder0PinB) == LOW) {  // check channel B to see which way
      // encoder is turning
      count = count - 1;         // CCW
    }
    else {
      count = count + 1;         // CW
    }
  }
  else                                        // found a high-to-low on channel A
  {
    if (digitalRead(encoder0PinB) == LOW) {   // check channel B to see which way
      // encoder is turning
      count = count + 1;          // CW
    }
    else {
      count = count - 1;          // CCW
    }

  }

}



LP, marko

Avtor: igo PrispevekObjavljeno: Čet Maj 09, 2019 6:39 pm    Naslov sporočila:  
----------------------------------------------------------------------------
Pripni besedilo napake.

Za abs(x) piše, da naj ne bi bilo nobene operacije znotraj oklepajev. Morda je to vzrok, a potem tudi za ++ ne bi delalo.
Koda:

void doEncoder() 

  if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB)) 
    { 
    count++;                // štej gor CW     
    } 
  else 
    { 
    count--;                //štej dol CCW
    } 

    countCEL = count % 10;  // trik, da ni treba uvesti dodatne spremenljivke
    countDEC = abs(countCEL); // decimalni del je ostanek pri deljenju z 10
    countCEL = count / 10; // ponoven izračun celega dela


Pa daj si malo preberi stvari na strani Arduino Reference:
https://www.arduino.cc/reference/en/

Avtor: Jaka57Kraj: Grosuplje PrispevekObjavljeno: Pet Maj 10, 2019 1:59 am    Naslov sporočila:  
----------------------------------------------------------------------------
Ni mi sicer jasno, zakaj tako kompliciranje?
Kaj naredi funkcija abs(x)?
Vrne absolutno vrednost argumenta, t.j., če x negativen, vrne x.
Vse kar kvačkaš v prekinitveni rutine se lahko naredi pri samem izpisu.
Recimo takole:

Koda:
#define encoder0PinA  2
#define encoder0PinB  3

volatile int count = 0;
int dec=0; 

void setup() {
  pinMode(encoder0PinA, INPUT);
  digitalWrite(encoder0PinA, HIGH);       // turn on pull-up resistor
  pinMode(encoder0PinB, INPUT);
  digitalWrite(encoder0PinB, HIGH);       // turn on pull-up resistor

  attachInterrupt(0, doEncoder, FALLING);  // FALLING, RISING, CHANGE
  Serial.begin (9600);
  Serial.println("start");                // a personal quirk
}

void loop() {
   Serial.print(count/10);  // ko naredi encoder EN obrat (360 stop.)
   Serial.print(".");
   dec=count%10;
 Serial.println (abs(dec));               //zaradi izpisa se znebimo minusa
 delay(300);
 
}

void doEncoder()
{
  if (digitalRead(encoder0PinA) == digitalRead(encoder0PinB))
    {
    count++;                // štej gor CW
      }
  else
    {
    count--;                //štej dol CCW
       }
}

Stran 2 od 3

Powered by phpBB © 2001,2002 phpBB Group