mirror of
https://github.com/kidoman/embd
synced 2024-12-22 12:50:19 +01:00
gpio: analog pin support for the bbb
This commit is contained in:
parent
f667c93b7a
commit
d64682bf34
124
bbb.go
124
bbb.go
@ -1,10 +1,19 @@
|
||||
package embd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func init() {
|
||||
Describers[HostBBB] = func(rev int) *Descriptor {
|
||||
return &Descriptor{
|
||||
GPIO: func() GPIO {
|
||||
return newGPIODriver(bbbPins)
|
||||
return newGPIODriver(bbbPins, newDigitalPin, newBBBAnalogPin)
|
||||
},
|
||||
I2C: newI2CDriver,
|
||||
}
|
||||
@ -69,12 +78,111 @@ var bbbPins = PinMap{
|
||||
&PinDesc{ID: "P9_30", Aliases: []string{"112", "GPIO_112", "SPI1_D1"}, Caps: CapNormal | CapSPI, DigitalLogical: 112},
|
||||
&PinDesc{ID: "P9_31", Aliases: []string{"110", "GPIO_110", "SPI1_SCLK"}, Caps: CapNormal | CapSPI, DigitalLogical: 110},
|
||||
&PinDesc{ID: "P9_32", Aliases: []string{"VADC"}},
|
||||
&PinDesc{ID: "P9_33", Aliases: []string{"AIN4"}, Caps: CapAnalog, AnalogLogical: 4},
|
||||
&PinDesc{ID: "P9_33", Aliases: []string{"4", "AIN4"}, Caps: CapAnalog, AnalogLogical: 4},
|
||||
&PinDesc{ID: "P9_34", Aliases: []string{"AGND"}},
|
||||
&PinDesc{ID: "P9_35", Aliases: []string{"AIN6"}, Caps: CapAnalog, AnalogLogical: 6},
|
||||
&PinDesc{ID: "P9_36", Aliases: []string{"AIN5"}, Caps: CapAnalog, AnalogLogical: 5},
|
||||
&PinDesc{ID: "P9_37", Aliases: []string{"AIN2"}, Caps: CapAnalog, AnalogLogical: 2},
|
||||
&PinDesc{ID: "P9_38", Aliases: []string{"AIN3"}, Caps: CapAnalog, AnalogLogical: 3},
|
||||
&PinDesc{ID: "P9_39", Aliases: []string{"AIN0"}, Caps: CapAnalog, AnalogLogical: 0},
|
||||
&PinDesc{ID: "P9_40", Aliases: []string{"AIN1"}, Caps: CapAnalog, AnalogLogical: 1},
|
||||
&PinDesc{ID: "P9_35", Aliases: []string{"6", "AIN6"}, Caps: CapAnalog, AnalogLogical: 6},
|
||||
&PinDesc{ID: "P9_36", Aliases: []string{"5", "AIN5"}, Caps: CapAnalog, AnalogLogical: 5},
|
||||
&PinDesc{ID: "P9_37", Aliases: []string{"2", "AIN2"}, Caps: CapAnalog, AnalogLogical: 2},
|
||||
&PinDesc{ID: "P9_38", Aliases: []string{"3", "AIN3"}, Caps: CapAnalog, AnalogLogical: 3},
|
||||
&PinDesc{ID: "P9_39", Aliases: []string{"0", "AIN0"}, Caps: CapAnalog, AnalogLogical: 0},
|
||||
&PinDesc{ID: "P9_40", Aliases: []string{"1", "AIN1"}, Caps: CapAnalog, AnalogLogical: 1},
|
||||
}
|
||||
|
||||
type bbbAnalogPin struct {
|
||||
n int
|
||||
|
||||
val *os.File
|
||||
|
||||
initialized bool
|
||||
}
|
||||
|
||||
func newBBBAnalogPin(n int) AnalogPin {
|
||||
return &bbbAnalogPin{n: n}
|
||||
}
|
||||
|
||||
func (p *bbbAnalogPin) N() int {
|
||||
return p.n
|
||||
}
|
||||
|
||||
func (p *bbbAnalogPin) init() error {
|
||||
if p.initialized {
|
||||
return nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if err = p.ensureEnabled(); err != nil {
|
||||
return err
|
||||
}
|
||||
if p.val, err = p.valueFile(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.initialized = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *bbbAnalogPin) ensureEnabled() error {
|
||||
file := "/sys/devices/bone_capemgr.8/slots"
|
||||
bytes, err := ioutil.ReadFile(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
str := string(bytes)
|
||||
if strings.Contains(str, "cape-bone-iio") {
|
||||
return nil
|
||||
}
|
||||
// Not initialized yet
|
||||
slots, err := os.OpenFile(file, os.O_WRONLY, os.ModeExclusive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer slots.Close()
|
||||
_, err = slots.WriteString("cape-bone-iio")
|
||||
return err
|
||||
}
|
||||
|
||||
func (p *bbbAnalogPin) valueFilePath() string {
|
||||
return fmt.Sprintf("/sys/devices/ocp.2/helper.14/AIN%v", p.n)
|
||||
}
|
||||
|
||||
func (p *bbbAnalogPin) openFile(path string) (*os.File, error) {
|
||||
return os.OpenFile(path, os.O_RDONLY, os.ModeExclusive)
|
||||
}
|
||||
|
||||
func (p *bbbAnalogPin) valueFile() (*os.File, error) {
|
||||
return p.openFile(p.valueFilePath())
|
||||
}
|
||||
|
||||
func (p *bbbAnalogPin) Read() (int, error) {
|
||||
if err := p.init(); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
p.val.Seek(0, 0)
|
||||
bytes, err := ioutil.ReadAll(p.val)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
str := string(bytes)
|
||||
str = strings.TrimSpace(str)
|
||||
return strconv.Atoi(str)
|
||||
}
|
||||
|
||||
func (p *bbbAnalogPin) Write(_ int) error {
|
||||
return errors.New("gpio: not implemented")
|
||||
}
|
||||
|
||||
func (p *bbbAnalogPin) Close() error {
|
||||
if !p.initialized {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := p.val.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
p.initialized = false
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -19,10 +19,14 @@ type digitalPin struct {
|
||||
initialized bool
|
||||
}
|
||||
|
||||
func newDigitalPin(n int) *digitalPin {
|
||||
func newDigitalPin(n int) DigitalPin {
|
||||
return &digitalPin{n: n}
|
||||
}
|
||||
|
||||
func (p *digitalPin) N() int {
|
||||
return p.n
|
||||
}
|
||||
|
||||
func (p *digitalPin) init() error {
|
||||
if p.initialized {
|
||||
return nil
|
||||
|
34
gpio.go
34
gpio.go
@ -13,6 +13,8 @@ const (
|
||||
)
|
||||
|
||||
type DigitalPin interface {
|
||||
N() int
|
||||
|
||||
Write(val int) error
|
||||
Read() (int, error)
|
||||
|
||||
@ -25,8 +27,18 @@ type DigitalPin interface {
|
||||
Close() error
|
||||
}
|
||||
|
||||
type AnalogPin interface {
|
||||
N() int
|
||||
|
||||
Write(val int) error
|
||||
Read() (int, error)
|
||||
|
||||
Close() error
|
||||
}
|
||||
|
||||
type GPIO interface {
|
||||
DigitalPin(key interface{}) (DigitalPin, error)
|
||||
AnalogPin(key interface{}) (AnalogPin, error)
|
||||
|
||||
Close() error
|
||||
}
|
||||
@ -87,3 +99,25 @@ func ActiveLow(key interface{}, b bool) error {
|
||||
|
||||
return pin.ActiveLow(b)
|
||||
}
|
||||
|
||||
func NewAnalogPin(key interface{}) (AnalogPin, error) {
|
||||
return gpioInstance.AnalogPin(key)
|
||||
}
|
||||
|
||||
func AnalogWrite(key interface{}, val int) error {
|
||||
pin, err := NewAnalogPin(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return pin.Write(val)
|
||||
}
|
||||
|
||||
func AnalogRead(key interface{}) (int, error) {
|
||||
pin, err := NewAnalogPin(key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return pin.Read()
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
package embd
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
type pin interface {
|
||||
@ -12,38 +11,53 @@ type pin interface {
|
||||
|
||||
type gpioDriver struct {
|
||||
pinMap PinMap
|
||||
|
||||
dpf func(n int) DigitalPin
|
||||
apf func(n int) AnalogPin
|
||||
|
||||
initializedPins map[string]pin
|
||||
}
|
||||
|
||||
func newGPIODriver(pinMap PinMap) *gpioDriver {
|
||||
func newGPIODriver(pinMap PinMap, dpf func(n int) DigitalPin, apf func(n int) AnalogPin) GPIO {
|
||||
return &gpioDriver{
|
||||
pinMap: pinMap,
|
||||
dpf: dpf,
|
||||
apf: apf,
|
||||
|
||||
initializedPins: map[string]pin{},
|
||||
}
|
||||
}
|
||||
|
||||
func (io *gpioDriver) lookupKey(key interface{}, cap int) (*PinDesc, bool) {
|
||||
return io.pinMap.Lookup(key, cap)
|
||||
}
|
||||
|
||||
func (io *gpioDriver) digitalPin(key interface{}) (*digitalPin, error) {
|
||||
pd, found := io.lookupKey(key, CapNormal)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("gpio: could not find pin matching %q", key)
|
||||
}
|
||||
|
||||
if pd.Caps != CapNormal {
|
||||
glog.Infof("gpio: pin %q is not a dedicated digital io pin. please refer to the system reference manual for more details", key)
|
||||
}
|
||||
|
||||
dp := newDigitalPin(pd.DigitalLogical)
|
||||
io.initializedPins[pd.ID] = dp
|
||||
|
||||
return dp, nil
|
||||
}
|
||||
|
||||
func (io *gpioDriver) DigitalPin(key interface{}) (DigitalPin, error) {
|
||||
return io.digitalPin(key)
|
||||
if io.dpf == nil {
|
||||
return nil, errors.New("gpio: digital io not supported on this host")
|
||||
}
|
||||
|
||||
pd, found := io.pinMap.Lookup(key, CapNormal)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("gpio: could not find pin matching %v", key)
|
||||
}
|
||||
|
||||
p := io.dpf(pd.DigitalLogical)
|
||||
io.initializedPins[pd.ID] = p
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (io *gpioDriver) AnalogPin(key interface{}) (AnalogPin, error) {
|
||||
if io.apf == nil {
|
||||
return nil, errors.New("gpio: analog io not supported on this host")
|
||||
}
|
||||
|
||||
pd, found := io.pinMap.Lookup(key, CapAnalog)
|
||||
if !found {
|
||||
return nil, fmt.Errorf("gpio: could not find pin matching %v", key)
|
||||
}
|
||||
|
||||
p := io.apf(pd.AnalogLogical)
|
||||
io.initializedPins[pd.ID] = p
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func (io *gpioDriver) Close() error {
|
||||
|
@ -2,6 +2,46 @@ package embd
|
||||
|
||||
import "testing"
|
||||
|
||||
type fakeDigitalPin struct {
|
||||
n int
|
||||
}
|
||||
|
||||
func (p *fakeDigitalPin) N() int {
|
||||
return p.n
|
||||
}
|
||||
|
||||
func (*fakeDigitalPin) SetDirection(dir Direction) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*fakeDigitalPin) Read() (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (*fakeDigitalPin) Write(val int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*fakeDigitalPin) ActiveLow(b bool) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*fakeDigitalPin) PullUp() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*fakeDigitalPin) PullDown() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*fakeDigitalPin) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newFakeDigitalPin(n int) DigitalPin {
|
||||
return &fakeDigitalPin{n}
|
||||
}
|
||||
|
||||
func TestGpioDriverDigitalPin(t *testing.T) {
|
||||
var tests = []struct {
|
||||
key interface{}
|
||||
@ -12,15 +52,86 @@ func TestGpioDriverDigitalPin(t *testing.T) {
|
||||
var pinMap = PinMap{
|
||||
&PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapNormal, DigitalLogical: 1},
|
||||
}
|
||||
driver := newGPIODriver(pinMap)
|
||||
driver := newGPIODriver(pinMap, newFakeDigitalPin, nil)
|
||||
for _, test := range tests {
|
||||
pin, err := driver.digitalPin(test.key)
|
||||
pin, err := driver.DigitalPin(test.key)
|
||||
if err != nil {
|
||||
t.Errorf("Looking up %v: unexpected error: %v", test.key, err)
|
||||
continue
|
||||
}
|
||||
if pin.n != test.n {
|
||||
t.Errorf("Looking up %v: got %v, want %v", test.key, pin.n, test.n)
|
||||
if pin.N() != test.n {
|
||||
t.Errorf("Looking up %v: got %v, want %v", test.key, pin.N(), test.n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type fakeAnalogPin struct {
|
||||
n int
|
||||
}
|
||||
|
||||
func (p *fakeAnalogPin) N() int {
|
||||
return p.n
|
||||
}
|
||||
|
||||
func (*fakeAnalogPin) Read() (int, error) {
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (*fakeAnalogPin) Write(val int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*fakeAnalogPin) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newFakeAnalogPin(n int) AnalogPin {
|
||||
return &fakeAnalogPin{n}
|
||||
}
|
||||
|
||||
func TestGpioDriverAnalogPin(t *testing.T) {
|
||||
var tests = []struct {
|
||||
key interface{}
|
||||
n int
|
||||
}{
|
||||
{1, 1},
|
||||
}
|
||||
var pinMap = PinMap{
|
||||
&PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapAnalog, AnalogLogical: 1},
|
||||
}
|
||||
driver := newGPIODriver(pinMap, nil, newFakeAnalogPin)
|
||||
for _, test := range tests {
|
||||
pin, err := driver.AnalogPin(test.key)
|
||||
if err != nil {
|
||||
t.Errorf("Looking up %v: unexpected error: %v", test.key, err)
|
||||
continue
|
||||
}
|
||||
if pin.N() != test.n {
|
||||
t.Errorf("Looking up %v: got %v, want %v", test.key, pin.N(), test.n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestGpioDriverUnavailablePinType(t *testing.T) {
|
||||
var pinMap = PinMap{
|
||||
&PinDesc{ID: "P1_1", Aliases: []string{"1"}, Caps: CapNormal, DigitalLogical: 1},
|
||||
&PinDesc{ID: "P1_2", Aliases: []string{"1"}, Caps: CapAnalog, AnalogLogical: 1},
|
||||
}
|
||||
driver := newGPIODriver(pinMap, nil, nil)
|
||||
_, err := driver.DigitalPin(1)
|
||||
if err == nil {
|
||||
t.Fatal("Looking up digital pin 1: did not get error")
|
||||
}
|
||||
expected := "gpio: digital io not supported on this host"
|
||||
if err.Error() != expected {
|
||||
t.Fatalf("Looking up digital pin 1: got error %q, expected %q", err, expected)
|
||||
}
|
||||
_, err = driver.AnalogPin(1)
|
||||
if err == nil {
|
||||
t.Fatal("Looking up analog pin 1: did not get error")
|
||||
}
|
||||
expected = "gpio: analog io not supported on this host"
|
||||
if err.Error() != expected {
|
||||
t.Fatalf("Looking up analog pin 1: got error %q, expected %q", err, expected)
|
||||
}
|
||||
}
|
||||
|
2
rpi.go
2
rpi.go
@ -9,7 +9,7 @@ func init() {
|
||||
|
||||
return &Descriptor{
|
||||
GPIO: func() GPIO {
|
||||
return newGPIODriver(pins)
|
||||
return newGPIODriver(pins, newDigitalPin, nil)
|
||||
},
|
||||
I2C: newI2CDriver,
|
||||
}
|
||||
|
1
samples/.gitignore
vendored
1
samples/.gitignore
vendored
@ -1,3 +1,4 @@
|
||||
analog
|
||||
bh1750fvi
|
||||
bmp085
|
||||
bmp180
|
||||
|
42
samples/analog.go
Normal file
42
samples/analog.go
Normal file
@ -0,0 +1,42 @@
|
||||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"time"
|
||||
|
||||
"github.com/kidoman/embd"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := embd.InitGPIO(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer embd.CloseGPIO()
|
||||
|
||||
pin, err := embd.NewAnalogPin(0)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer pin.Close()
|
||||
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, os.Interrupt, os.Kill)
|
||||
defer signal.Stop(quit)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-time.After(100 * time.Millisecond):
|
||||
val, err := pin.Read()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("reading: %v\n", val)
|
||||
case <-quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user