■ 関数
関数は C 言語とほぼ同じです。ただ、inline キーワードを使って、関数をインライン展開できます。ただし、インライン関数をネストさせることはできません。
また、safecall キーワードを使って、この関数の実行を排他的に行うことができます(★)。
---
[safecall] [inline] 戻り値型 名前(引数リスト)
{
// 関数のコード
}
---
ただし、関数のプロトタイプ宣言はできないので、その関数を使う前に関数を定義しておかなければなりません。ちょっと残念な仕様です。
ただし、関数が使えることで、main タスクの見通しはかなりよくなりました。
■ Trybod01 の各機能を関数にする
Trybod01 では、タッチするまで前進する、暗くなるまで前進する、などがそれぞれコーディングされています。これらを、関数を使ってコーディングしてみます。
■ 関数部分を別ソースに書く
Trybot02 の関数部分を別ソースとして書いてみます。
そうすることで、自分で作った関数を別プログラムでも使いまわすことができます。
関数は、Trybot02 で作ったモーター関係の関数と、センサー関係の関数として、別ソース "MotorFuncs01.nxc"、"SensorFuncs01.nxc" として作ります。これらを #include を使ってインクルードします。
ただ、これらの関数のソースの編集で、BricxCC を使う場合、エディタでキーワードを強調されるので、拡張子は "nxc" にしておいた方が便利でしょう。
□ #include "MotorFunctions01.nxc" // モーター関係 (1)
□ #include "SensorFunctions01.nxc" // センサー関係 (2)
それぞれの関数のソースをインクルードする。
こうすることで、ソースを分割して記述できるのでモジュール化することがしやすくなる。
ただし、MotorFunctions01.nxc などのソースのコンパイルエラーが、main タスクでコンパイルエラーとして出る場合もあるようだ。(★)
□ SetSensors(MASK_ALL_SENSORS); // (3)
4 つのセンサーを使う場合のマクロ定数。定義は、 "SensorFuncs01.nxc" で行っている。
□ #ifndef MOTOR_FUNCS01_H_ // (4)
□ #define MOTOR_FUNCS01_H_ // (4)
□ #endif // (4)
ヘッダーの2重インクルードの防止。
□ #define MASK_S1 (0x01 << S1) // (5)
□ #define MASK_S2 (0x01 << S2) // (5)
□ #define MASK_S3 (0x01 << S3) // (5)
□ #define MASK_S4 (0x01 << S4) // (5)
それぞれのセンサーに対応したビット位置を ON にするようなマスクを作る。
□ #define MASK_ALL_SENSORS (MASK_S1+MASK_S2+MASK_S3+MASK_S4) // 4つのセンサーすべて // (6)
4 つのセンサーに対応するビットをすべて ON にしたマスクをつくる。
■ センサーの値を表示する関数
つぎのように、センサーの値を表示する関数を作り、使います。
□ const string SENSOR_NAME[] = {"Touch", ..."US", }; // (1)
文字列定数の配列を定義します。
□ void OutSensorValue(int line, int sensor) // (2)
関数の定義。この関数は OutSensorValue 関数から呼ばれるので、inline キーワードはつけない。NBC では、インライン関数のネストはできないことに注意。
□ int num = ((sensor != S4) ? Sensor(sensor) : SensorUS(S4)); // (3)
超音波センサーと他のセンサーとは取得方法に違いがあることに注意。
□ string name = getSensorName(sensor); // SENSOR_NAME[sensor]; // (4)
配列を使って取得すると、なぜか実行時エラーになる。メッセージは「file error!」。なぜ?★
■ サブルーチン
C 言語には関数しかありませんが、NXC には、「サブルーチン」という機能があります。NXC では、inline でない関数で、戻り値がないものです。もっとも、これは NXC ではなく、これの前提になっている NQC の文法を引き継いでのことと考えられますので、とくに使わなくてもいいでしょう。(★)
以上
関数は C 言語とほぼ同じです。ただ、inline キーワードを使って、関数をインライン展開できます。ただし、インライン関数をネストさせることはできません。
また、safecall キーワードを使って、この関数の実行を排他的に行うことができます(★)。
---
[safecall] [inline] 戻り値型 名前(引数リスト)
{
// 関数のコード
}
---
ただし、関数のプロトタイプ宣言はできないので、その関数を使う前に関数を定義しておかなければなりません。ちょっと残念な仕様です。
ただし、関数が使えることで、main タスクの見通しはかなりよくなりました。
■ Trybod01 の各機能を関数にする
Trybod01 では、タッチするまで前進する、暗くなるまで前進する、などがそれぞれコーディングされています。これらを、関数を使ってコーディングしてみます。
--- /** * Trybot02.nxc * 「トライボット」使用 * 前進し、そこでタッチしたものをつかみ、後退、回転し、 * 黒線があるところまで前進し、黒線のところでつかんだものを離す。 * @author maruno * @version 1.0, 2008-01-08 * @since 1.0 */ #include "NXCDefs.h" #define PUT_THRESHOLD 51 // 物を置くしきい値 (cm) #define SOUND_THRESHOLD 50 // 音のしきい値 (dB) #define LIGHT_THRESHOLD 40 // 光のしきい値 /** * タッチされるまで前進する。 */ inline void ForwardUntilTouch() { OnFwd(OUT_BC, 75); until (SENSOR_1 == 1); Off(OUT_BC); } /** * 暗くなるまで前進する。 * @param threshold 暗くなるしきい値 */ inline void ForwardUntilDark(long threshold) { OnFwd(OUT_BC, 75); until (SENSOR_3 <threshold);
threshold); } /** * main タスク。 */ task main() { // センサーの設定 SetSensorTouch(S1); // タッチセンサーの設定 SetSensorSound(S2); // 音センサーの設定 SetSensorLight(S3); // 光センサーの設定 SetSensorLowspeed(S4); // 超音波センサーの設定 // 物が置かれるまで停止 StopUntilPut(PUT_THRESHOLD); // タッチするまで前進 ForwardUntilTouch(); // 音がするまで停止 StopUntilAnySound(SOUND_THRESHOLD); // アームを閉じる CloseArm(500); // 後退 ReverseMilliSeconds(500); // 回転 RotetoLeftMilliSeconds(500); // 黒線のところまで前進して停止 ForwardUntilDark(LIGHT_THRESHOLD); // アームを開く OpenArm(500); } ---
■ 関数部分を別ソースに書く
Trybot02 の関数部分を別ソースとして書いてみます。
そうすることで、自分で作った関数を別プログラムでも使いまわすことができます。
関数は、Trybot02 で作ったモーター関係の関数と、センサー関係の関数として、別ソース "MotorFuncs01.nxc"、"SensorFuncs01.nxc" として作ります。これらを #include を使ってインクルードします。
ただ、これらの関数のソースの編集で、BricxCC を使う場合、エディタでキーワードを強調されるので、拡張子は "nxc" にしておいた方が便利でしょう。
--- /** * Trybot03.nxc * 「トライボット」使用 * 前進し、そこでタッチしたものをつかみ、後退、回転し、 * 黒線があるところまで前進し、黒線のところでつかんだものを離す。 * @author maruno * @version 1.0, 2008-01-11 * @since 1.0 */ #include "NXCDefs.h" #include "MotorFuncs01.nxc" // モーター関係 (1) #include "SensorFuncs01.nxc" // センサー関係 (2) #define PUT_THRESHOLD 51 // 物を置くしきい値 (cm) #define SOUND_THRESHOLD 50 // 音のしきい値 (dB) #define LIGHT_THRESHOLD 40 // 光のしきい値 /** * main タスク。 */ task main() { // センサーの設定 SetSensors(MASK_ALL_SENSORS); // (3) // 物が置かれるまで停止 StopUntilPut(PUT_THRESHOLD); // タッチするまで前進 ForwardUntilTouch(); // 音がするまで停止 StopUntilAnySound(SOUND_THRESHOLD); // アームを閉じる CloseArm(500); // 後退 ReverseMilliSeconds(500); // 回転 RotetoLeftMilliSeconds(500); // 黒線のところまで前進して停止 ForwardUntilDark(LIGHT_THRESHOLD); // アームを開く OpenArm(500); } ---
□ #include "MotorFunctions01.nxc" // モーター関係 (1)
□ #include "SensorFunctions01.nxc" // センサー関係 (2)
それぞれの関数のソースをインクルードする。
こうすることで、ソースを分割して記述できるのでモジュール化することがしやすくなる。
ただし、MotorFunctions01.nxc などのソースのコンパイルエラーが、main タスクでコンパイルエラーとして出る場合もあるようだ。(★)
□ SetSensors(MASK_ALL_SENSORS); // (3)
4 つのセンサーを使う場合のマクロ定数。定義は、 "SensorFuncs01.nxc" で行っている。
--- #ifndef MOTOR_FUNCS01_H_ // (4) #define MOTOR_FUNCS01_H_ // (4) /** * MotorFuncs01.nxc * モーターを動かす関数 * @author maruno * @version 1.0, 2008-01-11 * @since 1.0 */ /** * タッチされるまで前進する。 */ inline void ForwardUntilTouch() { OnFwd(OUT_BC, 75); until (SENSOR_1 == 1); Off(OUT_BC); } /** * 暗くなるまで前進する。 * @param threshold 暗くなるしきい値 */ inline void ForwardUntilDark(long threshold) { OnFwd(OUT_BC, 75); until (SENSOR_3 <threshold);
threshold); } #endif // (4) ---
□ #ifndef MOTOR_FUNCS01_H_ // (4)
□ #define MOTOR_FUNCS01_H_ // (4)
□ #endif // (4)
ヘッダーの2重インクルードの防止。
--- #ifndef SENSOR_FUNCS01_H_ #define SENSOR_FUNCS01_H_ /** * SensorFunctions01.nxc * センサー関連の関数 * @author maruno * @version 1.0, 2008-01-11 * @since 1.0 */ #define MASK_S1 (0x01 << S1) // (5)
#define MASK_S2 (0x01 << S2) // (5)
#define MASK_S3 (0x01 << S3) // (5)
#define MASK_S4 (0x01 << S4) // (5)
#define MASK_ALL_SENSORS (MASK_S1+MASK_S2+MASK_S3+MASK_S4) // 4つのセンサーすべて // (6) /** * センサーを設定する。 * @param mask 使うセンサーのマスク * 76543210 * -------1 タッチセンサー * ------1- 音センサー * -----1-- 光センサー * ----1--- 超音波センサー */ inline void SetSensors(int mask) { if (mask & MASK_S1) SetSensorTouch(S1); // タッチセンサーの設定 if (mask & MASK_S2) SetSensorSound(S2); // 音センサーの設定 if (mask & MASK_S3) SetSensorLight(S3); // 光センサーの設定 if (mask & MASK_S4) SetSensorLowspeed(S4); // 超音波センサーの設定 } #endif ---
□ #define MASK_S1 (0x01 << S1) // (5)
□ #define MASK_S2 (0x01 << S2) // (5)
□ #define MASK_S3 (0x01 << S3) // (5)
□ #define MASK_S4 (0x01 << S4) // (5)
それぞれのセンサーに対応したビット位置を ON にするようなマスクを作る。
□ #define MASK_ALL_SENSORS (MASK_S1+MASK_S2+MASK_S3+MASK_S4) // 4つのセンサーすべて // (6)
4 つのセンサーに対応するビットをすべて ON にしたマスクをつくる。
■ センサーの値を表示する関数
つぎのように、センサーの値を表示する関数を作り、使います。
--- /** * Sensor02.nxc * 「トライボット」使用 * センサーの値を読み取り、表示する。 * @author maruno * @version 1.0, 2008-01-08 * @since 1.0 */ #include "NXCDefs.h" /** * センサー名を格納した文字列定数の配列 */ const string SENSOR_NAME[] = { "Touch", "Sound", "Light", "US", }; // (1) /** * センサーポートを指定し、それに対するセンサー名を取得する。 * @param sensor センサーのポート番号 */ string getSensorName(int sensor) { string name; switch (sensor) { case S1: name = "Touch"; break; case S2: name = "Sound"; break; case S3: name = "Light"; break; case S4: name = "US"; break; default: name = "undef"; break; } return name; } /** * ディスプレイの指定された行に指定されたセンサーの値を表示する。 * @param line 表示する行 * @param sensor センサーのポート番号 */ void OutSensorValue(int line, int sensor) // (2) { int num = ((sensor != S4) ? Sensor(sensor) : SensorUS(S4)); // (3) string value = NumToStr(num); string name = getSensorName(sensor); // SENSOR_NAME[sensor]; // (4) string msg = StrCat(name, " = ", value); TextOut(0, line, msg); } /** * main タスク。 */ task main() { // センサーの設定 SetSensorTouch(S1); // タッチセンサーの設定 SetSensorSound(S2); // 音センサーの設定 SetSensorLight(S3); // 光センサーの設定 SetSensorLowspeed(S4); // 超音波センサーの設定 while (true) { // タッチセンサーの値の表示 OutSensorValue(LCD_LINE1, S1); // 音センサーの値の表示 OutSensorValue(LCD_LINE2, S2); // 光センサーの値の表示 OutSensorValue(LCD_LINE3, S3); // 超音波センサーの値の表示 OutSensorValue(LCD_LINE4, S4); } } ---
□ const string SENSOR_NAME[] = {"Touch", ..."US", }; // (1)
文字列定数の配列を定義します。
□ void OutSensorValue(int line, int sensor) // (2)
関数の定義。この関数は OutSensorValue 関数から呼ばれるので、inline キーワードはつけない。NBC では、インライン関数のネストはできないことに注意。
□ int num = ((sensor != S4) ? Sensor(sensor) : SensorUS(S4)); // (3)
超音波センサーと他のセンサーとは取得方法に違いがあることに注意。
□ string name = getSensorName(sensor); // SENSOR_NAME[sensor]; // (4)
配列を使って取得すると、なぜか実行時エラーになる。メッセージは「file error!」。なぜ?★
■ サブルーチン
C 言語には関数しかありませんが、NXC には、「サブルーチン」という機能があります。NXC では、inline でない関数で、戻り値がないものです。もっとも、これは NXC ではなく、これの前提になっている NQC の文法を引き継いでのことと考えられますので、とくに使わなくてもいいでしょう。(★)
以上
※コメント投稿者のブログIDはブログ作成者のみに通知されます