Кнопка (GPIO) Нажатие логики в MicroPython

У меня был еще один открытый вопрос о логике итеративного меню, и проблема трансформировалась в логику кнопок, поэтому я разделяю их, поскольку исходный вопрос был действительно решен.

Мой код выглядит следующим образом:

      """ fit: a productivity logger """
import time
import sys
import os
import uhashlib
import machine
import framebuf
from ssd1306 import SSD1306_I2C

def final_print(sec,final_hash,final_survey):
    """ leaves the summary on the screen before shutting down """
    mins = sec // 60
    sec = sec % 60
    hours = mins // 60
    mins = mins % 60
    short_sec = int(sec)
    duration = (str(hours) + "/" + str(mins) + "/" + str(short_sec))
    oled_show("> fit the"+str(final_hash),str(final_survey),"//"+str(duration))
    time.sleep(30)
    oled_blank()

def timer_down(f_seconds,timer_focus):
    """ counts down for defined period """
    now = time.time()
    end = now + f_seconds
    while now < end:
        now = time.time()
        fit_progress(now,end,timer_focus,f_seconds)
        time.sleep(0.01)
#        if button1.value() == 0:
#             oled_show("","Ended Manually!","")
#             time.sleep(2)
#             break

def timer_up(timer_focus):
    """ counts up for indefinite period """
    now = time.time()
    while True:
        minutes = int((time.time() - now) / 60)
        oled_show(str(timer_focus)," for ",str(minutes))
        time.sleep(0.01)
#         if button1.value() == 0:
#             oled_show("","Ended Manually!","")
#             time.sleep(2)
#             break

def fit_progress(now,end,timer_focus,f_seconds):
    """ tracks progress of a count-down fit and prints to screen """
    remain = end - now
    f_minutes = int((remain)/60)
    j = 1 - (remain / f_seconds)
    pct = int(100*j)
    oled_show(str(timer_focus),str(f_minutes)+" min",str(pct)+"%")

def debounce(btn):
    """ some debounce control """
    count = 2
    while count > 0:
        if btn.value():
            count = 2
        else:
            count -= 1
        time.sleep(0.01)

def multi_choice(options):
    """ provides multi-choice menus for two-button navigation """
    for i in options:
        oled_show("> fit",i,"1:yes  2:next")
        # Wait for any button press.
        while 1:
            b1pressed = button1.value()
            b2pressed = button2.value()
            if b1pressed or b2pressed:
                break
        if b1pressed:
            print( i, "chosen" )
            debounce(button1)
            return i
        # We know B2 was pressed.
        debounce(button2)

def oled_show(message1,message2,message3):
    """ displays a three line message """
    oled.fill(0) # clear the display
    oled.text(message1,5,5)
    oled.text(message2,5,15)
    oled.text(message3,5,25)
    oled.show()

def oled_blank():
    """ blanks the oled display to avoid burn in """
    oled.fill(0)
    oled.show()

sda = machine.Pin(4)
scl = machine.Pin(5)
i2c = machine.I2C(0,sda=sda, scl=scl, freq=400000)
oled = SSD1306_I2C(128, 32, i2c)

button1 = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_UP)
button2 = machine.Pin(3, machine.Pin.IN, machine.Pin.PULL_UP)

F_TYPE = multi_choice(['30-minute fit','60-minute fit','indefinite fit'])

F_FOCUS = multi_choice(['personal fit','work fit','learn fit','admin fit'])

fStart = time.time()

if F_TYPE == "30-minute fit":
    timer_down(1800,F_FOCUS)
elif F_TYPE == "60-minute fit":
    timer_down(3600,F_FOCUS)
elif F_TYPE == "indefinite fit":
    timer_up(F_FOCUS)
else:
    sys.exit()

fEnd = time.time()

F_SURVEY = multi_choice(['went well','went ok','went poorly'])

fDuration = fEnd - fStart

F_HASH = uhashlib.sha256(str(fEnd).encode('utf-8')).digest()
F_HASH_SHORT = F_HASH[0:3]

fitdb = open("data.csv","a")
fitdb.write(str(F_HASH)+","+str(F_TYPE)+","+str(F_FOCUS)+","+str(F_SURVEY)+","+str(fStart)+","+str(fEnd)+","+str(fDuration)+"\n")
fitdb.close()

final_print(fDuration,F_HASH_SHORT,F_SURVEY)

В частности, вы можете видеть, что у меня определены две кнопки:

      button1 = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_UP)
button2 = machine.Pin(3, machine.Pin.IN, machine.Pin.PULL_UP)

И они в основном используются для выбора из меню с несколькими вариантами:

      def debounce(btn):
    """ some debounce control """
    count = 2
    while count > 0:
        if btn.value():
            count = 2
        else:
            count -= 1
        time.sleep(0.01)

def multi_choice(options):
    """ provides multi-choice menus for two-button navigation """
    for i in options:
        oled_show("> fit",i,"1:yes  2:next")
        # Wait for any button press.
        while 1:
            b1pressed = button1.value()
            b2pressed = button2.value()
            if b1pressed or b2pressed:
                break
        if b1pressed:
            print( i, "chosen" )
            debounce(button1)
            return i
        # We know B2 was pressed.
        debounce(button2)

Однако я столкнулся с проблемой, при которой кнопки можно нажимать только поочередно. То есть, когда запускается функция multi_choice, я могу нажать кнопку button1, чтобы выбрать первую опцию, или я могу нажать кнопку button2, чтобы перейти к следующей опции, но, например, если я нажму кнопку button2, она не зарегистрируется для второго нажатия ( чтобы выбрать второй вариант), я могу только затем нажать кнопку 1 ... если я сделаю это, я могу только затем нажать кнопку 2 далее.

Я уверен, что это просто логическая проблема, которую я не вижу.

Кнопки представляют собой обычные переключатели Cherry MX с мгновенным включением на выводах 2 и 3 GPIO. Они определенно работают надежно, но что-то в этой логике шаткое.

Следующий тест работает нормально, так что дело не в кнопках ...

      import machine
import time

button1 = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_UP)
button2 = machine.Pin(3, machine.Pin.IN, machine.Pin.PULL_UP)

while True:
    b1pressed = button1.value()
    b2pressed = button2.value()
    time.sleep(0.01)
    b1released = button1.value()
    b2released = button2.value()
    if b1pressed and not b1released:
        print('Button1 pressed!')
    if b2pressed and not b2released:
        print('Button2 pressed!')
    if not b2pressed and b2released:
        print('Button2 released!')
    elif not b1pressed and b1released:
        print('Button1 released!')

Я добавил несколько операторов печати, чтобы отладить это, и я могу видеть, как кнопки принимают и удерживают значения. Я чувствую, что мне нужно настроиться на искусственный сброс, может это то, что я могу сделать в debounce? Я пробовал несколько вещей, но пока не добился прогресса.

      def debounce(btn):
    """ some debounce control """
    count = 2
    while count > 0:
        if btn.value():
            count = 2
        else:
            count -= 1
        time.sleep(0.01)

def multi_choice(options):
    """ provides multi-choice menus for two-button navigation """
    for i in options:
        print("below for")
        print("button 1 below for",button1.value())
        print("button 2 below for",button2.value())
        oled_show("      fit",i,"1:sel    2:next")
        while 1:
            print("below while")
            print("button 1 below while",button1.value())
            print("button 2 below while",button2.value())
            b1pressed = button1.value()
            b2pressed = button2.value()
            if b1pressed or b2pressed:
                print("below first if")
                print("button 1 first if",button1.value())
                print("button 2 first if",button2.value())
                break
        if b1pressed:
            print("below second if")
            print("button 1 second if",button1.value())
            print("button 2 second if",button2.value())
            debounce(button1)
            return i
        debounce(button2)

и вывод вышеуказанной отладочной печати:

      >>> %Run -c $EDITOR_CONTENT
below for
button 1 below for 0
button 2 below for 1
below while
button 1 below while 0
button 2 below while 1
below first if
button 1 first if 0
button 2 first if 1
below for
button 1 below for 1
button 2 below for 0
below while
button 1 below while 1
button 2 below while 0
below first if
button 1 first if 1
button 2 first if 0
below second if
button 1 second if 1
button 2 second if 0
below for
button 1 below for 0
button 2 below for 1
below while
button 1 below while 0
button 2 below while 1
below first if
button 1 first if 0
button 2 first if 1
below for
button 1 below for 1
button 2 below for 0
below while
button 1 below while 1
button 2 below while 0
below first if
button 1 first if 1
button 2 first if 0
below second if
button 1 second if 1
button 2 second if 0

2 ответа

Решение

Хорошо, с помощью всех присутствующих, я понял это. Просто нужно было добавить немного сна, чтобы избежать перетекания нажатия клавиш из предыдущего цикла while.

      def multi_choice(options):
    """ provides multi-choice menus for two-button navigation """
    for i in options:
        oled_show("      fit",i,"1:sel    2:next")
        time.sleep(0.5)
        while 1:
            if not button1.value():
                return i
            if not button2.value():
                break

Спасибо всем!

Когда обе ваши кнопки привязаны к земле, и используется PULL_UP, код просто:

      import machine

button1 = machine.Pin(2, machine.Pin.IN, machine.Pin.PULL_UP)
button2 = machine.Pin(3, machine.Pin.IN, machine.Pin.PULL_UP)

while True:
    print('Button1 down!') if not button1.value() else print('Button1 up!') 
    print('Button2 down!') if not button2.value() else print('Button2 up!')

Логика проста. Ваша пуговица привязана к земле на одном конце и натянута на другом. Когда вы нажимаете эту кнопку, он соединяет контакт MCU с землей. При нажатии кнопки будет ноль, а при отпускании - единица. Если ты хочешь чтобы быть уверенным, когда вы нажимаете кнопку, вам нужно привязать кнопку к рейке Vcc и использовать на выводе MCU.

Весь этот код устранения ошибок, вероятно, даже не нужен, и даже если было необходимо создать цикл для обработки, это не ответ. Что делать, если ваша кнопка не отскакивает ~ теперь вы просто застряли в петле. Купите 74HC14 или сделайте простую RC-сеть, если вы беспокоитесь о подпрыгивании. Назначение всех этих произвольных значений состояниям кнопок и залипание кнопок в циклах, которые блокируют другие ваши кнопки, - это просто шум. Это очень просто: у вас есть один вопрос, который вы хотите задать дважды. «Эта кнопка нажата» ~ так что просто «задайте» этот вопрос дважды и двигайтесь дальше. Не требуется 20 строк кода, чтобы определить, где что-то 1 или 0.

В сторону: у вас нет отдельного "GND". Все ваши контакты «GND» подключены к одному и тому же «GND». У вас один источник питания, верно? Ну, один это один. Если разрезать торт на 8 частей, получится не 8 тортов.

Другие вопросы по тегам