GoにいればGoに従え(11) TinyGo、micro:bit v2でLED Matrix

Joseph Halfmoon

前回 micro:bit v2であればTinyGoからもADCが使用できることを確認。前々回まで v1.5で実験していたのですが器材を変更。しかし困った問題が。micro:bitのボード表面の5×5のLEDアレイ、見た目は同じ、でもv1.5とv2では結線が違います。折角作ったv1.5用のインタフェース関数は要作り直し。

※「GoにいればGoに従え」Go関連記事の総Index

v1.5とv2の記述が混ざっているTinyGoのドキュメント

TinyGoで各種マイコンのハードウエアを操作しようとするときに頼りになるのが、Machine packageについて記述したドキュメントです。BBC micro:bit に関しては以下のページです。

Documentation>>Reference>>Microcontrollers>>Machine package>>microbit

前回も書きましたが、BBC micro:bit の v1.x 系ボードと v2.x系ボードの見た目はよく似ていますが、搭載しているMCUが異なる上に、ボード上の回路も異なります。Machine packegeの記述は、v1.x系ボード用と v2.x系ボード用で2種類にわけた方がいいと思うのですが(個人の感想です)、現状は上記のページ一つしかありません(2023年3月23日現在。)

前々回から今回まで調べたことをまとめると以下のようになります。

    1. v2.0ボード(MCUはnRF52系)であれば上記ドキュメントのADCに関する記述通りでAD変換可能。一方nRF51系用のmachine packageのソースにはADCに関する記述が見つからない。TinyGoからターゲットをv1.x系のmicrobitにしてmachineパッケージのADCのAPIを呼び出そうとするとエラーになる。
    2. ボード表面の5x5のLEDアレイに関する端子の定義は、上記ドキュメントにはv1.x系ボードの端子配線向けの定義が記載されている。v2.x系ボードでは配線異なるのでそのままでは使えない。

今回は、2についての対処について書き留めときます。幸いなことに、Tinygoのmachineパッケージ・フォルダ内の以下のソース中にはv2.xボード用のLED端子の定義が記載されています。

board_microbit-v2.go

v1.x系と「似た」以下の端子名で定義されてますが、そもそも実体LEDとCOL/ROWの対応関係が、v1.xとv2.xで異なっているので、v1.x系用に作ったIF関数などはそのままでは動きませぬ。

    • LED_COL_1 ~ LED_COL_5
    • LED_ROW_1 ~ LED_ROW_5
過去回で作成したv1.x用の駆動ルーチンをv2.0に移植

以下の過去回で作成した、5x5アレイに任意のパターンを表示できる、ダイナミック駆動かつGoルーチン化によりメインと並行動作するLED駆動ルーチンを移植してみました。

GoにいればGoに従え(4) micro:bitのLED、goroutineで動的駆動じゃ

LED駆動部分のソースが以下に。

package main

import (
    "machine"
)

func InitLED() {
    machine.LED_ROW_1.Configure(machine.PinConfig{Mode: machine.PinOutput})
    machine.LED_ROW_2.Configure(machine.PinConfig{Mode: machine.PinOutput})
    machine.LED_ROW_3.Configure(machine.PinConfig{Mode: machine.PinOutput})
    machine.LED_ROW_4.Configure(machine.PinConfig{Mode: machine.PinOutput})
    machine.LED_ROW_5.Configure(machine.PinConfig{Mode: machine.PinOutput})
    machine.LED_COL_1.Configure(machine.PinConfig{Mode: machine.PinOutput})
    machine.LED_COL_2.Configure(machine.PinConfig{Mode: machine.PinOutput})
    machine.LED_COL_3.Configure(machine.PinConfig{Mode: machine.PinOutput})
    machine.LED_COL_4.Configure(machine.PinConfig{Mode: machine.PinOutput})
    machine.LED_COL_5.Configure(machine.PinConfig{Mode: machine.PinOutput})
    machine.LED_ROW_1.Low()
    machine.LED_ROW_2.Low()
    machine.LED_ROW_3.Low()
    machine.LED_ROW_4.Low()
    machine.LED_ROW_5.Low()
}

func SetLED(arg uint32) {
    if (arg & 0x01084210) != 0 {
        machine.LED_COL_1.Low()
    } else {
        machine.LED_COL_1.High()
    }
    if (arg & 0x00842108) != 0 {
        machine.LED_COL_2.Low()
    } else {
        machine.LED_COL_2.High()
    }
    if (arg & 0x00421084) != 0 {
        machine.LED_COL_3.Low()
    } else {
        machine.LED_COL_3.High()
    }
    if (arg & 0x00210842) != 0 {
        machine.LED_COL_4.Low()
    } else {
        machine.LED_COL_4.High()
    }
    if (arg & 0x00108421) != 0 {
        machine.LED_COL_5.Low()
    } else {
        machine.LED_COL_5.High()
    }
}

func DispLED1(arg uint32) {
    machine.LED_ROW_1.Low()
    machine.LED_ROW_2.Low()
    machine.LED_ROW_3.Low()
    machine.LED_ROW_4.Low()
    machine.LED_ROW_5.Low()
    if (arg & 0x01F00000) != 0 {
        SetLED(arg & 0x01F00000)
        machine.LED_ROW_1.High()
    }
}

func DispLED2(arg uint32) {
    machine.LED_ROW_1.Low()
    machine.LED_ROW_2.Low()
    machine.LED_ROW_3.Low()
    machine.LED_ROW_4.Low()
    machine.LED_ROW_5.Low()
    if (arg & 0x000F8000) != 0 {
        SetLED(arg & 0x000F8000)
        machine.LED_ROW_2.High()
    }
}

func DispLED3(arg uint32) {
    machine.LED_ROW_1.Low()
    machine.LED_ROW_2.Low()
    machine.LED_ROW_3.Low()
    machine.LED_ROW_4.Low()
    machine.LED_ROW_5.Low()
    if (arg & 0x00007C00) != 0 {
        SetLED(arg & 0x00007C00)
        machine.LED_ROW_3.High()
    }
}

func DispLED4(arg uint32) {
    machine.LED_ROW_1.Low()
    machine.LED_ROW_2.Low()
    machine.LED_ROW_3.Low()
    machine.LED_ROW_4.Low()
    machine.LED_ROW_5.Low()
    if (arg & 0x000003E0) != 0 {
        SetLED(arg & 0x000003E0)
        machine.LED_ROW_4.High()
    }
}

func DispLED5(arg uint32) {
    machine.LED_ROW_1.Low()
    machine.LED_ROW_2.Low()
    machine.LED_ROW_3.Low()
    machine.LED_ROW_4.Low()
    machine.LED_ROW_5.Low()
    if (arg & 0x0000001F) != 0 {
        SetLED(arg & 0x0000001F)
        machine.LED_ROW_5.High()
    }
}

一方、上記に表示をお願いするmain関数のサンプルが以下に。go func()部分も別ファイルに追い出した方がカッコ良いのですが、今回は表示だけなので、その動作が分かりやすい方向ということで。

package main

import (
    "time"
)

func main() {
    InitLED()
    var dispPattern [4]uint32
    dispPattern[0] = 0x01000000
    dispPattern[1] = 0x01151151
    dispPattern[2] = 0x00E8C62E
    dispPattern[3] = 0x01FFFFFF
    var pat = 0
    var row = 1

    go func() {
        for {
            switch row {
            case 1:
                DispLED1(dispPattern[pat])
            case 2:
                DispLED2(dispPattern[pat])
            case 3:
                DispLED3(dispPattern[pat])
            case 4:
                DispLED4(dispPattern[pat])
            case 5:
                DispLED5(dispPattern[pat])
            }
            row++
            if row > 5 {
                row = 1
            }
            time.Sleep(time.Millisecond * 7)
        }
    }()

    for {
        pat++
        if pat > 3 {
            pat = 0
        }
        time.Sleep(time.Second * 5)
    }
}
ビルドして実行

上記2つのソースファイルが含まれるGo プロジェクトのフォルダ内で、以下のコマンドラインを実行すれば、ソースがビルドされ、オブジェクトが microbit v2機のFlashに書き込まれます。

$ tinygo flash -target microbit-v2

実行した様子が以下に。MBv2DispGo

メンドかったけれども、v2対応版も作れた。

GoにいればGoに従え(10) TinyGo、micro:bit v2でのADC動作確認 へ戻る

GoにいればGoに従え(12) TinyGo、micro:bit v2、キーと温度センサ確認 へ進む