ardupilot/README_AP_HAL.md

19 KiB

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++ Strings, 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