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