|
|
|
@ -24,12 +24,12 @@ import (
|
|
|
|
"periph.io/x/periph/host/cpu"
|
|
|
|
"periph.io/x/periph/host/cpu"
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// NewSPI returns an object that communicates SPI over 3 or 4 pins.
|
|
|
|
// NewSPI returns an spi.PortCloser that communicates SPI over 3 or 4 pins.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// BUG(maruel): Completely untested.
|
|
|
|
// BUG(maruel): Completely untested.
|
|
|
|
//
|
|
|
|
//
|
|
|
|
// cs can be nil.
|
|
|
|
// cs can be nil.
|
|
|
|
func NewSPI(clk, mosi gpio.PinOut, miso gpio.PinIn, cs gpio.PinOut, speedHz int64) (*SPI, error) {
|
|
|
|
func NewSPI(clk, mosi gpio.PinOut, miso gpio.PinIn, cs gpio.PinOut) (*SPI, error) {
|
|
|
|
if err := clk.Out(gpio.High); err != nil {
|
|
|
|
if err := clk.Out(gpio.High); err != nil {
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -47,79 +47,104 @@ func NewSPI(clk, mosi gpio.PinOut, miso gpio.PinIn, cs gpio.PinOut, speedHz int6
|
|
|
|
return nil, err
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s := &SPI{
|
|
|
|
return &SPI{spiConn: spiConn{sck: clk, sdi: miso, sdo: mosi, csn: cs}}, nil
|
|
|
|
sck: clk,
|
|
|
|
|
|
|
|
sdi: miso,
|
|
|
|
|
|
|
|
sdo: mosi,
|
|
|
|
|
|
|
|
csn: cs,
|
|
|
|
|
|
|
|
mode: spi.Mode3,
|
|
|
|
|
|
|
|
bits: 8,
|
|
|
|
|
|
|
|
halfCycle: time.Second / time.Duration(speedHz) / time.Duration(2),
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return s, nil
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// SPI represents a SPI master implemented as bit-banging on 3 or 4 GPIO pins.
|
|
|
|
// SPI represents a SPI master port implemented as bit-banging on 3 or 4 GPIO
|
|
|
|
|
|
|
|
// pins.
|
|
|
|
type SPI struct {
|
|
|
|
type SPI struct {
|
|
|
|
sck gpio.PinOut // Clock
|
|
|
|
spiConn spiConn
|
|
|
|
sdi gpio.PinIn // MISO
|
|
|
|
|
|
|
|
sdo gpio.PinOut // MOSI
|
|
|
|
|
|
|
|
csn gpio.PinOut // CS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
|
|
|
freqPort physic.Frequency
|
|
|
|
|
|
|
|
freqDev physic.Frequency
|
|
|
|
|
|
|
|
mode spi.Mode
|
|
|
|
|
|
|
|
bits int
|
|
|
|
|
|
|
|
halfCycle time.Duration
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func (s *SPI) String() string {
|
|
|
|
func (s *SPI) String() string {
|
|
|
|
return fmt.Sprintf("bitbang/spi(%s, %s, %s, %s)", s.sck, s.sdi, s.sdo, s.csn)
|
|
|
|
return fmt.Sprintf("bitbang/spi(%s, %s, %s, %s)", s.spiConn.sck, s.spiConn.sdi, s.spiConn.sdo, s.spiConn.csn)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Close implements spi.ConnCloser.
|
|
|
|
// Close implements spi.PortCloser.
|
|
|
|
func (s *SPI) Close() error {
|
|
|
|
func (s *SPI) Close() error {
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Duplex implements spi.Conn.
|
|
|
|
// Connect implements spi.PortCloser.
|
|
|
|
func (s *SPI) Duplex() conn.Duplex {
|
|
|
|
func (s *SPI) Connect(f physic.Frequency, mode spi.Mode, bits int) (spi.Conn, error) {
|
|
|
|
// Maybe implement bitbanging SPI only in half mode?
|
|
|
|
if f < 0 {
|
|
|
|
return conn.Full
|
|
|
|
return nil, errors.New("bitbang-spi: invalid frequency")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if mode != spi.Mode3 {
|
|
|
|
|
|
|
|
return nil, fmt.Errorf("bitbang-spi: mode %v is not implemented", mode)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
s.spiConn.mu.Lock()
|
|
|
|
|
|
|
|
defer s.spiConn.mu.Unlock()
|
|
|
|
|
|
|
|
s.spiConn.freqDev = f
|
|
|
|
|
|
|
|
if s.spiConn.freqDev != 0 && (s.spiConn.freqPort == 0 || s.spiConn.freqDev < s.spiConn.freqPort) {
|
|
|
|
|
|
|
|
s.spiConn.halfCycle = f.Duration() / 2
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
s.spiConn.mode = mode
|
|
|
|
|
|
|
|
s.spiConn.bits = bits
|
|
|
|
|
|
|
|
return &s.spiConn, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// LimitSpeed implements spi.ConnCloser.
|
|
|
|
// LimitSpeed implements spi.PortCloser.
|
|
|
|
func (s *SPI) LimitSpeed(f physic.Frequency) error {
|
|
|
|
func (s *SPI) LimitSpeed(f physic.Frequency) error {
|
|
|
|
if f <= 0 {
|
|
|
|
if f <= 0 {
|
|
|
|
return errors.New("bitbang-spi: invalid frequency")
|
|
|
|
return errors.New("bitbang-spi: invalid frequency")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.mu.Lock()
|
|
|
|
s.spiConn.mu.Lock()
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
defer s.spiConn.mu.Unlock()
|
|
|
|
s.freqPort = f
|
|
|
|
s.spiConn.freqPort = f
|
|
|
|
if s.freqDev == 0 || s.freqPort < s.freqDev {
|
|
|
|
if s.spiConn.freqDev == 0 || s.spiConn.freqPort < s.spiConn.freqDev {
|
|
|
|
s.halfCycle = f.Duration() / 2
|
|
|
|
s.spiConn.halfCycle = f.Duration() / 2
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Connect implements spi.Conn.
|
|
|
|
// CLK implements spi.Pins.
|
|
|
|
func (s *SPI) Connect(f physic.Frequency, mode spi.Mode, bits int) error {
|
|
|
|
func (s *SPI) CLK() gpio.PinOut {
|
|
|
|
if f < 0 {
|
|
|
|
return s.spiConn.sck
|
|
|
|
return errors.New("bitbang-spi: invalid frequency")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if mode != spi.Mode3 {
|
|
|
|
// MOSI implements spi.Pins.
|
|
|
|
return fmt.Errorf("bitbang-spi: mode %v is not implemented", mode)
|
|
|
|
func (s *SPI) MOSI() gpio.PinOut {
|
|
|
|
}
|
|
|
|
return s.spiConn.sdo
|
|
|
|
s.mu.Lock()
|
|
|
|
}
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
|
|
|
s.freqDev = f
|
|
|
|
// MISO implements spi.Pins.
|
|
|
|
if s.freqDev != 0 && (s.freqPort == 0 || s.freqDev < s.freqPort) {
|
|
|
|
func (s *SPI) MISO() gpio.PinIn {
|
|
|
|
s.halfCycle = f.Duration() / 2
|
|
|
|
return s.spiConn.sdi
|
|
|
|
}
|
|
|
|
}
|
|
|
|
s.mode = mode
|
|
|
|
|
|
|
|
s.bits = bits
|
|
|
|
// CS implements spi.Pins.
|
|
|
|
return nil
|
|
|
|
func (s *SPI) CS() gpio.PinOut {
|
|
|
|
|
|
|
|
return s.spiConn.csn
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// spiConn implements spi.Conn.
|
|
|
|
|
|
|
|
type spiConn struct {
|
|
|
|
|
|
|
|
// Immutable.
|
|
|
|
|
|
|
|
sck gpio.PinOut // Clock
|
|
|
|
|
|
|
|
sdi gpio.PinIn // MISO
|
|
|
|
|
|
|
|
sdo gpio.PinOut // MOSI
|
|
|
|
|
|
|
|
csn gpio.PinOut // CS
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Mutable.
|
|
|
|
|
|
|
|
mu sync.Mutex
|
|
|
|
|
|
|
|
freqPort physic.Frequency
|
|
|
|
|
|
|
|
freqDev physic.Frequency
|
|
|
|
|
|
|
|
mode spi.Mode
|
|
|
|
|
|
|
|
bits int
|
|
|
|
|
|
|
|
halfCycle time.Duration
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (s *spiConn) String() string {
|
|
|
|
|
|
|
|
return fmt.Sprintf("bitbang/spi(%s, %s, %s, %s)", s.sck, s.sdi, s.sdo, s.csn)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Duplex implements spi.Conn.
|
|
|
|
|
|
|
|
func (s *spiConn) Duplex() conn.Duplex {
|
|
|
|
|
|
|
|
// Maybe implement bitbanging SPI only in half mode?
|
|
|
|
|
|
|
|
return conn.Full
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Tx implements spi.Conn.
|
|
|
|
// Tx implements spi.Conn.
|
|
|
|
@ -127,7 +152,7 @@ func (s *SPI) Connect(f physic.Frequency, mode spi.Mode, bits int) error {
|
|
|
|
// BUG(maruel): Implement mode.
|
|
|
|
// BUG(maruel): Implement mode.
|
|
|
|
// BUG(maruel): Implement bits.
|
|
|
|
// BUG(maruel): Implement bits.
|
|
|
|
// BUG(maruel): Test if read works.
|
|
|
|
// BUG(maruel): Test if read works.
|
|
|
|
func (s *SPI) Tx(w, r []byte) error {
|
|
|
|
func (s *spiConn) Tx(w, r []byte) 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")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -156,12 +181,12 @@ func (s *SPI) Tx(w, r []byte) error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// TxPackets implements spi.Conn.
|
|
|
|
// TxPackets implements spi.Conn.
|
|
|
|
func (s *SPI) TxPackets(p []spi.Packet) error {
|
|
|
|
func (s *spiConn) TxPackets(p []spi.Packet) error {
|
|
|
|
return errors.New("bitbang-spi: not implemented")
|
|
|
|
return errors.New("bitbang-spi: not implemented")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Write implements io.Writer.
|
|
|
|
// Write implements io.Writer.
|
|
|
|
func (s *SPI) Write(d []byte) (int, error) {
|
|
|
|
func (s *spiConn) Write(d []byte) (int, error) {
|
|
|
|
if err := s.Tx(d, nil); err != nil {
|
|
|
|
if err := s.Tx(d, nil); err != nil {
|
|
|
|
return 0, err
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -169,31 +194,31 @@ func (s *SPI) Write(d []byte) (int, error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CLK implements spi.Pins.
|
|
|
|
// CLK implements spi.Pins.
|
|
|
|
func (s *SPI) CLK() gpio.PinOut {
|
|
|
|
func (s *spiConn) CLK() gpio.PinOut {
|
|
|
|
return s.sck
|
|
|
|
return s.sck
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MOSI implements spi.Pins.
|
|
|
|
// MOSI implements spi.Pins.
|
|
|
|
func (s *SPI) MOSI() gpio.PinOut {
|
|
|
|
func (s *spiConn) MOSI() gpio.PinOut {
|
|
|
|
return s.sdo
|
|
|
|
return s.sdo
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MISO implements spi.Pins.
|
|
|
|
// MISO implements spi.Pins.
|
|
|
|
func (s *SPI) MISO() gpio.PinIn {
|
|
|
|
func (s *spiConn) MISO() gpio.PinIn {
|
|
|
|
return s.sdi
|
|
|
|
return s.sdi
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CS implements spi.Pins.
|
|
|
|
// CS implements spi.Pins.
|
|
|
|
func (s *SPI) CS() gpio.PinOut {
|
|
|
|
func (s *spiConn) CS() gpio.PinOut {
|
|
|
|
return s.csn
|
|
|
|
return s.csn
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
|
|
// sleep does a busy loop to act as fast as possible.
|
|
|
|
// sleep does a busy loop to act as fast as possible.
|
|
|
|
func (s *SPI) sleepHalfCycle() {
|
|
|
|
func (s *spiConn) sleepHalfCycle() {
|
|
|
|
cpu.Nanospin(s.halfCycle)
|
|
|
|
cpu.Nanospin(s.halfCycle)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var _ spi.Conn = &SPI{}
|
|
|
|
var _ spi.PortCloser = &SPI{}
|
|
|
|
var _ fmt.Stringer = &SPI{}
|
|
|
|
var _ fmt.Stringer = &SPI{}
|
|
|
|
|