Go
Linux
golang
embedded

GolangでGPIOの割り込み通知を受け取る

ユーザープロセスでGPIOの割り込みの通知を受け取る方法

GPIO Sysfs Interface for Userspace
このドキュメントのように、gpioが/sys/class/gpioでアクセス可能なようになっていれば、pollシステムコールでGPIOの割り込み通知を受け取ることができます。
これをGolangでやるには以下のようにします。

GolangでGPIOの割り込みの通知を受け取るサンプル

GPIO28が1から0に変化したときに標準出力にメッセージを表示します。

gpio_sample.go
package main

// Use CGO for struct pollfd

// #include <poll.h>
import "C"
import (
    "io/ioutil"
    "log"
    "os"
    "syscall"
    "unsafe"
)

func main() {
    setup()
    loop()
}

func loop() (err error) {
    var pfd C.struct_pollfd

    f, err := os.Open("/sys/class/gpio/gpio28/value")
    if err != nil {
        return err
    }
    defer f.Close()

    c := make([]byte, 3)
    f.Read(c)
    log.Printf("first val=%c\n", c[0])

    pfd.fd = C.int(f.Fd())
    pfd.events = C.POLLPRI
    timeout := 3000
    for {
        f.Seek(0, os.SEEK_SET)
        r1, _, errno := syscall.Syscall(
            syscall.SYS_POLL,
            uintptr(unsafe.Pointer(&pfd)),
            uintptr(1),
            uintptr(timeout))
        if errno != 0 {
            return errno
        }
        if r1 == 0 {
            log.Printf("Timeout\n")
        } else {
            f.Read(c)
            log.Printf("val=%c\n", c[0])
        }
    }
    return
}

func setup() {
    ioutil.WriteFile("/sys/class/gpio/export", []byte("28"), 0644)
    ioutil.WriteFile("/sys/class/gpio/gpio28/direction", []byte("in"), 0644)
    //ioutil.WriteFile("/sys/class/gpio/gpio28/edge", []byte("both"), 0644)
    //ioutil.WriteFile("/sys/class/gpio/gpio28/edge", []byte("rising"), 0644)
    ioutil.WriteFile("/sys/class/gpio/gpio28/edge", []byte("falling"), 0644)
}

システムコール pollを呼ぶために、カーネルのヘッダ内に定義されているstruct pollfdを使用します。そのためにcgoを使用しました。そのため、これをビルドするときには以下のようにCGO_ENABLED=1 とCCでクロスコンパイラを指定します。

 CGO_ENABLED=1 CC=arm-buildroot-linux-gnueabi-gcc  GOARCH=arm GOARM=7 go build