gobot.io/x/gobot/v2@v2.1.0/drivers/i2c/ssd1306_driver.go (about) 1 package i2c 2 3 import ( 4 "fmt" 5 "image" 6 ) 7 8 const ssd1306DefaultAddress = 0x3c 9 10 // register addresses for the ssd1306 11 const ( 12 // default values 13 ssd1306Width = 128 14 ssd1306Height = 64 15 ssd1306ExternalVCC = false 16 ssd1306SetStartLine = 0x40 17 // fundamental commands 18 ssd1306SetComOutput0 = 0xC0 19 ssd1306SetComOutput1 = 0xC1 20 ssd1306SetComOutput2 = 0xC2 21 ssd1306SetComOutput3 = 0xC3 22 ssd1306SetComOutput4 = 0xC4 23 ssd1306SetComOutput5 = 0xC5 24 ssd1306SetComOutput6 = 0xC6 25 ssd1306SetComOutput7 = 0xC7 26 ssd1306SetComOutput8 = 0xC8 27 ssd1306SetContrast = 0x81 28 // scrolling commands 29 ssd1306ContinuousHScrollRight = 0x26 30 ssd1306ContinuousHScrollLeft = 0x27 31 ssd1306ContinuousVHScrollRight = 0x29 32 ssd1306ContinuousVHScrollLeft = 0x2A 33 ssd1306StopScroll = 0x2E 34 ssd1306StartScroll = 0x2F 35 // adressing settings commands 36 ssd1306SetMemoryAddressingMode = 0x20 37 ssd1306ColumnAddr = 0x21 38 ssd1306PageAddr = 0x22 39 // hardware configuration commands 40 ssd1306SetSegmentRemap0 = 0xA0 41 ssd1306SetSegmentRemap127 = 0xA1 42 ssd1306DisplayOnResumeToRAM = 0xA4 43 ssd1306SetDisplayNormal = 0xA6 44 ssd1306SetDisplayInverse = 0xA7 45 ssd1306SetDisplayOff = 0xAE 46 ssd1306SetDisplayOn = 0xAF 47 // timing and driving scheme commands 48 ssd1306SetDisplayClock = 0xD5 49 ssd1306SetPrechargePeriod = 0xD9 50 ssd1306SetVComDeselectLevel = 0xDB 51 ssd1306SetMultiplexRatio = 0xA8 52 ssd1306SetComPins = 0xDA 53 ssd1306SetDisplayOffset = 0xD3 54 // charge pump command 55 ssd1306ChargePumpSetting = 0x8D 56 ) 57 58 // SSD1306Init contains the initialization settings for the ssd1306 display. 59 type SSD1306Init struct { 60 displayClock byte 61 multiplexRatio byte 62 displayOffset byte 63 startLine byte 64 chargePumpSetting byte 65 memoryAddressingMode byte 66 comPins byte 67 contrast byte 68 prechargePeriod byte 69 vComDeselectLevel byte 70 } 71 72 // GetSequence returns the initialization sequence for the ssd1306 display. 73 func (i *SSD1306Init) GetSequence() []byte { 74 return []byte{ 75 ssd1306SetDisplayNormal, 76 ssd1306SetDisplayOff, 77 ssd1306SetDisplayClock, i.displayClock, 78 ssd1306SetMultiplexRatio, i.multiplexRatio, 79 ssd1306SetDisplayOffset, i.displayOffset, 80 ssd1306SetStartLine | i.startLine, 81 ssd1306ChargePumpSetting, i.chargePumpSetting, 82 ssd1306SetMemoryAddressingMode, i.memoryAddressingMode, 83 ssd1306SetSegmentRemap0, 84 ssd1306SetComOutput0, 85 ssd1306SetComPins, i.comPins, 86 ssd1306SetContrast, i.contrast, 87 ssd1306SetPrechargePeriod, i.prechargePeriod, 88 ssd1306SetVComDeselectLevel, i.vComDeselectLevel, 89 ssd1306DisplayOnResumeToRAM, 90 ssd1306SetDisplayNormal, 91 } 92 } 93 94 // 128x64 init sequence 95 var ssd1306Init128x64 = &SSD1306Init{ 96 displayClock: 0x80, 97 multiplexRatio: 0x3F, 98 displayOffset: 0x00, 99 startLine: 0x00, 100 chargePumpSetting: 0x14, // 0x10 if external vcc is set 101 memoryAddressingMode: 0x00, 102 comPins: 0x12, 103 contrast: 0xCF, // 0x9F if external vcc is set 104 prechargePeriod: 0xF1, // 0x22 if external vcc is set 105 vComDeselectLevel: 0x40, 106 } 107 108 // 128x32 init sequence 109 var ssd1306Init128x32 = &SSD1306Init{ 110 displayClock: 0x80, 111 multiplexRatio: 0x1F, 112 displayOffset: 0x00, 113 startLine: 0x00, 114 chargePumpSetting: 0x14, // 0x10 if external vcc is set 115 memoryAddressingMode: 0x00, 116 comPins: 0x02, 117 contrast: 0x8F, // 0x9F if external vcc is set 118 prechargePeriod: 0xF1, // 0x22 if external vcc is set 119 vComDeselectLevel: 0x40, 120 } 121 122 // 96x16 init sequence 123 var ssd1306Init96x16 = &SSD1306Init{ 124 displayClock: 0x60, 125 multiplexRatio: 0x0F, 126 displayOffset: 0x00, 127 startLine: 0x00, 128 chargePumpSetting: 0x14, // 0x10 if external vcc is set 129 memoryAddressingMode: 0x00, 130 comPins: 0x02, 131 contrast: 0x8F, // 0x9F if external vcc is set 132 prechargePeriod: 0xF1, // 0x22 if external vcc is set 133 vComDeselectLevel: 0x40, 134 } 135 136 // DisplayBuffer represents the display buffer intermediate memory. 137 type DisplayBuffer struct { 138 width, height, pageSize int 139 buffer []byte 140 } 141 142 // NewDisplayBuffer creates a new DisplayBuffer. 143 func NewDisplayBuffer(width, height, pageSize int) *DisplayBuffer { 144 d := &DisplayBuffer{ 145 width: width, 146 height: height, 147 pageSize: pageSize, 148 } 149 d.buffer = make([]byte, d.Size()) 150 return d 151 } 152 153 // Size returns the memory size of the display buffer. 154 func (d *DisplayBuffer) Size() int { 155 return (d.width * d.height) / d.pageSize 156 } 157 158 // Clear the contents of the display buffer. 159 func (d *DisplayBuffer) Clear() { 160 d.buffer = make([]byte, d.Size()) 161 } 162 163 // SetPixel sets the x, y pixel with c color. 164 func (d *DisplayBuffer) SetPixel(x, y, c int) { 165 idx := x + (y/d.pageSize)*d.width 166 bit := uint(y) % uint(d.pageSize) 167 if c == 0 { 168 d.buffer[idx] &= ^(1 << bit) 169 } else { 170 d.buffer[idx] |= (1 << bit) 171 } 172 } 173 174 // Set sets the display buffer with the given buffer. 175 func (d *DisplayBuffer) Set(buf []byte) { 176 d.buffer = buf 177 } 178 179 // SSD1306Driver is a Gobot Driver for a SSD1306 Display. 180 type SSD1306Driver struct { 181 *Driver 182 initSequence *SSD1306Init 183 displayWidth int 184 displayHeight int 185 externalVCC bool 186 pageSize int 187 buffer *DisplayBuffer 188 } 189 190 // NewSSD1306Driver creates a new SSD1306Driver. 191 // 192 // Params: 193 // c Connector - the Adaptor to use with this Driver 194 // 195 // Optional params: 196 // WithBus(int): bus to use with this driver 197 // WithAddress(int): address to use with this driver 198 // WithSSD1306DisplayWidth(int): width of display (defaults to 128) 199 // WithSSD1306DisplayHeight(int): height of display (defaults to 64) 200 // WithSSD1306ExternalVCC: set true when using an external OLED supply (defaults to false) 201 // 202 func NewSSD1306Driver(c Connector, options ...func(Config)) *SSD1306Driver { 203 s := &SSD1306Driver{ 204 Driver: NewDriver(c, "SSD1306", ssd1306DefaultAddress), 205 displayHeight: ssd1306Height, 206 displayWidth: ssd1306Width, 207 externalVCC: ssd1306ExternalVCC, 208 } 209 s.afterStart = s.initialize 210 211 // set options 212 for _, option := range options { 213 option(s) 214 } 215 // set page size 216 s.pageSize = 8 217 // set display buffer 218 s.buffer = NewDisplayBuffer(s.displayWidth, s.displayHeight, s.pageSize) 219 // add commands 220 s.AddCommand("Display", func(params map[string]interface{}) interface{} { 221 err := s.Display() 222 return map[string]interface{}{"err": err} 223 }) 224 s.AddCommand("On", func(params map[string]interface{}) interface{} { 225 err := s.On() 226 return map[string]interface{}{"err": err} 227 }) 228 s.AddCommand("Off", func(params map[string]interface{}) interface{} { 229 err := s.Off() 230 return map[string]interface{}{"err": err} 231 }) 232 s.AddCommand("Clear", func(params map[string]interface{}) interface{} { 233 s.Clear() 234 return map[string]interface{}{} 235 }) 236 s.AddCommand("SetContrast", func(params map[string]interface{}) interface{} { 237 contrast := byte(params["contrast"].(byte)) 238 err := s.SetContrast(contrast) 239 return map[string]interface{}{"err": err} 240 }) 241 s.AddCommand("Set", func(params map[string]interface{}) interface{} { 242 x := int(params["x"].(int)) 243 y := int(params["y"].(int)) 244 c := int(params["c"].(int)) 245 s.Set(x, y, c) 246 return nil 247 }) 248 return s 249 } 250 251 // WithSSD1306DisplayWidth option sets the SSD1306Driver DisplayWidth option. 252 func WithSSD1306DisplayWidth(val int) func(Config) { 253 return func(c Config) { 254 d, ok := c.(*SSD1306Driver) 255 if ok { 256 d.displayWidth = val 257 } 258 } 259 } 260 261 // WithSSD1306DisplayHeight option sets the SSD1306Driver DisplayHeight option. 262 func WithSSD1306DisplayHeight(val int) func(Config) { 263 return func(c Config) { 264 d, ok := c.(*SSD1306Driver) 265 if ok { 266 d.displayHeight = val 267 } 268 } 269 } 270 271 // WithSSD1306ExternalVCC option sets the SSD1306Driver ExternalVCC option. 272 func WithSSD1306ExternalVCC(val bool) func(Config) { 273 return func(c Config) { 274 d, ok := c.(*SSD1306Driver) 275 if ok { 276 d.externalVCC = val 277 } 278 } 279 } 280 281 // Init initializes the ssd1306 display. 282 func (s *SSD1306Driver) Init() (err error) { 283 // turn off screen 284 if err = s.Off(); err != nil { 285 return err 286 } 287 // run through initialization commands 288 if err = s.commands(s.initSequence.GetSequence()); err != nil { 289 return err 290 } 291 if err = s.commands([]byte{ssd1306ColumnAddr, 0, byte(s.buffer.width) - 1}); err != nil { 292 return err 293 } 294 if err = s.commands([]byte{ssd1306PageAddr, 0, (byte(s.buffer.height / s.pageSize)) - 1}); err != nil { 295 return err 296 } 297 return nil 298 } 299 300 // On turns on the display. 301 func (s *SSD1306Driver) On() (err error) { 302 return s.command(ssd1306SetDisplayOn) 303 } 304 305 // Off turns off the display. 306 func (s *SSD1306Driver) Off() (err error) { 307 return s.command(ssd1306SetDisplayOff) 308 } 309 310 // Clear clears the display buffer. 311 func (s *SSD1306Driver) Clear() { 312 s.buffer.Clear() 313 } 314 315 // Set sets a pixel in the buffer. 316 func (s *SSD1306Driver) Set(x, y, c int) { 317 s.buffer.SetPixel(x, y, c) 318 } 319 320 // Reset clears display. 321 func (s *SSD1306Driver) Reset() (err error) { 322 if err = s.Off(); err != nil { 323 return err 324 } 325 s.Clear() 326 if err = s.On(); err != nil { 327 return err 328 } 329 return nil 330 } 331 332 // SetContrast sets the display contrast. 333 func (s *SSD1306Driver) SetContrast(contrast byte) (err error) { 334 err = s.commands([]byte{ssd1306SetContrast, contrast}) 335 return 336 } 337 338 // Display sends the memory buffer to the display. 339 func (s *SSD1306Driver) Display() (err error) { 340 _, err = s.connection.Write(append([]byte{0x40}, s.buffer.buffer...)) 341 return err 342 } 343 344 // ShowImage takes a standard Go image and displays it in monochrome. 345 func (s *SSD1306Driver) ShowImage(img image.Image) (err error) { 346 if img.Bounds().Dx() != s.displayWidth || img.Bounds().Dy() != s.displayHeight { 347 return fmt.Errorf("image must match display width and height: %dx%d", s.displayWidth, s.displayHeight) 348 } 349 s.Clear() 350 for y, w, h := 0, img.Bounds().Dx(), img.Bounds().Dy(); y < h; y++ { 351 for x := 0; x < w; x++ { 352 c := img.At(x, y) 353 if r, g, b, _ := c.RGBA(); r > 0 || g > 0 || b > 0 { 354 s.Set(x, y, 1) 355 } 356 } 357 } 358 return s.Display() 359 } 360 361 // command sends a command to the ssd1306 362 func (s *SSD1306Driver) command(b byte) (err error) { 363 _, err = s.connection.Write([]byte{0x80, b}) 364 return err 365 } 366 367 // commands sends a command sequence to the ssd1306 368 func (s *SSD1306Driver) commands(commands []byte) (err error) { 369 var command []byte 370 for _, d := range commands { 371 command = append(command, []byte{0x80, d}...) 372 } 373 _, err = s.connection.Write(command) 374 return err 375 } 376 377 func (s *SSD1306Driver) initialize() (err error) { 378 // check device size for supported resolutions 379 switch { 380 case s.displayWidth == 128 && s.displayHeight == 64: 381 s.initSequence = ssd1306Init128x64 382 case s.displayWidth == 128 && s.displayHeight == 32: 383 s.initSequence = ssd1306Init128x32 384 case s.displayWidth == 96 && s.displayHeight == 16: 385 s.initSequence = ssd1306Init96x16 386 default: 387 return fmt.Errorf("%dx%d resolution is unsupported, supported resolutions: 128x64, 128x32, 96x16", 388 s.displayWidth, s.displayHeight) 389 } 390 // check for external vcc 391 if s.externalVCC { 392 s.initSequence.chargePumpSetting = 0x10 393 s.initSequence.contrast = 0x9F 394 s.initSequence.prechargePeriod = 0x22 395 } 396 if err = s.Init(); err != nil { 397 return err 398 } 399 if err = s.On(); err != nil { 400 return err 401 } 402 return nil 403 }