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 supports I2C and SPI mode using synchronous communications (where you must manually invoke the driver every so often to receive data from the IMU). It also supports an asynchronous mode for SPI communications, where a dedicated thread handles communicating with the IMU when it has new data.
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");
}
}
}
}
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;
}
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);
}
}
CC1200 Driver
Author: Jamie Smith
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!
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);
}
}
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.
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
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!
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!