From 12db8443b05e8be34c02d388c3462b952ddb4487 Mon Sep 17 00:00:00 2001 From: Kunal Powar Date: Mon, 28 Apr 2014 01:18:14 +0530 Subject: [PATCH] spi: added spi feature for all bbb and rpi --- descriptor.go | 1 + host/bbb/bbb.go | 5 + host/generic/spibus.go | 233 +++++++++++++++++++++++++++++++++++++++++ host/rpi/rpi.go | 5 + samples/spisample.go | 53 ++++++++++ spi.go | 64 +++++++++++ spidriver.go | 38 +++++++ 7 files changed, 399 insertions(+) create mode 100644 host/generic/spibus.go create mode 100644 samples/spisample.go create mode 100644 spi.go create mode 100644 spidriver.go diff --git a/descriptor.go b/descriptor.go index 78208f3..b891008 100644 --- a/descriptor.go +++ b/descriptor.go @@ -14,6 +14,7 @@ type Descriptor struct { GPIODriver func() GPIODriver I2CDriver func() I2CDriver LEDDriver func() LEDDriver + SPIDriver func() SPIDriver } // The Describer type is a Descriptor provider. diff --git a/host/bbb/bbb.go b/host/bbb/bbb.go index c52f8a8..5a2e8ce 100644 --- a/host/bbb/bbb.go +++ b/host/bbb/bbb.go @@ -94,6 +94,8 @@ var ledMap = embd.LEDMap{ "beaglebone:green:usr3": []string{"3", "USR3", "usr3"}, } +var spiDeviceMinor = byte(1) + func ensureFeatureEnabled(id string) error { pattern := "/sys/devices/bone_capemgr.*/slots" file, err := embd.FindFirstMatchingFile(pattern) @@ -165,6 +167,9 @@ func init() { LEDDriver: func() embd.LEDDriver { return embd.NewLEDDriver(ledMap, generic.NewLED) }, + SPIDriver: func() embd.SPIDriver { + return embd.NewSPIDriver(spiDeviceMinor, generic.NewSPIBus) + }, } }) } diff --git a/host/generic/spibus.go b/host/generic/spibus.go new file mode 100644 index 0000000..d2d96ae --- /dev/null +++ b/host/generic/spibus.go @@ -0,0 +1,233 @@ +package generic + +import ( + "fmt" + "os" + "sync" + "syscall" + "unsafe" + + "github.com/golang/glog" + "github.com/kidoman/embd" +) + +const ( + SPI_IOC_WR_MODE = 0x40016B01 + SPI_IOC_WR_BITS_PER_WORD = 0x40016B03 + SPI_IOC_WR_MAX_SPEED_HZ = 0x40046B04 + + SPI_IOC_RD_MODE = 0x80016B01 + SPI_IOC_RD_BITS_PER_WORD = 0x80016B03 + SPI_IOC_RD_MAX_SPEED_HZ = 0x80046B04 + + SPI_IOC_MESSAGE_0 = 1073769216 //0x40006B00 + SPI_IOC_INCREMENTER = 2097152 //0x200000 + + DEFAULT_DELAYMS = uint16(0) + DEFAULT_SPI_BPW = uint8(8) + DEFAULT_SPI_SPEED = uint32(1000000) +) + +type spiIocTransfer struct { + tx_buf uint64 + rx_buf uint64 + + length uint32 + speed_hz uint32 + delay_usecs uint16 + bits_per_word uint8 +} + +type spiBus struct { + file *os.File + + spiDevMinor byte + + channel byte + mode byte + speed int + bpw int + delayms int + + mu sync.Mutex + + spiTransferData spiIocTransfer + initialized bool +} + +func spi_ioc_message_n(n uint32) uint32 { + return (SPI_IOC_MESSAGE_0 + (n * SPI_IOC_INCREMENTER)) +} + +func NewSPIBus(spiDevMinor, mode, channel byte, speed, bpw, delay int) embd.SPIBus { + return &spiBus{ + spiDevMinor: spiDevMinor, + mode: mode, + channel: channel, + speed: speed, + bpw: bpw, + delayms: delay, + } +} + +func (b *spiBus) init() error { + if b.initialized { + return nil + } + + var err error + if b.file, err = os.OpenFile(fmt.Sprintf("/dev/spidev%v.%v", b.spiDevMinor, b.channel), os.O_RDWR, os.ModeExclusive); err != nil { + return err + } + + err = b.setMode() + if err != nil { + return err + } + + b.spiTransferData = spiIocTransfer{} + + err = b.setSpeed() + if err != nil { + return err + } + + err = b.setBpw() + if err != nil { + return err + } + + b.setDelay() + + glog.V(2).Infof("spi: bus %v initialized", b.channel) + + b.initialized = true + return nil + +} + +func (b *spiBus) setMode() error { + var mode = uint8(b.mode) + var err error + glog.V(3).Infof("spi: setting spi mode to %v", mode) + + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), SPI_IOC_WR_MODE, uintptr(unsafe.Pointer(&mode))) + if errno != 0 { + err = syscall.Errno(errno) + glog.V(3).Infof("spi: failed to set mode due to %v", err.Error()) + return err + } + glog.V(3).Infof("spi: mode set to %v", mode) + return nil +} + +func (b *spiBus) setSpeed() error { + var speed uint32 + if b.speed > 0 { + speed = uint32(b.speed) + } else { + speed = DEFAULT_SPI_SPEED + } + + glog.V(3).Infof("spi: setting spi speed_max to %v", speed) + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), SPI_IOC_WR_MAX_SPEED_HZ, uintptr(unsafe.Pointer(&speed))) + if errno != 0 { + err := syscall.Errno(errno) + glog.V(3).Infof("spi: failed to set speed_max due to %v", err.Error()) + return err + } + glog.V(3).Infof("spi: speed_max set to %v", speed) + b.spiTransferData.speed_hz = speed + + return nil +} + +func (b *spiBus) setBpw() error { + var bpw uint8 + + if b.bpw > 0 { + bpw = uint8(b.bpw) + } else { + bpw = DEFAULT_SPI_BPW + } + + glog.V(3).Infof("spi: setting spi bpw to %v", bpw) + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), SPI_IOC_WR_BITS_PER_WORD, uintptr(unsafe.Pointer(&bpw))) + if errno != 0 { + err := syscall.Errno(errno) + glog.V(3).Infof("spi: failed to set bpw due to %v", err.Error()) + return err + } + glog.V(3).Infof("spi: bpw set to %v", bpw) + b.spiTransferData.bits_per_word = uint8(bpw) + return nil +} + +func (b *spiBus) setDelay() { + var delay uint16 + + if b.delayms > 0 { + delay = uint16(b.delayms) + } else { + delay = DEFAULT_DELAYMS + } + glog.V(3).Infof("spi: delay_ms set to %v", delay) + b.spiTransferData.delay_usecs = delay +} + +func (b *spiBus) TransferAndRecieveData(dataBuffer []uint8) error { + len := len(dataBuffer) + dataCarrier := b.spiTransferData + + dataCarrier.length = uint32(len) + dataCarrier.tx_buf = uint64(uintptr(unsafe.Pointer(&dataBuffer[0]))) + dataCarrier.rx_buf = uint64(uintptr(unsafe.Pointer(&dataBuffer[0]))) + + _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), uintptr(spi_ioc_message_n(1)), uintptr(unsafe.Pointer(&dataCarrier))) + if errno != 0 { + err := syscall.Errno(errno) + glog.V(3).Infof("spi: failed to read due to %v", err.Error()) + return err + } + return nil +} + +func (b *spiBus) ReceiveData(len int) ([]uint8, error) { + data := make([]uint8, len) + var err error + err = b.TransferAndRecieveData(data) + if err != nil { + return nil, err + } + return data, nil +} + +func (b *spiBus) TransferAndReceiveByte(data byte) (byte, error) { + d := make([]uint8, 1) + d[0] = uint8(data) + err := b.TransferAndRecieveData(d) + if err != nil { + return 0, err + } + return d[0], nil +} + +func (b *spiBus) ReceiveByte() (byte, error) { + d := make([]uint8, 1) + err := b.TransferAndRecieveData(d) + if err != nil { + return 0, err + } + return byte(d[0]), nil +} + +func (b *spiBus) Close() error { + b.mu.Lock() + defer b.mu.Unlock() + + if !b.initialized { + return nil + } + + return b.file.Close() +} diff --git a/host/rpi/rpi.go b/host/rpi/rpi.go index 36a9905..7814843 100644 --- a/host/rpi/rpi.go +++ b/host/rpi/rpi.go @@ -13,6 +13,8 @@ import ( "github.com/kidoman/embd/host/generic" ) +var spiDeviceMinor = byte(0) + var rev1Pins = embd.PinMap{ &embd.PinDesc{ID: "P1_3", Aliases: []string{"0", "GPIO_0", "SDA", "I2C0_SDA"}, Caps: embd.CapDigital | embd.CapI2C, DigitalLogical: 0}, &embd.PinDesc{ID: "P1_5", Aliases: []string{"1", "GPIO_1", "SCL", "I2C0_SCL"}, Caps: embd.CapDigital | embd.CapI2C, DigitalLogical: 1}, @@ -74,6 +76,9 @@ func init() { LEDDriver: func() embd.LEDDriver { return embd.NewLEDDriver(ledMap, generic.NewLED) }, + SPIDriver: func() embd.SPIDriver { + return embd.NewSPIDriver(spiDeviceMinor, generic.NewSPIBus) + }, } }) } diff --git a/samples/spisample.go b/samples/spisample.go new file mode 100644 index 0000000..647c649 --- /dev/null +++ b/samples/spisample.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + + "github.com/kidoman/embd" + + _ "github.com/kidoman/embd/host/all" +) + +func main() { + var err error + err = embd.InitSPI() + if err != nil { + panic(err) + } + defer embd.CloseSPI() + + spiBus := embd.NewSPIBus(embd.SPI_MODE_0, 0, 1000000, 8, 0) + defer spiBus.Close() + + dataBuf := make([]uint8, 3) + dataBuf[0] = uint8(1) + dataBuf[1] = uint8(2) + dataBuf[2] = uint8(3) + + err = spiBus.TransferAndRecieveData(dataBuf) + if err != nil { + panic(err) + } + + fmt.Println("Recived data is: %v", dataBuf) + + dataReceived, err := spiBus.ReceiveData(3) + if err != nil { + panic(err) + } + + fmt.Println("Recived data is: %v", dataReceived) + + dataByte := byte(1) + receivedByte, err := spiBus.TransferAndReceiveByte(dataByte) + if err != nil { + panic(err) + } + fmt.Println("Recived byte is: %v", receivedByte) + + receivedByte, err = spiBus.ReceiveByte() + if err != nil { + panic(err) + } + fmt.Println("Recived byte is: %v", receivedByte) +} diff --git a/spi.go b/spi.go new file mode 100644 index 0000000..e903084 --- /dev/null +++ b/spi.go @@ -0,0 +1,64 @@ +package embd + +const ( + spi_cpha = 0x01 + spi_cpol = 0x02 + + SPI_MODE_0 = (0 | 0) + SPI_MODE_1 = (0 | spi_cpha) + SPI_MODE_2 = (spi_cpol | 0) + SPI_MODE_3 = (spi_cpol | spi_cpha) +) + +type SPIBus interface { + TransferAndRecieveData(dataBuffer []uint8) error + + ReceiveData(len int) ([]uint8, error) + + TransferAndReceiveByte(data byte) (byte, error) + + ReceiveByte() (byte, error) + + Close() error +} + +type SPIDriver interface { + Bus(byte, byte, int, int, int) SPIBus + + Close() error +} + +var spiDriverInitialized bool +var spiDriverInstance SPIDriver + +func InitSPI() error { + if spiDriverInitialized { + return nil + } + + desc, err := DescribeHost() + if err != nil { + return err + } + + if desc.SPIDriver == nil { + return ErrFeatureNotSupported + } + + spiDriverInstance = desc.SPIDriver() + spiDriverInitialized = true + + return nil +} + +func CloseSPI() error { + return spiDriverInstance.Close() +} + +func NewSPIBus(mode, channel byte, speed, bpw, delay int) SPIBus { + if err := InitSPI(); err != nil { + panic(err) + } + + return spiDriverInstance.Bus(mode, channel, speed, bpw, delay) +} diff --git a/spidriver.go b/spidriver.go new file mode 100644 index 0000000..c2f21f6 --- /dev/null +++ b/spidriver.go @@ -0,0 +1,38 @@ +package embd + +import "sync" + +type spiBusFactory func(byte, byte, byte, int, int, int) SPIBus + +type spiDriver struct { + spiDevMinor byte + + busMap map[byte]SPIBus + busMapLock sync.Mutex + + spf spiBusFactory +} + +func NewSPIDriver(spiDevMinor byte, sbf spiBusFactory) SPIDriver { + return &spiDriver{ + spiDevMinor: spiDevMinor, + spf: sbf, + } +} + +func (s *spiDriver) Bus(mode, channel byte, speed, bpw, delay int) SPIBus { + s.busMapLock.Lock() + defer s.busMapLock.Unlock() + + b := s.spf(s.spiDevMinor, mode, channel, speed, bpw, delay) + s.busMap[channel] = b + return b +} + +func (s *spiDriver) Close() error { + for _, b := range s.busMap { + b.Close() + } + + return nil +}