Subsystems are just a convention used to provide several implementations of a peripheral (like microcontroller peripherals, an external imu board...), protocol (gps, communications...) or algorithm (control, estimation...). Function calls are written "by hand" and the build system takes care to compile the particular implementation.
They are selected and configured with a <subsystem name="foo" type="bar"> in the firmware section of the airframe file. All this does is basically include a makefile foo_bar.makefile that adds the respective sources and adds a few configuration options. (See conf/autopilot/subsystems/...) This makes it easier to put an airframe file together (they replace the old raw makefile section) and also allows us to change the code and move/rename files behind the scenes without breaking everyones airframe files.
Creating a new Subsystem
A new subsystem is required when adding support for new or alternate core functionalities, as listed above. Some new subsystems will rely on underlying peripheral or MCU peripheral drivers (for example a new IMU subsystem), others do not need this (for example a new set of control loops). An MCU peripheral uses on-chip hardware peripherals in the main microcontroller (such as the SPI hardware), while normal peripherals are external hardware such as an analog to digital converter integrated circuit (such as the MAX1168).
The general strategy for adding a new subsystem should be something along the lines of:
- (if required) write a peripheral driver for the specific device or device family in sw/airborne/peripherals/
- (if required) in the peripheral driver, use the sw/airborne/mcu_periph/*.[c|h] arch-independent mcu peripheral drivers to handle the communication and input-output operations
- (if required) you may wish to use a family generic c header (.h) file for register definitions, etc., if the driver could be used with a family of peripheral devices
- write a subsystem driver for the new functionality, device or device family in sw/airborne/subsystems/TYPE/
- you may wish to use a family generic c header (.h) file for register definitions, etc., if the driver could be used with a family of peripheral devices
- if you need to add complex features (for example, use a gpio for a reset, use an interrupt for data ready, etc), you will need to add some arch specific files in the correct location to handle some of the extra initialization and operation, for example in sw/airborne/arch/ARCHTYPE/subsystems|peripherals|mcu_periph|ETC/
- write a subsystem makefile to aid in building the code, found in conf/firmwares/subsystems/fixedwing|rotorcraft|shared/
- even though you technically need to make a new one, you can undoubtedly use existing examples to make your life easier. For example, for an IMU, try looking at the adxl345 peripheral driver, or possibly the imu_b2.[c|h] (which uses for example the ms2100 and max1168 peripherals) for some ideas
- last thing, make sure you USE MASTER for all development, and take a look at the Contributing page, and be sure to review the coding style guidelines
- heavily comment the code, especially regarding any magic numbers and operations (for example, scaling the input of a sensor)
Finally, master is always being updated and changed, so if you aren't quite sure about something, don't hesitate to ask on the mailing list. In addition, there may be style differences between newer, updated subsystems and older subsystems.
Modules use code generation in order to allow people to use them by only editing the vehicle's xml configuration as opposed to changing/adding to the code of the autopilot. Typically, function calls are generated for initialisation and in the mainloop for timers (periodic in paparazzi slang) and events. The event and periodic functions of the Modules get called at the end of event_task_ap and periodic_task_ap respectively.
Creating a new Module
- See Modules for more information
So the difference is that with subsystems you explicitly call the functions (e.g. control loops) from the main ap loop, whereas the event/periodic functions of the Modules get called at the end of the main event/periodic tasks. So Modules are perfect to add stuff like extra payload and sensors, but Subsystems give you tighter control over when and in what order to call functions (e.g. for estimation/control).
Modules naturally provide the "several implementations" feature of the Subsystems. So, we could have a "generic" main.c consisting only in calls to the generated functions. Nevertheless, care should be taken that switching to a "module only" configuration doesn't become detrimental to the readability and debugability of the code.
You can also wrap some subsystem code as a modules (e.g. infrared), or the other way round (e.g. imu_ppzuav). As you can see with the imu_ppzuav module it also still expects a direct ImuEvent call from main_ap, so you could say it's not a "pure" module.