ssd1306: switch to use DefaultOpts pattern

pull/1/head
Marc-Antoine Ruel 8 years ago
parent ffc32d2881
commit 3e5b1a8345

@ -27,7 +27,7 @@ func Example() {
}
defer b.Close()
dev, err := ssd1306.NewI2C(b, 128, 64, false)
dev, err := ssd1306.NewI2C(b, &ssd1306.DefaultOpts)
if err != nil {
log.Fatalf("failed to initialize ssd1306: %v", err)
}

@ -77,11 +77,24 @@ const (
UpLeft Orientation = 0x2A
)
// DefaultOpts is the recommended default options.
var DefaultOpts = Opts{
W: 128,
H: 64,
Rotated: false,
}
// Opts defines the options for the device.
type Opts struct {
W int
H int
// Rotated determines if the display is rotated by 180°.
Rotated bool
}
// NewSPI returns a Dev object that communicates over SPI to a SSD1306 display
// controller.
//
// If rotated is true, turns the display by 180°
//
// The SSD1306 can operate at up to 3.3Mhz, which is much higher than I²C. This
// permits higher refresh rates.
//
@ -95,7 +108,7 @@ const (
// The RES (reset) pin can be used outside of this driver but is not supported
// natively. In case of external reset via the RES pin, this device drive must
// be reinstantiated.
func NewSPI(p spi.Port, dc gpio.PinOut, w, h int, rotated bool) (*Dev, error) {
func NewSPI(p spi.Port, dc gpio.PinOut, opts *Opts) (*Dev, error) {
if dc == gpio.INVALID {
return nil, errors.New("ssd1306: use nil for dc to use 3-wire mode, do not use gpio.INVALID")
}
@ -110,16 +123,14 @@ func NewSPI(p spi.Port, dc gpio.PinOut, w, h int, rotated bool) (*Dev, error) {
if err != nil {
return nil, err
}
return newDev(c, w, h, rotated, true, dc)
return newDev(c, opts, true, dc)
}
// NewI2C returns a Dev object that communicates over I²C to a SSD1306 display
// controller.
//
// If rotated, turns the display by 180°
func NewI2C(i i2c.Bus, w, h int, rotated bool) (*Dev, error) {
func NewI2C(i i2c.Bus, opts *Opts) (*Dev, error) {
// Maximum clock speed is 1/2.5µs = 400KHz.
return newDev(&i2c.Dev{Bus: i, Addr: 0x3C}, w, h, rotated, false, nil)
return newDev(&i2c.Dev{Bus: i, Addr: 0x3C}, opts, false, nil)
}
// Dev is an open handle to the display controller.
@ -286,30 +297,30 @@ func (d *Dev) Invert(blackOnWhite bool) error {
// newDev is the common initialization code that is independent of the
// communication protocol (I²C or SPI) being used.
func newDev(c conn.Conn, w, h int, rotated, usingSPI bool, dc gpio.PinOut) (*Dev, error) {
if w < 8 || w > 128 || w&7 != 0 {
return nil, fmt.Errorf("ssd1306: invalid width %d", w)
func newDev(c conn.Conn, opts *Opts, usingSPI bool, dc gpio.PinOut) (*Dev, error) {
if opts.W < 8 || opts.W > 128 || opts.W&7 != 0 {
return nil, fmt.Errorf("ssd1306: invalid width %d", opts.W)
}
if h < 8 || h > 64 || h&7 != 0 {
return nil, fmt.Errorf("ssd1306: invalid height %d", h)
if opts.H < 8 || opts.H > 64 || opts.H&7 != 0 {
return nil, fmt.Errorf("ssd1306: invalid height %d", opts.H)
}
nbPages := h / 8
pageSize := w
nbPages := opts.H / 8
pageSize := opts.W
d := &Dev{
c: c,
spi: usingSPI,
dc: dc,
rect: image.Rect(0, 0, int(w), int(h)),
rect: image.Rect(0, 0, opts.W, opts.H),
buffer: make([]byte, nbPages*pageSize),
startPage: 0,
endPage: nbPages,
startCol: 0,
endCol: w,
endCol: opts.W,
// Signal that the screen must be redrawn on first draw().
scrolled: true,
}
if err := d.sendCommand(getInitCmd(w, h, rotated)); err != nil {
if err := d.sendCommand(getInitCmd(opts.W, opts.H, opts.Rotated)); err != nil {
return nil, err
}
return d, nil

@ -21,13 +21,13 @@ import (
func TestNewI2C_fail(t *testing.T) {
bus := i2ctest.Playback{DontPanic: true}
if d, err := NewI2C(&bus, 0, 64, false); d != nil || err == nil {
if d, err := NewI2C(&bus, &Opts{H: 64}); d != nil || err == nil {
t.Fatal(d, err)
}
if d, err := NewI2C(&bus, 64, 0, false); d != nil || err == nil {
if d, err := NewI2C(&bus, &Opts{W: 64}); d != nil || err == nil {
t.Fatal(d, err)
}
if d, err := NewI2C(&bus, 64, 64, true); d != nil || err == nil {
if d, err := NewI2C(&bus, &Opts{W: 64, H: 64, Rotated: true}); d != nil || err == nil {
t.Fatal(d, err)
}
if err := bus.Close(); err != nil {
@ -37,7 +37,7 @@ func TestNewI2C_fail(t *testing.T) {
func TestI2C_ColorModel(t *testing.T) {
bus := getI2CPlayback()
dev, err := NewI2C(bus, 128, 64, false)
dev, err := NewI2C(bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -51,7 +51,7 @@ func TestI2C_ColorModel(t *testing.T) {
func TestI2C_String(t *testing.T) {
bus := getI2CPlayback()
dev, err := NewI2C(bus, 128, 64, false)
dev, err := NewI2C(bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -77,7 +77,7 @@ func TestI2C_Draw_VerticalLSD_fast(t *testing.T) {
{Addr: 0x3c, W: buf},
},
}
dev, err := NewI2C(&bus, 128, 64, false)
dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -109,7 +109,7 @@ func TestI2C_Halt_Write(t *testing.T) {
{Addr: 0x3c, W: buf},
},
}
dev, err := NewI2C(&bus, 128, 64, false)
dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -136,7 +136,7 @@ func TestI2C_Halt_resume_fail(t *testing.T) {
},
DontPanic: true,
}
dev, err := NewI2C(&bus, 128, 64, false)
dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -158,7 +158,7 @@ func TestI2C_Write_invalid_size(t *testing.T) {
{Addr: 0x3c, W: initCmdI2C()},
},
}
dev, err := NewI2C(&bus, 128, 64, false)
dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -178,7 +178,7 @@ func TestI2C_Write_fail(t *testing.T) {
},
DontPanic: true,
}
dev, err := NewI2C(&bus, 128, 64, false)
dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -198,7 +198,7 @@ func TestI2C_Draw_fail(t *testing.T) {
},
DontPanic: true,
}
dev, err := NewI2C(&bus, 128, 64, false)
dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -220,7 +220,7 @@ func TestI2C_DrawGray(t *testing.T) {
{Addr: 0x3c, W: append([]byte{i2cData}, grayCheckboard()...)},
},
}
dev, err := NewI2C(&bus, 128, 64, false)
dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -250,7 +250,7 @@ func TestI2C_Scroll(t *testing.T) {
{Addr: 0x3c, W: []byte{0x0, 0x2e}},
},
}
dev, err := NewI2C(&bus, 128, 64, false)
dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -286,7 +286,7 @@ func TestI2C_SetContrast(t *testing.T) {
{Addr: 0x3c, W: []byte{0x0, 0x81, 0xff}},
},
}
dev, err := NewI2C(&bus, 128, 64, false)
dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -316,7 +316,7 @@ func TestI2C_Invert_Halt_resume(t *testing.T) {
{Addr: 0x3c, W: []byte{0x0, 0xaf, 0xa6}},
},
}
dev, err := NewI2C(&bus, 128, 64, false)
dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -345,7 +345,7 @@ func TestI2C_Halt(t *testing.T) {
},
DontPanic: true,
}
dev, err := NewI2C(&bus, 128, 64, false)
dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -363,23 +363,23 @@ func TestI2C_Halt(t *testing.T) {
//
func TestNewSPI_fail(t *testing.T) {
if d, err := NewSPI(&spitest.Playback{}, nil, 0, 64, false); d != nil || err == nil {
if d, err := NewSPI(&spitest.Playback{}, nil, &Opts{H: 64}); d != nil || err == nil {
t.Fatal(d, err)
}
if d, err := NewSPI(&configFail{}, nil, 64, 64, false); d != nil || err == nil {
if d, err := NewSPI(&configFail{}, nil, &Opts{W: 64, H: 64}); d != nil || err == nil {
t.Fatal(d, err)
}
if d, err := NewSPI(&spitest.Playback{}, gpio.INVALID, 64, 64, false); d != nil || err == nil {
if d, err := NewSPI(&spitest.Playback{}, gpio.INVALID, &DefaultOpts); d != nil || err == nil {
t.Fatal(d, err)
}
if d, err := NewSPI(&spitest.Playback{}, &failPin{fail: true}, 64, 64, false); d != nil || err == nil {
if d, err := NewSPI(&spitest.Playback{}, &failPin{fail: true}, &DefaultOpts); d != nil || err == nil {
t.Fatal(d, err)
}
}
func TestSPI_3wire(t *testing.T) {
// Not supported yet.
if dev, err := NewSPI(&spitest.Playback{}, nil, 128, 64, false); dev != nil || err == nil {
if dev, err := NewSPI(&spitest.Playback{}, nil, &DefaultOpts); dev != nil || err == nil {
t.Fatal("SPI 3-wire is not supported")
}
}
@ -390,7 +390,7 @@ func TestSPI_4wire_String(t *testing.T) {
Ops: []conntest.IO{{W: getInitCmd(128, 64, false)}},
},
}
dev, err := NewSPI(&port, &gpiotest.Pin{N: "pin1", Num: 42}, 128, 64, false)
dev, err := NewSPI(&port, &gpiotest.Pin{N: "pin1", Num: 42}, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -420,7 +420,7 @@ func TestSPI_4wire_Write_differential(t *testing.T) {
},
},
}
dev, err := NewSPI(&port, &gpiotest.Pin{N: "pin1", Num: 42}, 128, 64, false)
dev, err := NewSPI(&port, &gpiotest.Pin{N: "pin1", Num: 42}, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -450,7 +450,7 @@ func TestSPI_4wire_Write_differential_fail(t *testing.T) {
DontPanic: true,
},
}
dev, err := NewSPI(&port, &gpiotest.Pin{N: "pin1", Num: 42}, 128, 64, false)
dev, err := NewSPI(&port, &gpiotest.Pin{N: "pin1", Num: 42}, &DefaultOpts)
if err != nil {
t.Fatal(err)
}
@ -475,7 +475,7 @@ func TestSPI_4wire_gpio_fail(t *testing.T) {
},
}
pin := &failPin{fail: false}
dev, err := NewSPI(&port, pin, 128, 64, false)
dev, err := NewSPI(&port, pin, &DefaultOpts)
if err != nil {
t.Fatal(err)
}

@ -94,13 +94,14 @@ func (s *SmokeTest) Run(f *flag.FlagSet, args []string) (err error) {
if len(*dcName) != 0 {
dc = gpioreg.ByName(*dcName)
}
opts := ssd1306.Opts{W: *w, H: *h, Rotated: *rotated}
if !*record {
return s.run(i2cBus, spiPort, dc, *w, *h, *rotated)
return s.run(i2cBus, spiPort, dc, &opts)
}
i2cRecorder := i2ctest.Record{Bus: i2cBus}
spiRecorder := spitest.Record{Port: spiPort}
err = s.run(&i2cRecorder, &spiRecorder, dc, *w, *h, *rotated)
err = s.run(&i2cRecorder, &spiRecorder, dc, &opts)
if len(i2cRecorder.Ops) != 0 {
fmt.Printf("I²C recorder Addr: 0x%02X\n", i2cRecorder.Ops[0].Addr)
} else {
@ -151,16 +152,16 @@ func (s *SmokeTest) Run(f *flag.FlagSet, args []string) (err error) {
return err
}
func (s *SmokeTest) run(i2cBus i2c.Bus, spiPort spi.PortCloser, dc gpio.PinOut, w, h int, rotated bool) (err error) {
func (s *SmokeTest) run(i2cBus i2c.Bus, spiPort spi.PortCloser, dc gpio.PinOut, opts *ssd1306.Opts) (err error) {
s.timings = make([]time.Duration, 2)
start := time.Now()
i2cDev, err2 := ssd1306.NewI2C(i2cBus, w, h, rotated)
i2cDev, err2 := ssd1306.NewI2C(i2cBus, opts)
s.timings[0] = time.Since(start)
if err2 != nil {
return err2
}
start = time.Now()
spiDev, err2 := ssd1306.NewSPI(spiPort, dc, w, h, rotated)
spiDev, err2 := ssd1306.NewSPI(spiPort, dc, opts)
s.timings[1] = time.Since(start)
if err2 != nil {
return err2
@ -181,8 +182,8 @@ func (s *SmokeTest) run(i2cBus i2c.Bus, spiPort spi.PortCloser, dc gpio.PinOut,
// Right format, right size
imgBunny1bitLarge := image1bit.NewVerticalLSB(i2cDev.Bounds())
center := imgBunny1bit.Bounds()
draw.Src.Draw(imgBunny1bitLarge, center.Add(image.Point{X: (w - center.Dx()) / 2}), imgBunny1bit, image.Point{})
imgClear := make([]byte, w*h/8)
draw.Src.Draw(imgBunny1bitLarge, center.Add(image.Point{X: (opts.W - center.Dx()) / 2}), imgBunny1bit, image.Point{})
imgClear := make([]byte, opts.W*opts.H/8)
for i, d := range s.devices {
start := time.Now()
@ -332,7 +333,7 @@ func (s *SmokeTest) run(i2cBus i2c.Bus, spiPort spi.PortCloser, dc gpio.PinOut,
}
s.step("Restore")
imgStripes := broadStripes(w, h)
imgStripes := broadStripes(opts.W, opts.H)
for i, d := range s.devices {
start := time.Now()
if _, err := d.Write(imgStripes); err != nil {
@ -378,7 +379,7 @@ func (s *SmokeTest) run(i2cBus i2c.Bus, spiPort spi.PortCloser, dc gpio.PinOut,
}
s.printStr("Clear (redundant)")
imgPattern := binaryPattern(w, h)
imgPattern := binaryPattern(opts.W, opts.H)
for i, d := range s.devices {
start := time.Now()
if _, err := d.Write(imgPattern); err != nil {
@ -388,7 +389,7 @@ func (s *SmokeTest) run(i2cBus i2c.Bus, spiPort spi.PortCloser, dc gpio.PinOut,
}
s.step("Fill display with binary 0..255 pattern")
imgPattern[w+h/2] ^= 0x10
imgPattern[opts.W+opts.H/2] ^= 0x10
for i, d := range s.devices {
start := time.Now()
if _, err := d.Write(imgPattern); err != nil {

Loading…
Cancel
Save