树莓派 Pico 上使用 SSD1306 OLED 屏幕

SSD1306 OLED 屏幕模块配备 0.9 英寸迷你单色屏,128×32 分辨率,白光像素点。拥有极高的对比度,显示清晰极易辨识。OLED 仅使用树莓派的 I2C 和电源引脚,无需独立供电,即插即用。

接线

屏幕模块的 VCC 接树莓派 Pico 的 3V3 引脚
屏幕模块的 GND 接树莓派 Pico 的 GND 引脚
屏幕模块的 SDA 引脚接树莓派 Pico 的 GP14 引脚
屏幕模块的 SCL 引脚接树莓派 Pico 的 SCL 引脚

代码

这里使用开源的 MicroPython ssd1306 库。将下面的代码保存在 Pico 上,命名为 ssd1306.py。

# 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)

该库使用了 MicroPython 的 framebuf 库。关于 framebuf 库的用法可以参考文档,也可以通过这篇文章快速了解一下「MicroPython 帧缓冲区库 framebuf 的使用方法」。

下面是示例代码,在屏幕上输出指定的信息,将代码保存在 Pico 上,命名为 main.py。

import machine
import ssd1306

i2c = machine.I2C(1, sda=machine.Pin(14), scl=machine.Pin(15), freq=400_000)
print("I2C device: " + str(i2c.scan()[0]))
oled = ssd1306.SSD1306_I2C(128, 64, i2c)

oled.text("PICO LAB", 0, 0)
oled.hline(0, 10, 128, 1)
oled.text("Hello World!", 0, 26)
oled.hline(0, 48, 128, 1)
oled.text("PICO.NXEZ.COM", 0, 52)
oled.show()

显示效果如图。

下面的示例代码来画一个树莓派 Logo 并显示在屏幕上。将代码保存在 Pico 上,命名为 main.py。

import ssd1306
import framebuf

i2c = machine.I2C(1, sda=machine.Pin(14), scl=machine.Pin(15), freq=400_000)
oled = ssd1306.SSD1306_I2C(128, 64, i2c)

data = [0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x3E,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xE0,0x07,0x00,0x00,0x00,0x7F,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3F,0x80,0x03,0x80,0x00,0x01,0xF7,0xC0,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x01,0xFF,0x80,0x01,0xF8,0x00,0x0F,0x90,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xFF,0xE0,0x01,0xFF,0x00,0x0E,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x01,0xF8,0x01,0xFF,0xC0,0x3C,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x3C,0x00,0xFF,0xFF,0xE1,0xE0,0x70,0x00,0x60,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x38,0x00,0xFF,0xFF,0xE0,0x70,0x70,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x00,0xFF,0xFF,0xF0,0x39,0xC0,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0xFF,0xDF,0xF0,0x1D,0xC0,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x01,0xFE,0x01,0xF8,0x0F,0x80,0x00,0x30,0x00,0x00,0x00,
		0x00,0x00,0x00,0x01,0xC0,0x01,0xF8,0x00,0x7C,0x0F,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xC0,0x03,0xF8,0x00,0x3E,0x0F,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xC0,0x07,0xF0,0x00,0x1F,0x06,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xC0,0x0F,0xE0,0x00,0x1F,0x86,0x00,0x20,0x30,0x00,0x00,0x00,
		0x00,0x00,0x00,0x07,0xC0,0x3F,0xE0,0x00,0x0F,0x86,0x00,0x20,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xC0,0x7F,0xE0,0x00,0x0F,0xC6,0x00,0x40,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xE0,0x00,0x0F,0xF7,0x00,0xC0,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xFF,0xE0,0x00,0x0F,0xFF,0x00,0x80,0x60,0x00,0x00,0x00,
		0x00,0x00,0x00,0x3F,0xFF,0xFF,0xF0,0x00,0x0F,0xFF,0x01,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x39,0xFF,0x80,0xF0,0x00,0x0F,0x9F,0x83,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xFE,0x00,0x78,0x00,0x0F,0x0F,0xC6,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFE,0x00,0x3C,0x00,0x0E,0x07,0xFC,0x00,0xC0,0x00,0x00,0x00,
		0x00,0x00,0x00,0xE0,0x7C,0x00,0x1E,0x00,0x1E,0x03,0xFC,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x78,0x00,0x1F,0x00,0x3C,0x03,0xFC,0x03,0x80,0x00,0x00,0x00,0x00,0x00,0x01,0xC0,0x78,0x00,0x0F,0xE0,0xFC,0x03,0xFE,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xC0,0x78,0x00,0x0F,0xFF,0xFC,0x01,0xFF,0xFC,0x00,0x00,0x00,0x00,
		0x00,0x00,0x01,0xC0,0x78,0x00,0x0F,0xFF,0xFC,0x01,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xC0,0x78,0x00,0x0F,0xFF,0xFC,0x03,0xFF,0xF0,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xC0,0x78,0x00,0x0F,0xFF,0xFC,0x03,0xFF,0xFC,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x78,0x00,0x0F,0xE0,0xFC,0x03,0xFE,0x07,0x00,0x00,0x00,0x00,
		0x00,0x00,0x00,0xC0,0x78,0x00,0x0F,0x00,0x3C,0x03,0xFC,0x03,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7C,0x00,0x1E,0x00,0x1E,0x07,0xF8,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0x7E,0x00,0x3C,0x00,0x0E,0x07,0xFC,0x00,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x70,0xFE,0x00,0x38,0x00,0x0F,0x0F,0xC6,0x00,0xE0,0x00,0x00,0x00,
		0x00,0x00,0x00,0x38,0xFF,0x80,0xF8,0x00,0x07,0x9F,0x83,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xFF,0xE3,0xF0,0x00,0x07,0xFF,0x01,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xFF,0xFF,0xF0,0x00,0x07,0xF7,0x00,0x80,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xE0,0xFF,0xF0,0x00,0x07,0xE7,0x00,0xC0,0x30,0x00,0x00,0x00,
		0x00,0x00,0x00,0x07,0xC0,0x3F,0xF0,0x00,0x0F,0x86,0x00,0x40,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0xC0,0x0F,0xF0,0x00,0x0F,0x86,0x00,0x20,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xC0,0x07,0xF0,0x00,0x1F,0x0E,0x00,0x20,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x03,0xC0,0x03,0xF0,0x00,0x3E,0x0E,0x00,0x10,0x30,0x00,0x00,0x00,
		0x00,0x00,0x00,0x01,0xC0,0x01,0xF8,0x00,0x7C,0x4F,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x00,0xFC,0x00,0xF8,0x1F,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0xFF,0x03,0xF8,0x1D,0x80,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x7F,0xFF,0xF0,0x39,0xC0,0x00,0x30,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x70,0x00,0x7F,0xFF,0xE0,0xF1,0xC0,0x00,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x38,0x00,0x7F,0xFF,0xE1,0xE0,0x70,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1C,0x00,0x7F,0x00,0xFF,0xC0,0x70,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x01,0xF0,0x00,0xFF,0x00,0x3C,0x00,0x60,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x07,0xFF,0xC0,0x01,0xF8,0x00,0x0E,0x00,0xE0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0xFF,0x80,0x03,0xC0,0x00,0x07,0x90,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0xE0,0x07,0x80,0x00,0x01,0xF7,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x00,0x7F,0xC0,0x00,0x00,0x00,
		0x00,0x00,0x00,0x00,0x00,0x00,0x7F,0xFC,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]

buf = framebuf.FrameBuffer(bytearray(data), 128, 64, framebuf.MONO_HLSB)
oled.blit(buf, 0, 0)
oled.show()

运行效果如图。

你还可以:
查看系列教程中的其他文章
购买本教程所用到的 Pico 套件



1 评论

  1. 这个例程 在PICO 上不能正常运行 做了修改后可以显示代码如下:

    from machine import I2C
    from ssd1306 import SSD1306_I2C
    import utime
    machine.freq(280000000) #set cpu croe freq

    #——I2C Pin ——
    #machine.I2C(1,freq = 10)
    i2c = machine.I2C(1,sda=machine.Pin(2), scl=machine.Pin(3)) #100K
    oled = SSD1306_I2C(128, 64, i2c )# 创建OLED显示屏的实例,128×64分辨率

    temperature = 27 – ((machine.ADC(4).read_u16() * (3.3/65535) – 0.706)/0.001721)
    print(“ID_I2C device: ” + str(i2c.scan()[0]))
    print(“CPU Temperautre :”,temperature,” C”)
    print(“CPU Frequency: {:.2f} MHz”.format(machine.freq()/1000000))
    print(“—————————“)

    oled.text(“PICO LAB”, 0, 0)
    oled.hline(0, 10, 128, 1)
    oled.text(“Hello World!”, 0, 26)
    oled.hline(0, 48, 128, 1)
    oled.text(“PICO.NXEZ.COM”, 0, 52)
    oled.show()

发表评论

你的邮件地址不会公开


*