mirror of https://github.com/kidoman/embd
237 lines
5.8 KiB
Go
237 lines
5.8 KiB
Go
/*
|
|
Package ssd1306 allows controlling an SSD1306 OLED controller.
|
|
|
|
This currently supports only write-only operations and a SPI connection to the controller.
|
|
|
|
Resources
|
|
|
|
This library is based on these prior implementations:
|
|
https://github.com/adafruit/Adafruit_Python_SSD1306/blob/master/Adafruit_SSD1306/SSD1306.py
|
|
https://github.com/kakaryan/i2cssd1306/blob/master/ssd1306.go
|
|
|
|
Datasheet
|
|
https://cdn-shop.adafruit.com/datasheets/SSD1306.pdf
|
|
*/
|
|
package ssd1306
|
|
|
|
import (
|
|
"github.com/golang/glog"
|
|
"github.com/kidoman/embd"
|
|
"time"
|
|
)
|
|
|
|
const (
|
|
SSD1306_I2C_ADDRESS = 0x3C
|
|
SSD1306_SETCONTRAST = 0x81
|
|
SSD1306_DISPLAYALLON_RESUME = 0xA4
|
|
SSD1306_DISPLAYALLON = 0xA5
|
|
SSD1306_NORMALDISPLAY = 0xA6
|
|
SSD1306_INVERTDISPLAY = 0xA7
|
|
SSD1306_DISPLAYOFF = 0xAE
|
|
SSD1306_DISPLAYON = 0xAF
|
|
SSD1306_SETDISPLAYOFFSET = 0xD3
|
|
SSD1306_SETCOMPINS = 0xDA
|
|
SSD1306_SETVCOMDETECT = 0xDB
|
|
SSD1306_SETDISPLAYCLOCKDIV = 0xD5
|
|
SSD1306_SETPRECHARGE = 0xD9
|
|
SSD1306_SETMULTIPLEX = 0xA8
|
|
SSD1306_SETLOWCOLUMN = 0x00
|
|
SSD1306_SETHIGHCOLUMN = 0x10
|
|
SSD1306_SETSTARTLINE = 0x40
|
|
SSD1306_MEMORYMODE = 0x20
|
|
SSD1306_MEMORYMODE_HORIZ = 0x00
|
|
SSD1306_COLUMNADDR = 0x21
|
|
SSD1306_PAGEADDR = 0x22
|
|
SSD1306_COMSCANINC = 0xC0
|
|
SSD1306_COMSCANDEC = 0xC8
|
|
SSD1306_SEGREMAP = 0xA0
|
|
SSD1306_CHARGEPUMP = 0x8D
|
|
SSD1306_EXTERNALVCC = 0x1
|
|
SSD1306_SWITCHCAPVCC = 0x2
|
|
|
|
SSD1306_ACTIVATE_SCROLL = 0x2F
|
|
SSD1306_DEACTIVATE_SCROLL = 0x2E
|
|
SSD1306_SET_VERTICAL_SCROLL_AREA = 0xA3
|
|
SSD1306_RIGHT_HORIZONTAL_SCROLL = 0x26
|
|
SSD1306_LEFT_HORIZONTAL_SCROLL = 0x27
|
|
SSD1306_VERTICAL_AND_RIGHT_HORIZONTAL_SCROLL = 0x29
|
|
SSD1306_VERTICAL_AND_LEFT_HORIZONTAL_SCROLL = 0x2A
|
|
)
|
|
|
|
const (
|
|
memoryMode = SSD1306_MEMORYMODE_HORIZ
|
|
)
|
|
|
|
// SSD1306 represents an instance of an SSD1306 OLED controller.
|
|
type SSD1306 struct {
|
|
spiBus embd.SPIBus
|
|
dcPin embd.DigitalPin
|
|
resetPin embd.DigitalPin
|
|
vccState byte
|
|
width uint
|
|
height uint
|
|
pages uint
|
|
}
|
|
|
|
// NewSPI creates a new SSD1306 controller connected via the given SPIBus.
|
|
// The GPIO digital output pins that are connected to DC and Rst must also be provided.
|
|
// Finally the width x height of the OLED must be given where the width is usually 128 and height is either 32 or 64.
|
|
func NewSPI(spiBus embd.SPIBus, dcPin, resetPin embd.DigitalPin, width, height uint) (*SSD1306, error) {
|
|
controller := &SSD1306{
|
|
spiBus: spiBus,
|
|
dcPin: dcPin,
|
|
resetPin: resetPin,
|
|
vccState: SSD1306_SWITCHCAPVCC,
|
|
width: width,
|
|
height: height,
|
|
pages: height / 8,
|
|
}
|
|
|
|
err := controller.reset()
|
|
if err != nil {
|
|
glog.Errorf("ssd1306: failed to reset: %s", err)
|
|
return nil, err
|
|
}
|
|
err = controller.init()
|
|
if err != nil {
|
|
glog.Errorf("ssd1306: failed to init: %s", err)
|
|
return nil, err
|
|
}
|
|
|
|
return controller, nil
|
|
}
|
|
|
|
func (c *SSD1306) reset() error {
|
|
|
|
if err := c.resetPin.Write(embd.High); err != nil {
|
|
return err
|
|
}
|
|
time.Sleep(1 * time.Millisecond)
|
|
|
|
if err := c.resetPin.Write(embd.Low); err != nil {
|
|
return err
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
|
|
if err := c.resetPin.Write(embd.High); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *SSD1306) init() error {
|
|
|
|
if err := c.command(SSD1306_DISPLAYOFF); err != nil {
|
|
return err
|
|
}
|
|
if err := c.command(SSD1306_SETDISPLAYCLOCKDIV, 0x80); err != nil {
|
|
return err
|
|
}
|
|
if err := c.command(SSD1306_SETMULTIPLEX, 0x3F); err != nil {
|
|
return err
|
|
}
|
|
if err := c.command(SSD1306_SETDISPLAYOFFSET, 0x0); err != nil {
|
|
return err
|
|
}
|
|
if err := c.command(SSD1306_SETSTARTLINE | 0x0); err != nil {
|
|
return err
|
|
}
|
|
if c.vccState == SSD1306_EXTERNALVCC {
|
|
if err := c.command(SSD1306_CHARGEPUMP, 0x10); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err := c.command(SSD1306_CHARGEPUMP, 0x14); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := c.command(SSD1306_MEMORYMODE, memoryMode); err != nil {
|
|
return err
|
|
}
|
|
if err := c.command(SSD1306_SEGREMAP | 0x1); err != nil {
|
|
return err
|
|
}
|
|
if err := c.command(SSD1306_COMSCANDEC); err != nil {
|
|
return err
|
|
}
|
|
if err := c.command(SSD1306_SETCOMPINS, 0x12); err != nil {
|
|
return err
|
|
}
|
|
if c.vccState == SSD1306_EXTERNALVCC {
|
|
if err := c.command(SSD1306_SETCONTRAST, 0x9F); err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err := c.command(SSD1306_SETCONTRAST, 0xCF); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if c.vccState == SSD1306_EXTERNALVCC {
|
|
if err := c.command(SSD1306_SETPRECHARGE, 0x22); err != nil {
|
|
return err
|
|
}
|
|
|
|
} else {
|
|
if err := c.command(SSD1306_SETPRECHARGE, 0xF1); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := c.command(SSD1306_SETVCOMDETECT, 0x40); err != nil {
|
|
return err
|
|
}
|
|
if err := c.command(SSD1306_DISPLAYALLON_RESUME); err != nil {
|
|
return err
|
|
}
|
|
if err := c.command(SSD1306_NORMALDISPLAY); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := c.command(SSD1306_DISPLAYON); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *SSD1306) command(cmd ...byte) error {
|
|
c.dcPin.Write(embd.Low)
|
|
_, err := c.spiBus.Write(cmd)
|
|
|
|
return err
|
|
}
|
|
|
|
func (c *SSD1306) data(d ...byte) error {
|
|
c.dcPin.Write(embd.High)
|
|
_, err := c.spiBus.Write(d)
|
|
|
|
return err
|
|
}
|
|
|
|
// Display sends the given buffer to the controller to "rendered"
|
|
func (c *SSD1306) Display(buf Buffer) error {
|
|
if err := c.command(SSD1306_COLUMNADDR, 0, byte(c.width-1)); err != nil {
|
|
return err
|
|
}
|
|
if err := c.command(SSD1306_PAGEADDR, 0, byte(c.pages-1)); err != nil {
|
|
return err
|
|
}
|
|
return c.data(buf.Cells()...)
|
|
}
|
|
|
|
// Close turns the display off
|
|
func (c *SSD1306) Close() error {
|
|
if err := c.command(SSD1306_DISPLAYOFF); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// NewBuffer creates a buffer that is suitably configured to be used in Display calls.
|
|
func (c *SSD1306) NewBuffer() Buffer {
|
|
return newBuffer(c.width, c.pages, memoryMode)
|
|
}
|