Reaktionszeit bei Button eingaben?

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
DoGro
User
Beiträge: 15
Registriert: Montag 23. Mai 2022, 07:18

Guten Tag ,

Ich verwende ein Raspberry Pi Pico von 2020. Und programmiere das erste mal auf einem Microcontroller.

Dort ich versuche den Button S2 zum hochzählen zu programmieren, solange der Button S1 gedrückt ist.
Leider zählt er nicht bei jedem drücken hoch.
Ich weiß nicht genau woran das liegt, ob an den sleep Zeiten oder ob der Speicher überlastet ist?

Für Hilfe wäre ich sehr dankbar.
Viele Grüße
DoGro

Code: Alles auswählen

from machine import Pin
from time import sleep_ms

#Pins
button_S1 = Pin(16, Pin.IN, Pin.PULL_DOWN)
button_S2 = Pin(18, Pin.IN, Pin.PULL_DOWN)


class ButtonEvent:
    def __init__(self, name):
        self.name = name
        self.last = False
        
    def is_pressed(self):
        pressed = self.name.value()
        sleep_ms(50)
        self.last = pressed
        return pressed
        
    
    def has_changed(self):
        pressed = self.is_pressed()
        changed = (self.name.value() != self.last)
        self.last = pressed
        sleep_ms(50)
        return changed
    

def count():
    counter = 0
    button1 = ButtonEvent(button_S1)
    button2 = ButtonEvent(button_S2)
    sleep_ms(500)
    if button1.is_pressed():
        print('ha')
        x = True
        while x:  
            if button1.has_changed():
                print('Ende')
                x = False
            elif button2.has_changed():
                print('counter: '+ str(counter))
                counter += 1
    return counter


#Hauptprogramm
zahl = 0

zahl = count()
print(zahl)
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da ist viel im Argen. Ein Argument name zu nenne, obwohl es ein Button ist. Die ganze Schlaferei, und Anlage von ungenutztem Zustand wie self.last.

Das problem ist einfach zu lösen, indem du

- die IRQ Funktion beim Pin zu benutzen, der gezählt werden soll.
- wenn der IRQ auslöst, einfach abtragen, ob gerade der andere Button gedrückt ist. Wenn ja, Counter hochzählen.

In der Hauptschleife einfach dauerhaft den Zählerstand ausgeben, ggf mit schlafen dazwischen.
DoGro
User
Beiträge: 15
Registriert: Montag 23. Mai 2022, 07:18

Ah vielen Dank an die Interrupts habe ich gar nicht gedancht.

Code: Alles auswählen

from machine import Pin
import utime

# Button-Pins definieren
button_S1 = Pin(16, Pin.IN, Pin.PULL_DOWN)
button_S2 = Pin(18, Pin.IN, Pin.PULL_DOWN)


# Interrupt für Button 2 definieren
def button2_interrupt(pin):
    utime.sleep_ms(100)  #Entrpellen
    global counter
    if button_S1.value():
        counter += 1

# Interrupt an Pins binden
button_S2.irq(trigger=Pin.IRQ_FALLING, handler=button2_interrupt)

# Hauptprogramms
counter = 0
while True:
    print(counter)
    utime.sleep(1)
Vielen Dank für deine Hilfe

Das einzige Problem jetzt ist noch, dass der doch ab und zu bei einem Knopfdruck den Counter um 2 hochsetzt.
Liegt das an der sleep-Zeit?

Ich habe da an das Entprellen gedacht. Was das Phänomen verringert, allerdings nicht verhindert.
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Niemals im IRQ schlafen. Wenn du prellen Verhindern willst, nimm einen Zeitstempel & ignorier neue Ereignisse 100ms lang.

Wenn du pulldowns aktivierst, warum reagierst du dann auf die fallende Flanke? Das zählt dann doch loslassen.

Außer prellen fällt mir zur Ursache erstmal auch nichts ein. Ich würde da mit einem logic analyzer draufschauen.
DoGro
User
Beiträge: 15
Registriert: Montag 23. Mai 2022, 07:18

Hallo deets,

vielen Dank für deine Hilfe.
Das verwenden des Zeitstempel hat das Problem so gut wie komplett gelöst.
Die Feinabstimmung kann ich dann hoffentlich bei weiteren Projekten und mehr Erfahrung vornehmen.

Wie das mit dem Logic Analyzer funktioniert weiß ich noch nicht. In die Thematik muss ich mich erst einarbeiten.

Der Grund für die Abfallende Flanke liegt in meinem Code begründet.
Es soll über die beiden Buttons ein Code eingegeben werden.

Solange S1 gedrückt ist kann über den S2 eine Zahl eingegeben werden. Beim Loslassen von S1 wird der Wert dann gespeichert. :)

Hier nun mein aktuell verwendeter Code:

Code: Alles auswählen

# Variablen
counter = 0
saved_counters = []
code = [2, 2, 2, 2]
last_press_S1 = 0 # Zum Entprellen
last_press_S2 = 0
threshold = 100  

# Interrupt fürs loslassen Button S1
def button1_interrupt(pin):
    global last_press_S1
    current_time = ticks_ms()
    if current_time - last_press_S1 > threshold:
        saved_counters.append(save_and_reset_counter())
        led.value(1)
        sleep_ms(50)
        led.value(0)
        print('Button 1 losgelassen')
        last_press_S1 = current_time

# Interrupt fürs drücken Button S2 
def button2_interrupt(pin):
    global counter, last_press_S2
    current_time = ticks_ms()
    if current_time - last_press_S2 > threshold:
        print('Button 2 gedrückt')
        if button_S1.value():
            counter += 1
        last_press_S2 = current_time

# Interrupt an Pins binden
button_S1.irq(trigger=Pin.IRQ_FALLING, handler=button1_interrupt)
button_S2.irq(trigger=Pin.IRQ_RISING, handler=button2_interrupt)
DoGro
User
Beiträge: 15
Registriert: Montag 23. Mai 2022, 07:18

Ach ich habe natürlich das Blinken der LED auch noch aus dem Interrupt rausgeholt, sonst hätte ich ja wieder eine sleep_ms in der Funktion.
So funktioniert der Code und das Drücken der Taster fast immer Tadellos. :P

Danke Danke.

Code: Alles auswählen

# Button-Pins definieren
button_S1 = Pin(16, Pin.IN, Pin.PULL_DOWN)
button_S2 = Pin(18, Pin.IN, Pin.PULL_DOWN)
led = Pin(17, Pin.OUT)

# Variablen
counter = 0
saved_counters = []
code = [2, 2, 2, 2]
last_press_S1 = 0 # Zum Entprellen verwende ich einen Reaktionsstopp, damit nicht in den Interrupts mit sleep_ms gearbeitet werden muss.
last_press_S2 = 0
threshold = 100  # Schwellenwert in Millisekunden der gewartet wird

# Interrupt fürs loslassen Button S1
def button1_interrupt(pin):
    global last_press_S1
    current_time = ticks_ms()  
    if current_time - last_press_S1 > threshold:
        reaktion_S1()
        last_press_S1 = current_time

        
# Button S1 Reaktionen
def reaktion_S1():
    print('Button 1 losgelassen')
    saved_counters.append(save_and_reset_counter())  # Ruft die Funktion zum Speichern der Button S2 Anzahl in der Liste saved_counters auf.
    led.value(1)
    sleep_ms(50)
    led.value(0)    
    

# Interrupt fürs drücken Button S2 
def button2_interrupt(pin):
    global counter, last_press_S2
    current_time = ticks_ms()
    if current_time - last_press_S2 > threshold:
        print('Button 2 gedrückt')
        if button_S1.value():
            counter += 1
        last_press_S2 = current_time

# Interrupt an Pins binden
button_S1.irq(trigger=Pin.IRQ_FALLING, handler=button1_interrupt)
button_S2.irq(trigger=Pin.IRQ_RISING, handler=button2_interrupt)
Benutzeravatar
Dennis89
User
Beiträge: 1153
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

was heißt denn fast immer?

Ich weis ja nicht wie der Rest deine Codes aus sieht, aber man will eigentlich keine globalen Variablen in einem Programm haben. Wenn da noch mehr Code kommt, wird das alles sehr unübersichtlich.
Wenn man sich etwas merken will, dann nutzt man eine Klasse.

Das könnte dann so aussehen:

Code: Alles auswählen

from machine import Pin
from time import ticks_ms, ticks_diff, sleep


PIN_S1 = 16
PIN_S2 = 18


class ButtonCounter:
    def __init__(self, button_s1_pin, button_s2_pin, debounce_time=0.05):
        self.debounce_time = debounce_time
        self.button_s1 = Pin(button_s1_pin, Pin.IN, Pin.PULL_DOWN)
        button_s2 = Pin(button_s2_pin, Pin.IN, Pin.PULL_DOWN)
        button_s2.irq(handler=self.counting, trigger=Pin.IRQ_RAISING)
        self.timestamp = ticks_ms()
        self.counter = 0

    def counting(self, pin):
        if (
            ticks_diff(ticks_ms(), self.timestamp) >= self.debounce_time
            and self.button_s1.value()
        ):
            self.counter += 1
            self.timestamp = ticks_ms()


def main():
    button_counter = ButtonCounter(PIN_S1, PIN_S2)
    while True:
        print(button_counter.counter)
        sleep(0.5)


if __name__ == "__main__":
    main()
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
DoGro
User
Beiträge: 15
Registriert: Montag 23. Mai 2022, 07:18

Ahh,
ok das mit der Klasse war meine erste Idee.

Damit tue ich mich noch etwas schwer. Ich danke dir, für deine Anregung.
Werde das erstmal verarbeiten müssen.

Viele Grüße
Antworten