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  }