From d64682bf3480e44893e6f3096d0e4bfc316cb460 Mon Sep 17 00:00:00 2001 From: Karan Misra Date: Sun, 23 Mar 2014 04:59:35 +0530 Subject: [PATCH] gpio: analog pin support for the bbb --- bbb.go | 124 ++++++++++++++++++++++++++++++++++++++++++--- digitalpin.go | 6 ++- gpio.go | 34 +++++++++++++ gpiodriver.go | 66 ++++++++++++++---------- gpiodriver_test.go | 119 +++++++++++++++++++++++++++++++++++++++++-- rpi.go | 2 +- samples/.gitignore | 1 + samples/analog.go | 42 +++++++++++++++ 8 files changed, 354 insertions(+), 40 deletions(-) create mode 100644 samples/analog.go diff --git a/bbb.go b/bbb.go index 7189af4..cd47153 100644 --- a/bbb.go +++ b/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 } diff --git a/digitalpin.go b/digitalpin.go index 856f6dd..6f5bec8 100644 --- a/digitalpin.go +++ b/digitalpin.go @@ -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 diff --git a/gpio.go b/gpio.go index 6332822..b27fe64 100644 --- a/gpio.go +++ b/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() +} diff --git a/gpiodriver.go b/gpiodriver.go index d37686d..1ed29db 100644 --- a/gpiodriver.go +++ b/gpiodriver.go @@ -1,9 +1,8 @@ package embd import ( + "errors" "fmt" - - "github.com/golang/glog" ) type pin interface { @@ -11,39 +10,54 @@ type pin interface { } type gpioDriver struct { - pinMap PinMap + 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, + 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 { diff --git a/gpiodriver_test.go b/gpiodriver_test.go index 746c00a..d9e6369 100644 --- a/gpiodriver_test.go +++ b/gpiodriver_test.go @@ -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) + } +} diff --git a/rpi.go b/rpi.go index 709842b..ec83a41 100644 --- a/rpi.go +++ b/rpi.go @@ -9,7 +9,7 @@ func init() { return &Descriptor{ GPIO: func() GPIO { - return newGPIODriver(pins) + return newGPIODriver(pins, newDigitalPin, nil) }, I2C: newI2CDriver, } diff --git a/samples/.gitignore b/samples/.gitignore index 6e55bb7..0ea67af 100644 --- a/samples/.gitignore +++ b/samples/.gitignore @@ -1,3 +1,4 @@ +analog bh1750fvi bmp085 bmp180 diff --git a/samples/analog.go b/samples/analog.go new file mode 100644 index 0000000..2348989 --- /dev/null +++ b/samples/analog.go @@ -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 + } + } +}