@@ -48,20 +48,33 @@ class _SSD1306(framebuf.FrameBuffer):
4848 """Base class for SSD1306 display driver"""
4949
5050 # pylint: disable-msg=too-many-arguments
51- def __init__ (self , buffer , width , height , * , external_vcc , reset ):
51+ def __init__ (self , buffer , width , height , * , external_vcc , reset , page_addressing ):
5252 super ().__init__ (buffer , width , height )
5353 self .width = width
5454 self .height = height
5555 self .external_vcc = external_vcc
5656 # reset may be None if not needed
5757 self .reset_pin = reset
58+ self .page_addressing = page_addressing
5859 if self .reset_pin :
5960 self .reset_pin .switch_to_output (value = 0 )
6061 self .pages = self .height // 8
6162 # Note the subclass must initialize self.framebuf to a framebuffer.
6263 # This is necessary because the underlying data buffer is different
6364 # between I2C and SPI implementations (I2C needs an extra byte).
6465 self ._power = False
66+ # Parameters for efficient Page Addressing Mode (typical of U8Glib libraries)
67+ # Important as not all screens appear to support Horizontal Addressing Mode
68+ if self .page_addressing :
69+ self .pagebuffer = bytearray (width + 1 )
70+ self .pagebuffer [0 ] = 0x40 # Set first byte of data buffer to Co=0, D/C=1
71+ self .page_column_start = bytearray (2 )
72+ self .page_column_start [0 ] = self .width % 32
73+ self .page_column_start [1 ] = 0x10 + self .width // 32
74+ else :
75+ self .pagebuffer = None
76+ self .page_column_start = None
77+ # Let's get moving!
6578 self .poweron ()
6679 self .init_display ()
6780
@@ -86,7 +99,9 @@ def init_display(self):
8699 SET_DISP | 0x00 , # off
87100 # address setting
88101 SET_MEM_ADDR ,
89- 0x00 , # horizontal
102+ 0x10 # Page Addressing Mode
103+ if self .page_addressing
104+ else 0x00 , # Horizontal Addressing Mode
90105 # resolution and layout
91106 SET_DISP_START_LINE | 0x00 ,
92107 SET_SEG_REMAP | 0x01 , # column addr 127 mapped to SEG0
@@ -105,7 +120,7 @@ def init_display(self):
105120 SET_PRECHARGE ,
106121 0x22 if self .external_vcc else 0xF1 ,
107122 SET_VCOM_DESEL ,
108- 0x30 , # 0.83*Vcc
123+ 0x30 , # 0.83*Vcc # n.b. specs for ssd1306 64x32 oled screens imply this should be 0x40
109124 # display
110125 SET_CONTRAST ,
111126 0xFF , # maximum
@@ -159,22 +174,23 @@ def poweron(self):
159174
160175 def show (self ):
161176 """Update the display"""
162- xpos0 = 0
163- xpos1 = self .width - 1
164- if self .width == 64 :
165- # displays with width of 64 pixels are shifted by 32
166- xpos0 += 32
167- xpos1 += 32
168- if self .width == 72 :
169- # displays with width of 72 pixels are shifted by 28
170- xpos0 += 28
171- xpos1 += 28
172- self .write_cmd (SET_COL_ADDR )
173- self .write_cmd (xpos0 )
174- self .write_cmd (xpos1 )
175- self .write_cmd (SET_PAGE_ADDR )
176- self .write_cmd (0 )
177- self .write_cmd (self .pages - 1 )
177+ if not self .page_addressing :
178+ xpos0 = 0
179+ xpos1 = self .width - 1
180+ if self .width == 64 :
181+ # displays with width of 64 pixels are shifted by 32
182+ xpos0 += 32
183+ xpos1 += 32
184+ if self .width == 72 :
185+ # displays with width of 72 pixels are shifted by 28
186+ xpos0 += 28
187+ xpos1 += 28
188+ self .write_cmd (SET_COL_ADDR )
189+ self .write_cmd (xpos0 )
190+ self .write_cmd (xpos1 )
191+ self .write_cmd (SET_PAGE_ADDR )
192+ self .write_cmd (0 )
193+ self .write_cmd (self .pages - 1 )
178194 self .write_framebuf ()
179195
180196
@@ -191,10 +207,19 @@ class SSD1306_I2C(_SSD1306):
191207 """
192208
193209 def __init__ (
194- self , width , height , i2c , * , addr = 0x3C , external_vcc = False , reset = None
210+ self ,
211+ width ,
212+ height ,
213+ i2c ,
214+ * ,
215+ addr = 0x3C ,
216+ external_vcc = False ,
217+ reset = None ,
218+ page_addressing = False
195219 ):
196220 self .i2c_device = i2c_device .I2CDevice (i2c , addr )
197221 self .addr = addr
222+ self .page_addressing = page_addressing
198223 self .temp = bytearray (2 )
199224 # Add an extra byte to the data buffer to hold an I2C data/command byte
200225 # to use hardware-compatible I2C transactions. A memoryview of the
@@ -209,10 +234,11 @@ def __init__(
209234 height ,
210235 external_vcc = external_vcc ,
211236 reset = reset ,
237+ page_addressing = self .page_addressing ,
212238 )
213239
214240 def write_cmd (self , cmd ):
215- """Send a command to the SPI device"""
241+ """Send a command to the I2C device"""
216242 self .temp [0 ] = 0x80 # Co=1, D/C#=0
217243 self .temp [1 ] = cmd
218244 with self .i2c_device :
@@ -221,8 +247,18 @@ def write_cmd(self, cmd):
221247 def write_framebuf (self ):
222248 """Blast out the frame buffer using a single I2C transaction to support
223249 hardware I2C interfaces."""
224- with self .i2c_device :
225- self .i2c_device .write (self .buffer )
250+ if self .page_addressing :
251+ for page in range (self .pages ):
252+ self .write_cmd (0xB0 + page )
253+ self .write_cmd (self .page_column_start [0 ])
254+ self .write_cmd (self .page_column_start [1 ])
255+ self .pagebuffer [1 :] = self .buffer [
256+ 1 + self .width * page : 1 + self .width * (page + 1 )
257+ ]
258+ self .i2c_device .write (self .pagebuffer )
259+ else :
260+ with self .i2c_device :
261+ self .i2c_device .write (self .buffer )
226262
227263
228264# pylint: disable-msg=too-many-arguments
@@ -252,8 +288,14 @@ def __init__(
252288 external_vcc = False ,
253289 baudrate = 8000000 ,
254290 polarity = 0 ,
255- phase = 0
291+ phase = 0 ,
292+ page_addressing = False
256293 ):
294+ if page_addressing :
295+ raise NotImplementedError (
296+ "Page addressing mode with SPI has not yet been implemented."
297+ )
298+
257299 self .rate = 10 * 1024 * 1024
258300 dc .switch_to_output (value = 0 )
259301 self .spi_device = spi_device .SPIDevice (
@@ -267,6 +309,7 @@ def __init__(
267309 height ,
268310 external_vcc = external_vcc ,
269311 reset = reset ,
312+ page_addressing = self .page_addressing ,
270313 )
271314
272315 def write_cmd (self , cmd ):
0 commit comments