1
0
mirror of https://github.com/hybridgroup/gobot.git synced 2025-04-29 13:49:14 +08:00
hybridgroup.gobot/drivers/i2c/ssd1306_driver.go

428 lines
12 KiB
Go

package i2c
import (
"fmt"
"image"
"gobot.io/x/gobot"
)
// register addresses for the ssd1306
const (
// default values
ssd1306Width = 128
ssd1306Height = 64
ssd1306ExternalVCC = false
ssd1306SetStartLine = 0x40
ssd1306I2CAddress = 0x3c
// fundamental commands
ssd1306SetComOutput0 = 0xC0
ssd1306SetComOutput1 = 0xC1
ssd1306SetComOutput2 = 0xC2
ssd1306SetComOutput3 = 0xC3
ssd1306SetComOutput4 = 0xC4
ssd1306SetComOutput5 = 0xC5
ssd1306SetComOutput6 = 0xC6
ssd1306SetComOutput7 = 0xC7
ssd1306SetComOutput8 = 0xC8
ssd1306SetContrast = 0x81
// scrolling commands
ssd1306ContinuousHScrollRight = 0x26
ssd1306ContinuousHScrollLeft = 0x27
ssd1306ContinuousVHScrollRight = 0x29
ssd1306ContinuousVHScrollLeft = 0x2A
ssd1306StopScroll = 0x2E
ssd1306StartScroll = 0x2F
// adressing settings commands
ssd1306SetMemoryAddressingMode = 0x20
ssd1306ColumnAddr = 0x21
ssd1306PageAddr = 0x22
// hardware configuration commands
ssd1306SetSegmentRemap0 = 0xA0
ssd1306SetSegmentRemap127 = 0xA1
ssd1306DisplayOnResumeToRAM = 0xA4
ssd1306SetDisplayNormal = 0xA6
ssd1306SetDisplayInverse = 0xA7
ssd1306SetDisplayOff = 0xAE
ssd1306SetDisplayOn = 0xAF
// timing and driving scheme commands
ssd1306SetDisplayClock = 0xD5
ssd1306SetPrechargePeriod = 0xD9
ssd1306SetVComDeselectLevel = 0xDB
ssd1306SetMultiplexRatio = 0xA8
ssd1306SetComPins = 0xDA
ssd1306SetDisplayOffset = 0xD3
// charge pump command
ssd1306ChargePumpSetting = 0x8D
)
// SSD1306Init contains the initialization settings for the ssd1306 display.
type SSD1306Init struct {
displayClock byte
multiplexRatio byte
displayOffset byte
startLine byte
chargePumpSetting byte
memoryAddressingMode byte
comPins byte
contrast byte
prechargePeriod byte
vComDeselectLevel byte
}
// GetSequence returns the initialization sequence for the ssd1306 display.
func (i *SSD1306Init) GetSequence() []byte {
return []byte{
ssd1306SetDisplayNormal,
ssd1306SetDisplayOff,
ssd1306SetDisplayClock, i.displayClock,
ssd1306SetMultiplexRatio, i.multiplexRatio,
ssd1306SetDisplayOffset, i.displayOffset,
ssd1306SetStartLine | i.startLine,
ssd1306ChargePumpSetting, i.chargePumpSetting,
ssd1306SetMemoryAddressingMode, i.memoryAddressingMode,
ssd1306SetSegmentRemap0,
ssd1306SetComOutput0,
ssd1306SetComPins, i.comPins,
ssd1306SetContrast, i.contrast,
ssd1306SetPrechargePeriod, i.prechargePeriod,
ssd1306SetVComDeselectLevel, i.vComDeselectLevel,
ssd1306DisplayOnResumeToRAM,
ssd1306SetDisplayNormal,
}
}
// 128x64 init sequence
var ssd1306Init128x64 = &SSD1306Init{
displayClock: 0x80,
multiplexRatio: 0x3F,
displayOffset: 0x00,
startLine: 0x00,
chargePumpSetting: 0x14, // 0x10 if external vcc is set
memoryAddressingMode: 0x00,
comPins: 0x12,
contrast: 0xCF, // 0x9F if external vcc is set
prechargePeriod: 0xF1, // 0x22 if external vcc is set
vComDeselectLevel: 0x40,
}
// 128x32 init sequence
var ssd1306Init128x32 = &SSD1306Init{
displayClock: 0x80,
multiplexRatio: 0x1F,
displayOffset: 0x00,
startLine: 0x00,
chargePumpSetting: 0x14, // 0x10 if external vcc is set
memoryAddressingMode: 0x00,
comPins: 0x02,
contrast: 0x8F, // 0x9F if external vcc is set
prechargePeriod: 0xF1, // 0x22 if external vcc is set
vComDeselectLevel: 0x40,
}
// 96x16 init sequence
var ssd1306Init96x16 = &SSD1306Init{
displayClock: 0x60,
multiplexRatio: 0x0F,
displayOffset: 0x00,
startLine: 0x00,
chargePumpSetting: 0x14, // 0x10 if external vcc is set
memoryAddressingMode: 0x00,
comPins: 0x02,
contrast: 0x8F, // 0x9F if external vcc is set
prechargePeriod: 0xF1, // 0x22 if external vcc is set
vComDeselectLevel: 0x40,
}
// DisplayBuffer represents the display buffer intermediate memory.
type DisplayBuffer struct {
width, height, pageSize int
buffer []byte
}
// NewDisplayBuffer creates a new DisplayBuffer.
func NewDisplayBuffer(width, height, pageSize int) *DisplayBuffer {
d := &DisplayBuffer{
width: width,
height: height,
pageSize: pageSize,
}
d.buffer = make([]byte, d.Size())
return d
}
// Size returns the memory size of the display buffer.
func (d *DisplayBuffer) Size() int {
return (d.width * d.height) / d.pageSize
}
// Clear the contents of the display buffer.
func (d *DisplayBuffer) Clear() {
d.buffer = make([]byte, d.Size())
}
// SetPixel sets the x, y pixel with c color.
func (d *DisplayBuffer) SetPixel(x, y, c int) {
idx := x + (y/d.pageSize)*d.width
bit := uint(y) % uint(d.pageSize)
if c == 0 {
d.buffer[idx] &= ^(1 << bit)
} else {
d.buffer[idx] |= (1 << bit)
}
}
// Set sets the display buffer with the given buffer.
func (d *DisplayBuffer) Set(buf []byte) {
d.buffer = buf
}
// SSD1306Driver is a Gobot Driver for a SSD1306 Display.
type SSD1306Driver struct {
name string
connector Connector
connection Connection
Config
gobot.Commander
initSequence *SSD1306Init
displayWidth int
displayHeight int
externalVCC bool
pageSize int
buffer *DisplayBuffer
}
// NewSSD1306Driver creates a new SSD1306Driver.
//
// Params:
// conn Connector - the Adaptor to use with this Driver
//
// Optional params:
// WithBus(int): bus to use with this driver
// WithAddress(int): address to use with this driver
// WithSSD1306DisplayWidth(int): width of display (defaults to 128)
// WithSSD1306DisplayHeight(int): height of display (defaults to 64)
// WithSSD1306ExternalVCC: set true when using an external OLED supply (defaults to false)
//
func NewSSD1306Driver(a Connector, options ...func(Config)) *SSD1306Driver {
s := &SSD1306Driver{
name: gobot.DefaultName("SSD1306"),
Commander: gobot.NewCommander(),
connector: a,
Config: NewConfig(),
displayHeight: ssd1306Height,
displayWidth: ssd1306Width,
externalVCC: ssd1306ExternalVCC,
}
// set options
for _, option := range options {
option(s)
}
// set page size
s.pageSize = 8
// set display buffer
s.buffer = NewDisplayBuffer(s.displayWidth, s.displayHeight, s.pageSize)
// add commands
s.AddCommand("Display", func(params map[string]interface{}) interface{} {
err := s.Display()
return map[string]interface{}{"err": err}
})
s.AddCommand("On", func(params map[string]interface{}) interface{} {
err := s.On()
return map[string]interface{}{"err": err}
})
s.AddCommand("Off", func(params map[string]interface{}) interface{} {
err := s.Off()
return map[string]interface{}{"err": err}
})
s.AddCommand("Clear", func(params map[string]interface{}) interface{} {
s.Clear()
return map[string]interface{}{}
})
s.AddCommand("SetContrast", func(params map[string]interface{}) interface{} {
contrast := byte(params["contrast"].(byte))
err := s.SetContrast(contrast)
return map[string]interface{}{"err": err}
})
s.AddCommand("Set", func(params map[string]interface{}) interface{} {
x := int(params["x"].(int))
y := int(params["y"].(int))
c := int(params["c"].(int))
s.Set(x, y, c)
return nil
})
return s
}
// Name returns the Name for the Driver.
func (s *SSD1306Driver) Name() string { return s.name }
// SetName sets the Name for the Driver.
func (s *SSD1306Driver) SetName(n string) { s.name = n }
// Connection returns the connection for the Driver.
func (s *SSD1306Driver) Connection() gobot.Connection { return s.connector.(gobot.Connection) }
// Start starts the Driver up, and writes start command
func (s *SSD1306Driver) Start() (err error) {
// check device size for supported resolutions
switch {
case s.displayWidth == 128 && s.displayHeight == 64:
s.initSequence = ssd1306Init128x64
case s.displayWidth == 128 && s.displayHeight == 32:
s.initSequence = ssd1306Init128x32
case s.displayWidth == 96 && s.displayHeight == 16:
s.initSequence = ssd1306Init96x16
default:
return fmt.Errorf("%dx%d resolution is unsupported, supported resolutions: 128x64, 128x32, 96x16", s.displayWidth, s.displayHeight)
}
// check for external vcc
if s.externalVCC {
s.initSequence.chargePumpSetting = 0x10
s.initSequence.contrast = 0x9F
s.initSequence.prechargePeriod = 0x22
}
bus := s.GetBusOrDefault(s.connector.GetDefaultBus())
address := s.GetAddressOrDefault(ssd1306I2CAddress)
s.connection, err = s.connector.GetConnection(address, bus)
if err != nil {
return err
}
if err = s.Init(); err != nil {
return err
}
if err = s.On(); err != nil {
return err
}
return nil
}
// Halt returns true if device is halted successfully
func (s *SSD1306Driver) Halt() (err error) { return nil }
// WithSSD1306DisplayWidth option sets the SSD1306Driver DisplayWidth option.
func WithSSD1306DisplayWidth(val int) func(Config) {
return func(c Config) {
d, ok := c.(*SSD1306Driver)
if ok {
d.displayWidth = val
}
}
}
// WithSSD1306DisplayHeight option sets the SSD1306Driver DisplayHeight option.
func WithSSD1306DisplayHeight(val int) func(Config) {
return func(c Config) {
d, ok := c.(*SSD1306Driver)
if ok {
d.displayHeight = val
}
}
}
// WithSSD1306ExternalVCC option sets the SSD1306Driver ExternalVCC option.
func WithSSD1306ExternalVCC(val bool) func(Config) {
return func(c Config) {
d, ok := c.(*SSD1306Driver)
if ok {
d.externalVCC = val
}
}
}
// Init initializes the ssd1306 display.
func (s *SSD1306Driver) Init() (err error) {
// turn off screen
if err = s.Off(); err != nil {
return err
}
// run through initialization commands
if err = s.commands(s.initSequence.GetSequence()); err != nil {
return err
}
if err = s.commands([]byte{ssd1306ColumnAddr, 0, byte(s.buffer.width) - 1}); err != nil {
return err
}
if err = s.commands([]byte{ssd1306PageAddr, 0, (byte(s.buffer.height / s.pageSize)) - 1}); err != nil {
return err
}
return nil
}
// On turns on the display.
func (s *SSD1306Driver) On() (err error) {
return s.command(ssd1306SetDisplayOn)
}
// Off turns off the display.
func (s *SSD1306Driver) Off() (err error) {
return s.command(ssd1306SetDisplayOff)
}
// Clear clears the display buffer.
func (s *SSD1306Driver) Clear() {
s.buffer.Clear()
}
// Set sets a pixel in the buffer.
func (s *SSD1306Driver) Set(x, y, c int) {
s.buffer.SetPixel(x, y, c)
}
// Reset clears display.
func (s *SSD1306Driver) Reset() (err error) {
if err = s.Off(); err != nil {
return err
}
s.Clear()
if err = s.On(); err != nil {
return err
}
return nil
}
// SetContrast sets the display contrast.
func (s *SSD1306Driver) SetContrast(contrast byte) (err error) {
err = s.commands([]byte{ssd1306SetContrast, contrast})
return
}
// Display sends the memory buffer to the display.
func (s *SSD1306Driver) Display() (err error) {
_, err = s.connection.Write(append([]byte{0x40}, s.buffer.buffer...))
return err
}
// ShowImage takes a standard Go image and displays it in monochrome.
func (s *SSD1306Driver) ShowImage(img image.Image) (err error) {
if img.Bounds().Dx() != s.displayWidth || img.Bounds().Dy() != s.displayHeight {
return fmt.Errorf("image must match display width and height: %dx%d", s.displayWidth, s.displayHeight)
}
s.Clear()
for y, w, h := 0, img.Bounds().Dx(), img.Bounds().Dy(); y < h; y++ {
for x := 0; x < w; x++ {
c := img.At(x, y)
if r, g, b, _ := c.RGBA(); r > 0 || g > 0 || b > 0 {
s.Set(x, y, 1)
}
}
}
return s.Display()
}
// command sends a command to the ssd1306
func (s *SSD1306Driver) command(b byte) (err error) {
_, err = s.connection.Write([]byte{0x80, b})
return err
}
// commands sends a command sequence to the ssd1306
func (s *SSD1306Driver) commands(commands []byte) (err error) {
var command []byte
for _, d := range commands {
command = append(command, []byte{0x80, d}...)
}
_, err = s.connection.Write(command)
return err
}