Fully featured I2C and SPI driver for CEVA (Hilcrest)'s BNO080 and FSM300 Inertial Measurement Units.

Dependents:   BNO080-Examples BNO080-Examples

BNO080 Driver

by Jamie Smith / USC Rocket Propulsion Lab

After lots of development, we are proud to present our driver for the Hilcrest BNO080 IMU! This driver is inspired by SparkFun and Nathan Seidle's Arduino driver for this chip, but has been substantially rewritten and adapted.

It supports the main features of the chip, such as reading rotation and acceleration data, as well as some of its more esoteric functionality, such as counting steps and detecting whether the device is being hand-held.

Features

  • Support for 15 different data reports from the IMU, from acceleration to rotation to tap detection
  • Support for reading of sensor data, and automatic checking of update rate against allowed values in metadata
  • BNO_DEBUG switch enabling verbose, detailed output about communications with the chip for ease of debugging
  • Ability to tare sensor rotation and set mounting orientation
  • Can operate in several execution modes: polling I2C, polling SPI, and threaded SPI (which handles timing-critical functions in a dedicated thread, and automatically activates when the IMU has data available)
    • Also has experimental support for using asynchronous SPI transactions, allowing other threads to execute while communication with the BNO is occurring. Note that this functionality requires a patch to Mbed OS source code due to Mbed bug #13941
  • Calibration function
  • Reasonable code size for what you get: the library uses about 4K of flash and one instance of the object uses about 1700 bytes of RAM.

Documentation

Full Doxygen documentation is available online here

Example Code

Here's a simple example:

BNO080 Rotation Vector and Acceleration

#include <mbed.h>
#include <BNO080.h>

int main()
{
	Serial pc(USBTX, USBRX);

	// Create IMU, passing in output stream, pins, I2C address, and I2C frequency
	// These pin assignments are specific to my dev setup -- you'll need to change them
	BNO080I2C imu(&pc, p28, p27, p16, p30, 0x4a, 100000); 

	pc.baud(115200);
	pc.printf("============================================================\n");

	// 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
				pc.printf("IMU Rotation Euler: ");
				TVector3 eulerRadians = imu.rotationVector.euler();
				TVector3 eulerDegrees = eulerRadians * (180.0 / M_PI);
				eulerDegrees.print(pc, true);
				pc.printf("\n");
			}
			if (imu.hasNewData(BNO080::TOTAL_ACCELERATION))
			{
				// print the acceleration vector using its builtin print() method
				pc.printf("IMU Total Acceleration: ");
				imu.totalAcceleration.print(pc, true);
				pc.printf("\n");
			}
		}
	}

}


If you want more, a comprehensive, ready-to-run set of examples is available on my BNO080-Examples repository.

Credits

This driver makes use of a lightweight, public-domain library for vectors and quaternions available here.

Changelog

Version 2.1 (Nov 24 2020)

  • Added BNO080Async, which provides a threaded implementation of the SPI driver. This should help get the best performance and remove annoying timing requirements on the code calling the driver
  • Added experimental USE_ASYNC_SPI option
  • Fixed bug in v2.0 causing calibrations to fail

Version 2.0 (Nov 18 2020)

  • Added SPI support
  • Refactored buffer system so that SPI could be implemented as a subclass. Unfortunately this does substantially increase the memory usage of the driver, but I believe that the benefits are worth it.

Version 1.3 (Jul 21 2020)

  • Fix deprecation warnings and compile errors in Mbed 6
  • Fix compile errors in Arm Compiler (why doesn't it have M_PI????)

Version 1.2 (Jan 30 2020)

  • Removed accidental IRQ change
  • Fixed hard iron offset reading incorrectly due to missing cast

Version 1.1 (Jun 14 2019)

  • Added support for changing permanent orientation
  • Add FRS writing functions
  • Removed some errant printfs

Version 1.0 (Dec 29 2018)

  • Initial Mbed OS release
Committer:
Jamie Smith
Date:
Tue Nov 24 15:06:05 2020 -0800
Revision:
9:430f5302f9e1
Implement BNO080Async

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Jamie Smith 9:430f5302f9e1 1 #ifndef BNO080_BNO080ASYNC_H
Jamie Smith 9:430f5302f9e1 2 #define BNO080_BNO080ASYNC_H
Jamie Smith 9:430f5302f9e1 3
Jamie Smith 9:430f5302f9e1 4 #if MBED_CONF_RTOS_PRESENT
Jamie Smith 9:430f5302f9e1 5
Jamie Smith 9:430f5302f9e1 6 #include <BNO080.h>
Jamie Smith 9:430f5302f9e1 7 #include <Mutex.h>
Jamie Smith 9:430f5302f9e1 8
Jamie Smith 9:430f5302f9e1 9 /**
Jamie Smith 9:430f5302f9e1 10 * Asynchronous version of the SPI BNO080 driver.
Jamie Smith 9:430f5302f9e1 11 * Since the timing requirements of this driver are so different,
Jamie Smith 9:430f5302f9e1 12 * wouldn't it be great to have a dedicated thread to handle them?
Jamie Smith 9:430f5302f9e1 13 * This class provides exactly that, using an internal thread triggered
Jamie Smith 9:430f5302f9e1 14 * by interrupts to connect to the BNO.
Jamie Smith 9:430f5302f9e1 15 *
Jamie Smith 9:430f5302f9e1 16 * Note: the internal thread is started the first time begin() is called,
Jamie Smith 9:430f5302f9e1 17 * and is shut down when the class is destroyed.
Jamie Smith 9:430f5302f9e1 18 */
Jamie Smith 9:430f5302f9e1 19 class BNO080Async : public BNO080SPI
Jamie Smith 9:430f5302f9e1 20 {
Jamie Smith 9:430f5302f9e1 21 public:
Jamie Smith 9:430f5302f9e1 22 // Mutex protecting all sensor data in the driver.
Jamie Smith 9:430f5302f9e1 23 // You must lock this while reading data and unlock it when done.
Jamie Smith 9:430f5302f9e1 24 // While this is locked, the background thread is prevented from running.
Jamie Smith 9:430f5302f9e1 25 Mutex bnoDataMutex;
Jamie Smith 9:430f5302f9e1 26
Jamie Smith 9:430f5302f9e1 27 private:
Jamie Smith 9:430f5302f9e1 28 // thread in charge of communicating with the BNO
Jamie Smith 9:430f5302f9e1 29 Thread commThread;
Jamie Smith 9:430f5302f9e1 30
Jamie Smith 9:430f5302f9e1 31 // EventFlags to allow signalling the thread to wake up
Jamie Smith 9:430f5302f9e1 32 EventFlags wakeupFlags;
Jamie Smith 9:430f5302f9e1 33
Jamie Smith 9:430f5302f9e1 34 // main function for the thread.
Jamie Smith 9:430f5302f9e1 35 // Handles starting the comm loop.
Jamie Smith 9:430f5302f9e1 36 void threadMain();
Jamie Smith 9:430f5302f9e1 37
Jamie Smith 9:430f5302f9e1 38 // Code loop to communicate with the BNO.
Jamie Smith 9:430f5302f9e1 39 // Runs forever in normal operation.
Jamie Smith 9:430f5302f9e1 40 // Returns true to request a restart.
Jamie Smith 9:430f5302f9e1 41 // Returns false to request the thread to shutdown.
Jamie Smith 9:430f5302f9e1 42 bool commLoop();
Jamie Smith 9:430f5302f9e1 43
Jamie Smith 9:430f5302f9e1 44 // flag to indicate that we have valid data to send queued in the TX buffer.
Jamie Smith 9:430f5302f9e1 45 // Protected by bnoDataMutex.
Jamie Smith 9:430f5302f9e1 46 bool dataToSend = false;
Jamie Smith 9:430f5302f9e1 47
Jamie Smith 9:430f5302f9e1 48 // data for packet to send, if above flag is true
Jamie Smith 9:430f5302f9e1 49 uint8_t txPacketChannelNumber;
Jamie Smith 9:430f5302f9e1 50 uint8_t txPacketDataLength;
Jamie Smith 9:430f5302f9e1 51
Jamie Smith 9:430f5302f9e1 52 // Condition variable signaled whenever a packet is sent.
Jamie Smith 9:430f5302f9e1 53 ConditionVariable dataSentCV;
Jamie Smith 9:430f5302f9e1 54
Jamie Smith 9:430f5302f9e1 55 // flag used for updateData() return value.
Jamie Smith 9:430f5302f9e1 56 // True if any data packets have been received since the last update.
Jamie Smith 9:430f5302f9e1 57 bool dataReceived = false;
Jamie Smith 9:430f5302f9e1 58
Jamie Smith 9:430f5302f9e1 59 // true iff the thread is currently in the middle of a TX/RX operation.
Jamie Smith 9:430f5302f9e1 60 // Causes _int interrupts (which get generated by the comms process) to be ignored.
Jamie Smith 9:430f5302f9e1 61 bool inTXRX = false;
Jamie Smith 9:430f5302f9e1 62
Jamie Smith 9:430f5302f9e1 63 bool sendPacket(uint8_t channelNumber, uint8_t dataLength) override;
Jamie Smith 9:430f5302f9e1 64
Jamie Smith 9:430f5302f9e1 65 void clearSendBuffer() override;
Jamie Smith 9:430f5302f9e1 66
Jamie Smith 9:430f5302f9e1 67 // waiting for packet state info
Jamie Smith 9:430f5302f9e1 68 bool waitingForPacket = false;
Jamie Smith 9:430f5302f9e1 69 uint8_t waitingForChannel;
Jamie Smith 9:430f5302f9e1 70 uint8_t waitingForReportID;
Jamie Smith 9:430f5302f9e1 71 bool waitingPacketArrived = false;
Jamie Smith 9:430f5302f9e1 72 bool clearToRxNextPacket = false;
Jamie Smith 9:430f5302f9e1 73
Jamie Smith 9:430f5302f9e1 74 ConditionVariable waitingPacketArrivedCV;
Jamie Smith 9:430f5302f9e1 75 ConditionVariable waitingPacketProcessedCV;
Jamie Smith 9:430f5302f9e1 76
Jamie Smith 9:430f5302f9e1 77 bool waitForPacket(int channel, uint8_t reportID, std::chrono::milliseconds timeout = 125ms) override;
Jamie Smith 9:430f5302f9e1 78
Jamie Smith 9:430f5302f9e1 79 public:
Jamie Smith 9:430f5302f9e1 80 /**
Jamie Smith 9:430f5302f9e1 81 * Construct a BNO080Async.
Jamie Smith 9:430f5302f9e1 82 * This doesn't actually initialize the chip, you will need to call begin() for that.
Jamie Smith 9:430f5302f9e1 83 *
Jamie Smith 9:430f5302f9e1 84 * NOTE: while some schematics tell you to connect the BOOTN pin to the processor, this driver does not use or require it.
Jamie Smith 9:430f5302f9e1 85 * Just tie it to VCC per the datasheet.
Jamie Smith 9:430f5302f9e1 86 *
Jamie Smith 9:430f5302f9e1 87 * @param debugPort Serial port to write output to. Cannot be nullptr.
Jamie Smith 9:430f5302f9e1 88 * @param rstPin Hardware reset pin, resets the IMU
Jamie Smith 9:430f5302f9e1 89 * @param intPin Hardware interrupt pin, this is used for the IMU to signal the host that it has a message to send
Jamie Smith 9:430f5302f9e1 90 * @param wakePin Hardware wake pin, this is used by the processor to signal the BNO to wake up and receive a message
Jamie Smith 9:430f5302f9e1 91 * @param misoPin SPI MISO pin
Jamie Smith 9:430f5302f9e1 92 * @param mosiPin SPI MOSI pin
Jamie Smith 9:430f5302f9e1 93 * @param sclkPin SPI SCLK pin
Jamie Smith 9:430f5302f9e1 94 * @param csPin SPI CS pin
Jamie Smith 9:430f5302f9e1 95 * @param spiSpeed SPI frequency. The BNO's max is 3MHz.
Jamie Smith 9:430f5302f9e1 96 * @param threadPriority Priority to give the internal thread. Defaults to AboveNormal so it will run whenever it can.
Jamie Smith 9:430f5302f9e1 97 */
Jamie Smith 9:430f5302f9e1 98 BNO080Async(Stream *debugPort, PinName rstPin, PinName intPin, PinName wakePin, PinName misoPin, PinName mosiPin, PinName sclkPin, PinName csPin, int spiSpeed=3000000, osPriority threadPriority=osPriorityAboveNormal);
Jamie Smith 9:430f5302f9e1 99
Jamie Smith 9:430f5302f9e1 100 /**
Jamie Smith 9:430f5302f9e1 101 * Resets and connects to the IMU. Verifies that it's connected, and reads out its version
Jamie Smith 9:430f5302f9e1 102 * info into the class variables above.
Jamie Smith 9:430f5302f9e1 103 *
Jamie Smith 9:430f5302f9e1 104 * Async version also starts the internal communication thread, restarting it if it's
Jamie Smith 9:430f5302f9e1 105 * already started.
Jamie Smith 9:430f5302f9e1 106 *
Jamie Smith 9:430f5302f9e1 107 * If this function is failing, it would be a good idea to turn on BNO_DEBUG in the cpp file to get detailed output.
Jamie Smith 9:430f5302f9e1 108 *
Jamie Smith 9:430f5302f9e1 109 * @return whether or not initialization was successful
Jamie Smith 9:430f5302f9e1 110 */
Jamie Smith 9:430f5302f9e1 111 bool begin() override;
Jamie Smith 9:430f5302f9e1 112
Jamie Smith 9:430f5302f9e1 113 /**
Jamie Smith 9:430f5302f9e1 114 * In BNO080Async, there is no need to call updateData() in order to get new
Jamie Smith 9:430f5302f9e1 115 * results, but this call is provided for compatibility.
Jamie Smith 9:430f5302f9e1 116 *
Jamie Smith 9:430f5302f9e1 117 * It maintains its behavior of returning true iff packets have been received since the last call.
Jamie Smith 9:430f5302f9e1 118 * You must lock bnoDataMutex to call this.
Jamie Smith 9:430f5302f9e1 119 * @return
Jamie Smith 9:430f5302f9e1 120 */
Jamie Smith 9:430f5302f9e1 121 bool updateData() override;
Jamie Smith 9:430f5302f9e1 122
Jamie Smith 9:430f5302f9e1 123 /**
Jamie Smith 9:430f5302f9e1 124 * Locks the data mutex.
Jamie Smith 9:430f5302f9e1 125 */
Jamie Smith 9:430f5302f9e1 126 void lockMutex() override
Jamie Smith 9:430f5302f9e1 127 {
Jamie Smith 9:430f5302f9e1 128 bnoDataMutex.lock();
Jamie Smith 9:430f5302f9e1 129 }
Jamie Smith 9:430f5302f9e1 130
Jamie Smith 9:430f5302f9e1 131 /**
Jamie Smith 9:430f5302f9e1 132 * Unlocks the data mutex.
Jamie Smith 9:430f5302f9e1 133 */
Jamie Smith 9:430f5302f9e1 134 void unlockMutex() override
Jamie Smith 9:430f5302f9e1 135 {
Jamie Smith 9:430f5302f9e1 136 bnoDataMutex.unlock();
Jamie Smith 9:430f5302f9e1 137 }
Jamie Smith 9:430f5302f9e1 138 };
Jamie Smith 9:430f5302f9e1 139
Jamie Smith 9:430f5302f9e1 140 #endif
Jamie Smith 9:430f5302f9e1 141
Jamie Smith 9:430f5302f9e1 142
Jamie Smith 9:430f5302f9e1 143 #endif //MBED_CMAKE_TEST_PROJECT_BNO080ASYNC_H