代码之家  ›  专栏  ›  技术社区  ›  Alessandro Mendolia

用于GPIO Led闪烁的Python线程类

  •  3
  • Alessandro Mendolia  · 技术社区  · 8 年前

    不幸的是,我在尝试了所有方向后来到这里,想摆脱这个问题,但没有任何结果。

    我的想法是:

    我需要一个名为Led的类,该类在构造函数中只接受GPIO引脚并提供以下方法:

    • 灯亮起
    • 灯灭
    • 闪烁

    我所做的:

    我以这种方式构建了这个类:

    import RPi.GPIO as GPIO
    import time
    import threading
    from threading import Thread
    
    
    class Led(Thread):
    
        def __init__(self, led_pin):
            Thread.__init__(self)
            self.pin_stop = threading.Event()
            self.__led_pin = led_pin
            GPIO.setmode(GPIO.BCM)
            GPIO.setup(self.__led_pin, GPIO.OUT)
    
        def low(self, pin):
            GPIO.setup(pin, GPIO.OUT)
            GPIO.output(pin, GPIO.LOW)
    
        def blink(self, time_on=0.050, time_off=1):
            pin = threading.Thread(name='ledblink',target=self.__blink_pin, args=(time_on, time_off, self.pin_stop))
            pin.start()
    
        def __blink_pin(self, time_on, time_off, pin_stop):
            while not pin_stop.is_set():
                GPIO.output(self.__led_pin, GPIO.HIGH)
                time.sleep(time_on)
                GPIO.output(self.__led_pin, GPIO.LOW)
                time.sleep(time_off)
    
        def __stop(self):
            self.pin_stop.set()
    
    
        def reset(self):
            GPIO.cleanup()
    
        def off(self):
            self.__stop()
    
        def on(self):
            self.__stop()
            GPIO.output(self.__led_pin, GPIO.LOW)
            GPIO.output(self.__led_pin, GPIO.HIGH)
    

    其中blink方法负责无限期地闪烁led,直到关闭或打开方法调用。

    然后运行以下简单代码:

    from classes.leds import Led
    import time
    from random import randint
    
    Led16 = Led(16)
    
    def main():
        while True:
            if (randint(0, 1) == 1):
                Led16.blink()
            else:
                Led16.off()
            time.sleep(2)
    
    
    if __name__ == "__main__":
        main()
    

    发生了什么:

    每次调用方法时,Led对象似乎会生成一个新线程,其效果是GPIO线在多个线程之间共享。

    我的愿望是什么:

    我想保持led异步闪烁(显然)并控制Led16()对象状态,也许每次我校准其方法时都不会创建新线程,但到了这一点,我有点困惑。

    4 回复  |  直到 8 年前
        1
  •  2
  •   DisappointedByUnaccountableMod brnfd    8 年前

    你正在创建很多线程,因为你的 blink() 每次调用时都会创建一个新线程,而旧线程不会停止。

    我认为线程有几个选项:

    1. 只创建一次线程-例如在 __init__() -它以闪烁的时间间隔(即大部分时间处于睡眠状态)连续运行,读取实例变量并相应地设置LED。要更改led状态,请 闪烁() , on() off()

    2. 在blink例程中,如果线程已经在运行,则不要创建新线程,或者停止旧线程(等待它完成),然后启动一个新线程。

    你需要处理的事情是,你希望自己的行为是:

    • 如果led熄灭,则只要 on() 闪烁()
    • 如果led闪烁 闪烁() 再次调用。闪烁开/关序列不受干扰
    • 如果闪烁并且 关闭() 如果已经开始运行到完成,则称为“我希望打开循环”,即不应立即关闭led,因为这可能是一个很短的闪光,看起来很奇怪。

    创建一个新线程的关键是等待旧线程完成,而只需在一个线程中创建一次就可以了 __初始化() 并使其连续运行。当led亮起或熄灭时,时间段缩短(至数值 FAST_CYCLE )因此,当led关闭或打开时,它会快速反应,因为 sleep() 是一段很短的时间。

    关于代码的其他几点:

    • Thread -您正在中创建一个新线程 pin=...
    • 如果在编写代码时添加注释,通常会使阅读代码时更容易理解发生了什么。
    • self.pin = threading.Thread pin = threading.Thread reset() 您可以使用 join() 在继续之前确保它已退出
    • 由于闪烁时间段可以更改,并且线程必须使用最新的值,因此每次都使用self读取它们,而不是将它们作为参数传递给 __blink_pin() 例程,如果这样做,您也可以使用self来获取pin_stop信号量。

    类似这样(未经测试):

    import RPi.GPIO as GPIO
    import time
    import threading
    from threading import Thread
    
    class Led(object):
    
        LED_OFF = 0
        LED_ON = 1
        LED_FLASHING = 2
    
        # the short time sleep to use when the led is on or off to ensure the led responds quickly to changes to blinking
        FAST_CYCLE = 0.05
    
        def __init__(self, led_pin):
            # create the semaphore used to make thread exit
            self.pin_stop = threading.Event()
            # the pin for the LED
            self.__led_pin = led_pin
            # initialise the pin and turn the led off
            GPIO.setmode(GPIO.BCM)
            GPIO.setup(self.__led_pin, GPIO.OUT)
            # the mode for the led - off/on/flashing
            self.__ledmode = Led.LED_OFF
            # make sure the LED is off (this also initialises the times for the thread)
            self.off()
            # create the thread, keep a reference to it for when we need to exit
            self.__thread = threading.Thread(name='ledblink',target=self.__blink_pin)
            # start the thread
            self.__thread.start()
    
        def blink(self, time_on=0.050, time_off=1):
            # blinking will start at the next first period
            # because turning the led on now might look funny because we don't know
            # when the next first period will start - the blink routine does all the
            # timing so that will 'just work'
            self.__ledmode = Led.LED_FLASHING
            self.__time_on = time_on
            self.__time_off = time_off
    
        def off(self):
            self.__ledmode = LED_OFF
            # set the cycle times short so changes to ledmode are picked up quickly
            self.__time_on = Led.FAST_CYCLE
            self.__time_off = Led.FAST_CYCLE
            # could turn the LED off immediately, might make for a short flicker on if was blinking
    
        def on(self):
            self.__ledmode = LED_ON
            # set the cycle times short so changes to ledmode are picked up quickly
            self.__time_on = Led.FAST_CYCLE
            self.__time_off = Led.FAST_CYCLE
            # could turn the LED on immediately, might make for a short flicker off if was blinking
    
        def reset(self):
            # set the semaphore so the thread will exit after sleep has completed
            self.pin_stop.set()
            # wait for the thread to exit
            self.__thread.join()
            # now clean up the GPIO
            GPIO.cleanup()
    
        ############################################################################
        # below here are private methods
        def __turnledon(self, pin):
            GPIO.output(pin, GPIO.LOW)
    
        def __turnledoff(self, pin):
            GPIO.output(pin, GPIO.HIGH)
    
        # this does all the work
        # If blinking, there are two sleeps in each loop
        # if on or off, there is only one sleep to ensure quick response to blink()
        def __blink_pin(self):
            while not self.pin_stop.is_set():
                # the first period is when the LED will be on if blinking
                if self.__ledmode == Led.LED_ON or self.__ledmode == Led.LED_FLASHING: 
                    self.__turnledon()
                else:
                    self.__turnledoff()
                # this is the first sleep - the 'on' time when blinking
                time.sleep(self.__time_on)
                # only if blinking, turn led off and do a second sleep for the off time
                if self.__ledmode == Led.LED_FLASHING:
                    self.__turnledoff()
                    # do an extra check that the stop semaphore hasn't been set before the off-time sleep
                    if not self.pin_stop.is_set():
                        # this is the second sleep - off time when blinking
                        time.sleep(self.__time_off)
    
        2
  •  1
  •   Alessandro Mendolia    8 年前

    对于未来需要这一点的人,我对拟议的代码做了一些调整,似乎工作正常。

    再次感谢@barny

    import RPi.GPIO as GPIO
    import time
    import threading
    
    class Led(object):
    
        LED_OFF = 0
        LED_ON = 1
        LED_FLASHING = 2
    
        # the short time sleep to use when the led is on or off to ensure the led responds quickly to changes to blinking
        FAST_CYCLE = 0.05
    
        def __init__(self, led_pin):
            # create the semaphore used to make thread exit
            self.pin_stop = threading.Event()
            # the pin for the LED
            self.__led_pin = led_pin
            # initialise the pin and turn the led off
            GPIO.setmode(GPIO.BCM)
            GPIO.setup(self.__led_pin, GPIO.OUT)
            # the mode for the led - off/on/flashing
            self.__ledmode = Led.LED_OFF
            # make sure the LED is off (this also initialises the times for the thread)
            self.off()
            # create the thread, keep a reference to it for when we need to exit
            self.__thread = threading.Thread(name='ledblink',target=self.__blink_pin)
            # start the thread
            self.__thread.start()
    
        def blink(self, time_on=0.050, time_off=1):
            # blinking will start at the next first period
            # because turning the led on now might look funny because we don't know
            # when the next first period will start - the blink routine does all the
            # timing so that will 'just work'
            self.__ledmode = Led.LED_FLASHING
            self.__time_on = time_on
            self.__time_off = time_off
    
        def off(self):
            self.__ledmode = self.LED_OFF
            # set the cycle times short so changes to ledmode are picked up quickly
            self.__time_on = Led.FAST_CYCLE
            self.__time_off = Led.FAST_CYCLE
            # could turn the LED off immediately, might make for a short flicker on if was blinking
    
        def on(self):
            self.__ledmode = self.LED_ON
            # set the cycle times short so changes to ledmode are picked up quickly
            self.__time_on = Led.FAST_CYCLE
            self.__time_off = Led.FAST_CYCLE
            # could turn the LED on immediately, might make for a short flicker off if was blinking
    
        def reset(self):
            # set the semaphore so the thread will exit after sleep has completed
            self.pin_stop.set()
            # wait for the thread to exit
            self.__thread.join()
            # now clean up the GPIO
            GPIO.cleanup()
    
        3
  •  1
  •   Adrian Mole Chris    5 年前

    answer of Alessandro Mendolia ,只是缺少类的私有方法。下面添加了一些修复。这个 __turnledon() 不需要参数-它可以访问 self.__led_pin 已存储在初始化中。

    ############################################################################
    # below here are private methods
    def __turnledon(self):
        GPIO.output(self.__led_pin, GPIO.LOW)
    
    def __turnledoff(self):
        GPIO.output(self.__led_pin , GPIO.HIGH)
    
    # this does all the work
    # If blinking, there are two sleeps in each loop
    # if on or off, there is only one sleep to ensure quick response to blink()
    def __blink_pin(self):
        while not self.pin_stop.is_set():
            # the first period is when the LED will be on if blinking
            if self.__ledmode == BlinkerLed.LED_ON or self.__ledmode == BlinkerLed.LED_FLASHING: 
                self.__turnledon()
            else:
                self.__turnledoff()
            # this is the first sleep - the 'on' time when blinking
            time.sleep(self.__time_on)
            # only if blinking, turn led off and do a second sleep for the off time
            if self.__ledmode == BlinkerLed.LED_FLASHING:
                self.__turnledoff()
                # do an extra check that the stop semaphore hasn't been set before the off-time sleep
                if not self.pin_stop.is_set():
                    # this is the second sleep - off time when blinking
                    time.sleep(self.__time_off)
    
        4
  •  0
  •   Laurent R    7 年前

    我不确定这是否会有帮助,但我想到了这一点(使用 gpiozero

    from gpiozero import LED
    import time
    import threading
    
    class LEDplus():
        def __init__(self,pinnumber):
            self.led = LED(pinnumber)
            self.__loop = True
            self.__threading = threading.Thread(target=self.__blink)
    
    
        def on(self,):
            self.__loop = False
            self.maybejoin()
            self.led.on()
    
        def off(self, ):
            self.__loop = False
            self.maybejoin()
            self.led.off()
    
        def maybejoin(self,):
            if self.__threading.isAlive():
                self.__threading.join()
    
        def blink(self, pitch):
            self.__threading = threading.Thread(target=self.__blink, args=(pitch, ))
            self.__threading.start()
    
        def __blink(self, pitch=.25):
            self.__loop = True
            while self.__loop:
                self.led.toggle()
                time.sleep(pitch/2)
            self.led.off()
    
    green = LEDplus(18)
    green.blink(1)