spi: Refactor Conn and ConnCloser into Conn, Port and PortCloser (#131)

This clarifies the difference on ownership and makes it clear that DevParams()
is essentially a 'connection factory'.

The flow is:
  Bus -> Port -> Conn

Doing this caught inconsistencies in the unit tests. Made the structs in spitest
enforce the fact that DevParams() can only be called once.

In a follow up, I'll rename a lot of 'bus' to 'port' to be more consistent but
punting to not make this change unreviewable.
pull/1/head
M-A 9 years ago committed by GitHub
parent 61c1b2c139
commit f33f0732fb

@ -293,8 +293,9 @@ func (d *Dev) Halt() error {
// As per APA102-C spec, the chip's max refresh rate is 400hz. // As per APA102-C spec, the chip's max refresh rate is 400hz.
// https://en.wikipedia.org/wiki/Flicker_fusion_threshold is a recommended // https://en.wikipedia.org/wiki/Flicker_fusion_threshold is a recommended
// reading. // reading.
func New(s spi.Conn, numLights int, intensity uint8, temperature uint16) (*Dev, error) { func New(p spi.Port, numLights int, intensity uint8, temperature uint16) (*Dev, error) {
if err := s.DevParams(20000000, spi.Mode3, 8); err != nil { c, err := p.DevParams(20000000, spi.Mode3, 8)
if err != nil {
return nil, err return nil, err
} }
// End frames are needed to be able to push enough SPI clock signals due to // End frames are needed to be able to push enough SPI clock signals due to
@ -308,7 +309,7 @@ func New(s spi.Conn, numLights int, intensity uint8, temperature uint16) (*Dev,
return &Dev{ return &Dev{
Intensity: intensity, Intensity: intensity,
Temperature: temperature, Temperature: temperature,
s: s, s: c,
numLights: numLights, numLights: numLights,
rawBuf: buf, rawBuf: buf,
pixels: buf[4 : 4+4*numLights], pixels: buf[4 : 4+4*numLights],

@ -703,8 +703,8 @@ type configFail struct {
spitest.Record spitest.Record
} }
func (c *configFail) DevParams(maxHz int64, mode spi.Mode, bits int) error { func (c *configFail) DevParams(maxHz int64, mode spi.Mode, bits int) (spi.Conn, error) {
return errors.New("injected error") return nil, errors.New("injected error")
} }
func equalUint16(a, b []uint16) bool { func equalUint16(a, b []uint16) bool {

@ -176,12 +176,13 @@ func NewI2C(b i2c.Bus, opts *Opts) (*Dev, error) {
// //
// BUG(maruel): This code was not tested yet, still waiting for a SPI enabled // BUG(maruel): This code was not tested yet, still waiting for a SPI enabled
// device in the mail. // device in the mail.
func NewSPI(s spi.Conn, opts *Opts) (*Dev, error) { func NewSPI(p spi.Port, opts *Opts) (*Dev, error) {
// It works both in Mode0 and Mode3. // It works both in Mode0 and Mode3.
if err := s.DevParams(10000000, spi.Mode3, 8); err != nil { c, err := p.DevParams(10000000, spi.Mode3, 8)
if err != nil {
return nil, err return nil, err
} }
d := &Dev{d: s, isSPI: true} d := &Dev{d: c, isSPI: true}
if err := d.makeDev(opts); err != nil { if err := d.makeDev(opts); err != nil {
return nil, err return nil, err
} }

@ -545,6 +545,6 @@ type spiFail struct {
spitest.Playback spitest.Playback
} }
func (s *spiFail) DevParams(maxHz int64, mode spi.Mode, bits int) error { func (s *spiFail) DevParams(maxHz int64, mode spi.Mode, bits int) (spi.Conn, error) {
return errors.New("failing") return nil, errors.New("failing")
} }

@ -67,7 +67,7 @@ func (s *SmokeTest) Run(args []string) (err error) {
} }
i2cRecorder := i2ctest.Record{Bus: i2cBus} i2cRecorder := i2ctest.Record{Bus: i2cBus}
spiRecorder := spitest.Record{Conn: spiBus} spiRecorder := spitest.Record{Port: spiBus}
err = run(&i2cRecorder, &spiRecorder) err = run(&i2cRecorder, &spiRecorder)
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)
@ -119,7 +119,7 @@ func (s *SmokeTest) Run(args []string) (err error) {
return err return err
} }
func run(i2cBus i2c.Bus, spiBus spi.ConnCloser) (err error) { func run(i2cBus i2c.Bus, spiBus spi.PortCloser) (err error) {
opts := &bme280.Opts{ opts := &bme280.Opts{
Temperature: bme280.O16x, Temperature: bme280.O16x,
Pressure: bme280.O16x, Pressure: bme280.O16x,

@ -94,17 +94,17 @@ type Dev struct {
// Maximum I²C speed is 1Mhz. // Maximum I²C speed is 1Mhz.
// //
// MOSI is not used and should be grounded. // MOSI is not used and should be grounded.
func New(s spi.Conn, i i2c.Bus, cs gpio.PinOut) (*Dev, error) { func New(p spi.Port, i i2c.Bus, cs gpio.PinOut) (*Dev, error) {
// Sadly the Lepton will unconditionally send 27fps, even if the effective // Sadly the Lepton will unconditionally send 27fps, even if the effective
// rate is 9fps. // rate is 9fps.
mode := spi.Mode3 mode := spi.Mode3
if cs == nil { if cs == nil {
// Query the CS pin before disabling it. // Query the CS pin before disabling it.
p, ok := s.(spi.Pins) pins, ok := p.(spi.Pins)
if !ok { if !ok {
return nil, errors.New("lepton: require manual access to the CS pin") return nil, errors.New("lepton: require manual access to the CS pin")
} }
cs = p.CS() cs = pins.CS()
if cs == gpio.INVALID { if cs == gpio.INVALID {
return nil, errors.New("lepton: require manual access to a valid CS pin") return nil, errors.New("lepton: require manual access to a valid CS pin")
} }
@ -112,7 +112,8 @@ func New(s spi.Conn, i i2c.Bus, cs gpio.PinOut) (*Dev, error) {
} }
// TODO(maruel): Switch to 16 bits per word, so that big endian 16 bits word // TODO(maruel): Switch to 16 bits per word, so that big endian 16 bits word
// decoding is done by the SPI driver. // decoding is done by the SPI driver.
if err := s.DevParams(20000000, mode, 8); err != nil { s, err := p.DevParams(20000000, mode, 8)
if err != nil {
return nil, err return nil, err
} }
c, err := cci.New(i) c, err := cci.New(i)

@ -370,7 +370,7 @@ type spiStream struct {
err error err error
} }
func (s *spiStream) DevParams(maxHz int64, mode spi.Mode, bits int) error { func (s *spiStream) DevParams(maxHz int64, mode spi.Mode, bits int) (spi.Conn, error) {
if maxHz != 20000000 { if maxHz != 20000000 {
s.t.Fatal(maxHz) s.t.Fatal(maxHz)
} }
@ -380,7 +380,7 @@ func (s *spiStream) DevParams(maxHz int64, mode spi.Mode, bits int) error {
if bits != 8 { if bits != 8 {
s.t.Fatal(bits) s.t.Fatal(bits)
} }
return s.err return s, s.err
} }
func (s *spiStream) Tx(w, r []byte) error { func (s *spiStream) Tx(w, r []byte) error {

@ -122,7 +122,7 @@ type Dev struct {
// 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(s spi.Conn, dc gpio.PinOut, w, h int, rotated bool) (*Dev, error) { func NewSPI(p spi.Port, dc gpio.PinOut, w, h int, rotated bool) (*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")
} }
@ -133,10 +133,11 @@ func NewSPI(s spi.Conn, dc gpio.PinOut, w, h int, rotated bool) (*Dev, error) {
} else if err := dc.Out(gpio.Low); err != nil { } else if err := dc.Out(gpio.Low); err != nil {
return nil, err return nil, err
} }
if err := s.DevParams(3300000, spi.Mode0, bits); err != nil { c, err := p.DevParams(3300000, spi.Mode0, bits)
if err != nil {
return nil, err return nil, err
} }
return newDev(s, w, h, rotated, true, dc) return newDev(c, w, h, rotated, 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

@ -575,8 +575,8 @@ type configFail struct {
spitest.Record spitest.Record
} }
func (c *configFail) DevParams(maxHz int64, mode spi.Mode, bits int) error { func (c *configFail) DevParams(maxHz int64, mode spi.Mode, bits int) (spi.Conn, error) {
return errors.New("injected error") return nil, errors.New("injected error")
} }
type failPin struct { type failPin struct {

@ -97,7 +97,7 @@ func (s *SmokeTest) Run(args []string) (err error) {
} }
i2cRecorder := i2ctest.Record{Bus: i2cBus} i2cRecorder := i2ctest.Record{Bus: i2cBus}
spiRecorder := spitest.Record{Conn: spiBus} spiRecorder := spitest.Record{Port: spiBus}
err = s.run(&i2cRecorder, &spiRecorder, dc, *w, *h, *rotated) err = s.run(&i2cRecorder, &spiRecorder, dc, *w, *h, *rotated)
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)
@ -149,7 +149,7 @@ func (s *SmokeTest) Run(args []string) (err error) {
return err return err
} }
func (s *SmokeTest) run(i2cBus i2c.Bus, spiBus spi.ConnCloser, dc gpio.PinOut, w, h int, rotated bool) (err error) { func (s *SmokeTest) run(i2cBus i2c.Bus, spiBus spi.PortCloser, dc gpio.PinOut, w, h int, rotated bool) (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, w, h, rotated)

Loading…
Cancel
Save