## 驱动SSD1306显示屏 ### 1. I2C屏幕 # Raspberry Pi Pico OLED Display Test # Uses ssd1306 module # display-ssd1306-test.py # EETree Sci & Tech 2021 # https://www.eetree.cn import machine import utime sda=machine.Pin(11) scl=machine.Pin(10) i2c=machine.I2C(0, sda=sda, scl=scl, freq=400000) from ssd1306 import SSD1306_I2C oled = SSD1306_I2C(128, 32, i2c) print(i2c.scan()) oled.text('Welcome to the', 0, 0) oled.text('Pi Pico', 0, 10) oled.text('Display Demo', 0, 20) oled.show() utime.sleep(4) oled.fill(1) oled.show() utime.sleep(2) oled.fill(0) oled.show() while True: oled.text("Hello World",0,0) for i in range (0, 164): oled.scroll(1,0) oled.show() utime.sleep(0.01) 这段代码是基于I2C接口协议的,将两个GPIO引脚定义为SDA(GPIO20)和SCL(GPIO21)。 Pico有两组I2C总线,你可以使用几种不同的GPIO引脚来连接它们。但它们并不是随便的引脚,例如某些引脚被指定为总线0的SDA,只有它们才能用于SDA总线0。 然后我们使用机器库的I2C函数定义一个I2C连接。我们需要给它提供以下参数。 I2C总线号,在我们的例子中是0。 SDA引脚 SCL引脚 I2C总线频率--在我们的例子中是400KHz。 然后我们添加OLED库,并创建一个I2C OLED对象。我们将大小参数和I2C连接信息传递给它。 注意,我们没有传递I2C地址。SD1306 OLED显示器有一个固定的I2C地址,所以我们不需要指定它。 不过,我在这里添加了一行与显示器无关的内容,但可以让你扫描I2C总线,并打印出它发现占用的地址。请注意,在Shell中打印出来的是十进制,而不是你更可能习惯看到的十六进制。 回到OLED脚本! 我们开始在显示屏上打印,几行文字。注意我们如何指定每行开始的像素位置。 实际上,我们并没有直接打印到显示屏上,而是将数据发送到一个缓冲区。oled.show()这一行将把该缓冲区的数据传输到显示器上。 在打印完欢迎信息并按住它四秒钟后,我们再执行oled.fill(1)。这将打开显示器中的每一个像素,或者更准确地说,是缓冲区中的每一个像素。然后我们做一个oled.show()来显示填充。 我们将填充物保持在显示屏上两秒钟,然后执行oled.fill(0),当我们显示它时,它将关闭显示屏中的每个像素。 现在进入True循环。 我们再次输入一些文本,经典的 "Hello World"。但我们没有进行 "显示 "来显示它,而是开始一个for-loop。在这个循环中,我们使用led.scroll(1,0)将显示水平移动1像素(垂直移动0像素,这就是另一个参数)。 然后我们在屏幕上显示,然后休眠一段很短的时间。然后我们再做一次。 结果将是显示屏在屏幕上滚动。你可以在for-loop中使用参数来改变它。 将脚本加载到你的Pico上,然后观看显示。你应该看到欢迎文字,然后是显示填充,然后是空。之后,只要你让实验运行,滚动的 "Hello World "就会继续。 ### 2. SPI屏幕 from machine import Pin, SPI from ssd1306 import SSD1306_SPI import framebuf from time import sleep from utime import sleep_ms spi = SPI(1, 100000, mosi=Pin(11), sck=Pin(10)) oled = SSD1306_SPI(128, 32, spi, Pin(9),Pin(8), Pin(0)) #oled = SSD1306_SPI(WIDTH, HEIGHT, spi, dc,rst, cs) use GPIO PIN NUMBERS while True: try: for i in range(40): for j in range(56): oled.fill(0) oled.show() #sleep(1) oled.text("HELLO www.eetree.cn",i,j) oled.show() sleep_ms(50) except KeyboardInterrupt: break ### 3. Adafruit关于SSD1306的使用介绍 #### I2C初始化 import machine import ssd1306 i2c = machine.I2C(-1, machine.Pin(5), machine.Pin(4)) oled = ssd1306.SSD1306_I2C(128, 32, i2c) #### SPI初始化 import machine import ssd1306 spi = machine.SPI(1, baudrate=8000000, polarity=0, phase=0) oled = ssd1306.SSD1306_SPI(128, 32, spi, machine.Pin(15), machine.Pin(0), machine.Pin(16) ### 4. SSD1306库的调用 [[https://docs.micropython.org/en/latest/esp8266/tutorial/ssd1306.html|Using a SSD1306 OLED display]] SSD1306 OLED显示可以使用SPI或I2C接口,支持不同分辨率(128x64, 128x32, 72x40, 64x48)和颜色(白色, 黄色, 蓝色, 黄色 + 蓝色). 硬件SPI接口: from machine import Pin, SPI import ssd1306 hspi = SPI(1) # sck=14 (scl), mosi=13 (sda), miso=12 (unused) dc = Pin(4) # data/command rst = Pin(5) # reset cs = Pin(15) # chip select, some modules do not have a pin for this display = ssd1306.SSD1306_SPI(128, 64, hspi, dc, rst, cs) 用软件通过GPIO来模拟产生SPI接口: from machine import Pin, SoftSPI import ssd1306 spi = SoftSPI(baudrate=500000, polarity=1, phase=0, sck=Pin(14), mosi=Pin(13), miso=Pin(12)) dc = Pin(4) # data/command rst = Pin(5) # reset cs = Pin(15) # chip select, some modules do not have a pin for this display = ssd1306.SSD1306_SPI(128, 64, spi, dc, rst, cs) I2C接口: from machine import Pin, I2C import ssd1306 # using default address 0x3C i2c = I2C(sda=Pin(4), scl=Pin(5)) display = ssd1306.SSD1306_I2C(128, 64, i2c) 在第一行打印Hello World: display.text('Hello, World!', 0, 0, 1) display.show() 基本功能: display.poweroff() # power off the display, pixels persist in memory display.poweron() # power on the display, pixels redrawn display.contrast(0) # dim display.contrast(255) # bright display.invert(1) # display inverted display.invert(0) # display normal display.rotate(True) # rotate 180 degrees display.rotate(False) # rotate 0 degrees display.show() # write the contents of the FrameBuffer to display memory 对FrameBuffer进行子类化提供了对图形原语的支持 display.fill(0) # fill entire screen with colour=0 display.pixel(0, 10) # get pixel at x=0, y=10 display.pixel(0, 10, 1) # set pixel at x=0, y=10 to colour=1 display.hline(0, 8, 4, 1) # draw horizontal line x=0, y=8, width=4, colour=1 display.vline(0, 8, 4, 1) # draw vertical line x=0, y=8, height=4, colour=1 display.line(0, 0, 127, 63, 1) # draw a line from 0,0 to 127,63 display.rect(10, 10, 107, 43, 1) # draw a rectangle outline 10,10 to 107,43, colour=1 display.fill_rect(10, 10, 107, 43, 1) # draw a solid rectangle 10,10 to 107,43, colour=1 display.text('Hello World', 0, 0, 1) # draw some text at x=0, y=0, colour=1 display.scroll(20, 0) # scroll 20 pixels to the right # draw another FrameBuffer on top of the current one at the given coordinates import framebuf fbuf = framebuf.FrameBuffer(bytearray(8 * 8 * 1), 8, 8, framebuf.MONO_VLSB) fbuf.line(0, 0, 7, 7, 1) display.blit(fbuf, 10, 10, 0) # draw on top at x=10, y=10, key=0 display.show() 显示MicroPython的Logo并打印一些字符: display.fill(0) display.fill_rect(0, 0, 32, 32, 1) display.fill_rect(2, 2, 28, 28, 0) display.vline(9, 8, 22, 1) display.vline(16, 2, 22, 1) display.vline(23, 8, 22, 1) display.fill_rect(26, 24, 2, 4, 1) display.text('MicroPython', 40, 0, 1) display.text('SSD1306', 40, 12, 1) display.text('OLED 128x64', 40, 24, 1) display.show() ### 5. MicroPython官方的驱动 [[https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py|MicroPython SSD1306 OLED driver, I2C and SPI interfaces]] # MicroPython SSD1306 OLED driver, I2C and SPI interfaces from micropython import const import framebuf # register definitions SET_CONTRAST = const(0x81) SET_ENTIRE_ON = const(0xA4) SET_NORM_INV = const(0xA6) SET_DISP = const(0xAE) SET_MEM_ADDR = const(0x20) SET_COL_ADDR = const(0x21) SET_PAGE_ADDR = const(0x22) SET_DISP_START_LINE = const(0x40) SET_SEG_REMAP = const(0xA0) SET_MUX_RATIO = const(0xA8) SET_IREF_SELECT = const(0xAD) SET_COM_OUT_DIR = const(0xC0) SET_DISP_OFFSET = const(0xD3) SET_COM_PIN_CFG = const(0xDA) SET_DISP_CLK_DIV = const(0xD5) SET_PRECHARGE = const(0xD9) SET_VCOM_DESEL = const(0xDB) SET_CHARGE_PUMP = const(0x8D) # Subclassing FrameBuffer provides support for graphics primitives # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html class SSD1306(framebuf.FrameBuffer): def __init__(self, width, height, external_vcc): self.width = width self.height = height self.external_vcc = external_vcc self.pages = self.height // 8 self.buffer = bytearray(self.pages * self.width) super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB) self.init_display() def init_display(self): for cmd in ( SET_DISP, # display off # address setting SET_MEM_ADDR, 0x00, # horizontal # resolution and layout SET_DISP_START_LINE, # start at line 0 SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0 SET_MUX_RATIO, self.height - 1, SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0 SET_DISP_OFFSET, 0x00, SET_COM_PIN_CFG, 0x02 if self.width > 2 * self.height else 0x12, # timing and driving scheme SET_DISP_CLK_DIV, 0x80, SET_PRECHARGE, 0x22 if self.external_vcc else 0xF1, SET_VCOM_DESEL, 0x30, # 0.83*Vcc # display SET_CONTRAST, 0xFF, # maximum SET_ENTIRE_ON, # output follows RAM contents SET_NORM_INV, # not inverted SET_IREF_SELECT, 0x30, # enable internal IREF during display on # charge pump SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14, SET_DISP | 0x01, # display on ): # on self.write_cmd(cmd) self.fill(0) self.show() def poweroff(self): self.write_cmd(SET_DISP) def poweron(self): self.write_cmd(SET_DISP | 0x01) def contrast(self, contrast): self.write_cmd(SET_CONTRAST) self.write_cmd(contrast) def invert(self, invert): self.write_cmd(SET_NORM_INV | (invert & 1)) def rotate(self, rotate): self.write_cmd(SET_COM_OUT_DIR | ((rotate & 1) << 3)) self.write_cmd(SET_SEG_REMAP | (rotate & 1)) def show(self): x0 = 0 x1 = self.width - 1 if self.width != 128: # narrow displays use centred columns col_offset = (128 - self.width) // 2 x0 += col_offset x1 += col_offset self.write_cmd(SET_COL_ADDR) self.write_cmd(x0) self.write_cmd(x1) self.write_cmd(SET_PAGE_ADDR) self.write_cmd(0) self.write_cmd(self.pages - 1) self.write_data(self.buffer) class SSD1306_I2C(SSD1306): def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False): self.i2c = i2c self.addr = addr self.temp = bytearray(2) self.write_list = [b"\x40", None] # Co=0, D/C#=1 super().__init__(width, height, external_vcc) def write_cmd(self, cmd): self.temp[0] = 0x80 # Co=1, D/C#=0 self.temp[1] = cmd self.i2c.writeto(self.addr, self.temp) def write_data(self, buf): self.write_list[1] = buf self.i2c.writevto(self.addr, self.write_list) class SSD1306_SPI(SSD1306): def __init__(self, width, height, spi, dc, res, cs, external_vcc=False): self.rate = 10 * 1024 * 1024 dc.init(dc.OUT, value=0) res.init(res.OUT, value=0) cs.init(cs.OUT, value=1) self.spi = spi self.dc = dc self.res = res self.cs = cs import time self.res(1) time.sleep_ms(1) self.res(0) time.sleep_ms(10) self.res(1) super().__init__(width, height, external_vcc) def write_cmd(self, cmd): self.spi.init(baudrate=self.rate, polarity=0, phase=0) self.cs(1) self.dc(0) self.cs(0) self.spi.write(bytearray([cmd])) self.cs(1) def write_data(self, buf): self.spi.init(baudrate=self.rate, polarity=0, phase=0) self.cs(1) self.dc(1) self.cs(0) self.spi.write(buf) self.cs(1) ### 6. 汉字的显示 * 支持英文字号8x8,16x16,24x24,32x32 * 支持中文字号16x16,24x24,32x32 * 封装lcd操作常用接口 #### API def init_i2c(scl, sda, width, height): """ 初始化i2c接口 :param scl: i2c的时钟脚 :param sda: i2c的数据脚 :param width: oled屏幕的宽度像素 :param height: oled屏幕的高度像素 """ def clear(): """清除屏幕""" def show(): """屏幕刷新显示""" def pixel(x, y): """画点""" def text(string, x_axis, y_axis, font_size): """显示字符串.注意字符串必须是英文或者数字""" def set_font(font, font_size): """ 设置中文字库.允许设置多个不同大小的字库 字库必须是字典,格式示例: font = { 0xe4bda0: [0x08, 0x08, 0x08, 0x11, 0x11, 0x32, 0x34, 0x50, 0x91, 0x11, 0x12, 0x12, 0x14, 0x10, 0x10, 0x10, 0x80, 0x80, 0x80, 0xFE, 0x02, 0x04, 0x20, 0x20, 0x28, 0x24, 0x24, 0x22, 0x22, 0x20, 0xA0, 0x40], # 你 0xe5a5bd: [0x10, 0x10, 0x10, 0x10, 0xFC, 0x24, 0x24, 0x25, 0x24, 0x48, 0x28, 0x10, 0x28, 0x44, 0x84, 0x00, 0x00, 0xFC, 0x04, 0x08, 0x10, 0x20, 0x20, 0xFE, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xA0, 0x40] # 好 } """ def text_cn(string, x_axis, y_axis, font_size): """显示中文字符.注意字符必须是utf-8编码""" #### 显示英文字符 import ssd1306py as lcd lcd.init_i2c(22, 21, 128, 64) lcd.text('font8x8', 0, 0, 8) lcd.text('font16x16', 0, 20, 16) lcd.show() import ssd1306py as lcd lcd.init_i2c(22, 21, 128, 64) lcd.text('font32x32', 0, 0, 32) lcd.show() #### 显示汉字 可以使用在线转换工具查询:[[http://www.mytju.com/classcode/tools/encode_utf8.asp|在线转换工具]] 比如以下示例,显示汉字“你好”。 import ssd1306py as lcd lcd.init_i2c(22, 21, 128, 64) font16 = { 0xe4bda0: [0x08, 0x08, 0x08, 0x11, 0x11, 0x32, 0x34, 0x50, 0x91, 0x11, 0x12, 0x12, 0x14, 0x10, 0x10, 0x10, 0x80, 0x80, 0x80, 0xFE, 0x02, 0x04, 0x20, 0x20, 0x28, 0x24, 0x24, 0x22, 0x22, 0x20, 0xA0, 0x40], # 你 0xe5a5bd: [0x10, 0x10, 0x10, 0x10, 0xFC, 0x24, 0x24, 0x25, 0x24, 0x48, 0x28, 0x10, 0x28, 0x44, 0x84, 0x00, 0x00, 0xFC, 0x04, 0x08, 0x10, 0x20, 0x20, 0xFE, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xA0, 0x40] # 好 } font24 = { 0xe4bda0: [0x00, 0x01, 0x01, 0x03, 0x03, 0x02, 0x04, 0x04, 0x0E, 0x1C, 0x14, 0x24, 0x44, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x04, 0x06, 0x04, 0x00, 0x00, 0x00, 0x8C, 0x0C, 0x08, 0x18, 0x1F, 0x30, 0x21, 0x41, 0x41, 0x91, 0x19, 0x11, 0x31, 0x21, 0x41, 0x41, 0x81, 0x01, 0x11, 0x0F, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x0C, 0x10, 0x00, 0x00, 0x00, 0x20, 0x10, 0x18, 0x0C, 0x0C, 0x06, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00], # 你 0xe5a5bd: [0x00, 0x00, 0x06, 0x06, 0x06, 0x04, 0x04, 0x7F, 0x0C, 0x0C, 0x08, 0x08, 0x08, 0x18, 0x10, 0x11, 0x0D, 0x03, 0x02, 0x04, 0x18, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x00, 0x00, 0xC0, 0x40, 0x40, 0xC0, 0x80, 0xBF, 0x80, 0x80, 0x00, 0x00, 0x80, 0xC0, 0x60, 0x00, 0x07, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x18, 0x20, 0x40, 0x80, 0x80, 0x80, 0x84, 0xFE, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00] # 好 } font32 = { 0xe4bda0: [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x03, 0x07, 0x0D, 0x09, 0x11, 0x11, 0x21, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x40, 0x70, 0x60, 0xE0, 0xC0, 0xC1, 0x81, 0x03, 0x03, 0x86, 0x84, 0x8C, 0x88, 0x90, 0x81, 0x83, 0x83, 0x83, 0x86, 0x86, 0x8C, 0x88, 0x90, 0x90, 0xA0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xE0, 0xC0, 0xC0, 0x80, 0x80, 0xFF, 0x00, 0x10, 0x0C, 0x08, 0x08, 0x08, 0x88, 0x88, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0xF8, 0x38, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xFC, 0x18, 0x30, 0x20, 0x40, 0x00, 0x00, 0x00, 0x80, 0x40, 0x20, 0x30, 0x18, 0x1C, 0x0C, 0x0C, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00], # 你 0xe5a5bd: [0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x3F, 0x03, 0x03, 0x02, 0x06, 0x06, 0x04, 0x04, 0x0C, 0x0C, 0x08, 0x08, 0x0E, 0x01, 0x00, 0x00, 0x01, 0x03, 0x04, 0x08, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC0, 0x80, 0x81, 0x80, 0x00, 0x08, 0xFC, 0x08, 0x08, 0x18, 0x18, 0x18, 0x18, 0x17, 0x30, 0x30, 0x30, 0x60, 0x60, 0xC0, 0xF0, 0xBC, 0x8C, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x01, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0xFF, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0xFC, 0x1C, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0xF0, 0x70, 0xC0, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00] # 好 } lcd.init_i2c(22, 21, 128, 64) lcd.set_font(font16, 16) lcd.set_font(font24, 24) lcd.set_font(font32, 32) lcd.text_cn('你好', 0, 0, 16) lcd.text_cn('你好', 40, 00, 24) lcd.text_cn('你好', 0, 30, 32) lcd.show() ### 7. 滚动屏幕的程序 [[https://randomnerdtutorials.com/micropython-ssd1306-oled-scroll-shapes-esp32-esp8266/|滚动屏幕]] oled_width = 128 oled_height = 64 oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c) screen1_row1 = "Screen 1, row 1" screen1_row2 = "Screen 1, row 2" screen1_row3 = "Screen 1, row 3" screen2_row1 = "Screen 2, row 1" screen2_row2 = "Screen 2, row 2" screen3_row1 = "Screen 3, row 1" screen1 = [[0, 0 , screen1_row1], [0, 16, screen1_row2], [0, 32, screen1_row3]] screen2 = [[0, 0 , screen2_row1], [0, 16, screen2_row2]] screen3 = [[0, 40 , screen3_row1]] # Scroll in screen horizontally from left to right def scroll_in_screen(screen): for i in range (0, oled_width+1, 4): for line in screen: oled.text(line[2], -oled_width+i, line[1]) oled.show() if i!= oled_width: oled.fill(0) # Scroll out screen horizontally from left to right def scroll_out_screen(speed): for i in range ((oled_width+1)/speed): for j in range (oled_height): oled.pixel(i, j, 0) oled.scroll(speed,0) oled.show() # Continuous horizontal scroll def scroll_screen_in_out(screen): for i in range (0, (oled_width+1)*2, 1): for line in screen: oled.text(line[2], -oled_width+i, line[1]) oled.show() if i!= oled_width: oled.fill(0) # Scroll in screen vertically def scroll_in_screen_v(screen): for i in range (0, (oled_height+1), 1): for line in screen: oled.text(line[2], line[0], -oled_height+i+line[1]) oled.show() if i!= oled_height: oled.fill(0) # Scroll out screen vertically def scroll_out_screen_v(speed): for i in range ((oled_height+1)/speed): for j in range (oled_width): oled.pixel(j, i, 0) oled.scroll(0,speed) oled.show() # Continous vertical scroll def scroll_screen_in_out_v(screen): for i in range (0, (oled_height*2+1), 1): for line in screen: oled.text(line[2], line[0], -oled_height+i+line[1]) oled.show() if i!= oled_height: oled.fill(0) while True: # Scroll in, stop, scroll out (horizontal) scroll_in_screen(screen1) sleep(2) scroll_out_screen(4) scroll_in_screen(screen2) sleep(2) scroll_out_screen(4) scroll_in_screen(screen3) sleep(2) scroll_out_screen(4) # Continuous horizontal scroll scroll_screen_in_out(screen1) scroll_screen_in_out(screen2) scroll_screen_in_out(screen3) # Scroll in, stop, scroll out (vertical) scroll_in_screen_v(screen1) sleep(2) scroll_out_screen_v(4) scroll_in_screen_v(screen2) sleep(2) scroll_out_screen_v(4) scroll_in_screen_v(screen3) sleep(2) scroll_out_screen_v(4) # Continuous verticall scroll scroll_screen_in_out_v(screen1) scroll_screen_in_out_v(screen2) scroll_screen_in_out_v(screen3) ### 8. 绘图 使用[[https://github.com/adafruit/micropython-adafruit-gfx/blob/master/gfx.py|Adafruit GFX Arduino library to MicroPython]] # Port of Adafruit GFX Arduino library to MicroPython. # Based on: https://github.com/adafruit/Adafruit-GFX-Library # Author: Tony DiCola (original GFX author Phil Burgess) # License: MIT License (https://opensource.org/licenses/MIT) class GFX: def __init__(self, width, height, pixel, hline=None, vline=None): # Create an instance of the GFX drawing class. You must pass in the # following parameters: # - width = The width of the drawing area in pixels. # - height = The height of the drawing area in pixels. # - pixel = A function to call when a pixel is drawn on the display. # This function should take at least an x and y position # and then any number of optional color or other parameters. # You can also provide the following optional keyword argument to # improve the performance of drawing: # - hline = A function to quickly draw a horizontal line on the display. # This should take at least an x, y, and width parameter and # any number of optional color or other parameters. # - vline = A function to quickly draw a vertical line on the display. # This should take at least an x, y, and height paraemter and # any number of optional color or other parameters. self.width = width self.height = height self._pixel = pixel # Default to slow horizontal & vertical line implementations if no # faster versions are provided. if hline is None: self.hline = self._slow_hline else: self.hline = hline if vline is None: self.vline = self._slow_vline else: self.vline = vline def _slow_hline(self, x0, y0, width, *args, **kwargs): # Slow implementation of a horizontal line using pixel drawing. # This is used as the default horizontal line if no faster override # is provided. if y0 < 0 or y0 > self.height or x0 < -width or x0 > self.width: return for i in range(width): self._pixel(x0+i, y0, *args, **kwargs) def _slow_vline(self, x0, y0, height, *args, **kwargs): # Slow implementation of a vertical line using pixel drawing. # This is used as the default vertical line if no faster override # is provided. if y0 < -height or y0 > self.height or x0 < 0 or x0 > self.width: return for i in range(height): self._pixel(x0, y0+i, *args, **kwargs) def rect(self, x0, y0, width, height, *args, **kwargs): # Rectangle drawing function. Will draw a single pixel wide rectangle # starting in the upper left x0, y0 position and width, height pixels in # size. if y0 < -height or y0 > self.height or x0 < -width or x0 > self.width: return self.hline(x0, y0, width, *args, **kwargs) self.hline(x0, y0+height-1, width, *args, **kwargs) self.vline(x0, y0, height, *args, **kwargs) self.vline(x0+width-1, y0, height, *args, **kwargs) def fill_rect(self, x0, y0, width, height, *args, **kwargs): # Filled rectangle drawing function. Will draw a single pixel wide # rectangle starting in the upper left x0, y0 position and width, height # pixels in size. if y0 < -height or y0 > self.height or x0 < -width or x0 > self.width: return for i in range(x0, x0+width): self.vline(i, y0, height, *args, **kwargs) def line(self, x0, y0, x1, y1, *args, **kwargs): # Line drawing function. Will draw a single pixel wide line starting at # x0, y0 and ending at x1, y1. 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 = x1 - x0 dy = abs(y1 - y0) err = dx // 2 ystep = 0 if y0 < y1: ystep = 1 else: ystep = -1 while x0 <= x1: if steep: self._pixel(y0, x0, *args, **kwargs) else: self._pixel(x0, y0, *args, **kwargs) err -= dy if err < 0: y0 += ystep err += dx x0 += 1 def circle(self, x0, y0, radius, *args, **kwargs): # Circle drawing function. Will draw a single pixel wide circle with # center at x0, y0 and the specified radius. f = 1 - radius ddF_x = 1 ddF_y = -2 * radius x = 0 y = radius self._pixel(x0, y0 + radius, *args, **kwargs) self._pixel(x0, y0 - radius, *args, **kwargs) self._pixel(x0 + radius, y0, *args, **kwargs) self._pixel(x0 - radius, y0, *args, **kwargs) while x < y: if f >= 0: y -= 1 ddF_y += 2 f += ddF_y x += 1 ddF_x += 2 f += ddF_x self._pixel(x0 + x, y0 + y, *args, **kwargs) self._pixel(x0 - x, y0 + y, *args, **kwargs) self._pixel(x0 + x, y0 - y, *args, **kwargs) self._pixel(x0 - x, y0 - y, *args, **kwargs) self._pixel(x0 + y, y0 + x, *args, **kwargs) self._pixel(x0 - y, y0 + x, *args, **kwargs) self._pixel(x0 + y, y0 - x, *args, **kwargs) self._pixel(x0 - y, y0 - x, *args, **kwargs) def fill_circle(self, x0, y0, radius, *args, **kwargs): # Filled circle drawing function. Will draw a filled circule with # center at x0, y0 and the specified radius. self.vline(x0, y0 - radius, 2*radius + 1, *args, **kwargs) f = 1 - radius ddF_x = 1 ddF_y = -2 * radius x = 0 y = radius while x < y: if f >= 0: y -= 1 ddF_y += 2 f += ddF_y x += 1 ddF_x += 2 f += ddF_x self.vline(x0 + x, y0 - y, 2*y + 1, *args, **kwargs) self.vline(x0 + y, y0 - x, 2*x + 1, *args, **kwargs) self.vline(x0 - x, y0 - y, 2*y + 1, *args, **kwargs) self.vline(x0 - y, y0 - x, 2*x + 1, *args, **kwargs) def triangle(self, x0, y0, x1, y1, x2, y2, *args, **kwargs): # Triangle drawing function. Will draw a single pixel wide triangle # around the points (x0, y0), (x1, y1), and (x2, y2). self.line(x0, y0, x1, y1, *args, **kwargs) self.line(x1, y1, x2, y2, *args, **kwargs) self.line(x2, y2, x0, y0, *args, **kwargs) def fill_triangle(self, x0, y0, x1, y1, x2, y2, *args, **kwargs): # Filled triangle drawing function. Will draw a filled triangle around # the points (x0, y0), (x1, y1), and (x2, y2). if y0 > y1: y0, y1 = y1, y0 x0, x1 = x1, x0 if y1 > y2: y2, y1 = y1, y2 x2, x1 = x1, x2 if y0 > y1: y0, y1 = y1, y0 x0, x1 = x1, x0 a = 0 b = 0 y = 0 last = 0 if y0 == y2: a = x0 b = x0 if x1 < a: a = x1 elif x1 > b: b = x1 if x2 < a: a = x2 elif x2 > b: b = x2 self.hline(a, y0, b-a+1, *args, **kwargs) return dx01 = x1 - x0 dy01 = y1 - y0 dx02 = x2 - x0 dy02 = y2 - y0 dx12 = x2 - x1 dy12 = y2 - y1 if dy01 == 0: dy01 = 1 if dy02 == 0: dy02 = 1 if dy12 == 0: dy12 = 1 sa = 0 sb = 0 if y1 == y2: last = y1 else: last = y1-1 for y in range(y0, last+1): a = x0 + sa // dy01 b = x0 + sb // dy02 sa += dx01 sb += dx02 if a > b: a, b = b, a self.hline(a, y, b-a+1, *args, **kwargs) sa = dx12 * (y - y1) sb = dx02 * (y - y0) while y <= y2: a = x1 + sa // dy12 b = x0 + sb // dy02 sa += dx12 sb += dx02 if a > b: a, b = b, a self.hline(a, y, b-a+1, *args, **kwargs) y += 1 绘图示例: oled_width = 128 oled_height = 64 oled = ssd1306.SSD1306_I2C(oled_width, oled_height, i2c) graphics = gfx.GFX(oled_width, oled_height, oled.pixel) while True: graphics.line(0, 0, 127, 20, 1) oled.show() sleep(1) oled.fill(0) graphics.rect(10, 10, 50, 30, 1) oled.show() sleep(1) oled.fill(0) graphics.fill_rect(10, 10, 50, 30, 1) oled.show() sleep(1) oled.fill(0) graphics.circle(64, 32, 10, 1) oled.show() sleep(1) oled.fill(0) graphics.fill_circle(64, 32, 10, 1) oled.show() sleep(1) oled.fill(0) graphics.triangle(10,10,55,20,5,40,1) oled.show() sleep(1) oled.fill(0) graphics.fill_triangle(10,10,55,20,5,40,1) oled.show() sleep(1) oled.fill(0)