From b5e2d0acc7511be878851947ab4f34949f923663 Mon Sep 17 00:00:00 2001 From: Karan Misra Date: Thu, 27 Feb 2014 04:24:53 +0530 Subject: [PATCH] bring in the idea of a hardware abstraction layer --- .gitignore | 3 + doc.go | 2 +- gpio/data.go | 41 --- gpio/gpio.go | 221 +-------------- gpio/pin.go | 149 ---------- gpio/pindesc.go | 7 - gpio/pinmap.go | 24 -- hal.go | 35 +++ host/describe.go | 28 ++ host/detect.go | 78 ++++++ host/generic/linux/gpio/digitalpin.go | 114 ++++++++ host/generic/linux/gpio/gpio.go | 149 ++++++++++ host/generic/linux/i2c/bus.go | 278 +++++++++++++++++++ host/generic/linux/i2c/i2c.go | 43 +++ host/rpi/data.go | 45 +++ host/rpi/descriptor.go | 29 ++ i2c/i2c.go | 376 +------------------------- samples/.gitignore | 2 + samples/bh1750fvi.go | 10 +- samples/bmp085.go | 10 +- samples/bmp180.go | 10 +- samples/gpio.go | 20 +- samples/gpiodetect.go | 51 ++++ samples/l3gd20.go | 10 +- samples/lsm303.go | 10 +- samples/mcp4725.go | 9 +- samples/pca9685.go | 12 +- samples/servo.go | 13 +- samples/tmp006.go | 9 +- samples/us020.go | 20 +- sensor/bh1750fvi/bh1750fvi.go | 23 -- sensor/bmp085/bmp085.go | 33 --- sensor/bmp180/bmp180.go | 33 --- sensor/lsm303/lsm303.go | 23 -- sensor/us020/us020.go | 45 +-- 35 files changed, 994 insertions(+), 971 deletions(-) create mode 100644 .gitignore delete mode 100644 gpio/data.go delete mode 100644 gpio/pin.go delete mode 100644 gpio/pindesc.go delete mode 100644 gpio/pinmap.go create mode 100644 hal.go create mode 100644 host/describe.go create mode 100644 host/detect.go create mode 100644 host/generic/linux/gpio/digitalpin.go create mode 100644 host/generic/linux/gpio/gpio.go create mode 100644 host/generic/linux/i2c/bus.go create mode 100644 host/generic/linux/i2c/i2c.go create mode 100644 host/rpi/data.go create mode 100644 host/rpi/descriptor.go create mode 100644 samples/.gitignore create mode 100644 samples/gpiodetect.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..05d3170 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.floo* +.vagrant/ +Vagrantfile diff --git a/doc.go b/doc.go index 25e78e8..f5a8daf 100644 --- a/doc.go +++ b/doc.go @@ -26,4 +26,4 @@ Find out the heading from the LSM303 magnetometer: ... heading, err := lsm303.Heading() */ -package rpi +package embd diff --git a/gpio/data.go b/gpio/data.go deleted file mode 100644 index 7de3b1c..0000000 --- a/gpio/data.go +++ /dev/null @@ -1,41 +0,0 @@ -package gpio - -var rev1Pins = pinMap{ - &pinDesc{0, []string{"P1_3", "GPIO_0", "SDA", "I2C0_SDA"}, normal | i2c}, - &pinDesc{1, []string{"P1_5", "GPIO_1", "SCL", "I2C0_SCL"}, normal | i2c}, - &pinDesc{4, []string{"P1_7", "GPIO_4", "GPCLK0"}, normal}, - &pinDesc{14, []string{"P1_8", "GPIO_14", "TXD", "UART0_TXD"}, normal | uart}, - &pinDesc{15, []string{"P1_10", "GPIO_15", "RXD", "UART0_RXD"}, normal | uart}, - &pinDesc{17, []string{"P1_11", "GPIO_17"}, normal}, - &pinDesc{18, []string{"P1_12", "GPIO_18", "PCM_CLK"}, normal}, - &pinDesc{21, []string{"P1_13", "GPIO_21"}, normal}, - &pinDesc{22, []string{"P1_15", "GPIO_22"}, normal}, - &pinDesc{23, []string{"P1_16", "GPIO_23"}, normal}, - &pinDesc{24, []string{"P1_18", "GPIO_24"}, normal}, - &pinDesc{10, []string{"P1_19", "GPIO_10", "MOSI", "SPI0_MOSI"}, normal | spi}, - &pinDesc{9, []string{"P1_21", "GPIO_9", "MISO", "SPI0_MISO"}, normal | spi}, - &pinDesc{25, []string{"P1_22", "GPIO_25"}, normal}, - &pinDesc{11, []string{"P1_23", "GPIO_11", "SCLK", "SPI0_SCLK"}, normal | spi}, - &pinDesc{8, []string{"P1_24", "GPIO_8", "CE0", "SPI0_CE0_N"}, normal | spi}, - &pinDesc{7, []string{"P1_26", "GPIO_7", "CE1", "SPI0_CE1_N"}, normal | spi}, -} - -var rev2Pins = pinMap{ - &pinDesc{2, []string{"P1_3", "GPIO_2", "SDA", "I2C1_SDA"}, normal | i2c}, - &pinDesc{3, []string{"P1_5", "GPIO_3", "SCL", "I2C1_SCL"}, normal | i2c}, - &pinDesc{4, []string{"P1_7", "GPIO_4", "GPCLK0"}, normal}, - &pinDesc{14, []string{"P1_8", "GPIO_14", "TXD", "UART0_TXD"}, normal | uart}, - &pinDesc{15, []string{"P1_10", "GPIO_15", "RXD", "UART0_RXD"}, normal | uart}, - &pinDesc{17, []string{"P1_11", "GPIO_17"}, normal}, - &pinDesc{18, []string{"P1_12", "GPIO_18", "PCM_CLK"}, normal}, - &pinDesc{27, []string{"P1_13", "GPIO_27"}, normal}, - &pinDesc{22, []string{"P1_15", "GPIO_22"}, normal}, - &pinDesc{23, []string{"P1_16", "GPIO_23"}, normal}, - &pinDesc{24, []string{"P1_18", "GPIO_24"}, normal}, - &pinDesc{10, []string{"P1_19", "GPIO_10", "MOSI", "SPI0_MOSI"}, normal | spi}, - &pinDesc{9, []string{"P1_21", "GPIO_9", "MISO", "SPI0_MISO"}, normal | spi}, - &pinDesc{25, []string{"P1_22", "GPIO_25"}, normal}, - &pinDesc{11, []string{"P1_23", "GPIO_11", "SCLK", "SPI0_SCLK"}, normal | spi}, - &pinDesc{8, []string{"P1_24", "GPIO_8", "CE0", "SPI0_CE0_N"}, normal | spi}, - &pinDesc{7, []string{"P1_26", "GPIO_7", "CE1", "SPI0_CE1_N"}, normal | spi}, -} diff --git a/gpio/gpio.go b/gpio/gpio.go index 9d1f370..3e9881c 100644 --- a/gpio/gpio.go +++ b/gpio/gpio.go @@ -1,226 +1,29 @@ package gpio -import ( - "fmt" - "os" - "strconv" - "sync" - - "github.com/golang/glog" -) - type Direction int -type State int -type Pull int const ( - Input Direction = iota - Output + In Direction = iota + Out ) const ( - Low State = iota + Low int = iota High ) -const ( - PullOff Pull = iota - PullDown - PullUp -) +type DigitalPin interface { + Write(val int) error + Read() (int, error) -const ( - normal = 1 << iota - i2c - spi - uart -) + SetDir(dir Direction) error + ActiveLow(b bool) error -type gpio struct { - exporter, unexporter *os.File - - initialized bool - - pinMap pinMap - initializedPins map[int]*pin + Close() error } -var instance *gpio -var instanceLock sync.Mutex +type GPIO interface { + DigitalPin(key interface{}) (DigitalPin, error) -var Default *gpio - -func New() *gpio { - instanceLock.Lock() - defer instanceLock.Unlock() - - if instance == nil { - instance = &gpio{ - pinMap: rev2Pins, - initializedPins: map[int]*pin{}, - } - } - - return instance -} - -func (io *gpio) init() (err error) { - if io.initialized { - return - } - - if io.exporter, err = os.OpenFile("/sys/class/gpio/export", os.O_WRONLY, os.ModeExclusive); err != nil { - return - } - if io.unexporter, err = os.OpenFile("/sys/class/gpio/unexport", os.O_WRONLY, os.ModeExclusive); err != nil { - return - } - - io.initialized = true - - return -} - -func (io *gpio) lookupKey(key interface{}) (*pinDesc, bool) { - return io.pinMap.lookup(key) -} - -func (io *gpio) export(n int) (err error) { - _, err = io.exporter.WriteString(strconv.Itoa(n)) - return -} - -func (io *gpio) unexport(n int) (err error) { - _, err = io.unexporter.WriteString(strconv.Itoa(n)) - return -} - -func (io *gpio) pin(key interface{}) (pin *pin, err error) { - pd, found := io.lookupKey(key) - if !found { - err = fmt.Errorf("gpio: could not find pin matching %q", key) - return - } - - n := pd.n - - var ok bool - if pin, ok = io.initializedPins[n]; ok { - return - } - - if pd.caps&normal == 0 { - err = fmt.Errorf("gpio: sorry, pin %q cannot be used for GPIO", key) - return - } - - if pd.caps != normal { - glog.Infof("gpio: pin %q is not a dedicated GPIO pin. please refer to the system reference manual for more details", key) - } - - if err = io.export(n); err != nil { - return - } - - if pin, err = NewPin(n); err != nil { - io.unexport(n) - return - } - - io.initializedPins[n] = pin - - return -} - -func (io *gpio) Pin(key interface{}) (pin *pin, err error) { - if err = io.init(); err != nil { - return - } - - return io.pin(key) -} - -func (io *gpio) Mode(key interface{}, dir Direction) (err error) { - if err = io.init(); err != nil { - return - } - - var pin *pin - if pin, err = io.pin(key); err != nil { - return - } - - return pin.Mode(dir) -} - -func (io *gpio) Input(key interface{}) error { - return io.Mode(key, Input) -} - -func (io *gpio) Output(key interface{}) error { - return io.Mode(key, Output) -} - -func (io *gpio) Read(key interface{}) (state State, err error) { - if err = io.init(); err != nil { - return - } - - var pin *pin - if pin, err = io.pin(key); err != nil { - return - } - - return pin.Read() -} - -func (io *gpio) Write(key interface{}, state State) (err error) { - if err = io.init(); err != nil { - return - } - - var pin *pin - if pin, err = io.pin(key); err != nil { - return - } - - return pin.Write(state) -} - -func (io *gpio) Low(key interface{}) error { - return io.Write(key, Low) -} - -func (io *gpio) High(key interface{}) error { - return io.Write(key, High) -} - -func (io *gpio) SetActiveLow(key interface{}, b bool) (err error) { - if err = io.init(); err != nil { - return - } - - var pin *pin - if pin, err = io.pin(key); err != nil { - return - } - - return pin.SetActiveLow(b) -} - -func (io *gpio) ActiveLow(key interface{}) error { - return io.SetActiveLow(key, true) -} - -func (io *gpio) ActiveHigh(key interface{}) error { - return io.SetActiveLow(key, false) -} - -func (io *gpio) Close() { - for n := range io.initializedPins { - io.unexport(n) - } - - io.exporter.Close() - io.unexporter.Close() + Close() error } diff --git a/gpio/pin.go b/gpio/pin.go deleted file mode 100644 index b9fea91..0000000 --- a/gpio/pin.go +++ /dev/null @@ -1,149 +0,0 @@ -package gpio - -import ( - "fmt" - "os" - "path" -) - -type pin struct { - n int - - dir *os.File - val *os.File - activeLow *os.File - edge *os.File -} - -func NewPin(n int) (p *pin, err error) { - p = &pin{n: n} - err = p.init() - return -} - -func (p *pin) init() (err error) { - if p.dir, err = p.directionFile(); err != nil { - return - } - if p.val, err = p.valueFile(); err != nil { - return - } - if p.activeLow, err = p.activeLowFile(); err != nil { - return - } - if p.edge, err = p.edgeFile(); err != nil { - return - } - - return -} - -func (p *pin) basePath() string { - return fmt.Sprintf("/sys/class/gpio/gpio%v", p.n) -} - -func (p *pin) openFile(path string) (*os.File, error) { - return os.OpenFile(path, os.O_RDWR, os.ModeExclusive) -} - -func (p *pin) directionPath() string { - return path.Join(p.basePath(), "direction") -} - -func (p *pin) directionFile() (*os.File, error) { - return p.openFile(p.directionPath()) -} - -func (p *pin) valuePath() string { - return path.Join(p.basePath(), "value") -} - -func (p *pin) valueFile() (*os.File, error) { - return p.openFile(p.valuePath()) -} - -func (p *pin) activeLowPath() string { - return path.Join(p.basePath(), "active_low") -} - -func (p *pin) activeLowFile() (*os.File, error) { - return p.openFile(p.activeLowPath()) -} - -func (p *pin) edgePath() string { - return path.Join(p.basePath(), "edge") -} - -func (p *pin) edgeFile() (*os.File, error) { - return p.openFile(p.edgePath()) -} - -func (p *pin) Mode(dir Direction) (err error) { - str := "in" - if dir == Output { - str = "out" - } - _, err = p.dir.WriteString(str) - return -} - -func (p *pin) Input() error { - return p.Mode(Input) -} - -func (p *pin) Output() error { - return p.Mode(Output) -} - -func (p *pin) Read() (s State, err error) { - buf := make([]byte, 1) - if _, err = p.val.Read(buf); err != nil { - return - } - s = Low - if buf[0] == '1' { - s = High - } - return -} - -func (p *pin) Write(s State) (err error) { - str := "0" - if s == High { - str = "1" - } - _, err = p.val.WriteString(str) - return -} - -func (p *pin) Low() error { - return p.Write(Low) -} - -func (p *pin) High() error { - return p.Write(High) -} - -func (p *pin) SetActiveLow(b bool) (err error) { - str := "0" - if b { - str = "1" - } - _, err = p.activeLow.WriteString(str) - return -} - -func (p *pin) ActiveLow() error { - return p.SetActiveLow(true) -} - -func (p *pin) ActiveHigh() error { - return p.SetActiveLow(false) -} - -func (p *pin) Close() { - p.dir.Close() - p.val.Close() - p.activeLow.Close() - p.edge.Close() -} diff --git a/gpio/pindesc.go b/gpio/pindesc.go deleted file mode 100644 index 7a66b1a..0000000 --- a/gpio/pindesc.go +++ /dev/null @@ -1,7 +0,0 @@ -package gpio - -type pinDesc struct { - n int - ids []string - caps int -} diff --git a/gpio/pinmap.go b/gpio/pinmap.go deleted file mode 100644 index 908bfd2..0000000 --- a/gpio/pinmap.go +++ /dev/null @@ -1,24 +0,0 @@ -package gpio - -type pinMap []*pinDesc - -func (m pinMap) lookup(k interface{}) (*pinDesc, bool) { - switch key := k.(type) { - case int: - for i := range m { - if m[i].n == key { - return m[i], true - } - } - case string: - for i := range m { - for j := range m[i].ids { - if m[i].ids[j] == key { - return m[i], true - } - } - } - } - - return nil, false -} diff --git a/hal.go b/hal.go new file mode 100644 index 0000000..b9c9704 --- /dev/null +++ b/hal.go @@ -0,0 +1,35 @@ +package embd + +import ( + "github.com/kidoman/embd/gpio" + "github.com/kidoman/embd/host" + "github.com/kidoman/embd/i2c" +) + +const ( + In = gpio.In + Out = gpio.Out +) + +const ( + Low = gpio.Low + High = gpio.High +) + +func NewGPIO() (gpio.GPIO, error) { + desc, err := host.Describe() + if err != nil { + return nil, err + } + + return desc.GPIO(), nil +} + +func NewI2C() (i2c.I2C, error) { + desc, err := host.Describe() + if err != nil { + return nil, err + } + + return desc.I2C(), nil +} diff --git a/host/describe.go b/host/describe.go new file mode 100644 index 0000000..8fc3a89 --- /dev/null +++ b/host/describe.go @@ -0,0 +1,28 @@ +package host + +import ( + "errors" + + "github.com/kidoman/embd/gpio" + "github.com/kidoman/embd/host/rpi" + "github.com/kidoman/embd/i2c" +) + +type Descriptor interface { + GPIO() gpio.GPIO + I2C() i2c.I2C +} + +func Describe() (Descriptor, error) { + host, rev, err := Detect() + if err != nil { + return nil, err + } + + switch host { + case RPi: + return rpi.Descriptor(rev), nil + default: + return nil, errors.New("host: invalid host") + } +} diff --git a/host/detect.go b/host/detect.go new file mode 100644 index 0000000..4a3dad9 --- /dev/null +++ b/host/detect.go @@ -0,0 +1,78 @@ +package host + +import ( + "fmt" + "os/exec" + "strconv" + "strings" +) + +type Host int + +const ( + Null Host = iota + RPi + BBB +) + +func execOutput(name string, arg ...string) (output string, err error) { + var out []byte + if out, err = exec.Command(name, arg...).Output(); err != nil { + return + } + output = string(out) + return +} + +func nodeName() (string, error) { + return execOutput("uname", "-n") +} + +func kernelVersion() (major, minor, patch int, err error) { + output, err := execOutput("uname", "-r") + if err != nil { + return + } + + parts := strings.Split(output, ".") + + if major, err = strconv.Atoi(parts[0]); err != nil { + return + } + if minor, err = strconv.Atoi(parts[1]); err != nil { + return + } + if patch, err = strconv.Atoi(parts[2]); err != nil { + return + } + + return +} + +func Detect() (host Host, rev int, err error) { + major, minor, patch, err := kernelVersion() + if err != nil { + return + } + + if major < 3 || (major == 3 && minor < 8) { + err = fmt.Errorf("embd: linux kernel versions lower than 3.8 are not supported. you have %v.%v.%v", major, minor, patch) + return + } + + node, err := nodeName() + if err != nil { + return + } + + switch node { + case "raspberrypi": + host = RPi + case "beaglebone": + host = BBB + default: + err = fmt.Errorf("embd: your host %q is not supported at this moment. please request support at https://github.com/kidoman/embd/issues", node) + } + + return +} diff --git a/host/generic/linux/gpio/digitalpin.go b/host/generic/linux/gpio/digitalpin.go new file mode 100644 index 0000000..7393a78 --- /dev/null +++ b/host/generic/linux/gpio/digitalpin.go @@ -0,0 +1,114 @@ +package gpio + +import ( + "fmt" + "os" + "path" + + "github.com/kidoman/embd/gpio" +) + +type digitalPin struct { + n int + + dir *os.File + val *os.File + activeLow *os.File + edge *os.File +} + +func newDigitalPin(n int) (p *digitalPin, err error) { + p = &digitalPin{n: n} + err = p.init() + return +} + +func (p *digitalPin) init() (err error) { + if p.dir, err = p.directionFile(); err != nil { + return + } + if p.val, err = p.valueFile(); err != nil { + return + } + if p.activeLow, err = p.activeLowFile(); err != nil { + return + } + + return +} + +func (p *digitalPin) basePath() string { + return fmt.Sprintf("/sys/class/gpio/gpio%v", p.n) +} + +func (p *digitalPin) openFile(path string) (*os.File, error) { + return os.OpenFile(path, os.O_RDWR, os.ModeExclusive) +} + +func (p *digitalPin) directionFile() (*os.File, error) { + return p.openFile(path.Join(p.basePath(), "direction")) +} + +func (p *digitalPin) valueFile() (*os.File, error) { + return p.openFile(path.Join(p.basePath(), "value")) +} + +func (p *digitalPin) activeLowFile() (*os.File, error) { + return p.openFile(path.Join(p.basePath(), "active_low")) +} + +func (p *digitalPin) SetDir(dir gpio.Direction) (err error) { + str := "in" + if dir == gpio.Out { + str = "out" + } + _, err = p.dir.WriteString(str) + return +} + +func (p *digitalPin) Read() (val int, err error) { + buf := make([]byte, 1) + if _, err = p.val.Read(buf); err != nil { + return + } + val = 0 + if buf[0] == '1' { + val = 1 + } + return +} + +func (p *digitalPin) Write(val int) (err error) { + str := "0" + if val == gpio.High { + str = "1" + } + _, err = p.val.WriteString(str) + return +} + +func (p *digitalPin) ActiveLow(b bool) (err error) { + str := "0" + if b { + str = "1" + } + _, err = p.activeLow.WriteString(str) + return +} + +func (p *digitalPin) Close() error { + if err := p.dir.Close(); err != nil { + return err + } + if err := p.val.Close(); err != nil { + return err + } + if err := p.activeLow.Close(); err != nil { + return err + } + if err := p.edge.Close(); err != nil { + return err + } + + return nil +} diff --git a/host/generic/linux/gpio/gpio.go b/host/generic/linux/gpio/gpio.go new file mode 100644 index 0000000..2a0add0 --- /dev/null +++ b/host/generic/linux/gpio/gpio.go @@ -0,0 +1,149 @@ +package gpio + +import ( + "fmt" + "os" + "strconv" + + "github.com/golang/glog" + "github.com/kidoman/embd/gpio" +) + +const ( + Normal int = 1 << iota + I2C + UART + SPI +) + +type PinDesc struct { + N int + IDs []string + Caps int +} + +type PinMap []*PinDesc + +func (m PinMap) Lookup(k interface{}) (*PinDesc, bool) { + switch key := k.(type) { + case int: + for i := range m { + if m[i].N == key { + return m[i], true + } + } + case string: + for i := range m { + for j := range m[i].IDs { + if m[i].IDs[j] == key { + return m[i], true + } + } + } + } + + return nil, false +} + +type GPIO struct { + exporter, unexporter *os.File + + initialized bool + + pinMap PinMap + initializedPins map[int]*digitalPin +} + +func New(pinMap PinMap) *GPIO { + return &GPIO{ + pinMap: pinMap, + initializedPins: map[int]*digitalPin{}, + } +} + +func (io *GPIO) init() (err error) { + if io.initialized { + return + } + + if io.exporter, err = os.OpenFile("/sys/class/gpio/export", os.O_WRONLY, os.ModeExclusive); err != nil { + return + } + if io.unexporter, err = os.OpenFile("/sys/class/gpio/unexport", os.O_WRONLY, os.ModeExclusive); err != nil { + return + } + + io.initialized = true + + return +} + +func (io *GPIO) lookupKey(key interface{}) (*PinDesc, bool) { + return io.pinMap.Lookup(key) +} + +func (io *GPIO) export(n int) (err error) { + _, err = io.exporter.WriteString(strconv.Itoa(n)) + return +} + +func (io *GPIO) unexport(n int) (err error) { + _, err = io.unexporter.WriteString(strconv.Itoa(n)) + return +} + +func (io *GPIO) digitalPin(key interface{}) (p *digitalPin, err error) { + pd, found := io.lookupKey(key) + if !found { + err = fmt.Errorf("gpio: could not find pin matching %q", key) + return + } + + n := pd.N + + var ok bool + if p, ok = io.initializedPins[n]; ok { + return + } + + if pd.Caps&Normal == 0 { + err = fmt.Errorf("gpio: sorry, pin %q cannot be used for GPIO", key) + return + } + + if pd.Caps != Normal { + glog.Infof("gpio: pin %q is not a dedicated GPIO pin. please refer to the system reference manual for more details", key) + } + + if err = io.export(n); err != nil { + return + } + + if p, err = newDigitalPin(n); err != nil { + io.unexport(n) + return + } + + io.initializedPins[n] = p + + return +} + +func (io *GPIO) DigitalPin(key interface{}) (gpio.DigitalPin, error) { + if err := io.init(); err != nil { + return nil, err + } + + return io.digitalPin(key) +} + +func (io *GPIO) Close() error { + for n := range io.initializedPins { + io.unexport(n) + } + + io.exporter.Close() + io.unexporter.Close() + + return nil +} diff --git a/host/generic/linux/i2c/bus.go b/host/generic/linux/i2c/bus.go new file mode 100644 index 0000000..e7074a8 --- /dev/null +++ b/host/generic/linux/i2c/bus.go @@ -0,0 +1,278 @@ +package i2c + +import ( + "fmt" + "os" + "reflect" + "sync" + "syscall" + "time" + "unsafe" +) + +const ( + delay = 20 + + slaveCmd = 0x0703 // Cmd to set slave address + rdrwCmd = 0x0707 // Cmd to read/write data together + + rd = 0x0001 +) + +type i2c_msg struct { + addr uint16 + flags uint16 + len uint16 + buf uintptr +} + +type i2c_rdwr_ioctl_data struct { + msgs uintptr + nmsg uint32 +} + +type bus struct { + l byte + file *os.File + addr byte + mu sync.Mutex +} + +func newBus(l byte) (*bus, error) { + b := &bus{l: l} + + var err error + if b.file, err = os.OpenFile(fmt.Sprintf("/dev/i2c-%v", b.l), os.O_RDWR, os.ModeExclusive); err != nil { + return nil, err + } + + return b, nil +} + +func (b *bus) Close() error { + b.mu.Lock() + defer b.mu.Unlock() + + return b.file.Close() +} + +func (b *bus) setAddress(addr byte) (err error) { + if addr != b.addr { + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), slaveCmd, uintptr(addr)); errno != 0 { + err = syscall.Errno(errno) + return + } + + b.addr = addr + } + + return +} + +func (b *bus) ReadByte(addr byte) (value byte, err error) { + b.mu.Lock() + defer b.mu.Unlock() + + if err = b.setAddress(addr); err != nil { + return + } + + bytes := make([]byte, 1) + n, err := b.file.Read(bytes) + + if n != 1 { + err = fmt.Errorf("i2c: Unexpected number (%v) of bytes read", n) + } + + value = bytes[0] + + return +} + +func (b *bus) WriteByte(addr, value byte) (err error) { + b.mu.Lock() + defer b.mu.Unlock() + + if err = b.setAddress(addr); err != nil { + return + } + + n, err := b.file.Write([]byte{value}) + + if n != 1 { + err = fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteByte", n) + } + + return +} + +func (b *bus) WriteBytes(addr byte, value []byte) (err error) { + b.mu.Lock() + defer b.mu.Unlock() + + if err = b.setAddress(addr); err != nil { + return err + } + + for i := range value { + n, err := b.file.Write([]byte{value[i]}) + + if n != 1 { + return fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteBytes", n) + } + if err != nil { + return err + } + + time.Sleep(delay * time.Millisecond) + } + + return nil +} + +func (b *bus) ReadFromReg(addr, reg byte, value []byte) (err error) { + b.mu.Lock() + defer b.mu.Unlock() + + if err = b.setAddress(addr); err != nil { + return + } + + hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&value)) + + var messages [2]i2c_msg + messages[0].addr = uint16(addr) + messages[0].flags = 0 + messages[0].len = 1 + messages[0].buf = uintptr(unsafe.Pointer(®)) + + messages[1].addr = uint16(addr) + messages[1].flags = rd + messages[1].len = uint16(len(value)) + messages[1].buf = uintptr(unsafe.Pointer(hdrp.Data)) + + var packets i2c_rdwr_ioctl_data + + packets.msgs = uintptr(unsafe.Pointer(&messages)) + packets.nmsg = 2 + + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 { + return syscall.Errno(errno) + } + + return nil +} + +func (b *bus) ReadByteFromReg(addr, reg byte) (value byte, err error) { + buf := make([]byte, 1) + if err = b.ReadFromReg(addr, reg, buf); err != nil { + return + } + value = buf[0] + return +} + +func (b *bus) ReadWordFromReg(addr, reg byte) (value uint16, err error) { + buf := make([]byte, 2) + if err = b.ReadFromReg(addr, reg, buf); err != nil { + return + } + value = uint16((uint16(buf[0]) << 8) | uint16(buf[1])) + return +} + +func (b *bus) WriteToReg(addr, reg byte, value []byte) (err error) { + b.mu.Lock() + defer b.mu.Unlock() + + if err = b.setAddress(addr); err != nil { + return + } + + outbuf := append([]byte{reg}, value...) + + hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&outbuf)) + + var message i2c_msg + message.addr = uint16(addr) + message.flags = 0 + message.len = uint16(len(outbuf)) + message.buf = uintptr(unsafe.Pointer(&hdrp.Data)) + + var packets i2c_rdwr_ioctl_data + + packets.msgs = uintptr(unsafe.Pointer(&message)) + packets.nmsg = 1 + + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 { + err = syscall.Errno(errno) + return + } + + return +} + +func (b *bus) WriteByteToReg(addr, reg, value byte) (err error) { + b.mu.Lock() + defer b.mu.Unlock() + + if err = b.setAddress(addr); err != nil { + return + } + + outbuf := [...]byte{ + reg, + value, + } + + var message i2c_msg + message.addr = uint16(addr) + message.flags = 0 + message.len = uint16(len(outbuf)) + message.buf = uintptr(unsafe.Pointer(&outbuf)) + + var packets i2c_rdwr_ioctl_data + + packets.msgs = uintptr(unsafe.Pointer(&message)) + packets.nmsg = 1 + + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 { + err = syscall.Errno(errno) + return + } + + return +} + +func (b *bus) WriteWordToReg(addr, reg byte, value uint16) (err error) { + b.mu.Lock() + defer b.mu.Unlock() + + if err = b.setAddress(addr); err != nil { + return + } + + outbuf := [...]byte{ + reg, + byte(value >> 8), + byte(value), + } + + var messages i2c_msg + messages.addr = uint16(addr) + messages.flags = 0 + messages.len = uint16(len(outbuf)) + messages.buf = uintptr(unsafe.Pointer(&outbuf)) + + var packets i2c_rdwr_ioctl_data + + packets.msgs = uintptr(unsafe.Pointer(&messages)) + packets.nmsg = 1 + + if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 { + err = syscall.Errno(errno) + return + } + + return +} diff --git a/host/generic/linux/i2c/i2c.go b/host/generic/linux/i2c/i2c.go new file mode 100644 index 0000000..e967323 --- /dev/null +++ b/host/generic/linux/i2c/i2c.go @@ -0,0 +1,43 @@ +// Package i2c enables gophers i2c speaking ability. +package i2c + +import ( + "sync" + + "github.com/kidoman/embd/i2c" +) + +type I2C struct { + busMap map[byte]*bus + busMapLock sync.Mutex +} + +func New() *I2C { + return &I2C{ + busMap: make(map[byte]*bus), + } +} + +func (i *I2C) Bus(l byte) i2c.Bus { + i.busMapLock.Lock() + defer i.busMapLock.Unlock() + + var b *bus + + if b = i.busMap[l]; b == nil { + b = &bus{l: l} + i.busMap[l] = b + } + + return b +} + +func (i *I2C) Close() error { + for _, b := range i.busMap { + b.Close() + + delete(i.busMap, b.l) + } + + return nil +} diff --git a/host/rpi/data.go b/host/rpi/data.go new file mode 100644 index 0000000..b1eb59e --- /dev/null +++ b/host/rpi/data.go @@ -0,0 +1,45 @@ +package rpi + +import ( + "github.com/kidoman/embd/host/generic/linux/gpio" +) + +var rev1Pins = gpio.PinMap{ + &gpio.PinDesc{0, []string{"P1_3", "GPIO_0", "SDA", "I2C0_SDA"}, gpio.Normal | gpio.I2C}, + &gpio.PinDesc{1, []string{"P1_5", "GPIO_1", "SCL", "I2C0_SCL"}, gpio.Normal | gpio.I2C}, + &gpio.PinDesc{4, []string{"P1_7", "GPIO_4", "GPCLK0"}, gpio.Normal}, + &gpio.PinDesc{14, []string{"P1_8", "GPIO_14", "TXD", "UART0_TXD"}, gpio.Normal | gpio.UART}, + &gpio.PinDesc{15, []string{"P1_10", "GPIO_15", "RXD", "UART0_RXD"}, gpio.Normal | gpio.UART}, + &gpio.PinDesc{17, []string{"P1_11", "GPIO_17"}, gpio.Normal}, + &gpio.PinDesc{18, []string{"P1_12", "GPIO_18", "PCM_CLK"}, gpio.Normal}, + &gpio.PinDesc{21, []string{"P1_13", "GPIO_21"}, gpio.Normal}, + &gpio.PinDesc{22, []string{"P1_15", "GPIO_22"}, gpio.Normal}, + &gpio.PinDesc{23, []string{"P1_16", "GPIO_23"}, gpio.Normal}, + &gpio.PinDesc{24, []string{"P1_18", "GPIO_24"}, gpio.Normal}, + &gpio.PinDesc{10, []string{"P1_19", "GPIO_10", "MOSI", "SPI0_MOSI"}, gpio.Normal | gpio.SPI}, + &gpio.PinDesc{9, []string{"P1_21", "GPIO_9", "MISO", "SPI0_MISO"}, gpio.Normal | gpio.SPI}, + &gpio.PinDesc{25, []string{"P1_22", "GPIO_25"}, gpio.Normal}, + &gpio.PinDesc{11, []string{"P1_23", "GPIO_11", "SCLK", "SPI0_SCLK"}, gpio.Normal | gpio.SPI}, + &gpio.PinDesc{8, []string{"P1_24", "GPIO_8", "CE0", "SPI0_CE0_N"}, gpio.Normal | gpio.SPI}, + &gpio.PinDesc{7, []string{"P1_26", "GPIO_7", "CE1", "SPI0_CE1_N"}, gpio.Normal | gpio.SPI}, +} + +var rev2Pins = gpio.PinMap{ + &gpio.PinDesc{2, []string{"P1_3", "GPIO_2", "SDA", "I2C1_SDA"}, gpio.Normal | gpio.I2C}, + &gpio.PinDesc{3, []string{"P1_5", "GPIO_3", "SCL", "I2C1_SCL"}, gpio.Normal | gpio.I2C}, + &gpio.PinDesc{4, []string{"P1_7", "GPIO_4", "GPCLK0"}, gpio.Normal}, + &gpio.PinDesc{14, []string{"P1_8", "GPIO_14", "TXD", "UART0_TXD"}, gpio.Normal | gpio.UART}, + &gpio.PinDesc{15, []string{"P1_10", "GPIO_15", "RXD", "UART0_RXD"}, gpio.Normal | gpio.UART}, + &gpio.PinDesc{17, []string{"P1_11", "GPIO_17"}, gpio.Normal}, + &gpio.PinDesc{18, []string{"P1_12", "GPIO_18", "PCM_CLK"}, gpio.Normal}, + &gpio.PinDesc{27, []string{"P1_13", "GPIO_27"}, gpio.Normal}, + &gpio.PinDesc{22, []string{"P1_15", "GPIO_22"}, gpio.Normal}, + &gpio.PinDesc{23, []string{"P1_16", "GPIO_23"}, gpio.Normal}, + &gpio.PinDesc{24, []string{"P1_18", "GPIO_24"}, gpio.Normal}, + &gpio.PinDesc{10, []string{"P1_19", "GPIO_10", "MOSI", "SPI0_MOSI"}, gpio.Normal | gpio.SPI}, + &gpio.PinDesc{9, []string{"P1_21", "GPIO_9", "MISO", "SPI0_MISO"}, gpio.Normal | gpio.SPI}, + &gpio.PinDesc{25, []string{"P1_22", "GPIO_25"}, gpio.Normal}, + &gpio.PinDesc{11, []string{"P1_23", "GPIO_11", "SCLK", "SPI0_SCLK"}, gpio.Normal | gpio.SPI}, + &gpio.PinDesc{8, []string{"P1_24", "GPIO_8", "CE0", "SPI0_CE0_N"}, gpio.Normal | gpio.SPI}, + &gpio.PinDesc{7, []string{"P1_26", "GPIO_7", "CE1", "SPI0_CE1_N"}, gpio.Normal | gpio.SPI}, +} diff --git a/host/rpi/descriptor.go b/host/rpi/descriptor.go new file mode 100644 index 0000000..0966088 --- /dev/null +++ b/host/rpi/descriptor.go @@ -0,0 +1,29 @@ +package rpi + +import ( + "github.com/kidoman/embd/gpio" + lgpio "github.com/kidoman/embd/host/generic/linux/gpio" + li2c "github.com/kidoman/embd/host/generic/linux/i2c" + "github.com/kidoman/embd/i2c" +) + +type descriptor struct { + rev int +} + +func (d *descriptor) GPIO() gpio.GPIO { + var pins = rev1Pins + if d.rev > 1 { + pins = rev2Pins + } + + return lgpio.New(pins) +} + +func (d *descriptor) I2C() i2c.I2C { + return li2c.New() +} + +func Descriptor(rev int) *descriptor { + return &descriptor{rev} +} diff --git a/i2c/i2c.go b/i2c/i2c.go index 8c62c19..5aa07eb 100644 --- a/i2c/i2c.go +++ b/i2c/i2c.go @@ -1,26 +1,6 @@ // Package i2c enables gophers i2c speaking ability. package i2c -import ( - "fmt" - "os" - "reflect" - "sync" - "syscall" - "time" - "unsafe" -) - -const ( - delay = 20 - - slaveCmd = 0x0703 // Cmd to set slave address - rdrwCmd = 0x0707 // Cmd to read/write data together - - rd = 0x0001 -) - -// A Bus implements access to the I2C two wire bus. type Bus interface { // ReadByte reads a byte from the given address. ReadByte(addr byte) (value byte, err error) @@ -44,356 +24,8 @@ type Bus interface { WriteWordToReg(addr, reg byte, value uint16) error } -type i2c_msg struct { - addr uint16 - flags uint16 - len uint16 - buf uintptr -} - -type i2c_rdwr_ioctl_data struct { - msgs uintptr - nmsg uint32 -} - -var busMap map[byte]*bus -var busMapLock sync.Mutex - -// Default instance of the i2c bus -var Default Bus - -type bus struct { - l byte - file *os.File - addr byte - mu sync.Mutex -} - -func init() { - busMap = make(map[byte]*bus) - Default = NewBus(1) -} - -// NewBus creates a new I2C bus interface. The l variable -// controls which bus we connect to. -// -// For the newer RaspberryPi, the number is 1 (earlier model uses 0.) -func NewBus(l byte) Bus { - busMapLock.Lock() - defer busMapLock.Unlock() - - var b *bus - - if b = busMap[l]; b == nil { - b = &bus{l: l} - busMap[l] = b - } - - return b -} - -func (b *bus) init() (err error) { - if b.file != nil { - return - } - - if b.file, err = os.OpenFile(fmt.Sprintf("/dev/i2c-%v", b.l), os.O_RDWR, os.ModeExclusive); err != nil { - return - } - - return -} - -func (b *bus) setAddress(addr byte) (err error) { - if addr != b.addr { - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), slaveCmd, uintptr(addr)); errno != 0 { - err = syscall.Errno(errno) - return - } - - b.addr = addr - } - - return -} - -func (b *bus) ReadByte(addr byte) (value byte, err error) { - b.mu.Lock() - defer b.mu.Unlock() - - if err = b.init(); err != nil { - return - } - - if err = b.setAddress(addr); err != nil { - return - } - - bytes := make([]byte, 1) - n, err := b.file.Read(bytes) - - if n != 1 { - err = fmt.Errorf("i2c: Unexpected number (%v) of bytes read", n) - } - - value = bytes[0] - - return -} - -func (b *bus) WriteByte(addr, value byte) (err error) { - b.mu.Lock() - defer b.mu.Unlock() - - if err = b.init(); err != nil { - return - } - - if err = b.setAddress(addr); err != nil { - return - } - - n, err := b.file.Write([]byte{value}) - - if n != 1 { - err = fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteByte", n) - } - - return -} - -func (b *bus) WriteBytes(addr byte, value []byte) (err error) { - b.mu.Lock() - defer b.mu.Unlock() - - if err = b.init(); err != nil { - return - } - - if err = b.setAddress(addr); err != nil { - return err - } - - for i := range value { - n, err := b.file.Write([]byte{value[i]}) - - if n != 1 { - return fmt.Errorf("i2c: Unexpected number (%v) of bytes written in WriteBytes", n) - } - if err != nil { - return err - } - - time.Sleep(delay * time.Millisecond) - } - - return nil -} - -func (b *bus) ReadFromReg(addr, reg byte, value []byte) (err error) { - b.mu.Lock() - defer b.mu.Unlock() - - if err = b.init(); err != nil { - return - } - - if err = b.setAddress(addr); err != nil { - return - } - - hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&value)) - - var messages [2]i2c_msg - messages[0].addr = uint16(addr) - messages[0].flags = 0 - messages[0].len = 1 - messages[0].buf = uintptr(unsafe.Pointer(®)) - - messages[1].addr = uint16(addr) - messages[1].flags = rd - messages[1].len = uint16(len(value)) - messages[1].buf = uintptr(unsafe.Pointer(hdrp.Data)) - - var packets i2c_rdwr_ioctl_data - - packets.msgs = uintptr(unsafe.Pointer(&messages)) - packets.nmsg = 2 - - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 { - return syscall.Errno(errno) - } - - return nil -} - -func (b *bus) ReadByteFromReg(addr, reg byte) (value byte, err error) { - buf := make([]byte, 1) - if err = b.ReadFromReg(addr, reg, buf); err != nil { - return - } - value = buf[0] - return -} - -func (b *bus) ReadWordFromReg(addr, reg byte) (value uint16, err error) { - buf := make([]byte, 2) - if err = b.ReadFromReg(addr, reg, buf); err != nil { - return - } - value = uint16((uint16(buf[0]) << 8) | uint16(buf[1])) - return -} - -func (b *bus) WriteToReg(addr, reg byte, value []byte) (err error) { - b.mu.Lock() - defer b.mu.Unlock() - - if err = b.init(); err != nil { - return - } - - if err = b.setAddress(addr); err != nil { - return - } - - outbuf := append([]byte{reg}, value...) - - hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&outbuf)) - - var message i2c_msg - message.addr = uint16(addr) - message.flags = 0 - message.len = uint16(len(outbuf)) - message.buf = uintptr(unsafe.Pointer(&hdrp.Data)) - - var packets i2c_rdwr_ioctl_data - - packets.msgs = uintptr(unsafe.Pointer(&message)) - packets.nmsg = 1 - - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 { - err = syscall.Errno(errno) - return - } - - return -} - -func (b *bus) WriteByteToReg(addr, reg, value byte) (err error) { - b.mu.Lock() - defer b.mu.Unlock() - - if err = b.init(); err != nil { - return - } - - if err = b.setAddress(addr); err != nil { - return - } - - outbuf := [...]byte{ - reg, - value, - } - - var message i2c_msg - message.addr = uint16(addr) - message.flags = 0 - message.len = uint16(len(outbuf)) - message.buf = uintptr(unsafe.Pointer(&outbuf)) - - var packets i2c_rdwr_ioctl_data - - packets.msgs = uintptr(unsafe.Pointer(&message)) - packets.nmsg = 1 - - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 { - err = syscall.Errno(errno) - return - } - - return -} - -func (b *bus) WriteWordToReg(addr, reg byte, value uint16) (err error) { - b.mu.Lock() - defer b.mu.Unlock() - - if err = b.init(); err != nil { - return - } - - if err = b.setAddress(addr); err != nil { - return - } - - outbuf := [...]byte{ - reg, - byte(value >> 8), - byte(value), - } - - var messages i2c_msg - messages.addr = uint16(addr) - messages.flags = 0 - messages.len = uint16(len(outbuf)) - messages.buf = uintptr(unsafe.Pointer(&outbuf)) - - var packets i2c_rdwr_ioctl_data - - packets.msgs = uintptr(unsafe.Pointer(&messages)) - packets.nmsg = 1 - - if _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, b.file.Fd(), rdrwCmd, uintptr(unsafe.Pointer(&packets))); errno != 0 { - err = syscall.Errno(errno) - return - } - - return -} - -// ReadByte reads a byte from the given address. -func ReadByte(addr byte) (value byte, err error) { - return Default.ReadByte(addr) -} - -// WriteByte writes a byte to the given address. -func WriteByte(addr, value byte) error { - return Default.WriteByte(addr, value) -} - -// WriteBytes writes a slice bytes to the given address. -func WriteBytes(addr byte, value []byte) error { - return Default.WriteBytes(addr, value) -} - -// ReadFromReg reads n (len(value)) bytes from the given address and register. -func ReadFromReg(addr, reg byte, value []byte) error { - return Default.ReadFromReg(addr, reg, value) -} - -// ReadByteFromReg reads a byte from the given address and register. -func ReadByteFromReg(addr, reg byte) (value byte, err error) { - return Default.ReadByteFromReg(addr, reg) -} - -// ReadU16FromReg reads a unsigned 16 bit integer from the given address and register. -func ReadWordFromReg(addr, reg byte) (value uint16, err error) { - return Default.ReadWordFromReg(addr, reg) -} - -// WriteToReg writes len(value) bytes to the given address and register. -func WriteToReg(addr, reg byte, value []byte) error { - return Default.WriteToReg(addr, reg, value) -} - -// WriteByteToReg writes a byte to the given address and register. -func WriteByteToReg(addr, reg, value byte) error { - return Default.WriteByteToReg(addr, reg, value) -} - -// WriteU16ToReg -func WriteWordToReg(addr, reg byte, value uint16) error { - return Default.WriteWordToReg(addr, reg, value) +type I2C interface { + Bus(l byte) Bus + + Close() error } diff --git a/samples/.gitignore b/samples/.gitignore new file mode 100644 index 0000000..f4a4211 --- /dev/null +++ b/samples/.gitignore @@ -0,0 +1,2 @@ +gpio +gpiodetect diff --git a/samples/bh1750fvi.go b/samples/bh1750fvi.go index d907589..1bf8ebd 100644 --- a/samples/bh1750fvi.go +++ b/samples/bh1750fvi.go @@ -3,13 +3,17 @@ package main import ( "log" "time" - - "github.com/kidoman/embd/i2c" + "github.com/kidoman/embd" "github.com/kidoman/embd/sensor/bh1750fvi" ) func main() { - bus := i2c.NewBus(1) + i2c, err := embd.NewI2C() + if err != nil { + panic(err) + } + + bus := i2c.Bus(1) sensor := bh1750fvi.New(bh1750fvi.High, bus) defer sensor.Close() diff --git a/samples/bmp085.go b/samples/bmp085.go index 072f918..519acf4 100644 --- a/samples/bmp085.go +++ b/samples/bmp085.go @@ -3,13 +3,17 @@ package main import ( "log" "time" - - "github.com/kidoman/embd/i2c" + "github.com/kidoman/embd" "github.com/kidoman/embd/sensor/bmp085" ) func main() { - bus := i2c.NewBus(1) + i2c, err := embd.NewI2C() + if err != nil { + panic(err) + } + + bus := i2c.Bus(1) baro := bmp085.New(bus) defer baro.Close() diff --git a/samples/bmp180.go b/samples/bmp180.go index 0093545..7f899b1 100644 --- a/samples/bmp180.go +++ b/samples/bmp180.go @@ -3,13 +3,17 @@ package main import ( "log" "time" - - "github.com/kidoman/embd/i2c" + "github.com/kidoman/embd" "github.com/kidoman/embd/sensor/bmp180" ) func main() { - bus := i2c.NewBus(1) + i2c, err := embd.NewI2C() + if err != nil { + panic(err) + } + + bus := i2c.Bus(1) baro := bmp180.New(bus) defer baro.Close() diff --git a/samples/gpio.go b/samples/gpio.go index 979e3fd..48c78aa 100644 --- a/samples/gpio.go +++ b/samples/gpio.go @@ -3,31 +3,31 @@ package main import ( "time" - "github.com/kidoman/embd/gpio" + "github.com/kidoman/embd" ) func main() { - io := gpio.New() - defer io.Close() + gpio, err := embd.NewGPIO() + if err != nil { + panic(err) + } + defer gpio.Close() - pin, err := io.Pin("MOSI") + led, err := gpio.DigitalPin(10) if err != nil { panic(err) } - if err := pin.Output(); err != nil { + if err := led.SetDir(embd.Out); err != nil { panic(err) } - if err := pin.ActiveLow(); err != nil { - panic(err) - } - if err := pin.Low(); err != nil { + if err := led.Write(embd.High); err != nil { panic(err) } time.Sleep(1 * time.Second) - if err := pin.Input(); err != nil { + if err := led.SetDir(embd.In); err != nil { panic(err) } } diff --git a/samples/gpiodetect.go b/samples/gpiodetect.go new file mode 100644 index 0000000..cbd0e81 --- /dev/null +++ b/samples/gpiodetect.go @@ -0,0 +1,51 @@ +package main + +import ( + "time" + + "github.com/kidoman/embd" + "github.com/kidoman/embd/host" +) + +func main() { + h, _, err := host.Detect() + if err != nil { + return + } + + var pinNo interface{} + + switch h { + case host.BBB: + pinNo = "P9_31" + case host.RPi: + pinNo = 10 + default: + panic("host not supported (yet :P)") + } + + gpio, err := embd.NewGPIO() + if err != nil { + panic(err) + } + defer gpio.Close() + + led, err := gpio.DigitalPin(pinNo) + if err != nil { + panic(err) + } + defer led.Close() + + if err := led.SetDir(embd.Out); err != nil { + panic(err) + } + if err := led.Write(embd.High); err != nil { + panic(err) + } + + time.Sleep(1 * time.Second) + + if err := led.SetDir(embd.In); err != nil { + panic(err) + } +} diff --git a/samples/l3gd20.go b/samples/l3gd20.go index d47c904..6d824b8 100644 --- a/samples/l3gd20.go +++ b/samples/l3gd20.go @@ -5,13 +5,17 @@ import ( "os" "os/signal" "time" - - "github.com/kidoman/embd/i2c" + "github.com/kidoman/embd" "github.com/kidoman/embd/sensor/l3gd20" ) func main() { - bus := i2c.NewBus(1) + i2c, err := embd.NewI2C() + if err != nil { + panic(err) + } + + bus := i2c.Bus(1) gyro := l3gd20.New(bus, l3gd20.R250DPS) gyro.Debug = true diff --git a/samples/lsm303.go b/samples/lsm303.go index 6881386..399610f 100644 --- a/samples/lsm303.go +++ b/samples/lsm303.go @@ -4,12 +4,18 @@ import ( "log" "time" - "github.com/kidoman/embd/i2c" + "github.com/kidoman/embd" "github.com/kidoman/embd/sensor/lsm303" ) func main() { - bus := i2c.NewBus(1) + i2c, err := embd.NewI2C() + if err != nil { + panic(err) + } + defer i2c.Close() + + bus := i2c.Bus(1) mems := lsm303.New(bus) defer mems.Close() diff --git a/samples/mcp4725.go b/samples/mcp4725.go index dec98f0..275dee9 100644 --- a/samples/mcp4725.go +++ b/samples/mcp4725.go @@ -6,12 +6,17 @@ import ( "os" "os/signal" + "github.com/kidoman/embd" "github.com/kidoman/embd/controller/mcp4725" - "github.com/kidoman/embd/i2c" ) func main() { - bus := i2c.NewBus(1) + i2c, err := embd.NewI2C() + if err != nil { + panic(err) + } + + bus := i2c.Bus(1) dac := mcp4725.New(bus, 0x62) defer dac.Close() diff --git a/samples/pca9685.go b/samples/pca9685.go index a23f42d..978e50b 100644 --- a/samples/pca9685.go +++ b/samples/pca9685.go @@ -6,14 +6,20 @@ import ( "os/signal" "time" + "github.com/kidoman/embd" "github.com/kidoman/embd/controller/pca9685" - "github.com/kidoman/embd/i2c" ) func main() { - bus := i2c.NewBus(1) + i2c, err := embd.NewI2C() + if err != nil { + panic(err) + } - pca9685 := pca9685.New(bus, 0x41, 1000) + bus := i2c.Bus(1) + + pca9685 := pca9685.New(bus, 0x41) + pca9685.Freq = 1000 pca9685.Debug = true defer pca9685.Close() diff --git a/samples/servo.go b/samples/servo.go index dd44451..384b2c0 100644 --- a/samples/servo.go +++ b/samples/servo.go @@ -1,20 +1,25 @@ package main import ( - "log" "os" "os/signal" "time" + "github.com/kidoman/embd" "github.com/kidoman/embd/controller/pca9685" - "github.com/kidoman/embd/i2c" "github.com/kidoman/embd/motion/servo" ) func main() { - bus := i2c.NewBus(1) + i2c, err := embd.NewI2C() + if err != nil { + panic(err) + } - pwm := pca9685.New(bus, 0x41, 50) + bus := i2c.Bus(1) + + pwm := pca9685.New(bus, 0x41) + pwm.Freq = 50 pwm.Debug = true defer pwm.Close() diff --git a/samples/tmp006.go b/samples/tmp006.go index eaed5aa..78b8c2f 100644 --- a/samples/tmp006.go +++ b/samples/tmp006.go @@ -5,12 +5,17 @@ import ( "os" "os/signal" - "github.com/kidoman/embd/i2c" + "github.com/kidoman/embd" "github.com/kidoman/embd/sensor/tmp006" ) func main() { - bus := i2c.NewBus(1) + i2c, err := embd.NewI2C() + if err != nil { + panic(err) + } + + bus := i2c.Bus(1) sensor := tmp006.New(bus, 0x40) if status, err := sensor.Present(); err != nil || !status { diff --git a/samples/us020.go b/samples/us020.go index 59a535c..0cb5859 100644 --- a/samples/us020.go +++ b/samples/us020.go @@ -4,15 +4,27 @@ import ( "log" "time" + "github.com/kidoman/embd" "github.com/kidoman/embd/sensor/us020" - "github.com/stianeikeland/go-rpio" ) func main() { - rpio.Open() - defer rpio.Close() + gpio, err := embd.NewGPIO() + if err != nil { + panic(err) + } + defer gpio.Close() - rf := us020.New(10, 9, nil) + echoPin, err := gpio.DigitalPin(10) + if err != nil { + panic(err) + } + triggerPin, err := gpio.DigitalPin(9) + if err != nil { + panic(err) + } + + rf := us020.New(echoPin, triggerPin, nil) defer rf.Close() for { diff --git a/sensor/bh1750fvi/bh1750fvi.go b/sensor/bh1750fvi/bh1750fvi.go index 151c4c1..285eba4 100644 --- a/sensor/bh1750fvi/bh1750fvi.go +++ b/sensor/bh1750fvi/bh1750fvi.go @@ -53,9 +53,6 @@ type bh1750fvi struct { poll int } -// Default instance for BH1750FVI sensor -var Default = New(High, i2c.Default) - // Supports three modes: // "H" -> High resolution mode (1lx), takes 120ms (recommended). // "H2" -> High resolution mode 2 (0.5lx), takes 120ms (only use for low light). @@ -149,23 +146,3 @@ func (d *bh1750fvi) Close() { func (d *bh1750fvi) SetPollDelay(delay int) { d.poll = delay } - -// SetPollDelay sets the delay between run of data acquisition loop. -func SetPollDelay(delay int) { - Default.SetPollDelay(delay) -} - -// Lighting returns the ambient lighting in lx. -func Lighting() (lighting float64, err error) { - return Default.Lighting() -} - -// Run starts continuous sensor data acquisition loop. -func Run() (err error) { - return Default.Run() -} - -// Close. -func Close() { - Default.Close() -} diff --git a/sensor/bmp085/bmp085.go b/sensor/bmp085/bmp085.go index 04b5eca..a801a5e 100644 --- a/sensor/bmp085/bmp085.go +++ b/sensor/bmp085/bmp085.go @@ -76,9 +76,6 @@ type bmp085 struct { debug bool } -// Default instance of the BMP085 sensor. -var Default = New(i2c.Default) - // New creates a new BMP085 interface. The bus variable controls // the I2C bus used to communicate with the device. func New(bus i2c.Bus) BMP085 { @@ -440,33 +437,3 @@ func (d *bmp085) Close() { d.quit <- struct{}{} } } - -// SetPollDelay sets the delay between runs of the data acquisition loop. -func SetPollDelay(delay int) { - Default.SetPollDelay(delay) -} - -// Temperature returns the current temperature reading. -func Temperature() (temp float64, err error) { - return Default.Temperature() -} - -// Pressure returns the current pressure reading. -func Pressure() (pressure int, err error) { - return Default.Pressure() -} - -// Altitude returns the current altitude reading. -func Altitude() (altitude float64, err error) { - return Default.Altitude() -} - -// Run starts the sensor data acquisition loop. -func Run() (err error) { - return Default.Run() -} - -// Close. -func Close() { - Default.Close() -} diff --git a/sensor/bmp180/bmp180.go b/sensor/bmp180/bmp180.go index a8de45c..ede9ca4 100644 --- a/sensor/bmp180/bmp180.go +++ b/sensor/bmp180/bmp180.go @@ -76,9 +76,6 @@ type bmp180 struct { debug bool } -// Default instance of the BMP180 sensor. -var Default = New(i2c.Default) - // New creates a new BMP180 interface. The bus variable controls // the I2C bus used to communicate with the device. func New(bus i2c.Bus) BMP180 { @@ -440,33 +437,3 @@ func (d *bmp180) Close() { d.quit <- struct{}{} } } - -// SetPollDelay sets the delay between runs of the data acquisition loop. -func SetPollDelay(delay int) { - Default.SetPollDelay(delay) -} - -// Temperature returns the current temperature reading. -func Temperature() (temp float64, err error) { - return Default.Temperature() -} - -// Pressure returns the current pressure reading. -func Pressure() (pressure int, err error) { - return Default.Pressure() -} - -// Altitude returns the current altitude reading. -func Altitude() (altitude float64, err error) { - return Default.Altitude() -} - -// Run starts the sensor data acquisition loop. -func Run() (err error) { - return Default.Run() -} - -// Close. -func Close() { - Default.Close() -} diff --git a/sensor/lsm303/lsm303.go b/sensor/lsm303/lsm303.go index d3bba1f..2b1da49 100644 --- a/sensor/lsm303/lsm303.go +++ b/sensor/lsm303/lsm303.go @@ -70,9 +70,6 @@ type lsm303 struct { debug bool } -// Default instance of the LSM303 sensor. -var Default = New(i2c.Default) - // New creates a new LSM303 interface. The bus variable controls // the I2C bus used to communicate with the device. func New(bus i2c.Bus) LSM303 { @@ -185,23 +182,3 @@ func (d *lsm303) Close() (err error) { err = d.bus.WriteByteToReg(magAddress, magModeReg, MagSleep) return } - -// SetPollDelay sets the delay between runs of the data acquisition loop. -func SetPollDelay(delay int) { - Default.SetPollDelay(delay) -} - -// Heading returns the current heading [0, 360). -func Heading() (heading float64, err error) { - return Default.Heading() -} - -// Run starts the sensor data acquisition loop. -func Run() (err error) { - return Default.Run() -} - -// Close closes the sensor data acquisition loop and put the LSM303 into sleep mode. -func Close() (err error) { - return Default.Close() -} diff --git a/sensor/us020/us020.go b/sensor/us020/us020.go index 2b3f67f..1831874 100644 --- a/sensor/us020/us020.go +++ b/sensor/us020/us020.go @@ -6,7 +6,8 @@ import ( "sync" "time" - "github.com/stianeikeland/go-rpio" + "github.com/kidoman/embd" + "github.com/kidoman/embd/gpio" ) const ( @@ -29,13 +30,10 @@ var NullThermometer = &nullThermometer{} // US020 represents a US020 ultrasonic range finder. type US020 struct { - EchoPinNumber, TriggerPinNumber int + EchoPin, TriggerPin gpio.DigitalPin Thermometer Thermometer - echoPin rpio.Pin - triggerPin rpio.Pin - speedSound float64 initialized bool @@ -46,8 +44,8 @@ type US020 struct { // New creates a new US020 interface. The bus variable controls // the I2C bus used to communicate with the device. -func New(e, t int, thermometer Thermometer) *US020 { - return &US020{EchoPinNumber: e, TriggerPinNumber: t, Thermometer: thermometer} +func New(echoPin, triggerPin gpio.DigitalPin, thermometer Thermometer) *US020 { + return &US020{EchoPin: echoPin, TriggerPin: triggerPin, Thermometer: thermometer} } func (d *US020) setup() (err error) { @@ -61,11 +59,8 @@ func (d *US020) setup() (err error) { d.mu.Lock() defer d.mu.Unlock() - d.echoPin = rpio.Pin(d.EchoPinNumber) // ECHO port on the US020 - d.triggerPin = rpio.Pin(d.TriggerPinNumber) // TRIGGER port on the US020 - - d.echoPin.Input() - d.triggerPin.Output() + d.TriggerPin.SetDir(embd.Out) + d.EchoPin.SetDir(embd.In) if d.Thermometer == nil { d.Thermometer = NullThermometer @@ -97,16 +92,24 @@ func (d *US020) Distance() (distance float64, err error) { } // Generate a TRIGGER pulse - d.triggerPin.High() + d.TriggerPin.Write(gpio.High) time.Sleep(pulseDelay) - d.triggerPin.Low() + d.TriggerPin.Write(gpio.Low) if d.Debug { log.Print("us020: waiting for echo to go high") } // Wait until ECHO goes high - for d.echoPin.Read() == rpio.Low { + for { + v, err := d.EchoPin.Read() + if err != nil { + return 0, err + } + + if v != embd.Low { + break + } } startTime := time.Now() // Record time when ECHO goes high @@ -116,7 +119,15 @@ func (d *US020) Distance() (distance float64, err error) { } // Wait until ECHO goes low - for d.echoPin.Read() == rpio.High { + for { + v, err := d.EchoPin.Read() + if err != nil { + return 0, err + } + + if v != embd.High { + break + } } duration := time.Since(startTime) // Calculate time lapsed for ECHO to transition from high to low @@ -129,5 +140,5 @@ func (d *US020) Distance() (distance float64, err error) { // Close. func (d *US020) Close() { - d.echoPin.Output() + d.EchoPin.SetDir(embd.Out) }