diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 853da01..6cc8816 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -33,5 +33,6 @@ Marc-Antoine Ruel Matt Aimonetti Max Ekman Matias Insaurralde +Seán C McCord Stephan Sperber Thorsten von Eicken diff --git a/experimental/devices/bitbang/spi.go b/experimental/devices/bitbang/spi.go index 1e3595e..750dc49 100644 --- a/experimental/devices/bitbang/spi.go +++ b/experimental/devices/bitbang/spi.go @@ -76,6 +76,9 @@ func (s *SPI) Connect(f physic.Frequency, mode spi.Mode, bits int) (spi.Conn, er if mode&spi.LSBFirst == spi.LSBFirst { return nil, errors.New("bitbang-spi: LSBFirst mode not supported") } + if mode >= 0x20 { + return nil, fmt.Errorf("bitbang-spi: unhandled mode %d(%s)", mode, mode.String()) + } s.spiConn.mu.Lock() defer s.spiConn.mu.Unlock() s.spiConn.freqDev = f @@ -84,6 +87,14 @@ func (s *SPI) Connect(f physic.Frequency, mode spi.Mode, bits int) (spi.Conn, er } s.spiConn.mode = mode s.spiConn.bits = bits + s.spiConn.readAfterClockPulse = mode&spi.Mode1 == spi.Mode1 + + // Set clock idle polarity, ensuring an idle clock to start + s.spiConn.clockIdle = gpio.Level(mode&spi.Mode2 == spi.Mode2) + if err := s.spiConn.sck.Out(s.spiConn.clockIdle); err != nil { + return nil, fmt.Errorf("bitbang-spi: failed to idle clock: %v", err) + } + return &s.spiConn, nil } @@ -132,12 +143,14 @@ type spiConn struct { csn gpio.PinOut // CS // Mutable. - mu sync.Mutex - freqPort physic.Frequency - freqDev physic.Frequency - mode spi.Mode - bits int - halfCycle time.Duration + mu sync.Mutex + freqPort physic.Frequency + freqDev physic.Frequency + clockIdle gpio.Level + readAfterClockPulse bool + mode spi.Mode + bits int + halfCycle time.Duration } func (s *spiConn) String() string { @@ -150,46 +163,6 @@ 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 (HalfDuplex and LSBFirst remain to be done). @@ -213,14 +186,14 @@ func (s *spiConn) Tx(w, r []byte) (err error) { } s.sleepHalfCycle() - if err = s.clockOn(); err != nil { + if err = s.sck.Out(!s.clockIdle); 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) + if s.readAfterClockPulse { + if err = s.sck.Out(s.clockIdle); err != nil { + return fmt.Errorf("bitbang-spi: failed to idle clock: %v", err) } s.sleepHalfCycle() } @@ -231,9 +204,9 @@ func (s *spiConn) Tx(w, r []byte) (err error) { } } - if !s.readAfterClockPulse() { - if err = s.clockOff(); err != nil { - return fmt.Errorf("bitbang-spi: failed to unassert clock: %v", err) + if !s.readAfterClockPulse { + if err = s.sck.Out(s.clockIdle); err != nil { + return fmt.Errorf("bitbang-spi: failed to idle clock: %v", err) } } } @@ -284,5 +257,27 @@ func (s *spiConn) sleepHalfCycle() { cpu.Nanospin(s.halfCycle) } +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 +} + var _ spi.PortCloser = &SPI{} var _ fmt.Stringer = &SPI{}