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
pull/1/head
Seán C. McCord 8 years ago committed by M-A
parent 5b5d0de02f
commit d2f71ca23c

@ -70,8 +70,11 @@ func (s *SPI) Connect(f physic.Frequency, mode spi.Mode, bits int) (spi.Conn, er
if f < 0 { if f < 0 {
return nil, errors.New("bitbang-spi: invalid frequency") return nil, errors.New("bitbang-spi: invalid frequency")
} }
if mode != spi.Mode3 { if mode&spi.HalfDuplex == spi.HalfDuplex {
return nil, fmt.Errorf("bitbang-spi: mode %v is not implemented", mode) 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() s.spiConn.mu.Lock()
defer s.spiConn.mu.Unlock() defer s.spiConn.mu.Unlock()
@ -147,36 +150,97 @@ func (s *spiConn) Duplex() conn.Duplex {
return conn.Full 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. // 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): Implement bits.
// BUG(maruel): Test if read works. // 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) { if len(r) != 0 && len(w) != len(r) {
return errors.New("bitbang-spi: write and read buffers must be the same length") return errors.New("bitbang-spi: write and read buffers must be the same length")
} }
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
if s.csn != nil {
_ = s.csn.Out(gpio.Low) if err = s.assertCS(); err != nil {
s.sleepHalfCycle() return fmt.Errorf("bitbang-spi: failed to assert chip-select: %v", err)
} }
for i := uint(0); i < uint(len(w)*8); i++ { 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.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() 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 len(r) != 0 {
if s.sdi.Read() == gpio.High { if s.sdi.Read() == gpio.High {
r[i/8] |= 1 << (i % 8) 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 { if err = s.unassertCS(); err != nil {
_ = s.csn.Out(gpio.High) return fmt.Errorf("bitbang-spi: failed to unassert chip-select: %v", err)
} }
return nil return nil
} }

Loading…
Cancel
Save