1+ # MicroPython SSD1315 OLED driver, I2C and SPI interfaces
2+ import math
3+ import framebuf
4+ from micropython import const
5+
6+ # register definitions
7+ SET_CONTRAST = const (0x81 )
8+ SET_ENTIRE_ON = const (0xA4 )
9+ SET_NORM_INV = const (0xA6 )
10+ SET_DISP = const (0xAE )
11+ SET_MEM_ADDR = const (0x20 )
12+ SET_COL_ADDR = const (0x21 )
13+ SET_PAGE_ADDR = const (0x22 )
14+ SET_DISP_START_LINE = const (0x40 )
15+ SET_SEG_REMAP = const (0xA0 )
16+ SET_MUX_RATIO = const (0xA8 )
17+ SET_COM_OUT_DIR = const (0xC0 )
18+ SET_DISP_OFFSET = const (0xD3 )
19+ SET_COM_PIN_CFG = const (0xDA )
20+ SET_DISP_CLK_DIV = const (0xD5 )
21+ SET_PRECHARGE = const (0xD9 )
22+ SET_VCOM_DESEL = const (0xDB )
23+ SET_CHARGE_PUMP = const (0x8D )
24+
25+
26+ # Subclassing FrameBuffer provides support for graphics primitives
27+ # http://docs.micropython.org/en/latest/pyboard/library/framebuf.html
28+ class SSD1315 (framebuf .FrameBuffer ):
29+ def __init__ (self , width , height , external_vcc ):
30+ self .width = width
31+ self .height = height
32+ self .external_vcc = external_vcc
33+ self .pages = self .height // 8
34+ self .buffer = bytearray (self .pages * self .width )
35+ super ().__init__ (self .buffer , self .width , self .height , framebuf .MONO_VLSB )
36+ self .init_display ()
37+
38+ def init_display (self ):
39+ for cmd in (
40+ SET_DISP | 0x00 , # off
41+ # address setting
42+ SET_MEM_ADDR ,
43+ 0x00 , # horizontal
44+ # resolution and layout
45+ SET_DISP_START_LINE | 0x00 ,
46+ SET_SEG_REMAP | 0x01 , # column addr 127 mapped to SEG0
47+ SET_MUX_RATIO ,
48+ self .height - 1 ,
49+ SET_COM_OUT_DIR | 0x08 , # scan from COM[N] to COM0
50+ SET_DISP_OFFSET ,
51+ 0x00 ,
52+ SET_COM_PIN_CFG ,
53+ 0x02 if self .width > 2 * self .height else 0x12 ,
54+ # timing and driving scheme
55+ SET_DISP_CLK_DIV ,
56+ 0x80 ,
57+ SET_PRECHARGE ,
58+ 0x22 if self .external_vcc else 0xF1 ,
59+ SET_VCOM_DESEL ,
60+ 0x30 , # 0.83*Vcc
61+ # display
62+ SET_CONTRAST ,
63+ 0xFF , # maximum
64+ SET_ENTIRE_ON , # output follows RAM contents
65+ SET_NORM_INV , # not inverted
66+ # charge pump
67+ SET_CHARGE_PUMP ,
68+ 0x10 if self .external_vcc else 0x14 ,
69+ SET_DISP | 0x01 ,
70+ ): # on
71+ self .write_cmd (cmd )
72+ self .fill (0 )
73+ self .show ()
74+
75+ def poweroff (self ):
76+ self .write_cmd (SET_DISP | 0x00 )
77+
78+ def poweron (self ):
79+ self .write_cmd (SET_DISP | 0x01 )
80+
81+ def contrast (self , contrast ):
82+ self .write_cmd (SET_CONTRAST )
83+ self .write_cmd (contrast )
84+
85+ def invert (self , invert ):
86+ self .write_cmd (SET_NORM_INV | (invert & 1 ))
87+
88+ def show (self ):
89+ x0 = 0
90+ x1 = self .width - 1
91+ if self .width == 64 :
92+ # displays with width of 64 pixels are shifted by 32
93+ x0 += 32
94+ x1 += 32
95+ self .write_cmd (SET_COL_ADDR )
96+ self .write_cmd (x0 )
97+ self .write_cmd (x1 )
98+ self .write_cmd (SET_PAGE_ADDR )
99+ self .write_cmd (0 )
100+ self .write_cmd (self .pages - 1 )
101+ self .write_data (self .buffer )
102+
103+ def back_light (self , value ):
104+ """
105+ 背光调节
106+
107+ Args:
108+ value: 背光等级 0 ~ 255
109+ """
110+ self .contrast (value )
111+
112+
113+ class SSD1315_I2C (SSD1315 ):
114+ def __init__ (self , width , height , i2c , addr = 0x3D , external_vcc = False ):
115+ self .i2c = i2c
116+ self .addr = addr
117+ self .temp = bytearray (2 )
118+ self .write_list = [b"\x40 " , None ] # Co=0, D/C#=1
119+ super ().__init__ (width , height , external_vcc )
120+
121+ def write_cmd (self , cmd ):
122+ self .temp [0 ] = 0x80 # Co=1, D/C#=0
123+ self .temp [1 ] = cmd
124+ self .i2c .writeto (self .addr , self .temp )
125+
126+ def write_data (self , buf ):
127+ self .write_list [1 ] = buf
128+ self .i2c .writevto (self .addr , self .write_list )
129+
130+ def circle (self , center , radius , c , section = 100 ):
131+ """
132+ 画圆
133+
134+ Args:
135+ c: 颜色
136+ center: 中心(x, y)
137+ radius: 半径
138+ section: 分段
139+ """
140+ arr = []
141+ for m in range (section + 1 ):
142+ x = round (radius * math .cos ((2 * math .pi / section ) * m - math .pi ) + center [0 ])
143+ y = round (radius * math .sin ((2 * math .pi / section ) * m - math .pi ) + center [1 ])
144+ arr .append ([x , y ])
145+ for i in range (len (arr ) - 1 ):
146+ self .line (* arr [i ], * arr [i + 1 ], c )
147+
148+ def fill_circle (self , center , radius , c ):
149+ """
150+ 画填充圆
151+
152+ Args:
153+ c: 颜色
154+ center: 中心(x, y)
155+ radius: 半径
156+ """
157+ rsq = radius * radius
158+ for x in range (radius ):
159+ y = int (math .sqrt (rsq - x * x )) # 计算 y 坐标
160+ y0 = center [1 ] - y
161+ end_y = y0 + y * 2
162+ y0 = max (0 , min (y0 , self .height )) # 将 y0 限制在画布的范围内
163+ length = abs (end_y - y0 ) + 1
164+ self .vline (center [0 ] + x , y0 , length , c ) # 绘制左右两侧的垂直线
165+ self .vline (center [0 ] - x , y0 , length , c )
166+
167+
168+
169+ class SSD1315_SPI (SSD1315 ):
170+ def __init__ (self , width , height , spi , dc , res , cs , external_vcc = False ):
171+ self .rate = 10 * 1024 * 1024
172+ dc .init (dc .OUT , value = 0 )
173+ res .init (res .OUT , value = 0 )
174+ cs .init (cs .OUT , value = 1 )
175+ self .spi = spi
176+ self .dc = dc
177+ self .res = res
178+ self .cs = cs
179+ import time
180+
181+ self .res (1 )
182+ time .sleep_ms (1 )
183+ self .res (0 )
184+ time .sleep_ms (10 )
185+ self .res (1 )
186+ super ().__init__ (width , height , external_vcc )
187+
188+ def write_cmd (self , cmd ):
189+ self .spi .init (baudrate = self .rate , polarity = 0 , phase = 0 )
190+ self .cs (1 )
191+ self .dc (0 )
192+ self .cs (0 )
193+ self .spi .write (bytearray ([cmd ]))
194+ self .cs (1 )
195+
196+ def write_data (self , buf ):
197+ self .spi .init (baudrate = self .rate , polarity = 0 , phase = 0 )
198+ self .cs (1 )
199+ self .dc (1 )
200+ self .cs (0 )
201+ self .spi .write (buf )
202+ self .cs (1 )
203+
204+ def circle (self , center , radius , c , section = 100 ):
205+ """
206+ 画圆
207+
208+ Args:
209+ c: 颜色
210+ center: 中心(x, y)
211+ radius: 半径
212+ section: 分段
213+ """
214+ arr = []
215+ for m in range (section + 1 ):
216+ x = round (radius * math .cos ((2 * math .pi / section ) * m - math .pi ) + center [0 ])
217+ y = round (radius * math .sin ((2 * math .pi / section ) * m - math .pi ) + center [1 ])
218+ arr .append ([x , y ])
219+ for i in range (len (arr ) - 1 ):
220+ self .line (* arr [i ], * arr [i + 1 ], c )
221+
222+ def fill_circle (self , center , radius , c ):
223+ """
224+ 画填充圆
225+
226+ Args:
227+ c: 颜色
228+ center: 中心(x, y)
229+ radius: 半径
230+ """
231+ rsq = radius * radius
232+ for x in range (radius ):
233+ y = int (math .sqrt (rsq - x * x )) # 计算 y 坐标
234+ y0 = center [1 ] - y
235+ end_y = y0 + y * 2
236+ y0 = max (0 , min (y0 , self .height )) # 将 y0 限制在画布的范围内
237+ length = abs (end_y - y0 ) + 1
238+ self .vline (center [0 ] + x , y0 , length , c ) # 绘制左右两侧的垂直线
239+ self .vline (center [0 ] - x , y0 , length , c )
0 commit comments