When studying ArduPilot’s source code, the first question most people would ask is: where is ArduPilot’s program entry point?
The short answer is not surprising at all: there is a main function as every user space program does.
However, it’s not straightforward to tell where it is. So let’s find out.
Main as a Macro
In the file libraries/AP_HAL/AP_HAL_Main.h it defines a macro:
#define AP_HAL_MAIN_CALLBACKS(CALLBACKS) extern "C" { \ int AP_MAIN(int argc, char* const argv[]); \ int AP_MAIN(int argc, char* const argv[]) { \ hal.run(argc, argv, CALLBACKS); \ return 0; \ } \ }
and in the same file you can also find #define AP_MAIN main
.
So it turns out that AP_HAL_MAIN_CALLBACKS()
is actually the main function we are looking for.
It takes CALLBACKS
as an argument and passes it in hal.run(argc, argv, CALLBACKS);
.
Then, there are two things we want to understand in the rest of the post:
- Where the macro
AP_HAL_MAIN_CALLBACKS()
is used to create the main function - Where
hal.run(argc, argv, CALLBACKS);
is implemented
Where The Main Macro is Used
Taking ArduCopter as an example, we can find that it is used in the file ArduCopter/ArduCopter.cpp#L576 to create the main function and also assign the callback as an instance of the class Copter
ArduCopter/Copter.h#L183:
AP_HAL_MAIN_CALLBACKS(&copter);
where the variable copter
is declared in
Copter copter;
The Real Main Function
Now let’s focus on hal.run(argc, argv, CALLBACKS);
inside the main function.
Here, hal
is an instance of AP_HAL
declared as follows:
const AP_HAL::HAL& hal = AP_HAL::get_HAL();
This class AP_HAL
is hardware dependent — meaning the implementation is up to the platform specific drivers.
In the case of Linux, AP_HAL::get_HAL()
is implemented in the Linux’s driver:
const AP_HAL::HAL &AP_HAL::get_HAL() { return halInstance; }
where halInstance
is declared nearby:
static HAL_Linux halInstance;
So now we know hal
is actually an instance of class HAL_Linux
, we can then track where the hal.run(argc, argv, CALLBACKS);
call implements:
void HAL_Linux::run(int argc, char* const argv[], Callbacks* callbacks) const { // ... // Parse startup arguments ... // ... scheduler->init(); // ... // Initialize GPIO, RCIN/OUT, UART, AIN ... // ... callbacks->setup(); // ... while (!_should_exit) { callbacks->loop(); } // ... }
As you can see, this function does all the initialization. Most importantly, it contains the loop that consistently invokes the loop
function from the callbacks
which corresponds to copter
that is passed from the macro AP_HAL_MAIN_CALLBACKS(&copter)
as a parameter.
In case you are curious what the loop does in class Copter
, it invokes the scheduler’s function:
void Copter::loop() { scheduler.loop(); G_Dt = scheduler.get_last_loop_time_s(); }