Merge pull request #31 from Drahoslav7/feature/spi

SPI support
This commit is contained in:
Stian Eikeland 2018-11-29 16:34:33 +01:00 committed by GitHub
commit 14a5036b21
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 265 additions and 14 deletions

65
rpio.go
View File

@ -8,9 +8,13 @@ Supports simple operations such as:
- Pin read (high/low) - Pin read (high/low)
- Pin edge detection (no/rise/fall/any) - Pin edge detection (no/rise/fall/any)
- Pull up/down/off - Pull up/down/off
And clock/pwm related oparations: Also clock/pwm related oparations:
- Set Clock frequency - Set Clock frequency
- Set Duty cycle - Set Duty cycle
And SPI oparations:
- SPI transmit/recieve/exchange bytes
- Chip select
- Set speed
Example of use: Example of use:
@ -85,6 +89,7 @@ const (
gpioOffset = 0x200000 gpioOffset = 0x200000
clkOffset = 0x101000 clkOffset = 0x101000
pwmOffset = 0x20C000 pwmOffset = 0x20C000
spiOffset = 0x204000
memLength = 4096 memLength = 4096
) )
@ -93,6 +98,7 @@ var (
gpioBase int64 gpioBase int64
clkBase int64 clkBase int64
pwmBase int64 pwmBase int64
spiBase int64
) )
func init() { func init() {
@ -100,6 +106,7 @@ func init() {
gpioBase = base + gpioOffset gpioBase = base + gpioOffset
clkBase = base + clkOffset clkBase = base + clkOffset
pwmBase = base + pwmOffset pwmBase = base + pwmOffset
spiBase = base + spiOffset
} }
// Pin mode, a pin can be set in Input or Output, Clock or Pwm mode // Pin mode, a pin can be set in Input or Output, Clock or Pwm mode
@ -108,6 +115,7 @@ const (
Output Output
Clock Clock
Pwm Pwm
Spi
) )
// State of pin, High / Low // State of pin, High / Low
@ -137,9 +145,11 @@ var (
gpioMem []uint32 gpioMem []uint32
clkMem []uint32 clkMem []uint32
pwmMem []uint32 pwmMem []uint32
spiMem []uint32
gpioMem8 []uint8 gpioMem8 []uint8
clkMem8 []uint8 clkMem8 []uint8
pwmMem8 []uint8 pwmMem8 []uint8
spiMem8 []uint8
) )
// Set pin as Input // Set pin as Input
@ -232,10 +242,12 @@ func (pin Pin) EdgeDetected() bool {
return EdgeDetected(pin) return EdgeDetected(pin)
} }
// PinMode sets the mode (direction) of a given pin (Input, Output, Clock or Pwm) // PinMode sets the mode of a given pin (Input, Output, Clock, Pwm or Spi)
// //
// Clock is possible only for pins 4, 5, 6, 20, 21. // Clock is possible only for pins 4, 5, 6, 20, 21.
// Pwm is possible only for pins 12, 13, 18, 19. // Pwm is possible only for pins 12, 13, 18, 19.
//
// Spi mode should not be set by this directly, use SpiBegin instead.
func PinMode(pin Pin, mode Mode) { func PinMode(pin Pin, mode Mode) {
// Pin fsel register, 0 or 1 depending on bank // Pin fsel register, 0 or 1 depending on bank
@ -243,14 +255,17 @@ func PinMode(pin Pin, mode Mode) {
shift := (uint8(pin) % 10) * 3 shift := (uint8(pin) % 10) * 3
f := uint32(0) f := uint32(0)
const in = 0 // 000
const out = 1 // 001
const alt0 = 4 // 100 const alt0 = 4 // 100
const alt4 = 3 // 011
const alt5 = 2 // 010 const alt5 = 2 // 010
switch mode { switch mode {
case Input: case Input:
f = 0 // 000 f = in
case Output: case Output:
f = 1 // 001 f = out
case Clock: case Clock:
switch pin { switch pin {
case 4, 5, 6, 32, 34, 42, 43, 44: case 4, 5, 6, 32, 34, 42, 43, 44:
@ -269,12 +284,25 @@ func PinMode(pin Pin, mode Mode) {
default: default:
return return
} }
case Spi:
switch pin {
case 7, 8, 9, 10, 11: // SPI0
f = alt0
case 35, 36, 37, 38, 39: // SPI0
f = alt0
case 16, 17, 18, 19, 20, 21: // SPI1
f = alt4
case 40, 41, 42, 43, 44, 45: // SPI2
f = alt4
default:
return
}
} }
memlock.Lock() memlock.Lock()
defer memlock.Unlock() defer memlock.Unlock()
const pinMask = 7 // 0b111 - pinmode is 3 bits const pinMask = 7 // 111 - pinmode is 3 bits
gpioMem[fselReg] = (gpioMem[fselReg] &^ (pinMask << shift)) | (f << shift) gpioMem[fselReg] = (gpioMem[fselReg] &^ (pinMask << shift)) | (f << shift)
} }
@ -355,14 +383,14 @@ func DetectEdge(pin Pin, edge Edge) {
bit := uint32(1 << (p & 31)) bit := uint32(1 << (p & 31))
if edge&RiseEdge > 0 { // set bit if edge&RiseEdge > 0 { // set bit
gpioMem[renReg] = gpioMem[renReg] | bit gpioMem[renReg] |= bit
} else { // clear bit } else { // clear bit
gpioMem[renReg] = gpioMem[renReg] &^ bit gpioMem[renReg] &^= bit
} }
if edge&FallEdge > 0 { // set bit if edge&FallEdge > 0 { // set bit
gpioMem[fenReg] = gpioMem[fenReg] | bit gpioMem[fenReg] |= bit
} else { // clear bit } else { // clear bit
gpioMem[fenReg] = gpioMem[fenReg] &^ bit gpioMem[fenReg] &^= bit
} }
gpioMem[edsReg] = bit // to clear outdated detection gpioMem[edsReg] = bit // to clear outdated detection
@ -396,9 +424,9 @@ func PullMode(pin Pin, pull Pull) {
switch pull { switch pull {
case PullDown, PullUp: case PullDown, PullUp:
gpioMem[pullReg] = gpioMem[pullReg]&^3 | uint32(pull) gpioMem[pullReg] |= uint32(pull)
case PullOff: case PullOff:
gpioMem[pullReg] = gpioMem[pullReg] &^ 3 gpioMem[pullReg] &^= 3
} }
// Wait for value to clock in, this is ugly, sorry :( // Wait for value to clock in, this is ugly, sorry :(
@ -409,7 +437,7 @@ func PullMode(pin Pin, pull Pull) {
// Wait for value to clock in // Wait for value to clock in
time.Sleep(time.Microsecond) time.Sleep(time.Microsecond)
gpioMem[pullReg] = gpioMem[pullReg] &^ 3 gpioMem[pullReg] &^= 3
gpioMem[pullClkReg] = 0 gpioMem[pullClkReg] = 0
} }
@ -546,14 +574,14 @@ func SetDutyCycle(pin Pin, dutyLen, cycleLen uint32) {
func StopPwm() { func StopPwm() {
const pwmCtlReg = 0 const pwmCtlReg = 0
const pwen = 1 const pwen = 1
pwmMem[pwmCtlReg] = pwmMem[pwmCtlReg] &^ (pwen<<8 | pwen) pwmMem[pwmCtlReg] &^= pwen<<8 | pwen
} }
// Start pwm for both channels // Start pwm for both channels
func StartPwm() { func StartPwm() {
const pwmCtlReg = 0 const pwmCtlReg = 0
const pwen = 1 const pwen = 1
pwmMem[pwmCtlReg] = pwmMem[pwmCtlReg] | pwen<<8 | pwen pwmMem[pwmCtlReg] |= pwen<<8 | pwen
} }
// Open and memory map GPIO memory range from /dev/mem . // Open and memory map GPIO memory range from /dev/mem .
@ -593,6 +621,12 @@ func Open() (err error) {
return return
} }
// Memory map spi registers to slice
spiMem, spiMem8, err = memMap(file.Fd(), spiBase)
if err != nil {
return
}
return nil return nil
} }
@ -628,6 +662,9 @@ func Close() error {
if err := syscall.Munmap(pwmMem8); err != nil { if err := syscall.Munmap(pwmMem8); err != nil {
return err return err
} }
if err := syscall.Munmap(spiMem8); err != nil {
return err
}
return nil return nil
} }

191
spi.go Normal file
View File

@ -0,0 +1,191 @@
package rpio
import (
"errors"
)
type SpiDev int
// SPI devices.
// Only SPI0 supported for now.
const (
Spi0 SpiDev = iota
Spi1 // aux
Spi2 // aux
)
const (
csReg = 0
fifoReg = 1 // TX/RX FIFO
clkDivReg = 2
)
var (
SpiMapError = errors.New("SPI registers not mapped correctly - are you root?")
)
// Sets all pins of given SPI device to SPI mode
// dev\pin | CE0 | CE1 | CE2 | SCLK | MOSI | MISO |
// Spi0 | 7 | 8 | - | 9 | 10 | 11 |
// Spi1 | 16 | 17 | 18 | 19 | 20 | 21 |
// Spi2 | 40 | 41 | 42 | 43 | 44 | 45 |
//
// It also resets SPI control register.
//
// Note that you should disable SPI interface in raspi-config first!
func SpiBegin(dev SpiDev) error {
spiMem[csReg] = 0 // reset spi settings to default
if spiMem[csReg] == 0 {
// this should not read only zeroes after reset -> mem map failed
return SpiMapError
}
for _, pin := range getSpiPins(dev) {
pin.Mode(Spi)
}
clearSpiTxRxFifo()
setSpiDiv(128)
return nil
}
// Sets SPI pins of given device to default (Input) mode. See SpiBegin.
func SpiEnd(dev SpiDev) {
var pins = getSpiPins(dev)
for _, pin := range pins {
pin.Mode(Input)
}
}
// Set (maximal) speed [Hz] of SPI clock.
// Param speed may be as big as 125MHz in theory, but
// only values up to 31.25MHz are considered relayable.
func SpiSpeed(speed int) {
const baseFreq = 250 * 1000000
cdiv := uint32(baseFreq / speed)
setSpiDiv(cdiv)
}
// Select chip, one of 0, 1, 2
// for selecting slave on CE0, CE1, or CE2 pin
func SpiChipSelect(chip uint8) {
const csMask = 3 // chip select has 2 bits
cs := uint32(chip & csMask)
spiMem[csReg] = spiMem[csReg]&^csMask | cs
}
// Sets polarity (0/1) of active chip select
// default active=0
func SpiChipSelectPolarity(chip uint8, polarity uint8) {
if chip > 2 {
return
}
cspol := uint32(1 << (21 + chip)) // bit 21, 22 or 23 depending on chip
if polarity == 0 { // chip select is active low
spiMem[csReg] &^= cspol
} else { // chip select is active hight
spiMem[csReg] |= cspol
}
}
// Set polarity (0/1) and phase (0/1) of spi clock
// default polarity=0; phase=0
func SpiMode(polarity uint8, phase uint8) {
const cpol = 1 << 3
const cpha = 1 << 2
if polarity == 0 { // Rest state of clock = low
spiMem[csReg] &^= cpol
} else { // Rest state of clock = high
spiMem[csReg] |= cpol
}
if phase == 0 { // First SCLK transition at middle of data bit
spiMem[csReg] &^= cpha
} else { // First SCLK transition at beginning of data bit
spiMem[csReg] |= cpha
}
}
// SpiTransmit takes one or more bytes and send them to slave.
//
// Data received from slave are ignored.
// Use spread operator to send slice of bytes.
func SpiTransmit(data ...byte) {
SpiExchange(append(data[:0:0], data...)) // clone data because it will be rewriten by received bytes
}
// SpiReceive receives n bytes from slave.
//
// Note that n zeroed bytes are send to slave as side effect.
func SpiReceive(n int) []byte {
data := make([]byte, n, n)
SpiExchange(data)
return data
}
// Transmit all bytes in data to slave
// and simultaneously receives bytes from slave to data.
//
// If you want to only send or only receive, use SpiTransmit/SpiReceive
func SpiExchange(data []byte) {
const ta = 1 << 7 // transfer active
const txd = 1 << 18 // tx fifo can accept data
const rxd = 1 << 17 // rx fifo contains data
const done = 1 << 16
clearSpiTxRxFifo()
// set TA = 1
spiMem[csReg] |= ta
for i := range data {
// wait for TXD
for spiMem[csReg]&txd == 0 {
}
// write bytes to SPI_FIFO
spiMem[fifoReg] = uint32(data[i])
// wait for RXD
for spiMem[csReg]&rxd == 0 {
}
// read bytes from SPI_FIFO
data[i] = byte(spiMem[fifoReg])
}
// wait for DONE
for spiMem[csReg]&done == 0 {
}
// Set TA = 0
spiMem[csReg] &^= ta
}
// set spi clock divider value
func setSpiDiv(div uint32) {
const divMask = 1<<16 - 1 - 1 // cdiv have 16 bits and must be odd (for some reason)
spiMem[clkDivReg] = div & divMask
}
// clear both FIFOs
func clearSpiTxRxFifo() {
const clearTxRx = 1<<5 | 1<<4
spiMem[csReg] |= clearTxRx
}
func getSpiPins(dev SpiDev) []Pin {
switch dev {
case Spi0:
return []Pin{7, 8, 9, 10, 11}
// ommit 35, 36, 37, 38, 39 - only one set of SPI0 can be set in Spi mode at a time
case Spi1:
return []Pin{16, 17, 18, 19, 20, 21}
case Spi2:
return []Pin{40, 41, 42, 43, 44, 45}
default:
return []Pin{}
}
}

23
spi_test.go Normal file
View File

@ -0,0 +1,23 @@
package rpio
import ()
func ExampleSpiTransmit() {
SpiTransmit(0xFF) // send single byte
SpiTransmit(0xDE, 0xAD, 0xBE) // send several bytes
data := []byte{'H', 'e', 'l', 'l', 'o', 0}
SpiTransmit(data...) // send slice of bytes
}
func ExampleSpiBegin() {
err := SpiBegin(Spi0) // pins 7 to 11
if err != nil {
panic(err)
}
// any Spi functions must go there...
SpiTransmit(42)
SpiEnd(Spi0)
}