marunomaruno-memo

marunomaruno-memo

Lego Mindstoms NXT - NXC プログラミング (6) タスク

2008年01月22日 | LEGO
今まで作っていた NXT のプログラムはすべて main タスクだけで
した。関数をいくつか作りましたが、それらもタスクとしてはすべ
て main タスクの配下にあります。

今回は、main タスクとは別のタスクを作ってみましょう。

NXC では、タスクは最大で 255 個まで作れます。


■ 動きながら音を鳴らす

これは、「動く」タスクと、「音を鳴らす」タスクの 2 つを作り、
これらのタスクを main タスクから起動します。

これらの動きを、main タスクだけのシングルタスクで実現するの
は無理があります。
まあ、モーター自体はタスクがスリープしている間は動き続けるの
でいいとして、トーンを鳴らすスリープとタイミングを計るのは無
理があります。
これらは、別タスクとして動かすことで、スムーズに無理なく 2
つの動作を実現できるのです。

今回は、それぞれのタスクを無限ループするようにしているので、
止めるときは NTX の停止ボタンで止めます。


---
/**
 * MovingAndPlaying01.nxc
 * 「トライボット」使用
 * 1秒間前進し、その後1秒間停止、1秒間後退する。
 * その間に音を鳴らす。
 * @author maruno
 * @version 1.0, 2008-01-12
 * @since 1.0
 */
#include "NXCDefs.h"
#include "Music01.nxc"  // 音楽関係のマクロ  // (1)

/**
 * 音楽再生タスク
 */
task playing()  // (2)
{
    while (true) {
        PLAY_TONE(FR_C6,  NOTE_MINIM,    2, false, INTERVAL);
        PLAY_TONE(FR_C5,  NOTE_QUAVER,   2, false, INTERVAL);
        PLAY_TONE(FR_C4,  NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_C5,  NOTE_CROTCHET, 2, false, INTERVAL);

        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);

        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);
    }
}

/**
 * 移動タスク
 */
task moving()   // (3)
{
    while (true) {
        OnFwd(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
        Wait(500);
        OnRev(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
    }
}

/**
 * メインタスク
 */
task main()
{
    Precedes(playing, moving);  // (4)
}
---


□ #include "Music01.nxc" // 音楽関係のマクロ // (1)

前回の周波数等はマクロの定数としてヘッダーファイルに入れてお
きます。


□ task playing() // (2)

タスク playing の定義です。task キーワードを使います。関数と
同じように、使う前に定義しておく必要があります。


□ task moving() // (3)

タスク moving の定義です。


□ Precedes(playing, moving); // (4)

playing, moving タスクを起動します。


□ Precedes 関数
---
Precedes(task1, task2, ..., taskN) Function

指定されたタスクの実行をスケジュールします。

例:
Precedes(moving, drawing, playing);
---


---
#ifndef MUSIC01_H_
#define MUSIC01_H_
/**
 * Music01.ncx
 * 音楽関係のマクロ定義
 * @author maruno
 * @version 1.0, 2008-01-12
 * @since 1.0
 */

/*
 * 音符を演奏する時間(ミリ秒)
 */
#define NOTE_SEMIBREVE 1200                 // 全音符
#define NOTE_MINIM     (NOTE_SEMIBREVE / 2) // 二分音符
#define NOTE_CROTCHET  (NOTE_MINIM     / 2) // 四分音符
#define NOTE_QUAVER    (NOTE_CROTCHET  / 2) // 八分音符

/*
 * 周波数
 */
#define FR_B3  247
#define FR_B4  494
#define FR_B5  988
#define FR_B6  1976
#define FR_B7  3951
#define FR_B8  7902

#define FR_AS3 233
#define FR_AS4 466
#define FR_AS5 932
#define FR_AS6 1865
#define FR_AS7 3729
#define FR_AS8 7458

#define FR_A3  220
#define FR_A4  440
#define FR_A5  880
#define FR_A6  1760
#define FR_A7  3520
#define FR_A8  7040
#define FR_A9  14080

#define FR_GS4 415
#define FR_GS5 831
#define FR_GS6 1661
#define FR_GS7 3322
#define FR_GS8 6644
#define FR_GS9 13288

#define FR_G4  392
#define FR_G5  784
#define FR_G6  1568
#define FR_G7  3136
#define FR_G8  6272
#define FR_G9  12544

#define FR_FS4 370
#define FR_FS5 740
#define FR_FS6 1480
#define FR_FS7 2960
#define FR_FS8 5920
#define FR_FS9 11840

#define FR_F4  349
#define FR_F5  698
#define FR_F6  1397
#define FR_F7  2794
#define FR_F8  5588
#define FR_F9  11176

#define FR_E4  330
#define FR_E5  659
#define FR_E6  1319
#define FR_E7  2637
#define FR_E8  5274
#define FR_E9  10548

#define FR_DS4 311
#define FR_DS5 622
#define FR_DS6 1245
#define FR_DS7 2489
#define FR_DS8 4978
#define FR_DS9 9956

#define FR_D4  294
#define FR_D5  587
#define FR_D6  1175
#define FR_D7  2349
#define FR_D8  4699
#define FR_D9  9398

#define FR_CS4 277
#define FR_CS5 554
#define FR_CS6 1109
#define FR_CS7 2217
#define FR_CS8 4435
#define FR_CS9 8870

#define FR_C4  262
#define FR_C5  523
#define FR_C6  1047
#define FR_C7  2093
#define FR_C8  4186
#define FR_C9  8372

/*
 * 音符間の間隔(ミリ秒)
 */
#define INTERVAL 100

/**
 * トーンを鳴らすマクロ
 * @param frequency 周波数
 * @param duration  持続時間(ミリ秒)
 * @param volume    音量
 * @param bLoop     繰り返すか
 * @param interval  間隔時間(ミリ秒)
 */
#define PLAY_TONE(frequency, duration, volume, bLoop, interval) 
    do { 
        PlayToneEx((frequency), (duration), (volume), (bLoop)); 
        Wait((duration)+(interval)); 
    } while(false)

#endif
---




■ タッチセンサーで、演奏したり止めたりする

つぎに、タッチセンサーをスイッチ代わりにして、タッチするたび
に演奏したり止めたりするようにしてみましょう。

ただし、これだと、NBC の stopthread 文でエラーになる。なぜ?


---
# Error: Invalid opcode: stopthread
File "C:_docsLegoBricx_NXCMovingAndPlaying02.nxc" ; line
2482
# stopthread playing
---


---
/**
 * MovingAndPlaying02.nxc
 * 「トライボット」使用
 * 1秒間前進し、その後1秒間停止、1秒間後退する。
 * その間に演奏する。このとき、タッチセンサーが押されたら、演奏したり、止めたりする。
 * @author maruno
 * @version 1.0, 2008-01-12
 * @since 1.0
 */
#include "NXCDefs.h"
#include "Music01.nxc"  // 音楽関係のマクロ
#include "SensorFuncs02.nxc"  // センサー関係

bool isPlay;  // 演奏中かどうか  // (1)

/**
 * 音楽再生タスク
 */
task playing()
{
    while (true) {
        PLAY_TONE(FR_C6,  NOTE_MINIM,    2, false, INTERVAL);
        PLAY_TONE(FR_C5,  NOTE_QUAVER,   2, false, INTERVAL);
        PLAY_TONE(FR_C4,  NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_C5,  NOTE_CROTCHET, 2, false, INTERVAL);

        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);

        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
        PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);
    }
}

/**
 * 移動タスク
 */
task moving()
{
    while (true) {
        OnFwd(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
        Wait(500);
        OnRev(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
    }
}

/**
 * タッチセンサータスク
 */
task touching()   // (2)
{
    while (true) {
        if (SENSOR_1 == 1) {
            if (isPlay) {  //(3)
                StopTask(playing);  // (4)
                isPlay = false;
            } else {
                StartTask(playing);  // (5)
                isPlay = true;
            }
        }
    }
}

/**
 * メインタスク
 */
task main()
{
    // センサーの設定
    SetSensors(MASK_S1);

    // タスクの開始
    Precedes(moving, touching);
}
---



□ bool isPlay; // 演奏中かどうか // (1)

演奏中かどうかを表すフラグをグローバル変数として定義する。


□ task touching() // (2)

タッチセンサーのタスク。
演奏中のときにタッチされれば演奏を止め、演奏していないときに
タッチされると演奏を開始する。


□ if (isPlay) { //(3)

演奏中かどうかを判断。


□ StopTask(playing); // (4)
□ StartTask(playing); // (5)

playing タスクの終了、開始。


□ StartTask 関数
---
StartTask(task) Function

指定されたタスクを開始する。

例:
StartTask(sound); // start the sound task
---

□ StopTask 関数
---
StopTask(task) Function

指定されたタスクを停止する。

例:
StopTask(sound); // stop the sound task
---


■ しょうがないので、少し仕様を変えて、タッチセンサーが押さ
れたら、演奏を開始するようにした。

---
/**
 * MovingAndPlaying03.nxc
 * 「トライボット」使用
 * 1秒間前進し、その後1秒間停止、1秒間後退する。
 * その間に演奏する。このとき、タッチセンサーが押されたら、演奏を開始する。
 * @author maruno
 * @version 1.0, 2008-01-14
 * @since 1.0
 */
#include "NXCDefs.h"
#include "Music01.nxc"  // 音楽関係のマクロ
#include "SensorFuncs02.nxc"  // センサー関係

/**
 * 音楽再生タスク
 */
task playing()
{
    PLAY_TONE(FR_C6,  NOTE_MINIM,    2, false, INTERVAL);
    PLAY_TONE(FR_C5,  NOTE_QUAVER,   2, false, INTERVAL);
    PLAY_TONE(FR_C4,  NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_C5,  NOTE_CROTCHET, 2, false, INTERVAL);

    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_AS6, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);

    PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL);
    PLAY_TONE(FR_DS5, NOTE_CROTCHET, 2, false, INTERVAL + NOTE_CROTCHET);
}

/**
 * 移動タスク
 */
task moving()
{
    while (true) {
        OnFwd(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
        Wait(500);
        OnRev(OUT_BC, 75);
        Wait(500);
        Off(OUT_BC);
    }
}

/**
 * タッチセンサータスク
 * タッチされたら、演奏タスクを開始
 */
task touching()
{
    while (true) {
        if (SENSOR_1 == 1) {
            StartTask(playing);
        }
    }
}

/**
 * メインタスク
 */
task main()
{
    // センサーの設定
    SetSensors(MASK_S1);

    // タスクの開始
    Precedes(moving, touching);
}
---



以上



最新の画像もっと見る

コメントを投稿