使用Python,如何防止两个线程同时写入显示

时间:2018-11-22 03:15:15

标签: python callback interrupt

我正在一个Raspberry Pi项目中,在这里我有一个OLED显示器,它在True-loop中不断地用Python中的信息更新。但是,每当我按下按钮(GPIO 5)时,只要按下按钮,或者可能在预定的时间段内,我都希望显示一些其他静态信息(例如系统信息)。释放按钮后,“主循环”可能会再次接管。   我试图使用RPi.GPIO和回调函数来实现此功能以显示系统信息,但是问题是,即使在执行回调函数期间,主循环仍会继续写入OLED,如果按下GPIO 5:两个“线程”正在同时写入OLED ...

我假设我需要一种在回调函数期间暂停主循环执行的方法,并且我已经尝试了Semaphore和aquire / release,但是没有运气。我还考虑了将两个回调函数与Semaphore结合使用的可能性,但是由于主循环中显示的信息应该不断更新(例如,不是由中断驱动),所以我不确定这是否是我的解决方案。 / p>

在这一点上,我什至不知道接下来要用谷歌搜索什么。也许这里有人可以启发我?这是做这种事情的完全错误的方式吗? (有限的Python使用经验...)

下面是一个简化的代码示例,它模拟了我想做的事情。

import time
import Adafruit_GPIO.SPI as SPI
import RPi.GPIO as GPIO
import Adafruit_SSD1306
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

# Initialize
disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)
disp.begin()
disp.clear()
disp.display()
image = Image.new('1', (disp.width, disp.height))
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()
GPIO.setmode(GPIO.BCM) 
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up

def clear_display():
    draw.rectangle((0,0,disp.width,disp.height), outline=0, fill=0)
    disp.image(image)
    disp.display()

# Callback function 
def display_system_info(channel):
    draw.text((0, 0), "System info displayed",  font=font, fill=255)
    draw.text((0, 9), "for five seconds.",  font=font, fill=255)
    disp.image(image)
    disp.display()
    time.sleep(5)
    clear_display()

GPIO.add_event_detect(5, GPIO.RISING, callback=display_system_info, bouncetime=200)

try:
    while True:
        for counter in range(7):
            draw.text((0,counter*9), "Printing line {0:d}".format(counter),  font=font, fill=255)
            disp.image(image)
            disp.display()
            time.sleep(1)
        clear_display()
except KeyboardInterrupt:  
    GPIO.cleanup()       # clean up GPIO on CTRL+C exit
GPIO.cleanup()           # clean up GPIO on normal exit  

非常感谢您的帮助。

/ N

3 个答案:

答案 0 :(得分:0)

我正在使用全局变量-buttonPressed解决这种情况。 当您按下按钮(GPIO 5)时,buttonPressed设置为True,主循环不执行任何操作。 希望对您很清楚,对您有帮助。

import time
import Adafruit_GPIO.SPI as SPI
import RPi.GPIO as GPIO
import Adafruit_SSD1306
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

# Initialize
disp = Adafruit_SSD1306.SSD1306_128_64(rst=None)
disp.begin()
disp.clear()
disp.display()
image = Image.new('1', (disp.width, disp.height))
draw = ImageDraw.Draw(image)
font = ImageFont.load_default()
GPIO.setmode(GPIO.BCM) 
GPIO.setup(5, GPIO.IN, pull_up_down=GPIO.PUD_UP) # Input with pull-up

buttonPressed = False

def clear_display():
    draw.rectangle((0,0,disp.width,disp.height), outline=0, fill=0)
    disp.image(image)
    disp.display()

# Callback function 
def display_system_info(channel):
    global buttonPressed
    buttonPressed = True
    draw.text((0, 0), "System info displayed",  font=font, fill=255)
    draw.text((0, 9), "for five seconds.",  font=font, fill=255)
    disp.image(image)
    disp.display()
    time.sleep(5)
    clear_display()
    buttonPressed = False

GPIO.add_event_detect(5, GPIO.RISING, callback=display_system_info, bouncetime=200)

try:
    while True:
        if(not buttonPressed):
            for counter in range(7):
                draw.text((0,counter*9), "Printing line {0:d}".format(counter),  font=font, fill=255)
                disp.image(image)
                disp.display()
                time.sleep(1)
            clear_display()
except KeyboardInterrupt:  
    GPIO.cleanup()       # clean up GPIO on CTRL+C exit
GPIO.cleanup()           # clean up GPIO on normal exit  

尝试让我知道。

答案 1 :(得分:0)

根据您的应用程序,我将避免在回调内部进行任何实际工作。取而代之的是,我只设置一个主线程/循环可以处理的标志,或者将事件添加到由主线程/循环处理的队列中。

答案 2 :(得分:0)

我意识到对于这个特定的应用程序而言,中断并不是走好的路。取而代之的是,我重新编写了代码,以便仅在主循环空闲并等待时才按下按钮……这当然是大多数时间。感谢您的输入!