def printChar(self, c, bg_buf=None): # get the charactes pixel bitmap and dimensions if self.text_font: fmv, rows, cols = self.text_font.get_ch(c) else: raise AttributeError('No font selected') cbytes, cbits = divmod(cols, 8) # Not in packed format dcols = (cbytes + 1) * 8 if cbits else cbytes * 8 # cols for display pix_count = dcols * rows # number of bits in the char # test char fit if self.text_x + cols > self.text_width: # does the char fit on the screen? if self.text_scroll: self.printCR() # No, then CR self.printNewline(True) # NL: advance to the next line else: return 0 # Retrieve Background data if transparency is required if self.transparency: # in case of transpareny, the frame buffer content is needed if bg_buf is None: # buffer allocation needed? if len(self.bg_buf) < pix_count * 3: del(self.bg_buf) gc.collect() self.bg_buf = bytearray(pix_count * 3) # Make it bigger bg_buf = self.bg_buf self.setXY(self.text_x, self.text_y, self.text_x + dcols - 1, self.text_y + rows - 1) # set area TFT_io.tft_read_cmd_data_AS(0x2e, bg_buf, pix_count * 3) # read background data else: bg_buf = 0 # dummy assignment, since None is not accepted # Set XY range & print char self.setXY(self.text_x, self.text_y, self.text_x + dcols - 1, self.text_y + rows - 1) # set area TFT_io.displaySCR_charbitmap(addressof(fmv), pix_count, self.text_color, bg_buf) # display char! #advance pointer self.text_x += (cols + self.text_gap) return cols + self.text_gap
def drawVLine(self, x, y, l, color = None): # draw horiontal Line colorvect = self.colorvect if color is None else bytearray(color) if l < 0: # negative length, swap parameters l = -l y -= l self.setXY(x, y, x, y + l - 1) # set display window TFT_io.fillSCR_AS(colorvect, l)
def clrSCR(self, color = None): colorvect = self.BGcolorvect if color is None else bytearray(color) self.clrXY() TFT_io.fillSCR_AS(colorvect, (self.disp_x_size + 1) * (self.disp_y_size + 1)) self.setScrollArea(0, self.disp_y_size + 1, 0) self.setScrollStart(0) self.setTextPos(0,0)
def set_tft_mode(self, v_flip = False, h_flip = False, c_flip = False, orientation = LANDSCAPE): self.v_flip = v_flip # flip vertical self.h_flip = h_flip # flip horizontal self.c_flip = c_flip # flip blue/red self.orientation = orientation # LANDSCAPE/PORTRAIT TFT_io.tft_cmd_data_AS(0x36, bytearray([(self.orientation << 5) |(self.c_flip << 3) | (self.h_flip & 1) << 1 | (self.v_flip) & 1]), 1)
def fillRectangle(self, x1, y1, x2, y2, color=None): if x1 > x2: x1, x2 = x2, x1 if y1 > y2: y1, y2 = y2, y1 self.setXY(x1, y1, x2, y2) # set display window if color: TFT_io.fillSCR_AS(bytearray(color), (x2 - x1 + 1) * (y2 - y1 + 1)) else: TFT_io.fillSCR_AS(self.colorvect, (x2 - x1 + 1) * (y2 - y1 + 1))
def setScrollArea(self, tfa, vsa, bfa): TFT_io.tft_cmd_data_AS(0x33, bytearray( #set scrolling range [(tfa >> 8) & 0xff, tfa & 0xff, (vsa >> 8) & 0xff, vsa & 0xff, (bfa >> 8) & 0xff, bfa & 0xff]), 6) self.scroll_tfa = tfa self.scroll_vsa = vsa self.scroll_bfa = bfa self.setScrollStart(self.scroll_tfa) x, y = self.getTextPos() self.setTextPos(x, y) # realign pointers
def show(self): tft = self.tft width = self.width dx = 5 x0 = self.x0 x1 = self.x1 y0 = self.y0 y1 = self.y1 height = y1 - y0 if self.divisions > 0: dy = height / (self.divisions) # Tick marks for tick in range(self.divisions + 1): ypos = int(y0 + dy * tick) tft.draw_hline(x0, ypos, dx, self.fgcolor) tft.draw_hline(x1 - dx, ypos, dx, self.fgcolor) if self.legends is not None and self.font is not None: # Legends if len(self.legends) <= 1: dy = 0 else: dy = height / (len(self.legends) - 1) yl = self.y1 # Start at bottom for legend in self.legends: print_centered(tft, int(self.x0 + self.width / 2), int(yl), legend, self.fontcolor, self.font) yl -= dy if self.ptr_y is not None: # Restore background if it was saved tft.setXY(x0, self.ptr_y, x1, self.ptr_y) TFT_io.tft_write_data_AS(self.ptrbuf, self.ptrbytes) self.ptr_y = int(self.y1 - self._value * height) # y position of slider tft.setXY(x0, self.ptr_y, x1, self.ptr_y) # Read background TFT_io.tft_read_cmd_data_AS(0x2e, self.ptrbuf, self.ptrbytes) tft.draw_hline(x0, self.ptr_y, width, self.pointercolor) # Draw pointer
def printClrLine(self, mode = 0): # clear to end of line/bol/line if mode == 0: self.setXY(self.text_x, self.text_y, self.text_width - 1, self.text_y + self.text_rows - 1) # set display window TFT_io.fillSCR_AS(self.text_color, (self.text_width - self.text_x + 1) * self.text_rows) elif mode == 1 and self.text_x > 0: self.setXY(0, self.text_y, self.text_x - 1, self.text_y + self.text_rows - 1) # set display window TFT_io.fillSCR_AS(self.text_color, (self.text_x - 1) * self.text_rows) elif mode == 2: self.setXY(0, self.text_y, self.text_width - 1, self.text_y + self.text_rows - 1) # set display window TFT_io.fillSCR_AS(self.text_color, self.text_width * self.text_rows)
def render_bg(self, tft): if self.slide_y is not None: tft.setXY(*self.slide_coords()) TFT_io.tft_write_data_AS(self.slidebuf, self.slidebytes)
def save_background(self, tft): # Read background under slide if self.slide_y is not None: tft.setXY(*self.slide_coords()) TFT_io.tft_read_cmd_data_AS(0x2e, self.slidebuf, self.slidebytes)
def printClrSCR(self): # clear Area set by setScrollArea self.setXY(0, self.scroll_tfa, self.text_width - 1, self.scroll_tfa + self.scroll_vsa) # set display window TFT_io.fillSCR_AS(self.text_color, self.text_width * self.scroll_vsa) self.setScrollStart(self.scroll_tfa) self.setTextPos(0, self.scroll_tfa)
def tft_init(self, controller = "SSD1963", lcd_type = "LB04301", orientation = LANDSCAPE, v_flip = False, h_flip = False, power_control = True): # # For convenience, define X1..X1 and Y9..Y12 as output port using thy python functions. # X1..X8 will be redefind on the fly as Input by accessing the MODER control registers # when needed. Y9 is treate seperately, since it is used for Reset, which is done at python level # since it need long delays anyhow, 5 and 15 ms vs. 10 µs. # # Set TFT general defaults self.controller = controller self.lcd_type = lcd_type self.orientation = orientation self.v_flip = v_flip # flip vertical self.h_flip = h_flip # flip horizontal self.c_flip = 0 # flip blue/red self.rc_flip = 0 # flip row/column self.setColor((255, 255, 255)) # set FG color to white as can be. self.setBGColor((0, 0, 0)) # set BG to black self.bg_buf = bytearray() # self.pin_led = None # deferred init Flag self.power_control = power_control if self.power_control: # special treat for Power Pin self.pin_power = pyb.Pin("Y4", pyb.Pin.OUT_PP) self.power(True) ## switch Power on # pyb.delay(10) # this may have to be moved to the controller specific section if orientation == PORTRAIT: self.setXY = TFT_io.setXY_P self.drawPixel = TFT_io.drawPixel_P else: self.setXY = TFT_io.setXY_L self.drawPixel = TFT_io.drawPixel_L self.swapbytes = TFT_io.swapbytes self.swapcolors = TFT_io.swapcolors # ---------- for pin_name in ["X1", "X2", "X3", "X4", "X5", "X6", "X7", "X8", "Y10", "Y11", "Y12"]: pin = pyb.Pin(pin_name, pyb.Pin.OUT_PP) # set as output pin.value(1) ## set high as default # special treat for Reset self.pin_reset = pyb.Pin("Y9", pyb.Pin.OUT_PP) # Reset the device self.pin_reset.value(1) ## do a hard reset pyb.delay(10) self.pin_reset.value(0) ## Low pyb.delay(20) self.pin_reset.value(1) ## set high again pyb.delay(20) # # Now initialiize the LCD # This is for the SSD1963 controller and two specific LCDs. More may follow. # Data taken from the SSD1963 data sheet, SSD1963 Application Note and the LCD Data sheets # if controller == "SSD1963": # 1st approach for 480 x 272 TFT_io.tft_cmd_data(0xe2, bytearray(b'\x1d\x02\x54'), 3) # PLL multiplier, set PLL clock to 100M # N=0x2D for 6.5MHz, 0x1D for 10MHz crystal # PLLClock = Crystal * (Mult + 1) / (Div + 1) # The intermediate value Crystal * (Mult + 1) must be between 250MHz and 750 MHz TFT_io.tft_cmd_data(0xe0, bytearray(b'\x01'), 1) # PLL Enable pyb.delay(10) TFT_io.tft_cmd_data(0xe0, bytearray(b'\x03'), 1) pyb.delay(10) TFT_io.tft_cmd(0x01) # software reset pyb.delay(10) # # Settings for the LCD # # The LCDC_FPR depends on PLL clock and the reccomended LCD Dot clock DCLK # # LCDC_FPR = (DCLK * 1048576 / PLLClock) - 1 # # The other settings are less obvious, since the definitions of the SSD1963 data sheet and the # LCD data sheets differ. So what' common, even if the names may differ: # HDP Horizontal Panel width (also called HDISP, Thd). The value store in the register is HDP - 1 # VDP Vertical Panel Width (also called VDISP, Tvd). The value stored in the register is VDP - 1 # HT Total Horizontal Period, also called HP, th... The exact value does not matter # VT Total Vertical Period, alco called VT, tv, .. The exact value does not matter # HPW Width of the Horizontal sync pulse, also called HS, thpw. # VPW Width of the Vertical sync pulse, also called VS, tvpw # Front Porch (HFP and VFP) Time between the end of display data and the sync pulse # Back Porch (HBP and VBP Time between the start of the sync pulse and the start of display data. # HT = FP + HDP + BP and VT = VFP + VDP + VBP (sometimes plus sync pulse width) # Unfortunately, the controller does not use these front/back porch times, instead it uses an starting time # in the front porch area and defines (see also figures in chapter 13.3 of the SSD1963 data sheet) # HPS Time from that horiz. starting point to the start of the horzontal display area # LPS Time from that horiz. starting point to the horizontal sync pulse # VPS Time from the vert. starting point to the first line # FPS Time from the vert. starting point to the vertical sync pulse # # So the following relations must be held: # # HT > HDP + HPS # HPS >= HPW + LPS # HPS = Back Porch - LPS, or HPS = Horizontal back Porch # VT > VDP + VPS # VPS >= VPW + FPS # VPS = Back Porch - FPS, or VPS = Vertical back Porch # # LPS or FPS may have a value of zero, since the length of the front porch is detemined by the # other figures # # The best is to start with the recomendations of the lCD data sheet for Back porch, grab a # sync pulse with and the determine the other, such that they meet the relations. Typically, these # values allow for some ambuigity. # if lcd_type == "LB04301": # Size 480x272, 4.3", 24 Bit, 4.3" # # Value Min Typical Max # DotClock 5 MHZ 9 MHz 12 MHz # HT (Hor. Total 490 531 612 # HDP (Hor. Disp) 480 # HBP (back porch) 8 43 # HFP (Fr. porch) 2 8 # HPW (Hor. sync) 1 # VT (Vert. Total) 275 288 335 # VDP (Vert. Disp) 272 # VBP (back porch) 2 12 # VFP (fr. porch) 1 4 # VPW (vert. sync) 1 10 # # This table in combination with the relation above leads to the settings: # HPS = 43, HPW = 8, LPS = 0, HT = 531 # VPS = 14, VPW = 10, FPS = 0, VT = 288 # self.disp_x_size = 479 self.disp_y_size = 271 TFT_io.tft_cmd_data_AS(0xe6, bytearray(b'\x01\x70\xa3'), 3) # PLL setting for PCLK # (9MHz * 1048576 / 100MHz) - 1 = 94371 = 0x170a3 TFT_io.tft_cmd_data_AS(0xb0, bytearray( # # LCD SPECIFICATION [0x20, # 24 Color bits, HSync/VSync low, No Dithering 0x00, # TFT mode self.disp_x_size >> 8, self.disp_x_size & 0xff, # physical Width of TFT self.disp_y_size >> 8, self.disp_y_size & 0xff, # physical Height of TFT 0x00]), 7) # Last byte only required for a serial TFT TFT_io.tft_cmd_data_AS(0xb4, bytearray(b'\x02\x13\x00\x2b\x08\x00\x00\x00'), 8) # HSYNC, Set HT 531 HPS 43 HPW=Sync pulse 8 LPS 0 TFT_io.tft_cmd_data_AS(0xb6, bytearray(b'\x01\x20\x00\x0e\x0a\x00\x00'), 7) # VSYNC, Set VT 288 VPS 14 VPW 10 FPS 0 TFT_io.tft_cmd_data_AS(0x36, bytearray([(orientation & 1) << 5 | (h_flip & 1) << 1 | (v_flip) & 1]), 1) # rotation/ flip, etc., t.b.d. elif lcd_type == "AT070TN92": # Size 800x480, 7", 18 Bit, lower color bits ignored # # Value Min Typical Max # DotClock 26.4 MHz 33.3 MHz 46.8 MHz # HT (Hor. Total 862 1056 1200 # HDP (Hor. Disp) 800 # HBP (back porch) 46 46 46 # HFP (Fr. porch) 16 210 254 # HPW (Hor. sync) 1 40 # VT (Vert. Total) 510 525 650 # VDP (Vert. Disp) 480 # VBP (back porch) 23 23 23 # VFP (fr. porch) 7 22 147 # VPW (vert. sync) 1 20 # # This table in combination with the relation above leads to the settings: # HPS = 46, HPW = 8, LPS = 0, HT = 1056 # VPS = 23, VPW = 10, VPS = 0, VT = 525 # self.disp_x_size = 799 self.disp_y_size = 479 TFT_io.tft_cmd_data_AS(0xe6, bytearray(b'\x05\x53\xf6'), 3) # PLL setting for PCLK # (33.3MHz * 1048576 / 100MHz) - 1 = 349174 = 0x553f6 TFT_io.tft_cmd_data_AS(0xb0, bytearray( # # LCD SPECIFICATION [0x00, # 18 Color bits, HSync/VSync low, No Dithering/FRC 0x00, # TFT mode self.disp_x_size >> 8, self.disp_x_size & 0xff, # physical Width of TFT self.disp_y_size >> 8, self.disp_y_size & 0xff, # physical Height of TFT 0x00]), 7) # Last byte only required for a serial TFT TFT_io.tft_cmd_data_AS(0xb4, bytearray(b'\x04\x1f\x00\x2e\x08\x00\x00\x00'), 8) # HSYNC, Set HT 1056 HPS 46 HPW 8 LPS 0 TFT_io.tft_cmd_data_AS(0xb6, bytearray(b'\x02\x0c\x00\x17\x08\x00\x00'), 7) # VSYNC, Set VT 525 VPS 23 VPW 08 FPS 0 TFT_io.tft_cmd_data_AS(0x36, bytearray([(orientation & 1) << 5 | (h_flip & 1) << 1 | (v_flip) & 1]), 1) # rotation/ flip, etc., t.b.d. else: print("Wrong Parameter lcd_type: ", lcd_type) return TFT_io.tft_cmd_data_AS(0xBA, bytearray(b'\x0f'), 1) # GPIO[3:0] out 1 TFT_io.tft_cmd_data_AS(0xB8, bytearray(b'\x07\x01'), 1) # GPIO3=input, GPIO[2:0]=output TFT_io.tft_cmd_data_AS(0xf0, bytearray(b'\x00'), 1) # Pixel data Interface 8 Bit TFT_io.tft_cmd(0x29) # Display on TFT_io.tft_cmd_data_AS(0xbe, bytearray(b'\x06\xf0\x01\xf0\x00\x00'), 6) # Set PWM for B/L TFT_io.tft_cmd_data_AS(0xd0, bytearray(b'\x0d'), 1) # Set DBC: enable, agressive else: print("Wrong Parameter controller: ", controller) return # # Set character printing defaults # self.text_font = None self.setTextStyle(self.color, self.BGcolor, 0, None, 0) # # Init done. clear Screen and switch BG LED on # self.text_x = self.text_y = self.text_yabs = 0 self.clrSCR() # clear the display
def setScrollStart(self, lline): self.scroll_start = lline # store the logical first line TFT_io.tft_cmd_data_AS(0x37, bytearray([(lline >> 8) & 0xff, lline & 0xff]), 2)
def drawBitmap(self, x, y, sx, sy, data, mode = 24, colortable = None): self.setXY(x, y, x + sx - 1, y + sy - 1) if mode == 24: TFT_io.displaySCR_AS(data, sx * sy) elif mode == 16: TFT_io.displaySCR565_AS(data, sx * sy) elif mode == 1: if colortable is None: colortable = self.BMPcolortable # create colortable TFT_io.displaySCR_bmp(data, sx*sy, 1, colortable) elif mode == 2: if colortable is None: return TFT_io.displaySCR_bmp(data, sx*sy, 2, colortable) elif mode == 4: if colortable is None: return TFT_io.displaySCR_bmp(data, sx*sy, 4, colortable) elif mode == 8: if colortable is None: return TFT_io.displaySCR_bmp(data, sx*sy, 8, colortable)
def drawPixel_py(self, x, y, color): self.setXY(x, y, x, y) TFT_io.displaySCR_AS(color, 1) #