GoにいればGoに従え(13) TinyGo、micro:bit v2、I2Cは要変更

Joseph Halfmoon

micro:bitボードをv1.5からv2.0に機材変更するのにともない、v1.5で動作していたプログラムがv2.0でも動くのか確認作業中です。前回はキー割り込みとオンボードの温度センサは問題なく動作OK。I2Cも大丈夫だろ、と甘くみたらばダメでした。ここにもv1.5とv2.0の違いがあったのね。今回はその変更点をば。

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

micro:bit のI2Cバス、v1.5とv2.0の違い

結論から言うと以下のようでした。

    • micro:bit v1.5、オンボードペリフェラルとカードエッジの端子で1本のI2Cバスをシェアしていた。
    • micro:bit v2.0、オンボードペリフェラル用の内部I2Cバスと、カードエッジ用の外部I2Cバスが分離された。TinyGo上では、I2C0というバスが内部用で、I2C1というバスが外部用になった。また、I2C用のピン名も変更されている。

つまり、v1.5用のTinyGoソースをそのままコンパイルするとコンパイルはノーエラーですが、ピクりともバスは動きませぬ。外部用のI2C1に行先を向けなおした上に、ピン名も変更せねばなりません。

つまり、下記のように外部用I2Cを使う場合は以下のようにI2C1を指定した上でgoI2c1

v1.5のころは、SCL_PINとかで良かったピン名をSCL1_PINなどと変更する必要がありました。こんな感じ。goI2c1config

なお、micro:bitのI2Cバスについては以下に解説があります。

Use of the I2C bus

今回のメイン・プログラム

今回のメインプログラムは、前回使用の「チョイ直し」です。前回のおさらいをすると、

    1. goルーチン(コルーチン?による並行動作機構)によりLED Matrixは動的点灯する(パターン指定されれば)
    2. キーをプッシュすると割り込みでうけする。AキーとBキーで異なるパターンを設定する(それによってLED表示パターンが即座に変わる)
    3. メインループは上記とかかわりなく無限ループしながら、10秒置きに温度を測定して標準出力(USBシリアル)に温度を出力する

今回は3のメインループ部分に、「測定した温度をI2C1に接続した外部LCD(AQM1602)に表示する」という機能を追加してみました。前回同様氷点下の温度には対応してないっす。手抜き。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package main
import (
"fmt"
"machine"
"time"
)
var pat = 0
func boardTemperatureC() int {
return int(machine.ReadTemperature() / 1000)
}
func main() {
var msg1 []byte = []byte("Temperature")
var msg2 []byte = []byte("00")
var temp int
dispPattern := []uint32{
0x00000000, 0x01151151, 0x00E8C62E, 0x01FFFFFF,
}
InitLED()
go DispLoop(dispPattern[:])
InitAQM1602()
keyA := machine.BUTTONA
keyB := machine.BUTTONB
keyA.Configure(machine.PinConfig{Mode: machine.PinInput})
keyB.Configure(machine.PinConfig{Mode: machine.PinInput})
keyA.SetInterrupt(machine.PinFalling, func(machine.Pin) {
pat = 1
})
keyB.SetInterrupt(machine.PinFalling, func(machine.Pin) {
pat = 2
})
for {
temp = boardTemperatureC()
fmt.Printf("TEMP: %d\r\n", temp)
if temp < 99 {
msg2[1] = byte((temp % 10) + 0x30)
msg2[0] = byte((temp / 10) + 0x30)
}
fmt.Printf("CHR: %02x %02x\r\n", msg2[0], msg2[1])
DispStrOnLCD(msg1, msg2)
time.Sleep(time.Second * 10)
}
}
package main import ( "fmt" "machine" "time" ) var pat = 0 func boardTemperatureC() int { return int(machine.ReadTemperature() / 1000) } func main() { var msg1 []byte = []byte("Temperature") var msg2 []byte = []byte("00") var temp int dispPattern := []uint32{ 0x00000000, 0x01151151, 0x00E8C62E, 0x01FFFFFF, } InitLED() go DispLoop(dispPattern[:]) InitAQM1602() keyA := machine.BUTTONA keyB := machine.BUTTONB keyA.Configure(machine.PinConfig{Mode: machine.PinInput}) keyB.Configure(machine.PinConfig{Mode: machine.PinInput}) keyA.SetInterrupt(machine.PinFalling, func(machine.Pin) { pat = 1 }) keyB.SetInterrupt(machine.PinFalling, func(machine.Pin) { pat = 2 }) for { temp = boardTemperatureC() fmt.Printf("TEMP: %d\r\n", temp) if temp < 99 { msg2[1] = byte((temp % 10) + 0x30) msg2[0] = byte((temp / 10) + 0x30) } fmt.Printf("CHR: %02x %02x\r\n", msg2[0], msg2[1]) DispStrOnLCD(msg1, msg2) time.Sleep(time.Second * 10) } }
package main

import (
    "fmt"
    "machine"
    "time"
)

var pat = 0

func boardTemperatureC() int {
    return int(machine.ReadTemperature() / 1000)
}

func main() {
    var msg1 []byte = []byte("Temperature")
    var msg2 []byte = []byte("00")
    var temp int

    dispPattern := []uint32{
        0x00000000, 0x01151151, 0x00E8C62E, 0x01FFFFFF,
    }
    InitLED()
    go DispLoop(dispPattern[:])

    InitAQM1602()

    keyA := machine.BUTTONA
    keyB := machine.BUTTONB
    keyA.Configure(machine.PinConfig{Mode: machine.PinInput})
    keyB.Configure(machine.PinConfig{Mode: machine.PinInput})
    keyA.SetInterrupt(machine.PinFalling, func(machine.Pin) {
        pat = 1
    })
    keyB.SetInterrupt(machine.PinFalling, func(machine.Pin) {
        pat = 2
    })

    for {
        temp = boardTemperatureC()
        fmt.Printf("TEMP: %d\r\n", temp)
        if temp < 99 {
            msg2[1] = byte((temp % 10) + 0x30)
            msg2[0] = byte((temp / 10) + 0x30)
        }
        fmt.Printf("CHR: %02x %02x\r\n", msg2[0], msg2[1])
        DispStrOnLCD(msg1, msg2)
        time.Sleep(time.Second * 10)
    }
}
I2C経由のAQM1602インタフェースのコード

以下のコードは、『GoにいればGoに従え(8)』のメインプログラムだったソースを流用して改造したものです。上記のmain.goのお隣にこっそり置いておけばTinyGoが見つけてビルドしてくれます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package main
import (
"machine"
"time"
)
const AQM1602 = 0x3E
var i2c = machine.I2C1 //micro-bit v2 only
func WriteAQM1602Data(dat byte) {
i2c.WriteRegister(AQM1602, 0x40, []byte{dat})
time.Sleep(1 * time.Millisecond)
}
func WriteAQM1602Command(cmd byte) {
i2c.WriteRegister(AQM1602, 0x00, []byte{cmd})
time.Sleep(20 * time.Millisecond)
}
// micro-bit v2 only
func InitAQM1602() {
i2c.Configure(machine.I2CConfig{
Frequency: machine.TWI_FREQ_100KHZ,
SCL: machine.SCL1_PIN,
SDA: machine.SDA1_PIN,
})
time.Sleep(100 * time.Millisecond)
WriteAQM1602Command(0x38)
WriteAQM1602Command(0x39)
WriteAQM1602Command(0x14)
WriteAQM1602Command(0x77)
WriteAQM1602Command(0x56)
WriteAQM1602Command(0x6C)
WriteAQM1602Command(0x38)
WriteAQM1602Command(0x01)
WriteAQM1602Command(0x0C)
}
func DispStrOnLCD(arg1 []byte, arg2 []byte) {
WriteAQM1602Command(0x01) // Clear Display
WriteAQM1602Command(0x80) // Go to TOP LINE HOME
WriteAQM1602Command(0x00) // End of Command, Data bytes will follow
for i := 0; i < len(arg1); i++ {
WriteAQM1602Data(arg1[i])
}
WriteAQM1602Command(0xC0) // Go to BOTTOM LINE HOME
WriteAQM1602Command(0x00) // End of Command, Data bytes will follow
for i := 0; i < len(arg2); i++ {
WriteAQM1602Data(arg2[i])
}
}
package main import ( "machine" "time" ) const AQM1602 = 0x3E var i2c = machine.I2C1 //micro-bit v2 only func WriteAQM1602Data(dat byte) { i2c.WriteRegister(AQM1602, 0x40, []byte{dat}) time.Sleep(1 * time.Millisecond) } func WriteAQM1602Command(cmd byte) { i2c.WriteRegister(AQM1602, 0x00, []byte{cmd}) time.Sleep(20 * time.Millisecond) } // micro-bit v2 only func InitAQM1602() { i2c.Configure(machine.I2CConfig{ Frequency: machine.TWI_FREQ_100KHZ, SCL: machine.SCL1_PIN, SDA: machine.SDA1_PIN, }) time.Sleep(100 * time.Millisecond) WriteAQM1602Command(0x38) WriteAQM1602Command(0x39) WriteAQM1602Command(0x14) WriteAQM1602Command(0x77) WriteAQM1602Command(0x56) WriteAQM1602Command(0x6C) WriteAQM1602Command(0x38) WriteAQM1602Command(0x01) WriteAQM1602Command(0x0C) } func DispStrOnLCD(arg1 []byte, arg2 []byte) { WriteAQM1602Command(0x01) // Clear Display WriteAQM1602Command(0x80) // Go to TOP LINE HOME WriteAQM1602Command(0x00) // End of Command, Data bytes will follow for i := 0; i < len(arg1); i++ { WriteAQM1602Data(arg1[i]) } WriteAQM1602Command(0xC0) // Go to BOTTOM LINE HOME WriteAQM1602Command(0x00) // End of Command, Data bytes will follow for i := 0; i < len(arg2); i++ { WriteAQM1602Data(arg2[i]) } }
package main

import (
    "machine"
    "time"
)

const AQM1602 = 0x3E

var i2c = machine.I2C1 //micro-bit v2 only

func WriteAQM1602Data(dat byte) {
    i2c.WriteRegister(AQM1602, 0x40, []byte{dat})
    time.Sleep(1 * time.Millisecond)
}

func WriteAQM1602Command(cmd byte) {
    i2c.WriteRegister(AQM1602, 0x00, []byte{cmd})
    time.Sleep(20 * time.Millisecond)
}

// micro-bit v2 only
func InitAQM1602() {
    i2c.Configure(machine.I2CConfig{
        Frequency: machine.TWI_FREQ_100KHZ,
        SCL:       machine.SCL1_PIN,
        SDA:       machine.SDA1_PIN,
    })
    time.Sleep(100 * time.Millisecond)
    WriteAQM1602Command(0x38)
    WriteAQM1602Command(0x39)
    WriteAQM1602Command(0x14)
    WriteAQM1602Command(0x77)
    WriteAQM1602Command(0x56)
    WriteAQM1602Command(0x6C)
    WriteAQM1602Command(0x38)
    WriteAQM1602Command(0x01)
    WriteAQM1602Command(0x0C)
}

func DispStrOnLCD(arg1 []byte, arg2 []byte) {
    WriteAQM1602Command(0x01) // Clear Display
    WriteAQM1602Command(0x80) // Go to TOP LINE HOME
    WriteAQM1602Command(0x00) // End of Command, Data bytes will follow
    for i := 0; i < len(arg1); i++ {
        WriteAQM1602Data(arg1[i])
    }
    WriteAQM1602Command(0xC0) // Go to BOTTOM LINE HOME
    WriteAQM1602Command(0x00) // End of Command, Data bytes will follow
    for i := 0; i < len(arg2); i++ {
        WriteAQM1602Data(arg2[i])
    }
}
念のためLEDマトリックスの駆動コード

以下のファイルは前回とまったく同じものですが、念のため掲げておきます。

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
package main
import (
"machine"
"time"
)
var row = 1
func DispLoop(dispPattern []uint32) {
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)
}
}
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()
}
}
package main import ( "machine" "time" ) var row = 1 func DispLoop(dispPattern []uint32) { 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) } } 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() } }
package main

import (
    "machine"
    "time"
)

var row = 1

func DispLoop(dispPattern []uint32) {
    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)
    }	
}

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()
    }
}
実機動作確認

例によって以下のようにしてビルド&フラッシュ書き込みします。

$ tinygo flash -target=microbit-v2

microbit v2機でI2C経由でLCDに文字が書けました。goI2c1_DUT

micro:bitのv1.5とv2.0の回路図を以前読んでいた筈なのだけれど完全に忘れてましたな。年寄の忘却力デス。

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

GoにいればGoに従え(14) TinyGo、micro:bit v2、CDSセンサ読み取り へ進む