1
+ #
2
+ # MicroPython SH1106 OLED driver, I2C and SPI interfaces
3
+ #
4
+ # The MIT License (MIT)
5
+ #
6
+ # Copyright (c) 2016 Radomir Dopieralski (@deshipu),
7
+ # 2017 Robert Hammelrath (@robert-hh)
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ # of this software and associated documentation files (the "Software"), to deal
11
+ # in the Software without restriction, including without limitation the rights
12
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ # copies of the Software, and to permit persons to whom the Software is
14
+ # furnished to do so, subject to the following conditions:
15
+ #
16
+ # The above copyright notice and this permission notice shall be included in
17
+ # all copies or substantial portions of the Software.
18
+ #
19
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
+ # THE SOFTWARE.
26
+ #
27
+ # Sample code sections
28
+ # ------------ SPI ------------------
29
+ # Pin Map SPI
30
+ # - 3v - xxxxxx - Vcc
31
+ # - G - xxxxxx - Gnd
32
+ # - D7 - GPIO 13 - Din / MOSI fixed
33
+ # - D5 - GPIO 14 - Clk / Sck fixed
34
+ # - D8 - GPIO 4 - CS (optional, if the only connected device)
35
+ # - D2 - GPIO 5 - D/C
36
+ # - D1 - GPIO 2 - Res
37
+ #
38
+ # for CS, D/C and Res other ports may be chosen.
39
+ #
40
+ # from machine import Pin, SPI
41
+ # import sh1106
42
+
43
+ # spi = SPI(1, baudrate=1000000)
44
+ # display = sh1106.SH1106_SPI(128, 64, spi, Pin(5), Pin(2), Pin(4))
45
+ # display.sleep(False)
46
+ # display.fill(0)
47
+ # display.text('Testing 1', 0, 0, 1)
48
+ # display.show()
49
+ #
50
+ # --------------- I2C ------------------
51
+ #
52
+ # Pin Map I2C
53
+ # - 3v - xxxxxx - Vcc
54
+ # - G - xxxxxx - Gnd
55
+ # - D2 - GPIO 5 - SCK / SCL
56
+ # - D1 - GPIO 4 - DIN / SDA
57
+ # - D0 - GPIO 16 - Res
58
+ # - G - xxxxxx CS
59
+ # - G - xxxxxx D/C
60
+ #
61
+ # Pin's for I2C can be set almost arbitrary
62
+ #
63
+ # from machine import Pin, I2C
64
+ # import sh1106
65
+ #
66
+ # i2c = I2C(scl=Pin(5), sda=Pin(4), freq=400000)
67
+ # display = sh1106.SH1106_I2C(128, 64, i2c, Pin(16), 0x3c)
68
+ # display.sleep(False)
69
+ # display.fill(0)
70
+ # display.text('Testing 1', 0, 0, 1)
71
+ # display.show()
72
+
73
+ from micropython import const
74
+ import utime as time
75
+ import framebuf
76
+
77
+
78
+ # a few register definitions
79
+ _SET_CONTRAST = const (0x81 )
80
+ _SET_NORM_INV = const (0xa6 )
81
+ _SET_DISP = const (0xae )
82
+ _SET_SCAN_DIR = const (0xc0 )
83
+ _SET_SEG_REMAP = const (0xa0 )
84
+ _LOW_COLUMN_ADDRESS = const (0x00 )
85
+ _HIGH_COLUMN_ADDRESS = const (0x10 )
86
+ _SET_PAGE_ADDRESS = const (0xB0 )
87
+
88
+
89
+ class SH1106 :
90
+ def __init__ (self , width , height , external_vcc ):
91
+ self .width = width
92
+ self .height = height
93
+ self .external_vcc = external_vcc
94
+ self .pages = self .height // 8
95
+ self .buffer = bytearray (self .pages * self .width )
96
+ fb = framebuf .FrameBuffer (self .buffer , self .width , self .height ,
97
+ framebuf .MVLSB )
98
+ self .framebuf = fb
99
+ # set shortcuts for the methods of framebuf
100
+ self .fill = fb .fill
101
+ self .fill_rect = fb .fill_rect
102
+ self .hline = fb .hline
103
+ self .vline = fb .vline
104
+ self .line = fb .line
105
+ self .rect = fb .rect
106
+ self .pixel = fb .pixel
107
+ self .scroll = fb .scroll
108
+ self .text = fb .text
109
+ self .blit = fb .blit
110
+
111
+ self .init_display ()
112
+
113
+ def init_display (self ):
114
+ self .reset ()
115
+ self .fill (0 )
116
+ self .poweron ()
117
+ self .show ()
118
+
119
+ def poweroff (self ):
120
+ self .write_cmd (_SET_DISP | 0x00 )
121
+
122
+ def poweron (self ):
123
+ self .write_cmd (_SET_DISP | 0x01 )
124
+
125
+ def rotate (self , flag , update = True ):
126
+ if flag :
127
+ self .write_cmd (_SET_SEG_REMAP | 0x01 ) # mirror display vertically
128
+ self .write_cmd (_SET_SCAN_DIR | 0x08 ) # mirror display hor.
129
+ else :
130
+ self .write_cmd (_SET_SEG_REMAP | 0x00 )
131
+ self .write_cmd (_SET_SCAN_DIR | 0x00 )
132
+ if update :
133
+ self .show ()
134
+
135
+ def sleep (self , value ):
136
+ self .write_cmd (_SET_DISP | (not value ))
137
+
138
+ def contrast (self , contrast ):
139
+ self .write_cmd (_SET_CONTRAST )
140
+ self .write_cmd (contrast )
141
+
142
+ def invert (self , invert ):
143
+ self .write_cmd (_SET_NORM_INV | (invert & 1 ))
144
+
145
+ def show (self ):
146
+ for page in range (self .height // 8 ):
147
+ self .write_cmd (_SET_PAGE_ADDRESS | page )
148
+ self .write_cmd (_LOW_COLUMN_ADDRESS | 2 )
149
+ self .write_cmd (_HIGH_COLUMN_ADDRESS | 0 )
150
+ self .write_data (self .buffer [
151
+ self .width * page :self .width * page + self .width
152
+ ])
153
+
154
+ def reset (self , res ):
155
+ if res is not None :
156
+ res (1 )
157
+ time .sleep_ms (1 )
158
+ res (0 )
159
+ time .sleep_ms (20 )
160
+ res (1 )
161
+ time .sleep_ms (20 )
162
+
163
+
164
+ class SH1106_I2C (SH1106 ):
165
+ def __init__ (self , width , height , i2c , res = None , addr = 0x3c ,
166
+ external_vcc = False ):
167
+ self .i2c = i2c
168
+ self .addr = addr
169
+ self .res = res
170
+ self .temp = bytearray (2 )
171
+ if res is not None :
172
+ res .init (res .OUT , value = 1 )
173
+ super ().__init__ (width , height , external_vcc )
174
+
175
+ def write_cmd (self , cmd ):
176
+ self .temp [0 ] = 0x80 # Co=1, D/C#=0
177
+ self .temp [1 ] = cmd
178
+ self .i2c .writeto (self .addr , self .temp )
179
+
180
+ def write_data (self , buf ):
181
+ self .i2c .writeto (self .addr , b'\x40 ' + buf )
182
+
183
+ def reset (self ):
184
+ super ().reset (self .res )
185
+
186
+
187
+ class SH1106_SPI (SH1106 ):
188
+ def __init__ (self , width , height , spi , dc , res = None , cs = None ,
189
+ external_vcc = False ):
190
+ self .rate = 10 * 1000 * 1000
191
+ dc .init (dc .OUT , value = 0 )
192
+ if res is not None :
193
+ res .init (res .OUT , value = 0 )
194
+ if cs is not None :
195
+ cs .init (cs .OUT , value = 1 )
196
+ self .spi = spi
197
+ self .dc = dc
198
+ self .res = res
199
+ self .cs = cs
200
+ super ().__init__ (width , height , external_vcc )
201
+
202
+ def write_cmd (self , cmd ):
203
+ self .spi .init (baudrate = self .rate , polarity = 0 , phase = 0 )
204
+ if self .cs is not None :
205
+ self .cs (1 )
206
+ self .dc (0 )
207
+ self .cs (0 )
208
+ self .spi .write (bytearray ([cmd ]))
209
+ self .cs (1 )
210
+ else :
211
+ self .dc (0 )
212
+ self .spi .write (bytearray ([cmd ]))
213
+
214
+ def write_data (self , buf ):
215
+ self .spi .init (baudrate = self .rate , polarity = 0 , phase = 0 )
216
+ if self .cs is not None :
217
+ self .cs (1 )
218
+ self .dc (1 )
219
+ self .cs (0 )
220
+ self .spi .write (buf )
221
+ self .cs (1 )
222
+ else :
223
+ self .dc (1 )
224
+ self .spi .write (buf )
225
+
226
+ def reset (self ):
227
+ super ().reset (self .res )
0 commit comments