|
| 1 | +print("qemu.py running") |
| 2 | + |
| 3 | +import lcd_bus |
| 4 | +import lvgl as lv |
| 5 | +import machine |
| 6 | +import time |
| 7 | + |
| 8 | +import mpos.ui |
| 9 | + |
| 10 | +print("qemu.py display bus initialization") |
| 11 | +try: |
| 12 | + display_bus = lcd_bus.I80Bus( |
| 13 | + dc=7, |
| 14 | + wr=8, |
| 15 | + cs=6, |
| 16 | + data0=39, |
| 17 | + data1=40, |
| 18 | + data2=41, |
| 19 | + data3=42, |
| 20 | + data4=45, |
| 21 | + data5=46, |
| 22 | + data6=47, |
| 23 | + data7=48, |
| 24 | + reverse_color_bits=False # doesnt seem to do anything? |
| 25 | + ) |
| 26 | +except Exception as e: |
| 27 | + print(f"Error initializing display bus: {e}") |
| 28 | + print("Attempting hard reset in 3sec...") |
| 29 | + time.sleep(3) |
| 30 | + machine.reset() |
| 31 | + |
| 32 | +_BUFFER_SIZE = const(28800) |
| 33 | +fb1 = display_bus.allocate_framebuffer(_BUFFER_SIZE, lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_DMA) |
| 34 | +fb2 = display_bus.allocate_framebuffer(_BUFFER_SIZE, lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_DMA) |
| 35 | + |
| 36 | +import drivers.display.st7789 as st7789 |
| 37 | +# 320x200 => make 320x240 screenshot => it's 240x200 (but the display shows more than 200) |
| 38 | +mpos.ui.main_display = st7789.ST7789( |
| 39 | + data_bus=display_bus, |
| 40 | + frame_buffer1=fb1, |
| 41 | + frame_buffer2=fb2, |
| 42 | + display_width=170, # emulator st7789.c has 135 |
| 43 | + display_height=320, # emulator st7789.c has 240 |
| 44 | + color_space=lv.COLOR_FORMAT.RGB565, |
| 45 | + #color_space=lv.COLOR_FORMAT.RGB888, |
| 46 | + color_byte_order=st7789.BYTE_ORDER_RGB, |
| 47 | + # rgb565_byte_swap=False, # always False is data_bus.get_lane_count() == 8 |
| 48 | + reset_pin=5, |
| 49 | + backlight_pin=38, |
| 50 | + backlight_on_state=st7789.STATE_PWM, |
| 51 | +) |
| 52 | +mpos.ui.main_display.init() |
| 53 | +mpos.ui.main_display.set_power(True) |
| 54 | +mpos.ui.main_display.set_backlight(100) |
| 55 | + |
| 56 | +lv.init() |
| 57 | +#mpos.ui.main_display.set_rotation(lv.DISPLAY_ROTATION._90) # must be done after initializing display and creating the touch drivers, to ensure proper handling |
| 58 | +mpos.ui.main_display.set_color_inversion(True) # doesnt seem to do anything? |
| 59 | + |
| 60 | +# Button handling code: |
| 61 | +from machine import Pin |
| 62 | +btn_a = Pin(0, Pin.IN, Pin.PULL_UP) # 1 |
| 63 | +btn_b = Pin(14, Pin.IN, Pin.PULL_UP) # 2 |
| 64 | +btn_c = Pin(3, Pin.IN, Pin.PULL_UP) # 3 |
| 65 | + |
| 66 | +# Key repeat configuration |
| 67 | +# This whole debounce logic is only necessary because LVGL 9.2.2 seems to have an issue where |
| 68 | +# the lv_keyboard widget doesn't handle PRESSING (long presses) properly, it loses focus. |
| 69 | +REPEAT_INITIAL_DELAY_MS = 300 # Delay before first repeat |
| 70 | +REPEAT_RATE_MS = 100 # Interval between repeats |
| 71 | +last_key = None |
| 72 | +last_state = lv.INDEV_STATE.RELEASED |
| 73 | +key_press_start = 0 # Time when key was first pressed |
| 74 | +last_repeat_time = 0 # Time of last repeat event |
| 75 | + |
| 76 | +# Read callback |
| 77 | +# Warning: This gets called several times per second, and if it outputs continuous debugging on the serial line, |
| 78 | +# that will break tools like mpremote from working properly to upload new files over the serial line, thus needing a reflash. |
| 79 | +def keypad_read_cb(indev, data): |
| 80 | + global last_key, last_state, key_press_start, last_repeat_time |
| 81 | + since_last_repeat = 0 |
| 82 | + |
| 83 | + # Check buttons |
| 84 | + current_key = None |
| 85 | + current_time = time.ticks_ms() |
| 86 | + if btn_a.value() == 0: |
| 87 | + current_key = lv.KEY.PREV |
| 88 | + elif btn_b.value() == 0: |
| 89 | + current_key = lv.KEY.ENTER |
| 90 | + elif btn_c.value() == 0: |
| 91 | + current_key = lv.KEY.NEXT |
| 92 | + |
| 93 | + if (btn_a.value() == 0) and (btn_c.value() == 0): |
| 94 | + current_key = lv.KEY.ESC |
| 95 | + |
| 96 | + if current_key: |
| 97 | + if current_key != last_key: |
| 98 | + # New key press |
| 99 | + data.key = current_key |
| 100 | + data.state = lv.INDEV_STATE.PRESSED |
| 101 | + last_key = current_key |
| 102 | + last_state = lv.INDEV_STATE.PRESSED |
| 103 | + key_press_start = current_time |
| 104 | + last_repeat_time = current_time |
| 105 | + else: |
| 106 | + # No key pressed |
| 107 | + data.key = last_key if last_key else lv.KEY.ENTER |
| 108 | + data.state = lv.INDEV_STATE.RELEASED |
| 109 | + last_key = None |
| 110 | + last_state = lv.INDEV_STATE.RELEASED |
| 111 | + key_press_start = 0 |
| 112 | + last_repeat_time = 0 |
| 113 | + |
| 114 | + # Handle ESC for back navigation (only on initial PRESSED) |
| 115 | + if last_state == lv.INDEV_STATE.PRESSED: |
| 116 | + if current_key == lv.KEY.ESC: |
| 117 | + mpos.ui.back_screen() |
| 118 | + |
| 119 | + |
| 120 | +group = lv.group_create() |
| 121 | +group.set_default() |
| 122 | + |
| 123 | +# Create and set up the input device |
| 124 | +indev = lv.indev_create() |
| 125 | +indev.set_type(lv.INDEV_TYPE.KEYPAD) |
| 126 | +indev.set_read_cb(keypad_read_cb) |
| 127 | +indev.set_group(group) # is this needed? maybe better to move the default group creation to main.py so it's available everywhere... |
| 128 | +disp = lv.display_get_default() # NOQA |
| 129 | +indev.set_display(disp) # different from display |
| 130 | +indev.enable(True) # NOQA |
| 131 | +from mpos import InputManager |
| 132 | +InputManager.register_indev(indev) |
| 133 | + |
| 134 | +print("qemu.py finished") |
0 commit comments