ssd1306: add options to customize the hardware pin layout (#282)

Fixes #166
pull/1/head
David Sansome 8 years ago committed by M-A
parent 97f5f4fe5d
commit 5d01024987

@ -57,6 +57,8 @@ var DefaultOpts = Opts{
W: 128,
H: 64,
Rotated: false,
Sequential: false,
SwapTopBottom: false,
}
// Opts defines the options for the device.
@ -65,6 +67,14 @@ type Opts struct {
H int
// Rotated determines if the display is rotated by 180°.
Rotated bool
// Sequential corresponds to the Sequential/Alternative COM pin configuration
// in the OLED panel hardware. Try toggling this if half the rows appear to be
// missing on your display.
Sequential bool
// SwapTopBottom corresponds to the Left/Right remap COM pin configuration in
// the OLED panel hardware. Try toggling this if the top and bottom halves of
// your display are swapped.
SwapTopBottom bool
}
// NewSPI returns a Dev object that communicates over SPI to a SSD1306 display
@ -287,22 +297,30 @@ func newDev(c conn.Conn, opts *Opts, usingSPI bool, dc gpio.PinOut) (*Dev, error
// Signal that the screen must be redrawn on first draw().
scrolled: true,
}
if err := d.sendCommand(getInitCmd(opts.W, opts.H, opts.Rotated)); err != nil {
if err := d.sendCommand(getInitCmd(opts)); err != nil {
return nil, err
}
return d, nil
}
func getInitCmd(w, h int, rotated bool) []byte {
func getInitCmd(opts *Opts) []byte {
// Set COM output scan direction; C0 means normal; C8 means reversed
comScan := byte(0xC8)
// See page 40.
columnAddr := byte(0xA1)
if rotated {
if opts.Rotated {
// Change order both horizontally and vertically.
comScan = 0xC0
columnAddr = byte(0xA0)
}
// See page 40.
hwLayout := byte(0x02)
if !opts.Sequential {
hwLayout |= 0x10
}
if opts.SwapTopBottom {
hwLayout |= 0x20
}
// Set the max frequency. The problem with I²C is that it creates visible
// tear down. On SPI at high speed this is not visible. Page 23 pictures how
// to avoid tear down. For now default to max frequency.
@ -317,7 +335,7 @@ func getInitCmd(w, h int, rotated bool) []byte {
0x40, // Start display start line; 0
columnAddr, // Set segment remap; RESET is column 127.
comScan, //
0xDA, 0x12, // Set COM pins hardware configuration; see page 40
0xDA, hwLayout, // Set COM pins hardware configuration; see page 40
0x81, 0xFF, // Set max contrast
0xA4, // Set display to use GDDRAM content
0xA6, // Set normal display (0xA7 for inverted 0=lit, 1=dark)
@ -326,10 +344,10 @@ func getInitCmd(w, h int, rotated bool) []byte {
0xD9, 0xF1, // Set pre-charge period; from adafruit driver
0xDB, 0x40, // Set Vcomh deselect level; page 32
0x2E, // Deactivate scroll
0xA8, byte(h - 1), // Set multiplex ratio (number of lines to display)
0xA8, byte(opts.H - 1), // Set multiplex ratio (number of lines to display)
0x20, 0x00, // Set memory addressing mode to horizontal
0x21, 0, uint8(w - 1), // Set column address (Width)
0x22, 0, uint8(h/8 - 1), // Set page address (Pages)
0x21, 0, uint8(opts.W - 1), // Set column address (Width)
0x22, 0, uint8(opts.H/8 - 1), // Set page address (Pages)
0xAF, // Display on
}
}

@ -5,6 +5,7 @@
package ssd1306
import (
"bytes"
"errors"
"image"
"image/color"
@ -384,7 +385,7 @@ func TestSPI_3wire(t *testing.T) {
func TestSPI_4wire_String(t *testing.T) {
port := spitest.Playback{
Playback: conntest.Playback{
Ops: []conntest.IO{{W: getInitCmd(128, 64, false)}},
Ops: []conntest.IO{{W: getInitCmd(&Opts{W: 128, H: 64, Rotated: false})}},
},
}
dev, err := NewSPI(&port, &gpiotest.Pin{N: "pin1", Num: 42}, &DefaultOpts)
@ -409,7 +410,7 @@ func TestSPI_4wire_Write_differential(t *testing.T) {
port := spitest.Playback{
Playback: conntest.Playback{
Ops: []conntest.IO{
{W: getInitCmd(128, 64, false)},
{W: getInitCmd(&Opts{W: 128, H: 64, Rotated: false})},
{W: buf1},
// Reset to write only to the first page.
{W: []byte{0x21, 0x0, 0x7f, 0x22, 0x1, 0x1}},
@ -441,7 +442,7 @@ func TestSPI_4wire_Write_differential_fail(t *testing.T) {
port := spitest.Playback{
Playback: conntest.Playback{
Ops: []conntest.IO{
{W: getInitCmd(128, 64, false)},
{W: getInitCmd(&Opts{W: 128, H: 64, Rotated: false})},
{W: buf1},
},
DontPanic: true,
@ -468,7 +469,7 @@ func TestSPI_4wire_Write_differential_fail(t *testing.T) {
func TestSPI_4wire_gpio_fail(t *testing.T) {
port := spitest.Playback{
Playback: conntest.Playback{
Ops: []conntest.IO{{W: getInitCmd(128, 64, false)}},
Ops: []conntest.IO{{W: getInitCmd(&Opts{W: 128, H: 64, Rotated: false})}},
},
}
pin := &failPin{fail: false}
@ -489,10 +490,29 @@ func TestSPI_4wire_gpio_fail(t *testing.T) {
}
}
func TestInitCmd(t *testing.T) {
tests := []struct {
opts *Opts
wantSubslice []byte
}{
{opts: &Opts{W: 128, H: 64}, wantSubslice: []byte{0xDA, 0x12}},
{opts: &Opts{W: 128, H: 64, Sequential: true}, wantSubslice: []byte{0xDA, 0x02}},
{opts: &Opts{W: 128, H: 64, SwapTopBottom: true}, wantSubslice: []byte{0xDA, 0x32}},
{opts: &Opts{W: 128, H: 64, Sequential: true, SwapTopBottom: true}, wantSubslice: []byte{0xDA, 0x22}},
}
for _, test := range tests {
got := getInitCmd(test.opts)
if !bytes.Contains(got, test.wantSubslice) {
t.Errorf("getInitCmd(%v) -> %v, want %v", test.opts, got, test.wantSubslice)
}
}
}
//
func initCmdI2C() []byte {
return append([]byte{0}, getInitCmd(128, 64, false)...)
return append([]byte{0}, getInitCmd(&Opts{W: 128, H: 64, Rotated: false})...)
}
var preludeI2C = []byte{

Loading…
Cancel
Save