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を指定した上で
v1.5のころは、SCL_PINとかで良かったピン名をSCL1_PINなどと変更する必要がありました。こんな感じ。
なお、micro:bitのI2Cバスについては以下に解説があります。
今回のメイン・プログラム
今回のメインプログラムは、前回使用の「チョイ直し」です。前回のおさらいをすると、
-
- goルーチン(コルーチン?による並行動作機構)によりLED Matrixは動的点灯する(パターン指定されれば)
- キーをプッシュすると割り込みでうけする。AキーとBキーで異なるパターンを設定する(それによってLED表示パターンが即座に変わる)
- メインループは上記とかかわりなく無限ループしながら、10秒置きに温度を測定して標準出力(USBシリアル)に温度を出力する
今回は3のメインループ部分に、「測定した温度をI2C1に接続した外部LCD(AQM1602)に表示する」という機能を追加してみました。前回同様氷点下の温度には対応してないっす。手抜き。
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が見つけてビルドしてくれます。
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マトリックスの駆動コード
以下のファイルは前回とまったく同じものですが、念のため掲げておきます。
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に文字が書けました。
micro:bitのv1.5とv2.0の回路図を以前読んでいた筈なのだけれど完全に忘れてましたな。年寄の忘却力デス。