鳥なき里のマイコン屋(129) VS CodeでラズパイPico、SDKのTimerを使う

Joseph Halfmoon

前回まででPC上のVS Codeから、ラズパイPicoの母艦であるラズパイ4に接続してビルド&デバッグを行う環境ができました。環境ができるとホッとして止まってしまう。。。それではイケない。ボチボチとRaspberry Pi Picoの C/C++ SDKを使ってみて行きたいと思います。まずはTimerで定周期で関数を呼び出すところから。

※「鳥なき里のマイコン屋」投稿順Indexはこちら

(末尾に今回使用のサンプルプログラムの全文掲げました。)

ラズパイPicoのC/C++用のSDKについて勉強し始めました。階層構造をなして低レベルから高レベルまでのAPI関数などが準備されており中々立派です。この頃のマイコンはSDK充実しているものが多いですが、やはりラズパイは良く出来ています。直接ハードウエアを制御したければ低レベルのAPIを使えば良いし、手っ取り早く動作させたければ高レベルを使えば良い、と。その分、調べないといけない事柄が多いです。どこから手を付けたらよいか。

SDKのドキュメント

C/C++のSDKについては ダウンロード可能な raspberry-pi-pico-c-sdk.pdf があり、私もこれ読ませていただいているのです。しかしWeb上にもう少し参照しやすいものがありました。以下のページです。

Raspberry Pi Pico SDK Documentation

こちらはSDKのソースのドキュメンテーションコメントを定番のドキュメンテーションツールDoxygenで処理して生成されたページのようです。見たい情報へはこちらの方が素早く探せる感じがします。また、Example がまとまっていて参照しやすいところもポイント高いです。今回も上記のページからTimer関係のExampleを参照させていただきました。

今回作成のサンプルプログラム

開発環境構築の過程で、何度か定番のLチカ(blink)を取り上げてきましたが、今回もその変形です。

    1. 外付けのLEDとする(GPIO15に接続)
    2. TimerのAPIを使って、LEDをトグルする関数を500ミリ毎に呼び出す
    3. 上記とは無関係に、main関数内で2秒に1回カウンタをカウントアップし、その値をUSBシリアルに垂れ流す。2秒はsleep()関数で「眠って」やり過ごす形で消費。
    4. 100回カウントしたら、Timerを止めてプログラムを終了する(デバッガから動作されていればデバッガに制御が戻る)

mainの処理とは独立に、何か定期処理を入れたいときの雛形のつもり。

プロジェクトの生成

前回から使い始めた Raspberry Pi Pico Project Generator でプロジェクトを生成します。VS Codeのターミナルウインドウからコマンドライン引数で設定しても良いのですが、GUI起動する方が直観的なので、そちらでやってしまいました。こんな感じ。

iTimerTst0SettingAdd examples for Pico libraryにもチェックを入れたので、生成されたプロジェクトの中のソースには、いろいろ書き込まれていたのです。残念ながら今回行うのは単純なタイマ利用のLチカなので、ほぼ消してしまいました。

タイマの定周期呼び出しに使うAPI

今回使用したのは、以下の階層にある add_repeating_timer_ms() という指定した周期(ms)毎に、登録したコールバック関数を呼び出してくれるAPIです。

High Level APIs >> pico_time >> repeating_timer

VS CodeでこのAPIの定義をたどると、これはインライン関数展開され、結局以下の実体関数で処理されることが分かります。

alarm_pool_add_repeating_timer_us

ms単位といいながら、1000倍した値をus単位の関数に渡しています。今回は非人の目に見えるように500msですが、us単位のIO処理も同じ関数で処理されるので、呼び出し方法さえ変更すればもっと細かいレゾリューションで可能ということですね。処理そのものは alarm_poolという仕組みを使っているようです。複数の周期などにも同時に対処できるのだと思います。

なお、repeating_timer型の構造体変数を1個作成して渡さないとならないです。

実動作

アイキャッチ画像に動作しているところを掲げました。GPIO15に接続したLEDは予定どおり順調に動作しています。慌ててVS Codeのターミナルからminicomを走らせてUSBシリアルに垂れ流されてくるカウンタ値のモニタにかかりました。ちょっと手間取ったので、頭から見えなくて途中から。こんな感じ。

minicom へようこそ 2.7.1

オプション: I18n 
コンパイルされた日時は: Aug 13 2017, 15:25:34.
ポート /dev/ttyACM0, 11:48:26

CTRL-A Z を押すと、説明画面になります。

14
15
16
~以下略~

ちょっと戸惑ったのは、VS Codeのターミナルウインドウが小さすぎると minicomが起動しないこと。ウインドウの縦幅を広げたら起動しました。直近のVS Codeの更新で、ターミナルウインドウを画面下の細い領域(パネル?)においておかずに、エディタ領域にも持っていって縦長にできるようになっていたので、それを使った方が良かったかも。まあ、選択肢は多い方が良いですか。すぐ忘れるケド。

100まで数えて、終了メッセージを出し、制御がデバッガに戻ってきました。ケーブル短くしただけで、SWDデバッグ安定の動作だな。

PicoのSDK、ハイ、ロー、ミックスでボチボチ使ってみますかね。

鳥なき里のマイコン屋(128) VS CodeでラズパイPico、プロジェクトファイル生成 に戻る

鳥なき里のマイコン屋(130) VS CodeでラズパイPico、GPIOで割り込み に進む

pico_time APIのrepeating_timerを使って定周期処理を行うサンプルコード
#include <stdio.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/timer.h"

#define EXT_LED 15

int onOffFlag;

bool repeating_timer_callback(struct repeating_timer *t) {
    if (onOffFlag == 0) {
        gpio_put(EXT_LED, true);  // low active, LED OFF
        onOffFlag = 1;
    } else {
        gpio_put(EXT_LED, false); // low active, LED ON
        onOffFlag = 0;
    }
    return true;
}

int main()
{
    int counter = 0;
    struct repeating_timer timer;

    onOffFlag = 1;

    stdio_init_all();

    gpio_init(EXT_LED);
    gpio_set_dir(EXT_LED, GPIO_OUT);
    gpio_put(EXT_LED, true);
    
    add_repeating_timer_ms(250, repeating_timer_callback, NULL, &timer);

    while (counter < 100) {
        printf("%d\r\n", counter);
        sleep_ms(2000);
        counter++;
    }

    cancel_repeating_timer(&timer);
    puts("End of Execution.\r\n");

    return 0;
}