mirror of
https://github.com/stianeikeland/go-rpio.git
synced 2025-02-02 07:20:36 +01:00
Implement basic SPI
This commit is contained in:
parent
3abdd2207d
commit
094a866c10
80
rpio.go
80
rpio.go
|
@ -85,6 +85,7 @@ const (
|
||||||
gpioOffset = 0x200000
|
gpioOffset = 0x200000
|
||||||
clkOffset = 0x101000
|
clkOffset = 0x101000
|
||||||
pwmOffset = 0x20C000
|
pwmOffset = 0x20C000
|
||||||
|
spiOffset = 0x204000
|
||||||
|
|
||||||
memLength = 4096
|
memLength = 4096
|
||||||
)
|
)
|
||||||
|
@ -93,6 +94,7 @@ var (
|
||||||
gpioBase int64
|
gpioBase int64
|
||||||
clkBase int64
|
clkBase int64
|
||||||
pwmBase int64
|
pwmBase int64
|
||||||
|
spiBase int64
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -100,6 +102,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 +111,7 @@ const (
|
||||||
Output
|
Output
|
||||||
Clock
|
Clock
|
||||||
Pwm
|
Pwm
|
||||||
|
Spi
|
||||||
)
|
)
|
||||||
|
|
||||||
// State of pin, High / Low
|
// State of pin, High / Low
|
||||||
|
@ -137,9 +141,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
|
||||||
|
@ -243,14 +249,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,6 +278,19 @@ 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()
|
||||||
|
@ -284,19 +306,19 @@ func PinMode(pin Pin, mode Mode) {
|
||||||
func WritePin(pin Pin, state State) {
|
func WritePin(pin Pin, state State) {
|
||||||
p := uint8(pin)
|
p := uint8(pin)
|
||||||
|
|
||||||
// Clear register, 10 / 11 depending on bank
|
|
||||||
// Set register, 7 / 8 depending on bank
|
// Set register, 7 / 8 depending on bank
|
||||||
clearReg := p/32 + 10
|
// Clear register, 10 / 11 depending on bank
|
||||||
setReg := p/32 + 7
|
setReg := p/32 + 7
|
||||||
|
clearReg := p/32 + 10
|
||||||
|
|
||||||
memlock.Lock()
|
memlock.Lock()
|
||||||
defer memlock.Unlock()
|
|
||||||
|
|
||||||
if state == Low {
|
if state == Low {
|
||||||
gpioMem[clearReg] = 1 << (p & 31)
|
gpioMem[clearReg] = 1 << (p & 31)
|
||||||
} else {
|
} else {
|
||||||
gpioMem[setReg] = 1 << (p & 31)
|
gpioMem[setReg] = 1 << (p & 31)
|
||||||
}
|
}
|
||||||
|
memlock.Unlock() // not deferring saves ~600ns
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the state of a pin
|
// Read the state of a pin
|
||||||
|
@ -312,14 +334,23 @@ func ReadPin(pin Pin) State {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle a pin state (high -> low -> high)
|
// Toggle a pin state (high -> low -> high)
|
||||||
// TODO: probably possible to do this much faster without read
|
|
||||||
func TogglePin(pin Pin) {
|
func TogglePin(pin Pin) {
|
||||||
switch ReadPin(pin) {
|
p := uint8(pin)
|
||||||
case Low:
|
|
||||||
pin.High()
|
setReg := p/32 + 7
|
||||||
case High:
|
clearReg := p/32 + 10
|
||||||
pin.Low()
|
levelReg := p/32 + 13
|
||||||
|
|
||||||
|
bit := uint32(1 << (p & 31))
|
||||||
|
|
||||||
|
memlock.Lock()
|
||||||
|
|
||||||
|
if (gpioMem[levelReg] & bit) != 0 {
|
||||||
|
gpioMem[clearReg] = bit
|
||||||
|
} else {
|
||||||
|
gpioMem[setReg] = bit
|
||||||
}
|
}
|
||||||
|
memlock.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enable edge event detection on pin.
|
// Enable edge event detection on pin.
|
||||||
|
@ -346,14 +377,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
|
||||||
|
@ -387,9 +418,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 :(
|
||||||
|
@ -400,7 +431,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
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -537,14 +568,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 .
|
||||||
|
@ -584,6 +615,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,6 +656,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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
134
spi.go
Normal file
134
spi.go
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
package rpio
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
SpiMapError = errors.New("SPI registers not mapped correctly - are you root?")
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
SPI0 = iota // only spi0 supported for now
|
||||||
|
SPI1 // aux
|
||||||
|
SPI2 // aux
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
csReg = 0
|
||||||
|
fifoReg = 1 // TX/RX FIFO
|
||||||
|
clkDivReg = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
// Sets SPI pins of given device to SIP mode
|
||||||
|
// (CE0, CE1, [CE2], SCLK, MOSI, MISO)
|
||||||
|
// also reset SPI control register
|
||||||
|
func SpiBegin(dev int) 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
|
||||||
|
func SpiEnd(dev int) {
|
||||||
|
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
|
||||||
|
func SpiChipSelect(chip int) { // control & status
|
||||||
|
const csMask = 3 // chip select has 2 bits
|
||||||
|
|
||||||
|
cs := uint32(chip & csMask)
|
||||||
|
|
||||||
|
spiMem[csReg] = spiMem[csReg]&^csMask | cs
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transmit all bytes in data to slave
|
||||||
|
// and simultaneously receives bytes from slave to data
|
||||||
|
func SpiTransfer(data []byte) { // control & status
|
||||||
|
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
|
||||||
|
|
||||||
|
length := len(data)
|
||||||
|
i := 0 // data index
|
||||||
|
|
||||||
|
clearSpiTxRxFifo()
|
||||||
|
|
||||||
|
// set TA = 1
|
||||||
|
spiMem[csReg] |= ta
|
||||||
|
|
||||||
|
for i < length {
|
||||||
|
// Poll TXD writing bytes to SPI_FIFO
|
||||||
|
for spiMem[csReg]&txd == 0 {
|
||||||
|
}
|
||||||
|
spiMem[fifoReg] = uint32(data[i])
|
||||||
|
// Poll RXD reading bytes from SPI_FIFO
|
||||||
|
for spiMem[csReg]&rxd == 0 {
|
||||||
|
}
|
||||||
|
data[i] = byte(spiMem[fifoReg])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait for DONE
|
||||||
|
for spiMem[csReg]&done == 0 {
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set TA = 0
|
||||||
|
spiMem[csReg] &^= ta
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearSpiTxRxFifo() {
|
||||||
|
const clearTxRx = 1<<5 | 1<<4
|
||||||
|
|
||||||
|
spiMem[csReg] |= clearTxRx
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSpiPins(dev int) []Pin {
|
||||||
|
switch dev {
|
||||||
|
case SPI0:
|
||||||
|
return []Pin{
|
||||||
|
7, 8, 9, 10, 11,
|
||||||
|
// 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{}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user