ardupilot/README_AP_HAL.md

453 lines
19 KiB
Markdown

AP\_HAL Readme
Pat Hickey
Galois Inc
17 Sept 2011
--------
Overview
========
AP\_HAL is hardware abstraction layer for the ArduPilot project. The goal is
to separate AVR (via avr-libc) and Arduino (via the Arduino core and included
libraries like SPI and Wire) dependencies from the ArduPilot programs and
libraries.
This will make it possible to port the ArduPlane and ArduCopter programs
(and their associated libraries) to a new platform without changing any of
the ArduPlane or ArduCopter code, only implementing the AP\_HAL interface
for the new platform.
Currently, the AP\_HAL\_AVR library , found in /libraries/AP\_HAL\_AVR/, is an
implementation of AP\_HAL for the ArduPilot hardware platforms.
AP\_HAL\_AVR exports two HAL instances, `AP_HAL_AVR_APM1` and `AP_HAL_AVR_APM2`,
which provide the instances for APM1 and APM2 hardware.
-----------
Requirments
===========
AP\_HAL is designed to work with the Arduino 1.0.1 IDE with a special "Coreless"
modification, or with the ArduPilot Arduino.mk makefiles with a similar Coreless
extension. The Arduino.mk Makefile in the AP\_HAL development branch also
implements the coreless modification. You will not need to upgrade to a
coreless patched IDE if you are just using Makefiles.
The Coreless modification to the Arduino IDE will be made available as a patch
and as compiled IDEs on the ArduPilot project's Downloads section.
Why Coreless Arduino
--------------------
todo
Consequences of Coreless Arduino
--------------------------------
* Need to provide an empty "Arduino.h" somewhere in the include path of each
sketch. The line `#include <Arduino.h>` is added to each sketch by the
Arduino IDE's sketch preprocessor, as well as by the Makefile build.
* Need to provide an entry point `int main(void)` for the application.
Previously, a trivial implementation existed in the Arduino core:
```
int main(void) {
init(); /* Initialized the Arduino core functionality */
setup();
while(1) loop();
return 0; /* Never reached */
}
```
Each program built with the coreless Arduino build will need to provide its
own `main` function. The following implementation may be used in at the bottom
of the sketch PDE/INO file:
```
extern "C" {
int main(void) {
hal.init(NULL);
setup();
while(1) loop();
return 0;
}
}
```
The `extern "C"` wrapper is required because a PDE/INO file is compiled
as C++.
* Global objects which were previously available in all Arduino sketches, such
as Serial, Serial1, etc. no longer exist. This is by design - all of those
hardware interfaces should be accessed through the HAL.
-------------------------
Using The AP\_HAL Library
=========================
AP\_HAL, found in /libraries/AP\_HAL/, is a library of purely virtual classes:
there is no concrete code in the AP\_HAL library, only interfaces. All code in
the core ArduPlane & ArduCopter programs, ArduPilot libraries, and example
sketches should depend only on the interfaces exposed by AP\_HAL.
The collection of classes in the AP\_HAL library exist in the AP\_HAL C++
namespace. The convention is for a program to instantiate a single instance
of the `AP_HAL::HAL` class, under a reference to the name `hal`.
```
#include <AP_HAL.h>
const AP_HAL::HAL& hal = specific_hal_implementation;
```
This instance should be made in a single object file. All other object files,
including libraries (even those inside an AP\_HAL implementation, should use
the AP\_HAL interface by declaring an extern reference to `hal`.
```
#include <AP_HAL.h>
extern const AP_HAL::HAL& hal;
```
-----------------------------
Using The AP\_HAL\_AVR library
=============================
The AP\_HAL\_AVR library exports AP\_HAL::HAL instances for the APM1 and APM2.
These instances are made of a number of concrete classes which implement the
AP\_HAL interfaces for the AVR platform. These implementations depend only on
avr-libc and not the Arduino core.
Some of the code in AP\_HAL\_AVR, such as the the GPIO class's pinMode, read,
and write, has been derived directly from the source code of the Arduino core
pinMode, digitalRead, and digitalWrite.
When using the coreless Arduino IDE to build for AVR, you will need the
following three libraries included in the top level of your sketch:
#include <AP_Common.h>
#include <AP_HAL.h>
#include <AP_HAL_AVR.h>
and then declare one of the following hal lines depending on your platform:
const AP_HAL::HAL& hal = AP_HAL_AVR_APM1;
or
const AP_HAL::HAL& hal = AP_HAL_AVR_APM2;
-----------------------
AP\_HAL Library Contents
========================
The `AP_HAL` library is organized as follows:
AP\_HAL.h : exports all other headers for the library.
AP\_HAL\_Namespace.h : exposes the C++ namespace `AP_HAL`. The namespace
declaration declares each class by name (not implementation) and some useful
typedefs.
The AP\_HAL interface classes are each defined in a header file bearing their
name.
AP\_HAL::HAL
------------
The `AP_HAL::HAL` class is a container for the a complete set of device
drivers. The class is defined in `/libraries/AP_HAL/HAL.h`. It also has a
virtual (i.e. overridable) method to handle driver initialization. Each device
driver is exposed as a pointer an AP\_HAL driver class, (e.g. each serial
driver is exposed as a public `UARTDriver* uartN`).
The following drivers are public members of `AP_HAL::HAL`. (Each class is in
the `AP_HAL` namespace, left off for brevity.)
* `UARTDriver* uart0` : Corresponds to ArduPilot FastSerial library override
of the Arduino core Serial object
* `UARTDriver* uart1` : Corresponds to Serial1 object (as above)
* `UARTDriver* uart2` : Corresponds to Serial2 object (as above)
* `UARTDriver* uart3` : Corresponds to Serial3 object (as above)
* `I2CDriver* i2c` : Corresponds to ArduPilot `/libraries/I2C` driver
* `SPIDriver* spi` : Corresponds to Arduino library SPI object
* `AnalogIn* analogin` : Corresponds to
`/libraries/AP_AnalogSource/AP_AnalogSource_Arduino` driver
* `Storage* storage` : Corresponds to avr-libc's `<avr/eeprom.h>`
driver
* `Dataflash* dataflash` : Corresponds to ArduPilot `/libraries/DataFlash`
driver
* `ConsoleDriver* console` : New utility for warning and error reporting
* `GPIO* gpio` : Corresponds to Arduino core `pinMode`,
`digitalRead`, and `digitalWrite` functionality
* `RCInput* rcin` : Corresponds to PPM input side of ArduPilot
`/libraries/APM_RC` library
* `RCOutput* rcout` : Corresponds to PPM output side of ArduPilot
`/libraries/APM_RC` library
* `Scheduler* scheduler` : Encompasses both Arduino core timing functions
such as `millis` and `delay` and the ArduPilot
`/library/AP_PeriodicProcess/` driver.
`AP_HAL` also has an unimplemented `virtual void init(void* opts) const` method.
This method should initialize each driver before the call to a sketch's `setup`
method.
AP\_HAL::UARTDriver
--------------------
The `AP_HAL::UARTDriver` class is the AP\_HAL replacment for ArduPilot's
`FastSerial` library.
The `AP_HAL::UARTDriver` class is a pure virtual interface. The code is derived
directly from the `FastSerial` class. It provides the methods `begin()`,
`end()`, `flush()`, `is_initialized()`, `set_blocking_writes`, and
`tx_pending`. The class hierchary for `AP_HAL::UARTDriver` is also derived
directly from the `FastSerial` class's hierarchy . `AP_HAL::UARTDriver` is a
`public AP_HAL::BetterStream`, which is a `public AP_HAL::Stream`, which is a
`public AP_HAL::Print`.
The `utility/` directory contains the definitions of the `AP_HAL::Print`,
`AP_HAL::Stream`, and `AP_HAL::BetterStream` classes, as well as default
implementations for the `AP_HAL::Print` class.
`AP_HAL::Print` and `AP_HAL::Stream` are derived directly from the classes of
the same name in the Arduino core. Some methods dealing with the Arduino core's
C++ `String`s, and the `Printable` class, have been left out.
The `AP_HAL::Print` class has default implementations of all of the print
methods. Each of these methods is implemented in terms of
`virtual size_t write(uint8_t char)`, which is expected to be implemented by
a class deriving from `AP_HAL::Print`. This is the same structure as used in
the Arduino core.
The `AP_HAL::Stream` class is based on the Arduino core's Stream, but reduced
to just the methods we actually use in ArduPilot - `int available()`,
`int read()`, and `int peek()`. These methods are all pure virtual
The `AP_HAL::BetterStream` class is a pure virtual version of the class by the
same name from Mike Smith's `FastSerial` library. It exposes the methods
`int txspace()`, `void print_P()`, `void println_P`, `void printf()`, and
`void printf_P()`. As in FastSerial's BetterStream library, function names
postfixed with `_P` take a string in program space as their first argument.
To use the AVR program space types, the `AP_HAL::BetterStream` class depends on
the `<avr/pgmspace.h>` and `AP_Common.h` header files. This is the only part of
the `AP_HAL` library which still depends on an AVR specific library. We will
find a way to make this dependency modular by isolating the parts of the
`AP_Common` and `<avr/pgmspace.h>` headers which BetterStream depends upon
into a single header, and then conditionally defining those typedefs and
macros to innocuous implementations when compiling for other platforms.
AP\_HAL::I2CDriver
------------------
The `AP_HAL::I2CDriver` class is the AP\_HAL replacment for ArduPilot's
`I2C` library. (ArduPilot's `I2C` library is a replacment for the Arduino
provided `Wire` library. The `Wire` library is so bad I could cry.)
The `AP_HAL::I2CDriver` class is a pure virtual interface, found in
`/libraries/AP_HAL/I2CDriver.h.` The methods resemble those in defined by the
`I2C` class in `/libraries/I2C/I2C.h`, but to ease future implementations, all
of the old Arduino `Wire` class compatibility methods have been dropped.
The `I2CDriver` interface supports the timeout features we require to assure
safe timing properties when polling the hardware I2C peripeheral. It also
only exposes whole-transaction interfaces to the user, to support more efficient
implementations in a threaded environment.
AP\_HAL::SPIDriver
------------------
The `AP_HAL::SPIDriver` class is pure virtual and can be found in
`/libraries/AP_HAL/SPIDriver.h`.
The `AP_HAL::SPIDriver` class is derived from, but only slightly resembles the
Arduino core's `SPI` library. Methods we don't use have been removed, as has
the export of a global `SPI` object. The `begin` method has been swapped for a
more idiomatic `init` method, and `setClockDivider` has been replaced with a
more general `setSpeed` method. In addition, it is not possible to operate the
SPI device as a slave. The `uint8_t transfer(uint8_t data)` method remains in
common.
The `SPIDriver` interface (and the existing AVR implementation) will require
significant changes to support efficient implementations in a threaded
environment. Transfers will have to be possible in bulk, and somehow incorporate
the notion of the target device so that specialized hardware can manage the
slave device select lines. Currently, device select signals are sent using the
`AP_HAL::GPIO::write` method, which is the AP\_HAL analog of Arduino's
`digitalWrite`.
AP\_HAL::AnalogIn
-----------------
The `AP_HAL::AnalogIn` class is pure virtual and can be found in
`/libraries/AP_HAL/AnalogIn.h`. The pure virtual `AP_HAL::AnalogSource` class
is also defined in that class.
The `AP_HAL::AnalogSource` interface is based loosely on the `AP_AnalogSource`
interface in the existing AP\_AnalogSource library. At this time, an
`AP_HAL::AnalogSource` has a single method `float read()` which returns the
latest analog measurment. There are no methods for flow control - currently you
must assume the method will block until a measurment is ready; the timing is
entirely implementation defined.
The `AP_HAL::AnalogIn` interface does not have a close analog in the existing
libraries. Consider it to be, loosely, a factory for `AnalogSource` objects.
There are only two methods: the standard `void init(void*)` initializer and
a numbered interface to the available analog channels `AP_HAL::AnalogSource*
channel(int n)`. At this time the significance of each numbered channel is
determined by the implementation.
Extensions will be made to these interfaces as required to meet the needs of
other platforms. We will also have to consider making named channels, as
opposed to numbered channels, available from the `AP_HAL::AnalogIn` interface
AP\_HAL::Storage
----------------
The `AP_HAL::Storage` class is pure virtual and can be found in
`/libraries/AP_HAL/Storage.h`. It is the AP\_HAL interface to take the place
of the AVR EEPROM, possibly with other nonvolatile storage.
The `AP_HAL::Storage` interface very closely resembles the avr-libc eeprom
interface. The use of `uint8_t*` projections into storage space are subject to
change - it seems to make more sense to use integer types to designate these
locations, as there is no valid dereference of a pointer value.
AP\_HAL::Dataflash
------------------
The `AP_HAL::Dataflash` class is pure virtual and can be found in
`/libraries/AP_HAL/Dataflash.h`. It is based on the existing ArduPilot
`DataFlash` library found in `/libraries/DataFlash`. The `AP_HAL::Dataflash`
interface is a cleaned up version of that library's interface. Public member
variables have been replaced with getter methods, unused public interfaces
have been removed, and all methods have had their names translated to lowercase
and underscores for style.
Similar to the problems with the SPI library, the existing `DataFlash` library
interface is oriented for byte-at-a-time writes to the hardware device. This
interface may have to be revised to support bulk transfers for efficient use
in a threaded environment.
AP\_HAL::ConsoleDriver
----------------------
The `AP_HAL::ConsoleDriver` class is pure virtual and can be found in
`/libraries/AP_HAL/Console.h` . It is derived from the `AP_HAL::BetterStream`
class.
In the existing ArduPilot code, there is no unified way to send debugging
messages, warnings, and errors to the user. A dedicated Console driver, is
envisioned as a better way to communicate that sort of information to the user.
In addition to the `AP_HAL::BetterStream` interface, `AP_HAL::ConsoleDriver`
exposes the following methods for implementing a user defined backend. (I
envision this backend will be piped over a mavlink connection by application,
but right now I'm leaving it open ended.)
* `void init(void*implspecific)` : Standard init code. Should be called after
the UARTDrivers, but before all other drivers, in `AP_HAL::HAL::init`.
* `void backend_open()` : Start buffering reads and writes to user backend
* `void backend_close()` : Stop buffering reads and writes to user backend
* `int backend_read(uint8_t *data, int len)` : Read from user backend buffer.
Data sent by `write` through the `BetterStream` interface will be available
to `backend_read`, modulo oveflowing the internal buffers (undefined
behavior).
* `int backend_write(const uint8_t *data, int len)` : Write to user backend
buffer. Written data will be available by `read` through the `BetterStream`
interface, modulo overflowing the internal buffers (undefined behavior).
A few implementation guidelines:
* This is a low assurance data interface: it is more important to maintain the
timing properties of the system than deliver data properly. Assume the user
backend to the BetterStream interface will be operated synchronously, and take
care not to create deadlocks.
* Behavior while the user backend _is not_ open is undefined. It may be
appropriate to either implement a trivial interface (drop all writes, return -1
on all reads) or proxy to a UARTDriver, depending on the platform & developer's
needs.
* Behavior while the user backend _is_ open: activity on the `BetterStream`
interface should never block. Internal buffer sizes are undefined. Data that
doesn't fit into the internal buffers should be dropped.
AP\_HAL::RCInput
--------------
The `AP_HAL::RCInput` class is pure virtual and can be found in
`/libraries/AP_HAL/RCInput.h`. The RCInput interface is based on the
input related methods of the existing ArduPilot `APM_RC` class. RCInput methods
were separated from RCOutput methods for clarity.
The methods `uint8_t valid()` and `uint16_t read(uint8_t)` carry over the exact
interface found in `APM_RC`, which is to specify the number of valid channels,
and then read each channel out by number.
Based on the most common use case, which is to read the valid flag and then all
input channels sequentially, a new interface `uint8_t read(uint16_t* periods,
uint8_t len)` makes the valid flag available as the return value, and writes
the periods as an array to the memory specified by the first argument.
This bulk read interface may be more efficient in a threaded environment.
AP\_HAL::RCOutput
--------------
The `AP_HAL::RCOutput` class is pure virtual and can be found in
`/libraries/AP_HAL/RCOutput.h`. The RCOutput interface is based on the
input related methods of the existing ArduPilot `APM_RC` class. RCOutput methods
were separated from RCInput methods for clarity.
The output methods from `APM_RC` were reproduced here faithfully, with minor
differences to naming. As an extension, the `read` and `write` methods are
overloaded to also have versions which take or give arrays of values, as the
most common use case is to read or write all channels sequentially, and bulk
reads and writes may be more efficient in a threaded environment.
AP\_HAL::Scheduler
--------------
The `AP_HAL::Scheduler` class is pure virtual and can be found in
`/libraries/AP_HAL/Scheduler.h`. The `AP_HAL::Scheduler` interface
is designed to encapsulate both the timing utilities previously provided
by the Arduino core (i.e. `millis` and `delay`), as well as scheduling
asynchronous processes as a replacment to the ArduPilot `AP_PeriodicProcess`
driver.
The following methods are exposed by the `AP_HAL::Scheduler` interface:
* `void init(void* implspecific)` : Initializer should setup all hardware.
This should be the very first initializer called by AP_HAL::HAL::init.
* `void delay(uint32_t ms)` : Duplicates Arduino core `delay`. Will call
delay callback, if registered.
* `uint32_t millis()` : Duplicates Arduino core `millis`
* `uint32_t micros()` : Duplicates Arduino core `micros`
* `void delay_microseconds(uint16_t us)` : Duplicates Arduino core
`delayMicros`
* `void register_delay_callback(AP_HAL::Proc, uint16_t min_ms)`: Register
a callback to be used during calls to `delay`. Callback will be called at
a 1ms period. Second argument is the minimum length of time expected to
delay - set this to the ceiling of the runtime for the callback.
* `void register_timer_process(AP_HAL::TimedProc)` : Duplicates
`AP_PeriodicProcess::register_process`
* `void register_timer_failsafe(AP_HAL::TimedProc,
uint32_t period_us)` : Duplicates `AP_PeriodicProcess::set_failsafe`
* `void suspend_timer_procs()` : Duplicates
`AP_PeriodicProcess::suspend_timer`
* `void resume_timer_procs()` : Duplicates
`AP_PeriodicProcess:resume_timer`
Remaining ArduPilot AVR dependencies
====================================
The class `AP_Param` makes extensive use of AVR program space pointers and
accessors in its implementation. The definition of these types and accrssors
will have to be defined in a single external header file so they can be
replaced by innocuous types and accessors when compiling for other platforms