mirror of
https://github.com/stianeikeland/go-rpio.git
synced 2025-01-23 10:41:03 +01:00
commit
14a5036b21
65
rpio.go
65
rpio.go
|
@ -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
191
spi.go
Normal 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
23
spi_test.go
Normal 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)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user