|
| 1 | +# Extended Class to use TFT ST7789 in parallel bus |
| 2 | + |
| 3 | +from micropython import const |
| 4 | +from utime import sleep_ms, sleep_us |
| 5 | +from machine import Pin |
| 6 | + |
| 7 | +## Pins (wiring) example and description |
| 8 | +#ST7789 STM32F407VGT6 |
| 9 | +#LCD_RST <---> 3.3V #LCD reset control pin, active low, used to force a hardware reset of the board, if unused need be plug on 3.3V |
| 10 | +#LCD_CS <---> GND #LCD chip select control pin, active low, if used only one LCD can be plug on GND |
| 11 | +#LCD_RS <---> PD13 #LCD register command / data selection control pin, comand active low, data active high |
| 12 | +#LCD_WR <---> PD5 #LCD write control pin, active low |
| 13 | +#LCD_RD <---> PD4 #LCD read control pin, active low |
| 14 | +#GND <---> GND |
| 15 | +#5V <---> 5V |
| 16 | + |
| 17 | +#LCD_D2 <---> PD0 |
| 18 | +#LCD_D3 <---> PD1 |
| 19 | +#LCD_D4 <---> PE7 |
| 20 | +#LCD_D5 <---> PE8 |
| 21 | +#LCD_D6 <---> PE9 |
| 22 | +#LCD_D7 <---> PE10 |
| 23 | +#LCD_D0 <---> PD14 |
| 24 | +#LCD_D1 <---> PD15 |
| 25 | + |
| 26 | +## DEFINE PINS CONST |
| 27 | +_LCD_RST = const(None) #LCD reset control pin, active low |
| 28 | +_LCD_CS = const(None) #LCD chip select control pin, active low |
| 29 | +_LCD_RS = const(4) #LCD register command / data selection control pin, comand active low, data active high |
| 30 | +_LCD_WR = const(5) #LCD write control pin, active low |
| 31 | +_LCD_RD = const(None) #LCD read control pin, active low |
| 32 | + |
| 33 | +_LCD_D0 = const(12) |
| 34 | +_LCD_D1 = const(_LCD_D0 + 1) |
| 35 | +_LCD_D2 = const(_LCD_D0 + 2) |
| 36 | +_LCD_D3 = const(_LCD_D0 + 3) |
| 37 | +_LCD_D4 = const(_LCD_D0 + 4) |
| 38 | +_LCD_D5 = const(_LCD_D0 + 5) |
| 39 | +_LCD_D6 = const(_LCD_D0 + 6) |
| 40 | +_LCD_D7 = const(_LCD_D0 + 7) |
| 41 | + |
| 42 | +## ESP32 GPIO REGISTERS |
| 43 | +_ESP32_GPIO_OUT_REG = const(0x3FF44004) # GPIO0-31 output value. (R/W), 32 bits from rigth to left ref pins numbers from 0 to 31 |
| 44 | +_ESP32_GPIO_OUT_W1TS_REG = const(0x3FF44008) # GPIO0-31 output set register. For every bit that is 1 in the value written here, the corresponding bit in GPIO_OUT_REG will be set. (WO) |
| 45 | +_ESP32_GPIO_OUT_W1TC_REG = const(0x3FF4400C) # GPIO0-31 output clear register. For every bit that is 1 in the value written here, the corresponding bit in GPIO_OUT_REG will be cleared. (WO) |
| 46 | + |
| 47 | +_ESP32_GPIO_OUT1_REG = const(0x3FF44010) # GPIO32-39 output value. (R/W), 32 bits from rigth to left ref pins numbers from 32 to 39 |
| 48 | +_ESP32_GPIO_OUT1_W1TS_REG = const(0x3FF44014) # GPIO32-39 output set register. For every bit that is 1 in the value written here, the corresponding bit in GPIO_OUT_REG will be set. (WO) |
| 49 | +_ESP32_GPIO_OUT1_W1TC_REG = const(0x3FF44018) # GPIO32-39 output clear register. For every bit that is 1 in the value written here, the corresponding bit in GPIO_OUT_REG will be cleared. (WO) |
| 50 | + |
| 51 | +_ESP32_GPIO_IN_REG = const(0x3FF4403C) #GPIO 0-31 input register (RO) |
| 52 | +_ESP32_GPIO_IN1_REG = const(0x3FF44040) #GPIO 32-39 input register (RO) |
| 53 | + |
| 54 | +from st7789py import * |
| 55 | + |
| 56 | +_ENCODE_PIXEL = const(">H") |
| 57 | +_ENCODE_POS = const(">HH") |
| 58 | +_DECODE_PIXEL = const(">BBB") |
| 59 | + |
| 60 | +def _encode_pos(x, y): |
| 61 | + """Encode a postion into bytes.""" |
| 62 | + return struct.pack(_ENCODE_POS, x, y) |
| 63 | + |
| 64 | + |
| 65 | +def _encode_pixel(color): |
| 66 | + """Encode a pixel color into bytes.""" |
| 67 | + return struct.pack(_ENCODE_PIXEL, color) |
| 68 | + |
| 69 | +class ST7789_PARALLEL(ST7789): |
| 70 | + def __init__(self, width:(int) = 320, height:(int) = 240, rotation=0, |
| 71 | + rst_pin:(int,None) = _LCD_RST, # LCD reset control pin |
| 72 | + cs_pin:(int,None) = _LCD_CS, # LCD chip select control pin |
| 73 | + rs_pin:(int,None) = _LCD_RS, #**Required** LCD register command / data selection control pin, |
| 74 | + wr_pin:(int,None) = _LCD_WR, #**Required** LCD write control pin, active low |
| 75 | + rd_pin:(int,None) = _LCD_RD, # LCD read control pin, active low |
| 76 | + d0_pin:(int,None) = _LCD_D0 #**Required** LCD D0 pin, all otherpins will be pick seguentially after this. |
| 77 | + ): |
| 78 | + if height != 240 or width not in [320, 240, 135]: |
| 79 | + raise ValueError("Unsupported display. 320x240, 240x240 and 135x240 are supported.") |
| 80 | + if rs_pin is None: |
| 81 | + raise ValueError("rs pin (data/command) is required.") |
| 82 | + if d0_pin is None or (d0_pin + 8) > 31 : |
| 83 | + raise ValueError("d0_pin is required, need to be continuos and bellow 23, the rest of data pins will be assign seguentially, ex, d0_pin = 12, d1_pin=13, ... ") |
| 84 | + |
| 85 | + for x in (rst_pin, cs_pin, rs_pin, wr_pin, rd_pin, d0_pin): |
| 86 | + if x != None and ((x<0) or (x>44)) : |
| 87 | + raise ValueError("Pins numbers need to between 0 and 44") |
| 88 | + |
| 89 | + |
| 90 | + self.rst_pin_mask = self.cs_pin_mask = self.rs_pin_mask = (_ESP32_GPIO_OUT_REG, 0b0) |
| 91 | + self.wr_pin_mask = self.rd_pin_mask = self.d_pin_mask = self.rst_pin_mask |
| 92 | + |
| 93 | + self.rst_pin = self.reset = rst_pin |
| 94 | + if rst_pin != None : |
| 95 | + self.rst_pin = self.reset = Pin(rst_pin, Pin.OUT, value=1) |
| 96 | + if rst_pin < 32 : |
| 97 | + self.rst_pin_mask = (_ESP32_GPIO_OUT_REG, 1 << rst_pin) |
| 98 | + else : |
| 99 | + self.rst_pin_mask = (_ESP32_GPIO_OUT1_REG, 1 << rst_pin-32) |
| 100 | + |
| 101 | + self.cs_pin = self.cs = cs_pin |
| 102 | + if cs_pin != None : |
| 103 | + self.cs_pin = self.cs = Pin(cs_pin,Pin.OUT, value=1) |
| 104 | + if cs_pin < 32 : |
| 105 | + self.cs_pin_mask = (_ESP32_GPIO_OUT_REG, 1 << cs_pin) |
| 106 | + else : |
| 107 | + self.cs_pin_mask = (_ESP32_GPIO_OUT1_REG, 1 << cs_pin-32) |
| 108 | + |
| 109 | + self.rs_pin = self.dc = rs_pin |
| 110 | + if rs_pin != None : |
| 111 | + self.rs_pin = self.dc = Pin(rs_pin, Pin.OUT, value=0) |
| 112 | + if rs_pin < 32 : |
| 113 | + self.rs_pin_mask = (_ESP32_GPIO_OUT_REG, 1 << rs_pin) |
| 114 | + else : |
| 115 | + self.rs_pin_mask = (_ESP32_GPIO_OUT1_REG, 1 << rs_pin-32) |
| 116 | + |
| 117 | + self.wr_pin = wr_pin |
| 118 | + if wr_pin != None : |
| 119 | + self.wr_pin = Pin(wr_pin, Pin.OUT, value=1) |
| 120 | + if wr_pin < 32 : |
| 121 | + self.wr_pin_mask = (_ESP32_GPIO_OUT_REG, 1 << wr_pin) |
| 122 | + else : |
| 123 | + self.wr_pin_mask = (_ESP32_GPIO_OUT1_REG, 1 << wr_pin-32) |
| 124 | + |
| 125 | + self.rd_pin = rd_pin |
| 126 | + if rd_pin != None : |
| 127 | + self.rd_pin = Pin(rd_pin, Pin.OUT, value=1) |
| 128 | + if rd_pin < 32 : |
| 129 | + self.rd_pin_mask = (_ESP32_GPIO_OUT_REG, 1 << rd_pin) |
| 130 | + else : |
| 131 | + self.rd_pin_mask = (_ESP32_GPIO_OUT1_REG, 1 << rd_pin-32) |
| 132 | + |
| 133 | + self.d_pin = [] |
| 134 | + for x in range(0, 8) : |
| 135 | + self.d_pin.append(Pin(d0_pin + x, Pin.OUT, value=0)) |
| 136 | + self.d_pin_mask = (_ESP32_GPIO_OUT_REG, 0b11111111 << d0_pin) |
| 137 | + self.d0_pin = d0_pin |
| 138 | + |
| 139 | + self._display_width = self.width = width |
| 140 | + self._display_height = self.height = height |
| 141 | + self.xstart = 0 |
| 142 | + self.ystart = 0 |
| 143 | + |
| 144 | + self.backlight = None |
| 145 | + self._rotation = rotation % 4 |
| 146 | + |
| 147 | + self.hard_reset() |
| 148 | + self.soft_reset() |
| 149 | + self.sleep_mode(False) |
| 150 | + |
| 151 | + self._set_color_mode(COLOR_MODE_65K | COLOR_MODE_16BIT) |
| 152 | + time.sleep_ms(50) |
| 153 | + self.rotation(self._rotation) |
| 154 | + self.inversion_mode(False) |
| 155 | + time.sleep_ms(10) |
| 156 | + self._write(ST7789_NORON) |
| 157 | + time.sleep_ms(10) |
| 158 | + self.fill(0) |
| 159 | + self._write(ST7789_DISPON) |
| 160 | + time.sleep_ms(500) |
| 161 | + |
| 162 | + @micropython.viper |
| 163 | + def _write_v(self, data: ptr8, size:int, repeat:int): |
| 164 | + gpio_data = ptr32(self.d_pin_mask[0]) |
| 165 | + gpio_wr = ptr32(self.wr_pin_mask[0]) |
| 166 | + wr_pin_mask = int(self.wr_pin_mask[1]) |
| 167 | + d_pin_mask = int(self.d_pin_mask[1]) |
| 168 | + d0_pin = int(self.d0_pin) |
| 169 | + for _ in range(0,repeat): |
| 170 | + for x in range(0,size): |
| 171 | + gpio_data[2] = d_pin_mask |
| 172 | + gpio_data[1] = int(data[x] << d0_pin) |
| 173 | + gpio_wr[2] = wr_pin_mask |
| 174 | + gpio_wr[1] = wr_pin_mask |
| 175 | + |
| 176 | + @micropython.native |
| 177 | + def _write(self, command=None, data=None, repeat=1): |
| 178 | + """ParallelSPI write to the device: commands and data.""" |
| 179 | + if self.cs: |
| 180 | + self.cs.off() |
| 181 | + |
| 182 | + if command is not None: |
| 183 | + self.dc.off() |
| 184 | + if command is not None: |
| 185 | + command=bytearray([command]) |
| 186 | + self._write_v(command,len(command), repeat) |
| 187 | + if data is not None: |
| 188 | + self.dc.on() |
| 189 | + if data is not None: |
| 190 | + self._write_v(data,len(data), repeat) |
| 191 | + if self.cs: |
| 192 | + self.cs.on() |
| 193 | + |
| 194 | + def fill_rect(self, x, y, width, height, color): |
| 195 | + """ |
| 196 | + Draw a rectangle at the given location, size and filled with color. |
| 197 | +
|
| 198 | + Args: |
| 199 | + x (int): Top left corner x coordinate |
| 200 | + y (int): Top left corner y coordinate |
| 201 | + width (int): Width in pixels |
| 202 | + height (int): Height in pixels |
| 203 | + color (int): 565 encoded color |
| 204 | + """ |
| 205 | + self._set_window(x, y, x + width - 1, y + height - 1) |
| 206 | + pixel = _encode_pixel(color) |
| 207 | + |
| 208 | + self._write(None,pixel,width * height) |
| 209 | + |
0 commit comments