# 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(20)
scl=machine.Pin(21)
 
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 “就会继续。

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

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)

Using a SSD1306 OLED display

SSD1306 OLED显示可以使用SPI或I2C接口,支持不同分辨率(128×64, 128×32, 72×40, 64×48)和颜色(白色, 黄色, 蓝色, 黄色 + 蓝色).

硬件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()

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)