1from micropython import const
2import utime
3import framebuf
4from driver import SPI
5from driver import GPIO
6
7# register definitions
8SET_SCAN_DIR = const(0xc0)
9LOW_COLUMN_ADDRESS = const(0x00)
10HIGH_COLUMN_ADDRESS = const(0x10)
11SET_PAGE_ADDRESS = const(0xB0)
12SET_CONTRAST = const(0x81)
13SET_ENTIRE_ON = const(0xa4)
14SET_NORM_INV = const(0xa6)
15SET_DISP = const(0xae)
16SET_MEM_ADDR = const(0x20)
17SET_COL_ADDR = const(0x21)
18SET_PAGE_ADDR = const(0x22)
19SET_DISP_START_LINE = const(0x40)
20SET_SEG_REMAP = const(0xa0)
21SET_MUX_RATIO = const(0xa8)
22SET_COM_OUT_DIR = const(0xc0)
23SET_DISP_OFFSET = const(0xd3)
24SET_COM_PIN_CFG = const(0xda)
25SET_DISP_CLK_DIV = const(0xd5)
26SET_PRECHARGE = const(0xd9)
27SET_VCOM_DESEL = const(0xdb)
28SET_CHARGE_PUMP = const(0x8d)
29
30
31class SH1106:
32    def __init__(self, width, height):
33        self.width = width
34        self.height = height
35        self.pages = self.height // 8
36        self.buffer = bytearray(self.pages * self.width)
37        fb = framebuf.FrameBuffer(
38            self.buffer, self.width, self.height, framebuf.MVLSB)
39        self.framebuf = fb
40# set shortcuts for the methods of framebuf
41        self.fill = fb.fill
42        self.fill_rect = fb.fill_rect
43        self.hline = fb.hline
44        self.vline = fb.vline
45        self.line = fb.line
46        self.rect = fb.rect
47        self.pixel = fb.pixel
48        self.scroll = fb.scroll
49        self.text = fb.text
50        self.blit = fb.blit
51
52        # print("init done")
53
54        self.init_display()
55
56    def init_display(self):
57        self.reset()
58        for cmd in (
59            SET_DISP | 0x00,    # 关闭显示
60            SET_DISP_CLK_DIV, 0x80,  # 设置时钟分频因子
61            SET_MUX_RATIO, self.height - 1,  # 设置驱动路数 路数默认0x3F(1/64)
62            SET_DISP_OFFSET, 0x00,  # 设置显示偏移 偏移默认为0
63            SET_DISP_START_LINE | 0x00,  # 设置显示开始行[5:0]
64            SET_CHARGE_PUMP, 0x14,  # 电荷泵设置 bit2,开启/关闭
65            # 设置内存地址模式 [1:0],00,列地址模式;01,行地址模式;10,页地址模式;默认10;
66            SET_MEM_ADDR, 0x02,
67            SET_SEG_REMAP | 0x01,  # 段重定义设置,bit0:0,0->0;1,0->127;
68            # 设置COM扫描方向;bit3:0,普通模式;1,重定义模式 COM[N-1]->COM0;N:驱动路数
69            SET_COM_OUT_DIR | 0x08,
70            SET_COM_PIN_CFG, 0x12,  # 设置COM硬件引脚配置   [5:4]配置
71            SET_PRECHARGE, 0xf1,  # 设置预充电周期 [3:0],PHASE 1;[7:4],PHASE 2;
72            # 设置VCOMH 电压倍率 [6:4] 000,0.65*vcc;001,0.77*vcc;011,0.83*vcc;
73            SET_VCOM_DESEL, 0x30,
74            SET_CONTRAST, 0xff,  # 对比度设置 默认0x7F(范围1~255,越大越亮)
75            SET_ENTIRE_ON,  # 全局显示开启;bit0:1,开启;0,关闭;(白屏/黑屏)
76            SET_NORM_INV,  # 设置显示方式;bit0:1,反相显示;0,正常显示
77                SET_DISP | 0x01):  # 开启显示
78            self.write_cmd(cmd)
79        self.fill(1)
80        self.show()
81
82    def poweroff(self):
83        self.write_cmd(SET_DISP | 0x00)
84
85    def poweron(self):
86        self.write_cmd(SET_DISP | 0x01)
87
88    def rotate(self, flag, update=True):
89        if flag:
90            self.write_cmd(SET_SEG_REMAP | 0x01)  # mirror display vertically
91            self.write_cmd(SET_SCAN_DIR | 0x08)  # mirror display hor.
92        else:
93            self.write_cmd(SET_SEG_REMAP | 0x00)
94            self.write_cmd(SET_SCAN_DIR | 0x00)
95        if update:
96            self.show()
97
98    def sleep(self, value):
99        self.write_cmd(SET_DISP | (not value))
100
101    def contrast(self, contrast):
102        self.write_cmd(SET_CONTRAST)
103        self.write_cmd(contrast)
104
105    def invert(self, invert):
106        self.write_cmd(SET_NORM_INV | (invert & 1))
107
108    def show(self):
109        for page in range(self.height // 8):
110            self.write_cmd(SET_PAGE_ADDRESS | page)
111            self.write_cmd(LOW_COLUMN_ADDRESS)
112            self.write_cmd(HIGH_COLUMN_ADDRESS)
113            page_buffer = bytearray(self.width)
114            for i in range(self.width):
115                page_buffer[i] = self.buffer[self.width * page + i]
116            self.write_data(page_buffer)
117
118    def set_buffer(self, buffer):
119        for i in range(len(buffer)):
120            self.buffer[i] = buffer[i]
121
122    def draw_XBM(self, x, y, w, h, bitmap):
123        x_byte = (w//8) + (w % 8 != 0)
124        for nbyte in range(len(bitmap)):
125            for bit in range(8):
126                if(bitmap[nbyte] & (0b10000000 >> bit)):
127                    p_x = (nbyte % x_byte)*8+bit
128                    p_y = nbyte//x_byte
129                    self.pixel(x + p_x, y + p_y, 1)
130
131    # 以屏幕GRAM的原始制式去填充Buffer
132    def draw_buffer(self, x, y, w, h, bitmap):
133        y_byte = (h//8) + (h % 8 != 0)
134        for nbyte in range(len(bitmap)):
135            for bit in range(8):
136                if(bitmap[nbyte] & (1 << bit)):
137                    p_y = (nbyte % y_byte)*8+bit
138                    p_x = nbyte//y_byte
139                    self.pixel(x + p_x, y + p_y, 1)
140
141    def fill_rect(self, x, y, w, h, c):
142        self.fill_rect(x, y, w, h, c)
143
144    def fill_circle(self, x0, y0, r, c):
145        x = 0
146        y = r
147        deltax = 3
148        deltay = 2 - r - r
149        d = 1 - r
150        #print(x)
151        #print(y)
152        #print(deltax)
153        #print(deltay)
154        #print(d)
155        self.pixel(x + x0, y + y0, c)
156        self.pixel(x + x0, -y + y0, c)
157        for i in range(-r + x0, r + x0):
158            self.pixel(i, y0, c)
159        while x < y:
160            if d < 0:
161                d += deltax
162                deltax += 2
163                x = x +1
164            else:
165                d += (deltax + deltay)
166                deltax += 2
167                deltay += 2
168                x = x +1
169                y = y -1
170            for i in range(-x + x0, x + x0):
171                self.pixel(i, -y + y0, c)
172                self.pixel(i, y + y0, c)
173            for i in range(-y + x0, y + x0):
174                self.pixel(i, -x + y0, c)
175                self.pixel(i, x + y0, c)
176
177    def draw_circle(self, x0, y0, r, w, c):
178        self.fill_circle(x0, y0, r, c)
179        self.fill_circle(x0, y0, r -w, 0)
180
181    def reset(self, res):
182        if res is not None:
183            res.write(1)
184            utime.sleep_ms(1)
185            res.write(0)
186            utime.sleep_ms(20)
187            res.write(1)
188            utime.sleep_ms(20)
189
190
191class SH1106_I2C(SH1106):
192    def __init__(self, width, height, i2c, res=None, addr=0x3c):
193        self.i2c = i2c
194        self.addr = addr
195        self.res = res
196        self.temp = bytearray(2)
197        super().__init__(width, height)
198
199    def write_cmd(self, cmd):
200        self.temp[0] = 0x80  # Co=1, D/C#=0
201        self.temp[1] = cmd
202        self.i2c.write(self.temp)
203
204    def write_data(self, buf):
205        send_buf = bytearray(1 + len(buf))
206        send_buf[0] = 0x40
207        for i in range(len(buf)):
208            send_buf[i+1] = buf[i]
209        print(send_buf)
210        self.i2c.write(send_buf)
211
212    def reset(self):
213        super().reset(self.res)
214
215class SH1106_SPI(SH1106):
216    def __init__(self, width, height, spi, dc, res=None, cs=None):
217        self.spi = spi
218        self.dc = dc
219        self.res = res
220        self.cs = cs
221        super().__init__(width, height)
222
223    def write_cmd(self, cmd):
224        if self.cs is not None:
225            self.cs.write(1)
226            self.dc.write(0)
227            self.cs.write(0)
228            self.spi.write(bytearray([cmd]))
229            self.cs.write(1)
230        else:
231            self.dc.write(0)
232            self.spi.write(bytearray([cmd]))
233
234    def write_data(self, buf):
235        if self.cs is not None:
236            self.cs.write(1)
237            self.dc.write(1)
238            self.cs.write(0)
239            self.spi.write(buf)
240            self.cs.write(1)
241        else:
242            self.dc.write(1)
243            self.spi.write(buf)
244
245    def reset(self):
246        super().reset(self.res)
247