|
| 1 | +""" |
| 2 | +Please refer to the Chinese information:https://python.quectel.com/doc/API_reference/zh/ |
| 3 | +Please refer to English information: https://python.quectel.com/doc/doc/API_reference/en/ |
| 4 | +""" |
| 5 | + |
| 6 | +# Import necessary modules |
| 7 | +from machine import ExtInt, Pin |
| 8 | +from utime import sleep_ms |
| 9 | +from queue import Queue |
| 10 | +import _thread |
| 11 | + |
| 12 | +# Create a queue object to hold any events related to pressed keys |
| 13 | +key_evt_queue = Queue(8) |
| 14 | + |
| 15 | +# Function to handle and dispatch key events in a separate thread |
| 16 | +def key_evt_thread_entry(): |
| 17 | + while True: |
| 18 | + # Wait for an event to be added to the queue |
| 19 | + sleep_ms(1) |
| 20 | + if not key_evt_queue.empty(): |
| 21 | + # Retrieve the callback function, pin number, and event type from the queue |
| 22 | + event_cb, pin, event = key_evt_queue.get() |
| 23 | + # Call the specified callback function with the retrieved parameters |
| 24 | + event_cb(pin, event) |
| 25 | + |
| 26 | +# Start a new execution thread running the key event dispatcher function |
| 27 | +_thread.start_new_thread(key_evt_thread_entry, ()) |
| 28 | + |
| 29 | +# Define a class to abstract the concept of a physical key button |
| 30 | +class Key(): |
| 31 | + |
| 32 | + # Define constants representing the two types of key events (pressed or released) |
| 33 | + class Event(): |
| 34 | + PRESSED = 0x01 |
| 35 | + RELEASED = 0x02 |
| 36 | + |
| 37 | + # Define a custom exception class to be raised if unexpected values are passed as object parameters |
| 38 | + class Error(Exception): |
| 39 | + def __init__(self, value): |
| 40 | + self.value = value |
| 41 | + |
| 42 | + def __str__(self): |
| 43 | + return repr(self.value) |
| 44 | + |
| 45 | + # Constructor method for creating a new Key instance. |
| 46 | + # Parameters: |
| 47 | + # - pin: The GPIO pin number to which the key is connected |
| 48 | + # - level_on_pressed: Sets whether the associated GPIO input pin has a pull-up or pull-down resistor (0 for pull-up, 1 for pull-down) |
| 49 | + # - cared_event: A bitfield describing the desired event(s) to be detected by the key object (preferably using the Key.Event constants set earlier) |
| 50 | + # - event_cb: A callback function to be executed when a key event is detected |
| 51 | + def __init__(self, pin, level_on_pressed, cared_event, event_cb): |
| 52 | + # Validate and store the provided parameters |
| 53 | + self.pin = pin |
| 54 | + if level_on_pressed == 0: |
| 55 | + self.exti_pull = ExtInt.PULL_PU |
| 56 | + if cared_event == self.Event.PRESSED: |
| 57 | + self.exti_trigger_mode = ExtInt.IRQ_FALLING |
| 58 | + elif cared_event == self.Event.RELEASED: |
| 59 | + self.exti_trigger_mode = ExtInt.IRQ_RISING |
| 60 | + elif cared_event == self.Event.PRESSED | self.Event.RELEASED: |
| 61 | + self.exti_trigger_mode = ExtInt.IRQ_RISING_FALLING |
| 62 | + else: |
| 63 | + raise self.Error("Value error of <cared_event>!") |
| 64 | + elif level_on_pressed == 1: |
| 65 | + self.exti_pull = ExtInt.PULL_PD |
| 66 | + if cared_event == self.Event.PRESSED: |
| 67 | + self.exti_trigger_mode = ExtInt.IRQ_RISING |
| 68 | + elif cared_event == self.Event.RELEASED: |
| 69 | + self.exti_trigger_mode = ExtInt.IRQ_FALLING |
| 70 | + elif cared_event == self.Event.PRESSED | self.Event.RELEASED: |
| 71 | + self.exti_trigger_mode = ExtInt.IRQ_RISING_FALLING |
| 72 | + else: |
| 73 | + raise self.Error("Value error of <cared_event>!") |
| 74 | + else: |
| 75 | + raise self.Error("Value error of <level_on_pressed>!") |
| 76 | + |
| 77 | + self.level_on_pressed = level_on_pressed |
| 78 | + self.cared_event = cared_event |
| 79 | + self.event_cb = event_cb |
| 80 | + # Create an external interrupt object on the specified pin using the configured settings and passing the exit_cb method as the ISR |
| 81 | + self.exti = ExtInt(self.pin, self.exti_trigger_mode, self.exti_pull, self.exit_cb) |
| 82 | + # Enable the interrupt object |
| 83 | + self.exti.enable() |
| 84 | + |
| 85 | + # This method is called when a key is triggered by an interrupt. |
| 86 | + # It first disables the current ExtInt object and reads the level voltage of the GPIO pin associated with the key. |
| 87 | + # Depending on the GPIO voltage level, it determines whether the corresponding event (pressed/released) has occurred. |
| 88 | + # The event is then pushed to the shared key event queue in the format of (callback_function, pin_number, event_type). |
| 89 | + # The method finishes by re-enabling the ExtInt object for the key. |
| 90 | + |
| 91 | + def exit_cb(self, args): |
| 92 | + self.exti.disable() # Disable the current ExtInt object. |
| 93 | + sleep_ms(20) # Wait briefly to allow any voltage abnormalities to settle. |
| 94 | + |
| 95 | + gpio = Pin(args[0], Pin.IN, Pin.PULL_PU, 1) # Create a Pin object for the associated GPIO pin. |
| 96 | + level = gpio.read() # Read the current GPIO level. |
| 97 | + |
| 98 | + event = None |
| 99 | + # Determine if the corresponding event (pressed/released) has occurred based on the GPIO voltage level. |
| 100 | + if self.level_on_pressed == 0: |
| 101 | + if self.cared_event == self.Event.PRESSED and level == 0: |
| 102 | + event = self.cared_event |
| 103 | + if self.cared_event == self.Event.RELEASED and level == 1: |
| 104 | + event = self.cared_event |
| 105 | + if self.cared_event == self.Event.PRESSED | self.Event.RELEASED: |
| 106 | + if level == 0: |
| 107 | + event = self.Event.PRESSED |
| 108 | + else: |
| 109 | + event = self.Event.RELEASED |
| 110 | + else: |
| 111 | + if self.cared_event == self.Event.PRESSED and level == 1: |
| 112 | + event = self.cared_event |
| 113 | + if self.cared_event == self.Event.RELEASED and level == 0: |
| 114 | + event = self.cared_event |
| 115 | + if self.cared_event == self.Event.PRESSED | self.Event.RELEASED: |
| 116 | + if level == 1: |
| 117 | + event = self.Event.PRESSED |
| 118 | + else: |
| 119 | + event = self.Event.RELEASED |
| 120 | + |
| 121 | + # Add the event to the shared key event queue in the format of (callback function, pin number, event type). |
| 122 | + if event: |
| 123 | + key_evt_queue.put((self.event_cb, self.pin, event)) |
| 124 | + |
| 125 | + # Re-enable the ExtInt object for the key. |
| 126 | + self.exti = ExtInt(self.pin, self.exti_trigger_mode, self.exti_pull, self.exit_cb) |
| 127 | + self.exti.enable() |
| 128 | + |
| 129 | +# Main entry point of the program. It sets up two GPIO pins as keys (k1 and k2) and registers their events. |
| 130 | +# When an event occurs, the event_cb() function is called with arguments specifying the pin and event type. |
| 131 | +# The event_cb() function then handles the corresponding key press or release event. |
| 132 | +if __name__ == '__main__': |
| 133 | + # Set up the GPIO pins for the two keys |
| 134 | + k1 = Pin.GPIO4 |
| 135 | + k2 = Pin.GPIO30 |
| 136 | + |
| 137 | + def event_cb(pin, event): |
| 138 | + # Handle the key press/release event based on the pin number and event type. |
| 139 | + if pin == k1: |
| 140 | + if event == Key.Event.PRESSED: |
| 141 | + print("k1 pressed") |
| 142 | + else: |
| 143 | + print("k1 released") |
| 144 | + if pin == k2: |
| 145 | + if event == Key.Event.PRESSED: |
| 146 | + print("k2 pressed") |
| 147 | + else: |
| 148 | + print("k2 released") |
| 149 | + |
| 150 | + # Register the events for both keys and pass the event callback function. |
| 151 | + Key(k1, 0, Key.Event.PRESSED | Key.Event.RELEASED, event_cb) |
| 152 | + Key(k2, 0, Key.Event.PRESSED | Key.Event.RELEASED, event_cb) |
0 commit comments