差别
这里会显示出您选择的修订版和当前版本之间的差别。
两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
mp_oled [2021/09/30 11:31] gongyusu [Adafruit关于SSD1306的使用介绍] |
mp_oled [2023/08/01 10:49] (当前版本) group003 |
||
---|---|---|---|
行 1: | 行 1: | ||
## 驱动SSD1306显示屏 | ## 驱动SSD1306显示屏 | ||
- | |||
- | 添加显示屏 | ||
- | |||
- | 我们将进行的下一个实验是将一个OLED显示器连接到我们的Pico上,当然,也可以在上面打印一些东西。 | ||
- | |||
- | 我们将使用 I2C 显示屏,因此我们也将看到 Pico 如何使用 I2C 连接工作。记住,Pico有两条I2C总线。 | ||
- | |||
- | 我们的OLED是标准的1602型OLED显示器,到处都有。如果你愿意,也可以使用与我的显示屏尺寸不同的显示屏,只需在代码中更改尺寸即可。 | ||
- | |||
- | 这是我们如何把这些东西都挂起来的,只有四根线。 | ||
- | 我们的显示器需要一个库,我们可以使用Thonny ID安装。你可能会发现在全菜单模式下比在基本模式下更容易,但这两种方式都可以。 | ||
- | |||
- | 点击 "工具 "菜单 | ||
- | 点击 "管理包" | ||
- | 搜索 "ssd1306" | ||
- | 找到 "ssd1306.py "并安装它。 | ||
- | 现在我们已经安装了库,我们可以看一下演示OLED显示屏的脚本。 | ||
### 1. I2C屏幕 | ### 1. I2C屏幕 | ||
<code python> | <code python> | ||
- | # 树莓派 Pico OLED Display Test | + | # Raspberry Pi Pico OLED Display Test |
# Uses ssd1306 module | # Uses ssd1306 module | ||
# display-ssd1306-test.py | # display-ssd1306-test.py | ||
- | # DroneBot Workshop 2021 | + | # EETree Sci & Tech 2021 |
- | # https://dronebotworkshop.com | + | # https://www.eetree.cn |
import machine | import machine | ||
import utime | import utime | ||
- | sda=machine.Pin(20) | + | sda=machine.Pin(11) |
- | scl=machine.Pin(21) | + | scl=machine.Pin(10) |
i2c=machine.I2C(0, sda=sda, scl=scl, freq=400000) | i2c=machine.I2C(0, sda=sda, scl=scl, freq=400000) | ||
行 62: | 行 45: | ||
</code> | </code> | ||
- | 我们的 OLED 显示屏是一个 I2C 设备,所以你会注意到,在脚本的开头,我们将两个 GPIO 引脚定义为 SDA (GPIO 20) 和 SCL (GPIO 21)。 | + | 这段代码是基于I2C接口协议的,将两个GPIO引脚定义为SDA(GPIO20)和SCL(GPIO21)。 |
- | Pico有两条I2C总线,你可以使用几种不同的GPIO引脚来连接它们。但它们并不是随便的引脚,例如某些引脚被指定为总线0的SDA,只有它们才能用于SDA总线0。 | + | Pico有两组I2C总线,你可以使用几种不同的GPIO引脚来连接它们。但它们并不是随便的引脚,例如某些引脚被指定为总线0的SDA,只有它们才能用于SDA总线0。 |
然后我们使用机器库的I2C函数定义一个I2C连接。我们需要给它提供以下参数。 | 然后我们使用机器库的I2C函数定义一个I2C连接。我们需要给它提供以下参数。 | ||
行 124: | 行 107: | ||
</code> | </code> | ||
- | ----------------------------------------- | + | |
- | 14. 使用SSD1306 OLED显示 | + | ### 3. Adafruit关于SSD1306的使用介绍 |
+ | |||
+ | #### I2C初始化 | ||
+ | <code python> | ||
+ | import machine | ||
+ | import ssd1306 | ||
+ | i2c = machine.I2C(-1, machine.Pin(5), machine.Pin(4)) | ||
+ | oled = ssd1306.SSD1306_I2C(128, 32, i2c) | ||
+ | </code> | ||
+ | |||
+ | #### SPI初始化 | ||
+ | |||
+ | <code python> | ||
+ | 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) | ||
+ | </code> | ||
+ | |||
+ | ### 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)和颜色(白色, 黄色, 蓝色, 黄色 + 蓝色). | SSD1306 OLED显示可以使用SPI或I2C接口,支持不同分辨率(128x64, 128x32, 72x40, 64x48)和颜色(白色, 黄色, 蓝色, 黄色 + 蓝色). | ||
行 133: | 行 137: | ||
import ssd1306 | import ssd1306 | ||
- | hspi = SPI(1) # sck=14 (scl), mosi=13 (sda), miso=12 (unused) | + | hspi = SPI(1) # sck=14 (scl), mosi=13 (sda), miso=12 (unused) |
dc = Pin(4) # data/command | dc = Pin(4) # data/command | ||
行 142: | 行 146: | ||
</code> | </code> | ||
- | 软件SPI接口: | + | 用软件通过GPIO来模拟产生SPI接口: |
<code python> | <code python> | ||
from machine import Pin, SoftSPI | from machine import Pin, SoftSPI | ||
行 157: | 行 161: | ||
I2C接口: | I2C接口: | ||
- | |||
<code python> | <code python> | ||
from machine import Pin, I2C | from machine import Pin, I2C | ||
行 165: | 行 168: | ||
i2c = I2C(sda=Pin(4), scl=Pin(5)) | i2c = I2C(sda=Pin(4), scl=Pin(5)) | ||
display = ssd1306.SSD1306_I2C(128, 64, i2c) | display = ssd1306.SSD1306_I2C(128, 64, i2c) | ||
- | </code? | + | </code> |
在第一行打印Hello World: | 在第一行打印Hello World: | ||
行 189: | 行 192: | ||
<code python> | <code python> | ||
- | |||
display.fill(0) # fill entire screen with colour=0 | display.fill(0) # fill entire screen with colour=0 | ||
display.pixel(0, 10) # get pixel at x=0, y=10 | display.pixel(0, 10) # get pixel at x=0, y=10 | ||
行 200: | 行 202: | ||
display.text('Hello World', 0, 0, 1) # draw some text at x=0, y=0, 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 | display.scroll(20, 0) # scroll 20 pixels to the right | ||
- | |||
# draw another FrameBuffer on top of the current one at the given coordinates | # draw another FrameBuffer on top of the current one at the given coordinates | ||
行 226: | 行 227: | ||
- | ### Adafruit关于SSD1306的使用介绍 | + | ### 5. MicroPython官方的驱动 |
+ | [[https://github.com/micropython/micropython/blob/master/drivers/display/ssd1306.py|MicroPython SSD1306 OLED driver, I2C and SPI interfaces]] | ||
+ | <code python> | ||
+ | # MicroPython SSD1306 OLED driver, I2C and SPI interfaces | ||
- | #### I2C初始化 | + | from micropython import const |
- | If your display is connected to the board using I2C (like if using a Feather and the FeatherWing OLED) you'll first need to initialize the I2C bus. On MicroPython.org firmware which uses the machine API you can initialize I2C like the MicroPython I2C guide mentions. | + | import framebuf |
- | For example on a board like the ESP8266 you can run (assuming you're using the default SDA gpio #4 and SCL gpio #5 pins like on a Feather & SSD1306 FeatherWing): | ||
+ | # 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) | ||
+ | </code> | ||
+ | |||
+ | ### 6. 汉字的显示 | ||
+ | * 支持英文字号8x8,16x16,24x24,32x32 | ||
+ | * 支持中文字号16x16,24x24,32x32 | ||
+ | * 封装lcd操作常用接口 | ||
+ | |||
+ | #### API | ||
<code python> | <code python> | ||
- | import machine | + | def init_i2c(scl, sda, width, height): |
- | import ssd1306 | + | """ |
- | i2c = machine.I2C(-1, machine.Pin(5), machine.Pin(4)) | + | 初始化i2c接口 |
- | oled = ssd1306.SSD1306_I2C(128, 32, 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编码""" | ||
</code> | </code> | ||
- | Note that the first two parameters to the SSD1306_I2C class initializer are the width and height of the display in pixels. Be sure to use the right values for the display you're using! | + | #### 显示英文字符 |
+ | <code python> | ||
+ | import ssd1306py as lcd | ||
- | #### SPI初始化 | + | |
- | If your display is connected to the board using SPI you'll first need to initialize the SPI bus. On MicroPython.org firmware which uses the machine API you can initialize SPI like the MicroPython SPI guide mentions: | + | lcd.init_i2c(22, 21, 128, 64) |
+ | lcd.text('font8x8', 0, 0, 8) | ||
+ | lcd.text('font16x16', 0, 20, 16) | ||
+ | lcd.show() | ||
+ | |||
+ | </code> | ||
<code python> | <code python> | ||
- | import machine | + | import ssd1306py as lcd |
- | 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)) | + | lcd.init_i2c(22, 21, 128, 64) |
+ | lcd.text('font32x32', 0, 0, 32) | ||
+ | lcd.show() | ||
</code> | </code> | ||
- | Note the first two parameters to the SSD1306_SPI class initializer are the width and height of the display in pixels. Be sure to use the right values for the display you're using! | + | #### 显示汉字 |
+ | 可以使用在线转换工具查询:[[http://www.mytju.com/classcode/tools/encode_utf8.asp|在线转换工具]] | ||
- | The next parameters to the initializer are the pins connected to the display's DC, reset, and CS lines in that order. | + | 比如以下示例,显示汉字“你好”。 |
+ | <code python> | ||
+ | import ssd1306py as lcd | ||
- | #### Drawing | + | lcd.init_i2c(22, 21, 128, 64) |
- | Once the display is initialized using the code above you're ready to start drawing on it. Follow the drawing section on the CircuitPython page for all the details on pixel, fill, and other drawing functions. The usage of the library between CircuitPython and MicroPython is exactly the same! | + | |
+ | 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() | ||
+ | |||
+ | </code> | ||
+ | |||
+ | ### 7. 滚动屏幕的程序 | ||
+ | [[https://randomnerdtutorials.com/micropython-ssd1306-oled-scroll-shapes-esp32-esp8266/|滚动屏幕]] | ||
+ | <code python> | ||
+ | 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) | ||
+ | </code> | ||
+ | |||
+ | ### 8. 绘图 | ||
+ | 使用[[https://github.com/adafruit/micropython-adafruit-gfx/blob/master/gfx.py|Adafruit GFX Arduino library to MicroPython]] | ||
+ | |||
+ | <code python> | ||
+ | # 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 | ||
+ | </code> | ||
+ | |||
+ | |||
+ | 绘图示例: | ||
+ | <code python> | ||
+ | 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) | ||
+ | </code> |