From b7096cfe6c3238bff74504a95331496e4f7b939e Mon Sep 17 00:00:00 2001 From: Karan Misra Date: Mon, 17 Feb 2014 03:41:53 +0530 Subject: [PATCH] gpio library for rpi --- gpio/data.go | 41 +++++++++ gpio/gpio.go | 226 ++++++++++++++++++++++++++++++++++++++++++++++++ gpio/pin.go | 149 +++++++++++++++++++++++++++++++ gpio/pindesc.go | 7 ++ gpio/pinmap.go | 24 +++++ samples/gpio.go | 33 +++++++ 6 files changed, 480 insertions(+) create mode 100644 gpio/data.go create mode 100644 gpio/gpio.go create mode 100644 gpio/pin.go create mode 100644 gpio/pindesc.go create mode 100644 gpio/pinmap.go create mode 100644 samples/gpio.go diff --git a/gpio/data.go b/gpio/data.go new file mode 100644 index 0000000..e5c1370 --- /dev/null +++ b/gpio/data.go @@ -0,0 +1,41 @@ +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"}, normal | spi}, + &pinDesc{9, []string{"P1_21", "GPIO_9", "MISO"}, normal | spi}, + &pinDesc{25, []string{"P1_22", "GPIO_25"}, normal}, + &pinDesc{11, []string{"P1_23", "GPIO_11", "SCLK"}, normal | spi}, + &pinDesc{8, []string{"P1_24", "GPIO_8", "CE0"}, normal | spi}, + &pinDesc{7, []string{"P1_26", "GPIO_7", "CE1"}, 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 new file mode 100644 index 0000000..9d1f370 --- /dev/null +++ b/gpio/gpio.go @@ -0,0 +1,226 @@ +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 +) + +const ( + Low State = iota + High +) + +const ( + PullOff Pull = iota + PullDown + PullUp +) + +const ( + normal = 1 << iota + i2c + spi + uart +) + +type gpio struct { + exporter, unexporter *os.File + + initialized bool + + pinMap pinMap + initializedPins map[int]*pin +} + +var instance *gpio +var instanceLock sync.Mutex + +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() +} diff --git a/gpio/pin.go b/gpio/pin.go new file mode 100644 index 0000000..b9fea91 --- /dev/null +++ b/gpio/pin.go @@ -0,0 +1,149 @@ +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 new file mode 100644 index 0000000..7a66b1a --- /dev/null +++ b/gpio/pindesc.go @@ -0,0 +1,7 @@ +package gpio + +type pinDesc struct { + n int + ids []string + caps int +} diff --git a/gpio/pinmap.go b/gpio/pinmap.go new file mode 100644 index 0000000..908bfd2 --- /dev/null +++ b/gpio/pinmap.go @@ -0,0 +1,24 @@ +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/samples/gpio.go b/samples/gpio.go new file mode 100644 index 0000000..979e3fd --- /dev/null +++ b/samples/gpio.go @@ -0,0 +1,33 @@ +package main + +import ( + "time" + + "github.com/kidoman/embd/gpio" +) + +func main() { + io := gpio.New() + defer io.Close() + + pin, err := io.Pin("MOSI") + if err != nil { + panic(err) + } + + if err := pin.Output(); err != nil { + panic(err) + } + if err := pin.ActiveLow(); err != nil { + panic(err) + } + if err := pin.Low(); err != nil { + panic(err) + } + + time.Sleep(1 * time.Second) + + if err := pin.Input(); err != nil { + panic(err) + } +}