Motor speed feedback using encoder


I am attempting to measure close to real-time speed of a motor using an encoder attached to the motor gearbox. I have setup an external interrupt to fire whenever there is a rising edge from the sensor. It seems like it measures the motor speed okay, but every 5-10 speed values vary greatly from the rest. It gets worse as the motor speed is increased.

I set it up to calculate the time between interrupt calls (sensor rising edges) in microseconds. I averaged the most recent 4 periods of the time between rising edges from the sensor (in microseconds) and then multiplied the reciprocal of this value by a constant to calculate the speed. Why is my measurement not accurate? To verify the output of the optical sensor, I checked the output with an oscilloscope and the signal is clean and constant. When the motor as at a medium-low speed, the period of the actual signal from the sensor is 2.3 ms. Any help would be greatly appreciated. Below is part of my code.

period = 1000000
start = utime.ticks_us()        
def callback(line):                    # Encoder interrupt handler
    global period, start
    period = utime.ticks_diff(utime.ticks_us(),start)    # I think the order of these arguments contradicts what the datasheet says but if they are reversed, it returns a negative value.
    start = utime.ticks_us()
extint = pyb.ExtInt("P3", pyb.ExtInt.IRQ_RISING, pyb.Pin.PULL_NONE, callback)


    if (period > 100000):     # prevents division by 0.
        actual_speed = 0
        actual_speed = 10000/period
        actual_speed = ((speedBuffer[0]+speedBuffer[1]+speedBuffer[2]+speedBuffer[3])/4)

Hi, please see this post it might help:

Hi iabdalkader,

Thank you for the quick reply. I have looked over this code and it seems like it is focused on motor position rather than speed. If I wanted to calculate the speed, would I set up another interrupt to fire say every 200 us and then divide the position by this to get the speed? Then reset the position counter and start this process over?

You don’t need interrupts to count pulses, the timer does that in encoder mode (see the linked code), you could then use one interrupt to calculate the speed just like you said. EDIT: I think it sometimes skips because other higher-priority interrupts are running.

Okay thanks.

That makes sense. Encoder mode may work, however, I am not using quadrature sensing. So, I started looking into the Timer.IC mode, and from what I can tell, this will calculate the time between rising or falling edges from a single sensor input. This is exactly what I need.

When I implemented this method, I got a warning back that said, “ValueError: Pin(B12) doesn’t have an af for Timer(2)”

I am not sure why this is coming up. I have tried both timer 2 and 5, and I got similar errors. Am I doing this incorrectly? I really appreciate all of your help!


           # Timer number, clock divisor, reset count
tim2 = Timer(2,prescaler=83, period=0x3fffffff)
           # channel, mode, pin, polarity
ch3 =, pyb.Timer.IC, pin=pyb.Pin("P3"), polarity=pyb.Timer.RISING)

B12 (P3) doesn’t have timer 2 on it.

What pins do you have available?

Here are the pin alt methods.