Forum 2005-2010 (read only) > Syntax & Programs

Quadrature Encoders

<< < (5/8) > >>

retrolefty:
Here is what I've modified from the playground stuff to test out the two encoders I've recently obtained. I'm waiting on delivery of a stepper motor to see how well I can slave stepper movement to encoder movement. I've only posted the code from the two ISR routines as there is a size limit that won't let me past the complete listing. I had first used digitalReads and digitalWrite for the I/O but later converted to direct port I/O make the ISR faster, at least that is the plan.  ;)


--- Code: ---void doEncoder1() {
   if (PIND & 0x04) {                              // test for a low-to-high interrupt on channel A, same as if(digitalRead(encoderPinA) == HIGH)
       if ( !(PIND & 0x10)) {                      // check channel B for which way encoder turned, same as if(digitalRead(encoderPinB) == LOW)
          encoderPos = ++encoderPos;               // CW rotation
          PORTD = PIND | 0x40;                     // set direction output pin to 1 = forward, same as digitalWrite(encoderdir, HIGH);
         }
       else {
          encoderPos = --encoderPos;               // CCW rotation
          PORTD =PIND & 0xBF;                      // Set direction output pin to 0 = reverse, same as digitalWrite(encoderdir, LOW);
         }
   }
   else {                                          // it was a high-to-low interrupt on channel A
       if (PIND & 0x10) {                          // check channel B for which way encoder turned, same as if(digitalRead(encoderPinB)==HIGH)
          encoderPos = ++encoderPos;               // CW rotation
          PORTD = PIND | 0x40;                     // Set direction output pin to 1 = forward, same as digitalWrite(encoderdir, HIGH);
          }
       else {
          encoderPos = --encoderPos;               // CCW rotation
          PORTD =PIND & 0xBF;                      // Set direction output pin to 0 = reverse, same as digitalWrite(encoderdir, LOW);
          }
        }
   PORTD = PIND | 0x80;                            //  digitalWrite(encoderstep, HIGH);   generate step pulse high
   PORTD = PIND | 0x80;                            //  digitalWrite(encoderstep, HIGH);   add a small delay
   PORTD = PIND & 0x7F;                            //  digitalWrite(encoderstep, LOW);    reset step pulse
}                                                   // End of interrupt code for encoder #1


                                                   // Start of Interrupt routine for encoder #2
                                                   // added direct port access to speed up ISR handling of encoder interrupts
void doEncoder2(){
 if (PIND & 0x08) {                                // test for a low-to-high interrupt on channel A, same as if(digitalRead(encoder2PinA) == HIGH)
    if (!(PIND & 0x20)) {                          // check channel B for which way encoder turned, same as if(digitalRead(encoder2PinB) == LOW)
     encoder2Pos = ++encoder2Pos;                  // CW rotation
     PORTB = PINB | 0x01;                          // Set direction output pin to 1 = forward, same as digitalWrite(encoder2dir, HIGH);
    }
    else {
     encoder2Pos = --encoder2Pos;                  // CCW rotation
     PORTD =PIND & 0xFE;                           // Set direction output pin to 0 = reverse, same as digitalWrite(encoder2dir, LOW);
    }
 }
 else {                                            // it was a high-to-low interrupt on channel A
    if (PIND & 0x20) {                             // check channel B for which way encoder turned, same as if(digitalRead(encoder2PinB)==HIGH)
     encoder2Pos = ++encoder2Pos;                  // CW rotation
     PORTB = PINB | 0x01;                          // Set direction output pin to 1 = forward, same as digitalWrite(encoder2dir, HIGH);
     }
    else {
     encoder2Pos = --encoder2Pos;                  // CCW rotation
     PORTB =PINB & 0xFE;                           // Set direction output pin to 0 = reverse, same as digitalWrite(encoder2dir, LOW);
    }
 }
 PORTB = PINB | 0x02;                              // digitalWrite(encoder2step, HIGH);   generate step pulse high
 PORTB = PINB | 0x02;                              // digitalWrite(encoder2step, HIGH);   used to add a small delay
 PORTB = PINB & 0xFD;                              // digitalWrite(encoder2step, LOW);    reset step pulse
}                                                   // End of interrupt code for encoder #2


--- End code ---


Lefty

mem:
Lefty, I avoid direct port io unless absolutely neccessary. Here is a simple  sketch that works fine,  tested on the single encoder I have here

--- Code: ---// Read two Quadrature Encoders

const int encoder1PinA = 2;  // encoder 1 on pins 2 and 4
const int encoder1PinB = 4;
volatile int encoder1Pos = 0;

const int encoder2PinA = 3; //encoder 2 on pins 3 and 5
const int encoder2PinB = 5;
volatile int encoder2Pos = 0;


void setup() {
 Serial.begin(9600);
 attachInterrupt(0, doEncoder1, CHANGE);  // encoder pin on interrupt 0 (pin 2)
 attachInterrupt(1, doEncoder2, CHANGE);  // encoder pin on interrupt 1 (pin 3)
}

void loop() {
 int Pos1, Pos2;
 static int oldPos1, oldPos2;
 uint8_t oldSREG = SREG;
 
 cli();
 Pos1 = encoder1Pos;  
 Pos2 = encoder2Pos;
 SREG = oldSREG;
 
 if(Pos1 != oldPos1){
    Serial.print("Encoder 1=");
    Serial.println(Pos1,DEC);
    oldPos1 = Pos1;
 }
 if(Pos2 != oldPos2){
    Serial.print("Encoder 2=");
    Serial.println(Pos2,DEC);
    oldPos2 = Pos2;
 }  
}


void doEncoder1()
{
 if (digitalRead(encoder1PinA) == digitalRead(encoder1PinB))  
   encoder1Pos++;    // count up if both encoder pins are the same on pin change interupt
 else                                      
   encoder1Pos--;    //count diown if pins are different
}

void doEncoder2()
{
 if (digitalRead(encoder2PinA) == digitalRead(encoder2PinB))  
   encoder2Pos++;    // count up if both encoder pins are the same on pin change interupt
 else                                      
   encoder2Pos--;    //count diown if pins are different
}
--- End code ---

retrolefty:
[edit]Lefty, I avoid direct port io unless absolutly neccessary. Here is a simple  sketch that works fine,  tested on the single encoder I have here[/edit]

I understand the trade offs involved between 'mapped' I/O addressing Vs hard-coded addressing. I just wanted to see the difference in performance between the two methods as I spun the encoder as fast as I could. Direct addressing did miss fewer steps. Once I can determine the maximum speed that my stepper motor can reliably step at, I will revisit how best to do the encoder ISR I/O.

Thanks
Lefty

mem:
Lefty, I would be interested to hear your results.  I had guestimated that a 250 step encoder would need to be spinning at many hundreds perhaps thousands of rpm for the time between two steps get anywhere close to the duration of a digitalRead.

Have fun!

retrolefty:

--- Quote ---Have fun!
--- End quote ---


It has been a lot of fun so far. One reason I added a encoder step pulse output generator in the ISR is to maybe somehow detect any missed interrupt. However I would need some external means of comparing encoder input pulse counts Vs ISR generated encoder step output counts. Perhaps a sketch running on a second chip could be used. I can certainly tell that the ISR interrupt step counts is not kept up with by the counter in the main loop, using 57600 baud to print step counts.


--- Code: ---void loop() {  
 
 if ( encoderPos != encoderLast ) {                // Start of Rotary Encoder #1 Code
   if ( encoderPos > encoderLast )
      position = ++position;
        // digitalWrite(encoderdir, HIGH);         Set direction output pin to 1 = forward, doing this in ISR but could be moved here
   else position = --position;
        //  digitalWrite(encoderdir, LOW);         Set direction output pin to 0 = reverse, doing this in ISR but could be moved here
 encoderLast = encoderPos;
 
 if ((position % rate) == 0) {                     // test for ratio of encoder steps to action steps
                                                   // this results in a skipping ratio of the basic 128 or 256 steps per revolution of the encoder
   Serial.print ("Encoder #1 = ");                 // use serial output for demonstration showing encoder movement
   Serial.print (position, DEC);                   // position is the encoder step count done in the main loop
   Serial.print ("      ISR #1 = ");
   Serial.println (encoderPos, DEC);               // encoderPos in the encoder step count done in the ISR
        //  digitalWrite(encoderstep, HIGH);       generate step pulse, doing this now in the ISR, but could be moved here if desired
        //  delay(1);                              
        //  digitalWrite(encoderstep, LOW);          
   }
 }                                                 // End of Rotary #1 Encoder Code
   
 if ( encoder2Pos != encoder2Last ) {              // Start of Rotary Encoder #2 Code
--- End code ---


Lefty

Navigation

[0] Message Index

[#] Next page

[*] Previous page

Go to full version