@@ -48,20 +48,33 @@ class _SSD1306(framebuf.FrameBuffer):
48
48
"""Base class for SSD1306 display driver"""
49
49
50
50
# 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 ):
52
52
super ().__init__ (buffer , width , height )
53
53
self .width = width
54
54
self .height = height
55
55
self .external_vcc = external_vcc
56
56
# reset may be None if not needed
57
57
self .reset_pin = reset
58
+ self .page_addressing = page_addressing
58
59
if self .reset_pin :
59
60
self .reset_pin .switch_to_output (value = 0 )
60
61
self .pages = self .height // 8
61
62
# Note the subclass must initialize self.framebuf to a framebuffer.
62
63
# This is necessary because the underlying data buffer is different
63
64
# between I2C and SPI implementations (I2C needs an extra byte).
64
65
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!
65
78
self .poweron ()
66
79
self .init_display ()
67
80
@@ -86,7 +99,9 @@ def init_display(self):
86
99
SET_DISP | 0x00 , # off
87
100
# address setting
88
101
SET_MEM_ADDR ,
89
- 0x00 , # horizontal
102
+ 0x10 # Page Addressing Mode
103
+ if self .page_addressing
104
+ else 0x00 , # Horizontal Addressing Mode
90
105
# resolution and layout
91
106
SET_DISP_START_LINE | 0x00 ,
92
107
SET_SEG_REMAP | 0x01 , # column addr 127 mapped to SEG0
@@ -105,7 +120,7 @@ def init_display(self):
105
120
SET_PRECHARGE ,
106
121
0x22 if self .external_vcc else 0xF1 ,
107
122
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
109
124
# display
110
125
SET_CONTRAST ,
111
126
0xFF , # maximum
@@ -159,22 +174,23 @@ def poweron(self):
159
174
160
175
def show (self ):
161
176
"""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 )
178
194
self .write_framebuf ()
179
195
180
196
@@ -191,10 +207,19 @@ class SSD1306_I2C(_SSD1306):
191
207
"""
192
208
193
209
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
195
219
):
196
220
self .i2c_device = i2c_device .I2CDevice (i2c , addr )
197
221
self .addr = addr
222
+ self .page_addressing = page_addressing
198
223
self .temp = bytearray (2 )
199
224
# Add an extra byte to the data buffer to hold an I2C data/command byte
200
225
# to use hardware-compatible I2C transactions. A memoryview of the
@@ -209,10 +234,11 @@ def __init__(
209
234
height ,
210
235
external_vcc = external_vcc ,
211
236
reset = reset ,
237
+ page_addressing = self .page_addressing ,
212
238
)
213
239
214
240
def write_cmd (self , cmd ):
215
- """Send a command to the SPI device"""
241
+ """Send a command to the I2C device"""
216
242
self .temp [0 ] = 0x80 # Co=1, D/C#=0
217
243
self .temp [1 ] = cmd
218
244
with self .i2c_device :
@@ -221,8 +247,18 @@ def write_cmd(self, cmd):
221
247
def write_framebuf (self ):
222
248
"""Blast out the frame buffer using a single I2C transaction to support
223
249
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 )
226
262
227
263
228
264
# pylint: disable-msg=too-many-arguments
@@ -252,8 +288,14 @@ def __init__(
252
288
external_vcc = False ,
253
289
baudrate = 8000000 ,
254
290
polarity = 0 ,
255
- phase = 0
291
+ phase = 0 ,
292
+ page_addressing = False
256
293
):
294
+ if page_addressing :
295
+ raise NotImplementedError (
296
+ "Page addressing mode with SPI has not yet been implemented."
297
+ )
298
+
257
299
self .rate = 10 * 1024 * 1024
258
300
dc .switch_to_output (value = 0 )
259
301
self .spi_device = spi_device .SPIDevice (
@@ -267,6 +309,7 @@ def __init__(
267
309
height ,
268
310
external_vcc = external_vcc ,
269
311
reset = reset ,
312
+ page_addressing = self .page_addressing ,
270
313
)
271
314
272
315
def write_cmd (self , cmd ):
0 commit comments