├── LICENSE ├── README.md ├── TFT_io.py ├── TFTfont.py ├── calibrate.py ├── font14.py ├── font8mono.py ├── package.json ├── tft.py ├── touch.py ├── touch_bytecode.py ├── touchtest.py ├── xpt2046_syn.py └── xpttest.py /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Robert Hammelrath 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #touch pad Class for the XPT2046 controller 2 | 3 | **Description** 4 | 5 | A Python class for using a resistive touch pad with a XPT2046 controller. This port uses a software SPI for communication to the TFT, whic uses the following GPIO ports: 6 | 7 | - X12 for Clock 8 | - X11 for Data Out (from Pyboard to XPT2046) 9 | - Y2 for Data In (from XPT2046 to Pyboard) 10 | 11 | CS of the touch pad must be tied to GND. The touch pad will typically uses in combination with a TFT. In my case, it was glued to a 4.3" TFT with an SSD1963 controller. The class itself does not rely on an TFT, but for calibration a TFT is used. 12 | 13 | At the moment, the code is a basic package. It will deliver touch events. But the mapping to a TFT's coordinates is tested with that single TFT in Landscape mode only. 14 | 15 | The touch pad is used in 12 bit mode, returning values in the range of 0..4095 for the coordinates. A single raw sampling takes about 60µs. The result is somewhat noisy, and the touch pad has the habit of creating false touches in the transition of press and release. The function get_touch caters for that. 16 | 17 | 18 | **Functions** 19 | ``` 20 | Create instance: 21 | 22 | mytouch = TOUCH(controller, asyn=False, *, confidence=5, margin=50, 23 | delay=10, calibration=None, spi=None) 24 | controller: String with the controller model. At the moment, it is ignored 25 | asyn: Set True if asynchronous operation intended. In this instance the 26 | uasyncio library must be available. 27 | confidence: confidence level - number of consecutive touches with a 28 | margin smaller than the given level which the function will sample 29 | until it accepts it as a valid touch 30 | margin: Difference from mean centre at which touches are considered 31 | at the same position 32 | delay: Delay between samples in ms. (n/a if asynchronous) 33 | calibration: Tuple of 8 numbers, which transpose touch pad coordinates 34 | into TFT coordinates. 35 | You can determine these with the tool calibraty.py (see below) of the 36 | package. A vector of (0, 1, 0, 1, 0, 1, 0, 1) will deliver the raw 37 | values. The calibration is performed typically once. Once determined, 38 | you may also code these values into the sources. 39 | spi: A spi object which is used for communiation. If None is supplied, the 40 | driver creates this object with the pins X12, X11 and Y2 41 | 42 | Methods: 43 | 44 | touch_parameter(confidence=5, margin=10, delay=10, calibration=None) 45 | # Set the operational parameters of the touch pad. All parameters are optional 46 | confidence: confidence level - number of consecutive touches with a 47 | margin smaller than the given level which the function will sample 48 | until it accepts it as a valid touch 49 | margin: Difference from mean centre at which touches are considered 50 | at the same position 51 | delay: Delay between samples in ms. (n/a if asynchronous) 52 | calibration: Tuple of 8 numbers, which transpose touch pad coordinates 53 | into TFT coordinates. 54 | 55 | get_touch(initial=True, wait=True, raw=False, timeout=None) 56 | # This is the major data entry function. Parameters: 57 | initial: if True, wait for a non-touch state of the touch pad before getting 58 | the touch coordinates. This is the natural behavior. If False, get the next touch 59 | immediately and do not what for the stylus to be released. 60 | wait: If True, wait for a valid touch event. If False, return immediately if no 61 | touch is made. 62 | raw: Setting whether raw touch coordinates (True) or normalized ones 63 | (False) are returned setting the calibration vector to 64 | (0, 1, 0, 1, 0, 1, 0, 1) result in a identity mapping 65 | timeout: Timeout for the function, unit ms, for all situations where the function is 66 | told to wait, e.g. initial = True or wait = True. 67 | A value of None is considered as a timeout of an hour. 68 | 69 | The function returns a two value tuple (x,y) of the touch coordinates, 70 | or 'None', if either no touch is pressed or the timeout triggers. 71 | 72 | do_normalize(touch) 73 | # Transpose touch coordinates into TFT coordinates. The function requires 74 | the calibration values to be set to a reasonable value. It is called 75 | within get_touch too. Parameter: 76 | touch: a touch pad value tuple returned by get_touch() in raw mode 77 | or raw_touch() 78 | ----- lower level functions --- 79 | 80 | raw_touch() 81 | # determine the raw touch value and return immediately. The return value is a pair of 82 | touch pad coordinates, is a touch is present, or 'None' 83 | 84 | touch_talk(command, bits) 85 | # send commands to the touch pad controller and retrieves 'bits' data from it. 86 | It will always perform and return. No checking is done for the command value 87 | and the returned information. 88 | ``` 89 | 90 | **Files:** 91 | - touch.py: Source file with comments. 92 | - calibration.py: Code to determine the calibration of the touch pad, which 93 | allows to map between touch pad and screen coordinates. You will be asked 94 | to touch four points at the screen indicated by a cross-hair. 95 | The confidence level is set high, so keep your hand steady and use a stylus. 96 | If it fails at a certain point, release and touch again. 97 | The determined values are printed on the screen and at the USB interface. 98 | So you can copy them from there. Once the values are know, they are set 99 | temporarily, and you may try them. Just touch the screen. At the point of 100 | touching, a small green circle should light up. If the match is bad, 101 | repeat the calibration. 102 | - touchtest.py: Another sample test program, which creates a small four button 103 | keypad, which is defined by a table. 104 | - README.md: this one 105 | - LICENSE: The MIT license file 106 | 107 | **To Do** 108 | - consider ISR mode 109 | - test portrait mode 110 | 111 | **Short Version History** 112 | 113 | **0.1** 114 | Initial release with the basic functions 115 | 116 | **1.0** 117 | Added an asynchronous mode implemented by Peter Hinch 118 | 119 | **1.1** 120 | Asynchronous mode adapted to use uasyncio 121 | 122 | **1.2** 123 | Replace bit-bang communication by SPI built-in methods, making it less hardware 124 | dependent. 125 | -------------------------------------------------------------------------------- /TFT_io.py: -------------------------------------------------------------------------------- 1 | # 2 | # The MIT License (MIT) 3 | # 4 | # Copyright (c) 2016 Robert Hammelrath 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | # 24 | # Low level I/O drivers for the class supporting TFT LC-displays 25 | # with a parallel Interface 26 | # First example: Controller SSD1963 27 | # It uses X1..X8 for data and Y3, Y9, Y10, Y11 and Y12 for control signals. 28 | # The minimal connection is: 29 | # X1..X8 for data, Y9 for /Reset, Y10 for /RD, Y11 for /WR and Y12 for /RS 30 | # Then LED must be hard tied to Vcc and /CS to GND. 31 | # 32 | 33 | import pyb, stm 34 | from uctypes import addressof 35 | 36 | # define constants 37 | # 38 | RESET = const(1 << 10) ## Y9 39 | RD = const(1 << 11) ## Y10 40 | WR = const(0x01) ## Y11 41 | D_C = const(0x02) ## Y12 42 | 43 | LED = const(1 << 8) ## Y3 44 | POWER = const(1 << 9) ## Y4 45 | 46 | ## CS is not used and must be hard tied to GND 47 | 48 | PORTRAIT = const(1) 49 | LANDSCAPE = const(0) 50 | 51 | # 52 | # display font bitmap for text 53 | # 54 | @micropython.viper 55 | def displaySCR_charbitmap(bits: ptr8, size: int, control: ptr8, bg_buf: ptr8): 56 | gpioa = ptr8(stm.GPIOA) 57 | gpiob = ptr16(stm.GPIOB + stm.GPIO_BSRRL) 58 | # 59 | transparency = control[6] 60 | bm_ptr = 0 61 | bg_ptr = 0 62 | mask = 0x80 63 | # 64 | while size: 65 | 66 | if bits[bm_ptr] & mask: 67 | if transparency & 8: # Invert bg color as foreground 68 | gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr] # set data on port A 69 | gpiob[1] = WR # set WR low. C/D still high 70 | gpiob[0] = WR # set WR high again 71 | 72 | gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr + 1] # set data on port A 73 | gpiob[1] = WR # set WR low. C/D still high 74 | gpiob[0] = WR # set WR high again 75 | 76 | gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr + 2] # set data on port A 77 | gpiob[1] = WR # set WR low. C/D still high 78 | gpiob[0] = WR # set WR high again 79 | else: # not invert 80 | gpioa[stm.GPIO_ODR] = control[3] # set data on port A 81 | gpiob[1] = WR # set WR low. C/D still high 82 | gpiob[0] = WR # set WR high again 83 | 84 | gpioa[stm.GPIO_ODR] = control[4] # set data on port A 85 | gpiob[1] = WR # set WR low. C/D still high 86 | gpiob[0] = WR # set WR high again 87 | 88 | gpioa[stm.GPIO_ODR] = control[5] # set data on port A 89 | gpiob[1] = WR # set WR low. C/D still high 90 | gpiob[0] = WR # set WR high again 91 | else: 92 | if transparency & 1: # Dim background 93 | gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr] >> 1 # set data on port A 94 | gpiob[1] = WR # set WR low. C/D still high 95 | gpiob[0] = WR # set WR high again 96 | 97 | gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr + 1] >> 1 # set data on port A 98 | gpiob[1] = WR # set WR low. C/D still high 99 | gpiob[0] = WR # set WR high again 100 | 101 | gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr + 2] >> 1 # set data on port A 102 | gpiob[1] = WR # set WR low. C/D still high 103 | gpiob[0] = WR # set WR high again 104 | elif transparency & 2: # keep Background 105 | gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr] # set data on port A 106 | gpiob[1] = WR # set WR low. C/D still high 107 | gpiob[0] = WR # set WR high again 108 | 109 | gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr + 1] # set data on port A 110 | gpiob[1] = WR # set WR low. C/D still high 111 | gpiob[0] = WR # set WR high again 112 | 113 | gpioa[stm.GPIO_ODR] = bg_buf[bg_ptr + 2] # set data on port A 114 | gpiob[1] = WR # set WR low. C/D still high 115 | gpiob[0] = WR # set WR high again 116 | elif transparency & 4: # invert Background 117 | gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr] # set data on port A 118 | gpiob[1] = WR # set WR low. C/D still high 119 | gpiob[0] = WR # set WR high again 120 | 121 | gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr + 1] # set data on port A 122 | gpiob[1] = WR # set WR low. C/D still high 123 | gpiob[0] = WR # set WR high again 124 | 125 | gpioa[stm.GPIO_ODR] = 255 - bg_buf[bg_ptr + 2] # set data on port A 126 | gpiob[1] = WR # set WR low. C/D still high 127 | gpiob[0] = WR # set WR high again 128 | else: # not transparent 129 | gpioa[stm.GPIO_ODR] = control[0] # set data on port A 130 | gpiob[1] = WR # set WR low. C/D still high 131 | gpiob[0] = WR # set WR high again 132 | 133 | gpioa[stm.GPIO_ODR] = control[1] # set data on port A 134 | gpiob[1] = WR # set WR low. C/D still high 135 | gpiob[0] = WR # set WR high again 136 | 137 | gpioa[stm.GPIO_ODR] = control[2] # set data on port A 138 | gpiob[1] = WR # set WR low. C/D still high 139 | gpiob[0] = WR # set WR high again 140 | mask >>= 1 141 | if mask == 0: # mask reset & data ptr advance on byte exhaust 142 | mask = 0x80 143 | bm_ptr += 1 144 | size -= 1 145 | bg_ptr += 3 146 | # 147 | # display Windows BMP data, optionally with colortables 148 | # 149 | @micropython.viper 150 | def displaySCR_bmp(data: ptr8, size: int, bits: int, colortable: ptr8): 151 | gpioa = ptr8(stm.GPIOA) 152 | gpiob = ptr16(stm.GPIOB + stm.GPIO_BSRRL) 153 | # 154 | bm_ptr = 0 155 | shift = 8 - bits 156 | mask = ((1 << bits) - 1) << shift 157 | # 158 | while size: 159 | 160 | offset = ((data[bm_ptr] & mask) >> shift) * 4 161 | 162 | gpioa[stm.GPIO_ODR] = colortable[offset + 2] # Red 163 | gpiob[1] = WR # set WR low. C/D still high 164 | gpiob[0] = WR # set WR high again 165 | 166 | gpioa[stm.GPIO_ODR] = colortable[offset + 1] # green 167 | gpiob[1] = WR # set WR low. C/D still high 168 | gpiob[0] = WR # set WR high again 169 | 170 | gpioa[stm.GPIO_ODR] = colortable[offset + 0] # blue 171 | gpiob[1] = WR # set WR low. C/D still high 172 | gpiob[0] = WR # set WR high again 173 | 174 | mask >>= bits 175 | shift -= bits 176 | if mask == 0: # mask rebuild & data ptr advance on byte exhaust 177 | shift = 8 - bits 178 | mask = ((1 << bits) - 1) << shift 179 | bm_ptr += 1 180 | size -= 1 181 | # 182 | # Set the address range for various draw commands and set the TFT for expecting data 183 | # 184 | # 185 | # Assembler version of 186 | # SetXY: takes net about 6 µs including the call 187 | # 188 | @micropython.asm_thumb 189 | def setXY_L(r0, r1, r2, r3): 190 | # r0: x1, r1: y1, r2: x2, r3: y2 191 | # set up pointers to GPIO 192 | # r4: changing data 193 | # r5: bit mask for control lines 194 | # r6: GPIOA ODR register ptr 195 | # r7: GPIOB BSSRL register ptr 196 | movwt(r6, stm.GPIOA) # target 197 | add (r6, stm.GPIO_ODR) 198 | movwt(r7, stm.GPIOB) 199 | add (r7, stm.GPIO_BSRRL) 200 | b(start) 201 | #inline subroutine 202 | label(send) 203 | strb(r4, [r6, 0]) # set command byte 204 | strh(r5, [r7, 2]) # WR and D_C low 205 | strh(r5, [r7, 0]) # WR and D_C high 206 | bx(lr) 207 | # Emit command byte 208 | label(start) 209 | movw(r5, WR | D_C) 210 | mov (r4, 0x2a) 211 | bl(send) 212 | 213 | mov(r5, 8) 214 | mov(r4, r0) # get x1 215 | asr(r4, r5) # get the upper byte 216 | mov(r5, WR) 217 | bl(send) 218 | 219 | mov(r4, r0) 220 | bl(send) 221 | 222 | mov(r0, 8) # from here on r0 keeps 8 223 | mov(r4, r2) # get x2 224 | asr(r4, r0) # get the upper byte 225 | bl(send) 226 | 227 | mov(r4, r2) 228 | bl(send) 229 | # Emit command byte 230 | movw(r5, WR | D_C) 231 | mov (r4, 0x2b) 232 | bl(send) 233 | 234 | mov(r5, WR) 235 | mov(r4, r1) # get y1 236 | asr(r4, r0) # get the upper byte 237 | bl(send) 238 | 239 | mov(r4, r1) 240 | bl(send) 241 | 242 | mov(r4, r3) # get x2 243 | asr(r4, r0) # get the upper byte 244 | bl(send) 245 | 246 | mov(r4, r3) 247 | bl(send) 248 | # Emit command byte 249 | movw(r5, WR | D_C) 250 | mov (r4, 0x2c) 251 | bl(send) 252 | # and done 253 | @micropython.asm_thumb 254 | def setXY_P(r0, r1, r2, r3): 255 | # r0: x1, r1: y1, r2: x2, r3: y2 256 | # set up pointers to GPIO 257 | # r4: changing data 258 | # r5: bit mask for control lines 259 | # r6: GPIOA ODR register ptr 260 | # r7: GPIOB BSSRL register ptr 261 | movwt(r6, stm.GPIOA) # target 262 | add (r6, stm.GPIO_ODR) 263 | movwt(r7, stm.GPIOB) 264 | add (r7, stm.GPIO_BSRRL) 265 | b(start) 266 | #inline subroutine 267 | label(send) 268 | strb(r4, [r6, 0]) # set command byte 269 | strh(r5, [r7, 2]) # WR and D_C low 270 | strh(r5, [r7, 0]) # WR and D_C high 271 | bx(lr) 272 | # Emit command byte 273 | label(start) 274 | movw(r5, WR | D_C) 275 | mov (r4, 0x2b) 276 | bl(send) 277 | 278 | mov(r5, 8) 279 | mov(r4, r0) # get x1 280 | asr(r4, r5) # get the upper byte 281 | mov(r5, WR) 282 | bl(send) 283 | 284 | mov(r4, r0) 285 | bl(send) 286 | 287 | mov(r0, 8) # from here on r0 keeps 8 288 | mov(r4, r2) # get x2 289 | asr(r4, r0) # get the upper byte 290 | bl(send) 291 | 292 | mov(r4, r2) 293 | bl(send) 294 | # Emit command byte 295 | movw(r5, WR | D_C) 296 | mov (r4, 0x2a) 297 | bl(send) 298 | 299 | mov(r5, WR) 300 | mov(r4, r1) # get y1 301 | asr(r4, r0) # get the upper byte 302 | bl(send) 303 | 304 | mov(r4, r1) 305 | bl(send) 306 | 307 | mov(r4, r3) # get x2 308 | asr(r4, r0) # get the upper byte 309 | bl(send) 310 | 311 | mov(r4, r3) 312 | bl(send) 313 | # Emit command byte 314 | movw(r5, WR | D_C) 315 | mov (r4, 0x2c) 316 | bl(send) 317 | # and done 318 | # 319 | # Assembler version of 320 | # drawPixel, Landscape 321 | # 322 | @micropython.asm_thumb 323 | def drawPixel_L(r0, r1, r2): 324 | # r0: x, r1: y, r2: colorvector 325 | # set up pointers to GPIO 326 | # r4: changing data 327 | # r5: bit mask for control lines 328 | # r6: GPIOA ODR register ptr 329 | # r7: GPIOB BSSRL register ptr 330 | movwt(r6, stm.GPIOA) # target 331 | add (r6, stm.GPIO_ODR) 332 | movwt(r7, stm.GPIOB) 333 | add (r7, stm.GPIO_BSRRL) 334 | b(start) 335 | #inline subroutine 336 | label(send) 337 | strb(r4, [r6, 0]) # set command byte 338 | strh(r5, [r7, 2]) # WR and D_C low 339 | strh(r5, [r7, 0]) # WR and D_C high 340 | bx(lr) 341 | # Emit command byte 342 | label(start) 343 | movw(r5, WR | D_C) 344 | mov (r4, 0x2a) 345 | bl(send) 346 | 347 | mov(r5, 8) 348 | mov(r4, r0) # get x1 349 | asr(r4, r5) # get the upper byte 350 | mov(r5, WR) 351 | bl(send) 352 | 353 | mov(r4, r0) 354 | bl(send) 355 | 356 | mov(r0, 8) # from here on r0 keeps 8 357 | mov(r4, r2) # get x2 358 | asr(r4, r0) # get the upper byte 359 | bl(send) 360 | 361 | mov(r4, r2) 362 | bl(send) 363 | # Emit command byte 364 | movw(r5, WR | D_C) 365 | mov (r4, 0x2b) 366 | bl(send) 367 | 368 | mov(r5, WR) 369 | mov(r4, r1) # get y1 370 | asr(r4, r0) # get the upper byte 371 | bl(send) 372 | 373 | mov(r4, r1) 374 | bl(send) 375 | 376 | mov(r4, r3) # get x2 377 | asr(r4, r0) # get the upper byte 378 | bl(send) 379 | 380 | mov(r4, r3) 381 | bl(send) 382 | # Emit command byte 383 | movw(r5, WR | D_C) 384 | mov (r4, 0x2c) 385 | bl(send) 386 | # Send color 387 | mov(r5, WR) 388 | ldrb(r4, [r2, 0]) # red 389 | bl(send) 390 | 391 | ldrb(r4, [r2, 1]) # green 392 | bl(send) 393 | 394 | ldrb(r4, [r2, 2]) # blue 395 | bl(send) 396 | # and done 397 | # 398 | # Assembler version of 399 | # drawPixel, Portrait 400 | # 401 | @micropython.asm_thumb 402 | def drawPixel_P(r0, r1, r2): 403 | # r0: x, r1: y, r2: colorvector 404 | # set up pointers to GPIO 405 | # r4: changing data 406 | # r5: bit mask for control lines 407 | # r6: GPIOA ODR register ptr 408 | # r7: GPIOB BSSRL register ptr 409 | movwt(r6, stm.GPIOA) # target 410 | add (r6, stm.GPIO_ODR) 411 | movwt(r7, stm.GPIOB) 412 | add (r7, stm.GPIO_BSRRL) 413 | b(start) 414 | #inline subroutine 415 | label(send) 416 | strb(r4, [r6, 0]) # set command byte 417 | strh(r5, [r7, 2]) # WR and D_C low 418 | strh(r5, [r7, 0]) # WR and D_C high 419 | bx(lr) 420 | # Emit command byte 421 | label(start) 422 | movw(r5, WR | D_C) 423 | mov (r4, 0x2b) 424 | bl(send) 425 | 426 | mov(r5, 8) 427 | mov(r4, r0) # get x1 428 | asr(r4, r5) # get the upper byte 429 | mov(r5, WR) 430 | bl(send) 431 | 432 | mov(r4, r0) 433 | bl(send) 434 | 435 | mov(r0, 8) # from here on r0 keeps 8 436 | mov(r4, r2) # get x2 437 | asr(r4, r0) # get the upper byte 438 | bl(send) 439 | 440 | mov(r4, r2) 441 | bl(send) 442 | # Emit command byte 443 | movw(r5, WR | D_C) 444 | mov (r4, 0x2a) 445 | bl(send) 446 | 447 | mov(r5, WR) 448 | mov(r4, r1) # get y1 449 | asr(r4, r0) # get the upper byte 450 | bl(send) 451 | 452 | mov(r4, r1) 453 | bl(send) 454 | 455 | mov(r4, r3) # get x2 456 | asr(r4, r0) # get the upper byte 457 | bl(send) 458 | 459 | mov(r4, r3) 460 | bl(send) 461 | # Emit command byte 462 | movw(r5, WR | D_C) 463 | mov (r4, 0x2c) 464 | bl(send) 465 | # Send color 466 | mov(r5, WR) 467 | ldrb(r4, [r2, 0]) # red 468 | bl(send) 469 | 470 | ldrb(r4, [r2, 1]) # green 471 | bl(send) 472 | 473 | ldrb(r4, [r2, 2]) # blue 474 | bl(send) 475 | # Assembler version of 476 | # Fill screen by writing size pixels with the color given in data 477 | # data must be 3 bytes of red, green, blue 478 | # The area to be filled has to be set in advance by setXY 479 | # The speed is about 214 ns/pixel 480 | # 481 | @micropython.asm_thumb 482 | def fillSCR_AS(r0, r1): # r0: ptr to data, r1: number of pixels (3 bytes/pixel) 483 | # set up pointers to GPIO 484 | # r5: bit mask for control lines 485 | # r6: GPIOA ODR register ptr 486 | # r7: GPIOB BSSRL register ptr 487 | mov(r5, WR) 488 | movwt(r6, stm.GPIOA) # target 489 | add (r6, stm.GPIO_ODR) 490 | movwt(r7, stm.GPIOB) 491 | add (r7, stm.GPIO_BSRRL) 492 | ldrb(r2, [r0, 0]) # red 493 | ldrb(r3, [r0, 1]) # green 494 | ldrb(r4, [r0, 2]) # blue 495 | b(loopend) 496 | 497 | label(loopstart) 498 | strb(r2, [r6, 0]) # Store red 499 | strb(r5, [r7, 2]) # WR low 500 | # nop() 501 | strb(r5, [r7, 0]) # WR high 502 | 503 | strb(r3, [r6, 0]) # store blue 504 | strb(r5, [r7, 2]) # WR low 505 | nop() 506 | strb(r5, [r7, 0]) # WR high 507 | 508 | strb(r4, [r6, 0]) # store blue 509 | strb(r5, [r7, 2]) # WR low 510 | # nop() 511 | strb(r5, [r7, 0]) # WR high 512 | 513 | label(loopend) 514 | sub (r1, 1) # End of loop? 515 | bpl(loopstart) 516 | # 517 | # Assembler version of: 518 | # Fill screen by writing size pixels with the data 519 | # data must contains size triplets of red, green and blue data values 520 | # The area to be filled has to be set in advance by setXY 521 | # the speed is 266 ns for a byte triple 522 | # 523 | @micropython.asm_thumb 524 | def displaySCR_AS(r0, r1): # r0: ptr to data, r1: is number of pixels (3 bytes/pixel) 525 | # Color oder is blue-gree-red 526 | # set up pointers to GPIO 527 | # r5: bit mask for control lines 528 | # r6: GPIOA ODR register ptr 529 | # r7: GPIOB BSSRL register ptr 530 | mov(r5, WR) 531 | movwt(r6, stm.GPIOA) # target 532 | add (r6, stm.GPIO_ODR) 533 | movwt(r7, stm.GPIOB) 534 | add (r7, stm.GPIO_BSRRL) 535 | b(loopend) 536 | 537 | label(loopstart) 538 | ldrb(r2, [r0, 2]) # red 539 | strb(r2, [r6, 0]) # Store red 540 | strb(r5, [r7, 2]) # WR low 541 | strb(r5, [r7, 0]) # WR high 542 | 543 | ldrb(r2, [r0, 1]) # pre green 544 | strb(r2, [r6, 0]) # store greem 545 | strb(r5, [r7, 2]) # WR low 546 | strb(r5, [r7, 0]) # WR high 547 | 548 | ldrb(r2, [r0, 0]) # blue 549 | strb(r2, [r6, 0]) # store blue 550 | strb(r5, [r7, 2]) # WR low 551 | strb(r5, [r7, 0]) # WR high 552 | 553 | add (r0, 3) # advance data ptr 554 | 555 | label(loopend) 556 | sub (r1, 1) # End of loop? 557 | bpl(loopstart) 558 | # Assembler version of: 559 | # Fill screen by writing size pixels with the data 560 | # data must contains size packed duplets of red, green and blue data values 561 | # The area to be filled has to be set in advance by setXY 562 | # the speed is 266 ns for a byte pixel 563 | # 564 | @micropython.asm_thumb 565 | def displaySCR565_AS(r0, r1): # r0: ptr to data, r1: is number of pixels (3 bytes/pixel) 566 | # Color oder is blue-gree-red 567 | # set up pointers to GPIO 568 | # r5: bit mask for control lines 569 | # r6: GPIOA ODR register ptr 570 | # r7: GPIOB BSSRL register ptr 571 | mov(r5, WR) 572 | movwt(r6, stm.GPIOA) # target 573 | add (r6, stm.GPIO_ODR) 574 | movwt(r7, stm.GPIOB) 575 | add (r7, stm.GPIO_BSRRL) 576 | b(loopend) 577 | 578 | label(loopstart) 579 | 580 | ldrb(r2, [r0, 1]) # red 581 | mov (r3, 0xf8) # mask out lower 3 bits 582 | and_(r2, r3) 583 | strb(r2, [r6, 0]) # Store red 584 | strb(r5, [r7, 2]) # WR low 585 | strb(r5, [r7, 0]) # WR high 586 | 587 | ldrb(r2, [r0, 1]) # pre green 588 | mov (r3, 5) # shift 5 bits up to 589 | lsl(r2, r3) 590 | ldrb(r4, [r0, 0]) # get the next 3 bits 591 | mov (r3, 3) # shift 3 to the right 592 | lsr(r4, r3) 593 | orr(r2, r4) # add them to the first bits 594 | mov(r3, 0xfc) # mask off the lower two bits 595 | and_(r2, r3) 596 | strb(r2, [r6, 0]) # store green 597 | strb(r5, [r7, 2]) # WR low 598 | strb(r5, [r7, 0]) # WR high 599 | 600 | ldrb(r2, [r0, 0]) # blue 601 | mov (r3, 3) 602 | lsl(r2, r3) 603 | strb(r2, [r6, 0]) # store blue 604 | strb(r5, [r7, 2]) # WR low 605 | strb(r5, [r7, 0]) # WR high 606 | 607 | add (r0, 2) # advance data ptr 608 | 609 | label(loopend) 610 | 611 | sub (r1, 1) # End of loop? 612 | bpl(loopstart) 613 | # 614 | # Send a command and data to the TFT controller 615 | # cmd is the command byte, data must be a bytearray object with the command payload, 616 | # int is the size of the data 617 | # For the startup-phase use this function. 618 | # 619 | @micropython.viper 620 | def tft_cmd_data(cmd: int, data: ptr8, size: int): 621 | gpioa = ptr8(stm.GPIOA + stm.GPIO_ODR) 622 | gpiob = ptr16(stm.GPIOB + stm.GPIO_BSRRL) 623 | gpioa[0] = cmd # set data on port A 624 | gpiob[1] = D_C | WR # set C/D and WR low 625 | gpiob[0] = D_C | WR # set C/D and WR high 626 | for i in range(size): 627 | gpioa[0] = data[i] # set data on port A 628 | gpiob[1] = WR # set WR low. C/D still high 629 | gpiob[0] = WR # set WR high again 630 | # 631 | # Assembler version of send command & data to the TFT controller 632 | # data must be a bytearray object, int is the size of the data. 633 | # The speed is about 120 ns/byte 634 | # 635 | @micropython.asm_thumb 636 | def tft_cmd_data_AS(r0, r1, r2): # r0: command, r1: ptr to data, r2 is size in bytes 637 | # set up pointers to GPIO 638 | # r5: bit mask for control lines 639 | # r6: GPIOA ODR register ptr 640 | # r7: GPIOB BSSRL register ptr 641 | movwt(r6, stm.GPIOA) # target 642 | add (r6, stm.GPIO_ODR) 643 | movwt(r7, stm.GPIOB) 644 | add (r7, stm.GPIO_BSRRL) 645 | # Emit command byte 646 | mov(r5, WR | D_C) 647 | strb(r0, [r6, 0]) # set command byte 648 | strh(r5, [r7, 2]) # WR and D_C low 649 | strh(r5, [r7, 0]) # WR and D_C high 650 | # now loop though data 651 | mov(r5, WR) 652 | b(loopend) 653 | 654 | label(loopstart) 655 | ldrb(r4, [r1, 0]) # load data 656 | strb(r4, [r6, 0]) # Store data 657 | strh(r5, [r7, 2]) # WR low 658 | strh(r5, [r7, 0]) # WR high 659 | add (r1, 1) # advance data ptr 660 | 661 | label(loopend) 662 | sub (r2, 1) # End of loop? 663 | bpl(loopstart) 664 | # 665 | # Send a command to the TFT controller 666 | # 667 | @micropython.viper 668 | def tft_cmd(cmd: int): 669 | gpioa = ptr8(stm.GPIOA + stm.GPIO_ODR) 670 | gpiob = ptr16(stm.GPIOB + stm.GPIO_BSRRL) 671 | gpioa[0] = cmd # set data on port A 672 | gpiob[1] = D_C | WR # set C/D and WR low 673 | gpiob[0] = D_C | WR # set C/D and WR high 674 | # 675 | # Assembler version of send data to the TFT controller 676 | # data must be a bytearray object, int is the size of the data. 677 | # The speed is about 120 ns/byte 678 | # 679 | @micropython.asm_thumb 680 | def tft_write_data_AS(r0, r1): # r0: ptr to data, r1: is size in Bytes 681 | # set up pointers to GPIO 682 | # r5: bit mask for control lines 683 | # r6: GPIOA ODR register ptr 684 | # r7: GPIOB BSSRL register ptr 685 | movwt(r6, stm.GPIOA) # target 686 | add (r6, stm.GPIO_ODR) 687 | movwt(r7, stm.GPIOB) 688 | add (r7, stm.GPIO_BSRRL) 689 | mov(r5, WR) 690 | # and go, first test size for 0 691 | b(loopend) 692 | 693 | label(loopstart) 694 | ldrb(r3, [r0, 0]) # load data 695 | strb(r3, [r6, 0]) # Store data 696 | strh(r5, [r7, 2]) # WR low 697 | strh(r5, [r7, 0]) # WR high 698 | 699 | add (r0, 1) # advance data ptr 700 | label(loopend) 701 | sub (r1, 1) # End of loop? 702 | bpl(loopstart) 703 | # 704 | # Assembler version of send a command byte and read data from to the TFT controller 705 | # data must be a bytearray object, int is the size of the data. 706 | # The speed is about 130 ns/byte 707 | # 708 | @micropython.asm_thumb 709 | def tft_read_cmd_data_AS(r0, r1, r2): 710 | # r0: command, r1: ptr to data buffer, r2 is expected size in bytes 711 | # set up pointers to GPIO 712 | # r5: bit mask for control lines 713 | # r6: GPIOA base register ptr 714 | # r7: GPIOB BSSRL register ptr 715 | movwt(r6, stm.GPIOA) # target 716 | movwt(r7, stm.GPIOB) 717 | add (r7, stm.GPIO_BSRRL) 718 | # Emit command byte 719 | movw(r5, WR | D_C) 720 | strb(r0, [r6, stm.GPIO_ODR]) # set command byte 721 | strh(r5, [r7, 2]) # WR and D_C low 722 | strh(r5, [r7, 0]) # WR and D_C high 723 | # now switch gpioaa to input 724 | movw(r0, 0) 725 | strh(r0, [r6, stm.GPIO_MODER]) 726 | # now loop though data 727 | movw(r5, RD) 728 | b(loopend) 729 | 730 | label(loopstart) 731 | strh(r5, [r7, 2]) # RD low 732 | nop() # short delay 733 | nop() 734 | ldrb(r4, [r6, stm.GPIO_IDR]) # load data 735 | strh(r5, [r7, 0]) # RD high 736 | strb(r4, [r1, 0]) # Store data 737 | add (r1, 1) # advance data ptr 738 | 739 | label(loopend) 740 | sub (r2, 1) # End of loop? 741 | bpl(loopstart) 742 | # now switch gpioaa back to input 743 | movw(r0, 0x5555) 744 | strh(r0, [r6, stm.GPIO_MODER]) 745 | # 746 | # swap byte pairs in a buffer 747 | # sometimes needed for picture data 748 | # 749 | @micropython.asm_thumb 750 | def swapbytes(r0, r1): # bytearray, len(bytearray) 751 | mov(r2, 1) # divide loop count by 2 752 | lsr(r1, r2) # to avoid odd valued counter 753 | b(loopend) 754 | 755 | label(loopstart) 756 | ldrb(r2, [r0, 0]) 757 | ldrb(r3, [r0, 1]) 758 | strb(r3, [r0, 0]) 759 | strb(r2, [r0, 1]) 760 | add(r0, 2) 761 | 762 | label(loopend) 763 | sub (r1, 1) # End of loop? 764 | bpl(loopstart) 765 | 766 | # 767 | # swap colors red/blue in the buffer 768 | # 769 | @micropython.asm_thumb 770 | def swapcolors(r0, r1): # bytearray, len(bytearray) 771 | mov(r2, 3) 772 | udiv(r1, r1, r2) # 3 bytes per triple 773 | b(loopend) 774 | 775 | label(loopstart) 776 | ldrb(r2, [r0, 0]) 777 | ldrb(r3, [r0, 2]) 778 | strb(r3, [r0, 0]) 779 | strb(r2, [r0, 2]) 780 | add(r0, 3) 781 | 782 | label(loopend) 783 | sub (r1, 1) # End of loop? 784 | bpl(loopstart) 785 | 786 | -------------------------------------------------------------------------------- /TFTfont.py: -------------------------------------------------------------------------------- 1 | 2 | class TFTFont(object): 3 | def __init__(self, font, index, vert, horiz, nchars, firstchar = 32): 4 | self.firstchar = firstchar # ord(first_character) Future use: absent from C file. 5 | self.nchars = nchars # No. of chars in font 6 | self.bits_horiz = horiz # Width in pixels of char if rendered as monospaced 7 | self.bits_vert = vert # Height in pixels 8 | self._index = index 9 | self._font = font 10 | 11 | def get_ch(self, ch): 12 | from uctypes import addressof 13 | relch = ch - self.firstchar 14 | if relch > self.nchars or relch < 0: 15 | relch = 0 # instead of value error, typically this is space 16 | # raise ValueError('Character value {:} is unsupported.'.format(ch)) 17 | offset = relch * 2 # index is 2 bytes/char 18 | offset = self._index[offset] + (self._index[offset + 1] << 8) 19 | delta = (relch + 1) * 2 # index is 2 bytes/char 20 | delta = (self._index[delta] + (self._index[delta + 1] << 8)) - offset 21 | return addressof(self._font) + offset, self.bits_vert, (delta * 8) // self.bits_vert 22 | 23 | -------------------------------------------------------------------------------- /calibrate.py: -------------------------------------------------------------------------------- 1 | # 2 | # Some sample code 3 | # 4 | import os, gc 5 | from uctypes import addressof 6 | from tft import * 7 | from touch import * 8 | from font8mono import font8mono 9 | 10 | # 11 | # Get string dimensions in pixels 12 | # 13 | def get_stringsize(s, font): 14 | hor = 0 15 | for c in s: 16 | fontptr, vert, cols = font.get_ch(ord(c)) 17 | hor += cols 18 | return hor, vert 19 | 20 | def print_centered(tft, x, y, s, font): 21 | length, height = get_stringsize(s, font) 22 | tft.setTextPos(x - length // 2, y - height // 2) 23 | tft.printString(s) 24 | 25 | def draw_crosshair(tft, x, y): 26 | color = tft.getColor() # get previous color 27 | tft.setColor((255,0,0)) # red 28 | tft.drawHLine(x - 10, y, 20) 29 | tft.drawVLine(x, y - 10, 20) 30 | tft.setColor(color) # restore color 31 | 32 | def main(get_cal = False): 33 | 34 | mytft = TFT("SSD1963", "LB04301", LANDSCAPE) 35 | width, height = mytft.getScreensize() 36 | mytouch = TOUCH("XPT2046") 37 | mytft.backlight(100) # light on 38 | 39 | if get_cal: 40 | mytft.setTextStyle(None, None, 0, font8mono) 41 | mytouch.touch_parameter(confidence=20, margin = 40) # make it slow & precise 42 | print_centered(mytft, 240, 136, "Touch the crosshair in the upper left corner", font8mono) 43 | draw_crosshair(mytft, 10, 10) 44 | x1, y1 = mytouch.get_touch(raw = True) # need the raw values here 45 | 46 | mytft.clrSCR() 47 | print_centered(mytft, 240, 136, "Touch the crosshair in the upper right corner", font8mono) 48 | draw_crosshair(mytft, width - 11, 10) 49 | x2, y2 = mytouch.get_touch(raw = True) # need the raw values here 50 | 51 | mytft.clrSCR() 52 | print_centered(mytft, 240, 136, "Touch the crosshair in the lower left corner", font8mono) 53 | draw_crosshair(mytft, 10, height - 11) 54 | x3, y3 = mytouch.get_touch(raw = True) # need the raw values here 55 | 56 | mytft.clrSCR() 57 | print_centered(mytft, 240, 136, "Touch the crosshair in the lower right corner", font8mono) 58 | draw_crosshair(mytft, width - 11, height - 11) 59 | x4, y4 = mytouch.get_touch(raw = True) # need the raw values here 60 | 61 | mytft.clrSCR() 62 | xmul_top = (width - 20) / (x2 - x1) 63 | xadd_top = int(-x1 + 10 / xmul_top) 64 | xmul_bot = (width - 20) / (x4 - x3) 65 | xadd_bot = int(-x3 + 10 / xmul_bot) 66 | ymul_left = (height - 20) / (y3 - y1) 67 | yadd_left = int(-y1 + 10 / ymul_left) 68 | ymul_right = (height - 20) / (y4 - y2) 69 | yadd_right = int(-y2 + 10 / ymul_right) 70 | res = "({},{:6.4},{},{:6.4},{},{:6.4},{},{:6.4})".format( 71 | xadd_top, xmul_top, xadd_bot, xmul_bot, yadd_left, ymul_left, yadd_right, ymul_right) 72 | mytft.setColor((255,255,255)) 73 | print_centered(mytft, 240, 120, "Calibration =", font8mono) 74 | print_centered(mytft, 240, 136, res, font8mono) 75 | print ("Calibration =", res) 76 | mytouch.touch_parameter(confidence = 5, margin = 20, 77 | calibration = (xadd_top, xmul_top, xadd_bot, xmul_bot, yadd_left, ymul_left, yadd_right, ymul_right)) 78 | print_centered(mytft, 240, 152, "Now you may touch for testing", font8mono) 79 | else: 80 | print_centered(mytft, 240, 136, "Please touch me!", font8mono) 81 | mytft.setColor((0, 255, 0)) # green as can be 82 | while True: 83 | res = mytouch.get_touch() 84 | if res: 85 | mytft.fillCircle(res[0], res[1], 5) 86 | 87 | main(True) 88 | 89 | -------------------------------------------------------------------------------- /font14.py: -------------------------------------------------------------------------------- 1 | # Code generated by font-to-py.py. 2 | # Font: FreeSans.ttf 3 | version = '0.1' 4 | 5 | def height(): 6 | return 23 7 | 8 | def max_width(): 9 | return 23 10 | 11 | def hmap(): 12 | return True 13 | 14 | def reverse(): 15 | return False 16 | 17 | def monospaced(): 18 | return False 19 | 20 | _font =\ 21 | b'\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 22 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\xc0\xc0\xc0\xc0'\ 23 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\xc0\xc0\x00\x00\x00'\ 24 | b'\x00\x00\x08\x00\x00\x00\xcc\xcc\xcc\xcc\x88\x88\x00\x00\x00\x00'\ 25 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00'\ 26 | b'\x00\x04\x60\x04\x60\x0c\x60\x0c\x40\x7f\xf8\x7f\xf8\x08\xc0\x18'\ 27 | b'\xc0\x18\xc0\x18\x80\xff\xf0\xff\xf0\x31\x80\x31\x80\x31\x00\x33'\ 28 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x04\x00\x1f'\ 29 | b'\x00\x7f\xc0\x64\xc0\xc4\x60\xc4\x60\xc4\x00\xc4\x00\x74\x00\x7e'\ 30 | b'\x00\x1f\xc0\x05\xe0\x04\x60\xc4\x60\xc4\x60\xe4\xe0\x7f\xc0\x1f'\ 31 | b'\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x00\x00\x00'\ 32 | b'\x00\x00\x00\x00\x04\x00\x3c\x04\x00\x7e\x08\x00\xe7\x18\x00\xc3'\ 33 | b'\x10\x00\xe7\x30\x00\x7e\x20\x00\x3c\x40\x00\x00\x47\x80\x00\x8f'\ 34 | b'\xc0\x01\x9c\xe0\x01\x18\x60\x03\x18\x60\x02\x1c\xe0\x04\x0f\xc0'\ 35 | b'\x04\x07\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 36 | b'\x00\x00\x0f\x00\x00\x00\x00\x00\x0f\x00\x1f\x80\x39\xc0\x30\xc0'\ 37 | b'\x30\xc0\x19\x80\x1f\x00\x1e\x00\x37\x30\x63\xb0\xc1\xf0\xc0\xe0'\ 38 | b'\xc0\x60\xe1\xf0\x7f\xb0\x3e\x18\x00\x00\x00\x00\x00\x00\x00\x00'\ 39 | b'\x00\x00\x05\x00\x00\x00\xc0\xc0\xc0\xc0\x80\x80\x00\x00\x00\x00'\ 40 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x00\x00\x08\x10'\ 41 | b'\x30\x20\x60\x60\x40\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x60\x60\x60'\ 42 | b'\x30\x30\x10\x08\x08\x00\x00\x80\x40\x60\x20\x30\x30\x10\x18\x18'\ 43 | b'\x18\x18\x18\x18\x18\x18\x30\x30\x30\x60\x60\x40\x80\x09\x00\x00'\ 44 | b'\x00\x10\x00\x10\x00\xd6\x00\x7c\x00\x38\x00\x6c\x00\x48\x00\x00'\ 45 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 46 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00'\ 47 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x0c'\ 48 | b'\x00\x0c\x00\x0c\x00\xff\xc0\xff\xc0\x0c\x00\x0c\x00\x0c\x00\x0c'\ 49 | b'\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00'\ 50 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0'\ 51 | b'\xc0\x40\x40\x80\x00\x00\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 52 | b'\x00\x00\xf8\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06'\ 53 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 54 | b'\x00\xc0\xc0\x00\x00\x00\x00\x00\x06\x00\x00\x04\x0c\x08\x08\x18'\ 55 | b'\x10\x10\x10\x30\x20\x20\x20\x60\x40\x40\xc0\x80\x00\x00\x00\x00'\ 56 | b'\x00\x0d\x00\x00\x00\x00\x00\x1f\x00\x3f\x80\x71\xc0\x60\xc0\xc0'\ 57 | b'\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xc0\x60\x60'\ 58 | b'\xc0\x71\xc0\x3f\x80\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 59 | b'\x00\x0d\x00\x00\x00\x00\x00\x08\x00\x18\x00\x38\x00\xf8\x00\xf8'\ 60 | b'\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18\x00\x18'\ 61 | b'\x00\x18\x00\x18\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 62 | b'\x00\x0d\x00\x00\x00\x00\x00\x1f\x00\x7f\xc0\x60\xe0\xc0\x60\xc0'\ 63 | b'\x60\x00\x60\x00\x60\x01\xc0\x03\x80\x0f\x00\x1c\x00\x30\x00\x20'\ 64 | b'\x00\x40\x00\x7f\xe0\x7f\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 65 | b'\x00\x0d\x00\x00\x00\x00\x00\x1f\x80\x7f\xc0\x60\xe0\xc0\x60\xc0'\ 66 | b'\x60\x00\xe0\x0f\xc0\x0f\xc0\x00\xe0\x00\x60\x00\x60\xc0\x60\xc0'\ 67 | b'\x60\x60\xc0\x7f\xc0\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 68 | b'\x00\x0d\x00\x00\x00\x00\x00\x03\x00\x03\x00\x07\x00\x0f\x00\x1b'\ 69 | b'\x00\x13\x00\x33\x00\x63\x00\x43\x00\x83\x00\xff\xc0\xff\xc0\x03'\ 70 | b'\x00\x03\x00\x03\x00\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 71 | b'\x00\x0d\x00\x00\x00\x00\x00\x7f\xc0\x7f\xc0\x60\x00\x60\x00\x60'\ 72 | b'\x00\x6f\x00\x7f\xc0\xe1\xc0\x00\xe0\x00\x60\x00\x60\x00\x60\xc0'\ 73 | b'\x60\xe1\xc0\x7f\x80\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 74 | b'\x00\x0d\x00\x00\x00\x00\x00\x0f\x00\x3f\xc0\x70\xc0\x60\xe0\x40'\ 75 | b'\x00\xc0\x00\xcf\x00\xff\xc0\xe0\xc0\xc0\x60\xc0\x60\xc0\x60\x40'\ 76 | b'\x60\x70\xc0\x3f\xc0\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 77 | b'\x00\x0d\x00\x00\x00\x00\x00\xff\xc0\xff\xc0\x00\x80\x01\x80\x01'\ 78 | b'\x00\x03\x00\x06\x00\x06\x00\x0c\x00\x0c\x00\x08\x00\x18\x00\x18'\ 79 | b'\x00\x10\x00\x30\x00\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 80 | b'\x00\x0d\x00\x00\x00\x00\x00\x1f\x00\x3f\x80\x71\xc0\x60\xc0\x60'\ 81 | b'\xc0\x71\xc0\x3f\x80\x3f\x80\x71\xc0\xc0\x60\xc0\x60\xc0\x60\xc0'\ 82 | b'\x60\x60\xc0\x3f\x80\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 83 | b'\x00\x0d\x00\x00\x00\x00\x00\x1e\x00\x7f\x80\x61\x80\xc0\xc0\xc0'\ 84 | b'\xc0\xc0\xc0\xc0\xc0\x61\xc0\x7f\xc0\x3e\xc0\x00\xc0\x00\xc0\xc1'\ 85 | b'\x80\x63\x80\x7f\x00\x1e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 86 | b'\x00\x06\x00\x00\x00\x00\x00\x00\x00\xc0\xc0\x00\x00\x00\x00\x00'\ 87 | b'\x00\x00\x00\xc0\xc0\x00\x00\x00\x00\x00\x06\x00\x00\x00\x00\x00'\ 88 | b'\x00\x00\xc0\xc0\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xc0\x40\x40'\ 89 | b'\x80\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 90 | b'\x00\x00\x00\x00\x20\x01\xe0\x07\x80\x1e\x00\xf0\x00\xc0\x00\x78'\ 91 | b'\x00\x1e\x00\x07\xc0\x00\xe0\x00\x20\x00\x00\x00\x00\x00\x00\x00'\ 92 | b'\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 93 | b'\x00\x00\x00\x00\x00\x00\x00\xff\xe0\xff\xe0\x00\x00\x00\x00\xff'\ 94 | b'\xe0\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 95 | b'\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 96 | b'\x00\x00\x00\xc0\x00\xf0\x00\x3c\x00\x0f\x80\x01\xe0\x00\x60\x01'\ 97 | b'\xe0\x0f\x00\x3c\x00\xf0\x00\x80\x00\x00\x00\x00\x00\x00\x00\x00'\ 98 | b'\x00\x00\x00\x0d\x00\x00\x00\x3f\x00\x7f\x80\xe1\xc0\xc0\xc0\xc0'\ 99 | b'\xc0\x00\xc0\x01\xc0\x01\x80\x07\x00\x06\x00\x0c\x00\x0c\x00\x0c'\ 100 | b'\x00\x00\x00\x00\x00\x0c\x00\x0c\x00\x00\x00\x00\x00\x00\x00\x00'\ 101 | b'\x00\x00\x00\x17\x00\x00\x00\x00\x00\xfe\x00\x07\xff\x80\x0f\x03'\ 102 | b'\xc0\x1c\x00\xe0\x38\x70\x70\x71\xfb\x30\x61\x8f\x18\x63\x06\x18'\ 103 | b'\xc7\x06\x18\xc6\x06\x18\xc6\x04\x18\xc6\x0c\x30\xc6\x0c\x30\xe7'\ 104 | b'\x1c\x60\x63\xff\xe0\x71\xc7\x80\x38\x00\x00\x1e\x00\x00\x07\xfe'\ 105 | b'\x00\x01\xfc\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x03\x80'\ 106 | b'\x03\xc0\x07\xc0\x06\xc0\x06\xe0\x0e\x60\x0c\x60\x0c\x70\x18\x30'\ 107 | b'\x18\x30\x1f\xf8\x3f\xf8\x30\x18\x30\x1c\x70\x0c\x60\x0c\x60\x0e'\ 108 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\xff\x80'\ 109 | b'\xff\xc0\xc0\xe0\xc0\x60\xc0\x60\xc0\x60\xc0\xc0\xff\x80\xff\xc0'\ 110 | b'\xc0\x60\xc0\x30\xc0\x30\xc0\x30\xc0\x30\xc0\x60\xff\xe0\xff\xc0'\ 111 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x07\xe0'\ 112 | b'\x1f\xf0\x38\x38\x70\x1c\x60\x0c\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 113 | b'\xc0\x00\xc0\x00\xc0\x0c\x60\x0c\x70\x18\x38\x38\x1f\xf0\x07\xc0'\ 114 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\xff\x80'\ 115 | b'\xff\xe0\xc0\xe0\xc0\x30\xc0\x30\xc0\x18\xc0\x18\xc0\x18\xc0\x18'\ 116 | b'\xc0\x18\xc0\x18\xc0\x18\xc0\x30\xc0\x30\xc0\xe0\xff\xc0\xff\x80'\ 117 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\xff\xe0'\ 118 | b'\xff\xe0\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xff\xe0\xff\xe0'\ 119 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xff\xf0\xff\xf0'\ 120 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\xff\xe0'\ 121 | b'\xff\xe0\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xff\xc0\xff\xc0'\ 122 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00'\ 123 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x00\x07'\ 124 | b'\xe0\x00\x1f\xf8\x00\x38\x1c\x00\x70\x0e\x00\x60\x06\x00\xe0\x00'\ 125 | b'\x00\xc0\x00\x00\xc0\x00\x00\xc0\x7e\x00\xc0\x7e\x00\xc0\x06\x00'\ 126 | b'\xe0\x06\x00\x60\x06\x00\x70\x0e\x00\x38\x3e\x00\x1f\xf6\x00\x07'\ 127 | b'\xc2\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 128 | b'\x00\x11\x00\x00\x00\x00\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00\xc0'\ 129 | b'\x18\x00\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00\xff\xf8\x00\xff\xf8'\ 130 | b'\x00\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00'\ 131 | b'\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00\x00\x00\x00\x00\x00\x00\x00'\ 132 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\xc0\xc0\xc0\xc0\xc0'\ 133 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00'\ 134 | b'\x00\x0c\x00\x00\x00\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01'\ 135 | b'\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\x01\x80\xc1\x80\xc1'\ 136 | b'\x80\xe3\x80\x7f\x00\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 137 | b'\x00\x0f\x00\x00\x00\xc0\x38\xc0\x70\xc0\xe0\xc1\xc0\xc3\x80\xc7'\ 138 | b'\x00\xce\x00\xde\x00\xfe\x00\xf3\x00\xe3\x80\xc1\x80\xc0\xc0\xc0'\ 139 | b'\xe0\xc0\x60\xc0\x30\xc0\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 140 | b'\x00\x0d\x00\x00\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0'\ 141 | b'\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0'\ 142 | b'\x00\xc0\x00\xff\xc0\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 143 | b'\x00\x13\x00\x00\x00\x00\xe0\x07\x00\xe0\x07\x00\xf0\x0f\x00\xf0'\ 144 | b'\x0f\x00\xd8\x0b\x00\xd8\x1b\x00\xd8\x1b\x00\xcc\x13\x00\xcc\x33'\ 145 | b'\x00\xcc\x33\x00\xc6\x63\x00\xc6\x63\x00\xc6\x63\x00\xc3\xc3\x00'\ 146 | b'\xc3\xc3\x00\xc3\xc3\x00\xc1\x83\x00\x00\x00\x00\x00\x00\x00\x00'\ 147 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00\x00\xe0\x18\x00'\ 148 | b'\xe0\x18\x00\xf0\x18\x00\xf0\x18\x00\xd8\x18\x00\xdc\x18\x00\xcc'\ 149 | b'\x18\x00\xc6\x18\x00\xc7\x18\x00\xc3\x18\x00\xc1\x98\x00\xc1\x98'\ 150 | b'\x00\xc0\xd8\x00\xc0\xf8\x00\xc0\x78\x00\xc0\x38\x00\xc0\x38\x00'\ 151 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12'\ 152 | b'\x00\x00\x00\x00\x07\xe0\x00\x1f\xf8\x00\x38\x1c\x00\x70\x0e\x00'\ 153 | b'\x60\x06\x00\xe0\x07\x00\xc0\x03\x00\xc0\x03\x00\xc0\x03\x00\xc0'\ 154 | b'\x03\x00\xc0\x03\x00\xe0\x07\x00\x60\x06\x00\x70\x0e\x00\x38\x1c'\ 155 | b'\x00\x1f\xf8\x00\x07\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 156 | b'\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\xff\x80\xff\xe0\xc0\x60'\ 157 | b'\xc0\x30\xc0\x30\xc0\x30\xc0\x30\xc0\x70\xff\xe0\xff\xc0\xc0\x00'\ 158 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x00\x00'\ 159 | b'\x00\x00\x00\x00\x00\x00\x12\x00\x00\x00\x00\x07\xe0\x00\x1f\xf8'\ 160 | b'\x00\x38\x1c\x00\x70\x0e\x00\x60\x06\x00\xe0\x07\x00\xc0\x03\x00'\ 161 | b'\xc0\x03\x00\xc0\x03\x00\xc0\x03\x00\xc0\x03\x00\xe0\x07\x00\x60'\ 162 | b'\x26\x00\x70\x3e\x00\x38\x3c\x00\x1f\xfe\x00\x07\xe6\x00\x00\x02'\ 163 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00'\ 164 | b'\x00\xff\xc0\xff\xe0\xc0\x70\xc0\x30\xc0\x30\xc0\x30\xc0\x30\xc0'\ 165 | b'\x60\xff\xc0\xff\xe0\xc0\x70\xc0\x30\xc0\x30\xc0\x30\xc0\x30\xc0'\ 166 | b'\x30\xc0\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00'\ 167 | b'\x00\x0f\xc0\x3f\xf0\x30\x70\x60\x18\x60\x18\x60\x00\x70\x00\x3e'\ 168 | b'\x00\x1f\xe0\x01\xf0\x00\x38\x00\x18\xc0\x18\xc0\x18\x70\x30\x3f'\ 169 | b'\xf0\x0f\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00'\ 170 | b'\x00\xff\xf0\xff\xf0\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06'\ 171 | b'\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06\x00\x06'\ 172 | b'\x00\x06\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00'\ 173 | b'\x00\x00\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00\xc0\x18'\ 174 | b'\x00\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00'\ 175 | b'\xc0\x18\x00\xc0\x18\x00\xc0\x18\x00\xe0\x38\x00\x70\x70\x00\x3f'\ 176 | b'\xe0\x00\x1f\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 177 | b'\x00\x00\x00\x00\x0f\x00\x00\x00\x60\x0c\x60\x0c\x60\x1c\x30\x18'\ 178 | b'\x30\x18\x38\x38\x18\x30\x18\x30\x1c\x70\x0c\x60\x0c\x60\x0e\xe0'\ 179 | b'\x06\xc0\x06\xc0\x07\xc0\x03\x80\x03\x80\x00\x00\x00\x00\x00\x00'\ 180 | b'\x00\x00\x00\x00\x16\x00\x00\x00\x00\xe0\x70\x18\x60\x70\x38\x60'\ 181 | b'\x78\x38\x60\x78\x30\x70\xd8\x30\x30\xd8\x30\x30\xcc\x70\x30\xcc'\ 182 | b'\x60\x39\x8c\x60\x19\x86\x60\x19\x86\xe0\x1b\x86\xc0\x1f\x06\xc0'\ 183 | b'\x0f\x03\xc0\x0f\x03\xc0\x0e\x03\x80\x0e\x03\x80\x00\x00\x00\x00'\ 184 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0f\x00\x00\x00\x70'\ 185 | b'\x0c\x30\x18\x38\x38\x1c\x30\x0c\x60\x0e\xe0\x06\xc0\x03\x80\x03'\ 186 | b'\x80\x07\xc0\x06\xc0\x0e\x60\x1c\x70\x18\x30\x38\x18\x70\x1c\x60'\ 187 | b'\x0c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\xc0'\ 188 | b'\x1c\xe0\x18\x70\x38\x30\x30\x38\x70\x18\x60\x0c\xc0\x0f\xc0\x07'\ 189 | b'\x80\x07\x80\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03\x00\x03'\ 190 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0e\x00\x00\x00\xff'\ 191 | b'\xf0\xff\xf0\x00\x70\x00\x60\x00\xe0\x01\xc0\x03\x80\x03\x00\x06'\ 192 | b'\x00\x0e\x00\x1c\x00\x38\x00\x30\x00\x70\x00\xe0\x00\xff\xf0\xff'\ 193 | b'\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\xf0\xf0'\ 194 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 195 | b'\xc0\xc0\xf0\xf0\x06\x00\x00\x80\xc0\x40\x40\x60\x20\x20\x20\x30'\ 196 | b'\x10\x10\x10\x18\x08\x08\x0c\x04\x00\x00\x00\x00\x00\x06\x00\x00'\ 197 | b'\xf0\xf0\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30\x30'\ 198 | b'\x30\x30\x30\x30\xf0\xf0\x0b\x00\x00\x00\x00\x00\x18\x00\x1c\x00'\ 199 | b'\x14\x00\x36\x00\x26\x00\x62\x00\x63\x00\x41\x00\xc1\x80\x00\x00'\ 200 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 201 | b'\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 202 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 203 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xf8\x00\x00'\ 204 | b'\x00\x00\x00\x00\x00\x00\x06\x00\x00\x60\x30\x18\x00\x00\x00\x00'\ 205 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c'\ 206 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\x00\x7f'\ 207 | b'\x80\xc1\xc0\x00\xc0\x00\xc0\x1f\xc0\x7c\xc0\xc0\xc0\xc0\xc0\xc3'\ 208 | b'\xc0\x7e\xe0\x3c\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d'\ 209 | b'\x00\x00\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xcf\x00\xff'\ 210 | b'\xc0\xf1\xc0\xe0\xe0\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xe0\xe0\xf1'\ 211 | b'\xc0\xdf\x80\xcf\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c'\ 212 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x3f'\ 213 | b'\x80\x61\xc0\xe0\xc0\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xe0\xc0\x61'\ 214 | b'\xc0\x7f\x80\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d'\ 215 | b'\x00\x00\x00\x00\x60\x00\x60\x00\x60\x00\x60\x00\x60\x1e\x60\x3f'\ 216 | b'\xe0\x71\xe0\xe0\xe0\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xe0\xe0\x71'\ 217 | b'\xe0\x3f\x60\x1e\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c'\ 218 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x3f'\ 219 | b'\x80\x70\xc0\xe0\x60\xc0\x60\xff\xe0\xff\xe0\xc0\x00\xc0\x60\x60'\ 220 | b'\xc0\x3f\xc0\x1f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06'\ 221 | b'\x00\x00\x30\x70\x60\x60\x60\xf0\xf0\x60\x60\x60\x60\x60\x60\x60'\ 222 | b'\x60\x60\x60\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00\x00\x00\x00'\ 223 | b'\x00\x00\x00\x00\x00\x00\x1e\xc0\x7f\xc0\x61\xc0\xe1\xc0\xc0\xc0'\ 224 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xe1\xc0\x61\xc0\x7e\xc0\x1c\xc0\x00\xc0'\ 225 | b'\x01\xc0\xc1\x80\x7f\x80\x3e\x00\x0c\x00\x00\x00\xc0\x00\xc0\x00'\ 226 | b'\xc0\x00\xc0\x00\xc0\x00\xcf\x00\xdf\xc0\xf1\xc0\xe0\xc0\xc0\xc0'\ 227 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00'\ 228 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\xc0\xc0\x00\x00\x00'\ 229 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00'\ 230 | b'\x00\x06\x00\x00\x30\x30\x00\x00\x00\x30\x30\x30\x30\x30\x30\x30'\ 231 | b'\x30\x30\x30\x30\x30\x30\x30\x30\xf0\xe0\x0c\x00\x00\x00\xc0\x00'\ 232 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\xc1\x80\xc3\x00\xc6\x00\xcc\x00'\ 233 | b'\xdc\x00\xfc\x00\xee\x00\xc6\x00\xc3\x00\xc3\x80\xc1\x80\xc0\xc0'\ 234 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\xc0\xc0\xc0'\ 235 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00'\ 236 | b'\x00\x00\x00\x13\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 237 | b'\x00\x00\x00\x00\x00\x00\x00\xce\x3e\x00\xdf\x7f\x00\xe3\xc7\x00'\ 238 | b'\xc1\x83\x00\xc1\x83\x00\xc1\x83\x00\xc1\x83\x00\xc1\x83\x00\xc1'\ 239 | b'\x83\x00\xc1\x83\x00\xc1\x83\x00\xc1\x83\x00\x00\x00\x00\x00\x00'\ 240 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00'\ 241 | b'\x00\x00\x00\x00\x00\x00\x00\x00\xcf\x00\xdf\xc0\xf1\xc0\xe0\xc0'\ 242 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 243 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00\x00\x00'\ 244 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x1f\x00\x3f\x80\x71\xc0\xe0\xe0'\ 245 | b'\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xe0\xe0\x71\xc0\x3f\x80\x1f\x00'\ 246 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0d\x00\x00\x00\x00\x00'\ 247 | b'\x00\x00\x00\x00\x00\x00\x00\x00\xcf\x00\xdf\x80\xf1\xc0\xe0\xe0'\ 248 | b'\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xe0\xe0\xf1\xc0\xff\x80\xcf\x00'\ 249 | b'\xc0\x00\xc0\x00\xc0\x00\xc0\x00\x00\x00\x0d\x00\x00\x00\x00\x00'\ 250 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x60\x3f\xe0\x71\xe0\xe0\xe0'\ 251 | b'\xc0\x60\xc0\x60\xc0\x60\xc0\x60\xe0\xe0\x71\xe0\x7f\xe0\x1e\x60'\ 252 | b'\x00\x60\x00\x60\x00\x60\x00\x60\x00\x00\x08\x00\x00\x00\x00\x00'\ 253 | b'\x00\x00\xcc\xdc\xf0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00'\ 254 | b'\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 255 | b'\x00\x3f\x00\x7f\xc0\xe0\xc0\xc0\x00\xe0\x00\x7e\x00\x1f\x80\x01'\ 256 | b'\xc0\xc0\xc0\xe1\xc0\x7f\x80\x3f\x00\x00\x00\x00\x00\x00\x00\x00'\ 257 | b'\x00\x00\x00\x06\x00\x00\x00\x00\x00\x60\x60\xf0\xf0\x60\x60\x60'\ 258 | b'\x60\x60\x60\x60\x60\x70\x70\x00\x00\x00\x00\x00\x0c\x00\x00\x00'\ 259 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc0\xc0\xc0\xc0\xc0\xc0'\ 260 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc1\xc0\xe3\xc0\xfe\xc0'\ 261 | b'\x3c\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x0b\x00\x00\x00'\ 262 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x60\x60\xe0\x60\xc0'\ 263 | b'\x70\xc0\x31\x80\x31\x80\x19\x80\x1b\x00\x1b\x00\x0f\x00\x0e\x00'\ 264 | b'\x0e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00'\ 265 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 266 | b'\xc1\xc3\x00\x61\xc3\x00\x63\xc3\x00\x63\xc6\x00\x33\x66\x00\x33'\ 267 | b'\x66\x00\x32\x66\x00\x36\x6c\x00\x1e\x3c\x00\x1e\x3c\x00\x1c\x38'\ 268 | b'\x00\x0c\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 269 | b'\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 270 | b'\x00\x60\xc0\x71\xc0\x31\x80\x1b\x00\x0e\x00\x0e\x00\x0e\x00\x1b'\ 271 | b'\x00\x1b\x00\x31\x80\x60\xc0\x60\xc0\x00\x00\x00\x00\x00\x00\x00'\ 272 | b'\x00\x00\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 273 | b'\x00\xc0\xe0\x60\xc0\x60\xc0\x61\x80\x31\x80\x31\x80\x33\x00\x1b'\ 274 | b'\x00\x1b\x00\x1e\x00\x0e\x00\x0e\x00\x0c\x00\x0c\x00\x18\x00\x78'\ 275 | b'\x00\x70\x00\x0b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 276 | b'\x00\xff\x80\xff\x80\x03\x80\x03\x00\x06\x00\x0c\x00\x18\x00\x30'\ 277 | b'\x00\x60\x00\xc0\x00\xff\x80\xff\x80\x00\x00\x00\x00\x00\x00\x00'\ 278 | b'\x00\x00\x00\x08\x00\x00\x18\x38\x30\x30\x30\x30\x30\x30\x30\x30'\ 279 | b'\xe0\xe0\x30\x30\x30\x30\x30\x30\x30\x30\x38\x18\x06\x00\x00\xc0'\ 280 | b'\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0\xc0'\ 281 | b'\xc0\xc0\xc0\xc0\xc0\x08\x00\x00\xc0\xe0\x60\x60\x60\x60\x60\x60'\ 282 | b'\x60\x60\x38\x38\x60\x60\x60\x60\x60\x60\x60\x60\xe0\xc0\x0c\x00'\ 283 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x00'\ 284 | b'\xf8\x00\x9c\x80\x0f\x80\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00'\ 285 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 286 | 287 | _index =\ 288 | b'\x00\x00\x19\x00\x32\x00\x4b\x00\x7b\x00\xab\x00\xf2\x00\x22\x01'\ 289 | b'\x3b\x01\x54\x01\x6d\x01\x9d\x01\xcd\x01\xe6\x01\xff\x01\x18\x02'\ 290 | b'\x31\x02\x61\x02\x91\x02\xc1\x02\xf1\x02\x21\x03\x51\x03\x81\x03'\ 291 | b'\xb1\x03\xe1\x03\x11\x04\x2a\x04\x43\x04\x73\x04\xa3\x04\xd3\x04'\ 292 | b'\x03\x05\x4a\x05\x7a\x05\xaa\x05\xda\x05\x0a\x06\x3a\x06\x6a\x06'\ 293 | b'\xb1\x06\xf8\x06\x11\x07\x41\x07\x71\x07\xa1\x07\xe8\x07\x2f\x08'\ 294 | b'\x76\x08\xa6\x08\xed\x08\x1d\x09\x4d\x09\x7d\x09\xc4\x09\xf4\x09'\ 295 | b'\x3b\x0a\x6b\x0a\x9b\x0a\xcb\x0a\xe4\x0a\xfd\x0a\x16\x0b\x46\x0b'\ 296 | b'\x76\x0b\x8f\x0b\xbf\x0b\xef\x0b\x1f\x0c\x4f\x0c\x7f\x0c\x98\x0c'\ 297 | b'\xc8\x0c\xf8\x0c\x11\x0d\x2a\x0d\x5a\x0d\x73\x0d\xba\x0d\xea\x0d'\ 298 | b'\x1a\x0e\x4a\x0e\x7a\x0e\x93\x0e\xc3\x0e\xdc\x0e\x0c\x0f\x3c\x0f'\ 299 | b'\x83\x0f\xb3\x0f\xe3\x0f\x13\x10\x2c\x10\x45\x10\x5e\x10\x8e\x10'\ 300 | 301 | _mvfont = memoryview(_font) 302 | 303 | def _chr_addr(ordch): 304 | offset = 2 * (ordch - 32) 305 | return int.from_bytes(_index[offset:offset + 2], 'little') 306 | 307 | def get_ch(ch): 308 | ordch = ord(ch) 309 | ordch = ordch if ordch >= 32 and ordch <= 126 else ord('?') 310 | offset = _chr_addr(ordch) 311 | width = int.from_bytes(_font[offset:offset + 2], 'little') 312 | next_offs = _chr_addr(ordch +1) 313 | return _mvfont[offset + 2:next_offs], 23, width 314 | 315 | -------------------------------------------------------------------------------- /font8mono.py: -------------------------------------------------------------------------------- 1 | # Code generated by cfonts_to_trans_py.py 2 | import TFTfont 3 | _font8mono = (b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 4 | b'\x00\x00\x10\x10\x10\x10\x10\x10\x10\x00\x10\x00\x00\x00' 5 | b'\x00\x28\x28\x28\x28\x00\x00\x00\x00\x00\x00\x00\x00\x00' 6 | b'\x00\x14\x14\x14\x7e\x14\x28\x7e\x28\x28\x28\x00\x00\x00' 7 | b'\x08\x3c\x4a\x4a\x48\x3c\x0a\x0a\x4a\x4a\x3c\x08\x00\x00' 8 | b'\x00\x30\x4a\x4c\x38\x10\x10\x2c\x52\x12\x0c\x00\x00\x00' 9 | b'\x00\x18\x24\x24\x28\x10\x2a\x4a\x44\x4a\x3a\x00\x00\x00' 10 | b'\x00\x10\x10\x10\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00' 11 | b'\x00\x00\x04\x08\x08\x10\x10\x10\x10\x10\x10\x08\x08\x04' 12 | b'\x00\x00\x20\x10\x10\x08\x08\x08\x08\x08\x08\x10\x10\x20' 13 | b'\x00\x10\x38\x10\x28\x00\x00\x00\x00\x00\x00\x00\x00\x00' 14 | b'\x00\x00\x00\x10\x10\x10\xfe\x10\x10\x10\x00\x00\x00\x00' 15 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x10\x10\x00' 16 | b'\x00\x00\x00\x00\x00\x00\x00\x3c\x00\x00\x00\x00\x00\x00' 17 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00' 18 | b'\x00\x04\x04\x08\x08\x08\x10\x10\x10\x20\x20\x00\x00\x00' 19 | b'\x00\x3c\x42\x42\x42\x4a\x42\x42\x42\x42\x3c\x00\x00\x00' 20 | b'\x00\x08\x18\x28\x08\x08\x08\x08\x08\x08\x08\x00\x00\x00' 21 | b'\x00\x3c\x42\x42\x02\x04\x04\x08\x30\x40\x7e\x00\x00\x00' 22 | b'\x00\x3c\x42\x02\x02\x1c\x02\x02\x42\x42\x3c\x00\x00\x00' 23 | b'\x00\x04\x0c\x14\x14\x24\x24\x44\x7e\x04\x04\x00\x00\x00' 24 | b'\x00\x7e\x40\x40\x40\x7c\x42\x02\x02\x42\x3c\x00\x00\x00' 25 | b'\x00\x1c\x22\x42\x40\x7c\x42\x42\x42\x42\x3c\x00\x00\x00' 26 | b'\x00\x7e\x02\x04\x08\x08\x10\x10\x20\x20\x20\x00\x00\x00' 27 | b'\x00\x3c\x42\x42\x42\x3c\x42\x42\x42\x42\x3c\x00\x00\x00' 28 | b'\x00\x3c\x42\x42\x42\x42\x3e\x02\x42\x44\x38\x00\x00\x00' 29 | b'\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x10\x00\x00\x00' 30 | b'\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x10\x10\x10\x00' 31 | b'\x00\x00\x00\x02\x0c\x30\x40\x30\x0c\x02\x00\x00\x00\x00' 32 | b'\x00\x00\x00\x00\x7e\x00\x00\x7e\x00\x00\x00\x00\x00\x00' 33 | b'\x00\x00\x00\x40\x30\x0c\x02\x0c\x30\x40\x00\x00\x00\x00' 34 | b'\x00\x3c\x42\x42\x02\x02\x04\x08\x08\x00\x08\x00\x00\x00' 35 | b'\x00\x1c\x22\x42\x4e\x52\x52\x5e\x40\x20\x1c\x00\x00\x00' 36 | b'\x00\x10\x28\x28\x28\x44\x44\x7c\x44\x82\x82\x00\x00\x00' 37 | b'\x00\x7c\x42\x42\x42\x7c\x42\x42\x42\x42\x7c\x00\x00\x00' 38 | b'\x00\x1c\x22\x42\x40\x40\x40\x40\x42\x22\x1c\x00\x00\x00' 39 | b'\x00\x78\x44\x42\x42\x42\x42\x42\x42\x44\x78\x00\x00\x00' 40 | b'\x00\x7e\x40\x40\x40\x7c\x40\x40\x40\x40\x7e\x00\x00\x00' 41 | b'\x00\x7e\x40\x40\x40\x7c\x40\x40\x40\x40\x40\x00\x00\x00' 42 | b'\x00\x1c\x22\x42\x40\x40\x4e\x42\x42\x22\x1c\x00\x00\x00' 43 | b'\x00\x42\x42\x42\x42\x7e\x42\x42\x42\x42\x42\x00\x00\x00' 44 | b'\x00\x7c\x10\x10\x10\x10\x10\x10\x10\x10\x7c\x00\x00\x00' 45 | b'\x00\x02\x02\x02\x02\x02\x02\x02\x02\x42\x3c\x00\x00\x00' 46 | b'\x00\x42\x44\x48\x48\x50\x70\x48\x48\x44\x42\x00\x00\x00' 47 | b'\x00\x40\x40\x40\x40\x40\x40\x40\x40\x40\x7e\x00\x00\x00' 48 | b'\x00\x82\xc6\xaa\xaa\x92\x92\x82\x82\x82\x82\x00\x00\x00' 49 | b'\x00\x42\x62\x62\x52\x52\x4a\x4a\x46\x46\x42\x00\x00\x00' 50 | b'\x00\x18\x24\x42\x42\x42\x42\x42\x42\x24\x18\x00\x00\x00' 51 | b'\x00\x7c\x42\x42\x42\x42\x7c\x40\x40\x40\x40\x00\x00\x00' 52 | b'\x00\x18\x24\x42\x42\x42\x42\x42\x4a\x24\x1a\x00\x00\x00' 53 | b'\x00\x7c\x42\x42\x42\x7c\x44\x44\x42\x42\x42\x00\x00\x00' 54 | b'\x00\x3c\x42\x40\x40\x30\x0c\x02\x02\x42\x3c\x00\x00\x00' 55 | b'\x00\xfe\x10\x10\x10\x10\x10\x10\x10\x10\x10\x00\x00\x00' 56 | b'\x00\x42\x42\x42\x42\x42\x42\x42\x42\x42\x3c\x00\x00\x00' 57 | b'\x00\x82\x82\x44\x44\x44\x44\x28\x28\x28\x10\x00\x00\x00' 58 | b'\x00\x82\x82\x82\x92\x92\xaa\xaa\xaa\x44\x44\x00\x00\x00' 59 | b'\x00\x42\x24\x24\x18\x10\x08\x18\x24\x24\x42\x00\x00\x00' 60 | b'\x00\x82\x44\x44\x28\x28\x10\x10\x10\x10\x10\x00\x00\x00' 61 | b'\x00\x7e\x02\x04\x08\x08\x10\x10\x20\x40\x7e\x00\x00\x00' 62 | b'\x00\x1c\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x1c' 63 | b'\x00\x20\x20\x20\x10\x10\x08\x08\x08\x04\x04\x00\x00\x00' 64 | b'\x00\x38\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x08\x38' 65 | b'\x00\x18\x18\x24\x24\x42\x42\x00\x00\x00\x00\x00\x00\x00' 66 | b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\x00' 67 | b'\x00\x20\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 68 | b'\x00\x00\x00\x00\x3c\x42\x02\x3e\x42\x46\x3a\x00\x00\x00' 69 | b'\x00\x40\x40\x40\x5c\x62\x42\x42\x42\x62\x5c\x00\x00\x00' 70 | b'\x00\x00\x00\x00\x3c\x42\x40\x40\x40\x42\x3c\x00\x00\x00' 71 | b'\x00\x02\x02\x02\x3a\x46\x42\x42\x42\x46\x3a\x00\x00\x00' 72 | b'\x00\x00\x00\x00\x3c\x42\x42\x7e\x40\x42\x3c\x00\x00\x00' 73 | b'\x00\x0e\x10\x10\x7c\x10\x10\x10\x10\x10\x10\x00\x00\x00' 74 | b'\x00\x00\x00\x00\x3a\x46\x42\x42\x42\x46\x3a\x02\x42\x3c' 75 | b'\x00\x40\x40\x40\x5c\x62\x42\x42\x42\x42\x42\x00\x00\x00' 76 | b'\x00\x08\x00\x00\x38\x08\x08\x08\x08\x08\x08\x00\x00\x00' 77 | b'\x00\x08\x00\x00\x38\x08\x08\x08\x08\x08\x08\x08\x08\x70' 78 | b'\x00\x40\x40\x40\x44\x48\x50\x70\x48\x44\x42\x00\x00\x00' 79 | b'\x00\x38\x08\x08\x08\x08\x08\x08\x08\x08\x08\x00\x00\x00' 80 | b'\x00\x00\x00\x00\xfc\x92\x92\x92\x92\x92\x92\x00\x00\x00' 81 | b'\x00\x00\x00\x00\x5c\x62\x42\x42\x42\x42\x42\x00\x00\x00' 82 | b'\x00\x00\x00\x00\x3c\x42\x42\x42\x42\x42\x3c\x00\x00\x00' 83 | b'\x00\x00\x00\x00\x5c\x62\x42\x42\x42\x62\x5c\x40\x40\x40' 84 | b'\x00\x00\x00\x00\x3a\x46\x42\x42\x42\x46\x3a\x02\x02\x02' 85 | b'\x00\x00\x00\x00\x6c\x32\x20\x20\x20\x20\x20\x00\x00\x00' 86 | b'\x00\x00\x00\x00\x3c\x42\x40\x3c\x02\x42\x3c\x00\x00\x00' 87 | b'\x00\x00\x10\x10\x7c\x10\x10\x10\x10\x10\x0e\x00\x00\x00' 88 | b'\x00\x00\x00\x00\x42\x42\x42\x42\x42\x46\x3a\x00\x00\x00' 89 | b'\x00\x00\x00\x00\x82\x44\x44\x44\x28\x28\x10\x00\x00\x00' 90 | b'\x00\x00\x00\x00\x92\xaa\xaa\xaa\xaa\x44\x44\x00\x00\x00' 91 | b'\x00\x00\x00\x00\x44\x28\x10\x10\x10\x28\x44\x00\x00\x00' 92 | b'\x00\x00\x00\x00\x42\x22\x24\x14\x14\x08\x08\x08\x10\x60' 93 | b'\x00\x00\x00\x00\x7e\x04\x08\x10\x20\x40\x7e\x00\x00\x00' 94 | b'\x00\x18\x10\x10\x10\x10\x20\x20\x10\x10\x10\x10\x10\x18' 95 | b'\x00\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10\x10' 96 | b'\x00\x18\x08\x08\x08\x08\x04\x04\x08\x08\x08\x08\x08\x18' 97 | b'\x00\x00\x00\x00\x00\x32\x4c\x00\x00\x00\x00\x00\x00\x00' 98 | b'\x00\x00\x00\xc0\xc0\xc0\xc0\xc0\xc0\xc0\x00\x00\x00\x00' 99 | ) 100 | _font8mono_index = (b'\x00\x00\x0e\x00\x1c\x00\x2a\x00\x38\x00\x46\x00\x54\x00\x62\x00' 101 | b'\x70\x00\x7e\x00\x8c\x00\x9a\x00\xa8\x00\xb6\x00\xc4\x00\xd2\x00' 102 | b'\xe0\x00\xee\x00\xfc\x00\x0a\x01\x18\x01\x26\x01\x34\x01\x42\x01' 103 | b'\x50\x01\x5e\x01\x6c\x01\x7a\x01\x88\x01\x96\x01\xa4\x01\xb2\x01' 104 | b'\xc0\x01\xce\x01\xdc\x01\xea\x01\xf8\x01\x06\x02\x14\x02\x22\x02' 105 | b'\x30\x02\x3e\x02\x4c\x02\x5a\x02\x68\x02\x76\x02\x84\x02\x92\x02' 106 | b'\xa0\x02\xae\x02\xbc\x02\xca\x02\xd8\x02\xe6\x02\xf4\x02\x02\x03' 107 | b'\x10\x03\x1e\x03\x2c\x03\x3a\x03\x48\x03\x56\x03\x64\x03\x72\x03' 108 | b'\x80\x03\x8e\x03\x9c\x03\xaa\x03\xb8\x03\xc6\x03\xd4\x03\xe2\x03' 109 | b'\xf0\x03\xfe\x03\x0c\x04\x1a\x04\x28\x04\x36\x04\x44\x04\x52\x04' 110 | b'\x60\x04\x6e\x04\x7c\x04\x8a\x04\x98\x04\xa6\x04\xb4\x04\xc2\x04' 111 | b'\xd0\x04\xde\x04\xec\x04\xfa\x04\x08\x05\x16\x05\x24\x05\x32\x05' 112 | b'\x40\x05') 113 | 114 | font8mono = TFTfont.TFTFont(_font8mono, _font8mono_index, 14, 8, 96) 115 | 116 | fonts = {"font8mono":font8mono, 117 | } 118 | 119 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "urls": [ 3 | ["touch.py", "github:robert-hh/XPT2046-touch-pad-driver/touch.py"], 4 | ["xpt2046_syn.py", "github:robert-hh/XPT2046-touch-pad-driver/xpt2046_syn.py"] 5 | ], 6 | "version": "1.0.0", 7 | "deps": [] 8 | } -------------------------------------------------------------------------------- /tft.py: -------------------------------------------------------------------------------- 1 | # 2 | # The MIT License (MIT) 3 | # 4 | # Copyright (c) 2016 Robert Hammelrath 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | # 24 | # Some parts of the software are a port of code provided by Rinky-Dink Electronics, Henning Karlsen, 25 | # with the following copyright notice: 26 | # 27 | ## Copyright (C)2015 Rinky-Dink Electronics, Henning Karlsen. All right reserved 28 | ## This library is free software; you can redistribute it and/or 29 | ## modify it under the terms of the CC BY-NC-SA 3.0 license. 30 | ## Please see the included documents for further information. 31 | # 32 | # Class supporting TFT LC-displays with a parallel Interface 33 | # First example: Controller SSD1963 with a 4.3" or 7" display 34 | # 35 | # The minimal connection is: 36 | # X1..X8 for data, Y9 for /Reset, Y10 for /RD, Y11 for /WR and Y12 for /RS 37 | # Then LED must be hard tied to Vcc and /CS to GND. 38 | # 39 | 40 | import pyb, stm 41 | from uctypes import addressof 42 | import TFT_io 43 | 44 | # define constants 45 | # 46 | RESET = const(1 << 10) ## Y9 47 | RD = const(1 << 11) ## Y10 48 | WR = const(0x01) ## Y11 49 | D_C = const(0x02) ## Y12 50 | 51 | LED = const(1 << 8) ## Y3 52 | POWER = const(1 << 9) ## Y4 53 | 54 | ## CS is not used and must be hard tied to GND 55 | 56 | PORTRAIT = const(1) 57 | LANDSCAPE = const(0) 58 | 59 | class TFT: 60 | 61 | def __init__(self, controller = "SSD1963", lcd_type = "LB04301", orientation = LANDSCAPE, v_flip = False, h_flip = False): 62 | self.tft_init(controller, lcd_type, orientation, v_flip, h_flip) 63 | 64 | def tft_init(self, controller = "SSD1963", lcd_type = "LB04301", orientation = LANDSCAPE, v_flip = False, h_flip = False): 65 | # 66 | # For convenience, define X1..X1 and Y9..Y12 as output port using thy python functions. 67 | # X1..X8 will be redefind on the fly as Input by accessing the MODER control registers 68 | # when needed. Y9 is treate seperately, since it is used for Reset, which is done at python level 69 | # since it need long delays anyhow, 5 and 15 ms vs. 10 µs. 70 | # 71 | # Set TFT general defaults 72 | self.controller = controller 73 | self.lcd_type = lcd_type 74 | self.orientation = orientation 75 | self.v_flip = v_flip # flip vertical 76 | self.h_flip = h_flip # flip horizontal 77 | self.c_flip = 0 # flip blue/red 78 | self.rc_flip = 0 # flip row/column 79 | 80 | self.setColor((255, 255, 255)) # set FG color to white as can be. 81 | self.setBGColor((0, 0, 0)) # set BG to black 82 | # special treat for BG LED 83 | self.pin_led = pyb.Pin("Y3", pyb.Pin.OUT_PP) 84 | self.led_tim = pyb.Timer(4, freq=500) 85 | self.led_ch = self.led_tim.channel(3, pyb.Timer.PWM, pin=self.pin_led) 86 | self.led_ch.pulse_width_percent(0) # led off 87 | self.pin_led.value(0) ## switch BG LED off 88 | # special treat for Power Pin 89 | self.pin_power = pyb.Pin("Y4", pyb.Pin.OUT_PP) 90 | self.pin_power.value(1) ## switch Power on 91 | pyb.delay(10) 92 | # this may have to be moved to the controller specific section 93 | if orientation == PORTRAIT: 94 | self.setXY = TFT_io.setXY_P 95 | self.drawPixel = TFT_io.drawPixel_P 96 | else: 97 | self.setXY = TFT_io.setXY_L 98 | self.drawPixel = TFT_io.drawPixel_L 99 | self.swapbytes = TFT_io.swapbytes 100 | self.swapcolors = TFT_io.swapcolors 101 | # ---------- 102 | for pin_name in ["X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", 103 | "Y10", "Y11", "Y12"]: 104 | pin = pyb.Pin(pin_name, pyb.Pin.OUT_PP) # set as output 105 | pin.value(1) ## set high as default 106 | # special treat for Reset 107 | self.pin_reset = pyb.Pin("Y9", pyb.Pin.OUT_PP) 108 | # Reset the device 109 | self.pin_reset.value(1) ## do a hard reset 110 | pyb.delay(10) 111 | self.pin_reset.value(0) ## Low 112 | pyb.delay(20) 113 | self.pin_reset.value(1) ## set high again 114 | pyb.delay(20) 115 | # 116 | # Now initialiize the LCD 117 | # This is for the SSD1963 controller and two specific LCDs. More may follow. 118 | # Data taken from the SSD1963 data sheet, SSD1963 Application Note and the LCD Data sheets 119 | # 120 | if controller == "SSD1963": # 1st approach for 480 x 272 121 | TFT_io.tft_cmd_data(0xe2, bytearray(b'\x1d\x02\x54'), 3) # PLL multiplier, set PLL clock to 100M 122 | # N=0x2D for 6.5MHz, 0x1D for 10MHz crystal 123 | # PLLClock = Crystal * (Mult + 1) / (Div + 1) 124 | # The intermediate value Crystal * (Mult + 1) must be between 250MHz and 750 MHz 125 | TFT_io.tft_cmd_data(0xe0, bytearray(b'\x01'), 1) # PLL Enable 126 | pyb.delay(10) 127 | TFT_io.tft_cmd_data(0xe0, bytearray(b'\x03'), 1) 128 | pyb.delay(10) 129 | TFT_io.tft_cmd(0x01) # software reset 130 | pyb.delay(10) 131 | # 132 | # Settings for the LCD 133 | # 134 | # The LCDC_FPR depends on PLL clock and the reccomended LCD Dot clock DCLK 135 | # 136 | # LCDC_FPR = (DCLK * 1048576 / PLLClock) - 1 137 | # 138 | # The other settings are less obvious, since the definitions of the SSD1963 data sheet and the 139 | # LCD data sheets differ. So what' common, even if the names may differ: 140 | # HDP Horizontal Panel width (also called HDISP, Thd). The value store in the register is HDP - 1 141 | # VDP Vertical Panel Width (also called VDISP, Tvd). The value stored in the register is VDP - 1 142 | # HT Total Horizontal Period, also called HP, th... The exact value does not matter 143 | # VT Total Vertical Period, alco called VT, tv, .. The exact value does not matter 144 | # HPW Width of the Horizontal sync pulse, also called HS, thpw. 145 | # VPW Width of the Vertical sync pulse, also called VS, tvpw 146 | # Front Porch (HFP and VFP) Time between the end of display data and the sync pulse 147 | # Back Porch (HBP and VBP Time between the start of the sync pulse and the start of display data. 148 | # HT = FP + HDP + BP and VT = VFP + VDP + VBP (sometimes plus sync pulse width) 149 | # Unfortunately, the controller does not use these front/back porch times, instead it uses an starting time 150 | # in the front porch area and defines (see also figures in chapter 13.3 of the SSD1963 data sheet) 151 | # HPS Time from that horiz. starting point to the start of the horzontal display area 152 | # LPS Time from that horiz. starting point to the horizontal sync pulse 153 | # VPS Time from the vert. starting point to the first line 154 | # FPS Time from the vert. starting point to the vertical sync pulse 155 | # 156 | # So the following relations must be held: 157 | # 158 | # HT > HDP + HPS 159 | # HPS >= HPW + LPS 160 | # HPS = Back Porch - LPS, or HPS = Horizontal back Porch 161 | # VT > VDP + VPS 162 | # VPS >= VPW + FPS 163 | # VPS = Back Porch - FPS, or VPS = Vertical back Porch 164 | # 165 | # LPS or FPS may have a value of zero, since the length of the front porch is detemined by the 166 | # other figures 167 | # 168 | # The best is to start with the recomendations of the lCD data sheet for Back porch, grab a 169 | # sync pulse with and the determine the other, such that they meet the relations. Typically, these 170 | # values allow for some ambuigity. 171 | # 172 | if lcd_type == "LB04301": # Size 480x272, 4.3", 24 Bit, 4.3" 173 | # 174 | # Value Min Typical Max 175 | # DotClock 5 MHZ 9 MHz 12 MHz 176 | # HT (Hor. Total 490 531 612 177 | # HDP (Hor. Disp) 480 178 | # HBP (back porch) 8 43 179 | # HFP (Fr. porch) 2 8 180 | # HPW (Hor. sync) 1 181 | # VT (Vert. Total) 275 288 335 182 | # VDP (Vert. Disp) 272 183 | # VBP (back porch) 2 12 184 | # VFP (fr. porch) 1 4 185 | # VPW (vert. sync) 1 10 186 | # 187 | # This table in combination with the relation above leads to the settings: 188 | # HPS = 43, HPW = 8, LPS = 0, HT = 531 189 | # VPS = 14, VPW = 10, FPS = 0, VT = 288 190 | # 191 | self.disp_x_size = 479 192 | self.disp_y_size = 271 193 | TFT_io.tft_cmd_data_AS(0xe6, bytearray(b'\x01\x70\xa3'), 3) # PLL setting for PCLK 194 | # (9MHz * 1048576 / 100MHz) - 1 = 94371 = 0x170a3 195 | TFT_io.tft_cmd_data_AS(0xb0, bytearray( # # LCD SPECIFICATION 196 | [0x20, # 24 Color bits, HSync/VSync low, No Dithering 197 | 0x00, # TFT mode 198 | self.disp_x_size >> 8, self.disp_x_size & 0xff, # physical Width of TFT 199 | self.disp_y_size >> 8, self.disp_y_size & 0xff, # physical Height of TFT 200 | 0x00]), 7) # Last byte only required for a serial TFT 201 | TFT_io.tft_cmd_data_AS(0xb4, bytearray(b'\x02\x13\x00\x2b\x08\x00\x00\x00'), 8) 202 | # HSYNC, Set HT 531 HPS 43 HPW=Sync pulse 8 LPS 0 203 | TFT_io.tft_cmd_data_AS(0xb6, bytearray(b'\x01\x20\x00\x0e\x0a\x00\x00'), 7) 204 | # VSYNC, Set VT 288 VPS 14 VPW 10 FPS 0 205 | TFT_io.tft_cmd_data_AS(0x36, bytearray([(orientation & 1) << 5 | (h_flip & 1) << 1 | (v_flip) & 1]), 1) 206 | # rotation/ flip, etc., t.b.d. 207 | elif lcd_type == "AT070TN92": # Size 800x480, 7", 18 Bit, lower color bits ignored 208 | # 209 | # Value Min Typical Max 210 | # DotClock 26.4 MHz 33.3 MHz 46.8 MHz 211 | # HT (Hor. Total 862 1056 1200 212 | # HDP (Hor. Disp) 800 213 | # HBP (back porch) 46 46 46 214 | # HFP (Fr. porch) 16 210 254 215 | # HPW (Hor. sync) 1 40 216 | # VT (Vert. Total) 510 525 650 217 | # VDP (Vert. Disp) 480 218 | # VBP (back porch) 23 23 23 219 | # VFP (fr. porch) 7 22 147 220 | # VPW (vert. sync) 1 20 221 | # 222 | # This table in combination with the relation above leads to the settings: 223 | # HPS = 46, HPW = 8, LPS = 0, HT = 1056 224 | # VPS = 23, VPW = 10, VPS = 0, VT = 525 225 | # 226 | self.disp_x_size = 799 227 | self.disp_y_size = 479 228 | TFT_io.tft_cmd_data_AS(0xe6, bytearray(b'\x05\x53\xf6'), 3) # PLL setting for PCLK 229 | # (33.3MHz * 1048576 / 100MHz) - 1 = 349174 = 0x553f6 230 | TFT_io.tft_cmd_data_AS(0xb0, bytearray( # # LCD SPECIFICATION 231 | [0x00, # 18 Color bits, HSync/VSync low, No Dithering/FRC 232 | 0x00, # TFT mode 233 | self.disp_x_size >> 8, self.disp_x_size & 0xff, # physical Width of TFT 234 | self.disp_y_size >> 8, self.disp_y_size & 0xff, # physical Height of TFT 235 | 0x00]), 7) # Last byte only required for a serial TFT 236 | TFT_io.tft_cmd_data_AS(0xb4, bytearray(b'\x04\x1f\x00\x2e\x08\x00\x00\x00'), 8) 237 | # HSYNC, Set HT 1056 HPS 46 HPW 8 LPS 0 238 | TFT_io.tft_cmd_data_AS(0xb6, bytearray(b'\x02\x0c\x00\x17\x08\x00\x00'), 7) 239 | # VSYNC, Set VT 525 VPS 23 VPW 08 FPS 0 240 | TFT_io.tft_cmd_data_AS(0x36, bytearray([(orientation & 1) << 5 | (h_flip & 1) << 1 | (v_flip) & 1]), 1) 241 | # rotation/ flip, etc., t.b.d. 242 | else: 243 | print("Wrong Parameter lcd_type: ", lcd_type) 244 | return 245 | TFT_io.tft_cmd_data_AS(0xBA, bytearray(b'\x0f'), 1) # GPIO[3:0] out 1 246 | TFT_io.tft_cmd_data_AS(0xB8, bytearray(b'\x07\x01'), 1) # GPIO3=input, GPIO[2:0]=output 247 | 248 | TFT_io.tft_cmd_data_AS(0xf0, bytearray(b'\x00'), 1) # Pixel data Interface 8 Bit 249 | 250 | TFT_io.tft_cmd(0x29) # Display on 251 | TFT_io.tft_cmd_data_AS(0xbe, bytearray(b'\x06\xf0\x01\xf0\x00\x00'), 6) 252 | # Set PWM for B/L 253 | TFT_io.tft_cmd_data_AS(0xd0, bytearray(b'\x0d'), 1) # Set DBC: enable, agressive 254 | else: 255 | print("Wrong Parameter controller: ", controller) 256 | return 257 | # 258 | # Set character printing defaults 259 | # 260 | self.text_font = None 261 | self.setTextStyle(self.color, self.BGcolor, 0, None, 0) 262 | # 263 | # Init done. clear Screen and switch BG LED on 264 | # 265 | self.text_x = self.text_y = self.text_yabs = 0 266 | self.clrSCR() # clear the display 267 | # self.backlight(100) ## switch BG LED on 268 | # 269 | # Return screen dimensions 270 | # 271 | def getScreensize(self): 272 | if self.orientation == LANDSCAPE: 273 | return (self.disp_x_size + 1, self.disp_y_size + 1) 274 | else: 275 | return (self.disp_y_size + 1, self.disp_x_size + 1) 276 | # 277 | # set backlight brightness 278 | # 279 | def backlight(self, percent): 280 | percent = max(0, min(percent, 100)) 281 | self.led_ch.pulse_width_percent(percent) # set LED 282 | # 283 | # switch power on/off 284 | # 285 | def power(self, onoff): 286 | if onoff: 287 | self.pin_power.value(True) ## switch power on or off 288 | else: 289 | self.pin_power.value(False) 290 | 291 | # 292 | # set the tft flip modes 293 | # 294 | def set_tft_mode(self, v_flip = False, h_flip = False, c_flip = False, orientation = LANDSCAPE): 295 | self.v_flip = v_flip # flip vertical 296 | self.h_flip = h_flip # flip horizontal 297 | self.c_flip = c_flip # flip blue/red 298 | self.orientation = orientation # LANDSCAPE/PORTRAIT 299 | TFT_io.tft_cmd_data_AS(0x36, 300 | bytearray([(self.orientation << 5) |(self.c_flip << 3) | (self.h_flip & 1) << 1 | (self.v_flip) & 1]), 1) 301 | # rotation/ flip, etc., t.b.d. 302 | # 303 | # get the tft flip modes 304 | # 305 | def get_tft_mode(self): 306 | return (self.v_flip, self.h_flip, self.c_flip, self.orientation) # 307 | # 308 | # set the color used for the draw commands 309 | # 310 | def setColor(self, fgcolor): 311 | self.color = fgcolor 312 | self.colorvect = bytearray(self.color) # prepare byte array 313 | # 314 | # Set BG color used for the draw commands 315 | # 316 | def setBGColor(self, bgcolor): 317 | self.BGcolor = bgcolor 318 | self.BGcolorvect = bytearray(self.BGcolor) # prepare byte array 319 | self.BMPcolortable = bytearray([self.BGcolorvect[2], # create colortable 320 | self.BGcolorvect[1], self.BGcolorvect[0],0, 321 | self.colorvect[2], self.colorvect[1], self.colorvect[0],0]) 322 | # 323 | # get the color used for the draw commands 324 | # 325 | def getColor(self): 326 | return self.color 327 | # 328 | # get BG color used for 329 | # 330 | def getBGColor(self): 331 | return self.BGcolor 332 | # 333 | # Draw a single pixel at location x, y with color 334 | # Rather slow at 40µs/Pixel 335 | # 336 | def drawPixel_py(self, x, y, color): 337 | self.setXY(x, y, x, y) 338 | TFT_io.displaySCR_AS(color, 1) # 339 | # 340 | # clear screen, set it to BG color. 341 | # 342 | def clrSCR(self, color = None): 343 | colorvect = self.BGcolorvect if color is None else bytearray(color) 344 | self.clrXY() 345 | TFT_io.fillSCR_AS(colorvect, (self.disp_x_size + 1) * (self.disp_y_size + 1)) 346 | self.setScrollArea(0, self.disp_y_size + 1, 0) 347 | self.setScrollStart(0) 348 | self.setTextPos(0,0) 349 | # 350 | # reset the address range to fullscreen 351 | # 352 | def clrXY(self): 353 | if self.orientation == LANDSCAPE: 354 | self.setXY(0, 0, self.disp_x_size, self.disp_y_size) 355 | else: 356 | self.setXY(0, 0, self.disp_y_size, self.disp_x_size) 357 | # 358 | # Draw a line from x1, y1 to x2, y2 with the color set by setColor() 359 | # Straight port from the UTFT Library at Rinky-Dink Electronics 360 | # 361 | def drawLine(self, x1, y1, x2, y2, color = None): 362 | if y1 == y2: 363 | self.drawHLine(x1, y1, x2 - x1 + 1, color) 364 | elif x1 == x2: 365 | self.drawVLine(x1, y1, y2 - y1 + 1, color) 366 | else: 367 | colorvect = self.colorvect if color is None else bytearray(color) 368 | dx, xstep = (x2 - x1, 1) if x2 > x1 else (x1 - x2, -1) 369 | dy, ystep = (y2 - y1, 1) if y2 > y1 else (y1 - y2, -1) 370 | col, row = x1, y1 371 | if dx < dy: 372 | t = - (dy >> 1) 373 | while True: 374 | self.drawPixel(col, row, colorvect) 375 | if row == y2: 376 | return 377 | row += ystep 378 | t += dx 379 | if t >= 0: 380 | col += xstep 381 | t -= dy 382 | else: 383 | t = - (dx >> 1) 384 | while True: 385 | self.drawPixel(col, row, colorvect) 386 | if col == x2: 387 | return 388 | col += xstep 389 | t += dy 390 | if t >= 0: 391 | row += ystep 392 | t -= dx 393 | # 394 | # Draw a horizontal line with 1 Pixel width, from x,y to x + l - 1, y 395 | # Straight port from the UTFT Library at Rinky-Dink Electronics 396 | # 397 | def drawHLine(self, x, y, l, color = None): # draw horiontal Line 398 | colorvect = self.colorvect if color is None else bytearray(color) 399 | if l < 0: # negative length, swap parameters 400 | l = -l 401 | x -= l 402 | self.setXY(x, y, x + l - 1, y) # set display window 403 | TFT_io.fillSCR_AS(colorvect, l) 404 | # 405 | # Draw a vertical line with 1 Pixel width, from x,y to x, y + l - 1 406 | # Straight port from the UTFT Library at Rinky-Dink Electronics 407 | # 408 | def drawVLine(self, x, y, l, color = None): # draw horiontal Line 409 | colorvect = self.colorvect if color is None else bytearray(color) 410 | if l < 0: # negative length, swap parameters 411 | l = -l 412 | y -= l 413 | self.setXY(x, y, x, y + l - 1) # set display window 414 | TFT_io.fillSCR_AS(colorvect, l) 415 | # 416 | # Draw rectangle from x1, y1, to x2, y2 417 | # Straight port from the UTFT Library at Rinky-Dink Electronics 418 | # 419 | def drawRectangle(self, x1, y1, x2, y2, color = None): 420 | if x1 > x2: 421 | x1, x2 = x2, x1 422 | if y1 > y2: 423 | y1, y2 = y2, y1 424 | self.drawHLine(x1, y1, x2 - x1 + 1, color) 425 | self.drawHLine(x1, y2, x2 - x1 + 1, color) 426 | self.drawVLine(x1, y1, y2 - y1 + 1, color) 427 | self.drawVLine(x2, y1, y2 - y1 + 1, color) 428 | # 429 | # Fill rectangle 430 | # Almost straight port from the UTFT Library at Rinky-Dink Electronics 431 | # 432 | def fillRectangle(self, x1, y1, x2, y2, color=None): 433 | if x1 > x2: 434 | x1, x2 = x2, x1 435 | if y1 > y2: 436 | y1, y2 = y2, y1 437 | self.setXY(x1, y1, x2, y2) # set display window 438 | if color: 439 | TFT_io.fillSCR_AS(bytearray(color), (x2 - x1 + 1) * (y2 - y1 + 1)) 440 | else: 441 | TFT_io.fillSCR_AS(self.colorvect, (x2 - x1 + 1) * (y2 - y1 + 1)) 442 | 443 | # 444 | # Draw smooth rectangle from x1, y1, to x2, y2 445 | # Straight port from the UTFT Library at Rinky-Dink Electronics 446 | # 447 | def drawClippedRectangle(self, x1, y1, x2, y2, color = None): 448 | if x1 > x2: 449 | x1, x2 = x2, x1 450 | if y1 > y2: 451 | y1, y2 = y2, y1 452 | if (x2-x1) > 4 and (y2-y1) > 4: 453 | colorvect = self.colorvect if color is None else bytearray(color) 454 | self.drawPixel(x1 + 2,y1 + 1, colorvect) 455 | self.drawPixel(x1 + 1,y1 + 2, colorvect) 456 | self.drawPixel(x2 - 2,y1 + 1, colorvect) 457 | self.drawPixel(x2 - 1,y1 + 2, colorvect) 458 | self.drawPixel(x1 + 2,y2 - 1, colorvect) 459 | self.drawPixel(x1 + 1,y2 - 2, colorvect) 460 | self.drawPixel(x2 - 2,y2 - 1, colorvect) 461 | self.drawPixel(x2 - 1,y2 - 2, colorvect) 462 | self.drawHLine(x1 + 3, y1, x2 - x1 - 5, colorvect) 463 | self.drawHLine(x1 + 3, y2, x2 - x1 - 5, colorvect) 464 | self.drawVLine(x1, y1 + 3, y2 - y1 - 5, colorvect) 465 | self.drawVLine(x2, y1 + 3, y2 - y1 - 5, colorvect) 466 | # 467 | # Fill smooth rectangle from x1, y1, to x2, y2 468 | # Straight port from the UTFT Library at Rinky-Dink Electronics 469 | # 470 | def fillClippedRectangle(self, x1, y1, x2, y2, color = None): 471 | if x1 > x2: 472 | t = x1; x1 = x2; x2 = t 473 | if y1 > y2: 474 | t = y1; y1 = y2; y2 = t 475 | if (x2-x1) > 4 and (y2-y1) > 4: 476 | for i in range(((y2 - y1) // 2) + 1): 477 | if i == 0: 478 | self.drawHLine(x1 + 3, y1 + i, x2 - x1 - 5, color) 479 | self.drawHLine(x1 + 3, y2 - i, x2 - x1 - 5, color) 480 | elif i == 1: 481 | self.drawHLine(x1 + 2, y1 + i, x2 - x1 - 3, color) 482 | self.drawHLine(x1 + 2, y2 - i, x2 - x1 - 3, color) 483 | elif i == 2: 484 | self.drawHLine(x1 + 1, y1 + i, x2 - x1 - 1, color) 485 | self.drawHLine(x1 + 1, y2 - i, x2 - x1 - 1, color) 486 | else: 487 | self.drawHLine(x1, y1 + i, x2 - x1 + 1, color) 488 | self.drawHLine(x1, y2 - i, x2 - x1 + 1, color) 489 | # 490 | # draw a circle at x, y with radius 491 | # Straight port from the UTFT Library at Rinky-Dink Electronics 492 | # 493 | def drawCircle(self, x, y, radius, color = None): 494 | 495 | colorvect = self.colorvect if color is None else bytearray(color) 496 | 497 | f = 1 - radius 498 | ddF_x = 1 499 | ddF_y = -2 * radius 500 | x1 = 0 501 | y1 = radius 502 | 503 | self.drawPixel(x, y + radius, colorvect) 504 | self.drawPixel(x, y - radius, colorvect) 505 | self.drawPixel(x + radius, y, colorvect) 506 | self.drawPixel(x - radius, y, colorvect) 507 | 508 | while x1 < y1: 509 | if f >= 0: 510 | y1 -= 1 511 | ddF_y += 2 512 | f += ddF_y 513 | x1 += 1 514 | ddF_x += 2 515 | f += ddF_x 516 | self.drawPixel(x + x1, y + y1, colorvect) 517 | self.drawPixel(x - x1, y + y1, colorvect) 518 | self.drawPixel(x + x1, y - y1, colorvect) 519 | self.drawPixel(x - x1, y - y1, colorvect) 520 | self.drawPixel(x + y1, y + x1, colorvect) 521 | self.drawPixel(x - y1, y + x1, colorvect) 522 | self.drawPixel(x + y1, y - x1, colorvect) 523 | self.drawPixel(x - y1, y - x1, colorvect) 524 | # 525 | # fill a circle at x, y with radius 526 | # Straight port from the UTFT Library at Rinky-Dink Electronics 527 | # Instead of calculating x = sqrt(r*r - y*y), it searches the x 528 | # for r*r = x*x + x*x 529 | # 530 | def fillCircle(self, x, y, radius, color = None): 531 | r_square = radius * radius * 4 532 | for y1 in range (-(radius * 2), 1): 533 | y_square = y1 * y1 534 | for x1 in range (-(radius * 2), 1): 535 | if x1*x1+y_square <= r_square: 536 | x1i = x1 // 2 537 | y1i = y1 // 2 538 | self.drawHLine(x + x1i, y + y1i, 2 * (-x1i), color) 539 | self.drawHLine(x + x1i, y - y1i, 2 * (-x1i), color) 540 | break; 541 | # 542 | # Draw a bitmap at x,y with size sx, sy 543 | # mode determines the type of expected data 544 | # mode = 1: The data contains 1 bit per pixel, mapped to fg/bg color 545 | # unless a colortable is provided 546 | # mode = 2: The data contains 2 bit per pixel; a colortable with 4 entries must be provided 547 | # mode = 4: The data contains 4 bit per pixel; 548 | # a colortable with 16 entries must be provided 549 | # mode = 8: The data contains 8 bit per pixel; 550 | # a colortable with 256 entries must be provided 551 | # mode = 16: The data must contain 2 packed bytes/pixel red/green/blue in 565 format 552 | # mode = 24: The data must contain 3 bytes/pixel red/green/blue 553 | # 554 | def drawBitmap(self, x, y, sx, sy, data, mode = 24, colortable = None): 555 | self.setXY(x, y, x + sx - 1, y + sy - 1) 556 | if mode == 24: 557 | TFT_io.displaySCR_AS(data, sx * sy) 558 | elif mode == 16: 559 | TFT_io.displaySCR565_AS(data, sx * sy) 560 | elif mode == 1: 561 | if colortable is None: 562 | colortable = self.BMPcolortable # create colortable 563 | TFT_io.displaySCR_bmp(data, sx*sy, 1, colortable) 564 | elif mode == 2: 565 | if colortable is None: 566 | return 567 | TFT_io.displaySCR_bmp(data, sx*sy, 2, colortable) 568 | elif mode == 4: 569 | if colortable is None: 570 | return 571 | TFT_io.displaySCR_bmp(data, sx*sy, 4, colortable) 572 | elif mode == 8: 573 | if colortable is None: 574 | return 575 | TFT_io.displaySCR_bmp(data, sx*sy, 8, colortable) 576 | 577 | # 578 | # set scroll area to the region between the first and last line 579 | # 580 | def setScrollArea(self, tfa, vsa, bfa): 581 | TFT_io.tft_cmd_data_AS(0x33, bytearray( #set scrolling range 582 | [(tfa >> 8) & 0xff, tfa & 0xff, 583 | (vsa >> 8) & 0xff, vsa & 0xff, 584 | (bfa >> 8) & 0xff, bfa & 0xff]), 6) 585 | self.scroll_tfa = tfa 586 | self.scroll_vsa = vsa 587 | self.scroll_bfa = bfa 588 | self.setScrollStart(self.scroll_tfa) 589 | x, y = self.getTextPos() 590 | self.setTextPos(x, y) # realign pointers 591 | # 592 | # get scroll area of the region between the first and last line 593 | # 594 | def getScrollArea(self): 595 | return self.scroll_tfa, self.scroll_vsa, self.scroll_bfa 596 | # 597 | # set the line which is displayed first 598 | # 599 | def setScrollStart(self, lline): 600 | self.scroll_start = lline # store the logical first line 601 | TFT_io.tft_cmd_data_AS(0x37, bytearray([(lline >> 8) & 0xff, lline & 0xff]), 2) 602 | # 603 | # get the line which is displayed first 604 | # 605 | def getScrollStart(self): 606 | return self.scroll_start # get the logical first line 607 | 608 | # 609 | # Scroll vsa up/down by a number of pixels 610 | # 611 | def scroll(self, pixels): 612 | line = ((self.scroll_start - self.scroll_tfa + pixels) % self.scroll_vsa 613 | + self.scroll_tfa) 614 | self.setScrollStart(line) # set the new line 615 | # 616 | # Set text position 617 | # 618 | def setTextPos(self, x, y, clip = False, scroll = True): 619 | self.text_width, self.text_height = self.getScreensize() ## height possibly wrong 620 | self.text_x = x 621 | if self.scroll_tfa <= y < (self.scroll_tfa + self.scroll_vsa): # in scroll area ? check later for < or <= 622 | # correct position relative to scroll start 623 | self.text_y = (y + self.scroll_start - self.scroll_tfa) 624 | if self.text_y >= (self.scroll_tfa + self.scroll_vsa): 625 | self.text_y -= self.scroll_vsa 626 | else: # absolute 627 | self.text_y = y 628 | self.text_yabs = y 629 | # Hint: self.text_yabs = self.text_y - self.scroll_start) % self.scroll_vsa + self.scroll_tfa) 630 | if clip and (self.text_x + clip) < self.text_width: 631 | self.text_width = self.text_x + clip 632 | self.text_scroll = scroll 633 | # 634 | # Get text position 635 | # 636 | def getTextPos(self, abs = True): 637 | if abs: 638 | return (self.text_x, self.text_yabs) 639 | else: 640 | return (self.text_x, self.text_y) 641 | # 642 | # Set Text Style 643 | # 644 | def setTextStyle(self, fgcolor=None, bgcolor=None, transparency=None, font=None, gap=None): 645 | if font is not None: 646 | self.text_font = font 647 | self.text_rows, self.text_cols, nchar, first = font.get_properties() # 648 | if transparency is not None: 649 | self.transparency = transparency 650 | if gap is not None: 651 | self.text_gap = gap 652 | if bgcolor is not None: 653 | self.text_bgcolor = bgcolor 654 | if fgcolor is not None: 655 | self.text_fgcolor = fgcolor 656 | self.text_color = (bytearray(self.text_bgcolor) 657 | + bytearray(self.text_fgcolor) 658 | + bytearray([self.transparency])) 659 | # 660 | # Get Text Style: return (color, bgcolor, font, transpareny, gap) 661 | # 662 | def getTextStyle(self): 663 | return (self.text_color[3:6], self.text_color[0:3], 664 | self.transparency, self.text_font, self.text_gap) 665 | 666 | # 667 | # Check, if a new line is to be opened 668 | # if yes, advance, including scrolling, and clear line, if flags is set 669 | # Obsolete? 670 | # 671 | def printNewline(self, clear = False): 672 | if (self.text_yabs + self.text_rows) >= (self.scroll_tfa + self.scroll_vsa): # does the line fit? 673 | self.scroll(self.text_rows) # no. scroll 674 | else: # Yes, just advance pointers 675 | self.text_yabs += self.text_rows 676 | self.setTextPos(self.text_x, self.text_yabs) 677 | if clear: 678 | self.printClrLine(2) # clear actual line 679 | # 680 | # Carriage Return 681 | # 682 | def printCR(self): # clear to end of line 683 | self.text_x = 0 684 | # 685 | # clear line modes 686 | # 687 | def printClrLine(self, mode = 0): # clear to end of line/bol/line 688 | if mode == 0: 689 | self.setXY(self.text_x, self.text_y, 690 | self.text_width - 1, self.text_y + self.text_rows - 1) # set display window 691 | TFT_io.fillSCR_AS(self.text_color, (self.text_width - self.text_x + 1) * self.text_rows) 692 | elif mode == 1 and self.text_x > 0: 693 | self.setXY(0, self.text_y, 694 | self.text_x - 1, self.text_y + self.text_rows - 1) # set display window 695 | TFT_io.fillSCR_AS(self.text_color, (self.text_x - 1) * self.text_rows) 696 | elif mode == 2: 697 | self.setXY(0, self.text_y, 698 | self.text_width - 1, self.text_y + self.text_rows - 1) # set display window 699 | TFT_io.fillSCR_AS(self.text_color, self.text_width * self.text_rows) 700 | # 701 | # clear sreen modes 702 | # 703 | def printClrSCR(self): # clear Area set by setScrollArea 704 | self.setXY(0, self.scroll_tfa, 705 | self.text_width - 1, self.scroll_tfa + self.scroll_vsa) # set display window 706 | TFT_io.fillSCR_AS(self.text_color, self.text_width * self.scroll_vsa) 707 | self.setScrollStart(self.scroll_tfa) 708 | self.setTextPos(0, self.scroll_tfa) 709 | # 710 | # Print string s, returning the length of the printed string in pixels 711 | # 712 | def printString(self, s, bg_buf=None): 713 | len = 0 714 | for c in s: 715 | cols = self.printChar(c, bg_buf) 716 | if cols == 0: # could not print (any more) 717 | break 718 | len += cols 719 | return len 720 | # 721 | # Print string c using the given char bitmap at location x, y, returning the width of the printed char in pixels 722 | # 723 | def printChar(self, c, bg_buf=None): 724 | # get the charactes pixel bitmap and dimensions 725 | if self.text_font: 726 | fontptr, rows, cols = self.text_font.get_ch(ord(c)) 727 | else: 728 | raise AttributeError('No font selected') 729 | pix_count = cols * rows # number of bits in the char 730 | # test char fit 731 | if self.text_x + cols > self.text_width: # does the char fit on the screen? 732 | if self.text_scroll: 733 | self.printCR() # No, then CR 734 | self.printNewline(True) # NL: advance to the next line 735 | else: 736 | return 0 737 | # Retrieve Background data if transparency is required 738 | if self.transparency: # in case of transpareny, the frame buffer content is needed 739 | if bg_buf is None: # buffer allocation needed? 740 | bg_buf = bytearray(pix_count * 3) # sigh... 741 | self.setXY(self.text_x, self.text_y, self.text_x + cols - 1, self.text_y + rows - 1) # set area 742 | TFT_io.tft_read_cmd_data_AS(0x2e, bg_buf, pix_count * 3) # read background data 743 | else: 744 | bg_buf = 0 # dummy assignment, since None is not accepted 745 | # Set XY range & print char 746 | self.setXY(self.text_x, self.text_y, self.text_x + cols - 1, self.text_y + rows - 1) # set area 747 | TFT_io.displaySCR_charbitmap(fontptr, pix_count, self.text_color, bg_buf) # display char! 748 | #advance pointer 749 | self.text_x += (cols + self.text_gap) 750 | return cols + self.text_gap 751 | -------------------------------------------------------------------------------- /touch.py: -------------------------------------------------------------------------------- 1 | # asyncio version 2 | # The MIT License (MIT) 3 | # 4 | # Copyright (c) 2016, 2017 Robert Hammelrath (basic driver) 5 | # 2016 Peter Hinch (asyncio extension) 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | # 25 | # Class supporting the resisitve touchpad of TFT LC-displays 26 | # 27 | import pyb, stm 28 | from machine import SPI, Pin 29 | # define constants 30 | # 31 | T_GETX = const(0xd0) ## 12 bit resolution 32 | T_GETY = const(0x90) ## 12 bit resolution 33 | T_GETZ1 = const(0xb8) ## 8 bit resolution 34 | T_GETZ2 = const(0xc8) ## 8 bit resolution 35 | # 36 | X_LOW = const(10) ## lowest reasonable X value from the touchpad 37 | Y_HIGH = const(4090) ## highest reasonable Y value 38 | 39 | class TOUCH: 40 | # 41 | # Init just sets the PIN's to In / out as required 42 | # async: set True if asynchronous operation intended 43 | # confidence: confidence level - number of consecutive touches with a margin smaller than the given level 44 | # which the function will sample until it accepts it as a valid touch 45 | # margin: Distance from mean centre at which touches are considered at the same position 46 | # delay: Delay between samples in ms. (n/a if asynchronous) 47 | # 48 | DEFAULT_CAL = (-3917, -0.127, -3923, -0.1267, -3799, -0.07572, -3738, -0.07814) 49 | 50 | def __init__(self, controller="XPT2046", asyn=False, *, confidence=5, margin=50, delay=10, calibration=None, spi = None): 51 | if spi is None: 52 | self.spi = SPI(-1, baudrate=1000000, sck=Pin("X12"), mosi=Pin("X11"), miso=Pin("Y2")) 53 | else: 54 | self.spi = spi 55 | self.recv = bytearray(3) 56 | self.xmit = bytearray(3) 57 | # set default values 58 | self.ready = False 59 | self.touched = False 60 | self.x = 0 61 | self.y = 0 62 | self.buf_length = 0 63 | cal = TOUCH.DEFAULT_CAL if calibration is None else calibration 64 | self.asynchronous = False 65 | self.touch_parameter(confidence, margin, delay, cal) 66 | if asyn: 67 | self.asynchronous = True 68 | import uasyncio as asyncio 69 | loop = asyncio.get_event_loop() 70 | loop.create_task(self._main_thread()) 71 | 72 | # set parameters for get_touch() 73 | # res: Resolution in bits of the returned values, default = 10 74 | # confidence: confidence level - number of consecutive touches with a margin smaller than the given level 75 | # which the function will sample until it accepts it as a valid touch 76 | # margin: Difference from mean centre at which touches are considered at the same position 77 | # delay: Delay between samples in ms. 78 | # 79 | def touch_parameter(self, confidence=5, margin=50, delay=10, calibration=None): 80 | if not self.asynchronous: # Ignore attempts to change on the fly. 81 | confidence = max(min(confidence, 25), 5) 82 | if confidence != self.buf_length: 83 | self.buff = [[0,0] for x in range(confidence)] 84 | self.buf_length = confidence 85 | self.delay = max(min(delay, 100), 5) 86 | margin = max(min(margin, 100), 1) 87 | self.margin = margin * margin # store the square value 88 | if calibration: 89 | self.calibration = calibration 90 | 91 | # get_touch(): Synchronous use. get a touch value; Parameters: 92 | # 93 | # initital: Wait for a non-touch state before getting a sample. 94 | # True = Initial wait for a non-touch state 95 | # False = Do not wait for a release 96 | # wait: Wait for a touch or not? 97 | # False: Do not wait for a touch and return immediately 98 | # True: Wait until a touch is pressed. 99 | # raw: Setting whether raw touch coordinates (True) or normalized ones (False) are returned 100 | # setting the calibration vector to (0, 1, 0, 1, 0, 1, 0, 1) result in a identity mapping 101 | # timeout: Longest time (ms, or None = 1 hr) to wait for a touch or release 102 | # 103 | # Return (x,y) or None 104 | # 105 | def get_touch(self, initial=True, wait=True, raw=False, timeout=None): 106 | if self.asynchronous: 107 | return None # Should only be called in synhronous mode 108 | if timeout is None: 109 | timeout = 3600000 # set timeout to 1 hour 110 | # 111 | if initial: ## wait for a non-touch state 112 | sample = True 113 | while sample and timeout > 0: 114 | sample = self.raw_touch() 115 | pyb.delay(self.delay) 116 | timeout -= self.delay 117 | if timeout <= 0: # after timeout, return None 118 | return None 119 | # 120 | buff = self.buff 121 | buf_length = self.buf_length 122 | buffptr = 0 123 | nsamples = 0 124 | while timeout > 0: 125 | if nsamples == buf_length: 126 | meanx = sum([c[0] for c in buff]) // buf_length 127 | meany = sum([c[1] for c in buff]) // buf_length 128 | dev = sum([(c[0] - meanx)**2 + (c[1] - meany)**2 for c in buff]) / buf_length 129 | if dev <= self.margin: # got one; compare against the square value 130 | if raw: 131 | return (meanx, meany) 132 | else: 133 | return self.do_normalize((meanx, meany)) 134 | # get a new value 135 | sample = self.raw_touch() # get a touch 136 | if sample is None: 137 | if not wait: 138 | return None 139 | nsamples = 0 # Invalidate buff 140 | else: 141 | buff[buffptr] = sample # put in buff 142 | buffptr = (buffptr + 1) % buf_length 143 | nsamples = min(nsamples + 1, buf_length) 144 | pyb.delay(self.delay) 145 | timeout -= self.delay 146 | return None 147 | 148 | # Asynchronous use: this thread maintains self.x and self.y 149 | async def _main_thread(self): 150 | import uasyncio as asyncio 151 | buff = self.buff 152 | buf_length = self.buf_length 153 | buffptr = 0 154 | nsamples = 0 155 | await asyncio.sleep(0) 156 | while True: 157 | if nsamples == buf_length: 158 | meanx = sum([c[0] for c in buff]) // buf_length 159 | meany = sum([c[1] for c in buff]) // buf_length 160 | dev = sum([(c[0] - meanx)**2 + (c[1] - meany)**2 for c in buff]) / buf_length 161 | if dev <= self.margin: # got one; compare against the square value 162 | self.ready = True 163 | self.x, self.y = self.do_normalize((meanx, meany)) 164 | sample = self.raw_touch() # get a touch 165 | if sample is None: 166 | self.touched = False 167 | self.ready = False 168 | nsamples = 0 # Invalidate buff 169 | else: 170 | self.touched = True 171 | buff[buffptr] = sample # put in buff 172 | buffptr = (buffptr + 1) % buf_length 173 | nsamples = min(nsamples + 1, buf_length) 174 | await asyncio.sleep(0) 175 | 176 | # Asynchronous get_touch 177 | def get_touch_async(self): 178 | if self.ready: 179 | self.ready = False 180 | return self.x, self.y 181 | return None 182 | # 183 | # do_normalize(touch) 184 | # calculate the screen coordinates from the touch values, using the calibration values 185 | # touch must be the tuple return by get_touch 186 | # 187 | def do_normalize(self, touch): 188 | xmul = self.calibration[3] + (self.calibration[1] - self.calibration[3]) * (touch[1] / 4096) 189 | xadd = self.calibration[2] + (self.calibration[0] - self.calibration[2]) * (touch[1] / 4096) 190 | ymul = self.calibration[7] + (self.calibration[5] - self.calibration[7]) * (touch[0] / 4096) 191 | yadd = self.calibration[6] + (self.calibration[4] - self.calibration[6]) * (touch[0] / 4096) 192 | x = int((touch[0] + xadd) * xmul) 193 | y = int((touch[1] + yadd) * ymul) 194 | return (x, y) 195 | # 196 | # raw_touch(tuple) 197 | # raw read touch. Returns (x,y) or None 198 | # 199 | def raw_touch(self): 200 | global CONTROL_PORT 201 | x = self.touch_talk(T_GETX, 12) 202 | y = self.touch_talk(T_GETY, 12) 203 | if x > X_LOW and y < Y_HIGH: # touch pressed? 204 | return (x, y) 205 | else: 206 | return None 207 | # 208 | # Send a command to the touch controller and wait for the response 209 | # cmd: command byte 210 | # bits: expected data size. Reasonable values are 8 and 12 211 | # 212 | def touch_talk(self, cmd, bits): 213 | self.xmit[0] = cmd 214 | self.spi.write_readinto(self.xmit, self.recv) 215 | return (self.recv[1] * 256 + self.recv[2]) >> (15 - bits) 216 | 217 | -------------------------------------------------------------------------------- /touch_bytecode.py: -------------------------------------------------------------------------------- 1 | # 2 | # The MIT License (MIT) 3 | # 4 | # Copyright (c) 2016 Robert Hammelrath 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in 14 | # all copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | # THE SOFTWARE. 23 | # 24 | # Class supporting the resisitve touchpad of TFT LC-displays 25 | # First example: Controller XPT2046 26 | # It uses Y5..Y8 of PyBoard 27 | # 28 | import pyb, stm 29 | # define constants 30 | # 31 | PCB_VERSION = 2 32 | 33 | #if PCB_VERSION == 1: 34 | # CONTROL_PORT = stm.GPIOB 35 | # T_CLOCK = const(1 << 15) ## Y8 = B15 36 | # T_DOUT = const(1 << 14) ## Y7 = B14 37 | # T_DIN = const(1 << 13) ## Y6 = B13 38 | # T_IRQ = const(1 << 12) ## Y5 = B12 39 | 40 | if PCB_VERSION == 2: 41 | CONTROL_PORT = stm.GPIOC 42 | T_CLOCK = const(1 << 5) ## X12 = C5 43 | T_DOUT = const(1 << 4) ## X11 = C4 44 | T_DIN = const(1 << 7) ## Y2 = C7 45 | T_IRQ = const(1 << 6) ## Y1 = C6 46 | 47 | # T_CS is not used and must be hard tied to GND 48 | 49 | T_GETX = const(0xd0) ## 12 bit resolution 50 | T_GETY = const(0x90) ## 12 bit resolution 51 | T_GETZ1 = const(0xb8) ## 8 bit resolution 52 | T_GETZ2 = const(0xc8) ## 8 bit resolution 53 | # 54 | X_LOW = const(10) ## lowest reasonable X value from the touchpad 55 | Y_HIGH = const(4090) ## highest reasonable Y value 56 | 57 | class TOUCH: 58 | # 59 | # Init just sets the PIN's to In / out as required 60 | # objsched: scheduler if asynchronous operation intended 61 | # confidence: confidence level - number of consecutive touches with a margin smaller than the given level 62 | # which the function will sample until it accepts it as a valid touch 63 | # margin: Difference from mean centre at which touches are considered at the same position 64 | # delay: Delay between samples in ms. (n/a if asynchronous) 65 | # 66 | DEFAULT_CAL = (-3917, -0.127, -3923, -0.1267, -3799, -0.07572, -3738, -0.07814) 67 | def __init__(self, controller = "XPT2046", objsched = None, *, confidence = 5, margin = 50, delay = 10, calibration = None): 68 | if PCB_VERSION == 1: 69 | self.pin_clock = pyb.Pin("Y8", pyb.Pin.OUT_PP) 70 | self.pin_clock.value(0) 71 | self.pin_d_out = pyb.Pin("Y7", pyb.Pin.OUT_PP) 72 | self.pin_d_in = pyb.Pin("Y6", pyb.Pin.IN) 73 | self.pin_irq = pyb.Pin("Y5", pyb.Pin.IN) 74 | else: 75 | self.pin_clock = pyb.Pin("X11", pyb.Pin.OUT_PP) 76 | self.pin_clock.value(0) 77 | self.pin_d_out = pyb.Pin("X12", pyb.Pin.OUT_PP) 78 | self.pin_d_in = pyb.Pin("Y1", pyb.Pin.IN) 79 | self.pin_irq = pyb.Pin("Y2", pyb.Pin.IN) 80 | # set default values 81 | self.ready = False 82 | self.touched = False 83 | self.x = 0 84 | self.y = 0 85 | self.buf_length = 0 86 | cal = TOUCH.DEFAULT_CAL if calibration is None else calibration 87 | self.asynchronous = False 88 | self.touch_parameter(confidence, margin, delay, cal) 89 | if objsched is not None: 90 | self.asynchronous = True 91 | objsched.add_thread(self._main_thread()) 92 | 93 | # set parameters for get_touch() 94 | # res: Resolution in bits of the returned values, default = 10 95 | # confidence: confidence level - number of consecutive touches with a margin smaller than the given level 96 | # which the function will sample until it accepts it as a valid touch 97 | # margin: Difference from mean centre at which touches are considered at the same position 98 | # delay: Delay between samples in ms. 99 | # 100 | def touch_parameter(self, confidence = 5, margin = 50, delay = 10, calibration = None): 101 | if not self.asynchronous: # Ignore attempts to change on the fly. 102 | confidence = max(min(confidence, 25), 5) 103 | if confidence != self.buf_length: 104 | self.buff = [[0,0] for x in range(confidence)] 105 | self.buf_length = confidence 106 | self.delay = max(min(delay, 100), 5) 107 | margin = max(min(margin, 100), 1) 108 | self.margin = margin * margin # store the square value 109 | if calibration: 110 | self.calibration = calibration 111 | 112 | # get_touch(): Synchronous use. get a touch value; Parameters: 113 | # 114 | # initital: Wait for a non-touch state before getting a sample. 115 | # True = Initial wait for a non-touch state 116 | # False = Do not wait for a release 117 | # wait: Wait for a touch or not? 118 | # False: Do not wait for a touch and return immediately 119 | # True: Wait until a touch is pressed. 120 | # raw: Setting whether raw touch coordinates (True) or normalized ones (False) are returned 121 | # setting the calibration vector to (0, 1, 0, 1, 0, 1, 0, 1) result in a identity mapping 122 | # timeout: Longest time (ms, or None = 1 hr) to wait for a touch or release 123 | # 124 | # Return (x,y) or None 125 | # 126 | def get_touch(self, initial = True, wait = True, raw = False, timeout = None): 127 | if self.asynchronous: 128 | return None # Should only be called in synhronous mode 129 | if timeout == None: 130 | timeout = 3600000 # set timeout to 1 hour 131 | # 132 | if initial: ## wait for a non-touch state 133 | sample = True 134 | while sample and timeout > 0: 135 | sample = self.raw_touch() 136 | pyb.delay(self.delay) 137 | timeout -= self.delay 138 | if timeout <= 0: # after timeout, return None 139 | return None 140 | # 141 | buff = self.buff 142 | buf_length = self.buf_length 143 | buffptr = 0 144 | nsamples = 0 145 | while timeout > 0: 146 | if nsamples == buf_length: 147 | meanx = sum([c[0] for c in buff]) // buf_length 148 | meany = sum([c[1] for c in buff]) // buf_length 149 | dev = sum([(c[0] - meanx)**2 + (c[1] - meany)**2 for c in buff]) / buf_length 150 | if dev <= self.margin: # got one; compare against the square value 151 | if raw: 152 | return (meanx, meany) 153 | else: 154 | return self.do_normalize((meanx, meany)) 155 | # get a new value 156 | sample = self.raw_touch() # get a touch 157 | if sample == None: 158 | if not wait: 159 | return None 160 | nsamples = 0 # Invalidate buff 161 | else: 162 | buff[buffptr] = sample # put in buff 163 | buffptr = (buffptr + 1) % buf_length 164 | nsamples = min(nsamples +1, buf_length) 165 | pyb.delay(self.delay) 166 | timeout -= self.delay 167 | return None 168 | 169 | # Asynchronous use: this thread maintains self.x and self.y 170 | def _main_thread(self): 171 | buff = self.buff 172 | buf_length = self.buf_length 173 | buffptr = 0 174 | nsamples = 0 175 | yield # Initialisation complete, wait for scheduler to start 176 | while True: 177 | if nsamples == buf_length: 178 | meanx = sum([c[0] for c in buff]) // buf_length 179 | meany = sum([c[1] for c in buff]) // buf_length 180 | dev = sum([(c[0] - meanx)**2 + (c[1] - meany)**2 for c in buff]) / buf_length 181 | if dev <= self.margin: # got one; compare against the square value 182 | self.ready = True 183 | self.x, self.y = self.do_normalize((meanx, meany)) 184 | sample = self.raw_touch() # get a touch 185 | if sample == None: 186 | self.touched = False 187 | self.ready = False 188 | nsamples = 0 # Invalidate buff 189 | else: 190 | self.touched = True 191 | buff[buffptr] = sample # put in buff 192 | buffptr = (buffptr + 1) % buf_length 193 | nsamples = min(nsamples + 1, buf_length) 194 | yield 195 | 196 | # Asynchronous get_touch 197 | def get_touch_async(self): 198 | if self.ready: 199 | self.ready = False 200 | return self.x, self.y 201 | return None 202 | # 203 | # do_normalize(touch) 204 | # calculate the screen coordinates from the touch values, using the calibration values 205 | # touch must be the tuple return by get_touch 206 | # 207 | def do_normalize(self, touch): 208 | xmul = self.calibration[3] + (self.calibration[1] - self.calibration[3]) * (touch[1] / 4096) 209 | xadd = self.calibration[2] + (self.calibration[0] - self.calibration[2]) * (touch[1] / 4096) 210 | ymul = self.calibration[7] + (self.calibration[5] - self.calibration[7]) * (touch[0] / 4096) 211 | yadd = self.calibration[6] + (self.calibration[4] - self.calibration[6]) * (touch[0] / 4096) 212 | x = int((touch[0] + xadd) * xmul) 213 | y = int((touch[1] + yadd) * ymul) 214 | return (x, y) 215 | # 216 | # raw_touch(tuple) 217 | # raw read touch. Returns (x,y) or None 218 | # 219 | def raw_touch(self): 220 | x = self.touch_talk(T_GETX, 12) 221 | y = self.touch_talk(T_GETY, 12) 222 | if x > X_LOW and y < Y_HIGH: # touch pressed? 223 | return (x, y) 224 | else: 225 | return None 226 | # 227 | # Send a command to the touch controller and wait for the response 228 | # cmd is the command byte 229 | # int is the expected size of return data bits 230 | # port is the gpio base port 231 | # 232 | # Straight down coding of the data sheet's timing diagram 233 | # This is the slow bytecode implementations 234 | # Clock low & high cycles must last at least 200ns 235 | # At the moment it is set to about 25us each, 236 | # 1050µs total at 168 MHz clock rate. 237 | # Total net time for a 12 bit sample: ~ 1050 µs 238 | # 239 | def touch_talk(self, cmd, bits): 240 | 241 | global CONTROL_PORT 242 | gpio_bsr = CONTROL_PORT + stm.GPIO_BSRRL 243 | gpio_idr = CONTROL_PORT + stm.GPIO_IDR 244 | # 245 | # now shift the command out, which is 8 bits 246 | # data is sampled at the low-> high transient 247 | # 248 | stm.mem16[gpio_bsr + 2] = T_CLOCK # Empty clock cycle before start, maybe obsolete 249 | mask = 0x80 # high bit first 250 | for i in range(8): 251 | stm.mem16[gpio_bsr + 2] = T_CLOCK # set clock low in the beginning 252 | if cmd & mask: 253 | stm.mem16[gpio_bsr + 0] = T_DOUT # set data bit high 254 | else: 255 | stm.mem16[gpio_bsr + 2] = T_DOUT # set data bit low 256 | for i in range(1): pass #delay 257 | stm.mem16[gpio_bsr + 0] = T_CLOCK # set clock high 258 | mask >>= 1 259 | stm.mem16[gpio_bsr + 2] = T_CLOCK | T_DOUT# Another clock & data, low 260 | stm.mem16[gpio_bsr + 0] = T_CLOCK # clock High 261 | # 262 | # now shift the data in, which is 8 or 12 bits 263 | # data is sampled after the high->low transient 264 | # 265 | result = 0 266 | for i in range(bits): 267 | stm.mem16[gpio_bsr + 2] = T_CLOCK # Clock low 268 | if stm.mem16[gpio_idr + 0] & T_DIN: # get data 269 | bit = 1 270 | else: 271 | bit = 0 272 | result = (result << 1) | bit # shift data in 273 | stm.mem16[gpio_bsr + 0] = T_CLOCK # Clock high 274 | # 275 | # another clock cycle, maybe obsolete 276 | # 277 | stm.mem16[gpio_bsr + 2] = T_CLOCK # Another clock toggle, low 278 | stm.mem16[gpio_bsr + 0] = T_CLOCK # clock High 279 | stm.mem16[gpio_bsr + 2] = T_CLOCK # Clock low 280 | # now we're ready to leave 281 | return result 282 | 283 | -------------------------------------------------------------------------------- /touchtest.py: -------------------------------------------------------------------------------- 1 | # 2 | # Some sample code 3 | # 4 | import os, gc 5 | from uctypes import addressof 6 | from tft import * 7 | from touch import * 8 | import font14 9 | 10 | # 11 | # Get string dimensions in pixels 12 | # 13 | def get_stringsize(s, font): 14 | hor = 0 15 | for c in s: 16 | _, vert, cols = font.get_ch(c) 17 | hor += cols 18 | return hor, vert 19 | 20 | def print_centered(tft, x, y, s, color, font): 21 | length, height = get_stringsize(s, font) 22 | tft.setTextStyle(color, None, 2, font) 23 | tft.setTextPos(x - length // 2, y - height // 2) 24 | tft.printString(s) 25 | 26 | # 27 | # buttonarray for touchpad: 28 | # 29 | # Example 30 | # Define touch area | Define display of button | Text in Button 31 | # Value Type Area Display fgcolor bgcolor text color 32 | # str "c"/"r"/"s" Tuple False/"f"/"b" opt. opt. value opt. 33 | # "A" "c" (x,y,r) "f" (0,255,0) False "Yes" None 34 | # "B" "c" (x1,y1,x2,y2) "f" (255,0,0) False "No" None 35 | # "OK" "r" (x1,y1,x2,y2) "b" (0,0,255) False "OK" None 36 | # 37 | keytable = [ 38 | [ "A", "c", ( 50, 50, 25), "f", (0, 255, 0), False, "Yes", (0,0,0)], 39 | [ "B", "c", (120, 50, 25), "f", (255, 0, 0), False, "No", False], 40 | [ "C", "c", (190, 50, 25), "b", (0, 0, 255), False, "???", False], 41 | [ "Q", "s", (260, 30, 320, 70), "f", (128, 128, 128), False, "Quit", False], 42 | ] 43 | 44 | def get_from_keybd(tft, touchpad, keytable, font): 45 | # 46 | # first, check, if buttons are to be displayed 47 | # 48 | if not keytable: 49 | return None 50 | fgcolor = tft.getColor() # save old colors 51 | bgcolor = tft.getBGColor() 52 | for key in keytable: 53 | dtype = key[3] 54 | if dtype: # display the button? 55 | if key[4]: # change color? 56 | tft.setColor(key[4]) 57 | if key[5]: # change BG color? 58 | tft.setBGColor(key[5]) 59 | if key[7]: # Font color? 60 | fontcolor = key[7] 61 | else: 62 | fontcolor = fgcolor 63 | if key[1] == "c": # circle 64 | if dtype == "b": 65 | tft.drawCircle(key[2][0], key[2][1], key[2][2]) 66 | elif dtype == "f": 67 | tft.fillCircle(key[2][0], key[2][1], key[2][2]) 68 | if key[6]: 69 | print_centered(tft, key[2][0], key[2][1], key[6], fontcolor, font) 70 | elif key[1] == "r": # rectangle 71 | if dtype == "b": 72 | tft.drawRectangle(key[2][0], key[2][1], key[2][2], key[2][3]) 73 | elif dtype == "f": 74 | tft.fillRectangle(key[2][0], key[2][1], key[2][2], key[2][3]) 75 | if key[6]: 76 | tft.setColor(fontcolor) 77 | print_centered(tft, (key[2][0] + key[2][2]) // 2, (key[2][1] + key[2][3]) // 2, key[6], fontcolor, font) 78 | elif key[1] == "s": # clipped rectangle 79 | if dtype == "b": 80 | tft.drawClippedRectangle(key[2][0], key[2][1], key[2][2], key[2][3]) 81 | elif dtype == "f": 82 | tft.fillClippedRectangle(key[2][0], key[2][1], key[2][2], key[2][3]) 83 | if key[6]: 84 | tft.setColor(fontcolor) 85 | print_centered(tft, (key[2][0] + key[2][2]) // 2, (key[2][1] + key[2][3]) // 2, key[6], fontcolor, font) 86 | tft.setColor(fgcolor) # restore them 87 | tft.setBGColor(bgcolor) 88 | # get a touch value 89 | value = touchpad.get_touch() # get a touch 90 | # 91 | # check whether it is in one of the button areas 92 | # 93 | if value: # did not get a None 94 | for key in keytable: 95 | if key[1] == "c": # circler 96 | dx = value[0] - key[2][0] 97 | dy = value[1] - key[2][1] 98 | if (dx * dx + dy * dy) < (key[2][2] * key[2][2]): # Pythagoras is alive! 99 | return key[0] 100 | elif key[1] in ("r", "s"): # rectangle 101 | if key[2][0] <= value[0] <= key[2][2] and key[2][1] <= value[1] <= key[2][3]: 102 | return key[0] 103 | return None 104 | 105 | def main(): 106 | 107 | mytft = TFT("SSD1963", "LB04301", LANDSCAPE) 108 | mytouch = TOUCH("XPT2046") 109 | mytft.backlight(100) # light on 110 | 111 | rtn = "" 112 | while rtn != "Q": 113 | rtn = get_from_keybd(mytft, mytouch, keytable, font14) 114 | print("Returned: ", rtn) 115 | mytft.setTextPos(0, 150) 116 | mytft.setTextStyle(None, None, 0, font14) 117 | mytft.printString("Button Value: " + repr(rtn) + " ") 118 | 119 | 120 | main() 121 | -------------------------------------------------------------------------------- /xpt2046_syn.py: -------------------------------------------------------------------------------- 1 | # asyncio version 2 | # The MIT License (MIT) 3 | # 4 | # Copyright (c) 2016, 2017 Robert Hammelrath (basic driver) 5 | # 2016 Peter Hinch (asyncio extension) 6 | # 7 | # Permission is hereby granted, free of charge, to any person obtaining a copy 8 | # of this software and associated documentation files (the "Software"), to deal 9 | # in the Software without restriction, including without limitation the rights 10 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | # copies of the Software, and to permit persons to whom the Software is 12 | # furnished to do so, subject to the following conditions: 13 | # 14 | # The above copyright notice and this permission notice shall be included in 15 | # all copies or substantial portions of the Software. 16 | # 17 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | # THE SOFTWARE. 24 | # 25 | # Class supporting the resisitve touchpad of TFT LC-displays 26 | # 27 | from time import sleep_ms 28 | from machine import SPI, Pin 29 | # define constants 30 | # 31 | T_GETX = const(0xd0) ## 12 bit resolution 32 | T_GETY = const(0x90) ## 12 bit resolution 33 | T_GETZ1 = const(0xb8) ## 8 bit resolution 34 | T_GETZ2 = const(0xc8) ## 8 bit resolution 35 | # 36 | X_LOW = const(10) ## lowest reasonable X value from the touchpad 37 | Y_HIGH = const(4090) ## highest reasonable Y value 38 | 39 | class XPT2046: 40 | # 41 | # Init just sets the PIN's to In / out as required 42 | # async: set True if asynchronous operation intended 43 | # confidence: confidence level - number of consecutive touches with a margin smaller than the given level 44 | # which the function will sample until it accepts it as a valid touch 45 | # margin: Distance from mean centre at which touches are considered at the same position 46 | # delay: Delay between samples in ms. (n/a if asynchronous) 47 | # 48 | DEFAULT_CAL = (-3917, -0.127, -3923, -0.1267, -3799, -0.07572, -3738, -0.07814) 49 | 50 | def __init__(self, spi=None, cs=None, *, confidence=5, margin=50, delay=10, calibration=None): 51 | if spi is None: 52 | raise IOError("The SPI object has to be supplied") 53 | else: 54 | self.spi = spi 55 | self.cs = cs 56 | self.recv = bytearray(3) 57 | self.xmit = bytearray(3) 58 | # set default values 59 | self.ready = False 60 | self.touched = False 61 | self.x = 0 62 | self.y = 0 63 | self.buf_length = 0 64 | cal = XPT2046.DEFAULT_CAL if calibration is None else calibration 65 | self.touch_parameter(confidence, margin, delay, cal) 66 | 67 | # set parameters for get_touch() 68 | # res: Resolution in bits of the returned values, default = 10 69 | # confidence: confidence level - number of consecutive touches with a margin smaller than the given level 70 | # which the function will sample until it accepts it as a valid touch 71 | # margin: Difference from mean centre at which touches are considered at the same position 72 | # delay: Delay between samples in ms. 73 | # 74 | def touch_parameter(self, confidence=5, margin=50, delay=10, calibration=None): 75 | confidence = max(min(confidence, 25), 5) 76 | if confidence != self.buf_length: 77 | self.buff = [[0,0] for x in range(confidence)] 78 | self.buf_length = confidence 79 | self.delay = max(min(delay, 100), 5) 80 | margin = max(min(margin, 100), 1) 81 | self.margin = margin * margin # store the square value 82 | if calibration: 83 | self.calibration = calibration 84 | 85 | # get_touch(): Synchronous use. get a touch value; Parameters: 86 | # 87 | # initital: Wait for a non-touch state before getting a sample. 88 | # True = Initial wait for a non-touch state 89 | # False = Do not wait for a release 90 | # wait: Wait for a touch or not? 91 | # False: Do not wait for a touch and return immediately 92 | # True: Wait until a touch is pressed. 93 | # raw: Setting whether raw touch coordinates (True) or normalized ones (False) are returned 94 | # setting the calibration vector to (0, 1, 0, 1, 0, 1, 0, 1) result in a identity mapping 95 | # timeout: Longest time (ms, or None = 1 hr) to wait for a touch or release 96 | # 97 | # Return (x,y) or None 98 | # 99 | def get_touch(self, *, initial=True, wait=True, raw=False, timeout=None): 100 | if timeout is None: 101 | timeout = 3600000 # set timeout to 1 hour 102 | # 103 | if initial: ## wait for a non-touch state 104 | sample = True 105 | while sample and timeout > 0: 106 | sample = self.raw_touch() 107 | sleep_ms(self.delay) 108 | timeout -= self.delay 109 | if timeout <= 0: # after timeout, return None 110 | return None 111 | # 112 | buff = self.buff 113 | buf_length = self.buf_length 114 | buffptr = 0 115 | nsamples = 0 116 | while timeout > 0: 117 | if nsamples == buf_length: 118 | meanx = sum([c[0] for c in buff]) // buf_length 119 | meany = sum([c[1] for c in buff]) // buf_length 120 | dev = sum([(c[0] - meanx)**2 + (c[1] - meany)**2 for c in buff]) / buf_length 121 | if dev <= self.margin: # got one; compare against the square value 122 | if raw: 123 | return (meanx, meany) 124 | else: 125 | return self.do_normalize((meanx, meany)) 126 | # get a new value 127 | sample = self.raw_touch() # get a touch 128 | if sample is None: 129 | if not wait: 130 | return None 131 | nsamples = 0 # Invalidate buff 132 | else: 133 | buff[buffptr] = sample # put in buff 134 | buffptr = (buffptr + 1) % buf_length 135 | nsamples = min(nsamples + 1, buf_length) 136 | sleep_ms(self.delay) 137 | timeout -= self.delay 138 | return None 139 | # 140 | # do_normalize(touch) 141 | # calculate the screen coordinates from the touch values, using the calibration values 142 | # touch must be the tuple return by get_touch 143 | # 144 | def do_normalize(self, touch): 145 | xmul = self.calibration[3] + (self.calibration[1] - self.calibration[3]) * (touch[1] / 4096) 146 | xadd = self.calibration[2] + (self.calibration[0] - self.calibration[2]) * (touch[1] / 4096) 147 | ymul = self.calibration[7] + (self.calibration[5] - self.calibration[7]) * (touch[0] / 4096) 148 | yadd = self.calibration[6] + (self.calibration[4] - self.calibration[6]) * (touch[0] / 4096) 149 | x = int((touch[0] + xadd) * xmul) 150 | y = int((touch[1] + yadd) * ymul) 151 | return (x, y) 152 | # 153 | # raw_touch(tuple) 154 | # raw read touch. Returns (x,y) or None 155 | # 156 | def raw_touch(self): 157 | x = self.touch_talk(T_GETX, 12) 158 | y = self.touch_talk(T_GETY, 12) 159 | if x > X_LOW and y < Y_HIGH: # touch pressed? 160 | return (x, y) 161 | else: 162 | return None 163 | # 164 | # Send a command to the touch controller and wait for the response 165 | # cmd: command byte 166 | # bits: expected data size. Reasonable values are 8 and 12 167 | # 168 | def touch_talk(self, cmd, bits): 169 | if self.cs is not None: 170 | self.cs(0) 171 | self.xmit[0] = cmd 172 | self.spi.write_readinto(self.xmit, self.recv) 173 | if self.cs is not None: 174 | self.cs(1) 175 | return (self.recv[1] * 256 + self.recv[2]) >> (15 - bits) 176 | 177 | -------------------------------------------------------------------------------- /xpttest.py: -------------------------------------------------------------------------------- 1 | from machine import SPI 2 | from xpt2046 import XPT2046 3 | from time import sleep 4 | 5 | spi = SPI(1, baudrate=1000000) 6 | 7 | xpt = XPT2046(spi) 8 | 9 | while True: 10 | p = xpt.get_touch(raw=True) 11 | if p is not None: 12 | print(p) 13 | sleep(0.1) 14 | if p[0] < 500 and p[1] < 500: 15 | break 16 | 17 | 18 | 19 | --------------------------------------------------------------------------------