gpio: analog pin support for the bbb

This commit is contained in:
Karan Misra 2014-03-23 04:59:35 +05:30
parent f667c93b7a
commit d64682bf34
8 changed files with 354 additions and 40 deletions

124
bbb.go
View File

@ -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
}

View File

@ -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
View File

@ -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()
}

View File

@ -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 {

View File

@ -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
View File

@ -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
View File

@ -1,3 +1,4 @@
analog
bh1750fvi
bmp085
bmp180

42
samples/analog.go Normal file
View 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
}
}
}