From d2f71ca23c87ace673b70959950f9d50bdb98b8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=C3=A1n=20C=2E=20McCord?= Date: Mon, 6 Aug 2018 11:18:38 -0400 Subject: [PATCH] bitbang/spi: improve mode handling (#267) - Handles clock-related Mode options for bitbanging SPI. - Adds error handling for each errorable operation in spiConn.Tx - Adds support for NoCS mode option - Re-adds early catch for unhandled modes, now just HalfDuplex and LSBFirst Fixes #266 --- experimental/devices/bitbang/spi.go | 88 +++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 12 deletions(-) diff --git a/experimental/devices/bitbang/spi.go b/experimental/devices/bitbang/spi.go index e877c99..1e3595e 100644 --- a/experimental/devices/bitbang/spi.go +++ b/experimental/devices/bitbang/spi.go @@ -70,8 +70,11 @@ func (s *SPI) Connect(f physic.Frequency, mode spi.Mode, bits int) (spi.Conn, er if f < 0 { return nil, errors.New("bitbang-spi: invalid frequency") } - if mode != spi.Mode3 { - return nil, fmt.Errorf("bitbang-spi: mode %v is not implemented", mode) + if mode&spi.HalfDuplex == spi.HalfDuplex { + return nil, errors.New("bitbang-spi: half-duplex mode not supported") + } + if mode&spi.LSBFirst == spi.LSBFirst { + return nil, errors.New("bitbang-spi: LSBFirst mode not supported") } s.spiConn.mu.Lock() defer s.spiConn.mu.Unlock() @@ -147,36 +150,97 @@ func (s *spiConn) Duplex() conn.Duplex { return conn.Full } +func (s *spiConn) clockOn() error { + if s.mode&spi.Mode2 == spi.Mode2 { + return s.sck.Out(gpio.Low) + } + return s.sck.Out(gpio.High) +} + +func (s *spiConn) clockOff() error { + if s.mode&spi.Mode2 == spi.Mode2 { + return s.sck.Out(gpio.High) + } + return s.sck.Out(gpio.Low) +} + +func (s *spiConn) readAfterClockPulse() bool { + return s.mode&spi.Mode1 == spi.Mode1 +} + +func (s *spiConn) assertCS() error { + if s.csn == nil || s.mode&spi.NoCS == spi.NoCS { + return nil + } + if err := s.csn.Out(gpio.Low); err != nil { + return err + } + s.sleepHalfCycle() + return nil +} + +func (s *spiConn) unassertCS() error { + if s.csn == nil || s.mode&spi.NoCS == spi.NoCS { + return nil + } + if err := s.csn.Out(gpio.High); err != nil { + return err + } + s.sleepHalfCycle() + return nil +} + // Tx implements spi.Conn. // -// BUG(maruel): Implement mode. +// BUG(maruel): Implement mode (HalfDuplex and LSBFirst remain to be done). // BUG(maruel): Implement bits. // BUG(maruel): Test if read works. -func (s *spiConn) Tx(w, r []byte) error { +func (s *spiConn) Tx(w, r []byte) (err error) { if len(r) != 0 && len(w) != len(r) { return errors.New("bitbang-spi: write and read buffers must be the same length") } + s.mu.Lock() defer s.mu.Unlock() - if s.csn != nil { - _ = s.csn.Out(gpio.Low) - s.sleepHalfCycle() + + if err = s.assertCS(); err != nil { + return fmt.Errorf("bitbang-spi: failed to assert chip-select: %v", err) } + for i := uint(0); i < uint(len(w)*8); i++ { - _ = s.sdo.Out(w[i/8]&(1<<(i%8)) != 0) + if err = s.sdo.Out(w[i/8]&(1<<(i%8)) != 0); err != nil { + return fmt.Errorf("bitbang-spi: failed to send bit %d of word %d: %v", i%8, i/8, err) + } + s.sleepHalfCycle() - _ = s.sck.Out(gpio.Low) + if err = s.clockOn(); err != nil { + return fmt.Errorf("bitbang-spi: failed to assert clock: %v", err) + } s.sleepHalfCycle() + + if s.readAfterClockPulse() { + if err = s.clockOff(); err != nil { + return fmt.Errorf("bitbang-spi: failed to unassert clock: %v", err) + } + s.sleepHalfCycle() + } + if len(r) != 0 { if s.sdi.Read() == gpio.High { r[i/8] |= 1 << (i % 8) } } - _ = s.sck.Out(gpio.Low) + + if !s.readAfterClockPulse() { + if err = s.clockOff(); err != nil { + return fmt.Errorf("bitbang-spi: failed to unassert clock: %v", err) + } + } } - if s.csn != nil { - _ = s.csn.Out(gpio.High) + if err = s.unassertCS(); err != nil { + return fmt.Errorf("bitbang-spi: failed to unassert chip-select: %v", err) } + return nil }