Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: BNO080-Examples BNO080-Examples
BNO080.cpp
00001 // 00002 // USC RPL BNO080 driver. 00003 // 00004 00005 /* 00006 * Overview of BNO080 Communications 00007 * =============================================== 00008 * 00009 * Hilcrest has developed a protocol called SHTP (Sensor Hub Transport Protocol) for binary communications with 00010 * the BNO080 and the other IMUs it sells. Over this protocol, SH-2 (Sensor Hub 2) messages are sent to configure 00011 * the chip and read data back. 00012 * 00013 * SHTP messages are divided at two hierarchical levels: first the channel, then the report ID. Each category 00014 * of messages (system commands, sensor data reports, etc.) has its own channel, and the individual messages 00015 * in each channel are identified by their report id, which is the first byte of the message payload (note that the 00016 * datasheets don't *always* call the first byte the report ID, but that byte does identify the report, so I'm going 00017 * with it). 00018 * 00019 * =============================================== 00020 * 00021 * Information about the BNO080 is split into three datasheets. Here's the download links and what they cover: 00022 * 00023 * - the BNO080 datasheet: http://www.hillcrestlabs.com/download/5a05f340566d07c196001ec1 00024 * -- Chip pinouts 00025 * -- Example circuits 00026 * -- Physical specifications 00027 * -- Supported reports and configuration settings (at a high level) 00028 * -- List of packets on the SHTP executable channel 00029 * 00030 * - the SHTP protocol: http://www.hillcrestlabs.com/download/59de8f99cd829e94dc0029d7 00031 * -- SHTP transmit and receive protcols (for SPI, I2C, and UART) 00032 * -- SHTP binary format 00033 * -- packet types on the SHTP command channel 00034 * 00035 * - the SH-2 reference: http://www.hillcrestlabs.com/download/59de8f398934bf6faa00293f 00036 * -- list of packets and their formats for all channels other than command and executable 00037 * -- list of FRS (Flash Record System) entries and their formats 00038 * 00039 * =============================================== 00040 * 00041 * Overview of SHTP channels: 00042 * 00043 * 0 -> Command 00044 * -- Used for protocol-global packets, currently only the advertisement packet (which lists all the channels) and error reports 00045 * 00046 * 1 -> Executable 00047 * -- Used for things that control the software on the chip: commands to reset and sleep 00048 * -- Also used by the chip to report when it's done booting up 00049 * 00050 * 2 -> Control 00051 * -- Used to send configuration commands to the IMU and for it to send back responses. 00052 * -- Common report IDs: Command Request (0xF2), Set Feature (0xFD) 00053 * 00054 * 3 -> Sensor Reports 00055 * -- Used for sensors to send back data reports. 00056 * -- AFAIK the only report ID on this channel will be 0xFB (Report Base Timestamp); sensor data is sent in a series of structures 00057 * following an 0xFB 00058 * 00059 * 4 -> Wake Sensor Reports 00060 * -- same as above, but for sensors configured to wake the device 00061 * 00062 * 5 -> Gyro Rotation Vector 00063 * -- a dedicated channel for the Gyro Rotation Vector sensor report 00064 * -- Why does this get its own channel? I don't know!!! 00065 */ 00066 00067 #include "BNO080.h" 00068 #include "BNO080Constants.h" 00069 #include <cinttypes> 00070 #include <algorithm> 00071 00072 /// Set to 1 to enable debug printouts. Should be very useful if the chip is giving you trouble. 00073 /// When debugging, it is recommended to use the highest possible serial baudrate so as not to interrupt the timing of operations. 00074 #define BNO_DEBUG 0 00075 00076 BNO080Base::BNO080Base(Stream *debugPort, PinName user_INTPin, PinName user_RSTPin) : 00077 _debugPort(debugPort), 00078 _int(user_INTPin), 00079 _rst(user_RSTPin, 1), 00080 commandSequenceNumber(0), 00081 stability(UNKNOWN), 00082 stepDetected(false), 00083 stepCount(0), 00084 significantMotionDetected(false), 00085 shakeDetected(false), 00086 xAxisShake(false), 00087 yAxisShake(false), 00088 zAxisShake(false) 00089 { 00090 // zero sequence numbers 00091 memset(sequenceNumber, 0, sizeof(sequenceNumber)); 00092 } 00093 00094 bool BNO080Base::begin() 00095 { 00096 //Configure the BNO080 00097 00098 _rst = 0; // Reset BNO080 00099 ThisThread::sleep_for(1ms); // Min length not specified in datasheet? 00100 _rst = 1; // Bring out of reset 00101 00102 // wait for a falling edge (NOT just a low) on the INT pin to denote startup 00103 Timer timeoutTimer; 00104 timeoutTimer.start(); 00105 00106 bool highDetected = false; 00107 bool lowDetected = false; 00108 00109 while(true) 00110 { 00111 if(timeoutTimer.elapsed_time() > BNO080_RESET_TIMEOUT) 00112 { 00113 _debugPort->printf("Error: BNO080 reset timed out, chip not detected.\n"); 00114 return false; 00115 } 00116 00117 // simple edge detector 00118 if(!highDetected) 00119 { 00120 if(_int == 1) 00121 { 00122 highDetected = true; 00123 } 00124 } 00125 else if(!lowDetected) 00126 { 00127 if(_int == 0) 00128 { 00129 lowDetected = true; 00130 } 00131 } 00132 else 00133 { 00134 // high and low detected 00135 break; 00136 } 00137 } 00138 00139 #if BNO_DEBUG 00140 _debugPort->printf("BNO080 detected!\r\n"); 00141 #endif 00142 00143 // At system startup, the hub must send its full advertisement message (see SHTP 5.2 and 5.3) to the 00144 // host. It must not send any other data until this step is complete. 00145 // We don't actually care what's in it, we're just using it as a signal to indicate that the reset is complete. 00146 receivePacket(); 00147 00148 // now, after startup, the BNO will send an Unsolicited Initialize response (SH-2 section 6.4.5.2), and an Executable Reset command 00149 if(!waitForPacket(CHANNEL_EXECUTABLE, EXECUTABLE_REPORTID_RESET)) 00150 { 00151 _debugPort->printf("No initialization report from BNO080.\n"); 00152 return false; 00153 } 00154 else 00155 { 00156 #if BNO_DEBUG 00157 _debugPort->printf("BNO080 reports initialization successful!\n"); 00158 #endif 00159 } 00160 00161 // Finally, we want to interrogate the device about its model and version. 00162 clearSendBuffer(); 00163 txShtpData[0] = SHTP_REPORT_PRODUCT_ID_REQUEST; //Request the product ID and reset info 00164 txShtpData[1] = 0; //Reserved 00165 sendPacket(CHANNEL_CONTROL, 2); 00166 00167 waitForPacket(CHANNEL_CONTROL, SHTP_REPORT_PRODUCT_ID_RESPONSE, 5ms); 00168 00169 if (rxShtpData[0] == SHTP_REPORT_PRODUCT_ID_RESPONSE) 00170 { 00171 majorSoftwareVersion = rxShtpData[2]; 00172 minorSoftwareVersion = rxShtpData[3]; 00173 patchSoftwareVersion = (rxShtpData[13] << 8) | rxShtpData[12]; 00174 partNumber = (rxShtpData[7] << 24) | (rxShtpData[6] << 16) | (rxShtpData[5] << 8) | rxShtpData[4]; 00175 buildNumber = (rxShtpData[11] << 24) | (rxShtpData[10] << 16) | (rxShtpData[9] << 8) | rxShtpData[8]; 00176 00177 #if BNO_DEBUG 00178 _debugPort->printf("BNO080 reports as SW version %hhu.%hhu.%hu, build %lu, part no. %lu\n", 00179 majorSoftwareVersion, minorSoftwareVersion, patchSoftwareVersion, 00180 buildNumber, partNumber); 00181 #endif 00182 00183 } 00184 else 00185 { 00186 _debugPort->printf("Bad response from product ID command.\n"); 00187 return false; 00188 } 00189 00190 // successful init 00191 return true; 00192 00193 } 00194 00195 void BNO080Base::tare(bool zOnly) 00196 { 00197 clearSendBuffer(); 00198 00199 // from SH-2 section 6.4.4.1 00200 txShtpData[3] = 0; // perform tare now 00201 00202 if(zOnly) 00203 { 00204 txShtpData[4] = 0b100; // tare Z axis 00205 } 00206 else 00207 { 00208 txShtpData[4] = 0b111; // tare X, Y, and Z axes 00209 } 00210 00211 txShtpData[5] = 0; // reorient all motion outputs 00212 00213 sendCommand(COMMAND_TARE); 00214 } 00215 00216 bool BNO080Base::enableCalibration(bool calibrateAccel, bool calibrateGyro, bool calibrateMag) 00217 { 00218 // send the Configure ME Calibration command 00219 clearSendBuffer(); 00220 00221 txShtpData[3] = static_cast<uint8_t>(calibrateAccel ? 1 : 0); 00222 txShtpData[4] = static_cast<uint8_t>(calibrateGyro ? 1 : 0); 00223 txShtpData[5] = static_cast<uint8_t>(calibrateMag ? 1 : 0); 00224 00225 txShtpData[6] = 0; // Configure ME Calibration command 00226 00227 txShtpData[7] = 0; // planar accelerometer calibration always disabled 00228 00229 sendCommand(COMMAND_ME_CALIBRATE); 00230 00231 // now, wait for the response 00232 if(!waitForPacket(CHANNEL_CONTROL, SHTP_REPORT_COMMAND_RESPONSE)) 00233 { 00234 #if BNO_DEBUG 00235 _debugPort->printf("Timeout waiting for calibration response!\n"); 00236 #endif 00237 return false; 00238 } 00239 00240 if(rxShtpData[2] != COMMAND_ME_CALIBRATE) 00241 { 00242 #if BNO_DEBUG 00243 _debugPort->printf("Received wrong response to calibration command!\n"); 00244 #endif 00245 return false; 00246 } 00247 00248 if(rxShtpData[5] != 0) 00249 { 00250 #if BNO_DEBUG 00251 _debugPort->printf("IMU reports calibrate command failed!\n"); 00252 #endif 00253 return false; 00254 } 00255 00256 // acknowledge checks out! 00257 return true; 00258 } 00259 00260 bool BNO080Base::saveCalibration() 00261 { 00262 clearSendBuffer(); 00263 00264 // no arguments 00265 sendCommand(COMMAND_SAVE_DCD); 00266 00267 // now, wait for the response 00268 if(!waitForPacket(CHANNEL_CONTROL, SHTP_REPORT_COMMAND_RESPONSE)) 00269 { 00270 #if BNO_DEBUG 00271 _debugPort->printf("Timeout waiting for calibration response!\n"); 00272 #endif 00273 return false; 00274 } 00275 00276 if(rxShtpData[2] != COMMAND_SAVE_DCD) 00277 { 00278 #if BNO_DEBUG 00279 _debugPort->printf("Received wrong response to calibration command!\n"); 00280 #endif 00281 return false; 00282 } 00283 00284 if(rxShtpData[5] != 0) 00285 { 00286 #if BNO_DEBUG 00287 _debugPort->printf("IMU reports calibrate command failed!\n"); 00288 #endif 00289 return false; 00290 } 00291 00292 // acknowledge checks out! 00293 return true; 00294 } 00295 00296 void BNO080Base::setSensorOrientation(Quaternion orientation) 00297 { 00298 clearSendBuffer(); 00299 00300 // convert floats to Q 00301 int16_t Q_x = floatToQ(orientation.x(), ORIENTATION_QUAT_Q_POINT); 00302 int16_t Q_y = floatToQ(orientation.y(), ORIENTATION_QUAT_Q_POINT); 00303 int16_t Q_z = floatToQ(orientation.z(), ORIENTATION_QUAT_Q_POINT); 00304 int16_t Q_w = floatToQ(orientation.w(), ORIENTATION_QUAT_Q_POINT); 00305 00306 txShtpData[3] = 2; // set reorientation 00307 00308 txShtpData[4] = static_cast<uint8_t>(Q_x & 0xFF); //P1 - X component LSB 00309 txShtpData[5] = static_cast<uint8_t>(Q_x >> 8); //P2 - X component MSB 00310 00311 txShtpData[6] = static_cast<uint8_t>(Q_y & 0xFF); //P3 - Y component LSB 00312 txShtpData[7] = static_cast<uint8_t>(Q_y >> 8); //P4 - Y component MSB 00313 00314 txShtpData[8] = static_cast<uint8_t>(Q_z & 0xFF); //P5 - Z component LSB 00315 txShtpData[9] = static_cast<uint8_t>(Q_z >> 8); //P6 - Z component MSB 00316 00317 txShtpData[10] = static_cast<uint8_t>(Q_w & 0xFF); //P7 - W component LSB 00318 txShtpData[11] = static_cast<uint8_t>(Q_w >> 8); //P8 - W component MSB 00319 00320 //Using this shtpData packet, send a command 00321 sendCommand(COMMAND_TARE); // Send tare command 00322 00323 // NOTE: unlike literally every other command, a sensor orientation command is never acknowledged in any way. 00324 } 00325 00326 #define ORIENTATION_RECORD_LEN 4 00327 00328 bool BNO080Base::setPermanentOrientation(Quaternion orientation) 00329 { 00330 uint32_t orientationRecord[ORIENTATION_RECORD_LEN]; 00331 00332 // each word is one element of the quaternion 00333 orientationRecord[0] = static_cast<uint32_t>(floatToQ_dword(orientation.x(), FRS_ORIENTATION_Q_POINT)); 00334 orientationRecord[1] = static_cast<uint32_t>(floatToQ_dword(orientation.y(), FRS_ORIENTATION_Q_POINT)); 00335 orientationRecord[2] = static_cast<uint32_t>(floatToQ_dword(orientation.z(), FRS_ORIENTATION_Q_POINT)); 00336 orientationRecord[3] = static_cast<uint32_t>(floatToQ_dword(orientation.w(), FRS_ORIENTATION_Q_POINT)); 00337 00338 return writeFRSRecord(FRS_RECORDID_SYSTEM_ORIENTATION, orientationRecord, ORIENTATION_RECORD_LEN); 00339 } 00340 00341 bool BNO080Base::updateData() 00342 { 00343 if(_int.read() != 0) 00344 { 00345 // no waiting packets 00346 return false; 00347 } 00348 00349 while(_int.read() == 0) 00350 { 00351 if(!receivePacket()) 00352 { 00353 // comms error 00354 return false; 00355 } 00356 00357 processPacket(); 00358 00359 // Allow time for the IMU to ready another packet if it has one 00360 wait_us(150); // time between packets measured with a logic analyzer 00361 } 00362 00363 // packets were received, so data may have changed 00364 return true; 00365 } 00366 00367 uint8_t BNO080Base::getReportStatus(Report report) 00368 { 00369 uint8_t reportNum = static_cast<uint8_t>(report); 00370 if(reportNum > STATUS_ARRAY_LEN) 00371 { 00372 return 0; 00373 } 00374 00375 return reportStatus[reportNum]; 00376 } 00377 00378 const char* BNO080Base::getReportStatusString(Report report) 00379 { 00380 switch(getReportStatus(report)) 00381 { 00382 case 0: 00383 return "Unreliable"; 00384 case 1: 00385 return "Accuracy Low"; 00386 case 2: 00387 return "Accuracy Medium"; 00388 case 3: 00389 return "Accuracy High"; 00390 default: 00391 return "Error"; 00392 } 00393 } 00394 00395 bool BNO080Base::hasNewData(Report report) 00396 { 00397 uint8_t reportNum = static_cast<uint8_t>(report); 00398 if(reportNum > STATUS_ARRAY_LEN) 00399 { 00400 return false; 00401 } 00402 00403 bool newData = reportHasBeenUpdated[reportNum]; 00404 reportHasBeenUpdated[reportNum] = false; // clear flag 00405 return newData; 00406 } 00407 00408 //Sends the packet to enable the rotation vector 00409 void BNO080Base::enableReport(Report report, uint16_t timeBetweenReports) 00410 { 00411 #if BNO_DEBUG 00412 // check time is valid 00413 float periodSeconds = static_cast<float>(timeBetweenReports / 1000.0); 00414 00415 if(periodSeconds < getMinPeriod(report)) 00416 { 00417 _debugPort->printf("Error: attempt made to set report 0x%02hhx to period of %.06f s, which is smaller than its min period of %.06f s.\r\n", 00418 static_cast<uint8_t>(report), periodSeconds, getMinPeriod(report)); 00419 return; 00420 } 00421 00422 #endif 00423 setFeatureCommand(static_cast<uint8_t>(report), timeBetweenReports); 00424 00425 // note: we don't wait for ACKs on these packets because they can take quite a while, like half a second, to come in 00426 } 00427 00428 void BNO080Base::disableReport(Report report) 00429 { 00430 // set the report's polling period to zero to disable it 00431 setFeatureCommand(static_cast<uint8_t>(report), 0); 00432 } 00433 00434 uint32_t BNO080Base::getSerialNumber() 00435 { 00436 uint32_t serNoBuffer; 00437 00438 if(!readFRSRecord(FRS_RECORDID_SERIAL_NUMBER, &serNoBuffer, 1)) 00439 { 00440 return 0; 00441 } 00442 00443 return serNoBuffer; 00444 } 00445 00446 float BNO080Base::getRange(Report report) 00447 { 00448 loadReportMetadata(report); 00449 00450 return qToFloat_dword(metadataRecord[1], getQ1(report)); 00451 } 00452 00453 00454 float BNO080Base::getResolution(Report report) 00455 { 00456 loadReportMetadata(report); 00457 00458 return qToFloat_dword(metadataRecord[2], getQ1(report)); 00459 } 00460 00461 float BNO080Base::getPower(Report report) 00462 { 00463 loadReportMetadata(report); 00464 00465 uint16_t powerQ = static_cast<uint16_t>(metadataRecord[3] & 0xFFFF); 00466 00467 return qToFloat_dword(powerQ, POWER_Q_POINT); 00468 } 00469 00470 float BNO080Base::getMinPeriod(Report report) 00471 { 00472 loadReportMetadata(report); 00473 00474 return metadataRecord[4] / 1e6f; // convert from microseconds to seconds 00475 } 00476 00477 float BNO080Base::getMaxPeriod(Report report) 00478 { 00479 loadReportMetadata(report); 00480 00481 if(getMetaVersion() == 3) 00482 { 00483 // no max period entry in this record format 00484 return -1.0f; 00485 } 00486 00487 return metadataRecord[9] / 1e6f; // convert from microseconds to seconds 00488 } 00489 00490 void BNO080Base::printMetadataSummary(Report report) 00491 { 00492 #if BNO_DEBUG 00493 if(!loadReportMetadata(report)) 00494 { 00495 _debugPort->printf("Failed to load report metadata!\n"); 00496 } 00497 00498 _debugPort->printf("======= Metadata for report 0x%02hhx =======\n", static_cast<uint8_t>(report)); 00499 00500 _debugPort->printf("Range: +- %.04f units\n", getRange(report)); 00501 _debugPort->printf("Resolution: %.04f units\n", getResolution(report)); 00502 _debugPort->printf("Power Used: %.03f mA\n", getPower(report)); 00503 _debugPort->printf("Min Period: %.06f s\n", getMinPeriod(report)); 00504 _debugPort->printf("Max Period: %.06f s\n\n", getMaxPeriod(report)); 00505 00506 #endif 00507 } 00508 00509 int16_t BNO080Base::getQ1(Report report) 00510 { 00511 loadReportMetadata(report); 00512 00513 return static_cast<int16_t>(metadataRecord[7] & 0xFFFF); 00514 } 00515 00516 int16_t BNO080Base::getQ2(Report report) 00517 { 00518 loadReportMetadata(report); 00519 00520 return static_cast<int16_t>(metadataRecord[7] >> 16); 00521 } 00522 00523 int16_t BNO080Base::getQ3(Report report) 00524 { 00525 loadReportMetadata(report); 00526 00527 return static_cast<int16_t>(metadataRecord[8] >> 16); 00528 } 00529 00530 void BNO080Base::processPacket() 00531 { 00532 if(rxShtpHeader[2] == CHANNEL_CONTROL) 00533 { 00534 // currently no command reports are read 00535 } 00536 else if(rxShtpHeader[2] == CHANNEL_EXECUTABLE) 00537 { 00538 // currently no executable reports are read 00539 } 00540 else if(rxShtpHeader[2] == CHANNEL_COMMAND) 00541 { 00542 00543 } 00544 else if(rxShtpHeader[2] == CHANNEL_REPORTS || rxShtpHeader[2] == CHANNEL_WAKE_REPORTS) 00545 { 00546 if(rxShtpData[0] == SHTP_REPORT_BASE_TIMESTAMP) 00547 { 00548 // sensor data packet 00549 parseSensorDataPacket(); 00550 } 00551 } 00552 } 00553 00554 // sizes of various sensor data packet elements 00555 #define SIZEOF_BASE_TIMESTAMP 5 00556 #define SIZEOF_TIMESTAMP_REBASE 5 00557 #define SIZEOF_ACCELEROMETER 10 00558 #define SIZEOF_LINEAR_ACCELERATION 10 00559 #define SIZEOF_GYROSCOPE_CALIBRATED 10 00560 #define SIZEOF_MAGNETIC_FIELD_CALIBRATED 10 00561 #define SIZEOF_MAGNETIC_FIELD_UNCALIBRATED 16 00562 #define SIZEOF_ROTATION_VECTOR 14 00563 #define SIZEOF_GAME_ROTATION_VECTOR 12 00564 #define SIZEOF_GEOMAGNETIC_ROTATION_VECTOR 14 00565 #define SIZEOF_TAP_DETECTOR 5 00566 #define SIZEOF_STABILITY_REPORT 6 00567 #define SIZEOF_STEP_DETECTOR 8 00568 #define SIZEOF_STEP_COUNTER 12 00569 #define SIZEOF_SIGNIFICANT_MOTION 6 00570 #define SIZEOF_SHAKE_DETECTOR 6 00571 00572 void BNO080Base::parseSensorDataPacket() 00573 { 00574 size_t currReportOffset = 0; 00575 00576 // every sensor data report first contains a timestamp offset to show how long it has been between when 00577 // the host interrupt was sent and when the packet was transmitted. 00578 // We don't use interrupts and don't care about times, so we can throw this out. 00579 currReportOffset += SIZEOF_BASE_TIMESTAMP; 00580 00581 while(currReportOffset < rxPacketLength) 00582 { 00583 // lots of sensor reports use 3 16-bit numbers stored in bytes 4 through 9 00584 // we can save some time by parsing those out here. 00585 uint16_t data1 = (uint16_t)rxShtpData[currReportOffset + 5] << 8 | rxShtpData[currReportOffset + 4]; 00586 uint16_t data2 = (uint16_t)rxShtpData[currReportOffset + 7] << 8 | rxShtpData[currReportOffset + 6]; 00587 uint16_t data3 = (uint16_t)rxShtpData[currReportOffset + 9] << 8 | rxShtpData[currReportOffset + 8]; 00588 00589 uint8_t reportNum = rxShtpData[currReportOffset]; 00590 00591 if(reportNum != SENSOR_REPORTID_TIMESTAMP_REBASE) 00592 { 00593 // set status from byte 2 00594 reportStatus[reportNum] = static_cast<uint8_t>(rxShtpData[currReportOffset + 2] & 0b11); 00595 00596 // set updated flag 00597 reportHasBeenUpdated[reportNum] = true; 00598 } 00599 00600 switch(rxShtpData[currReportOffset]) 00601 { 00602 case SENSOR_REPORTID_TIMESTAMP_REBASE: 00603 currReportOffset += SIZEOF_TIMESTAMP_REBASE; 00604 break; 00605 00606 case SENSOR_REPORTID_ACCELEROMETER: 00607 00608 totalAcceleration = TVector3 ( 00609 qToFloat(data1, ACCELEROMETER_Q_POINT), 00610 qToFloat(data2, ACCELEROMETER_Q_POINT), 00611 qToFloat(data3, ACCELEROMETER_Q_POINT)); 00612 00613 currReportOffset += SIZEOF_ACCELEROMETER; 00614 break; 00615 00616 case SENSOR_REPORTID_LINEAR_ACCELERATION: 00617 00618 linearAcceleration = TVector3 ( 00619 qToFloat(data1, ACCELEROMETER_Q_POINT), 00620 qToFloat(data2, ACCELEROMETER_Q_POINT), 00621 qToFloat(data3, ACCELEROMETER_Q_POINT)); 00622 00623 currReportOffset += SIZEOF_LINEAR_ACCELERATION; 00624 break; 00625 00626 case SENSOR_REPORTID_GRAVITY: 00627 00628 gravityAcceleration = TVector3 ( 00629 qToFloat(data1, ACCELEROMETER_Q_POINT), 00630 qToFloat(data2, ACCELEROMETER_Q_POINT), 00631 qToFloat(data3, ACCELEROMETER_Q_POINT)); 00632 00633 currReportOffset += SIZEOF_LINEAR_ACCELERATION; 00634 break; 00635 00636 case SENSOR_REPORTID_GYROSCOPE_CALIBRATED: 00637 00638 gyroRotation = TVector3 ( 00639 qToFloat(data1, GYRO_Q_POINT), 00640 qToFloat(data2, GYRO_Q_POINT), 00641 qToFloat(data3, GYRO_Q_POINT)); 00642 00643 currReportOffset += SIZEOF_GYROSCOPE_CALIBRATED; 00644 break; 00645 00646 case SENSOR_REPORTID_MAGNETIC_FIELD_CALIBRATED: 00647 00648 magField = TVector3 ( 00649 qToFloat(data1, MAGNETOMETER_Q_POINT), 00650 qToFloat(data2, MAGNETOMETER_Q_POINT), 00651 qToFloat(data3, MAGNETOMETER_Q_POINT)); 00652 00653 currReportOffset += SIZEOF_MAGNETIC_FIELD_CALIBRATED; 00654 break; 00655 00656 case SENSOR_REPORTID_MAGNETIC_FIELD_UNCALIBRATED: 00657 { 00658 magFieldUncalibrated = TVector3 ( 00659 qToFloat(data1, MAGNETOMETER_Q_POINT), 00660 qToFloat(data2, MAGNETOMETER_Q_POINT), 00661 qToFloat(data3, MAGNETOMETER_Q_POINT)); 00662 00663 uint16_t ironOffsetXQ = (uint16_t) rxShtpData[currReportOffset + 11] << 8 | rxShtpData[currReportOffset + 10]; 00664 uint16_t ironOffsetYQ = (uint16_t) rxShtpData[currReportOffset + 13] << 8 | rxShtpData[currReportOffset + 12]; 00665 uint16_t ironOffsetZQ = (uint16_t) rxShtpData[currReportOffset + 15] << 8 | rxShtpData[currReportOffset + 14]; 00666 00667 hardIronOffset = TVector3 ( 00668 qToFloat(ironOffsetXQ, MAGNETOMETER_Q_POINT), 00669 qToFloat(ironOffsetYQ, MAGNETOMETER_Q_POINT), 00670 qToFloat(ironOffsetZQ, MAGNETOMETER_Q_POINT)); 00671 00672 currReportOffset += SIZEOF_MAGNETIC_FIELD_UNCALIBRATED; 00673 } 00674 break; 00675 00676 case SENSOR_REPORTID_ROTATION_VECTOR: 00677 { 00678 uint16_t realPartQ = (uint16_t) rxShtpData[currReportOffset + 11] << 8 | rxShtpData[currReportOffset + 10]; 00679 uint16_t accuracyQ = (uint16_t) rxShtpData[currReportOffset + 13] << 8 | rxShtpData[currReportOffset + 12]; 00680 00681 rotationVector = TVector4( 00682 qToFloat(data1, ROTATION_Q_POINT), 00683 qToFloat(data2, ROTATION_Q_POINT), 00684 qToFloat(data3, ROTATION_Q_POINT), 00685 qToFloat(realPartQ, ROTATION_Q_POINT)); 00686 00687 rotationAccuracy = qToFloat(accuracyQ, ROTATION_ACCURACY_Q_POINT); 00688 00689 currReportOffset += SIZEOF_ROTATION_VECTOR; 00690 } 00691 break; 00692 00693 case SENSOR_REPORTID_GAME_ROTATION_VECTOR: 00694 { 00695 uint16_t realPartQ = (uint16_t) rxShtpData[currReportOffset + 11] << 8 | rxShtpData[currReportOffset + 10]; 00696 00697 gameRotationVector = TVector4( 00698 qToFloat(data1, ROTATION_Q_POINT), 00699 qToFloat(data2, ROTATION_Q_POINT), 00700 qToFloat(data3, ROTATION_Q_POINT), 00701 qToFloat(realPartQ, ROTATION_Q_POINT)); 00702 00703 currReportOffset += SIZEOF_GAME_ROTATION_VECTOR; 00704 } 00705 break; 00706 00707 case SENSOR_REPORTID_GEOMAGNETIC_ROTATION_VECTOR: 00708 { 00709 uint16_t realPartQ = (uint16_t) rxShtpData[currReportOffset + 11] << 8 | rxShtpData[currReportOffset + 10]; 00710 uint16_t accuracyQ = (uint16_t) rxShtpData[currReportOffset + 13] << 8 | rxShtpData[currReportOffset + 12]; 00711 00712 geomagneticRotationVector = TVector4( 00713 qToFloat(data1, ROTATION_Q_POINT), 00714 qToFloat(data2, ROTATION_Q_POINT), 00715 qToFloat(data3, ROTATION_Q_POINT), 00716 qToFloat(realPartQ, ROTATION_Q_POINT)); 00717 00718 geomagneticRotationAccuracy = qToFloat(accuracyQ, ROTATION_ACCURACY_Q_POINT); 00719 00720 currReportOffset += SIZEOF_GEOMAGNETIC_ROTATION_VECTOR; 00721 } 00722 break; 00723 00724 case SENSOR_REPORTID_TAP_DETECTOR: 00725 00726 // since we got the report, a tap was detected 00727 tapDetected = true; 00728 00729 doubleTap = (rxShtpData[currReportOffset + 4] & (1 << 6)) != 0; 00730 00731 currReportOffset += SIZEOF_TAP_DETECTOR; 00732 break; 00733 00734 case SENSOR_REPORTID_STABILITY_CLASSIFIER: 00735 { 00736 uint8_t classificationNumber = rxShtpData[currReportOffset + 4]; 00737 00738 if(classificationNumber > 4) 00739 { 00740 classificationNumber = 0; 00741 } 00742 00743 stability = static_cast<Stability>(classificationNumber); 00744 00745 currReportOffset += SIZEOF_STABILITY_REPORT; 00746 } 00747 break; 00748 00749 case SENSOR_REPORTID_STEP_DETECTOR: 00750 00751 // the fact that we got the report means that a step was detected 00752 stepDetected = true; 00753 00754 currReportOffset += SIZEOF_STEP_DETECTOR; 00755 00756 break; 00757 00758 case SENSOR_REPORTID_STEP_COUNTER: 00759 00760 stepCount = rxShtpData[currReportOffset + 9] << 8 | rxShtpData[currReportOffset + 8]; 00761 00762 currReportOffset += SIZEOF_STEP_COUNTER; 00763 00764 break; 00765 00766 case SENSOR_REPORTID_SIGNIFICANT_MOTION: 00767 00768 // the fact that we got the report means that significant motion was detected 00769 significantMotionDetected = true; 00770 00771 currReportOffset += SIZEOF_SIGNIFICANT_MOTION; 00772 00773 break; 00774 00775 case SENSOR_REPORTID_SHAKE_DETECTOR: 00776 00777 shakeDetected = true; 00778 00779 xAxisShake = (rxShtpData[currReportOffset + 4] & 1) != 0; 00780 yAxisShake = (rxShtpData[currReportOffset + 4] & (1 << 1)) != 0; 00781 zAxisShake = (rxShtpData[currReportOffset + 4] & (1 << 2)) != 0; 00782 00783 currReportOffset += SIZEOF_SHAKE_DETECTOR; 00784 00785 break; 00786 00787 default: 00788 _debugPort->printf("Error: unrecognized report ID in sensor report: %hhx. Byte %u, length %hu\n", rxShtpData[currReportOffset], currReportOffset, rxPacketLength); 00789 return; 00790 } 00791 00792 if(currReportOffset >= SHTP_RX_PACKET_SIZE) 00793 { 00794 _debugPort->printf("Error: sensor report longer than packet buffer! Some data was not read! Increase buffer size or decrease number of reports!\r\n"); 00795 return; 00796 } 00797 } 00798 00799 } 00800 00801 bool BNO080Base::waitForPacket(int channel, uint8_t reportID, std::chrono::milliseconds timeout) 00802 { 00803 Timer timeoutTimer; 00804 timeoutTimer.start(); 00805 00806 while(timeoutTimer.elapsed_time() <= timeout) 00807 { 00808 if(_int.read() == 0) 00809 { 00810 if(!receivePacket(timeout)) 00811 { 00812 return false; 00813 } 00814 00815 if(channel == rxShtpHeader[2] && reportID == rxShtpData[0]) 00816 { 00817 // found correct packet! 00818 return true; 00819 } 00820 else 00821 { 00822 // other data packet, send to proper channels 00823 processPacket(); 00824 } 00825 } 00826 } 00827 00828 _debugPort->printf("Packet wait timeout.\n"); 00829 return false; 00830 } 00831 00832 //Given a register value and a Q point, convert to float 00833 //See https://en.wikipedia.org/wiki/Q_(number_format) 00834 float BNO080Base::qToFloat(int16_t fixedPointValue, uint8_t qPoint) 00835 { 00836 float qFloat = fixedPointValue; 00837 qFloat *= pow(2.0f, qPoint * -1); 00838 return (qFloat); 00839 } 00840 00841 float BNO080Base::qToFloat_dword(uint32_t fixedPointValue, int16_t qPoint) 00842 { 00843 float qFloat = fixedPointValue; 00844 qFloat *= pow(2.0f, qPoint * -1); 00845 return (qFloat); 00846 } 00847 00848 //Given a floating point value and a Q point, convert to Q 00849 //See https://en.wikipedia.org/wiki/Q_(number_format) 00850 int16_t BNO080Base::floatToQ(float qFloat, uint8_t qPoint) 00851 { 00852 int16_t qVal = static_cast<int16_t>(qFloat * pow(2.0f, qPoint)); 00853 return qVal; 00854 } 00855 00856 int32_t BNO080Base::floatToQ_dword(float qFloat, uint16_t qPoint) 00857 { 00858 int32_t qVal = static_cast<int32_t>(qFloat * pow(2.0f, qPoint)); 00859 return qVal; 00860 } 00861 //Tell the sensor to do a command 00862 //See 6.3.8 page 41, Command request 00863 //The caller is expected to set P0 through P8 prior to calling 00864 void BNO080Base::sendCommand(uint8_t command) 00865 { 00866 txShtpData[0] = SHTP_REPORT_COMMAND_REQUEST; //Command Request 00867 txShtpData[1] = commandSequenceNumber++; //Increments automatically each function call 00868 txShtpData[2] = command; //Command 00869 00870 //Caller must set these 00871 /*shtpData[3] = 0; //P0 00872 shtpData[4] = 0; //P1 00873 shtpData[5] = 0; //P2 00874 shtpData[6] = 0; 00875 shtpData[7] = 0; 00876 shtpData[8] = 0; 00877 shtpData[9] = 0; 00878 shtpData[10] = 0; 00879 shtpData[11] = 0;*/ 00880 00881 //Transmit packet on channel 2, 12 bytes 00882 sendPacket(CHANNEL_CONTROL, 12); 00883 } 00884 00885 //Given a sensor's report ID, this tells the BNO080 to begin reporting the values 00886 //Also sets the specific config word. Useful for personal activity classifier 00887 void BNO080Base::setFeatureCommand(uint8_t reportID, uint16_t timeBetweenReports, uint32_t specificConfig) 00888 { 00889 uint32_t microsBetweenReports = static_cast<uint32_t>(timeBetweenReports * 1000); 00890 00891 const uint32_t batchMicros = 0; 00892 00893 txShtpData[0] = SHTP_REPORT_SET_FEATURE_COMMAND; //Set feature command. Reference page 55 00894 txShtpData[1] = reportID; //Feature Report ID. 0x01 = Accelerometer, 0x05 = Rotation vector 00895 txShtpData[2] = 0; //Feature flags 00896 txShtpData[3] = 0; //Change sensitivity (LSB) 00897 txShtpData[4] = 0; //Change sensitivity (MSB) 00898 txShtpData[5] = (microsBetweenReports >> 0) & 0xFF; //Report interval (LSB) in microseconds. 0x7A120 = 500ms 00899 txShtpData[6] = (microsBetweenReports >> 8) & 0xFF; //Report interval 00900 txShtpData[7] = (microsBetweenReports >> 16) & 0xFF; //Report interval 00901 txShtpData[8] = (microsBetweenReports >> 24) & 0xFF; //Report interval (MSB) 00902 txShtpData[9] = (batchMicros >> 0) & 0xFF; //Batch Interval (LSB) 00903 txShtpData[10] = (batchMicros >> 8) & 0xFF; //Batch Interval 00904 txShtpData[11] = (batchMicros >> 16) & 0xFF;//Batch Interval 00905 txShtpData[12] = (batchMicros >> 24) & 0xFF;//Batch Interval (MSB) 00906 txShtpData[13] = (specificConfig >> 0) & 0xFF; //Sensor-specific config (LSB) 00907 txShtpData[14] = (specificConfig >> 8) & 0xFF; //Sensor-specific config 00908 txShtpData[15] = (specificConfig >> 16) & 0xFF; //Sensor-specific config 00909 txShtpData[16] = (specificConfig >> 24) & 0xFF; //Sensor-specific config (MSB) 00910 00911 //Transmit packet on channel 2, 17 bytes 00912 sendPacket(CHANNEL_CONTROL, 17); 00913 } 00914 00915 bool BNO080Base::readFRSRecord(uint16_t recordID, uint32_t* readBuffer, uint16_t readLength) 00916 { 00917 // send initial read request 00918 clearSendBuffer(); 00919 00920 txShtpData[0] = SHTP_REPORT_FRS_READ_REQUEST; 00921 // read offset of 0 -> start at the start of the record 00922 txShtpData[2] = 0; 00923 txShtpData[3] = 0; 00924 // record ID 00925 txShtpData[4] = static_cast<uint8_t>(recordID & 0xFF); 00926 txShtpData[5] = static_cast<uint8_t>(recordID >> 8); 00927 // block size 00928 txShtpData[6] = static_cast<uint8_t>(readLength & 0xFF); 00929 txShtpData[7] = static_cast<uint8_t>(readLength >> 8); 00930 00931 sendPacket(CHANNEL_CONTROL, 8); 00932 00933 // now, read back the responses 00934 size_t readOffset = 0; 00935 while(readOffset < readLength) 00936 { 00937 // it seems like it can take quite a long time for FRS data to be read, so we have to increase the timeout 00938 if(!waitForPacket(CHANNEL_CONTROL, SHTP_REPORT_FRS_READ_RESPONSE, 300ms)) 00939 { 00940 #if BNO_DEBUG 00941 _debugPort->printf("Error: did not receive FRS read response after sending read request!\n"); 00942 #endif 00943 return false; 00944 } 00945 00946 uint8_t status = static_cast<uint8_t>(rxShtpData[1] & 0b1111); 00947 uint8_t dataLength = rxShtpData[1] >> 4; 00948 00949 // check status 00950 if(status == 1) 00951 { 00952 #if BNO_DEBUG 00953 _debugPort->printf("Error: FRS reports invalid record ID!\n"); 00954 #endif 00955 return false; 00956 } 00957 else if(status == 2) 00958 { 00959 #if BNO_DEBUG 00960 _debugPort->printf("Error: FRS is busy!\n"); 00961 #endif 00962 return false; 00963 } 00964 else if(status == 4) 00965 { 00966 #if BNO_DEBUG 00967 _debugPort->printf("Error: FRS reports offset is out of range!\n"); 00968 #endif 00969 return false; 00970 } 00971 else if(status == 5) 00972 { 00973 #if BNO_DEBUG 00974 _debugPort->printf("Error: FRS reports record %hx is empty!\n", recordID); 00975 #endif 00976 return false; 00977 } 00978 else if(status == 8) 00979 { 00980 #if BNO_DEBUG 00981 _debugPort->printf("Error: FRS reports flash memory device unavailable!\n"); 00982 #endif 00983 return false; 00984 } 00985 00986 // check data length 00987 if(dataLength == 0) 00988 { 00989 #if BNO_DEBUG 00990 _debugPort->printf("Error: Received FRS packet with 0 data length!\n"); 00991 #endif 00992 return false; 00993 } 00994 else if(dataLength == 1) 00995 { 00996 if(readOffset + 1 != readLength) 00997 { 00998 #if BNO_DEBUG 00999 _debugPort->printf("Error: Received 1 length packet but more than 1 byte remains to be be read!\n"); 01000 #endif 01001 return false; 01002 } 01003 } 01004 01005 // now, _finally_, read the dang words 01006 readBuffer[readOffset] = (rxShtpData[7] << 24) | (rxShtpData[6] << 16) | (rxShtpData[5] << 8) | (rxShtpData[4]); 01007 01008 // check if we only wanted the first word 01009 ++readOffset; 01010 if(readOffset == readLength) 01011 { 01012 break; 01013 } 01014 01015 readBuffer[readOffset] = (rxShtpData[11] << 24) | (rxShtpData[10] << 16) | (rxShtpData[9] << 8) | (rxShtpData[8]); 01016 readOffset++; 01017 } 01018 01019 // read successful 01020 return true; 01021 01022 } 01023 01024 bool BNO080Base::writeFRSRecord(uint16_t recordID, uint32_t* buffer, uint16_t length) 01025 { 01026 // send initial write request, which tells the chip where we're writing 01027 clearSendBuffer(); 01028 01029 txShtpData[0] = SHTP_REPORT_FRS_WRITE_REQUEST; 01030 // length to write (must be <= record length) 01031 txShtpData[2] = static_cast<uint8_t>(length & 0xFF); 01032 txShtpData[3] = static_cast<uint8_t>(length >> 8); 01033 // record ID 01034 txShtpData[4] = static_cast<uint8_t>(recordID & 0xFF); 01035 txShtpData[5] = static_cast<uint8_t>(recordID >> 8); 01036 01037 sendPacket(CHANNEL_CONTROL, 6); 01038 01039 // wait for FRS to become ready 01040 if(!waitForPacket(CHANNEL_CONTROL, SHTP_REPORT_FRS_WRITE_RESPONSE, 300ms)) 01041 { 01042 #if BNO_DEBUG 01043 _debugPort->printf("Error: did not receive FRS write ready response after sending write request!\r\n"); 01044 #endif 01045 return false; 01046 } 01047 01048 if(rxShtpData[1] != 4) 01049 { 01050 #if BNO_DEBUG 01051 _debugPort->printf("Error: FRS reports error initiating write operation: %hhu!\r\n", rxShtpData[1]); 01052 #endif 01053 return false; 01054 } 01055 01056 // now, send the actual data 01057 for(uint16_t wordIndex = 0; wordIndex < length; wordIndex += 2) 01058 { 01059 // send packet containing 2 words 01060 clearSendBuffer(); 01061 txShtpData[0] = SHTP_REPORT_FRS_WRITE_DATA; 01062 01063 // offset to write at 01064 txShtpData[2] = static_cast<uint8_t>(wordIndex & 0xFF); 01065 txShtpData[3] = static_cast<uint8_t>(wordIndex >> 8); 01066 01067 // data 0 01068 *reinterpret_cast<uint32_t*>(txShtpData + 4) = buffer[wordIndex]; 01069 01070 // data 1, if it exists 01071 if(wordIndex != length - 1) 01072 { 01073 *reinterpret_cast<uint32_t*>(txShtpData + 8) = buffer[wordIndex + 1]; 01074 } 01075 01076 sendPacket(CHANNEL_CONTROL, 12); 01077 01078 // wait for acknowledge 01079 if(!waitForPacket(CHANNEL_CONTROL, SHTP_REPORT_FRS_WRITE_RESPONSE, 300ms)) 01080 { 01081 #if BNO_DEBUG 01082 _debugPort->printf("Error: did not receive FRS write response after sending write data!\r\n"); 01083 #endif 01084 return false; 01085 } 01086 01087 uint8_t status = rxShtpData[1]; 01088 01089 switch(status) 01090 { 01091 case 0: 01092 if(length - wordIndex >= 2) 01093 { 01094 // status OK, write still in progress 01095 } 01096 else 01097 { 01098 #if BNO_DEBUG 01099 _debugPort->printf("Error: FRS reports write in progress when it should be complete!\r\n"); 01100 #endif 01101 return false; 01102 } 01103 break; 01104 case 3: 01105 case 8: 01106 if(length - wordIndex <= 2) 01107 { 01108 // status OK, write complete 01109 } 01110 else 01111 { 01112 #if BNO_DEBUG 01113 _debugPort->printf("Error: FRS reports write complete when it should be still going!\n"); 01114 #endif 01115 return false; 01116 } 01117 break; 01118 case 1: 01119 #if BNO_DEBUG 01120 _debugPort->printf("Error: FRS reports invalid record ID!\n"); 01121 #endif 01122 return false; 01123 case 2: 01124 #if BNO_DEBUG 01125 _debugPort->printf("Error: FRS is busy!\n"); 01126 #endif 01127 return false; 01128 case 5: 01129 #if BNO_DEBUG 01130 _debugPort->printf("Error: FRS reports write failed!\n"); 01131 #endif 01132 return false; 01133 case 6: 01134 #if BNO_DEBUG 01135 _debugPort->printf("Error: FRS reports data received while not in write mode!\n"); 01136 #endif 01137 return false; 01138 case 7: 01139 #if BNO_DEBUG 01140 _debugPort->printf("Error: FRS reports invalid length!\n"); 01141 #endif 01142 return false; 01143 case 9: 01144 #if BNO_DEBUG 01145 _debugPort->printf("Error: FRS reports invalid data for this record!\n"); 01146 #endif 01147 return false; 01148 01149 case 10: 01150 #if BNO_DEBUG 01151 _debugPort->printf("Error: FRS reports flash device unavailable!\n"); 01152 #endif 01153 return false; 01154 01155 case 11: 01156 #if BNO_DEBUG 01157 _debugPort->printf("Error: FRS reports record is read-only!\n"); 01158 #endif 01159 return false; 01160 default: 01161 #if BNO_DEBUG 01162 _debugPort->printf("Error: FRS reports unknown result code %hhu!\n", status); 01163 #endif 01164 break; 01165 01166 } 01167 } 01168 01169 // write complete 01170 return true; 01171 } 01172 01173 //Pretty prints the contents of a packet in the given buffer 01174 void BNO080Base::printPacket(uint8_t * buffer) 01175 { 01176 #if BNO_DEBUG 01177 //Print the four byte header 01178 _debugPort->printf("Header:"); 01179 for (uint8_t x = 0 ; x < 4 ; x++) 01180 { 01181 _debugPort->printf(" "); 01182 _debugPort->printf("%02hhx", buffer[x]); 01183 } 01184 01185 //Calculate the number of data bytes in this packet 01186 uint16_t totalLength = (static_cast<uint16_t>(buffer[1]) << 8 | buffer[0]); 01187 totalLength &= ~(1 << 15); 01188 uint16_t packetLength = totalLength - 4; //Remove the header bytes from the data count 01189 01190 uint16_t printLength = std::min(packetLength, static_cast<uint16_t>(40)); //Artificial limit. We don't want the phone book. 01191 01192 _debugPort->printf(" Body:"); 01193 for (uint16_t x = 0 ; x < printLength ; x++) 01194 { 01195 _debugPort->printf(" "); 01196 _debugPort->printf("%02hhx", buffer[x + SHTP_HEADER_SIZE]); 01197 } 01198 01199 _debugPort->printf(", Length:"); 01200 _debugPort->printf("%d", packetLength + SHTP_HEADER_SIZE); 01201 _debugPort->printf(", SeqNum: %hhu", buffer[3]); 01202 01203 _debugPort->printf(", Channel:"); 01204 if (buffer[2] == 0) _debugPort->printf("Command"); 01205 else if (buffer[2] == 1) _debugPort->printf("Executable"); 01206 else if (buffer[2] == 2) _debugPort->printf("Control"); 01207 else if (buffer[2] == 3) _debugPort->printf("Sensor-report"); 01208 else if (buffer[2] == 4) _debugPort->printf("Wake-report"); 01209 else if (buffer[2] == 5) _debugPort->printf("Gyro-vector"); 01210 else _debugPort->printf("%hhu", buffer[2]); 01211 01212 _debugPort->printf("\n"); 01213 #endif 01214 } 01215 01216 01217 void BNO080Base::clearSendBuffer() 01218 { 01219 memset(txPacketBuffer, 0, SHTP_HEADER_SIZE + SHTP_MAX_TX_PACKET_SIZE); 01220 } 01221 01222 bool BNO080Base::loadReportMetadata(BNO080Base::Report report) 01223 { 01224 uint16_t reportMetaRecord = 0; 01225 01226 // first, convert the report into the correct FRS record ID for that report's metadata 01227 // data from SH-2 section 5.1 01228 switch(report) 01229 { 01230 case TOTAL_ACCELERATION: 01231 reportMetaRecord = 0xE301; 01232 break; 01233 case LINEAR_ACCELERATION: 01234 reportMetaRecord = 0xE303; 01235 break; 01236 case GRAVITY_ACCELERATION: 01237 reportMetaRecord = 0xE304; 01238 break; 01239 case GYROSCOPE: 01240 reportMetaRecord = 0xE306; 01241 break; 01242 case MAG_FIELD: 01243 reportMetaRecord = 0xE309; 01244 break; 01245 case MAG_FIELD_UNCALIBRATED: 01246 reportMetaRecord = 0xE30A; 01247 break; 01248 case ROTATION: 01249 reportMetaRecord = 0xE30B; 01250 break; 01251 case GEOMAGNETIC_ROTATION: 01252 reportMetaRecord = 0xE30D; 01253 break; 01254 case GAME_ROTATION: 01255 reportMetaRecord = 0xE30C; 01256 break; 01257 case TAP_DETECTOR: 01258 reportMetaRecord = 0xE313; 01259 break; 01260 case STABILITY_CLASSIFIER: 01261 reportMetaRecord = 0xE317; 01262 break; 01263 case STEP_DETECTOR: 01264 reportMetaRecord = 0xE314; 01265 break; 01266 case STEP_COUNTER: 01267 reportMetaRecord = 0xE315; 01268 break; 01269 case SIGNIFICANT_MOTION: 01270 reportMetaRecord = 0xE316; 01271 break; 01272 case SHAKE_DETECTOR: 01273 reportMetaRecord = 0xE318; 01274 break; 01275 } 01276 01277 // if we already have that data stored, everything's OK 01278 if(bufferMetadataRecord == reportMetaRecord) 01279 { 01280 return true; 01281 } 01282 01283 // now, load the metadata into the buffer 01284 if(!readFRSRecord(reportMetaRecord, metadataRecord, METADATA_BUFFER_LEN)) 01285 { 01286 // clear this so future calls won't try to use the cached version 01287 bufferMetadataRecord = 0; 01288 01289 return false; 01290 } 01291 01292 bufferMetadataRecord = reportMetaRecord; 01293 01294 return true; 01295 } 01296 01297 BNO080I2C::BNO080I2C(Stream *debugPort, PinName user_SDApin, PinName user_SCLpin, PinName user_INTPin, PinName user_RSTPin, 01298 uint8_t i2cAddress, int i2cPortSpeed) : 01299 BNO080Base(debugPort, user_INTPin, user_RSTPin), 01300 _i2cPort(user_SDApin, user_SCLpin), 01301 _i2cAddress(i2cAddress) 01302 { 01303 01304 //Get user settings 01305 _i2cPortSpeed = i2cPortSpeed; 01306 if(_i2cPortSpeed > 4000000) 01307 { 01308 _i2cPortSpeed = 4000000; //BNO080 max is 400Khz 01309 } 01310 _i2cPort.frequency(_i2cPortSpeed); 01311 } 01312 01313 01314 //Given the data packet, send the header then the data 01315 //Returns false if sensor does not ACK 01316 bool BNO080I2C::sendPacket(uint8_t channelNumber, uint8_t dataLength) 01317 { 01318 01319 uint16_t totalLength = dataLength + 4; //Add four bytes for the header 01320 01321 txShtpHeader[0] = totalLength & 0xFF; 01322 txShtpHeader[1] = totalLength >> 8; 01323 txShtpHeader[2] = channelNumber; 01324 txShtpHeader[3] = sequenceNumber[channelNumber]; 01325 01326 #if BNO_DEBUG 01327 _debugPort->printf("Transmitting packet: ----------------\n"); 01328 printPacket(txPacketBuffer); 01329 #endif 01330 01331 // send packet to IMU 01332 int writeResult = _i2cPort.write(_i2cAddress << 1, reinterpret_cast<char*>(txPacketBuffer), totalLength); 01333 01334 if(writeResult != 0) 01335 { 01336 _debugPort->printf("BNO I2C write failed!\n"); 01337 return false; 01338 } 01339 return true; 01340 } 01341 01342 //Check to see if there is any new data available 01343 //Read the contents of the incoming packet into the shtpData array 01344 bool BNO080I2C::receivePacket(std::chrono::milliseconds timeout) 01345 { 01346 Timer waitStartTime; 01347 waitStartTime.start(); 01348 01349 while(_int.read() != 0) 01350 { 01351 if(waitStartTime.elapsed_time() > timeout) 01352 { 01353 _debugPort->printf("BNO I2C header wait timeout\n"); 01354 return false; 01355 } 01356 01357 } 01358 01359 // start the transaction and contact the IMU 01360 _i2cPort.start(); 01361 01362 // to indicate an i2c read, shift the 7 bit address up 1 bit and set bit 0 to a 1 01363 int writeResult = _i2cPort.write((_i2cAddress << 1) | 0x1); 01364 01365 if(writeResult != 1) 01366 { 01367 _debugPort->printf("BNO I2C read failed!\n"); 01368 return false; 01369 } 01370 01371 //Get the first four bytes, aka the packet header 01372 uint8_t packetLSB = static_cast<uint8_t>(_i2cPort.read(true)); 01373 uint8_t packetMSB = static_cast<uint8_t>(_i2cPort.read(true)); 01374 uint8_t channelNumber = static_cast<uint8_t>(_i2cPort.read(true)); 01375 uint8_t sequenceNum = static_cast<uint8_t>(_i2cPort.read(true)); //Not sure if we need to store this or not 01376 01377 //Store the header info 01378 rxShtpHeader[0] = packetLSB; 01379 rxShtpHeader[1] = packetMSB; 01380 rxShtpHeader[2] = channelNumber; 01381 rxShtpHeader[3] = sequenceNum; 01382 01383 if(rxShtpHeader[0] == 0xFF && rxShtpHeader[1] == 0xFF) 01384 { 01385 // invalid according to BNO080 datasheet section 1.4.1 01386 01387 #if BNO_DEBUG 01388 _debugPort->printf("Recieved 0xFFFF packet length, protocol error!\n"); 01389 #endif 01390 return false; 01391 } 01392 01393 //Calculate the number of data bytes in this packet 01394 rxPacketLength = (static_cast<uint16_t>(packetMSB) << 8 | packetLSB); 01395 01396 // Clear the MSbit. 01397 // This bit indicates if this package is a continuation of the last. TBH, I don't really know what this means (it's not really explained in the datasheet) 01398 // but we don't actually care about any of the advertisement packets 01399 // that use this, so we can just cut off the rest of the packet by releasing chip select. 01400 rxPacketLength &= ~(1 << 15); 01401 01402 if (rxPacketLength == 0) 01403 { 01404 // Packet is empty 01405 return (false); //All done 01406 } 01407 01408 rxPacketLength -= 4; //Remove the header bytes from the data count 01409 01410 //Read incoming data into the shtpData array 01411 for (uint16_t dataSpot = 0 ; dataSpot < rxPacketLength ; dataSpot++) 01412 { 01413 bool sendACK = dataSpot < rxPacketLength - 1; 01414 01415 // per the datasheet, 0xFF is used as filler for the receiver to transmit back 01416 uint8_t incoming = static_cast<uint8_t>(_i2cPort.read(sendACK)); 01417 if (dataSpot < (sizeof(rxPacketBuffer) - SHTP_HEADER_SIZE)) //BNO080 can respond with upto 270 bytes, avoid overflow 01418 rxShtpData[dataSpot] = incoming; //Store data into the shtpData array 01419 } 01420 01421 _i2cPort.stop(); 01422 01423 /* 01424 // first read the header to get the length of the packet 01425 int i2cResult; 01426 i2cResult = _i2cPort.read(_i2cAddress << 1, reinterpret_cast<char*>(packetBuffer), SHTP_HEADER_SIZE, false); 01427 if(i2cResult != 0) 01428 { 01429 _debugPort->printf("BNO I2C length read failed!\n"); 01430 return false; 01431 } 01432 01433 if(shtpHeader[0] == 0xFF && shtpHeader[1] == 0xFF) 01434 { 01435 // invalid according to BNO080 datasheet section 1.4.1 01436 #if BNO_DEBUG 01437 _debugPort->printf("Recieved 0xFFFF packet length, protocol error!\n"); 01438 #endif 01439 return false; 01440 } 01441 01442 //Calculate the number of data bytes in this packet 01443 uint16_t totalLength = (static_cast<uint16_t>(shtpHeader[1]) << 8 | shtpHeader[0]); 01444 01445 // Clear the MSbit. 01446 // This bit indicates if this package is a continuation of the last. TBH, I don't really know what this means (it's not really explained in the datasheet) 01447 // but we don't actually care about any of the advertisement packets 01448 // that use this, so we can just cut off the rest of the packet by releasing chip select. 01449 totalLength &= ~(1 << 15); 01450 01451 if (totalLength == 0) 01452 { 01453 // Packet is empty 01454 return (false); //All done 01455 } 01456 01457 // only receive as many bytes as we can fit 01458 if(totalLength > sizeof(packetBuffer)) 01459 { 01460 totalLength = sizeof(packetBuffer); 01461 } 01462 01463 packetLength = totalLength - 4; //Remove the header bytes from the data count 01464 01465 waitStartTime.reset(); 01466 01467 while(_int.read() != 0) 01468 { 01469 if(waitStartTime.elapsed_time() > timeout) 01470 { 01471 _debugPort->printf("BNO I2C wait timeout\n"); 01472 return false; 01473 } 01474 01475 } 01476 01477 //Read the actual packet bytes 01478 _debugPort->printf("Attempting to read %" PRIu16 " bytes\n", totalLength); 01479 i2cResult = _i2cPort.read(_i2cAddress << 1, reinterpret_cast<char*>(packetBuffer), totalLength); 01480 if(i2cResult != 0) 01481 { 01482 _debugPort->printf("BNO I2C read failed!\n"); 01483 return false; 01484 } 01485 */ 01486 01487 #if BNO_DEBUG 01488 _debugPort->printf("Recieved packet: ----------------\n"); 01489 printPacket(rxPacketBuffer); // note: add 4 for the header length 01490 #endif 01491 01492 return (true); //We're done! 01493 } 01494 01495 BNO080SPI::BNO080SPI(Stream *debugPort, PinName rstPin, PinName intPin, PinName wakePin, PinName misoPin, 01496 PinName mosiPin, PinName sclkPin, PinName csPin, int spiSpeed): 01497 BNO080Base(debugPort, intPin, rstPin), 01498 _spiPort(mosiPin, misoPin, sclkPin, csPin, use_gpio_ssel), 01499 _wakePin(wakePin, 1), // wake pin needs to be 1 on reset so that the BNO boots up into SPI mode 01500 _spiSpeed(spiSpeed) 01501 { 01502 _spiPort.frequency(spiSpeed); 01503 _spiPort.format(8, 3); 01504 _spiPort.set_default_write_value(0x0); 01505 } 01506 01507 bool BNO080SPI::sendPacket(uint8_t channelNumber, uint8_t dataLength) 01508 { 01509 if(_int.read() == 0) 01510 { 01511 // The BNO is already awake because it has a packet it wants to send to us 01512 } 01513 else 01514 { 01515 // assert WAKE to tell the BNO to prepare for a transfer 01516 _wakePin = 0; 01517 01518 Timer waitStartTime; 01519 waitStartTime.start(); 01520 const std::chrono::milliseconds timeout = 10ms; 01521 01522 while(_int.read() != 0) 01523 { 01524 if(waitStartTime.elapsed_time() > timeout) 01525 { 01526 _debugPort->printf("BNO SPI wake wait timeout\n"); 01527 _wakePin = 1; 01528 return false; 01529 } 01530 } 01531 _wakePin = 1; 01532 } 01533 01534 uint16_t totalLength = dataLength + 4; //Add four bytes for the header 01535 01536 txShtpHeader[0] = totalLength & 0xFF; 01537 txShtpHeader[1] = totalLength >> 8; 01538 txShtpHeader[2] = channelNumber; 01539 txShtpHeader[3] = sequenceNumber[channelNumber]; 01540 01541 #if BNO_DEBUG 01542 _debugPort->printf("Transmitting packet: ----------------\n"); 01543 printPacket(txPacketBuffer); 01544 #endif 01545 01546 // wipe out any existing RX packet header so we know if we received a packet 01547 memset(rxPacketBuffer, 0, SHTP_HEADER_SIZE); 01548 01549 // send packet to IMU. 01550 // This also might receive the first part of another packet, which there is no way to avoid. 01551 spiTransferAndWait(txPacketBuffer, totalLength, rxPacketBuffer, totalLength); 01552 01553 if(rxShtpHeader[0] == 0 && rxShtpHeader[0] == 0) 01554 { 01555 // no header data so no packet received 01556 return true; 01557 } 01558 else 01559 { 01560 // received first part of data packet while writing 01561 if(receiveCompletePacket(totalLength)) 01562 { 01563 // received data packet, send to proper channels 01564 processPacket(); 01565 return true; 01566 } 01567 01568 // receive failed 01569 return false; 01570 } 01571 } 01572 01573 //Check to see if there is any new data available 01574 //Read the contents of the incoming packet into the shtpData array 01575 bool BNO080SPI::receivePacket(std::chrono::milliseconds timeout) 01576 { 01577 Timer waitStartTime; 01578 waitStartTime.start(); 01579 01580 while (_int.read() != 0) 01581 { 01582 if (waitStartTime.elapsed_time() > timeout) 01583 { 01584 _debugPort->printf("BNO SPI header wait timeout\n"); 01585 return false; 01586 } 01587 01588 } 01589 01590 // read the header bytes first. 01591 spiTransferAndWait(nullptr, 0, rxPacketBuffer, SHTP_HEADER_SIZE); 01592 01593 // now read the data 01594 return receiveCompletePacket(SHTP_HEADER_SIZE, timeout); 01595 } 01596 01597 bool BNO080SPI::receiveCompletePacket(size_t bytesRead, std::chrono::milliseconds timeout) 01598 { 01599 // Process header bytes 01600 // ------------------------------------------------------------------------ 01601 if (rxShtpHeader[0] == 0xFF && rxShtpHeader[1] == 0xFF) 01602 { 01603 // invalid according to BNO080 datasheet section 1.4.1 01604 01605 #if BNO_DEBUG 01606 _debugPort->printf("Recieved 0xFFFF packet length, protocol error!\n"); 01607 #endif 01608 return false; 01609 } 01610 01611 //Calculate the number of data bytes in this packet 01612 uint16_t totalLength = (static_cast<uint16_t>(rxShtpHeader[1]) << 8 | rxShtpHeader[0]); 01613 01614 // Clear the MSbit. 01615 totalLength &= ~(1 << 15); 01616 01617 if (totalLength == 0) 01618 { 01619 // Packet is empty 01620 return (false); //All done 01621 } 01622 01623 rxPacketLength = totalLength - SHTP_HEADER_SIZE; 01624 01625 if(totalLength <= bytesRead) 01626 { 01627 // the original transaction already read the completed packet! We're done. 01628 return true; 01629 } 01630 01631 // Receive data 01632 // ------------------------------------------------------------------------ 01633 01634 // Wait for it to be ready to talk to us again. 01635 // Note: in my testing this takes about 200ms 01636 Timer waitStartTime; 01637 waitStartTime.start(); 01638 while (_int.read() != 0) 01639 { 01640 if (waitStartTime.elapsed_time() > timeout) 01641 { 01642 _debugPort->printf("BNO SPI continued packet header wait timeout\n"); 01643 return false; 01644 } 01645 01646 } 01647 01648 if(rxPacketLength > SHTP_RX_PACKET_SIZE) 01649 { 01650 _debugPort->printf("Packet too long (%" PRIu16 " bytes), increase SHTP_RX_PACKET_SIZE\n", rxPacketLength); 01651 _debugPort->printf("Packet dropped, expect subsequent driver errors.\n"); 01652 return false; 01653 } 01654 01655 if(bytesRead == SHTP_HEADER_SIZE) 01656 { 01657 // just read the entire packet into the buffer 01658 spiTransferAndWait(nullptr, 0, rxPacketBuffer, totalLength); 01659 } 01660 else 01661 { 01662 // we want to receive a new header, plus the remaining data bytes that haven't been read. 01663 size_t receiveLength = SHTP_HEADER_SIZE + (totalLength - bytesRead); 01664 01665 // read remaining bytes into the data buffer starting at the next byte 01666 spiTransferAndWait(nullptr, 0, rxPacketBuffer + bytesRead, receiveLength); 01667 01668 // erase the new header we just read, leaving only the data as a contiguous block 01669 std::memmove(rxPacketBuffer + bytesRead, rxPacketBuffer + bytesRead + SHTP_HEADER_SIZE, receiveLength - SHTP_HEADER_SIZE); 01670 } 01671 01672 #if BNO_DEBUG 01673 _debugPort->printf("Recieved packet: ----------------\n"); 01674 printPacket(rxPacketBuffer); 01675 #endif 01676 01677 //ThisThread::sleep_for(4ms); 01678 01679 // done! 01680 return true; 01681 } 01682 01683 #if USE_ASYNC_SPI 01684 01685 void BNO080SPI::spiTransferAndWait(const uint8_t *tx_buffer, int tx_length, uint8_t *rx_buffer, int rx_length) 01686 { 01687 _spiPort.transfer(tx_buffer, tx_length, rx_buffer, rx_length, 01688 callback(this, &BNO080SPI::onSPITransferComplete), 01689 SPI_EVENT_COMPLETE | SPI_EVENT_ERROR); 01690 01691 uint32_t waitResult = spiCompleteFlag.wait_any(SPI_EVENT_ALL); 01692 if(!(waitResult & SPI_EVENT_COMPLETE)) 01693 { 01694 // at least let the user know the error happened... 01695 _debugPort->printf("BNO Async SPI Error %" PRIu32 "\n", waitResult); 01696 } 01697 01698 } 01699 01700 void BNO080SPI::onSPITransferComplete(int event) 01701 { 01702 spiCompleteFlag.set(event); 01703 } 01704 01705 #endif
Generated on Wed Jul 13 2022 10:30:01 by
1.7.2
Hilcrest BNO080