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() defer b.Close()
dev, err := ssd1306.NewI2C(b, 128, 64, false) dev, err := ssd1306.NewI2C(b, &ssd1306.DefaultOpts)
if err != nil { if err != nil {
log.Fatalf("failed to initialize ssd1306: %v", err) log.Fatalf("failed to initialize ssd1306: %v", err)
} }

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

@ -21,13 +21,13 @@ import (
func TestNewI2C_fail(t *testing.T) { func TestNewI2C_fail(t *testing.T) {
bus := i2ctest.Playback{DontPanic: true} 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) 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) 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) t.Fatal(d, err)
} }
if err := bus.Close(); err != nil { if err := bus.Close(); err != nil {
@ -37,7 +37,7 @@ func TestNewI2C_fail(t *testing.T) {
func TestI2C_ColorModel(t *testing.T) { func TestI2C_ColorModel(t *testing.T) {
bus := getI2CPlayback() bus := getI2CPlayback()
dev, err := NewI2C(bus, 128, 64, false) dev, err := NewI2C(bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -51,7 +51,7 @@ func TestI2C_ColorModel(t *testing.T) {
func TestI2C_String(t *testing.T) { func TestI2C_String(t *testing.T) {
bus := getI2CPlayback() bus := getI2CPlayback()
dev, err := NewI2C(bus, 128, 64, false) dev, err := NewI2C(bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -77,7 +77,7 @@ func TestI2C_Draw_VerticalLSD_fast(t *testing.T) {
{Addr: 0x3c, W: buf}, {Addr: 0x3c, W: buf},
}, },
} }
dev, err := NewI2C(&bus, 128, 64, false) dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -109,7 +109,7 @@ func TestI2C_Halt_Write(t *testing.T) {
{Addr: 0x3c, W: buf}, {Addr: 0x3c, W: buf},
}, },
} }
dev, err := NewI2C(&bus, 128, 64, false) dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -136,7 +136,7 @@ func TestI2C_Halt_resume_fail(t *testing.T) {
}, },
DontPanic: true, DontPanic: true,
} }
dev, err := NewI2C(&bus, 128, 64, false) dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -158,7 +158,7 @@ func TestI2C_Write_invalid_size(t *testing.T) {
{Addr: 0x3c, W: initCmdI2C()}, {Addr: 0x3c, W: initCmdI2C()},
}, },
} }
dev, err := NewI2C(&bus, 128, 64, false) dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -178,7 +178,7 @@ func TestI2C_Write_fail(t *testing.T) {
}, },
DontPanic: true, DontPanic: true,
} }
dev, err := NewI2C(&bus, 128, 64, false) dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -198,7 +198,7 @@ func TestI2C_Draw_fail(t *testing.T) {
}, },
DontPanic: true, DontPanic: true,
} }
dev, err := NewI2C(&bus, 128, 64, false) dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -220,7 +220,7 @@ func TestI2C_DrawGray(t *testing.T) {
{Addr: 0x3c, W: append([]byte{i2cData}, grayCheckboard()...)}, {Addr: 0x3c, W: append([]byte{i2cData}, grayCheckboard()...)},
}, },
} }
dev, err := NewI2C(&bus, 128, 64, false) dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -250,7 +250,7 @@ func TestI2C_Scroll(t *testing.T) {
{Addr: 0x3c, W: []byte{0x0, 0x2e}}, {Addr: 0x3c, W: []byte{0x0, 0x2e}},
}, },
} }
dev, err := NewI2C(&bus, 128, 64, false) dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -286,7 +286,7 @@ func TestI2C_SetContrast(t *testing.T) {
{Addr: 0x3c, W: []byte{0x0, 0x81, 0xff}}, {Addr: 0x3c, W: []byte{0x0, 0x81, 0xff}},
}, },
} }
dev, err := NewI2C(&bus, 128, 64, false) dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -316,7 +316,7 @@ func TestI2C_Invert_Halt_resume(t *testing.T) {
{Addr: 0x3c, W: []byte{0x0, 0xaf, 0xa6}}, {Addr: 0x3c, W: []byte{0x0, 0xaf, 0xa6}},
}, },
} }
dev, err := NewI2C(&bus, 128, 64, false) dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -345,7 +345,7 @@ func TestI2C_Halt(t *testing.T) {
}, },
DontPanic: true, DontPanic: true,
} }
dev, err := NewI2C(&bus, 128, 64, false) dev, err := NewI2C(&bus, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -363,23 +363,23 @@ func TestI2C_Halt(t *testing.T) {
// //
func TestNewSPI_fail(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) 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) 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) 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) t.Fatal(d, err)
} }
} }
func TestSPI_3wire(t *testing.T) { func TestSPI_3wire(t *testing.T) {
// Not supported yet. // 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") 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)}}, 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 { if err != nil {
t.Fatal(err) 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -450,7 +450,7 @@ func TestSPI_4wire_Write_differential_fail(t *testing.T) {
DontPanic: true, 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 { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
@ -475,7 +475,7 @@ func TestSPI_4wire_gpio_fail(t *testing.T) {
}, },
} }
pin := &failPin{fail: false} pin := &failPin{fail: false}
dev, err := NewSPI(&port, pin, 128, 64, false) dev, err := NewSPI(&port, pin, &DefaultOpts)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }

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

Loading…
Cancel
Save