├── package.json ├── LICENSE.md ├── sh1107 demo v319 spi 128x128.py ├── sh1107 demo v319 spi 128x64.py ├── sh1107 demo v319 i2c 128x128.py ├── sh1107 demo v319 i2c 128x64.py ├── README.md └── sh1107.py /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "urls": [ 3 | ["sh1107.py", "github:peter-l5/SH1107/sh1107.py"] 4 | ], 5 | "version": "1.0.0", 6 | "deps": [] 7 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 peter-l5 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /sh1107 demo v319 spi 128x128.py: -------------------------------------------------------------------------------- 1 | # sh1107 driver demo code v319 2 | # this code is intended for a 128*128 pixel display 3 | 4 | print('starting test') 5 | 6 | # from machine import Pin, I2C 7 | from machine import Pin, SPI 8 | import sh1107 9 | print('dir sh1107: ', dir(sh1107)) 10 | import gc 11 | import sys 12 | import time #as time 13 | import framebuf 14 | import array 15 | 16 | 17 | # basic test code SPI 18 | # spi1 = SPI(1, baudrate=1_000_000, sck=Pin(14), mosi=Pin(15), miso=Pin(12)) 19 | # display = sh1107.SH1107_SPI(64, 128, spi1, Pin(21), Pin(20), Pin(13)) 20 | # display.sleep(False)flip 21 | # display.fill(0) 22 | # display.text('SH1107', 0, 0, 1) 23 | # display.text('driver', 0, 8, 1) 24 | # display.show() 25 | # 26 | # time.sleep(2) 27 | 28 | # full test code 29 | print('version ',sys.implementation) 30 | print('Initial free: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 31 | spi1 = SPI(1, baudrate=1_000_000, sck=Pin(14), mosi=Pin(15), miso=Pin(12)) 32 | print('SPI created: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 33 | display = sh1107.SH1107_SPI(128, 128, spi1, Pin(21), Pin(20), Pin(13), rotate=0) 34 | print('display created: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 35 | 36 | 37 | ## common I2C and SPI test code below ## 38 | 39 | display.sleep(False) 40 | display.fill(0) 41 | display.text('SH1107', 0, 0, 1) 42 | display.text('driver', 0, 8, 1) 43 | display.show() 44 | time.sleep(1) 45 | 46 | display.fill(0) 47 | display.show() 48 | if sh1107._fb_variant == 2: 49 | for i in range(17): 50 | display.large_text(str(i % 10), 0, 0, max(i,1), c=1, r=90*i) 51 | display.show() 52 | time.sleep(0.1) 53 | display.fill(0) 54 | for i in range(5): 55 | for j in range(5): 56 | display.large_text(str("big text"), (i) % 2 *56, (i+1) % 2 *56, 2, 1, 90*i, 90*j) 57 | display.show() 58 | time.sleep(.2) 59 | display.fill(0) 60 | print('framebuf2 framebuffer extension tests: triangles and circles') 61 | for i in range (0, 32, 4): 62 | display.triangle(0+3*i, i, 127-i, i, 127-i, 127-3*i, c=1) 63 | display.show() 64 | for i in range (0, 32, 4): 65 | display.triangle(i, 0+3*i, i, 127-i, 127-3*i, 127-i, c=1) 66 | display.show() 67 | time.sleep(2) 68 | display.fill(0) 69 | # display.show() 70 | # display.triangle(0, 0, 0, 127, 127, 127, c=1, f=True) 71 | # display.show() 72 | # time.sleep(2) 73 | display.fill(0) 74 | display.show() 75 | for i in range (0, 64, 4): 76 | display.circle(64, 64, 64-i , c=1) 77 | display.show() 78 | time.sleep(2) 79 | display.fill(0) 80 | display.show() 81 | for i in range (0, 128, 32): 82 | for j in range (0, 128, 32): 83 | display.rect(i, j, 32, 32, c=(i+j)//32 % 2, f=True) 84 | display.show() 85 | display.circle(i+16, j+16, 15 , c=((i+j)//32 +1) % 2, f=True) 86 | display.show() 87 | time.sleep(2) 88 | display.fill(0) 89 | display.show() 90 | 91 | 92 | #display.sleep(False) 93 | display.fill(1) 94 | display.show() 95 | display.show() 96 | time.sleep(1) 97 | display.fill(0) 98 | time.sleep(1) 99 | full_update_flag = False 100 | display.text('Testing 1', 0, 0, 1) 101 | display.show(full_update_flag) 102 | display.show(full_update_flag) 103 | time.sleep(1) 104 | display.text('Testing 16', 16, 16, 1) 105 | display.show(full_update_flag) 106 | time.sleep(1) 107 | display.fill_rect(0, 26, 128, 4, 1) 108 | # display.text('Testing 32', 16, 32, 1) 109 | display.show(full_update_flag) 110 | time.sleep(1) 111 | display.text('Testing 120', 16, 120, 1) 112 | display.show(full_update_flag) 113 | time.sleep(1) 114 | display.text('0----+----1----+-', 0, 88, 1) 115 | display.text('01234567890123456', 0, 96, 1) 116 | for i in range (16): 117 | p=display.pixel(i,96) 118 | # p=i // 10 119 | display.text(str(p),i*8,104) 120 | for j in range (4): 121 | display.pixel(i*8+j*2,114,1) 122 | 123 | 124 | display.show(full_update_flag) 125 | print('bilt test') 126 | smallbuffer=bytearray(8) 127 | letter=framebuf.FrameBuffer(smallbuffer,8,8,framebuf.MONO_HMSB) 128 | print('letter dir', dir(letter)) 129 | letter.text('K',0,0,1) 130 | display.blit(letter,0,56) 131 | display.show(full_update_flag) 132 | 133 | print('large text test') 134 | try: 135 | display.large_text('QuiteBIG', 0, 32, 2, 1) 136 | except: 137 | display.text('framebuf2', 0, 40 ,1) 138 | display.text('not loaded', 0, 48, 1) 139 | 140 | display.show(True) 141 | # sys.exit(0) 142 | time.sleep(2) 143 | 144 | print('contrast demo') 145 | print('free memory' , gc.mem_free()) 146 | for i in range (256): 147 | display.contrast(i) 148 | contrast_text='contrast '+str(i) 149 | display.fill_rect(16,64,96,8,0) 150 | display.text(contrast_text, 16, 64, 1) 151 | display.show(full_update_flag) 152 | time.sleep_ms(25) 153 | 154 | display.contrast(0x5F) 155 | 156 | print('scroll test (start line)') 157 | print('free memory' , gc.mem_free()) 158 | for i in range (128): 159 | display.display_start_line(i) 160 | time.sleep_ms(4) 161 | for i in range (128): 162 | display.display_start_line(127-i) 163 | time.sleep_ms(4) 164 | 165 | display.display_start_line(0) 166 | time.sleep_ms(500) 167 | 168 | print('free memory' , gc.mem_free()) 169 | 170 | 171 | print('invert test') 172 | display.flip() 173 | time.sleep(2) 174 | display.invert(1) 175 | time.sleep(2) 176 | display.invert(0) 177 | time.sleep(2) 178 | display.flip() 179 | time.sleep(2) 180 | display.invert(1) 181 | time.sleep(2) 182 | display.invert(0) 183 | time.sleep(1) 184 | display.fill(0) 185 | display.show(full_update_flag) 186 | 187 | 188 | print('large text test 3') 189 | try: 190 | display.large_text('BIGx3', 0, 0, 3, 1) 191 | except: 192 | display.text('large_text', 0, 0, 1) 193 | display.text('method from', 0, 8, 1) 194 | display.text('framebuf2', 0, 16 ,1) 195 | display.text('not loaded', 0, 24, 1) 196 | display.show(full_update_flag) 197 | 198 | print('large text test 4') 199 | try: 200 | display.large_text('HUGE', 0, 64, 4, 1) 201 | except: 202 | pass 203 | display.show(full_update_flag) 204 | 205 | time.sleep(2) 206 | display.fill(0) 207 | display.show() 208 | 209 | print('large text test 16') 210 | try: 211 | display.large_text('1', 0, 0, 16, 1) 212 | display.show() 213 | time.sleep(1) 214 | display.fill(0) 215 | display.large_text('2', 0, 0, 16, 1) 216 | display.show() 217 | time.sleep(1) 218 | display.fill(0) 219 | display.large_text('3', 0, 0, 16, 1) 220 | display.show() 221 | time.sleep(1) 222 | display.fill(0) 223 | display.large_text('!', 0, 0, 16, 1) 224 | display.show() 225 | time.sleep(1) 226 | display.invert(1) 227 | time.sleep(1) 228 | display.invert(0) 229 | time.sleep(1) 230 | display.invert(1) 231 | time.sleep(1) 232 | display.invert(0) 233 | time.sleep(1) 234 | except: 235 | pass 236 | 237 | time.sleep(1) 238 | 239 | for i in range(64): 240 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2) 241 | display.show() 242 | # time.sleep_ms(10) 243 | time.sleep(1) 244 | display.fill(0) 245 | display.show() 246 | 247 | for i in range(64): 248 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2, f=False) 249 | # display.sleep(i % 2) 250 | display.show() 251 | 252 | time.sleep(1) 253 | display.fill(0) 254 | display.show() 255 | 256 | for i in range(64): 257 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2, f=True) 258 | # display.sleep(i % 2) 259 | display.show() 260 | 261 | # time.sleep(1) 262 | # display.fill(0) 263 | # display.show() 264 | # 265 | # for i in range(64): 266 | # display.fill_rect(i, i, 128-i*2, 128-i*2, c = i % 2) 267 | # # display.sleep(i % 2) 268 | # display.show() 269 | 270 | # for i in range(64): 271 | # display.sleep(i % 2) 272 | 273 | time.sleep(2) 274 | display.fill(0) 275 | display.show() 276 | display.show() 277 | display.show() 278 | display.show() 279 | display.show() 280 | display.show() 281 | display.show() 282 | display.show() 283 | display.show() 284 | display.show() 285 | 286 | display.ellipse(64, 48, 56, 32, 1) 287 | display.show() 288 | time.sleep(1) 289 | display.ellipse(64, 48, 56, 32, 1, 1, 0xD) 290 | display.show() 291 | time.sleep(1) 292 | coords=array.array('h', [0,0,0,40,40,40 ]) 293 | print(coords) 294 | display.fill(0) 295 | display.poly(10,10,coords, 1,1) 296 | display.show() 297 | time.sleep(3) 298 | 299 | display.poweroff() 300 | 301 | -------------------------------------------------------------------------------- /sh1107 demo v319 spi 128x64.py: -------------------------------------------------------------------------------- 1 | # sh1107 driver demo code v319 2 | # this code is intended for a 128*128 pixel display 3 | 4 | print('starting test') 5 | 6 | # from machine import Pin, I2C 7 | from machine import Pin, SPI 8 | import sh1107 9 | print('dir sh1107: ', dir(sh1107)) 10 | import gc 11 | import sys 12 | import time #as time 13 | import framebuf 14 | import array 15 | 16 | 17 | # basic test code SPI 18 | # spi1 = SPI(1, baudrate=1_000_000, sck=Pin(14), mosi=Pin(15), miso=Pin(12)) 19 | # display = sh1107.SH1107_SPI(64, 128, spi1, Pin(21), Pin(20), Pin(13)) 20 | # display.sleep(False)flip 21 | # display.fill(0) 22 | # display.text('SH1107', 0, 0, 1) 23 | # display.text('driver', 0, 8, 1) 24 | # display.show() 25 | # 26 | # time.sleep(2) 27 | 28 | # full test code 29 | print('version ',sys.implementation) 30 | print('Initial free: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 31 | spi1 = SPI(1, baudrate=1_000_000, sck=Pin(14), mosi=Pin(15), miso=Pin(12)) 32 | print('SPI created: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 33 | display = sh1107.SH1107_SPI(128, 64, spi1, Pin(21), Pin(20), Pin(13), rotate=0) 34 | print('display created: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 35 | 36 | 37 | ## common I2C and SPI test code below ## 38 | 39 | display.sleep(False) 40 | display.fill(0) 41 | display.text('SH1107', 0, 0, 1) 42 | display.text('driver', 0, 8, 1) 43 | display.show() 44 | time.sleep(1) 45 | 46 | display.fill(0) 47 | display.show() 48 | if sh1107._fb_variant == 2: 49 | for i in range(17): 50 | display.large_text(str(i % 10), 0, 0, max(i,1), c=1, r=90*i) 51 | display.show() 52 | time.sleep(0.1) 53 | display.fill(0) 54 | for i in range(5): 55 | for j in range(5): 56 | display.large_text(str("big text"), (i) % 2 *56, (i+1) % 2 *56, 2, 1, 90*i, 90*j) 57 | display.show() 58 | time.sleep(.2) 59 | display.fill(0) 60 | print('framebuf2 framebuffer extension tests: triangles and circles') 61 | for i in range (0, 32, 4): 62 | display.triangle(0+3*i, i, 127-i, i, 127-i, 127-3*i, c=1) 63 | display.show() 64 | for i in range (0, 32, 4): 65 | display.triangle(i, 0+3*i, i, 127-i, 127-3*i, 127-i, c=1) 66 | display.show() 67 | time.sleep(2) 68 | display.fill(0) 69 | # display.show() 70 | # display.triangle(0, 0, 0, 127, 127, 127, c=1, f=True) 71 | # display.show() 72 | # time.sleep(2) 73 | display.fill(0) 74 | display.show() 75 | for i in range (0, 64, 4): 76 | display.circle(64, 64, 64-i , c=1) 77 | display.show() 78 | time.sleep(2) 79 | display.fill(0) 80 | display.show() 81 | for i in range (0, 128, 32): 82 | for j in range (0, 128, 32): 83 | display.rect(i, j, 32, 32, c=(i+j)//32 % 2, f=True) 84 | display.show() 85 | display.circle(i+16, j+16, 15 , c=((i+j)//32 +1) % 2, f=True) 86 | display.show() 87 | time.sleep(2) 88 | display.fill(0) 89 | display.show() 90 | 91 | 92 | #display.sleep(False) 93 | display.fill(1) 94 | display.show() 95 | display.show() 96 | time.sleep(1) 97 | display.fill(0) 98 | time.sleep(1) 99 | full_update_flag = False 100 | display.text('Testing 1', 0, 0, 1) 101 | display.show(full_update_flag) 102 | display.show(full_update_flag) 103 | time.sleep(1) 104 | display.text('Testing 16', 16, 16, 1) 105 | display.show(full_update_flag) 106 | time.sleep(1) 107 | display.fill_rect(0, 26, 128, 4, 1) 108 | # display.text('Testing 32', 16, 32, 1) 109 | display.show(full_update_flag) 110 | time.sleep(1) 111 | display.text('Testing 120', 16, 120, 1) 112 | display.show(full_update_flag) 113 | time.sleep(1) 114 | display.text('0----+----1----+-', 0, 88, 1) 115 | display.text('01234567890123456', 0, 96, 1) 116 | for i in range (16): 117 | p=display.pixel(i,96) 118 | # p=i // 10 119 | display.text(str(p),i*8,104) 120 | for j in range (4): 121 | display.pixel(i*8+j*2,114,1) 122 | 123 | 124 | display.show(full_update_flag) 125 | print('bilt test') 126 | smallbuffer=bytearray(8) 127 | letter=framebuf.FrameBuffer(smallbuffer,8,8,framebuf.MONO_HMSB) 128 | print('letter dir', dir(letter)) 129 | letter.text('K',0,0,1) 130 | display.blit(letter,0,56) 131 | display.show(full_update_flag) 132 | 133 | print('large text test') 134 | try: 135 | display.large_text('QuiteBIG', 0, 32, 2, 1) 136 | except: 137 | display.text('framebuf2', 0, 40 ,1) 138 | display.text('not loaded', 0, 48, 1) 139 | 140 | display.show(True) 141 | # sys.exit(0) 142 | time.sleep(2) 143 | 144 | print('contrast demo') 145 | print('free memory' , gc.mem_free()) 146 | for i in range (256): 147 | display.contrast(i) 148 | contrast_text='contrast '+str(i) 149 | display.fill_rect(16,64,96,8,0) 150 | display.text(contrast_text, 16, 64, 1) 151 | display.show(full_update_flag) 152 | time.sleep_ms(25) 153 | 154 | display.contrast(0x5F) 155 | 156 | print('scroll test (start line)') 157 | print('free memory' , gc.mem_free()) 158 | for i in range (128): 159 | display.display_start_line(i) 160 | time.sleep_ms(4) 161 | for i in range (128): 162 | display.display_start_line(127-i) 163 | time.sleep_ms(4) 164 | 165 | display.display_start_line(0) 166 | time.sleep_ms(500) 167 | 168 | print('free memory' , gc.mem_free()) 169 | 170 | 171 | print('invert test') 172 | display.flip() 173 | time.sleep(2) 174 | display.invert(1) 175 | time.sleep(2) 176 | display.invert(0) 177 | time.sleep(2) 178 | display.flip() 179 | time.sleep(2) 180 | display.invert(1) 181 | time.sleep(2) 182 | display.invert(0) 183 | time.sleep(1) 184 | display.fill(0) 185 | display.show(full_update_flag) 186 | 187 | 188 | print('large text test 3') 189 | try: 190 | display.large_text('BIGx3', 0, 0, 3, 1) 191 | except: 192 | display.text('large_text', 0, 0, 1) 193 | display.text('method from', 0, 8, 1) 194 | display.text('framebuf2', 0, 16 ,1) 195 | display.text('not loaded', 0, 24, 1) 196 | display.show(full_update_flag) 197 | 198 | print('large text test 4') 199 | try: 200 | display.large_text('HUGE', 0, 64, 4, 1) 201 | except: 202 | pass 203 | display.show(full_update_flag) 204 | 205 | time.sleep(2) 206 | display.fill(0) 207 | display.show() 208 | 209 | print('large text test 16') 210 | try: 211 | display.large_text('1', 0, 0, 16, 1) 212 | display.show() 213 | time.sleep(1) 214 | display.fill(0) 215 | display.large_text('2', 0, 0, 16, 1) 216 | display.show() 217 | time.sleep(1) 218 | display.fill(0) 219 | display.large_text('3', 0, 0, 16, 1) 220 | display.show() 221 | time.sleep(1) 222 | display.fill(0) 223 | display.large_text('!', 0, 0, 16, 1) 224 | display.show() 225 | time.sleep(1) 226 | display.invert(1) 227 | time.sleep(1) 228 | display.invert(0) 229 | time.sleep(1) 230 | display.invert(1) 231 | time.sleep(1) 232 | display.invert(0) 233 | time.sleep(1) 234 | except: 235 | pass 236 | 237 | time.sleep(1) 238 | 239 | for i in range(64): 240 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2) 241 | display.show() 242 | # time.sleep_ms(10) 243 | time.sleep(1) 244 | display.fill(0) 245 | display.show() 246 | 247 | for i in range(64): 248 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2, f=False) 249 | # display.sleep(i % 2) 250 | display.show() 251 | 252 | time.sleep(1) 253 | display.fill(0) 254 | display.show() 255 | 256 | for i in range(64): 257 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2, f=True) 258 | # display.sleep(i % 2) 259 | display.show() 260 | 261 | # time.sleep(1) 262 | # display.fill(0) 263 | # display.show() 264 | # 265 | # for i in range(64): 266 | # display.fill_rect(i, i, 128-i*2, 128-i*2, c = i % 2) 267 | # # display.sleep(i % 2) 268 | # display.show() 269 | 270 | # for i in range(64): 271 | # display.sleep(i % 2) 272 | 273 | time.sleep(2) 274 | display.fill(0) 275 | display.show() 276 | display.show() 277 | display.show() 278 | display.show() 279 | display.show() 280 | display.show() 281 | display.show() 282 | display.show() 283 | display.show() 284 | display.show() 285 | 286 | display.ellipse(64, 48, 56, 32, 1) 287 | display.show() 288 | time.sleep(1) 289 | display.ellipse(64, 48, 56, 32, 1, 1, 0xD) 290 | display.show() 291 | time.sleep(1) 292 | coords=array.array('h', [0,0,0,40,40,40 ]) 293 | print(coords) 294 | display.fill(0) 295 | display.poly(10,10,coords, 1,1) 296 | display.show() 297 | time.sleep(3) 298 | 299 | display.poweroff() 300 | 301 | -------------------------------------------------------------------------------- /sh1107 demo v319 i2c 128x128.py: -------------------------------------------------------------------------------- 1 | # sh1107 driver demo code v319 2 | # this code is intended for a 128*128 pixel display 3 | 4 | print('starting test') 5 | 6 | from machine import Pin, I2C, SoftI2C 7 | import sh1107 8 | print('dir sh1107: ', dir(sh1107)) 9 | import gc 10 | import sys 11 | import time #as time 12 | import framebuf 13 | import array 14 | 15 | 16 | # # basic test code I2C 17 | # i2c0 = SoftI2C(scl=Pin(5), sda=Pin(4), freq=400000) 18 | i2c0 = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) 19 | print('I2C scan: ',i2c0.scan()) 20 | # display = sh1107.SH1107_I2C(128, 128, i2c0, Pin(16), address=0x3d, rotate=90) 21 | display = sh1107.SH1107_I2C(128, 128, i2c0, address=61, rotate=0) 22 | time.sleep(0.5) 23 | # display.sleep(False) 24 | display.fill(0) 25 | display.text('SH1107', 0, 0, 1) 26 | display.text('driver', 0, 8, 1) 27 | display.show() 28 | time.sleep(1) 29 | display.fill(0) 30 | display.show() 31 | 32 | # full test code 33 | print('version ',sys.implementation) 34 | print('Initial free memory: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 35 | i2c0 = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) 36 | print('I2C scan: ',i2c0.scan()) 37 | print('I2C created: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 38 | display = sh1107.SH1107_I2C(128, 128, i2c0, address=61, rotate=90) 39 | # display = sh1107.SH1107_I2C(128, 128, i2c0, Pin(16), address=0x3d, rotate=0) 40 | print('display created: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 41 | 42 | 43 | ## common I2C and SPI test code below ## 44 | 45 | display.sleep(False) 46 | display.fill(0) 47 | display.text('SH1107', 0, 0, 1) 48 | display.text('driver', 0, 8, 1) 49 | display.show() 50 | time.sleep(1) 51 | 52 | display.fill(0) 53 | display.show() 54 | if sh1107._fb_variant == 2: 55 | for i in range(17): 56 | display.large_text(str(i % 10), 0, 0, max(i,1), c=1, r=90*i) 57 | display.show() 58 | time.sleep(0.1) 59 | display.fill(0) 60 | for i in range(5): 61 | for j in range(5): 62 | display.large_text(str("big text"), (i) % 2 *56, (i+1) % 2 *56, 2, 1, 90*i, 90*j) 63 | display.show() 64 | time.sleep(.2) 65 | display.fill(0) 66 | print('framebuf2 framebuffer extension tests: triangles and circles') 67 | for i in range (0, 32, 4): 68 | display.triangle(0+3*i, i, 127-i, i, 127-i, 127-3*i, c=1) 69 | display.show() 70 | for i in range (0, 32, 4): 71 | display.triangle(i, 0+3*i, i, 127-i, 127-3*i, 127-i, c=1) 72 | display.show() 73 | time.sleep(2) 74 | display.fill(0) 75 | # display.show() 76 | # display.triangle(0, 0, 0, 127, 127, 127, c=1, f=True) 77 | # display.show() 78 | # time.sleep(2) 79 | display.fill(0) 80 | display.show() 81 | for i in range (0, 64, 4): 82 | display.circle(64, 64, 64-i , c=1) 83 | display.show() 84 | time.sleep(2) 85 | display.fill(0) 86 | display.show() 87 | for i in range (0, 128, 32): 88 | for j in range (0, 128, 32): 89 | display.rect(i, j, 32, 32, c=(i+j)//32 % 2, f=True) 90 | display.show() 91 | display.circle(i+16, j+16, 15 , c=((i+j)//32 +1) % 2, f=True) 92 | display.show() 93 | time.sleep(2) 94 | display.fill(0) 95 | display.show() 96 | 97 | 98 | #display.sleep(False) 99 | display.fill(1) 100 | display.show() 101 | display.show() 102 | time.sleep(1) 103 | display.fill(0) 104 | time.sleep(1) 105 | full_update_flag = False 106 | display.text('Testing 1', 0, 0, 1) 107 | display.show(full_update_flag) 108 | display.show(full_update_flag) 109 | time.sleep(1) 110 | display.text('Testing 16', 16, 16, 1) 111 | display.show(full_update_flag) 112 | time.sleep(1) 113 | display.fill_rect(0, 26, 128, 4, 1) 114 | # display.text('Testing 32', 16, 32, 1) 115 | display.show(full_update_flag) 116 | time.sleep(1) 117 | display.text('Testing 120', 16, 120, 1) 118 | display.show(full_update_flag) 119 | time.sleep(1) 120 | display.text('0----+----1----+-', 0, 88, 1) 121 | display.text('01234567890123456', 0, 96, 1) 122 | for i in range (16): 123 | p=display.pixel(i,96) 124 | # p=i // 10 125 | display.text(str(p),i*8,104) 126 | for j in range (4): 127 | display.pixel(i*8+j*2,114,1) 128 | 129 | 130 | display.show(full_update_flag) 131 | print('bilt test') 132 | smallbuffer=bytearray(8) 133 | letter=framebuf.FrameBuffer(smallbuffer,8,8,framebuf.MONO_HMSB) 134 | print('letter dir', dir(letter)) 135 | letter.text('K',0,0,1) 136 | display.blit(letter,0,56) 137 | display.show(full_update_flag) 138 | 139 | print('large text test') 140 | try: 141 | display.large_text('QuiteBIG', 0, 32, 2, 1) 142 | except: 143 | display.text('framebuf2', 0, 40 ,1) 144 | display.text('not loaded', 0, 48, 1) 145 | 146 | display.show(True) 147 | # sys.exit(0) 148 | time.sleep(2) 149 | 150 | print('contrast demo') 151 | print('free memory' , gc.mem_free()) 152 | for i in range (256): 153 | display.contrast(i) 154 | contrast_text='contrast '+str(i) 155 | display.fill_rect(16,64,96,8,0) 156 | display.text(contrast_text, 16, 64, 1) 157 | display.show(full_update_flag) 158 | time.sleep_ms(25) 159 | 160 | display.contrast(0x5F) 161 | 162 | print('scroll test (start line)') 163 | print('free memory' , gc.mem_free()) 164 | for i in range (128): 165 | display.display_start_line(i) 166 | time.sleep_ms(4) 167 | for i in range (128): 168 | display.display_start_line(127-i) 169 | time.sleep_ms(4) 170 | 171 | display.display_start_line(0) 172 | time.sleep_ms(500) 173 | 174 | print('free memory' , gc.mem_free()) 175 | 176 | 177 | print('invert test') 178 | display.flip() 179 | time.sleep(2) 180 | display.invert(1) 181 | time.sleep(2) 182 | display.invert(0) 183 | time.sleep(2) 184 | display.flip() 185 | time.sleep(2) 186 | display.invert(1) 187 | time.sleep(2) 188 | display.invert(0) 189 | time.sleep(1) 190 | display.fill(0) 191 | display.show(full_update_flag) 192 | 193 | 194 | print('large text test 3') 195 | try: 196 | display.large_text('BIGx3', 0, 0, 3, 1) 197 | except: 198 | display.text('large_text', 0, 0, 1) 199 | display.text('method from', 0, 8, 1) 200 | display.text('framebuf2', 0, 16 ,1) 201 | display.text('not loaded', 0, 24, 1) 202 | display.show(full_update_flag) 203 | 204 | print('large text test 4') 205 | try: 206 | display.large_text('HUGE', 0, 64, 4, 1) 207 | except: 208 | pass 209 | display.show(full_update_flag) 210 | 211 | time.sleep(2) 212 | display.fill(0) 213 | display.show() 214 | 215 | print('large text test 16') 216 | try: 217 | display.large_text('1', 0, 0, 16, 1) 218 | display.show() 219 | time.sleep(1) 220 | display.fill(0) 221 | display.large_text('2', 0, 0, 16, 1) 222 | display.show() 223 | time.sleep(1) 224 | display.fill(0) 225 | display.large_text('3', 0, 0, 16, 1) 226 | display.show() 227 | time.sleep(1) 228 | display.fill(0) 229 | display.large_text('!', 0, 0, 16, 1) 230 | display.show() 231 | time.sleep(1) 232 | display.invert(1) 233 | time.sleep(1) 234 | display.invert(0) 235 | time.sleep(1) 236 | display.invert(1) 237 | time.sleep(1) 238 | display.invert(0) 239 | time.sleep(1) 240 | except: 241 | pass 242 | 243 | time.sleep(1) 244 | 245 | for i in range(64): 246 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2) 247 | display.show() 248 | # time.sleep_ms(10) 249 | time.sleep(1) 250 | display.fill(0) 251 | display.show() 252 | 253 | for i in range(64): 254 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2, f=False) 255 | # display.sleep(i % 2) 256 | display.show() 257 | 258 | time.sleep(1) 259 | display.fill(0) 260 | display.show() 261 | 262 | for i in range(64): 263 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2, f=True) 264 | # display.sleep(i % 2) 265 | display.show() 266 | 267 | # time.sleep(1) 268 | # display.fill(0) 269 | # display.show() 270 | # 271 | # for i in range(64): 272 | # display.fill_rect(i, i, 128-i*2, 128-i*2, c = i % 2) 273 | # # display.sleep(i % 2) 274 | # display.show() 275 | 276 | # for i in range(64): 277 | # display.sleep(i % 2) 278 | 279 | time.sleep(2) 280 | display.fill(0) 281 | display.show() 282 | display.show() 283 | display.show() 284 | display.show() 285 | display.show() 286 | display.show() 287 | display.show() 288 | display.show() 289 | display.show() 290 | display.show() 291 | 292 | display.ellipse(64, 48, 56, 32, 1) 293 | display.show() 294 | time.sleep(1) 295 | display.ellipse(64, 48, 56, 32, 1, 1, 0xD) 296 | display.show() 297 | time.sleep(1) 298 | coords=array.array('h', [0,0,0,40,40,40 ]) 299 | print(coords) 300 | display.fill(0) 301 | display.poly(10,10,coords, 1,1) 302 | display.show() 303 | time.sleep(3) 304 | 305 | display.poweroff() 306 | 307 | -------------------------------------------------------------------------------- /sh1107 demo v319 i2c 128x64.py: -------------------------------------------------------------------------------- 1 | # sh1107 driver demo code v319 2 | # this code is intended for a 128*128 pixel display 3 | 4 | print('starting test') 5 | 6 | from machine import Pin, I2C, SoftI2C 7 | import sh1107 8 | print('dir sh1107: ', dir(sh1107)) 9 | import gc 10 | import sys 11 | import time #as time 12 | import framebuf 13 | import array 14 | 15 | 16 | # # basic test code I2C 17 | # i2c0 = SoftI2C(scl=Pin(5), sda=Pin(4), freq=400000) 18 | i2c0 = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) 19 | print('I2C scan: ',i2c0.scan()) 20 | # display = sh1107.SH1107_I2C(128, 128, i2c0, Pin(16), address=0x3d, rotate=90) 21 | display = sh1107.SH1107_I2C(128, 64, i2c0, address=60, rotate=0) 22 | #time.sleep(0.5) 23 | # display.sleep(False) 24 | display.fill(0) 25 | display.text('SH1107', 0, 0, 1) 26 | display.text('driver', 0, 8, 1) 27 | display.show() 28 | time.sleep(1) 29 | display.fill(0) 30 | display.show() 31 | 32 | # full test code 33 | print('version ',sys.implementation) 34 | print('Initial free memory: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 35 | i2c0 = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) 36 | print('I2C scan: ',i2c0.scan()) 37 | print('I2C created: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 38 | display = sh1107.SH1107_I2C(128, 64, i2c0, address=60, rotate=90) 39 | # display = sh1107.SH1107_I2C(128, 128, i2c0, Pin(16), address=0x3d, rotate=0) 40 | print('display created: {} allocated: {}'.format(gc.mem_free(), gc.mem_alloc())) 41 | 42 | 43 | ## common I2C and SPI test code below ## 44 | 45 | display.sleep(False) 46 | display.fill(0) 47 | display.text('SH1107', 0, 0, 1) 48 | display.text('driver', 0, 8, 1) 49 | display.show() 50 | time.sleep(1) 51 | 52 | display.fill(0) 53 | display.show() 54 | if sh1107._fb_variant == 2: 55 | for i in range(17): 56 | display.large_text(str(i % 10), 0, 0, max(i,1), c=1, r=90*i) 57 | display.show() 58 | time.sleep(0.1) 59 | display.fill(0) 60 | for i in range(5): 61 | for j in range(5): 62 | display.large_text(str("big text"), (i) % 2 *56, (i+1) % 2 *56, 2, 1, 90*i, 90*j) 63 | display.show() 64 | time.sleep(.2) 65 | display.fill(0) 66 | print('framebuf2 framebuffer extension tests: triangles and circles') 67 | for i in range (0, 32, 4): 68 | display.triangle(0+3*i, i, 127-i, i, 127-i, 127-3*i, c=1) 69 | display.show() 70 | for i in range (0, 32, 4): 71 | display.triangle(i, 0+3*i, i, 127-i, 127-3*i, 127-i, c=1) 72 | display.show() 73 | time.sleep(2) 74 | display.fill(0) 75 | # display.show() 76 | # display.triangle(0, 0, 0, 127, 127, 127, c=1, f=True) 77 | # display.show() 78 | # time.sleep(2) 79 | display.fill(0) 80 | display.show() 81 | for i in range (0, 64, 4): 82 | display.circle(64, 64, 64-i , c=1) 83 | display.show() 84 | time.sleep(2) 85 | display.fill(0) 86 | display.show() 87 | for i in range (0, 128, 32): 88 | for j in range (0, 128, 32): 89 | display.rect(i, j, 32, 32, c=(i+j)//32 % 2, f=True) 90 | display.show() 91 | display.circle(i+16, j+16, 15 , c=((i+j)//32 +1) % 2, f=True) 92 | display.show() 93 | time.sleep(2) 94 | display.fill(0) 95 | display.show() 96 | 97 | 98 | #display.sleep(False) 99 | display.fill(1) 100 | display.show() 101 | display.show() 102 | time.sleep(1) 103 | display.fill(0) 104 | time.sleep(1) 105 | full_update_flag = False 106 | display.text('Testing 1', 0, 0, 1) 107 | display.show(full_update_flag) 108 | display.show(full_update_flag) 109 | time.sleep(1) 110 | display.text('Testing 16', 16, 16, 1) 111 | display.show(full_update_flag) 112 | time.sleep(1) 113 | display.fill_rect(0, 26, 128, 4, 1) 114 | # display.text('Testing 32', 16, 32, 1) 115 | display.show(full_update_flag) 116 | time.sleep(1) 117 | display.text('Testing 120', 16, 120, 1) 118 | display.show(full_update_flag) 119 | time.sleep(1) 120 | display.text('0----+----1----+-', 0, 88, 1) 121 | display.text('01234567890123456', 0, 96, 1) 122 | for i in range (16): 123 | p=display.pixel(i,96) 124 | # p=i // 10 125 | display.text(str(p),i*8,104) 126 | for j in range (4): 127 | display.pixel(i*8+j*2,114,1) 128 | 129 | 130 | display.show(full_update_flag) 131 | print('bilt test') 132 | smallbuffer=bytearray(8) 133 | letter=framebuf.FrameBuffer(smallbuffer,8,8,framebuf.MONO_HMSB) 134 | print('letter dir', dir(letter)) 135 | letter.text('K',0,0,1) 136 | display.blit(letter,0,56) 137 | display.show(full_update_flag) 138 | 139 | print('large text test') 140 | try: 141 | display.large_text('QuiteBIG', 0, 32, 2, 1) 142 | except: 143 | display.text('framebuf2', 0, 40 ,1) 144 | display.text('not loaded', 0, 48, 1) 145 | 146 | display.show(True) 147 | # sys.exit(0) 148 | time.sleep(2) 149 | 150 | print('contrast demo') 151 | print('free memory' , gc.mem_free()) 152 | for i in range (256): 153 | display.contrast(i) 154 | contrast_text='contrast '+str(i) 155 | display.fill_rect(16,64,96,8,0) 156 | display.text(contrast_text, 16, 64, 1) 157 | display.show(full_update_flag) 158 | time.sleep_ms(25) 159 | 160 | display.contrast(0x5F) 161 | 162 | print('scroll test (start line)') 163 | print('free memory' , gc.mem_free()) 164 | for i in range (128): 165 | display.display_start_line(i) 166 | time.sleep_ms(4) 167 | for i in range (128): 168 | display.display_start_line(127-i) 169 | time.sleep_ms(4) 170 | 171 | display.display_start_line(0) 172 | time.sleep_ms(500) 173 | 174 | print('free memory' , gc.mem_free()) 175 | 176 | 177 | print('invert test') 178 | display.flip() 179 | time.sleep(2) 180 | display.invert(1) 181 | time.sleep(2) 182 | display.invert(0) 183 | time.sleep(2) 184 | display.flip() 185 | time.sleep(2) 186 | display.invert(1) 187 | time.sleep(2) 188 | display.invert(0) 189 | time.sleep(1) 190 | display.fill(0) 191 | display.show(full_update_flag) 192 | 193 | 194 | print('large text test 3') 195 | try: 196 | display.large_text('BIGx3', 0, 0, 3, 1) 197 | except: 198 | display.text('large_text', 0, 0, 1) 199 | display.text('method from', 0, 8, 1) 200 | display.text('framebuf2', 0, 16 ,1) 201 | display.text('not loaded', 0, 24, 1) 202 | display.show(full_update_flag) 203 | 204 | print('large text test 4') 205 | try: 206 | display.large_text('HUGE', 0, 64, 4, 1) 207 | except: 208 | pass 209 | display.show(full_update_flag) 210 | 211 | time.sleep(2) 212 | display.fill(0) 213 | display.show() 214 | 215 | print('large text test 16') 216 | try: 217 | display.large_text('1', 0, 0, 16, 1) 218 | display.show() 219 | time.sleep(1) 220 | display.fill(0) 221 | display.large_text('2', 0, 0, 16, 1) 222 | display.show() 223 | time.sleep(1) 224 | display.fill(0) 225 | display.large_text('3', 0, 0, 16, 1) 226 | display.show() 227 | time.sleep(1) 228 | display.fill(0) 229 | display.large_text('!', 0, 0, 16, 1) 230 | display.show() 231 | time.sleep(1) 232 | display.invert(1) 233 | time.sleep(1) 234 | display.invert(0) 235 | time.sleep(1) 236 | display.invert(1) 237 | time.sleep(1) 238 | display.invert(0) 239 | time.sleep(1) 240 | except: 241 | pass 242 | 243 | time.sleep(1) 244 | 245 | for i in range(64): 246 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2) 247 | display.show() 248 | # time.sleep_ms(10) 249 | time.sleep(1) 250 | display.fill(0) 251 | display.show() 252 | 253 | for i in range(64): 254 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2, f=False) 255 | # display.sleep(i % 2) 256 | display.show() 257 | 258 | time.sleep(1) 259 | display.fill(0) 260 | display.show() 261 | 262 | for i in range(64): 263 | display.rect(i, i, 128-i*2, 128-i*2, c = i % 2, f=True) 264 | # display.sleep(i % 2) 265 | display.show() 266 | 267 | # time.sleep(1) 268 | # display.fill(0) 269 | # display.show() 270 | # 271 | # for i in range(64): 272 | # display.fill_rect(i, i, 128-i*2, 128-i*2, c = i % 2) 273 | # # display.sleep(i % 2) 274 | # display.show() 275 | 276 | # for i in range(64): 277 | # display.sleep(i % 2) 278 | 279 | time.sleep(2) 280 | display.fill(0) 281 | display.show() 282 | display.show() 283 | display.show() 284 | display.show() 285 | display.show() 286 | display.show() 287 | display.show() 288 | display.show() 289 | display.show() 290 | display.show() 291 | 292 | display.ellipse(64, 48, 56, 32, 1) 293 | display.show() 294 | time.sleep(1) 295 | display.ellipse(64, 48, 56, 32, 1, 1, 0xD) 296 | display.show() 297 | time.sleep(1) 298 | coords=array.array('h', [0,0,0,40,40,40 ]) 299 | print(coords) 300 | display.fill(0) 301 | display.poly(10,10,coords, 1,1) 302 | display.show() 303 | time.sleep(3) 304 | 305 | display.poweroff() 306 | 307 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MicroPython SH1107 display driver for 128x128 and 128x64 pixel displays - with large text, triangles and circles 2 | 3 | MicroPython driver for SH1107-based OLED displays with support for I2C and SPI connections. This driver works with 128x128 and 128x64 pixel displays. (For a list of tested displays, see: [tested displays](#tested-displays), below.) 4 | 5 | This driver was derived from the [SH1106 driver](https://github.com/robert-hh/SH1106) made by @robert-hh and others. It has been adapted for the SH1107 and incorporates several enhancements. 6 | 7 | The large text, triangle and circle methods in the MicroPython FrameBuffer extension [framebuf2](https://github.com/peter-l5/framebuf2) are supported, as are the ellipse and poly methods added to the FrameBuffer class in MicroPython version 1.20.0. 8 | 9 | Note: from version release v1.3.0 (build v317) which added support for 128x64 displays, the use of the rotate parameter has changed. Thus, for a previous setting of 90 (degrees), 0 should now be used and similarly for 0, 180, and 270, values of 270, 90 and 180 respectively should be used instead. 10 | 11 | ## Features and performance 12 | 13 | This driver offers **screen rotation**: the screen can be initialised at 0, 90, 180 or 270 degrees rotation. The rotation can be changed by 180 degrees after initialisation, but not by 90 degrees clock-wise or anti-clockwise. This is because 90 and 270 degrees use a different framebuffer mode and screen updating method which are set on initialisation. 14 | 15 | The driver includes some optimisation for partial screen updates which typically reduce the amount of data written to the screen and increase the speed of updates and display responsiveness. With an I2C connection at 400,000 bps a 128x128 display will achieve about 16 frames per second when orientated at 90 or 270 degrees and 10 frames per second at 0 or 180 degrees. Partial updates are faster, for example, 1 row of text can be updated in around 5 milliseconds (tested values using a Raspberry Pi pico at standard clock speed). Faster updates can be achieved by running the I2C connection at 1,000,000 bps (although this is faster than the rated speed for the SH1107).
16 | An SPI connection at 40 MHz can achieve full screen updates in around 5ms when orientated at 90 or 270 degrees and about 20ms at 0 or 180 degrees. Updates for 128x64 displays are faster. 17 | 18 | The driver builds in the facility to use the **`large_text()`**, **`triangle()`** and **`circle()`** methods in the MicroPython FrameBuffer extension [framebuf2](https://github.com/peter-l5/framebuf2). Moreover, some limited **hardware scrolling** functionality can be used with the `display_start_line()` method. 19 | 20 | ## Display connection 21 | 22 | ### I2C 23 | SCL and SDA have to be connected as minimum. The driver can reset the device with the reset PIN (this is not required for some displays, for example those tested). 24 | 25 | ### SPI 26 | SCLK, MOSI, D/C are always required. If the display is the only SPI device, CS may be tied to GND. Reset has also to be connected, unless it is driven by an external circuit. 27 | 28 | ## Usage 29 | 30 | The [sh1107.py module code](/sh1107.py) should be uploaded to the Raspberry Pico Pi (or other Microcontroller running MicroPython). The large font, triangle and circles extension is added by additionally uploading the `framebuf2.py` module code. (See [framebuf2](https://github.com/peter-l5/framebuf2).) 31 | 32 | ## Classes 33 | 34 | The module includes the class `SH1107` and the derived classes `SH1107_I2C` and `SH1107_SPI`. The I2C and SPI classes provide equivalent methods. 35 | 36 | ### I2C 37 | ``` 38 | display = sh1107.SH1107_I2C(width, 39 | height, 40 | i2c, 41 | res=None, 42 | address=0x3d, 43 | rotate=0, 44 | external_vcc=False, 45 | delay_ms=200) 46 | ``` 47 | - width (always 128) and height (128 or 64) define the size of the display 48 | - i2c is an I2C object, which has to be created beforehand, and sets the SDA and SCL pins 49 | - res is the optional GPIO Pin object for the reset connection 50 | - address is the I2C address of the display. Default value is 0x3d (or 61 in decimal) 51 | - rotate defines display content rotation in degrees (can be 0, 90, 180 or 270) 52 | - delay_ms sets a delay in milliseconds in the display power on sequence and wake from sleep 53 | (the SH1107 datasheet suggests a 100ms delay, in practice a 200ms seems more effective 54 | in reducing I2C communication errors) 55 | 56 | ### SPI 57 | ``` 58 | display = sh1107.SH1107_SPI(width, 59 | height, 60 | spi, 61 | dc, 62 | res=None, 63 | cs=None, 64 | rotate=0, 65 | external_vcc=False, 66 | delay_ms=100) 67 | ``` 68 | - width (always 128) and height (128 or 64) define the size of the display 69 | - spi is an SPI object, which has to be created beforehand, and sets the SCL and MOSI pins 70 | MISO is not used 71 | - dc is the GPIO Pin object for the Data/Command selection 72 | - res is the optional GPIO Pin object for the reset connection 73 | - cs is the optional GPIO Pin object for the CS connection 74 | - rotate defines display content rotation in degrees (can be 0, 90, 180 or 270) 75 | - delay_ms sets a delay in milliseconds in the display power on sequence and wake from sleep 76 | (the SH1107 datasheet suggests a 100ms delay) 77 | 78 | ## Methods and Properties 79 | 80 | The following methods and properties are available for controlling the display
81 | **`poweron()`**
82 | **`poweroff()`** - the display memory is retained in this state, power consumption is reduced to a <5uA for the display (other components on a board may increase this, of course)
83 | **`sleep(value)`** - `sleep(0)` calls `poweron()`; `sleep()` or `sleep(1)` calls `poweroff()`
84 | **`is_awake()`** this property returns the sleep (False) / wake (True) status of the display
85 | **`show(full_update=False)`** - this method updates the display from the framebuffer. It has some optimisation to to update only areas of the screen with changes. To force a complete update of the screen, set the optional `full_update` parameter to `True`
86 | **`contrast()`** - this command effectively sets the screen brightness. segment power consumption is proportional to screen contrast. valid values are in the range 0 to 255. the SH1107 default power on value is 128, however this module initialises the display with the contrast set to zero
87 | **`invert(invert)`** - this method inverts the display to black on white, instead of black on white. the parameter `invert` takes the values `True` or `False`
88 | **`flip(flag=None, update=True)`** - if no value is provided for the `flag` parameter the screen is rotated by 180 degrees from its current orientation, otherwise if the `flag` parameter is set to `True`, the screen rotation is set to 180 degrees, or 0 degrees for `False`. A full screen update is performed unless `update` is set to `False`
89 | **`display_start_line()`** - provides some limited scrolling
90 | 91 | ## FrameBuffer methods 92 | 93 | The driver works with all [MicroPython FrameBuffer drawing methods](https://docs.micropython.org/en/v1.20.0/library/framebuf.html "MicroPython FrameBuffer v1.20.0") (as at MicroPython 1.20.0). The syntax of the `fill_rect` method available in versions 1.19.1 and earlier (but not 1.20.0) is also supported. 94 | 95 | ### Example (I2C) 96 | ``` 97 | from machine import Pin, I2C 98 | import sh1107 99 | 100 | i2c0 = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) 101 | display = sh1107.SH1107_I2C(128, 128, i2c0, address=0x3d, rotate=90) 102 | display.sleep(False) 103 | display.fill(0) 104 | display.text('SH1107', 0, 0, 1) 105 | display.text('driver', 0, 8, 1) 106 | display.show() 107 | ``` 108 | See example code for further details and usage demonstrations of other methods. (The example code was written for 128x128 displays and has only been partially adapted for 64x128 displays.) 109 | 110 | ### Example (SPI) 111 | 112 | Example code for SPI is included in the repository. (The code was written for 128x128 displays 113 | and has only been partially adapted for 64x128 displays.) 114 | 115 | ## Tested displays 116 | 117 | This driver has been tested with a Raspberry Pi Pico and the displays listed below. It should work with other 128x128 and 128x64 size displays. (Whilst the code works with the tested displays, other 128x64 displays *might* need changes to some setup/control parameters, depending on how the display panel is connected to the driver IC.) 118 | 119 | ### 128x128 displays 120 | 121 | - [Adafruit 1.12 inch OLED](https://www.adafruit.com/product/5297 "Adafruit 1.12 inch OLED") (128x128 pixels, tested with I2C interface, no reset Pin needed in I2C mode) (at 3.3 volts) 122 | - [Pimoroni 1.12 inch OLED](https://shop.pimoroni.com/products/1-12-oled-breakout?variant=12628508704851 "Pimoroni 1.12 inch OLED") (128x128 pixels, SPI version tested) (with blocks of pixels lit, this display was found to provide more even brightness with a 5V rather than 3.3V supply) 123 | 124 | ### 128x64 displays 125 | 126 | - [Adafruit FeatherWing OLED - 128x64 OLED](https://www.adafruit.com/product/4650 "Adafruit FeatherWing OLED - 128x64 OLED") (I2C interface tested via STEMMA QT/ Qwiic connector) 127 | - [Waveshare 1.3 inch OLED Module (C)](https://www.waveshare.com/wiki/1.3inch_OLED_Module_(C) "Waveshare 1.3inch OLED Module (C)") (128x64 pixels, default SPI interface tested) 128 | 129 | ## Requirements 130 | 131 | This code has been tested with MicroPython versions 1.18, 1.19.1 and 1.20.0. 132 | 133 | The MicroPython FrameBuffer extension [framebuf2](https://github.com/peter-l5/framebuf2) is recommended for its large text, triangle and circle methods. 134 | 135 | ## Release notes 136 | 137 | #### release v1.3.2 (build 319) 138 | 139 | - fixes an error in the is_awake() property 140 | - parameterizes the power on / wake from sleep delay 141 | - adds additional SH1107 commands to the start-up sequence (all set to defaults) 142 | - amends the DC-DC converter setting to the SH1107 power on reset default of `0xad81` from `0xad8d` 143 | 144 | 145 | #### Release v1.3.0 (build 317) 146 | 147 | - adds support for 128x64 displays and the ellipse and poly methods in MicroPython 1.20.0 148 | - usage of the rotate parameter is amended (see above) 149 | 150 | #### Release v1.2.0 (build 311) 151 | 152 | - optimisation for the rotation feature of the large_text() method of the framebuf2 module added 153 | 154 | #### build 310 155 | 156 | - updated optimisations for the circle and triangle method of the framebuf2 module added 157 | - repaired a logic error in the sleep method 158 | 159 | #### Release v1.1.0 (build 305) 160 | 161 | - `is_awake` property added 162 | - fixed an issue where the negative co-ordinates for framebuffer methods could trip an error 163 | - fixed an issue where the SPI interface would re-initialise in `SH1107_SPI.write_command()` and `SH1107_SPI.write_data()` 164 | - amended the `fill_rect()` method for compatibility with the "latest" MicroPython release 165 | 166 | #### Release v1.0.0 (build 216) 167 | 168 | - code tidied up 169 | - annotations added for register values that would need changing for 128x64 displays 170 | 171 | #### build 210 172 | 173 | - added `set_start_line()` method which implements scrolling in one direction (whether x or y depends on display orientation) 174 | - added `_SET_DISPLAY_OFFSET` constant and included display offset command in start-up sequence. 175 | - added 100ms sleep after power on command in line with diagrams in the SH1107 datasheet 176 | - added page address = 0 command to start up sequence. This improves reliability of start-up 177 | -------------------------------------------------------------------------------- /sh1107.py: -------------------------------------------------------------------------------- 1 | # MicroPython SH1107 OLED driver, I2C interfaces 2 | # tested with Raspberry Pi Pico and adafruit 1.12 inch QWIC OLED display 3 | # sh1107 driver v319 4 | __version__ = "v319" 5 | __repo__ = "https://github.com/peter-l5/SH1107" 6 | # 7 | # The MIT License (MIT) 8 | # 9 | # Copyright (c) 2016 Radomir Dopieralski (@deshipu), 10 | # 2017-2021 Robert Hammelrath (@robert-hh) 11 | # 2021 Tim Weber (@scy) 12 | # 2022-2023 Peter Lumb (peter-l5) 13 | # 14 | # Permission is hereby granted, free of charge, to any person obtaining a copy 15 | # of this software and associated documentation files (the "Software"), to deal 16 | # in the Software without restriction, including without limitation the rights 17 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 18 | # copies of the Software, and to permit persons to whom the Software is 19 | # furnished to do so, subject to the following conditions: 20 | # 21 | # The above copyright notice and this permission notice shall be included in 22 | # all copies or substantial portions of the Software. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 30 | # THE SOFTWARE. 31 | # 32 | # 33 | # Sample code sections for RaspberryPi Pico pin assignments 34 | # ------------ SPI ------------------ 35 | # Pin Map SPI 36 | # - 3v3 - xxxxxx - Vcc 37 | # - G - xxxxxx - Gnd 38 | # - D7 - GPIO 15 - TX / MOSI fixed 39 | # - D5 - GPIO 14 - SCK / Sck fixed 40 | # - D8 - GPIO 13 - CS (optional, if the only connected device) 41 | # - D2 - GPIO 21 - DC [Data/Command] 42 | # - D1 - GPIO 20 - Res [reset] 43 | # 44 | # spi1 = SPI(1, baudrate=10_000_000, sck=Pin(14), mosi=Pin(15), miso=Pin(12)) 45 | # display = sh1107.SH1107_SPI(128, 128, spi1, Pin(21), Pin(20), Pin(13)) 46 | # display.sleep(False) 47 | # display.fill(0) 48 | # display.text('SH1107', 0, 0, 1) 49 | # display.text('driver', 0, 8, 1) 50 | # display.show() 51 | # 52 | # --------------- I2C ------------------ 53 | # 54 | # reset PIN is not needed in some implementations 55 | # Pin Map I2C 56 | # - 3v3 - xxxxxx - Vcc 57 | # - G - xxxxxx - Gnd 58 | # - D2 - GPIO 5 - SCK / SCL 59 | # - D1 - GPIO 4 - DIN / SDA 60 | # - D0 - GPIO 16 - Res 61 | # - G - xxxxxx CS 62 | # - G - xxxxxx D/C 63 | # 64 | # using hardware I2C 65 | 66 | # from machine import Pin, I2C 67 | # import sh1107 68 | # 69 | # i2c0 = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000) 70 | # display = sh1107.SH1107_I2C(128, 128, i2c0, address=0x3d, rotate=90) 71 | # display.sleep(False) 72 | # display.fill(0) 73 | # display.text('SH1107', 0, 0, 1) 74 | # display.text('driver', 0, 8, 1) 75 | # display.show() 76 | 77 | __version__ = "v317" 78 | __repo__ = "https://github.com/peter-l5/SH1107" 79 | 80 | ## SH1107 module code 81 | from micropython import const 82 | import time 83 | # import extended framebuffer if available) 84 | try: 85 | import framebuf2 as framebuf 86 | _fb_variant = 2 87 | except: 88 | import framebuf 89 | _fb_variant = 1 90 | print("SH1107: framebuf is ", ("standard" if _fb_variant ==1 else "extended") ) 91 | 92 | # a few register definitions with SH1107 data sheet reference numbers 93 | _LOW_COLUMN_ADDRESS = const(0x00) # 1. Set Column Address 4 lower bits (POR = 00H) 94 | _HIGH_COLUMN_ADDRESS = const(0x10) # 2. Set Column Address 4 higher bits (POR = 10H) 95 | _MEM_ADDRESSING_MODE = const(0x20) # 3. Set Memory addressing mode 96 | # 0x20 horizontal addressing; 0x21 vertical addressing 97 | _SET_CONTRAST = const(0x8100) # 4. Set Contrast Control (double byte command) 98 | _SET_SEGMENT_REMAP = const(0xa0) # 5. Set Segment Re-map: (A0H - A1H) 99 | _SET_MULTIPLEX_RATIO = const(0xA800) # 6. Set Multiplex Ratio: (Double Bytes Command) 100 | # duty = 1/64 [3f] or 128 [7f] (POR) 101 | _SET_NORMAL_INVERSE = const(0xa6) # 8. Set Normal/Reverse Display: (A6H -A7H) 102 | _SET_DISPLAY_OFFSET = const(0xD300) # 9. Set Display Offset: (Double Bytes Command) 103 | # second byte may need amending for some displays 104 | # some 128x64 displays (eg Adafruit feather wing 4650) 105 | # require 0xD360 106 | _SET_DC_DC_CONVERTER_SF = const(0xad81) # 10. Set DC-DC Setting (set charge pump enable) 107 | # Set DC-DC enable (a=0:disable; a=1:enable) 108 | # 0xad81 is POR value and may be needed for 128x64 displays 109 | _SET_DISPLAY_OFF = const(0xae) # 11. Display OFF/ON: (AEH - AFH) 110 | _SET_DISPLAY_ON = const(0xaf) # 11. Display OFF/ON: (AEH - AFH) 111 | _SET_PAGE_ADDRESS = const(0xB0) # 12. Set Page Address (using 4 low bits) 112 | _SET_SCAN_DIRECTION = const(0xC0) # 13. Set Common Output Scan Direction: (C0H - C8H) 113 | _SET_DISP_CLK_DIV = const(0xD550) # 14. Set the frequency of the internal display clocks (DCLKs). 114 | # 0x50 is the POR value 115 | _SET_DIS_PRECHARGE = const(0xD922) # 15. Set the duration of the pre-charge period. The interval is counted in number of DCLK 116 | # 0x22 is default POR value 117 | _SET_VCOM_DSEL_LEVEL = const(0xDB35) # 16. This command is to set the common pad output voltage level at deselect stage. 118 | # POR value 0x35 (0.77 * Vref) 119 | _SET_DISPLAY_START_LINE = const(0xDC00) # 17. Set Display Start Line (double byte command) 120 | 121 | 122 | class SH1107(framebuf.FrameBuffer): 123 | 124 | def __init__(self, width, height, external_vcc, delay_ms=200, rotate=0): 125 | self.width = width 126 | self.height = height 127 | self.external_vcc = external_vcc 128 | self.delay_ms = delay_ms 129 | self.flip_flag = False 130 | self.rotate90 = rotate == 90 or rotate == 270 131 | self.rotate = rotate 132 | self.inverse = False 133 | if self.rotate90: 134 | self.width, self.height = self.height, self.width 135 | self.pages = self.height // 8 136 | self.row_width = self.width // 8 137 | self.bufsize = self.pages * self.width 138 | self.displaybuf = bytearray(self.bufsize) 139 | self.displaybuf_mv = memoryview(self.displaybuf) 140 | self.pages_to_update = 0 141 | self._is_awake = False 142 | if self.rotate90: 143 | super().__init__(self.displaybuf, self.width, self.height, 144 | framebuf.MONO_VLSB) 145 | else: 146 | super().__init__(self.displaybuf, self.width, self.height, 147 | framebuf.MONO_HMSB) 148 | self.init_display() 149 | 150 | def init_display(self): 151 | multiplex_ratio = 0x7F if (self.height == 128) else 0x3F 152 | self.reset() 153 | self.poweroff() 154 | self.fill(0) 155 | self.write_command((_SET_MULTIPLEX_RATIO | multiplex_ratio).to_bytes(2,"big")) 156 | self.write_command((_MEM_ADDRESSING_MODE | (0x00 if self.rotate90 else 0x01)).to_bytes(1,"big")) 157 | self.write_command(_SET_PAGE_ADDRESS.to_bytes(1,"big")) # set page address to zero 158 | self.write_command(_SET_DC_DC_CONVERTER_SF.to_bytes(2,"big")) 159 | self.write_command(_SET_DISP_CLK_DIV.to_bytes(2,"big")) 160 | self.write_command(_SET_VCOM_DSEL_LEVEL.to_bytes(2,"big")) 161 | self.write_command(_SET_DIS_PRECHARGE.to_bytes(2,"big")) 162 | self.contrast(0) 163 | self.invert(0) 164 | # requires a call to flip() for setting up 165 | self.flip(self.flip_flag) 166 | self.poweron() 167 | 168 | def poweron(self): 169 | self.write_command(_SET_DISPLAY_ON.to_bytes(1,"big")) 170 | self._is_awake = True 171 | time.sleep_ms(self.delay_ms) # SH1107 datasheet recommends a delay in power on sequence 172 | 173 | def poweroff(self): 174 | self.write_command(_SET_DISPLAY_OFF.to_bytes(1,"big")) 175 | self._is_awake = False 176 | 177 | def sleep(self, value=True): 178 | if value == True: 179 | self.poweroff() 180 | else: 181 | self.poweron() 182 | 183 | @property 184 | def is_awake(self) -> bool: 185 | return self._is_awake 186 | 187 | def flip(self, flag=None, update=True): 188 | if flag is None: 189 | flag = not self.flip_flag 190 | if self.height == 128 and self.width == 128: 191 | row_offset = 0x00 192 | elif self.rotate90: 193 | row_offset = 0x60 194 | else: 195 | row_offset = 0x20 if (self.rotate == 180) ^ flag else 0x60 196 | remap = 0x00 if (self.rotate in (90, 180)) ^ flag else 0x01 197 | direction = 0x08 if (self.rotate in (180, 270)) ^ flag else 0x00 198 | self.write_command((_SET_DISPLAY_OFFSET | row_offset).to_bytes(2,"big")) 199 | self.write_command((_SET_SEGMENT_REMAP | remap ).to_bytes(1,"big") ) 200 | self.write_command((_SET_SCAN_DIRECTION | direction ).to_bytes(1,"big") ) 201 | self.flip_flag = flag 202 | if update: 203 | self.show(True) # full update 204 | 205 | def display_start_line(self, value): 206 | """ 207 | 17. Set Display Start Line:(Double Bytes Command) 208 | valid values are 0 (Power on /Reset) to 127 (x00-x7F) 209 | """ 210 | self.write_command((_SET_DISPLAY_START_LINE | (value & 0x7F)).to_bytes(2,"big")) 211 | 212 | def contrast(self, contrast): 213 | """ 214 | 4. contrast can be between 0 (low), 0x80 (POR) and 0xff (high) 215 | the segment current increases with higher values 216 | """ 217 | self.write_command((_SET_CONTRAST | (contrast & 0xFF)).to_bytes(2,"big")) 218 | 219 | def invert(self, invert=None): 220 | if invert == None: 221 | invert = not self.inverse 222 | self.write_command((_SET_NORMAL_INVERSE | (invert & 1)).to_bytes(1,"big")) 223 | self.inverse = invert 224 | 225 | def show(self, full_update: bool = False): 226 | # _start = time.ticks_us() 227 | (w, p, db_mv) = (self.width, self.pages, self.displaybuf_mv) 228 | current_page = 1 229 | if full_update: 230 | pages_to_update = (1 << p) - 1 231 | else: 232 | pages_to_update = self.pages_to_update 233 | if self.rotate90: 234 | buffer_3Bytes = bytearray(3) 235 | buffer_3Bytes[1] = _LOW_COLUMN_ADDRESS 236 | buffer_3Bytes[2] = _HIGH_COLUMN_ADDRESS 237 | for page in range(p): 238 | if pages_to_update & current_page: 239 | buffer_3Bytes[0] = _SET_PAGE_ADDRESS | page 240 | self.write_command(buffer_3Bytes) 241 | page_start = w * page 242 | self.write_data(db_mv[page_start : page_start + w]) 243 | current_page <<= 1 244 | else: 245 | row_bytes = w // 8 246 | buffer_2Bytes = bytearray(2) 247 | for start_row in range(0, p * 8, 8): 248 | if pages_to_update & current_page: 249 | for row in range(start_row, start_row + 8): 250 | buffer_2Bytes[0] = row & 0x0f # low column (low col. cmd is 0x00) 251 | buffer_2Bytes[1] = _HIGH_COLUMN_ADDRESS | (row >> 4) 252 | self.write_command(buffer_2Bytes) 253 | slice_start = row * row_bytes 254 | self.write_data(db_mv[slice_start : slice_start + row_bytes]) 255 | current_page <<= 1 256 | self.pages_to_update = 0 257 | # print("screen update used ", (time.ticks_us() - _start) / 1000, "ms") 258 | 259 | def pixel(self, x, y, c=None): 260 | if c is None: 261 | return super().pixel(x, y) 262 | else: 263 | super().pixel(x, y , c) 264 | page = y // 8 265 | self.pages_to_update |= 1 << page 266 | 267 | def text(self, text, x, y, c=1): 268 | super().text(text, x, y, c) 269 | self.register_updates(y, y + 7) 270 | 271 | def line(self, x0, y0, x1, y1, c): 272 | super().line(x0, y0, x1, y1, c) 273 | self.register_updates(y0, y1) 274 | 275 | def hline(self, x, y, w, c): 276 | super().hline(x, y, w, c) 277 | self.register_updates(y) 278 | 279 | def vline(self, x, y, h, c): 280 | super().vline(x, y, h, c) 281 | self.register_updates(y, y + h - 1) 282 | 283 | def fill(self, c): 284 | super().fill(c) 285 | self.pages_to_update = (1 << self.pages) - 1 286 | 287 | def blit(self, fbuf, x, y, key=-1, palette=None): 288 | super().blit(fbuf, x, y, key, palette) 289 | self.register_updates(y, y + self.height) 290 | 291 | def scroll(self, x, y): 292 | # my understanding is that scroll() does a full screen change 293 | super().scroll(x, y) 294 | self.pages_to_update = (1 << self.pages) - 1 295 | 296 | # rect() and fill_rect() amended to be compatible with new rect() method 297 | # from latest micropython as well as 1.20.0 and previous versions 298 | def fill_rect(self, x, y, w, h, c): 299 | try: 300 | super().fill_rect(x, y, w, h, c) 301 | except: 302 | super().rect(x, y, w, h, c, f=True) 303 | self.register_updates(y, y + h - 1) 304 | 305 | def rect(self, x, y, w, h, c, f=None): 306 | if f == None or f == False: 307 | super().rect(x, y, w, h, c) 308 | else: 309 | try: 310 | super().rect(x, y, w, h, c, f) 311 | except: 312 | super().fill_rect(x, y, w, h, c) 313 | self.register_updates(y, y + h - 1) 314 | 315 | def ellipse(self, x, y, xr, yr, c, *args, **kwargs): 316 | super().ellipse(x, y, xr, yr, c, *args, **kwargs) 317 | self.register_updates(y - yr, y + yr) 318 | 319 | def poly(self, *args, **kwargs): 320 | super().poly(*args, **kwargs) 321 | self.pages_to_update = (1 << self.pages) - 1 322 | 323 | # conditionally define optimisations for framebuf extension if loaded 324 | if _fb_variant == 2: 325 | def large_text(self, s, x, y, m, c=1, r=0, *args, **kwargs): 326 | try: 327 | super().large_text(s, x, y, m, c, r, *args, **kwargs) 328 | except: 329 | raise Exception("extended framebuffer v206+ required") 330 | h = (8 * m) * (1 if r is None or r % 360 // 90 in (0, 2) else len(s)) 331 | self.register_updates(y, y + h - 1) 332 | 333 | def circle(self, x, y, radius, c, f:bool = None): 334 | super().circle(x, y, radius, c, f) 335 | self.register_updates(y-radius, y+radius) 336 | 337 | def triangle(self, x0, y0, x1, y1, x2, y2, c, f: bool = None): 338 | super().triangle(x0, y0, x1, y1, x2, y2, c, f) 339 | self.register_updates(min(y0, y1, y2), max(y0, y1, y2)) 340 | 341 | def register_updates(self, y0, y1=None): 342 | y1 = min((y1 if y1 is not None else y0), self.height - 1) 343 | # this function takes the top and optional bottom address of the changes made 344 | # and updates the pages_to_change list with any changed pages 345 | # that are not yet on the list 346 | start_page = y0 // 8 347 | end_page = (y1 // 8) if (y1 is not None) else start_page 348 | # rearrange start_page and end_page if coordinates were given from bottom to top 349 | if start_page > end_page: 350 | start_page, end_page = end_page, start_page 351 | # ensure that start and end page values for update are non-negative (-ve is off-screen) 352 | if end_page >= 0: 353 | if start_page < 0: 354 | start_page = 0 355 | for page in range(start_page, end_page + 1): 356 | self.pages_to_update |= 1 << page 357 | 358 | def reset(self, res): 359 | if res is not None: 360 | res(1) 361 | time.sleep_ms(1) # sleep for 1 millisecond 362 | res(0) 363 | time.sleep_ms(20) # sleep for 20 milliseconds 364 | res(1) 365 | time.sleep_ms(20) # sleep for 20 milliseconds 366 | 367 | class SH1107_I2C(SH1107): 368 | def __init__(self, width, height, i2c, res=None, address=0x3d, 369 | rotate=0, external_vcc=False, delay_ms=200): 370 | self.i2c = i2c 371 | self.address = address 372 | self.res = res 373 | if res is not None: 374 | res.init(res.OUT, value=1) 375 | super().__init__(width, height, external_vcc, delay_ms, rotate) 376 | 377 | def write_command(self, command_list): 378 | self.i2c.writeto(self.address, b"\x00" + command_list) 379 | 380 | def write_data(self, buf): 381 | self.i2c.writevto(self.address, (b"\x40", buf)) 382 | 383 | def reset(self): 384 | super().reset(self.res) 385 | 386 | class SH1107_SPI(SH1107): 387 | def __init__(self, width, height, spi, dc, res=None, cs=None, 388 | rotate=0, external_vcc=False, delay_ms=0): 389 | dc.init(dc.OUT, value=0) 390 | if res is not None: 391 | res.init(res.OUT, value=0) 392 | if cs is not None: 393 | cs.init(cs.OUT, value=1) 394 | self.spi = spi 395 | self.dc = dc 396 | self.res = res 397 | self.cs = cs 398 | super().__init__(width, height, external_vcc, delay_ms, rotate) 399 | 400 | def write_command(self, cmd): 401 | if self.cs is not None: 402 | self.cs(1) 403 | self.dc(0) 404 | self.cs(0) 405 | self.spi.write(cmd) 406 | self.cs(1) 407 | else: 408 | self.dc(0) 409 | self.spi.write(cmd) 410 | 411 | def write_data(self, buf): 412 | if self.cs is not None: 413 | self.cs(1) 414 | self.dc(1) 415 | self.cs(0) 416 | self.spi.write(buf) 417 | self.cs(1) 418 | else: 419 | self.dc(1) 420 | self.spi.write(buf) 421 | 422 | def reset(self): 423 | super().reset(self.res) --------------------------------------------------------------------------------