├── LICENSE ├── cmd └── spitest │ └── main.go ├── go.mod ├── go.sum ├── kernel.go ├── spi.go └── udev └── 90-spidev.rules /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Eric Cooper 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a 4 | copy of this software and associated documentation files (the "Software"), 5 | to deal in the Software without restriction, including without limitation 6 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 7 | and/or sell copies of the Software, and to permit persons to whom the 8 | Software is furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 18 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 19 | DEALINGS IN THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /cmd/spitest/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "log" 7 | "strconv" 8 | 9 | "github.com/ecc1/spi" 10 | ) 11 | 12 | var ( 13 | device = flag.String("d", "/dev/spidev5.1", "SPI `device`") 14 | speed = flag.Int("s", 1000000, "SPI `speed` (Hz)") 15 | customCS = flag.Int("cs", 0, "use `GPIO#` as custom chip select") 16 | ) 17 | 18 | func main() { 19 | flag.Parse() 20 | var values []byte 21 | for _, v := range flag.Args() { 22 | b, err := strconv.ParseUint(v, 16, 8) 23 | if err != nil { 24 | log.Fatal(err) 25 | } 26 | values = append(values, byte(b)) 27 | } 28 | dev, err := spi.Open(*device, *speed, *customCS) 29 | if err != nil { 30 | log.Fatal(err) 31 | } 32 | defer dev.Close() 33 | if len(values)%2 == 1 { 34 | values = append(values, 0) 35 | } 36 | response := make([]byte, len(values)) 37 | fmt.Printf("send: % X\n", values) 38 | err = dev.Transfer(values, response) 39 | if err != nil { 40 | log.Fatalf("%s: %v", *device, err) 41 | } 42 | fmt.Printf("recv: % X\n", response) 43 | } 44 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ecc1/spi 2 | 3 | go 1.13 4 | 5 | require ( 6 | github.com/ecc1/gpio v0.0.0-20200212231225-d40e43fcf8f5 7 | golang.org/x/sys v0.5.0 8 | ) 9 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/ecc1/gpio v0.0.0-20171107174639-450ac9ea6df7 h1:87CukuBNR73j2KmipKlPkTLR1JaxTt0r9HXTDLUclD4= 2 | github.com/ecc1/gpio v0.0.0-20171107174639-450ac9ea6df7/go.mod h1:LXSJyYdvUHvdCZFUJDqKHv0aRRXrTfZIVlYfBOslPmQ= 3 | github.com/ecc1/gpio v0.0.0-20200212231225-d40e43fcf8f5 h1:caoskihCyJpXaV3oRLl+jYuE+aLRnfcIprUQ6oBxTh8= 4 | github.com/ecc1/gpio v0.0.0-20200212231225-d40e43fcf8f5/go.mod h1:ZcIrkf+E8KutUpAcNHOHaf2NYukHYOlYTCDxV5zzn04= 5 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo= 6 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 7 | golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= 8 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 9 | -------------------------------------------------------------------------------- /kernel.go: -------------------------------------------------------------------------------- 1 | package spi 2 | 3 | // Definitions from 4 | // C naming is used for ease in keeping this file in sync. 5 | 6 | type spi_ioc_transfer struct { 7 | tx_buf uint64 8 | rx_buf uint64 9 | 10 | len uint32 11 | speed_hz uint32 12 | 13 | delay_usecs uint16 14 | bits_per_word uint8 15 | cs_change uint8 16 | tx_nbits uint8 17 | rx_nbits uint8 18 | pad uint16 19 | } 20 | 21 | // Not all of these are used, but are defined for completeness. 22 | const ( 23 | spi_CPHA = 0x01 24 | spi_CPOL = 0x02 25 | 26 | spi_MODE_0 = 0 27 | spi_MODE_1 = spi_CPHA 28 | spi_MODE_2 = spi_CPOL 29 | spi_MODE_3 = spi_CPOL | spi_CPHA 30 | 31 | spi_CS_HIGH = 0x04 32 | spi_LSB_FIRST = 0x08 33 | spi_3WIRE = 0x10 34 | spi_LOOP = 0x20 35 | spi_NO_CS = 0x40 36 | spi_READY = 0x80 37 | spi_TX_DUAL = 0x100 38 | spi_TX_QUAD = 0x200 39 | spi_RX_DUAL = 0x400 40 | spi_RX_QUAD = 0x800 41 | 42 | spi_IOC_MESSAGE_base = 0x40006B00 43 | spi_IOC_MESSAGE_incr = 0x200000 44 | 45 | // Read / Write of SPI mode (spi_MODE_0..spi_MODE_3) (limited to 8 bits) 46 | spi_IOC_RD_MODE = 0x80016B01 47 | spi_IOC_WR_MODE = 0x40016B01 48 | 49 | // Read / Write SPI bit justification 50 | spi_IOC_RD_LSB_FIRST = 0x80016B02 51 | spi_IOC_WR_LSB_FIRST = 0x40016B02 52 | 53 | // Read / Write SPI device word length (1..N) 54 | spi_IOC_RD_BITS_PER_WORD = 0x80016B03 55 | spi_IOC_WR_BITS_PER_WORD = 0x40016B03 56 | 57 | // Read / Write SPI device default max speed Hz 58 | spi_IOC_RD_MAX_SPEED_HZ = 0x80046B04 59 | spi_IOC_WR_MAX_SPEED_HZ = 0x40046B04 60 | 61 | // Read / Write of the SPI mode field 62 | spi_IOC_RD_MODE32 = 0x80046B05 63 | spi_IOC_WR_MODE32 = 0x40046B05 64 | ) 65 | 66 | func spi_IOC_MESSAGE(n uint) uint { 67 | return spi_IOC_MESSAGE_base + n*spi_IOC_MESSAGE_incr 68 | } 69 | -------------------------------------------------------------------------------- /spi.go: -------------------------------------------------------------------------------- 1 | package spi 2 | 3 | import ( 4 | "fmt" 5 | "unsafe" 6 | 7 | "github.com/ecc1/gpio" 8 | "golang.org/x/sys/unix" 9 | ) 10 | 11 | // Device represents an SPI device. 12 | type Device struct { 13 | fd int 14 | speed int 15 | cs gpio.OutputPin 16 | } 17 | 18 | // Open opens the given SPI device at the specified speed (in Hertz) 19 | // If customCS in not zero, that pin number is used as a custom chip-select. 20 | func Open(spiDevice string, speed int, customCS int) (*Device, error) { 21 | fd, err := unix.Open(spiDevice, unix.O_RDWR, 0) 22 | if err != nil { 23 | return nil, fmt.Errorf("%s: %w", spiDevice, err) 24 | } 25 | // Ensure exclusive access. 26 | err = unix.Flock(fd, unix.LOCK_EX|unix.LOCK_NB) 27 | switch err { 28 | case nil: 29 | if customCS == 0 { 30 | return &Device{fd: fd, speed: speed}, nil 31 | } 32 | case unix.EWOULDBLOCK: 33 | _ = unix.Close(fd) 34 | return nil, fmt.Errorf("%s: device is in use", spiDevice) 35 | default: 36 | _ = unix.Close(fd) 37 | return nil, fmt.Errorf("%s: %w", spiDevice, err) 38 | } 39 | // Use specified GPIO pin as custom chip-select. 40 | cs, err := gpio.Output(customCS, true, false) 41 | if err != nil { 42 | _ = unix.Close(fd) 43 | return nil, fmt.Errorf("GPIO %d for chip select: %w", customCS, err) 44 | } 45 | return &Device{fd: fd, speed: speed, cs: cs}, nil 46 | } 47 | 48 | // Close closes the SPI device. 49 | func (dev *Device) Close() error { 50 | return unix.Close(dev.fd) 51 | } 52 | 53 | // Transfer performs an SPI transfer operation (send and receive). 54 | func (dev *Device) Transfer(snd, rcv []byte) error { 55 | if len(snd) != len(rcv) { 56 | return fmt.Errorf("transfer buffers must be the same length (snd = %d, rcv = %d)", len(snd), len(rcv)) 57 | } 58 | if dev.cs != nil { 59 | dev.cs.Write(true) 60 | defer dev.cs.Write(false) 61 | } 62 | sndAddr := uint64(uintptr(unsafe.Pointer(&snd[0]))) 63 | rcvAddr := uint64(uintptr(unsafe.Pointer(&rcv[0]))) 64 | tr := spi_ioc_transfer{ 65 | tx_buf: sndAddr, 66 | rx_buf: rcvAddr, 67 | len: uint32(len(snd)), 68 | speed_hz: uint32(dev.speed), 69 | delay_usecs: 0, 70 | bits_per_word: 8, 71 | } 72 | return dev.syscall(spi_IOC_MESSAGE(1), unsafe.Pointer(&tr)) 73 | } 74 | 75 | // Mode returns the mode of the SPI device. 76 | func (dev *Device) Mode() (uint8, error) { 77 | var mode uint8 78 | err := dev.syscallU8(spi_IOC_RD_MODE, &mode) 79 | return mode, err 80 | } 81 | 82 | // SetMode sets the mode of the SPI device. 83 | func (dev *Device) SetMode(mode uint8) error { 84 | return dev.syscallU8(spi_IOC_WR_MODE, &mode) 85 | } 86 | 87 | // LSBFirst returns bit order of the SPI device. 88 | func (dev *Device) LSBFirst() (bool, error) { 89 | var b uint8 90 | err := dev.syscallU8(spi_IOC_RD_LSB_FIRST, &b) 91 | if b != 0 { 92 | return true, err 93 | } 94 | return false, err 95 | } 96 | 97 | // SetLSBFirst sets the bit order of the SPI device. 98 | func (dev *Device) SetLSBFirst(lsb bool) error { 99 | var b uint8 100 | if lsb { 101 | b = 1 102 | } 103 | return dev.syscallU8(spi_IOC_WR_LSB_FIRST, &b) 104 | } 105 | 106 | // BitsPerWord returns the word size of the SPI device. 107 | func (dev *Device) BitsPerWord() (int, error) { 108 | var bits uint8 109 | err := dev.syscallU8(spi_IOC_RD_BITS_PER_WORD, &bits) 110 | return int(bits), err 111 | } 112 | 113 | // SetBitsPerWord sets the word size of the SPI device. 114 | func (dev *Device) SetBitsPerWord(n int) error { 115 | bits := uint8(n) 116 | return dev.syscallU8(spi_IOC_WR_BITS_PER_WORD, &bits) 117 | } 118 | 119 | // MaxSpeed returns the maximum speed of the SPI device, in Hertz. 120 | func (dev *Device) MaxSpeed() (int, error) { 121 | var speed uint32 122 | err := dev.syscallU32(spi_IOC_RD_MAX_SPEED_HZ, &speed) 123 | return int(speed), err 124 | } 125 | 126 | // SetMaxSpeed sets the maximum speed of the SPI device, in Hertz. 127 | func (dev *Device) SetMaxSpeed(n int) error { 128 | speed := uint32(n) 129 | return dev.syscallU32(spi_IOC_WR_MAX_SPEED_HZ, &speed) 130 | } 131 | 132 | func (dev *Device) syscallU8(op uint, arg *uint8) error { 133 | return dev.syscall(op, unsafe.Pointer(arg)) 134 | } 135 | 136 | func (dev *Device) syscallU32(op uint, arg *uint32) error { 137 | return dev.syscall(op, unsafe.Pointer(arg)) 138 | } 139 | 140 | func (dev *Device) syscall(op uint, arg unsafe.Pointer) error { 141 | _, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(dev.fd), uintptr(op), uintptr(arg)) 142 | if errno != 0 { 143 | return error(errno) 144 | } 145 | return nil 146 | } 147 | -------------------------------------------------------------------------------- /udev/90-spidev.rules: -------------------------------------------------------------------------------- 1 | ACTION=="add", KERNEL=="spidev*", GROUP="spi", MODE="0664" 2 | --------------------------------------------------------------------------------