WiringPi und GPIO verträglichkeitsproblem?

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
Dominikin9
User
Beiträge: 13
Registriert: Mittwoch 4. Januar 2017, 17:39

Guten Abend!

Ich bin gerade dabei eine Motorsteuerung zu programmieren.
Die Motoren haben Magnetic Encoder oben und fürs auslesen benutze ich eine Library.
Um die Motoren anzusteuern benutze ich auch eine Library.

Als ich dann alles in einem Quellcode Geschieben hab und ausprobiert hab, bekam ich den Fehler:
wiringPiSetup: You must only call this once per program run. This is a fatal error. Please fix your code.*

Das ist der Quellcode und als ich die Befehle für den Motor auskommentiert hab, hat es funktioniert.

Kann mir wer vielleicht erklären und helfen wie ich den Fehler beheben kann?
Danke im Vorraus :D.

Code: Alles auswählen

motors.enable()
gpioen = gaugette.gpio.GPIO() 
#encoder eins
A_PIN = 7 
B_PIN = 9	
encoder = gaugette.rotary_encoder.RotaryEncoder(gpioen, A_PIN, B_PIN)
encoder.start()
Motorlibrary:

Code: Alles auswählen

import wiringpi

# Motor speeds for this library are specified as numbers
# between -MAX_SPEED and MAX_SPEED, inclusive.
_max_speed = 480  # 19.2 MHz / 2 / 480 = 20 kHz
MAX_SPEED = _max_speed

io_initialized = False
def io_init():
  global io_initialized
  if io_initialized:
    return

  wiringpi.wiringPiSetupGpio()
  wiringpi.pinMode(12, wiringpi.GPIO.PWM_OUTPUT)
  wiringpi.pinMode(13, wiringpi.GPIO.PWM_OUTPUT)

  wiringpi.pwmSetMode(wiringpi.GPIO.PWM_MODE_MS)
  wiringpi.pwmSetRange(MAX_SPEED)
  wiringpi.pwmSetClock(2)

  wiringpi.pinMode(22, wiringpi.GPIO.OUTPUT)
  wiringpi.pinMode(23, wiringpi.GPIO.OUTPUT)
  wiringpi.pinMode(24, wiringpi.GPIO.OUTPUT)
  wiringpi.pinMode(25, wiringpi.GPIO.OUTPUT)

  io_initialized = True

class Motor(object):
    MAX_SPEED = _max_speed

    def __init__(self, pwm_pin, dir_pin, en_pin):
        self.pwm_pin = pwm_pin
        self.dir_pin = dir_pin
        self.en_pin = en_pin

    def enable(self):
        io_init()
        wiringpi.digitalWrite(self.en_pin, 1)

    def disable(self):
        io_init()
        wiringpi.digitalWrite(self.en_pin, 0)

    def setSpeed(self, speed):
        if speed < 0:
            speed = -speed
            dir_value = 1
        else:
            dir_value = 0

        if speed > MAX_SPEED:
            speed = MAX_SPEED

        io_init()
        wiringpi.digitalWrite(self.dir_pin, dir_value)
        wiringpi.pwmWrite(self.pwm_pin, speed)

class Motors(object):
    MAX_SPEED = _max_speed

    def __init__(self):
        self.motor1 = Motor(12, 24, 22)
        self.motor2 = Motor(13, 25, 23)

    def enable(self):
        self.motor1.enable()
        self.motor2.enable()

    def disable(self):
        self.motor1.disable()
        self.motor2.disable()

    def setSpeeds(self, m1_speed, m2_speed):
        self.motor1.setSpeed(m1_speed)
        self.motor2.setSpeed(m2_speed)

motors = Motors()
Magnetic Encoder Library:

Code: Alles auswählen

#----------------------------------------------------------------------
# rotary_encoder.py from https://github.com/guyc/py-gaugette
# Guy Carpenter, Clearwater Software
#
# This is a class for reading quadrature rotary encoders
# like the PEC11 Series available from Adafruit:
#   http://www.adafruit.com/products/377
# The datasheet for this encoder is here:
#   http://www.adafruit.com/datasheets/pec11.pdf
#
# This library expects the common pin C to be connected
# to ground.  Pins A and B will have their pull-up resistor
# pulled high.
#
# Usage:
#
#     import gaugette.rotary_encoder
#     A_PIN = 7  # use wiring pin numbers here
#     B_PIN = 9
#     gpio = gaugette.gpio.GPIO()
#     encoder = gaugette.rotary_encoder.RotaryEncoder(gpio, A_PIN, B_PIN)
#     while 1:
#       delta = encoder.get_delta() # returns 0,1,or -1
#       if delta!=0:
#         print delta

import math
import threading
import time

class RotaryEncoder:

    #----------------------------------------------------------------------
    # Pass the wiring pin numbers here.  See:
    #  https://projects.drogon.net/raspberry-p ... gpi2/pins/
    #----------------------------------------------------------------------
    def __init__(self, gpio, a_pin, b_pin):
        self.gpio = gpio
        self.a_pin = a_pin
        self.b_pin = b_pin

        self.gpio.setup(self.a_pin, self.gpio.IN, self.gpio.PUD_UP)
        self.gpio.setup(self.b_pin, self.gpio.IN, self.gpio.PUD_UP)

        self.steps = 0
        self.last_delta = 0
        self.r_seq = self.rotation_sequence()

        # steps_per_cycle and self.remainder are only used in get_cycles which
        # returns a coarse-granularity step count.  By default
        # steps_per_cycle is 4 as there are 4 steps per
        # detent on my encoder, and get_cycles() will return a signed
        # count of full detent steps.
        self.steps_per_cycle = 4
        self.remainder = 0

    # Gets the 2-bit rotation state of the current position
    # This is deprecated - we now use rotation_sequence instead.
    def rotation_state(self):
        a_state = self.gpio.input(self.a_pin)
        b_state = self.gpio.input(self.b_pin)
        r_state = a_state | b_state << 1
        return r_state

    # Returns the quadrature encoder state converted into
    # a numerical sequence 0,1,2,3,0,1,2,3...
    #
    # Turning the encoder clockwise generates these
    # values for switches B and A:
    #  B A
    #  0 0
    #  0 1
    #  1 1
    #  1 0
    # We convert these to an ordinal sequence number by returning
    #   seq = (A ^ B) | B << 2
    #
    def rotation_sequence(self):
        a_state = self.gpio.input(self.a_pin)
        b_state = self.gpio.input(self.b_pin)
        r_seq = (a_state ^ b_state) | b_state << 1
        return r_seq

    def update(self):
        delta = 0
        r_seq = self.rotation_sequence()
        if r_seq != self.r_seq:
            delta = (r_seq - self.r_seq) % 4
            if delta == 3:
                delta = -1
            elif delta == 2:
                delta = int(math.copysign(delta, self.last_delta))  # same direction as previous, 2 steps

            self.last_delta = delta
            self.r_seq = r_seq
        self.steps += delta

    def get_steps(self):
        steps = self.steps
        self.steps = 0
        return steps

    # get_cycles returns a scaled down step count to match (for example)
    # the detents on an encoder switch.  If you have 4 delta steps between
    # each detent, and you want to count only full detent steps, use
    # get_cycles() instead of get_delta().  It returns -1, 0 or 1.  If
    # you have 2 steps per detent, set encoder.steps_per_cycle to 2
    # before you call this method.
    def get_cycles(self):
        # python negative integers do not behave like they do in C.
        #   -1 // 2 = -1 (not 0)
        #   -1 % 2 =  1 (not -1)
        # // is integer division operator.  Note the behaviour of the / operator
        # when used on integers changed between python 2 and 3.
        # See http://www.python.org/dev/peps/pep-0238/
        self.remainder += self.get_steps()
        cycles = self.remainder // self.steps_per_cycle
        self.remainder %= self.steps_per_cycle # remainder always remains positive
        return cycles

    def start(self):
        def isr():
            self.update()
        self.gpio.trigger(self.a_pin, self.gpio.EDGE_BOTH, isr)
        self.gpio.trigger(self.b_pin, self.gpio.EDGE_BOTH, isr)

    class Worker(threading.Thread):
        def __init__(self, gpio, a_pin, b_pin):
            threading.Thread.__init__(self)
            self.lock = threading.Lock()
            self.stopping = False
            self.encoder = RotaryEncoder(gpio, a_pin, b_pin)
            self.daemon = True
            self.delta = 0
            self.delay = 0.001

        def run(self):
            while not self.stopping:
                self.encoder.update()
                time.sleep(self.delay)

        def stop(self):
            self.stopping = True

        def get_steps(self):
            return self.encoder.get_steps()

Mit freundlichen Grüßen
Dominic
BlackJack

@Dominikin9: So ein bisschen vom dem Code kennen wir ja schon aus Motorkarte Library. Du verwendest da immer noch ``global`` und rufst eine Initialisierungsfunktion mehrfach auf. Ich weiss jetzt nicht ob es tatsächlich daran liegt, aber `threading` kommt in dem Programm auch vor, und bei der Kombination verliere ich schon die Lust das überhaupt durchzulesen.
Dominikin9
User
Beiträge: 13
Registriert: Mittwoch 4. Januar 2017, 17:39

Ja stimmt genau und bei dem was ich geschrieben habe benutze ich keine globalen variablen mehr.
Die unteren beiden Codes stammen nicht von mir, der erste ist von dem Hersteller meiner Motorkarte (Pololu) und der zweite
wurde mir empfohlen damit ich die Encoder auslesen kann.

Und wo wird die Initialisierung zwei mal aufgerufen?

MfG Dominic
BlackJack

@Dominikin9: `gaugette` verwendet ja auch `wiringpi` also muss dort ja auch irgendwo die Setupfunktion aufgerufen werden.
Dominikin9
User
Beiträge: 13
Registriert: Mittwoch 4. Januar 2017, 17:39

@BlackJack Aja stimmt genau, dran hab ich überhaupt nicht gedacht.
Danke für den Tipp das wird es sicher sein.

Aber die Frage ist wie kann ich jetzt im Hauptcode das Setup machen, damit es die Library auch verwendet?
Bzw `gaugette` verwendet `wiringpi` ja nicht nur die Motorlibrary oder irre ich mich da gerade?

MfG Dominic
Antworten