Mbedでマルチスレッド

これはロ技研アドベントカレンダー13日目の記事です。

今年は部員がたくさん増えたので、カレンダーが2つになりました。

はじめに

こんにちは、tokkyo13です。最近寒いですね。こんなに寒いのに、お布団から出るという行為に賃金が発生しないのはおかしいと思うんですよ。

さて、今日はmbedでマルチスレッドを使う話です。知っているとマイコンのプログラミングも幅が広がると思います。それでは行ってみましょう。

マルチスレッドって何?

マルチスレッドとは簡単に言うと複数の処理を並列で実行する仕組みのことです。単純なプログラムだと、実行の流れは1つだけで、ループや関数呼び出しを除けば、基本的に上から順に実行されます。この実行の流れを2つ以上に増やすのがマスチスレッドです。プログラムを実行してるCPUが分身の術を使って、複数の処理を同時に実行するようなイメージで考えてくれればいいと思います。マルチスレッドに対して単純なプログラムのことをシングルスレッドと呼んだりもします。

余談ですが、『暗殺教室』という漫画に「殺せんせー」というキャラクターが登場します。殺せんせーは高速移動をしながら、その残像を使って独立した動きをする分身を作り出します。あれがまさにマルチスレッドです、と言えばピンとくる人がいるかも知れませんね。

RTOS on Mbed

Mbedでは、RTOS (Real-time operating system) の機能が提供されています。RTOSについての細かい話はしませんが、RTOSの機能を使ってマルチスレッドが実現できます。

RTOSの提供する機能の使い方は、Mbedのウェブサイトに詳しく書かれています。今日はこれらの中から、

  • スレッド
  • 相互排他

の2つを扱ってみようと思います。

RTOSの使い方

これから、ソースコードを示したりしますので、コンパイルの方法について紹介します。

オンラインコンパイラの場合は、テンプレートとしてRTOS basic example.を選択して、main.cppを書き換えればOKです。

PlatformIOを使う場合は、まずProject Examplesからmbed-rtosを選択します。次に、インポートされたプロジェクトファイルの中にあるplatformio.iniというファイルを、使用するマイコン用に書き換えます。NucleoF446REの場合は、次のように書き込めばOKです。あとは、srcフォルダ下のmain.cppを書き換えましょう。

[env:nucleo_f446re]
platform = ststm32
framework = mbed
board = nucleo_f446re
build_flags = -DPIO_FRAMEWORK_MBED_RTOS_PRESENT

以下、NucleoF446RE用のプログラムを想定してプログラム例を示します。

スレッド

下にあるのが、1つのLEDを2秒周期で点滅させるプログラムです。

#include "mbed.h"

DigitalOut led1(A0);

int main()
{
    while (true)
    {
        led1 = !led1;
        wait(1);
    }
}

これにスレッドを1つ加えて、1秒周期で点滅するLEDを1つ追加してみましょう。次のようなプログラムになります。

#include "mbed.h"

DigitalOut led1(A0);
DigitalOut led2(A1);
Thread thread; // threadという名前のスレッドを宣言

void led2_thread() // 追加したスレッドが実行する用の関数
{
    while (true)
    {
        led2 = !led2;
        wait(1);
    }
}

int main() // main は必ず実行されるスレッド
{
    thread.start(led2_thread); // thread に led2_thread 関数の実行を開始させる

    while (true)
    {
        led1 = !led1;
        wait(0.5);
    }

    thread.join(); // thread の終了を待つ
}

使い方は上のプログラムを見ればだいたい分かると思いますが、説明します。

  1. まず、増やしたい数だけスレッドを宣言します。(5行目)
  2. 次に、増やしたスレッドが実行する関数を記述します。(7行目)
  3. 次に、メインのスレッド内で、新しいスレッドを開始します。(18行目)
  4. 増やしたスレッドの終了を待ってからメインのスレッドを終了する場合 (26行目)

メインのスレッドというのは、main関数から始まって順に実行されていく処理の流れのことです。また、4. についてですが、上のプログラムは無限ループなので、スレッドは終了しません。join()は、増やしたスレッドの終了を待ち、タイミングを揃えるときなどに使います。

これを実行すると、次の動画のようになります。

https://platform.twitter.com/widgets.js

相互排除

上で紹介したプログラムは、各スレッドが自分専用のLEDを持っていました。しかし、1つのLEDしか使えなかったらどうなるでしょう。次のプログラムは、1つのLEDを0.6秒周期で点滅させようとするスレッドと、0.2秒周期で点滅させようとするスレッドが競合状態になっている例です。

#include "mbed.h"

DigitalOut led1(A0);
Thread thread;

void led2_thread()
{
    while (true)
    {
        led1 = !led1;
        wait(0.1);
    }
}

int main()
{
    thread.start(led2_thread);

    while (true)
    {
        led1 = !led1;
        wait(0.3);
    }

    thread.join();
}

これを実行すると、次の動画のように、なんだかおかしな挙動になります。面白いですね。

https://platform.twitter.com/widgets.js

このような競合を防ぐには、LEDへのアクセスが可能なスレッドを1つに絞る必要があります。そのために使われるのが、相互排除です。相互排除を実現する機能として、ミューテックス(Mutex)という機能が提供されています。Mutexは、簡単に言うとアクセス権のようなもので、取得したり返却したりできます。アクセス権を取得することを「ロック」といい、アクセス権を返すことを「アンロック」といいます。

では、Mutexを使って先程のプログラムを書き換えましょう。

#include "mbed.h"

DigitalOut led1(A0);
Thread thread;
Mutex mutex; // ミューテックスの宣言

void led2_thread()
{
    while (true)
    {
        mutex.lock(); // ミューテックスをロック
        for (int i = 0; i < 6; i++) // 0.2秒周期で3回点滅
        {
            led1 = !led1;
            wait(0.1);
        }
        mutex.unlock(); // ミューテックスをアンロック
        wait(0.5);
    }
}

int main()
{
    thread.start(led2_thread);

    while (true)
    {
        mutex.lock(); // ミューテックスをロック
        for (int i = 0; i < 6; i++) // 0.6秒周期で3回点滅
        {
            led1 = !led1;
            wait(0.3);
        }
        mutex.unlock(); // ミューテックスをアンロック
        wait(0.5);
    }

    thread.join();
}

LEDの点滅プログラムは都合上、3回点滅して0.5秒間待つようにしています。この0.5秒の間に他のスレッドがLEDへのアクセス権を奪えるようにです。

結果、LEDは0.2秒周期の点滅と0.6秒周期の点滅を交互に実行するようになります。その様子は次の動画のようになります。

https://platform.twitter.com/widgets.js

この様に、Mutexは限られたリソース(今回の場合LED)を一時的に独占したいときに利用します。限られたリソースを使う前にロック、使い終わったらアンロックです。

おわりに

この記事で説明したのは、RTOSの機能のごく一部です。マルチスレッドや同期実行に興味が湧いた人はMbedのウェブサイトを読んで、サンプルプログラムを実行してみると良いと思います。

広告

メカ・ダチョウを作ってみた!

皆さんこんにちは、ロボット技術研究会のnversationcoです。今回はTAMIYAのロボクラフトシリーズからメカ・ダチョウを作った時のことについて書こうと思います。

恥ずかしながらロボットのサークルに入っておいてロボットの組み立てキットなどほとんど手に取ったことがなかったので買っちゃいました。パッケージはこんな感じです。 Continue reading “メカ・ダチョウを作ってみた!”

剣道で学ぶPID制御器設計

こんにちは。syuntoku14です。

アドベントカレンダー10日目なんですが、僕がこれを投稿しているのは12日です。なんだったらカリフォルニアで時差があるので13日目です。遅れてごめんなさい。

今回はPID根軌跡を使ったPID制御器の設計の一通りの流れを書きました。眠いです。

wordpress非常に書きにくかったので、はてなブログに書きました。リンク先に飛んでね。

http://syuntoku14.hatenablog.com/entry/2018/12/13/141247