0
\$\begingroup\$

I'm working with a BeagleBone Black and using Adafruit's IO Python library. Wrote a simple quadrature decoding function and it works perfectly fine when the motor runs at about 1800 RPM. But when the motor runs at higher speeds, the code starts missing some of the interrupts and the encoder counts start to accumulate errors. Do you guys have any suggestions as to how I can make the code more efficient or if there are functions which can cycle the interrupts at a higher frequency.

Thanks, Kel

Here's the code:

# Define encoder count function
def encodercount(term):
global counts       
global Encoder_A
global Encoder_A_old
global Encoder_B
global Encoder_B_old
global error


Encoder_A = GPIO.input('P8_7')  # stores the value of the encoders at time of interrupt
Encoder_B = GPIO.input('P8_8')

if Encoder_A == Encoder_A_old and Encoder_B == Encoder_B_old:
# this will be an error
    error += 1
    print 'Error count is %s' %error

elif (Encoder_A == 1 and Encoder_B_old == 0) or (Encoder_A == 0 and Encoder_B_old == 1):
# this will be clockwise rotation
    counts += 1
    print 'Encoder count is %s' %counts
    print 'AB is %s %s' % (Encoder_A, Encoder_B)

elif (Encoder_A == 1 and Encoder_B_old == 1) or (Encoder_A == 0 and Encoder_B_old == 0):
# this will be counter-clockwise rotation
    counts -= 1
    print 'Encoder count is %s' %counts
    print 'AB is %s %s' % (Encoder_A, Encoder_B)

else:
#this will be an error as well
    error += 1
    print 'Error count is %s' %error

Encoder_A_old = Encoder_A     # store the current encoder values as old values to be used as comparison in the next loop
Encoder_B_old = Encoder_B       

# Initialize the interrupts - these trigger on the both the rising and falling 
GPIO.add_event_detect('P8_7', GPIO.BOTH, callback = encodercount)   # Encoder A
GPIO.add_event_detect('P8_8', GPIO.BOTH, callback = encodercount)   # Encoder B

# This is the part of the code which runs normally in the background
while True:
    time.sleep(1)
\$\endgroup\$
2
\$\begingroup\$

Note that the BBB has three hardware quadrature decoders on-chip, and two appear to be accessible via the connectors. Look for eQEP functions. Some support software appears to be available now for these, search for Beaglebone black quadrature, etc.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ I didn't look up the information (if the eQEP are available) but if they are, this is the only correct solution. Don't try to solve a hard-realtime problem in software. Use the hardware that is given and load it off to the dedicated hardware. \$\endgroup\$ – Tom L. Sep 23 '15 at 4:29
1
\$\begingroup\$

I would recommend offloading it to a small FPGA or CPLD, then reading the counts out via a serial port of some sort - e.g. SPI. A small Spartan FPGA could keep track of a large number of encoders running at high speed, and the processor can simply read out the counters as necessary. GPIO is not designed for this application. Unless you want to dedicate an entire core just sitting on the pins and polling continuously with a while loop, the best solution is to offload this to an external device.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Ah, a hardware solution. I've done the decoding with discrete digital IC's. (it takes a surprising number of them, but it's fast) \$\endgroup\$ – George Herold Aug 22 '14 at 12:26
0
\$\begingroup\$

You have quite a few logical tests joined together, which will slow things down:

if (Encoder_A == 1 and Encoder_B_old == 0) or (Encoder_A == 0 and Encoder_B_old == 1):

That one line requires SEVEN tests (==, and, ==, or, ==, and, ==).

Also, this doesn't seem like a true interrupt routine as you are explicitly having to check if the pins really have changed state at the top. If this was a real interrupt triggered by a pin change then that test is redundant.

Print statements in an interrupt routine are a major no-no in situations like this as they slow the whole routine down, printing on the console takes time - in fact, ANY call to an external function takes time. Set a flag and move on, run the print routine somewhere else by checking the flag, and don't let it weigh your interrupt down.

I don't know how efficiently Python works and which patterns may be "most optimum" but I would (in C) be inclined to something like this:

if(a != old_a) // A has changed
{
    if(a == 1)
    {
        if(b == 0)
        {
            count++; // Encoder moved fwd
        }
        else // We don't care what else, anything is an error
        {
            error; // b cannot == a
        }
    }
    else // Must be 0, no need to check
    {
        if(b == 1)
        {
            count--; // Encoder moved back
        }
        else
        {
            error; // b cannot == a
        }
    }
}
else if(b != old_b) // B has changed
{
    if(b == 1)
    {
        if(a == 0)
        {
            count++; // Encoder moved fwd
        }
        else
        {
            error; // b cannot == a
        }
    }
    else
    {
        if(a == 1)
        {
            count--; // Encoder moved bbck
        }
        else
        {
            error; // b cannot == a
        }
    }
}
else // neither has changed
{
    error;
}
old_a = a;
old_b = b;

You may notice that this is nested so that the processor does the minimum number of tests to arrive at the result, and we try to fall out to an error as a by-product of being unsuccessful rather than explicitly catch errors by careful (and time-consuming) tests.

Without knowing how efficiently / smartly Python compiles it's hard to guess on more efficient ways of doing this.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.