GoにいればGoに従え(43) ラズパイPico、TIMERアラーム、一部は使われている?

Joseph Halfmoon

ラズパイPicoにTinyGoのオブジェクトを書き込んでラズパイPicoハードウエアをどう制御したものか手探りしてます。今回はTIMERにやってまいりました。TinyGoのmachineパッケージから直接TIMER使えないのだけれど、調べてみるとTIMERはしっかり使われている形跡があります。使っているのは誰ぞ?

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

※実機動作確認は Arm Cortex-M0+コアのRP2040チップ搭載、Raspberry Pi Pico機にTinyGoのオブジェクトを書き込んで行っています。ビルドはWindows11上です。

※RP2040のデータシート(PDF)はこちら

Raspberry Pi Picoのタイマ

ラズパイPicoのペリフェラルは、「普通の」半導体屋さんのマイコン搭載のペリフェラルに比べると「一味違う」味付けです。タイマもユニーク。勝手にかいつまむとこんな感じ。

    1. 普通のマイコンのタイマであると、タイマの周期などを設定するために、クロック選択、プリスケーラの設定、リロード値の設定など一杯設定がある。ラズパイPicoのタイマは、1μ秒単位で固定。メンドクサイ設定などなし。
    2. 普通のマイコンのタイマであると、カウント幅は用途に応じて8ビット、16ビットなどあり、せいぜい32ビット最大。アップ、ダウンカウントもあり。またタイマ本数は多数。しかし、ラズパイPicoのタイマは64ビット幅のアップカウント1本。
    3. 普通のマイコンタイマには、PWM出力、インプットキャプチャなど入出力機能が付随している(というかそれがメインのお仕事であることが多い。)ラズパイPicoの場合、それら入出力機能はPWMモジュールに追い出されており、TIMERは純粋に時間を測る機能のみ。
    4. ラズパイPicoの場合、64ビットのカウント値に対して下32ビットのアラーム値を比較してアラーム割り込みを発生させる。アラームは4本。

このような特徴のラズパイPicoのタイマ、TinyGoで記述した「ユーザ」プログラムから使えるのだか、使えないのだか、ちょっとばかり探ってみました。

実験に使用したTinyGoソース

いつもながら、手抜きでベタなTinyGoのソースです。何もTIMERに設定していないのだけれど、タイマのカウンタが動いているのか、また、アラームが使われているのか探るもの。ただし、アラームは0番と3番だけ調べてます。1,2が無いのはメンドかっただけっす。

package main

import (
    "device/rp"
    "fmt"
    "runtime/volatile"
    "time"
)

func main() {
    tim := rp.TIMER
    for {
        RAWL := volatile.LoadUint32(&tim.TIMERAWL.Reg)
        fmt.Printf("RAWL: %d\n", RAWL)
        ARMEDf := tim.GetARMED()
        fmt.Printf("ARMED: 0x%01x\n", ARMEDf)
        R0 := tim.GetINTR_ALARM_0()
        E0 := tim.GetINTE_ALARM_0()
        F0 := tim.GetINTF_ALARM_0()
        S0 := tim.GetINTS_ALARM_0()
        fmt.Printf("0) R:%d E:%d F:%d S:%d\n", R0, E0, F0, S0)
        A0 := volatile.LoadUint32(&tim.ALARM0.Reg)
        fmt.Printf("ALARM0: 0x%08x\n", A0)
        R3 := tim.GetINTR_ALARM_3()
        E3 := tim.GetINTE_ALARM_3()
        F3 := tim.GetINTF_ALARM_3()
        S3 := tim.GetINTS_ALARM_3()
        fmt.Printf("3) R:%d E:%d F:%d S:%d\n", R3, E3, F3, S3)
        A3 := volatile.LoadUint32(&tim.ALARM3.Reg)
        fmt.Printf("ALARM3: 0x%08x\n", A3)
        time.Sleep(time.Second * 4)
    }
}
実機動作確認

上記のソースをビルドして走らせ、USBシリアルに出力されてくる出力をキャプチャしたものが以下に。ALARM0runs

まずは、予想通りな事柄。RAWLの出力は毎回異なった値が読めています。よって、

    • タイマのカウンタは特に初期設定しなくても走っている

続いてアラームについて分かったこと。

    • アラーム0の割り込みはイネーブルになっている上、アラーム0の設定値は毎回更新されているように見える
    • 一方、アラーム3の割り込みはディセーブル。アラーム3の設定値はゼロのまま動かない。

いったい「誰が」アラーム0を使っているのか?試みにALARM0の設定値のインターバルを計算してみました。こんな感じ。

0x0356a75b – 0x3199bcb = 0x3D0B90

上記の結果を十進にすると4000656、つまりは約4秒! 胸に手を当ててよーく考えたら心あたりがありました。これだ、これに違いない。。。

 time.Sleep(time.Second * 4)

time.Sleep()関数に与えた引数の値みたいね。そうか、time.Sleep()がALARM0を使っているようだね。。。そうだったら尊重しないと。てゆうか、time.Sleep()あればよくね。

GoにいればGoに従え(42) ラズパイPico、SSD1315接続、OLEDで表示 へ戻る

GoにいればGoに従え(44) ラズパイPico、USB HIDデバイスその1 へ進む