前回のおさらい
前回からだいぶ間が開きました。
前回は、機能追加を行うための基本的な事柄として、sketchの作成方法を見ました。その中で以下のことを見ました。
- setup()は最初の一回だけ呼ばれる。
- setup()が呼ばれたあとで、loop()が繰り返し呼ばれる。
- 何か、halというグローバル変数がある。少なくともコンソールへの表示は何かのインスタンス経由で行われるようだ。
- AP_HAL_MAIN()という謎マクロがソースの一番下にある。
そして、以下の疑問点を最後に列挙しました。
- setup()とloop()は誰が呼び出すのか?
- AP_HAL_MAIN()とは何者か?
- halの具体的内容は?
- 一番最初の図には「ユーザプロセス」とある。今書いたsketchとプロセスの関係は?
今回はソースを読んで、これらの疑問点を確認します。
AP_HAL_MAIN()を確認する
AP_HAL_MAIN()は以下のヘッダファイルに定義されているマクロです。
# ifndef __AP_HAL_LINUX_MAIN_H__
# define __AP_HAL_LINUX_MAIN_H__
# if CONFIG_HAL_BOARD == HAL_BOARD_LINUX
# define AP_HAL_MAIN() extern "C" {\
int main (int argc, char * const argv[]) { \
hal.init(argc, argv); \
hal.scheduler->system_initialized(); \
setup();\
for(;;) loop();\
return 0;\
}\
}
# endif // HAL_BOARD_LINUX
# endif // __AP_HAL_LINUX_MAIN_H__
実装を見ると、確かにsetup()が1度だけ呼ばれて、その後loop()が無限に呼ばれます。また、setup()とloop()を呼び出している関数名がmain()であることにも注目してください。
ここまでで、以下のことがわかりました。
疑問点 | 対応する実装 |
---|---|
setup()とloop()は誰が呼び出すのか? | AP_HAL_MAIN()で定義するmain()関数経由で呼ばれる |
AP_HAL_MAIN()とは何者か? | C/C++で実行時に呼ばれるmain()関数を定義している |
一番最初の図には「ユーザプロセス」とある。今書いたsketchとプロセスの関係は? | sketch実装がmain()から呼ばれている。よって、sketch自体があるプロセスの一部となっている。プロセス自体の仕組みはこれから読んでいきます。 |
halとは何者か
AP_HAL_MAIN()の中には以下の実装が含まれています。
hal.init(argc, argv); \
hal.scheduler->system_initialized(); \
halとは何者でしょうか。halというのは、このページのAP_HALの説明にあるとおり、ハードウェアの差異を吸収する抽象化層です。基本的には、具体的なハードの詳細を意識することなくコードの実装ができるように準備されたものです。
さて、前回読んだスケルトンコードには、以下の実装がありました。
const AP_HAL::HAL& hal = AP_HAL_BOARD_DRIVER;
halについて、詳しく知りたければ、AP_HAL_BOARD_DRIVERを見れば良さそうです。
検索すると、以下のヘッダファイルで定義されています。
# elif CONFIG_HAL_BOARD == HAL_BOARD_LINUX
# define AP_HAL_BOARD_DRIVER AP_HAL_Linux
/* 以下略 */
AP_HAL_BOARD_DRIVERの実体はAP_HAL_Linuxだということがわかりました。
また、CONFIG_HAL_BOARDの値によって、define値が変わることもわかります。
AP_HAL_Linuxを見る
AP_HAL_Linuxは以下のヘッダで定義されています。
class HAL_Linux : public AP_HAL::HAL {
public:
HAL_Linux();
void init(int argc, char * const * argv) const;
};
また、コンストラクタは以下のファイルで実装されています。
/* HAL_Linuxのコンストラクタ */
HAL_Linux::HAL_Linux() :
AP_HAL::HAL(
&uartADriver,
&uartBDriver,
&uartCDriver,
NULL, /* no uartD */
&uartEDriver,
# if CONFIG_HAL_BOARD_SUBTYPE == HAL_BOARD_SUBTYPE_LINUX_BEBOP
&i2cDriver0,
&i2cDriver1,
&i2cDriver2,
# else
&i2cDriver0,
NULL,
NULL,
# endif
&spiDeviceManager,
&analogIn,
&storageDriver,
&uartADriver,
&gpioDriver,
&rcinDriver,
&rcoutDriver,
&schedulerInstance,
&utilInstance)
{}
const HAL_Linux AP_HAL_Linux;
さて、クラス定義をみるとわかりますが、クラスHAL_Linuxは、クラスAP_HAL::HALを継承しています。
定義は、ardupilot/libraries/AP_HAL/HAL.h にあります。
AP_HAL::HALは長いのでここでは引用しません。しかし、Ardupilotでは基本となるクラスですので、必ず目を通してください。
疑問点 | 対応する実装 |
---|---|
halの具体的内容は? | Linuxベースのシステムでは、クラスHAL_LinuxのインスタンスAP_HAL_Linux。AP_HAL::HALを継承している。 |
次回
次回は、以下の実装を掘り下げます。次回は少し難しくなるかもしれません。
hal.init(argc, argv); \
hal.scheduler->system_initialized(); \