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
BNO080Async.h@9:430f5302f9e1, 2020-11-24 (annotated)
- Committer:
- Jamie Smith
- Date:
- Tue Nov 24 15:06:05 2020 -0800
- Revision:
- 9:430f5302f9e1
Implement BNO080Async
Who changed what in which revision?
User | Revision | Line number | New 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 |