LoginSignup
3
4

More than 3 years have passed since last update.

TinyGo で goroutine + pin interrupts を使う

Last updated at Posted at 2020-05-17

TinyGo を使うと Go でマイコン開発ができます。
組込開発で goroutine + pin interrupts を使ってみたので紹介します。
(個人的には) 気持ちよく記載することができます。

完成品

  • 開始時にスクロールで TinyGo demo と表示
  • 以降ループ
    • 77ms 毎に白い LED を点滅、 LCD 左下のカウンタを更新
    • 500ms 毎に LCD 右下のカウンタを更新
    • LCD の 1 行目にボタン状態を表示
    • PyPortal 本体の LED にボタン状態を表示

環境

goroutine 部分

以下のように go timer77ms() などにより 3 つの goroutine を起動しています。
goroutine からは chan 経由で for / select に処理を依頼します。

aqm0802 (I2C 接続のキャラクタ液晶) に対しては単一の main() 関数のみからアクセスするため、安全に更新できます。

Go らしいすっきりとした記載になっていて、個人的には非常に書きやすいです。

func main() {
    // 省略

    go timer77ms(chCnt1, led2)
    go timer500ms(chCnt2)
    go disp(chBtn, chDisp, led1)

    for {
        select {
        case d := <-chDisp:
            aqm0802.SetCursor(0, 0)
            aqm0802.Print(d.String())

        case cnt := <-chCnt1:
            aqm0802.SetCursor(0, 1)
            aqm0802.Print(fmt.Sprintf("%4d", cnt))

        case cnt := <-chCnt2:
            aqm0802.SetCursor(4, 1)
            aqm0802.Print(fmt.Sprintf("%4d", cnt))
        }
        time.Sleep(1 * time.Millisecond)
    }
}

pin interrputs 部分

※2020/05/17 時点では、まだ merge されていないので注意が必要です
※API も変更になるかもしれません

以下の関数は、入力 pin (実際のマイコンの足に対応する) を引数にとって、 pin の値が変わった時に通知する chan を返す という動きをします。

pin interrupts では、割込発生時には func(p machine.Pin) がコールされます。
この func(p machine.Pin) は、割込にて処理されるため長時間ブロックするような処理を書いてはいけません。
後述のコード例では ch <- b している個所について select を使っていますが、万が一の際にブロックしないようにするためです。
また、 ch := make(chan bool, 3) により少しバッファを持たせているのもブロック対策です。
空きメモリに合わせて多めに取っておくのは一つの手ですし、チャタリングしないようにしておくのも良いです。

内部で var state chvolatile.Register8 という見慣れない表記があります。
その中では p により、該当ピンの情報を知ることができますが、該当ピンの状態は (処理するまでに) 既に更新されている可能性があります。
なので、ソフト的に処理するようにしています。
#割込が発生しない場合があると、内部状態が食い違ってしまいますが・・・

func pinInterruptChan(pin machine.Pin) <-chan bool {
    var state volatile.Register8
    ch := make(chan bool, 3)

    pin.SetInterrupt(machine.PinToggle, func(p machine.Pin) {
        b := false
        if state.Get() != 1 {
            state.Set(1)
            b = true
        } else {
            state.Set(0)
        }
        select {
        case ch <- b:
        default:
        }
    })

    return ch
}

まとめ

TinyGO で goroutine + pin interrupts を使ってみました。
ソースコードは以下にあります。
現状は、 pin interrupts がマージされていないので、開発版の tinygo を自分でビルドする必要があります。

3
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
4