Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add page addressing mode #57

Merged
merged 6 commits into from
Feb 23, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 67 additions & 24 deletions adafruit_ssd1306.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,33 @@ class _SSD1306(framebuf.FrameBuffer):
"""Base class for SSD1306 display driver"""

# pylint: disable-msg=too-many-arguments
def __init__(self, buffer, width, height, *, external_vcc, reset):
def __init__(self, buffer, width, height, *, external_vcc, reset, page_addressing):
tannewt marked this conversation as resolved.
Show resolved Hide resolved
super().__init__(buffer, width, height)
self.width = width
self.height = height
self.external_vcc = external_vcc
# reset may be None if not needed
self.reset_pin = reset
self.page_addressing = page_addressing
if self.reset_pin:
self.reset_pin.switch_to_output(value=0)
self.pages = self.height // 8
# Note the subclass must initialize self.framebuf to a framebuffer.
# This is necessary because the underlying data buffer is different
# between I2C and SPI implementations (I2C needs an extra byte).
self._power = False
# Parameters for efficient Page Addressing Mode (typical of U8Glib libraries)
# Important as not all screens appear to support Horizontal Addressing Mode
if self.page_addressing:
self.pagebuffer = bytearray(width + 1)
self.pagebuffer[0] = 0x40 # Set first byte of data buffer to Co=0, D/C=1
self.page_column_start = bytearray(2)
self.page_column_start[0] = self.width % 32
self.page_column_start[1] = 0x10 + self.width // 32
else:
self.pagebuffer = None
self.page_column_start = None
# Let's get moving!
self.poweron()
self.init_display()

Expand All @@ -86,7 +99,9 @@ def init_display(self):
SET_DISP | 0x00, # off
# address setting
SET_MEM_ADDR,
0x00, # horizontal
0x10 # Page Addressing Mode
if self.page_addressing
else 0x00, # Horizontal Addressing Mode
# resolution and layout
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
Expand All @@ -105,7 +120,7 @@ def init_display(self):
SET_PRECHARGE,
0x22 if self.external_vcc else 0xF1,
SET_VCOM_DESEL,
0x30, # 0.83*Vcc
0x30, # 0.83*Vcc # n.b. specs for ssd1306 64x32 oled screens imply this should be 0x40
# display
SET_CONTRAST,
0xFF, # maximum
Expand Down Expand Up @@ -159,22 +174,23 @@ def poweron(self):

def show(self):
"""Update the display"""
xpos0 = 0
xpos1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
xpos0 += 32
xpos1 += 32
if self.width == 72:
# displays with width of 72 pixels are shifted by 28
xpos0 += 28
xpos1 += 28
self.write_cmd(SET_COL_ADDR)
self.write_cmd(xpos0)
self.write_cmd(xpos1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
if not self.page_addressing:
xpos0 = 0
xpos1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
xpos0 += 32
xpos1 += 32
if self.width == 72:
# displays with width of 72 pixels are shifted by 28
xpos0 += 28
xpos1 += 28
self.write_cmd(SET_COL_ADDR)
self.write_cmd(xpos0)
self.write_cmd(xpos1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_framebuf()


Expand All @@ -191,10 +207,19 @@ class SSD1306_I2C(_SSD1306):
"""

def __init__(
self, width, height, i2c, *, addr=0x3C, external_vcc=False, reset=None
self,
width,
height,
i2c,
*,
addr=0x3C,
external_vcc=False,
reset=None,
page_addressing=False
):
self.i2c_device = i2c_device.I2CDevice(i2c, addr)
self.addr = addr
self.page_addressing = page_addressing
self.temp = bytearray(2)
# Add an extra byte to the data buffer to hold an I2C data/command byte
# to use hardware-compatible I2C transactions. A memoryview of the
Expand All @@ -209,10 +234,11 @@ def __init__(
height,
external_vcc=external_vcc,
reset=reset,
page_addressing=self.page_addressing,
)

def write_cmd(self, cmd):
"""Send a command to the SPI device"""
"""Send a command to the I2C device"""
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
with self.i2c_device:
Expand All @@ -221,8 +247,18 @@ def write_cmd(self, cmd):
def write_framebuf(self):
"""Blast out the frame buffer using a single I2C transaction to support
hardware I2C interfaces."""
with self.i2c_device:
self.i2c_device.write(self.buffer)
if self.page_addressing:
for page in range(self.pages):
self.write_cmd(0xB0 + page)
self.write_cmd(self.page_column_start[0])
self.write_cmd(self.page_column_start[1])
self.pagebuffer[1:] = self.buffer[
1 + self.width * page : 1 + self.width * (page + 1)
]
self.i2c_device.write(self.pagebuffer)
else:
with self.i2c_device:
self.i2c_device.write(self.buffer)


# pylint: disable-msg=too-many-arguments
Expand Down Expand Up @@ -252,8 +288,14 @@ def __init__(
external_vcc=False,
baudrate=8000000,
polarity=0,
phase=0
phase=0,
page_addressing=False
):
if page_addressing:
raise NotImplementedError(
"Page addressing mode with SPI has not yet been implemented."
)

self.rate = 10 * 1024 * 1024
dc.switch_to_output(value=0)
self.spi_device = spi_device.SPIDevice(
Expand All @@ -267,6 +309,7 @@ def __init__(
height,
external_vcc=external_vcc,
reset=reset,
page_addressing=self.page_addressing,
)

def write_cmd(self, cmd):
Expand Down