|
| 1 | +from micropython import const # NOQA |
| 2 | +import display_driver_framework |
| 3 | + |
| 4 | +import lcd_bus |
| 5 | +import gc |
| 6 | +import lvgl as lv |
| 7 | + |
| 8 | + |
| 9 | +STATE_HIGH = display_driver_framework.STATE_HIGH |
| 10 | +STATE_LOW = display_driver_framework.STATE_LOW |
| 11 | +STATE_PWM = display_driver_framework.STATE_PWM |
| 12 | + |
| 13 | +_SET_CONTRAST = const(0x81) |
| 14 | +_SET_NORM_INV = const(0xA6) |
| 15 | +_SET_DISP = const() |
| 16 | +_DISP_OFF = const(0xAE) |
| 17 | +_DISP_ON = const(0xAF) |
| 18 | +_SET_COL_ADDR = const(0x21) |
| 19 | +_SET_PAGE_ADDR = const(0x22) |
| 20 | + |
| 21 | + |
| 22 | +class SSD1306(display_driver_framework.DisplayDriver): |
| 23 | + |
| 24 | + def __init__( |
| 25 | + self, |
| 26 | + data_bus, |
| 27 | + display_width, |
| 28 | + display_height, |
| 29 | + frame_buffer1=None, |
| 30 | + frame_buffer2=None, |
| 31 | + reset_pin=None, |
| 32 | + reset_state=STATE_HIGH, |
| 33 | + power_pin=None, |
| 34 | + backlight_pin=None, |
| 35 | + backlight_on_state=STATE_HIGH, |
| 36 | + offset_x=0, |
| 37 | + offset_y=0, |
| 38 | + color_space=lv.COLOR_FORMAT.RGB888, # NOQA |
| 39 | + rgb565_byte_swap=False |
| 40 | + ): |
| 41 | + |
| 42 | + if not isinstance(data_bus, (lcd_bus.SPIBus, lcd_bus.I2CBus)): |
| 43 | + raise ValueError('Only SPI and I2C lcd busses allowed') |
| 44 | + |
| 45 | + buf_size = int( |
| 46 | + display_width * |
| 47 | + display_height * |
| 48 | + lv.color_format_get_size(color_space) |
| 49 | + ) |
| 50 | + |
| 51 | + if frame_buffer1 is None: |
| 52 | + |
| 53 | + gc.collect() |
| 54 | + |
| 55 | + for flags in ( |
| 56 | + lcd_bus.MEMORY_INTERNAL | lcd_bus.MEMORY_DMA, |
| 57 | + lcd_bus.MEMORY_SPIRAM | lcd_bus.MEMORY_DMA, |
| 58 | + lcd_bus.MEMORY_INTERNAL, |
| 59 | + lcd_bus.MEMORY_SPIRAM |
| 60 | + ): |
| 61 | + try: |
| 62 | + frame_buffer1 = ( |
| 63 | + data_bus.allocate_framebuffer(buf_size, flags) |
| 64 | + ) |
| 65 | + |
| 66 | + if (flags | lcd_bus.MEMORY_DMA) == flags: |
| 67 | + frame_buffer2 = ( |
| 68 | + data_bus.allocate_framebuffer(buf_size, flags) |
| 69 | + ) |
| 70 | + |
| 71 | + break |
| 72 | + except MemoryError: |
| 73 | + frame_buffer1 = data_bus.free_framebuffer(frame_buffer1) |
| 74 | + |
| 75 | + if frame_buffer1 is None: |
| 76 | + raise MemoryError( |
| 77 | + f'Unable to allocate memory for frame buffer ({buf_size})' |
| 78 | + # NOQA |
| 79 | + ) |
| 80 | + |
| 81 | + self._pages = int(display_height // 8) |
| 82 | + |
| 83 | + if len(frame_buffer1) != buf_size: |
| 84 | + raise ValueError(f'Framebuffer is too small ({buf_size}') |
| 85 | + |
| 86 | + super().__init__( |
| 87 | + data_bus=data_bus, |
| 88 | + display_width=display_width, |
| 89 | + display_height=display_height, |
| 90 | + frame_buffer1=frame_buffer1, |
| 91 | + frame_buffer2=frame_buffer2, |
| 92 | + reset_pin=reset_pin, |
| 93 | + reset_state=reset_state, |
| 94 | + power_pin=None, |
| 95 | + backlight_pin=backlight_pin, |
| 96 | + backlight_on_state=backlight_on_state, |
| 97 | + offset_x=offset_x, |
| 98 | + offset_y=offset_y, |
| 99 | + color_byte_order=display_driver_framework.BYTE_ORDER_RGB, |
| 100 | + color_space=color_space, # NOQA |
| 101 | + rgb565_byte_swap=rgb565_byte_swap, |
| 102 | + _cmd_bits=8, |
| 103 | + _param_bits=8, |
| 104 | + _init_bus=True |
| 105 | + ) |
| 106 | + |
| 107 | + self._power_pin = power_pin |
| 108 | + |
| 109 | + def set_constrast(self, value): |
| 110 | + self._param_buf[0] = value & 0xFF |
| 111 | + self.set_params(_SET_CONTRAST, self._param_mv[:1]) |
| 112 | + |
| 113 | + def set_color_inversion(self, value): |
| 114 | + self.set_params(_SET_NORM_INV | (int(bool(value)) & 1)) |
| 115 | + |
| 116 | + def get_power(self): |
| 117 | + return self._power |
| 118 | + |
| 119 | + def set_power(self, value): |
| 120 | + self._power = bool(value) |
| 121 | + |
| 122 | + if self._power: |
| 123 | + self.set_params(_DISP_ON) |
| 124 | + else: |
| 125 | + self.set_params(_DISP_OFF) |
| 126 | + |
| 127 | + def _flush_cb(self, _, area, color_p): |
| 128 | + |
| 129 | + x1 = 0 |
| 130 | + x2 = self.display_width - 1 |
| 131 | + |
| 132 | + if self.display_width == 64: |
| 133 | + # displays with width of 64 pixels are shifted by 32 |
| 134 | + x1 += 32 |
| 135 | + x2 += 32 |
| 136 | + |
| 137 | + y1 = 0 |
| 138 | + y2 = self._pages - 1 |
| 139 | + |
| 140 | + self._param_buf[0] = x1 |
| 141 | + self._param_buf[1] = x2 |
| 142 | + self.set_params(_SET_COL_ADDR, self._param_mv[:2]) |
| 143 | + |
| 144 | + self._param_buf[0] = y1 |
| 145 | + self._param_buf[1] = y2 |
| 146 | + self.set_params(_SET_PAGE_ADDR, self._param_mv[:2]) |
| 147 | + |
| 148 | + size = ( |
| 149 | + (area.x2 - area.x1 + 1) * |
| 150 | + (area.y2 - area.y1 + 1) * |
| 151 | + lv.color_format_get_size(self._color_space) |
| 152 | + ) |
| 153 | + |
| 154 | + # we have to use the __dereference__ method because this method is |
| 155 | + # what converts from the C_Array object the binding passes into a |
| 156 | + # memoryview object that can be passed to the bus drivers |
| 157 | + data_view = color_p.__dereference__(size) |
| 158 | + self._data_bus.tx_color(0, data_view, x1, y1, x2, y2, self._rotation, self._disp_drv.flush_is_last()) |
0 commit comments