import gc from machine import I2C from lcd import color, constant, font class LCD(object): def __init__(self, dt=constant.I2C128x64, baud=constant.I2CBAUDRATE): self.__width = int(dt['width'] if 'width' in dt else 0) self.__height = int(dt['height'] if 'height' in dt else 0) self.__pages = int(self.__height / 8) self.__buff_size = int(self.__width * self.__height / 8) self.__buffer = [0] * self.__buff_size self.__i2c = I2C(0, I2C.MASTER, baudrate=baud) self.__write(constant.SSD1306_DISPLAYOFF) self.__write(constant.SSD1306_SETDISPLAYCLOCKDIV, 0x80) self.__write(constant.SSD1306_SETDISPLAYOFFSET, 0x0) self.__write(constant.SSD1306_SETSTARTLINE | 0x0) self.__write(constant.SSD1306_CHARGEPUMP, 0x14) self.__write(constant.SSD1306_MEMORYMODE, 0x00) self.__write(constant.SSD1306_COLUMNADDR, 0, self.__width - 1) self.__write(constant.SSD1306_PAGEADDR, 0, self.__pages - 1) self.__write(constant.SSD1306_SEGREMAP | 0x1) self.__write(constant.SSD1306_COMSCANDEC) self.__write(constant.SSD1306_SETPRECHARGE, 0xF1) self.__write(constant.SSD1306_SETVCOMDETECT, 0x40) self.__write(constant.SSD1306_DISPLAYALLON_RESUME) self.__write(constant.SSD1306_NORMALDISPLAY) if dt == constant.I2C128x32: self.__write(constant.SSD1306_SETMULTIPLEX, 0x1F) self.__write(constant.SSD1306_SETCOMPINS, 0x02) self.__write(constant.SSD1306_SETCONTRAST, 0x8F) elif dt == constant.I2C128x64: self.__write(constant.SSD1306_SETMULTIPLEX, 0x3F) self.__write(constant.SSD1306_SETCOMPINS, 0x12) self.__write(constant.SSD1306_SETCONTRAST, 0xCF) else: raise NotImplementedError self.display_clear(True) self.display_off() def __write(self, *args): b = [0] if len(args) == 1 and type(args[0]) == bytearray: b = args[0] else: b = bytearray(b + list(args)) self.__i2c.writeto(constant.SSD1306_I2C_ADDRESS, b) gc.collect() def display_contrast(self, c): if c < 0 or c > 255: raise ValueError("Contrast must be between 0-255") self.__write(constant.SSD1306_SETCONTRAST, c) def display_off(self): self.__write(constant.SSD1306_DISPLAYOFF) def display_on(self): self.__write(constant.SSD1306_DISPLAYON) def display_clear(self, update=False): self.__buffer = [0] * self.__buff_size if update: self.display_update() def draw_px(self, x, y, clr=color.WHITE): pos = int(y / 8) * self.__width + x bm = (0x1 << (y % 8)) bpos = self.__buffer[pos] self.__buffer[pos] = bpos | bm if clr == color.WHITE else bpos & (0xff ^ bm) def draw_line(self, x0, y0, x1, y1, clr=color.WHITE): if x0 == x1: self.__draw_v_line(x0, y0, abs(y1 - y0), clr) elif y0 == y1: self.__draw_h_line(x0, y0, abs(x1 - x0), clr) else: self.__draw_line(x0, y0, x1, y1, clr) def __draw_line(self, x0, y0, x1, y1, clr): steep = abs(y1 - y0) > abs(x1 - x0) if steep: x0, y0 = y0, x0 x1, y1 = y1, x1 if x0 > x1: x0, x1 = x1, x0 y0, y1 = y1, y0 dx = abs(x1 - x0) dy = abs(y1 - y0) err = dx / 2 ystep = 1 if y0 < y1 else -1 y = y0 for x in range(x0, x1): if steep: self.draw_px(y, x, clr) else: self.draw_px(x, y, clr) err = err - dy if err < 0: y += ystep err += dx def __draw_v_line(self, x, y, h, clr): for y1 in range(y, y + h): self.draw_px(x, y1, clr) def __draw_h_line(self, x, y, w, clr): for x1 in range(x, x + w): self.draw_px(x1, y, clr) def __bresenham_plot(self, x, d1, y, d2, clr): self.draw_px(x + d1, y + d2) self.draw_px(x + d1, y - d2) self.draw_px(x - d1, y + d2) self.draw_px(x - d1, y - d2) self.draw_px(x + d2, y + d1) self.draw_px(x + d2, y - d1) self.draw_px(x - d2, y + d1) self.draw_px(x - d2, y - d1) def __bresenham_line(self, x, d1, y, d2, clr): self.__draw_v_line(x + d1, y - d2, 2 * d2, clr) self.__draw_v_line(x + d2, y - d1, 2 * d1, clr) self.__draw_v_line(x - d1, y - d2, 2 * d2, clr) self.__draw_v_line(x - d2, y - d1, 2 * d1, clr) def draw_circle(self, x0, y0, r, clr=color.WHITE): x = r y = 0 err = 0 while x >= y: self.__bresenham_plot(x0, x, y0, y, clr) if err <= 0: y += 1 err += (2 * y) + 1 else: x -= 1 err -= (2 * x) + 1 def draw_rect(self, x, y, w, h, clr=color.WHITE): self.__draw_h_line(x, y, w, clr) self.__draw_h_line(x, y + h, w, clr) self.__draw_v_line(x, y, h, clr) self.__draw_v_line(x + w, y, h, clr) def fill_circle(self, x0, y0, r, clr=color.WHITE): x = r y = 0 err = 0 while x >= y: self.__bresenham_line(x0, x, y0, y, clr) if err <= 0: y += 1 err += (2 * y) + 1 else: x -= 1 err -= (2 * x) + 1 def fill_rect(self, x, y, w, h, clr=color.WHITE): for i in range(x, x+w): self.__draw_v_line(i, y, h, clr) def fill_screen(self, clr=color.WHITE): self.fill_rect(0, 0, self.__width, self.__height, clr) def invert_colors(self): for i in range(0, len(self.__buffer)): self.__buffer[i] = ~self.__buffer[i] def draw_string(self, x, y, s, f=font.ASCII, clr=color.WHITE): mx = int(self.__width / (f['width'] + 1)) my = int(self.__height / (f['height'] + 1)) if x < 0 or (x + len(s)) > mx: raise ValueError("Out of X range") if y < 0 or y > my: raise ValueError("Out of Y range") pos = (self.__width) * y + ((f['width'] + 1) * x) for i in range(0, len(s)): c = 5 * (ord(s[i]) - 32) for p in range(0, f['width']): self.__buffer[pos + p] = f['value'][c + p] pos = pos + 6 def display_text_mid_x(self, l, f=font.ASCII): mx = int(self.__width / (f['width'] + 1)) return int((mx - l) / 2) def display_text_mid_y(self, f=font.ASCII): my = int(self.__height / (f['height'] + 1)) return int(my / 2) def display_update(self): self.__write(constant.SSD1306_SETLOWCOLUMN) self.__write(constant.SSD1306_SETHIGHCOLUMN) self.__write(constant.SSD1306_SETSTARTLINE) line = [0] * 17 line[0] = 0x40 for i in range(0, len(self.__buffer), 16): for pos in range(0, 16): line[pos + 1] = self.__buffer[pos + i] self.__write(bytearray(line))