Author Topic: Any good examples on rotary encoder library "WITH" velocity ?  (Read 27878 times)

0 Members and 1 Guest are viewing this topic.

Online BravoV

  • Super Contributor
  • ***
  • Posts: 6253
  • Country: 00
Any good examples on rotary encoder library "WITH" velocity ?
« on: September 13, 2013, 07:31:12 am »
The title should be clear, appreciate if you can point out some links/references on the code library that you think is proven and reliable, or at least good one. Really love to learn how to do it properly and efficiently from the experts/pros.

I don't need polling method, only ISR type, and don't worry about the cpu power, it will be used on relatively new arm m4f generation, so in term of raw power, cmiiw this should not be a problem right ?

TIA

Edit : Polling methods are also welcomed.  :P
« Last Edit: September 24, 2013, 01:11:51 am by BravoV »
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 7367
  • Country: nz
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #1 on: September 13, 2013, 07:41:52 am »
Would be pretty simple to write your own.

Use one of the hardware timers to generate a slow clock, maybe 100Hz, and use it to get a timestamp on each interrupt then calc velocity and direction.
Maybe add some filtering to the velocity.


« Last Edit: September 13, 2013, 07:46:20 am by Psi »
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8229
  • Country: 00
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #2 on: September 13, 2013, 10:23:55 am »
Not sure what "With velocity" means.

But a typical approach to read an encoder is via a state machine. That can be easily implemented via polling or via two port-change / external interrupts. The code is almost identical in either case.
================================
https://dannyelectronics.wordpress.com/
 

Offline Psi

  • Super Contributor
  • ***
  • Posts: 7367
  • Country: nz
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #3 on: September 13, 2013, 10:55:58 am »
velocity means how fast the encoder is being rotated.

You can then do different things depending on the rotation speed.
ie,
have slow movement changing a setting by 0.1
a medium movement changing it by 1
and a fast movement changing it by 10 

Or use a function to calculate the change amount based on the speed in real time.


« Last Edit: September 13, 2013, 10:57:34 am by Psi »
Greek letter 'Psi' (not Pounds per Square Inch)
 

Offline Rerouter

  • Super Contributor
  • ***
  • Posts: 4394
  • Country: au
  • Question Everything... Except This Statement
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #4 on: September 13, 2013, 12:40:04 pm »
The best way to manage interrupts is to keep them short and sweet,

my approach to this would be to first capture current time, but only for the first count (do your direction math in the interrupt and make count + or -)
increment a volatile count variable, and capture the time after the reading,

then in your main loop, it reads out the counts, and the 2 times to normal variables quickly then resets the interrupt count and time, freeing it up to continue counting

this then gives you an average if you receive many on a slow loop, and for a fast loop, you would run a rolling average, to determine velocity,

 

Offline nessatse

  • Regular Contributor
  • *
  • Posts: 98
  • Country: za
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #5 on: September 13, 2013, 05:28:12 pm »
Here is a snippet that I wrote a while ago. It is specifically written for the common (cheap) rotary encoders that give four counts per detent.  The count handles two encoders and you can set independent velocity based increments for each one.  There are three velocity thresholds and associated increment values, coarse, medium and fine.


I do not use pin change interrupts, but do everything in a 1kHz timer interrupt, it makes it a lot easier in my opinion.
 
PS.  This is not a library, I just hacked bits out of my program and added a few comments, so it quite possible that I may have omitted a few pertinent bit, in which case I apologise.

PPS.  This code is for AVR & gcc, but most of it pretty generic, do it should easily translate to another architecture

Code: [Select]
// encoder 1 & 2 pins


#define SW1A PB0
#define SW1B PB1
#define SW2A PC2
#define SW2B PC3


// Timer ticks per second


#define TICKTIME 1000


// Encoder limits and increments
// 4 transitions for 1 count
#define ENDCODERSCALE 4
// Max encoder count
#define ENCODERMAX (3000)
// These are the value increments seen by the main program for the
// three velocity ranges
#define ENC1_FINE 1
#define ENC1_MEDIUM 10
#define ENC1_COARSE 50
#define ENC2_FINE 10
#define ENC2_MEDIUM 20
#define ENC2_COARSE 50


// velocity thresholds for encoders
// Speed is actually number of ticks between clicks
#define SPEED_LOW   (TICKTIME/2)
#define SPEED_MED   (TICKTIME/20)
#define SPEED_HI   (TICKTIME/50)


volatile uint32_t ticks;


// Encoder state transition table
// The table is indexed by a four bit number representing
// the previous and current state of the A&B pins of the encoder
// The value is what we should add to the current encoder count, either +1
// for forward motion, -1 for reverse or 0 for an invalid state transition
uint8_t enc_states[16] PROGMEM = {
    0, //0000
    1, //0001
    -1,//0010
    0, //0011
    -1,//0100
    0, //0101
    0, //0110
    1, //0111
    1, //1000
    0, //1001
    0, //1010
    -1,//1011
    0, //1100
    -1,//1101
    1, //1110
    0  //1111
};


// The state structure for each encoder.  This allows for
// independent velocity dependent increments.  The only bit the
// main routine needs to look at is 'value' which will vary between
// zero and ENCODERMAX


typedef struct {
    uint8_t state;
    volatile uint16_t value;
    uint16_t inc;
    uint16_t inc_fine;
    uint16_t inc_med;
    uint16_t inc_coarse;
    int8_t count;
    uint32_t speed;
    uint32_t last_tick;
} encoder_t;


// Array for the two encoder state structures
encoder_t encoders[2];


// bit 0 and 1 indicates to the main loop that the encoders have changed
// needs to be reset by the main loop after processing
volatile uint8_t encoder_flags;


// Checks if velocity thresholds crossed and adjusts the increment value
static void check_enc_speed(uint8_t enc_no)
{
    encoders[enc_no].speed = ticks - encoders[enc_no].last_tick;
    encoders[enc_no].last_tick = ticks;
    if (encoders[enc_no].speed < SPEED_MED) {
        if (encoders[enc_no].speed < SPEED_HI) {
            encoders[enc_no].inc = encoders[enc_no].inc_coarse;
        } else {
            encoders[enc_no].inc = encoders[enc_no].inc_med;
        }
    } else {
        encoders[enc_no].inc = encoders[enc_no].inc_fine;
    }
}


// Update the encoder value


static void update_encoder(uint8_t enc_no,uint8_t buttons)
{
    encoders[enc_no].count += (int8_t)pgm_read_byte(&(enc_states[buttons]));
    if (encoders[enc_no].count > 3) {
        check_enc_speed(enc_no);
        if (encoders[enc_no].value < (ENCODERMAX-encoders[enc_no].inc)) {
            encoders[enc_no].value+=encoders[enc_no].inc;
        } else {
            encoders[enc_no].value = ENCODERMAX;
        }
        encoder_flags |= 0x01 << enc_no;
        encoders[enc_no].count = 0;
    } else if (encoders[enc_no].count < -3) {
        check_enc_speed(enc_no);
        if (encoders[enc_no].value >= encoders[enc_no].inc) {
            encoders[enc_no].value-=encoders[enc_no].inc;
        } else {
            encoders[enc_no].value = 0;
        }
        encoder_flags |= 0x01 << enc_no;
        encoders[enc_no].count = 0;
    }
    encoders[enc_no].state = (buttons & 0b0011) << 2;
}


// 1 kHz timer interrupt.  Reads encoders and adjusts velocity and values.


ISR (TIMER0_COMPA_vect)
{
    uint8_t state;


    ticks++;


    // check encoder 0
    state = encoders[0].state | (PINB & (_BV(SW1A)|_BV(SW1B)));
    update_encoder(0,state);
    // check encoder 1
    // convoluted logic, but encoder 2 is on portc 2&3
    state = encoders[1].state | ((PINC & (_BV(SW2A)|_BV(SW2B))) >> 2);
    update_encoder(1,state);
}
« Last Edit: September 14, 2013, 07:46:53 am by nessatse »
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8229
  • Country: 00
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #6 on: September 13, 2013, 08:01:21 pm »
This is what I would do:

Code: [Select]
#define ENC_PORT PINB     //encoder on portb
#define ENC_A       (1<<0)  //ch a on pin0
#define ENC_B       (1<<2)  //ch b on pin2

//read an encoder
//+/- indicates rotational speed
signed char enc_read(void) {
  static unsigned char enc_prev=0;  //previous values for enc. lowest 4 bits effective. A(-1) B(-1) A(0) B(0)
  //static signed char enc_count=0; //enc count, if absolute value is required
  unsigned char tmp = ENC_PORT;  //read the port

  enc_prev = enc_prev << 2; //shift enc_prev to make space for new readings
  enc_prev |= ((tmp & ENC_A)?0x02:0) | ((tmp & ENC_B)?0x01:0); //update enc_prev
  return enc_stats[enc_prev & 0x0f];  //if relative count is need
  //return enc_count+=enc_stats[enc_prev & 0x0f]; //if absolute counts are needed
}

You can run it through isrs - with minor changes but the idea is the same.
================================
https://dannyelectronics.wordpress.com/
 

Online BravoV

  • Super Contributor
  • ***
  • Posts: 6253
  • Country: 00
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #7 on: September 14, 2013, 08:31:17 am »
Appreciate the replies and the algorithms.  :-+

Also to nessatse & dannyf, many thanks for the examples, now its time for this noob to digest it, this might take a while.   :P

Offline andersm

  • Super Contributor
  • ***
  • Posts: 1070
  • Country: fi
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #8 on: September 14, 2013, 10:34:01 am »
As a quick sidenote, as your hardware seems to be pretty modern (Cortex-M4F), have you checked to see if the hardware supports reading encoders directly? It's usually a special mode of the timer/counter hardware.

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 17998
  • Country: nl
    • NCT Developments
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #9 on: September 14, 2013, 11:45:29 am »
Interrupts won't work very well because the encoders can bounce pretty bad. Its better to poll using a timer interrupt unless the controller has hardware support for an encoder.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

alm

  • Guest
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #10 on: September 14, 2013, 11:55:20 am »
I wouldn't use pin change interrupts unless it was with an optical encoder which doesn't bounce.
 

Offline mrflibble

  • Super Contributor
  • ***
  • Posts: 1947
  • Country: nl
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #11 on: September 14, 2013, 12:07:01 pm »
Interrupts won't work very well because the encoders can bounce pretty bad. Its better to poll using a timer interrupt unless the controller has hardware support for an encoder.

On MSP430 I do exactly this, since the average encoder has a nice bit of bounce in it. Have a shift register that samples the input and then debounce it. On STM32 I use the hardware support for quadrature encoders on the timer modules.
 

Online BravoV

  • Super Contributor
  • ***
  • Posts: 6253
  • Country: 00
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #12 on: September 16, 2013, 03:34:23 am »
As a quick sidenote, as your hardware seems to be pretty modern (Cortex-M4F), have you checked to see if the hardware supports reading encoders directly? It's usually a special mode of the timer/counter hardware.

Yes, I'm aware of that, latest TI Tiva cortex m4f which I have has  two dedicated hardware based quadrature encoder interface (QEI) module in it, also I have the older Stellaris which they stripped down version which doesn't have that.

Its just I'm eager to learn the code/flow/algorithm from experienced fellows.


Interrupts won't work very well because the encoders can bounce pretty bad. Its better to poll using a timer interrupt unless the controller has hardware support for an encoder.
I wouldn't use pin change interrupts unless it was with an optical encoder which doesn't bounce.

Thanks for the advice. Actually I learned my hard lesson well when naively I tried with cheap mechanical encoder that bounces like hell, that made me have to add dedicated external classic 555 ics to debounce it.  |O
Currently I'm planning to use optical version.


On MSP430 I do exactly this, since the average encoder has a nice bit of bounce in it. Have a shift register that samples the input and then debounce it. On STM32 I use the hardware support for quadrature encoders on the timer modules.

Any chance I can take a peek at your code too ?
« Last Edit: September 16, 2013, 06:59:42 am by BravoV »
 

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8229
  • Country: 00
Re: Any good examples on rotary encoder library "WITH" velocity ?
« Reply #13 on: September 16, 2013, 10:27:24 am »
Quote
cheap mechanical encoder that bounces like hell

Nothing a little rc filter couldn't cure. Look up its datasheet.
================================
https://dannyelectronics.wordpress.com/
 

Offline mrflibble

  • Super Contributor
  • ***
  • Posts: 1947
  • Country: nl
Re: Any good examples on ISR based rotary encoder library "WITH" velocity ?
« Reply #14 on: September 16, 2013, 11:38:34 am »
Any chance I can take a peek at your code too ?

For STM32 I use something like this:

quadrature_encoder.cpp
Code: [Select]
#include "quadrature_encoder.hpp"

#include "ch.h"
#include "hal.h"

QuadratureEncoder::QuadratureEncoder(void)
{
}

//
// http://stackoverflow.com/questions/15203069/stm32-rotary-encoder-config-on-tim4
// http://forum.chibios.org/phpbb/viewtopic.php?f=2&t=247&start=40
// http://tronixstuff.wordpress.com/2010/06/26/getting-started-with-arduino-chapter-eleven/
// [url]https://www.sparkfun.com/products/9117[/url]
//
// PB.4 = TIM3 CH1
// PB.5 = TIM3 CH2
//
void QuadratureEncoder::Init(void)
{
//  GPIO_PinAFConfig (GPIOB, GPIO_PinSource4, GPIO_AF_TIM3); // PB.4 = TIM3 CH1
//  GPIO_PinAFConfig (GPIOB, GPIO_PinSource5, GPIO_AF_TIM3); // PB.5 = TIM3 CH2
palSetPadMode(GPIOB, 4, PAL_MODE_ALTERNATE(2) | PAL_STM32_PUDR_PULLUP | PAL_STM32_OSPEED_HIGHEST); /* AF2 = TIM3 */
palSetPadMode(GPIOB, 5, PAL_MODE_ALTERNATE(2) | PAL_STM32_PUDR_PULLUP | PAL_STM32_OSPEED_HIGHEST); /* AF2 = TIM3 */

//  RCC_APB1PeriphClockCmd (RCC_APB1Periph_TIM3, ENABLE);
    RCC->APB1ENR |= (1 <<  1); // TIM3

    //
    // RM0090, page 408
    // 011: Encoder mode 3 - Counter counts up/down on both TI1FP1 and TI2FP2 edges depending on the level of the other input.
    //
    // http://stackoverflow.com/questions/15203069/stm32-rotary-encoder-config-on-tim4
    // I have used an STM32F407 to read encoder counts from 3 optical encoders. I am using ChibiOS RTOS so the timer struct is
    // slightly different from the ST Peripheral library timer struct, but the information is basically the same. Here is how I
    // configure the registers of the actual timers:
    //
stm32_tim_t *timer = STM32_TIM3;
timer->SMCR  = 3;          // Encoder mode 3
timer->CCER  = 0;          // rising edge polarity
timer->ARR   = 0xFFFF;     // count from 0-ARR or ARR-0
timer->CCMR1 = 0xC1C1;     // f_DTS/16, N=8, IC1->TI1, IC2->TI2
timer->CNT   = 0;          // Initialize counter
timer->EGR   = 1;          // Generate an update eventcd
timer->CR1   = 1;          // Enable the counter

prev_encoder_count = GetCount();
}

int16_t QuadratureEncoder::GetCount(void)
{
return (int16_t) TIM3->CNT;
}

int16_t QuadratureEncoder::GetIncrement(void)
{
int16_t encoder_count = GetCount();
int16_t increment     = encoder_count - prev_encoder_count;
prev_encoder_count    = encoder_count;
return increment;
}

quadrature_encoder.hpp
Code: [Select]
#ifndef QUADRATURE_ENCODER_HPP_
#define QUADRATURE_ENCODER_HPP_

#include "ch.h"

class QuadratureEncoder
{
private:
    int16_t prev_encoder_count;
    int16_t GetCount(void);

public:
    QuadratureEncoder(void);
    void Init(void);
    int16_t GetIncrement(void);
//    void Run(void);
//    void SetCallback(void (*cb)(void));
};

Then in main you have:
Code: [Select]
static QuadratureEncoder myQuadratureEncoder;

int main(void) {
// ..
    myQuadratureEncoder.Init();


And then in your favorite timer routine you call myQuadratureEncoder.GetIncrement().
 

Online BravoV

  • Super Contributor
  • ***
  • Posts: 6253
  • Country: 00
Re: Any good examples on rotary encoder library "WITH" velocity ?
« Reply #15 on: September 17, 2013, 04:42:57 am »
mrflibble, thank you !  :-+

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 17998
  • Country: nl
    • NCT Developments
Re: Any good examples on rotary encoder library "WITH" velocity ?
« Reply #16 on: September 17, 2013, 10:01:04 am »
That is pretty complicated. You just need to look for edges on one line 'A' and then check the state of line 'B'. Executing two 'if' statements is enough.
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Online BravoV

  • Super Contributor
  • ***
  • Posts: 6253
  • Country: 00
Re: Any good examples on rotary encoder library "WITH" velocity ?
« Reply #17 on: September 17, 2013, 11:48:57 am »
TerminalJack505, wow, thank you very much for the examples, really love the state diagram too, really handy for a noob like me !  :-+

That is pretty complicated. You just need to look for edges on one line 'A' and then check the state of line 'B'. Executing two 'if' statements is enough.
Nothing at all complicated about it.  It's pretty much software development 101.

Ok, when it comes to these, just sitting quietly at the corner cause I'm too noob to comment/argue/debate which is better, just observe and learn should this turn into "quality" discussion on this routine.  :P

Nctnico, if you have any examples from your past experiences/projects related to this, really appreciate if you could share a snippet on that encoder part, no need to tidy it up, just copy & paste on the core section, I will learn to read & digest it from there.

Offline dannyf

  • Super Contributor
  • ***
  • Posts: 8229
  • Country: 00
Re: Any good examples on rotary encoder library "WITH" velocity ?
« Reply #18 on: September 17, 2013, 12:46:18 pm »
Quote
really love the state diagram too

You don't really need that - the 4-bit array posted earlier is a nice implementation of that.
================================
https://dannyelectronics.wordpress.com/
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1205
  • Country: 00
Re: Any good examples on rotary encoder library "WITH" velocity ?
« Reply #19 on: September 17, 2013, 12:57:43 pm »
Offending messages removed.
 

Offline airiclenz

  • Contributor
  • Posts: 28
  • Country: se
    • Airic Lenz
Re: Any good examples on rotary encoder library "WITH" velocity ?
« Reply #20 on: September 17, 2013, 01:06:40 pm »
Here is a pretty good example for the Arduino. It is well explained and can easily be ported to other systems:

http://www.buxtronix.net/2011/10/rotary-encoders-done-properly.html

Good luck,
Airic
 

Offline mrflibble

  • Super Contributor
  • ***
  • Posts: 1947
  • Country: nl
Re: Any good examples on rotary encoder library "WITH" velocity ?
« Reply #21 on: September 17, 2013, 01:46:10 pm »
Offending messages removed.

Too bad you removed the FSM as well, because now the responses to that make no sense. Besides, sometimes looking at it from a state machine angle can be helpful/instructive/fun.
 

Offline nctnico

  • Super Contributor
  • ***
  • Posts: 17998
  • Country: nl
    • NCT Developments
Re: Any good examples on rotary encoder library "WITH" velocity ?
« Reply #22 on: September 17, 2013, 01:56:59 pm »
Nctnico, if you have any examples from your past experiences/projects related to this, really appreciate if you could share a snippet on that encoder part, no need to tidy it up, just copy & paste on the core section, I will learn to read & digest it from there.
This is a cut down snippet which is called from a timer interrupt. In order to be get the data from the IRQ process space into the user process space without disabling the interrupts / missing steps I use two accumulators to count the cw and ccw steps. In the user process I simply calculate the difference between the current accumulator values and the previous values.

Velocity can be added by adding a counter which counts the number of calls between two edges. The variable a can be increased when the counter reaches smaller values.

static uint32_t rotation_accu_cw, rotation_accu_ccw;

void rotary_handler()
{
   static uint32_t prev_rotary_a;
   uint32_t a=1;

   if (prev_rotary_a != GET_ROTARY_A())
      {
      //edge
      prev_rotary_a = GET_ROTARY_A();
               if (prev_rotary_a==0)
         {
         //falling edge
         if (GET_ROTARY_B()!=0)
            {
            rotation_accu_cw+=a;
            }
         else
            {
            rotation_accu_ccw+=a;
            }
         }
      else
         {
         //rising edge
         if (GET_ROTARY_B()==0)
            {
            rotation_accu_cw+=a;
            }
         else
            {
            rotation_accu_ccw+=a;
            }
         }
      }
}
« Last Edit: September 17, 2013, 01:59:05 pm by nctnico »
There are small lies, big lies and then there is what is on the screen of your oscilloscope.
 

Offline TerminalJack505

  • Super Contributor
  • ***
  • Posts: 1205
  • Country: 00
Re: Any good examples on rotary encoder library "WITH" velocity ?
« Reply #23 on: September 17, 2013, 02:15:28 pm »
Offending messages removed.

Too bad you removed the FSM as well, because now the responses to that make no sense. Besides, sometimes looking at it from a state machine angle can be helpful/instructive/fun.

Sorry.  That's the only time I've ever deleted any of my posts.  I know how it messes up the preceding discussion.

I thought I could add something to the discussion but apparently anything more complicated than a "Hello, World!" solution is too complicated.   |O
 

Offline Tepe

  • Frequent Contributor
  • **
  • Posts: 544
  • Country: dk
Re: Any good examples on rotary encoder library "WITH" velocity ?
« Reply #24 on: September 17, 2013, 02:38:39 pm »
Offending messages removed.

Too bad you removed the FSM as well, because now the responses to that make no sense. Besides, sometimes looking at it from a state machine angle can be helpful/instructive/fun.

Sorry.  That's the only time I've ever deleted any of my posts.  I know how it messes up the preceding discussion.

I thought I could add something to the discussion but apparently anything more complicated than a "Hello, World!" solution is too complicated.   |O

Are you willing to insert them in the post again?
ceterum censeo systemd-inem esse delendam
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf