前言
自己探索代码的第一步是使用库的示例概述。遵循 arduino 传统,我们为大多数共享库提供了示例概述。一个 ‘sketch’ 就是一个主程序,编写为一个 cpp 文件。
了解 ArduPilot 中使用的库 API 和约定是理解代码的关键。因此,使用库中的示例概述是一个很好的入门方法。作为一个开始,你应该阅读、构建和运行以下库的示例概述:
- libraries/AP_GPS/examples/GPS_AUTO_test
- libraries/AP_InertialSensor/examples/INS_generic
- libraries/AP_Compass/examples/AP_Compass_test
- libraries/AP_Baro/examples/BARO_generic
- libraries/AP_AHRS/examples/AHRS_Test
例如,以下将在 Pixhawk 上构建和安装 AP_GPS 示例概述:
cd $ARDUPILOT_HOME # the top-level of an ArduPilot repository ./waf configure --board=Pixhawk1 ./waf build --target examples/GPS_AUTO_test --upload
waf 可以列出它能构建的示例:
1. cd $ARDUPILOT_HOME 2. ./waf list | grep 'examples'
一旦你上传了这个例子,你就可以通过连接到控制台来查看输出。控制台是什么,取决于板子的类型。在 Pixhawk 板上,它是 USB 接口。所以只要用你喜欢的串行程序连接到 USB 设备(波特率并不重要)。
例如,如果你安装了 mavproxy,你可以这样做来连接到 Linux 上的 Pixhawk:
mavproxy.py --setup --master /dev/serial/by-id/usb-3D_Robotics_PX4_FMU_v2.x_0-if00
使用 --setup 选项可以使 mavproxy 进入原始串行模式,而不是经过处理的 MAVLink 模式。 这就是你对示例概述的需要。
1 在SITL中运行示例
某些示例也可以在 SITL 中运行。例如,运行协议解码器示例:
cd $ARDUPILOT_HOME # the top-level of an ArduPilot repository ./waf configure --board sitl ./waf build --target examples/RCProtocolDecoder
要开始示例,请直接运行它:
./build/sitl/examples/RCProtocolDecoder -M quad -C
2 理解示例概述代码
当你在阅读示例概述代码(例如 GPS_AUTO_test 代码)时,你会注意到一些一开始可能看起来很奇怪的事情:
- 它声明了一个‘hal’变量作为一个引用;
- 这段代码很粗糙,没有很好的注释;
- setup() 和 loop() 函数。
2.1 hal引用
每个使用 AP_HAL 功能的文件都需要声明一个 hal 引用。这样就可以访问 AP_HAL :: HAL 对象,该对象可以访问所有硬件的具体功能,包括将消息打印到控制台,休眠以及与 I2C 和 SPI 总线通信等。
实际的 hal 变量被隐藏在板子具体的 AP_HAL_XXX 库中。每个文件中的引用只是提供了一种方便的方法来获取 hal。
最常用的 hal 函数是:
- hal.console-> printf() 打印字符串;
- AP_HAL :: millis()和AP_HAL :: micros() 用于获取启动后的时间;(毫秒和微秒)
hal.scheduler-> delay()和hal.scheduler-> delay_microseconds() 用于休眠一小段时间;
hal.gpio-> pinMode(),hal.gpio-> read() 和 hal.gpio-> write() 用于访问 GPIO 引脚;
- 通过 hal.i2c 访问 I2C;
- 通过 hal.spi 访问 SPI;
现在,查看 libraries/AP_HAL 目录,查看 HAL 上可用功能的完整列表。
2.2 setup()和loop()函数
你会注意到,每个示例都有一个 setup() 函数和 loop() 函数。当电路板启动时调用 setup() 函数。实际的调用来自每个板子的 HAL 内部,
所以 main() 函数被隐藏在 HAL 内部,然后在板子具体启动完成后调用 setup()。
setup() 函数只被调用一次,通常是初始化库,并且可能会打印一个“hello”标语以表明它正在启动。
setup() 完成后,loop() 函数被持续调用(由 AP_HAL 中的主代码调用)。示例的主要工作通常在loop() 函数中。
请注意,对于更复杂的电路板,这种 setup()/loop() 布置只是冰山一角。这可能会让人觉得 ArduPilot 是单线程的,但事实上下面还有很多事情要做,在具有线程的主板(例如 Pixhawk 和基于 Linux 的主板)上,实际上会启动许多实时线程。请参阅下面有关理解 ArduPilot 线程的部分。
2.3 AP_HAL_MAIN()宏
你会注意到在每个示例的底部有这样额外的一行:
AP_HAL_MAIN();
这是一个 HAL 宏,产生必要的代码来声明一个 C++ 主函数,以及任何 HAL 的板级初始化代码。你几乎不必担心它的工作原理,但是如果你很好奇,可以在每个 HAL 的 AP_HAL_XXX 目录中查找 #define。它通常在 AP_HAL_XXX_Main.h 中。
2.4 粗略的示例代码
你会注意到,这些示例概述是相当粗糙的,而且注释不清。这是你为代码做出贡献的机会!当你在阅读示例概述并探索其工作原理时,给代码添加一些注释以解释 API,然后提交一个拉取请求(submit a pull request),这样其他人就可以从你的研究中受益。