pasaxet
Posts: 4
Joined: Fri Nov 21, 2014 10:00 pm

Limits of Multithreading

Fri Nov 21, 2014 10:14 pm

Hi everyone,

Let me preface this by saying that I am not an expert on programming, processors, or anything. I'm just a college student learning as I go. I am working on a project that requires reading data from a set of digital calipers as seen here: http://robocombo.blogspot.com/2010/12/u ... rface.html

The good news is that I have successfully figured out how to interpret the binary data from the calipers into an integer. Right now, the code displays an integer on the terminal exactly as it appears on the calipers. It reads the signals from the caliper through two GPIO pins and decodes the data. Great.

This project also involves controlling a binary stepper motor from the same Raspberry Pi. This involves driving output signals across five other GPIO pins. Because of this, my original plan was to create a subclass of the Threading module called Caliper that would run the code to read the signal from the calipers in the background while the motor controller ran in the foreground. This Caliper subclass would provide some kind of method called read() or something that would spit out the integer value whenever I need it. This would be used in a feedback loop to move the motor with precision.

However, I've run into an unfortunate problem. While testing my Caliper subclass, it seemed to be going too slow. The caliper sends out little "packets" of data in 24-bit bursts, and while the Pi was able to handle this just fine as the main thread, it was only picking up maybe a third of the bits while running in the background. I did a little research and what I believe to be the problem is that the Pi has only one core and is thus incapable of hardware multi-threading, and software multi-threading eats up too much time with context switching (I believe there is something called a Global Interpreter Lock [GIL] or something?)

It looks like my only choice is to use some kind of external MCU to read the calipers, and then set up some kind of serial connection (I was thinking I2C) to the Raspberry Pi. That way the Pi can just poll the I2C connection for the caliper data while still running the motor. However, this is highly unappealing since we have a big deadline coming up, and frankly I don't think I have the time or skill to learn how to code in embedded C in that amount of time (tutorials and documentation are severely lacking).

So before I go down that road, I wanted to ask here if there is any workaround for this problem that would allow me to continue to run all of the code on a single Raspberry Pi. Again, I don't know much about advanced programming concepts or processors/multithreading, etc. so it's difficult for me to ask for specifics. But any help at all would be greatly appreciated.

Thanks!

I can post my code upon request, if that would be helpful.

jamesh
Raspberry Pi Engineer & Forum Moderator
Raspberry Pi Engineer & Forum Moderator
Posts: 24188
Joined: Sat Jul 30, 2011 7:41 pm

Re: Limits of Multithreading

Sat Nov 22, 2014 12:05 pm

The Pi is very much capable of multithreading/multitasking, and the ARM core has instruction specifically for multithreading. It does it all the time just running Linux with hundreds of threads running at the same time.

I suspect an issue in your code, perhaps one thread is waiting for another and causing delays? Multithreaded code is not easy to get right.

The Pi is runing at 700Mhz, (700 million instructions per second approximately), so should be more than capable of doing what you need. Perhaps overclocking may help?

Are you using a compiled or interpreted language (eg. C or Python?). Python because of the massive overheads of running interpreted, may exhibit the issues you are seeing, which will not be there in C.
Principal Software Engineer at Raspberry Pi (Trading) Ltd.
Contrary to popular belief, humorous signatures are allowed. Here's an example...
“I think it’s wrong that only one company makes the game Monopoly.” – Steven Wright

User avatar
joan
Posts: 14473
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Limits of Multithreading

Sat Nov 22, 2014 12:17 pm

pigpio would have no problem implementing your requirements in C or Python (or any other language you fancied).

pasaxet
Posts: 4
Joined: Fri Nov 21, 2014 10:00 pm

Re: Limits of Multithreading

Sat Nov 22, 2014 8:00 pm

jamesh, I am running it in Python. It is very likely that my problem is simply due to poor coding: as I said, I'm fairly inexperienced in this area. I'm going to try the pigpio library joan suggested above and if that doesn't work I'll look into writing the program in C instead.

joan, I just started looking into pigpio and it looks very promising. I'm going to try this out tomorrow and I'll report the results. Thanks!

User avatar
joan
Posts: 14473
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Limits of Multithreading

Sat Nov 22, 2014 8:13 pm

pasaxet wrote: ...
I'm going to try this out tomorrow and I'll report the results. Thanks!
To do accurate timing you'll need to use the callback functionality.

User avatar
Richard-TX
Posts: 1549
Joined: Tue May 28, 2013 3:24 pm
Location: North Texas

Re: Limits of Multithreading

Sun Nov 23, 2014 2:05 pm

Writing your program in c and NOT using 3rd party libraries will yield the best performance. All of the 3rd party libraries slow things down significantly. Some are worse than others. The trick is to pick a library that has enough performance so that your app runs correctly. I do not use 3rd party libraries.
Richard
Doing Unix since 1985.
The 9-25-2013 image of Wheezy can be found at:
http://downloads.raspberrypi.org/raspbian/images/raspbian-2013-09-27/2013-09-25-wheezy-raspbian.zip

pasaxet
Posts: 4
Joined: Fri Nov 21, 2014 10:00 pm

Re: Limits of Multithreading

Sun Nov 23, 2014 10:33 pm

@joan, I'm testing out my code right now using pigpio and I'm getting the following error

Code: Select all

Traceback (most recent call last):
  File "caliper.py", line 102, in <module>
    c = caliper.Caliper(pi, 21, 17)
  File "/home/pi/caliper.py", line 34, in __init__
    pi.set_mode(clock, pigpio.INPUT)
  File "/usr/local/lib/python2.7/dist-packages/pigpio.py", line 914, in set_mode
    return _u2i(_pigpio_command(self.sl, _PI_CMD_MODES, gpio, mode))
  File "/usr/local/lib/python2.7/dist-packages/pigpio.py", line 709, in _u2i
    raise error(error_text(v))
pigpio.error: 'no permission to update gpio'
I Googled that error and wasn't able to find why it's happening. Can you provide some insight?



The full code is below

Code: Select all


#!/usr/bin/env python

import atexit
import pigpio

class Caliper:
    def __init__(self, pi, clock, data):
        # Initialize GPIO pins
        self.pi = pi
        self.clock = clock
        self.data = data

        self.active = True

        self.cb = None

        atexit.register(self.kill)

        # Two variables for value: one which updates in real time and one
        # which will hold its value through each clock burst
        self.value = -999
        self.last_value = 0

        # Bit counter
        self.bits = 0

        # Updates on each rising edge of clock to keep track of bursts
        #self.last_tick = 0

        # Flag to signal when we're ready to capture data bits
        self.ready = False

        # Set GPIO pins as inputs w/ pull-up resistors
        pi.set_mode(clock, pigpio.INPUT)
        pi.set_pull_up_down(clock, pigpio.PUD_UP)
        pi.set_mode(data, pigpio.INPUT)
        pi.set_pull_up_down(data, pigpio.PUD_UP)

        # Set watchdog timer to 80ms to check for clock rests
        pi.set_watchdog(clock, 80);

        # Initialize callback function for clock pin
        self.cb = pi.callback(clock, pigpio.RISING_EDGE, self._cb)

    def _cb(self, gpio, level, tick):
        if level == pigpio.TIMEOUT:
            # Watchdog timer expired; prepare for new set of data
            self.value = 0
            self.bits = 0
            self.ready = True
        elif level == pigpio.HIGH:
            # Read the data pin first before it has time to change
            new_data = pi.read(self.data)

            self.bits += 1

            """
            diff = pigpio.tickDiff(self.last_tick, tick)
            if diff > 80000:
                self.value = 0
                self.bits = 0
                self.ready = True
            """

            if self.ready:
                if self.bits <= 16: # Only capture first 16 bits
                    if not new_data: # re-invert the signal
                        self.value |= 0x8000 # set MSB to 1
                    self.value = self.value >> 1 # and shift right
                elif self.bits == 21: # bit 21 is parity bit
                    if not new_data: # if parity bit == 0, number is negative
                        self.vaue = (~self.value) + 1
                elif self.bits >= 22: # ignore the rest
                    self.last_value = value
                    self.ready = False

            #self.last_tick = tick

    def get(self):
        if self.active:
            return self.last_value

    def kill(self):
        self.pi.set_watchdog(self.clock, 0)

        if self.cb != None:
            self.cb.cancel()
            self.cb = None

        self.active = False

if __name__ == "__main__":

    import time
    import pigpio
    import caliper

    INTERVAL = 2

    pi = pigpio.pi()

    c = caliper.Caliper(pi, 21, 17)

    r = 0

    next_reading = time.time()

    try:
        while True:

            r += 1

            print("{} {}".format(r, c.get()))

            next_reading += INTERVAL

            time.sleep(next_reading - time.time())
    finally:

        c.kill()

        pi.stop()

User avatar
joan
Posts: 14473
Joined: Thu Jul 05, 2012 5:09 pm
Location: UK

Re: Limits of Multithreading

Sun Nov 23, 2014 10:42 pm

It appears to be saying that you shouldn't be using gpio 21 (clock). pigpio only uses Broadcom numbering for the gpios.

gpio 21 is pin 13 on Rev.1 B's. On Rev.2 B's there is no gpio 21, it is replaced by gpio 27 on pin 13.

Which model of Pi are you using? pigpio thinks it is a Rev. 2 B.
Last edited by joan on Mon Nov 24, 2014 7:22 am, edited 1 time in total.

pasaxet
Posts: 4
Joined: Fri Nov 21, 2014 10:00 pm

Re: Limits of Multithreading

Mon Nov 24, 2014 2:16 am

Ah yes, that seems to be the problem. I had referenced an old (wrong) pin out for the Raspberry Pi that said board pin 13 was 21, not 27. I changed that and the error is gone. Thanks!

User avatar
experix
Posts: 204
Joined: Mon Nov 10, 2014 7:39 pm
Location: Coquille OR
Contact: Website

Re: Limits of Multithreading

Mon Nov 24, 2014 7:48 pm

If the RPi has the chops for your hardware problem, you can do it with experix. This is an interpreted language system that provides all the calculations you might want to do, easy-to-use graph drawing, conditional branching, timers, usb, webcam and socket operations, and (of interest to you) ability to run commands in threads. The threading implementation has minimal overhead. Check it out at experix.sourceforge.net and contact me for help setting it up. One caveat: I haven't gotten around to fiddling with the RPi gpio pins, and don't know how long it might take to add an experix function for that.

User avatar
perfo
Posts: 95
Joined: Mon Jan 30, 2012 2:42 am

Re: Limits of Multithreading

Sat Sep 12, 2015 9:04 pm

I know it's nearly a year old but how did you get on with this project ?
I want to do something similar. I'm my tinkering even though the PI is capable of these things (especially the new speedy version) I prefer to hand off real world real time things to other devices. So I'd be using the pi to interrogate the callipers and work out which and what stepper I want to move then send the commands to move stepper A X number of mm or steps to the dedicated controller this prevents any shenanigans with the pi trying to do the numerous real time boring bits...

Return to “Advanced users”