Arm Mbed OS support forum

USC RPL's Driver Compendium


At USC Rocket Propulsion Lab, we use Mbed OS a lot! And since we have gotten so much out of this community, we are trying to contribute our own efforts back whenever possible.
Me and the other engineers on my team have written quite a few drivers and other utilities over the last few years, and I wanted to create a single thread to collect and demonstrate them all. We’d also hope that this can be a place where people can ask questions and get support with any of these projects, so feel free to comment if you run into any trouble.
Without further ado, here’s the full list:

BNO080 Driver

Author: Jamie Smith

Use This To:

Control CEVA (formerly Hilcrest)‘s BNO080 and FSM300 IMU modules.
These sophisticated IMU modules provide a 9-axis orientation reading combining magnetometer, accelerometer, and gyroscope data. RPL currently uses them in concert with other sensors for measuring our rockets’ flight dynamics. However, they’re also very useful for a variety of mobile device and human interface applications.

Note: This driver urrently only supports I2C and synchronous communication. An update to enable asynchronous communication support and SPI is coming in the next few months.

Code Example:

int main()
{
    BNO080 imu(&pc, p28, p27, p16, p30, 0x4a, 100000); 
 
 
    // Tell the IMU to report rotation every 100ms and acceleration every 200ms
    imu.enableReport(BNO080::ROTATION, 100);
    imu.enableReport(BNO080::TOTAL_ACCELERATION, 200);
 
    while (true)
    {
        wait(.001f);
        
        // poll the IMU for new data -- this returns true if any packets were received
        if(imu.updateData())
        {
            // now check for the specific type of data that was received (can be multiple at once)
            if (imu.hasNewData(BNO080::ROTATION))
            {
                // convert quaternion to Euler degrees and print
                printf("IMU Rotation Euler: ");
                TVector3 eulerRadians = imu.rotationVector.euler();
                TVector3 eulerDegrees = eulerRadians * (180.0 / M_PI);
                eulerDegrees.print(pc, true);
                pc.printf("\n");
            }
        }
    }
}

Code Repository

Examples Repository

ADIS16467 Driver

Author: Rita Yang

Use This To:

Read data from Analog Devices’ ADIS16467 line of precision IMU modules. This “wish-it-was-military-grade” IMU is much, much more accurate than consumer grade units like the BNO080, boasting three-sig-fig accelerometer precision and gyroscope drift of less than a degree per second. However, it is also priced to match this accuracy, and it’s missing the sensor fusion and integration features found on complete IMU modules. So, it’s ideally suited to robotics and aerospace applications, as a building block of a complete motion recording and/or control system.

This driver supports nearly all features of the IMU, including burst and individual reads, decimation factors, and delta angles/positions.

Code Example

int main() {
 
    //Create IMU, passing in all parameters
    //All pins here are arbitrary, you will need to configure them accordingly
    ADIS16467 imu(&pc, PIN_SPI_MOSI, PIN_SPI_MISO, PIN_SPI_SCK, PIN_ADIS_CS, PIN_ADIS_RST);
 
    //Initialize the IMU
    imu.initADIS();
 
    struct ADIS16467::BurstReadResult result;
    imu.burstRead(result);
 
    printf("Gyro X : %.2f degree/sec\r\n", ((float)result.gyroX * imu.GYRO_CONV));
    printf("Gyro Y : %.2f degree/sec\r\n", ((float)result.gyroY * imu.GYRO_CONV));
    printf("Gyro Z : %.2f degree/sec\r\n", ((float)result.gyroZ * imu.GYRO_CONV));
    printf("Accel X : %.2f mg\r\n", ((float)result.accelX * imu.ACCEL_CONV));
    printf("Accel Y : %.2f mg\r\n", ((float)result.accelY * imu.ACCEL_CONV));
    printf("Accel Z : %.2f mg\r\n", ((float)result.accelZ * imu.ACCEL_CONV));
    printf("Internal Temperature : %.2f C\r\n", ((float)result.internalTemp * imu.TEMP_CONV));
    printf("Data Counter : %5d\r\n", result.dataCounter);
    printf("Checksum Value : %5d\r\n", result.checkSum);
 
    return 0;
}

Code Repository

Examples Repository

U-Blox MAX-8 GPS Driver

Authors: Adhyyan Sekhsaria, Jamie Smith

Use This To:

Read a variety of common data items from U-Blox’s MAX-8 series of GPS modules. Our driver automatically configures a number of reports, and provides all the important GPS data including position, time, velocity, and fix quality. U-Blox’s GPS modules have solid performance and include a number of handy features, such as I2C and SPI interfaces, an active antenna power supply, and a much larger variety of available reports than standard NMEA GPSs.

Note: This driver currently only supports I2C (who needs UART anyway?!?). We are working on an SPI version as well as updating to add support for U-Blox’s 9th gen GPS modules.

Note 2: U-Blox publishes an official Mbed driver for their GPS units (a fact that we didn’t learn about until after we wrote this…). However, as far as I can tell, this driver only provides low-level communications and doesn’t handle encoding or decoding the binary message data. In contrast, our driver handles the entire process of encoding and decoding the data.

Code Example

int main(){
    MAX8U gps(&pc, PIN_I2C_SDA, PIN_I2C_SCL, p25);
    bool booted = gps.begin();
 
    if(!booted){
        //handle error
    }
    booted = gps.configure();
    if(!booted){
        //handle error
    }
 
    while(true){
        bool newMessageRcvd = gps.update();
        printf(">Position: %.06f %c, %.06f %c, Height %.02f m\r\n",
                  std::abs(gps.latitude), gps.latitude > 0 ? 'N' : 'S',
                  std::abs(gps.longitude), gps.longitude > 0 ? 'E' : 'W',
                  gps.height);
 
        // accuracy
        printf(">Fix: Quality: %" PRIu8 ", Num Satellites: %" PRIu8 ", Position Accuracy: %.02f m\r\n",
                static_cast<uint8_t>(gps.fixQuality), gps.numSatellites, gps.posAccuracy);
    }
}

Code Repository

CC1200 Driver

Author: Jamie Smith

CC1200 Dev Kit

Use This To:

Send and receive data using Texas Instruments’ CC1200 (and CC1201) radio transceiver ICs. These radio chips are incredibly flexible, and feature a variety of different configurable frequency bands (from 136MHz-960MHz), radio modulations (ASK, FSK, and OOK), and symbol rates (1Hz-500kHz), as well as a litany of other packet formatting options. The CC1200 automatically handles forming data into packets and checksumming it, and also provides 128 byte FIFOs for data being transmitted and received. So, your processor can let the CC1200 handle the real-time aspect of radio communications and pick up the data later at its leisure. All in all, the CC1200 is a solid option if you want to add RF communications to an existing MCU or board.

Since this radio has so many different configuration settings to set, historically users have been limited to the premade configuration values that TI provides in their SmartRF Studio. However, since RPL needed to experiment with lots of different settings on the CC1200, this driver implements the calculation of all register values from the raw RF settings (such as symbol rate and frequency) that you select. This wasn’t easy, and required us to fight through many vague datasheet register descriptions and calculations (as well as some that were flat-out wrong), but it’s working well now! And thanks to our labor, you can select absolutely any RF configuration supported by the chip.

Note: The CC1200 supports operation as an 802.15.4 transceiver, but RPL hasn’t implemented this functionality because we don’t need it. If you did want to implement this, however, this driver would be a good place to start, and we’re open to contributions!

Code Repository

Examples Repository

Morse Code Transmitter Example

SerialStream

Author: Jamie Smith

Use This To:

Convert a BufferedSerial or UnbufferedSerial object into an Mbed Stream class. This lets you call printf() and scanf() on it, and pass it to existing (Mbed 5 legacy) code that expects a Stream.

Note: The use of Stream does introduce some additional overhead into your code, and this was part of the reason BufferedSerial did not implement Stream. However, this is unlikely to make a difference in many applications.

Note 2: There is also a newer way to use printf() with BufferedSerial using fdopen(); this (along with some other interesting info) can be found in the PR that deprecated Serial. In my opinion, this is something that ARM has done an atrocious job of documenting and it’s criminal that the only place I found this information was by digging through closed PRs! (though it’s also buried in the FileHandle docs if you look hard enough)

Code Example:

#include <mbed.h>
#include "SerialStream.h"
 
BufferedSerial serial(USBTX, USBRX, 115200);
SerialStream<BufferedSerial> pc(serial);
 
int main() {
 
    while(true)
    {
        pc.printf("Hello world!\n");
        ThisThread::sleep_for(1000);
    }
}

Code Repository

Mbed-Benchtest

Authors: Jamie Smith, Luka Govedič, Jasper Swallen

Use This To:

Run code that uses Mbed RTOS on your desktop PC. This code nearly perfectly emulates the behavior of ARM’s RTX RTOS using our own RTXOff(-Board) RTOS, so you can test your threaded application code from the comfort of your own desktop.

Currently only the RTOS as well as a few stubbed Mbed OS headers are implemented, so you can only run application code, not code that communicates with hardware. However, in the future as we become more invested in testing infrastructure, we hope to add emulation for common Mbed classes like Serial and Timer.

Code Repository

KX134-1211 Driver

Author: Jasper Swallen

Use This To:

Communicate with Kionix’s KX134-1211 accelerometer IC. Especially at its size and price point, this IC is amazingly accurate, beating the ADXL375 that we used to use by almost a factor of 10 in accuracy and resolution. It also sports extremely high measurement range (up to ±64g) and update rate (up to 25.6kHz). We’re pretty excited about using it on our future projects!

Code Example:

    KX134 kx134Obj(PIN_SPI_MOSI, PIN_SPI_MISO, PIN_SPI_SCK, PIN_SPI_CS,
                   PIN_INT1, PIN_INT2, PIN_RST);

    if(!kx134Obj.init())
    {
        printf("Failed to initialize KX134\r\n");
        return 1;
    }

    printf("Successfully initialized KX134\r\n");

    int hz = 25600;

    // initialize asynch reading
    kx134Obj.enableRegisterWriting();
    kx134Obj.setOutputDataRateHz(hz);
    kx134Obj.setAccelRange(KX134::Range::RANGE_64G);
    kx134Obj.disableRegisterWriting();
    
    int16_t output[3];
    kx134Obj.getAccelerations(output);
    float ax = kx134Obj.convertRawToGravs(output[0]);
    float ay = kx134Obj.convertRawToGravs(output[1]);
    float az = kx134Obj.convertRawToGravs(output[2]);

Code Repository (w/ example code)

AccurateWaiter

Author: Jamie Smith

AccurateWaiter Test

Use This To:

Wait for an exact number of microseconds while allowing other threads to run. In standard Mbed OS, threaded applications are limited by the precision of ThisThread::sleep_for(), which only allows sleeping for a whole number of milliseconds. This limitation comes from the underlying RTX RTOS, which uses a 1 ms scheduling interrupt. However, AccurateWaiter uses some EventFlags trickery to get around this, allowing a thread to sleep for a time accurate to the millisecond and then wake up immediately with little overhead. Use it to create finer-grain threaded apps then ever before!

Code Repository

Example Code


This is all we’ve got for now, but we are always making more drivers as we build new hardware. Expect to see drivers for the INA226, ADS124S0x, and BQ34Z100 soonish!

6 Likes