返回教程正文

配套源码

Adafruit_BNO08x.cpp

app/sketch/lib/Adafruit_BNO08x_Ventuno/src/Adafruit_BNO08x.cpp
ROS 2 与 App 通讯 03:发布 BNO086 IMU 数据app/sketch/lib/Adafruit_BNO08x_Ventuno/src/Adafruit_BNO08x.cpp
C++698 行
/*!
 *  @file Adafruit_BNO08x.cpp
 *
 *  @mainpage Adafruit BNO08x 9-DOF Orientation IMU Fusion Breakout
 *
 *  @section intro_sec Introduction
 *
 * 	I2C Driver for the Library for the BNO08x 9-DOF Orientation IMU Fusion
 * Breakout
 *
 * 	This is a library for the Adafruit BNO08x breakout:
 * 	https://www.adafruit.com/product/4754
 *
 * 	Adafruit invests time and resources providing this open source code,
 *  please support Adafruit and open-source hardware by purchasing products from
 * 	Adafruit!
 *
 *  @section dependencies Dependencies
 *  This library depends on the Adafruit BusIO library
 *
 *  This library depends on the Adafruit Unified Sensor library
 *
 *  @section author Author
 *
 *  Bryan Siepert for Adafruit Industries
 *
 * 	@section license License
 *
 * 	BSD (see license.txt)
 *
 * 	@section  HISTORY
 *
 *     v1.0 - First release
 */

#include "Arduino.h"
#include <Wire.h>

#include "Adafruit_BNO08x.h"

static Adafruit_SPIDevice *spi_dev = NULL; ///< Pointer to SPI bus interface
static int8_t _int_pin = -1, _reset_pin = -1;

static Adafruit_I2CDevice *i2c_dev = NULL; ///< Pointer to I2C bus interface
static HardwareSerial *uart_dev = NULL;

static sh2_SensorValue_t *_sensor_value = NULL;
static bool _reset_occurred = false;
static volatile uint8_t i2c_stage = 0;
static volatile uint16_t i2c_last_packet_size = 0;

static int i2chal_write(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len);
static int i2chal_read(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len,
                       uint32_t *t_us);
static void i2chal_close(sh2_Hal_t *self);
static int i2chal_open(sh2_Hal_t *self);

static int uarthal_write(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len);
static int uarthal_read(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len,
                        uint32_t *t_us);
static void uarthal_close(sh2_Hal_t *self);
static int uarthal_open(sh2_Hal_t *self);

static bool spihal_wait_for_int(void);
static int spihal_write(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len);
static int spihal_read(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len,
                       uint32_t *t_us);
static void spihal_close(sh2_Hal_t *self);
static int spihal_open(sh2_Hal_t *self);

static uint32_t hal_getTimeUs(sh2_Hal_t *self);
static void hal_callback(void *cookie, sh2_AsyncEvent_t *pEvent);
static void sensorHandler(void *cookie, sh2_SensorEvent_t *pEvent);
static void hal_hardwareReset(void);

/**
 * @brief Construct a new Adafruit_BNO08x::Adafruit_BNO08x object
 *
 */

/**
 * @brief Construct a new Adafruit_BNO08x::Adafruit_BNO08x object
 *
 * @param reset_pin The arduino pin # connected to the BNO Reset pin
 */
/*
 * @description         : 读取Ventuno适配层当前I2C传输阶段
 * @param               : 无
 * @return              : 0空闲; 1进入读取; 2等待INT; 3读取包头; 5读取数据段
 */
uint8_t adafruitBno08xGetI2cStage() {
  return i2c_stage;
}

/*
 * @description         : 读取Ventuno适配层最近一次SHTP包长度
 * @param               : 无
 * @return              : 最近一次从SHTP包头解析出的总字节数
 */
uint16_t adafruitBno08xGetLastPacketSize() {
  return i2c_last_packet_size;
}

Adafruit_BNO08x::Adafruit_BNO08x(int8_t reset_pin) { _reset_pin = reset_pin; }

/**
 * @brief Destroy the Adafruit_BNO08x::Adafruit_BNO08x object
 *
 */
Adafruit_BNO08x::~Adafruit_BNO08x(void) {
  // if (temp_sensor)
  //   delete temp_sensor;
}

/*!
 *    @brief  Sets up the hardware and initializes I2C
 *    @param  i2c_address
 *            The I2C address to be used.
 *    @param  wire
 *            The Wire object to be used for I2C connections.
 *    @param  sensor_id
 *            The unique ID to differentiate the sensors from others
 *    @return True if initialization was successful, otherwise false.
 */
bool Adafruit_BNO08x::begin_I2C(
    uint8_t i2c_address,
    TwoWire *wire,
    int32_t sensor_id,
    int8_t int_pin) {
  _int_pin = int_pin;
  if (_int_pin != -1) {
    pinMode(_int_pin, INPUT_PULLUP);
  }

  if (i2c_dev) {
    delete i2c_dev; // remove old interface
  }

  i2c_dev = new Adafruit_I2CDevice(i2c_address, wire);

  if (!i2c_dev->begin()) {
    Serial.println(F("I2C address not found"));
    return false;
  }

  _HAL.open = i2chal_open;
  _HAL.close = i2chal_close;
  _HAL.read = i2chal_read;
  _HAL.write = i2chal_write;
  _HAL.getTimeUs = hal_getTimeUs;

  return _init(sensor_id);
}

/**
 *  @brief  Sets up the hardware and initializes UART
 *
 * @param serial Pointer to Stream (HardwareSerial/SoftwareSerial) interface
 * @param sensor_id
 *            The user-defined ID to differentiate different sensors
 * @return  true if initialization was successful, otherwise false.
 */
bool Adafruit_BNO08x::begin_UART(HardwareSerial *serial, int32_t sensor_id) {
  uart_dev = serial;

  _HAL.open = uarthal_open;
  _HAL.close = uarthal_close;
  _HAL.read = uarthal_read;
  _HAL.write = uarthal_write;
  _HAL.getTimeUs = hal_getTimeUs;

  return _init(sensor_id);
}

/*!
 *    @brief  Sets up the hardware and initializes hardware SPI
 *    @param  cs_pin The arduino pin # connected to chip select
 *    @param  int_pin The arduino pin # connected to BNO08x INT
 *    @param  theSPI The SPI object to be used for SPI connections.
 *    @param  sensor_id
 *            The user-defined ID to differentiate different sensors
 *    @return true if initialization was successful, otherwise false.
 */
bool Adafruit_BNO08x::begin_SPI(uint8_t cs_pin, uint8_t int_pin,
                                SPIClass *theSPI, int32_t sensor_id) {
  i2c_dev = NULL;

  _int_pin = int_pin;
  pinMode(_int_pin, INPUT_PULLUP);

  if (spi_dev) {
    delete spi_dev; // remove old interface
  }
  spi_dev = new Adafruit_SPIDevice(cs_pin,
                                   1000000,               // frequency
                                   SPI_BITORDER_MSBFIRST, // bit order
                                   SPI_MODE3,             // data mode
                                   theSPI);
  if (!spi_dev->begin()) {
    return false;
  }

  _HAL.open = spihal_open;
  _HAL.close = spihal_close;
  _HAL.read = spihal_read;
  _HAL.write = spihal_write;
  _HAL.getTimeUs = hal_getTimeUs;

  return _init(sensor_id);
}

/*!  @brief Initializer for post i2c/spi init
 *   @param sensor_id Optional unique ID for the sensor set
 *   @returns True if chip identified and initialized
 */
bool Adafruit_BNO08x::_init(int32_t sensor_id) {
  int status;

  hardwareReset();

  // Open SH2 interface (also registers non-sensor event handler.)
  status = sh2_open(&_HAL, hal_callback, NULL);
  if (status != SH2_OK) {
    return false;
  }

  // Check connection partially by getting the product id's
  memset(&prodIds, 0, sizeof(prodIds));
  status = sh2_getProdIds(&prodIds);
  if (status != SH2_OK) {
    return false;
  }

  // Register sensor listener
  sh2_setSensorCallback(sensorHandler, NULL);

  return true;
}

/**
 * @brief Reset the device using the Reset pin
 *
 */
void Adafruit_BNO08x::hardwareReset(void) { hal_hardwareReset(); }

/**
 * @brief Check if a reset has occured
 *
 * @return true: a reset has occured false: no reset has occoured
 */
bool Adafruit_BNO08x::wasReset(void) {
  bool x = _reset_occurred;
  _reset_occurred = false;

  return x;
}

/**
 * @brief Fill the given sensor value object with a new report
 *
 * @param value Pointer to an sh2_SensorValue_t struct to fil
 * @return true: The report object was filled with a new report
 * @return false: No new report available to fill
 */
bool Adafruit_BNO08x::getSensorEvent(sh2_SensorValue_t *value) {
  _sensor_value = value;

  value->timestamp = 0;

  sh2_service();

  if (value->timestamp == 0 && value->sensorId != SH2_GYRO_INTEGRATED_RV) {
    // no new events
    return false;
  }

  return true;
}

/**
 * @brief Enable the given report type
 *
 * @param sensorId The report ID to enable
 * @param interval_us The update interval for reports to be generated, in
 * microseconds
 * @return true: success false: failure
 */
bool Adafruit_BNO08x::enableReport(sh2_SensorId_t sensorId,
                                   uint32_t interval_us) {
  static sh2_SensorConfig_t config;

  // These sensor options are disabled or not used in most cases
  config.changeSensitivityEnabled = false;
  config.wakeupEnabled = false;
  config.changeSensitivityRelative = false;
  config.alwaysOnEnabled = false;
  config.changeSensitivity = 0;
  config.batchInterval_us = 0;
  config.sensorSpecific = 0;

  config.reportInterval_us = interval_us;
  int status = sh2_setSensorConfig(sensorId, &config);

  if (status != SH2_OK) {
    return false;
  }

  return true;
}

/**************************************** I2C interface
 * ***********************************************************/

static int i2chal_open(sh2_Hal_t *self) {
  // Serial.println("I2C HAL open");
  uint8_t softreset_pkt[] = {5, 0, 1, 0, 1};
  bool success = false;
  for (uint8_t attempts = 0; attempts < 5; attempts++) {
    if (i2c_dev->write(softreset_pkt, 5)) {
      success = true;
      break;
    }
    delay(30);
  }
  if (!success)
    return -1;
  delay(300);
  return 0;
}

static void i2chal_close(sh2_Hal_t *self) {
  // Serial.println("I2C HAL close");
}

/*
 * @description         : 等待BNO086通过低电平INT通知I2C数据已经就绪
 * @param               : 无
 * @return              : true在500ms内检测到低电平; false等待超时
 */
static bool i2chal_wait_for_interrupt() {
  for (uint16_t elapsedMs = 0; elapsedMs < 500; ++elapsedMs) {
    if (digitalRead(_int_pin) == LOW) {
      return true;
    }
    delay(1);
  }
  return false;
}

/*
 * @description         : 在BNO086分段读取发生NAK时按CEVA建议进行有限次数重试
 * @param buffer        : 接收I2C数据的缓冲区
 * @param length        : 本次需要读取的字节数
 * @return              : true读取成功; false三次尝试均失败
 */
static bool i2chal_read_with_retry(uint8_t *buffer, size_t length) {
  constexpr uint8_t kMaximumAttempts = 3;
  for (uint8_t attempt = 0; attempt < kMaximumAttempts; ++attempt) {
    if (i2c_dev->read(buffer, length)) {
      return true;
    }
    if (attempt + 1 < kMaximumAttempts) {
      delay(1);
    }
  }
  return false;
}

/*
 * @description         : 读取一个Ventuno Wire缓冲大小的BNO086 SHTP分片并交由CEVA层组包
 * @param self          : SH-2硬件抽象接口实例
 * @param pBuffer       : 接收SHTP分片的目标缓冲区
 * @param len           : 目标缓冲区容量
 * @param t_us          : 输出本次读取的微秒时间戳
 * @return              : 正数为有效分片字节数; 0表示无数据或读取失败
 */
static int i2chal_read(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len,
                       uint32_t *t_us) {
  constexpr unsigned kFragmentReadLength = 32;
  i2c_stage = 1;

  if (_int_pin != -1) {
    i2c_stage = 2;
    if (!i2chal_wait_for_interrupt()) {
      i2c_stage = 0;
      return 0;
    }
  }

  i2c_stage = 3;
  uint8_t fragment[kFragmentReadLength] = {0};
  if (!i2chal_read_with_retry(fragment, kFragmentReadLength)) {
    i2c_stage = 0;
    return 0;
  }

  uint16_t packetSize =
      static_cast<uint16_t>(fragment[0]) |
      (static_cast<uint16_t>(fragment[1]) << 8);
  packetSize &= ~0x8000;
  i2c_last_packet_size = packetSize;

  if (packetSize < 4) {
    i2c_stage = 0;
    return 0;
  }

  const unsigned bytesToReturn =
      packetSize < kFragmentReadLength ? packetSize : kFragmentReadLength;
  if (bytesToReturn > len) {
    i2c_stage = 0;
    return 0;
  }

  memcpy(pBuffer, fragment, bytesToReturn);
  if (t_us != nullptr) {
    *t_us = micros();
  }
  i2c_stage = 0;
  return static_cast<int>(bytesToReturn);
}

static int i2chal_write(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len) {
  size_t i2c_buffer_max = i2c_dev->maxBufferSize();

  /*
  Serial.print("I2C HAL write packet size: ");
  Serial.print(len);
  Serial.print(" & max buffer size: ");
  Serial.println(i2c_buffer_max);
  */

  uint16_t write_size = min(i2c_buffer_max, len);
  if (!i2c_dev->write(pBuffer, write_size)) {
    return 0;
  }

  return write_size;
}

/**************************************** UART interface
 * ***********************************************************/

static int uarthal_open(sh2_Hal_t *self) {
  // Serial.println("UART HAL open");
  uart_dev->begin(3000000);

  // flush input
  while (uart_dev->available()) {
    uart_dev->read();
    yield();
  }

  // send a software reset
  uint8_t softreset_pkt[] = {0x7E, 1, 5, 0, 1, 0, 1, 0x7E};
  for (int i = 0; i < sizeof(softreset_pkt); i++) {
    uart_dev->write(softreset_pkt[i]);
    delay(1);
  }

  return 0;
}

static void uarthal_close(sh2_Hal_t *self) {
  // Serial.println("UART HAL close");
  uart_dev->end();
}

static int uarthal_read(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len,
                        uint32_t *t_us) {
  uint8_t c;
  uint16_t packet_size = 0;

  // Serial.println("UART HAL read");

  // read packet start
  while (1) {
    yield();

    if (!uart_dev->available()) {
      continue;
    }
    c = uart_dev->read();
    // Serial.print(c, HEX); Serial.print(", ");
    if (c == 0x7E) {
      break;
    }
  }

  // read protocol id
  while (uart_dev->available() < 2) {
    yield();
  }
  c = uart_dev->read();
  // Serial.print(c, HEX); Serial.print(", ");
  if (c == 0x7E) {
    c = uart_dev->read();
    // Serial.print(c, HEX); Serial.print(", ");
    if (c != 0x01) {
      return 0;
    }
  } else if (c != 0x01) {
    return 0;
  }

  while (true) {
    yield();

    if (!uart_dev->available()) {
      continue;
    }
    c = uart_dev->read();
    // Serial.print(c, HEX); Serial.print(", ");
    if (c == 0x7E) {
      break;
    }
    if (c == 0x7D) {
      // escape!
      while (!uart_dev->available()) {
        continue;
      }
      c = uart_dev->read();
      c ^= 0x20;
    }
    pBuffer[packet_size] = c;
    packet_size++;
  }

  /*
  Serial.print("Read UART packet size: ");
  Serial.println(packet_size);
  for (int i=0; i<packet_size; i++) {
    Serial.print(pBuffer[i], HEX);
    Serial.print(", ");
    if (i % 16 == 15) Serial.println();
  }
  Serial.println();
  */

  return packet_size;
}

static int uarthal_write(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len) {
  uint8_t c;

  // Serial.print("UART HAL write packet size: ");
  // Serial.println(len);

  // start byte
  uart_dev->write(0x7E);
  delay(1);
  // protocol id
  uart_dev->write(0x01);
  delay(1);

  for (int i = 0; i < len; i++) {
    c = pBuffer[i];
    if ((c == 0x7E) || (c == 0x7D)) {
      uart_dev->write(0x7D); // control
      delay(1);
      c ^= 0x20;
    }
    uart_dev->write(c);
    delay(1);
  }
  // end byte
  uart_dev->write(0x7E);

  return len;
}

/**************************************** UART interface
 * ***********************************************************/

static int spihal_open(sh2_Hal_t *self) {
  // Serial.println("SPI HAL open");

  spihal_wait_for_int();

  return 0;
}

static bool spihal_wait_for_int(void) {
  for (int i = 0; i < 500; i++) {
    if (!digitalRead(_int_pin))
      return true;
    // Serial.print(".");
    delay(1);
  }
  // Serial.println("Timed out!");
  hal_hardwareReset();

  return false;
}

static void spihal_close(sh2_Hal_t *self) {
  // Serial.println("SPI HAL close");
}

static int spihal_read(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len,
                       uint32_t *t_us) {
  // Serial.println("SPI HAL read");

  uint16_t packet_size = 0;

  if (!spihal_wait_for_int()) {
    return 0;
  }

  if (!spi_dev->read(pBuffer, 4, 0x00)) {
    return 0;
  }

  // Determine amount to read
  packet_size = (uint16_t)pBuffer[0] | (uint16_t)pBuffer[1] << 8;
  // Unset the "continue" bit
  packet_size &= ~0x8000;

  /*
  Serial.print("Read SHTP header. ");
  Serial.print("Packet size: ");
  Serial.print(packet_size);
  Serial.print(" & buffer size: ");
  Serial.println(len);
  */

  if (packet_size > len) {
    return 0;
  }

  if (!spihal_wait_for_int()) {
    return 0;
  }

  if (!spi_dev->read(pBuffer, packet_size, 0x00)) {
    return 0;
  }

  return packet_size;
}

static int spihal_write(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len) {
  // Serial.print("SPI HAL write packet size: ");
  // Serial.println(len);

  if (!spihal_wait_for_int()) {
    return 0;
  }

  spi_dev->write(pBuffer, len);

  return len;
}

/**************************************** HAL interface
 * ***********************************************************/

static void hal_hardwareReset(void) {
  if (_reset_pin != -1) {
    // Serial.println("BNO08x Hardware reset");

    pinMode(_reset_pin, OUTPUT);
    digitalWrite(_reset_pin, HIGH);
    delay(10);
    digitalWrite(_reset_pin, LOW);
    delay(10);
    digitalWrite(_reset_pin, HIGH);
    delay(10);
  }
}

static uint32_t hal_getTimeUs(sh2_Hal_t *self) {
  uint32_t t = millis() * 1000;
  // Serial.printf("I2C HAL get time: %d\n", t);
  return t;
}

static void hal_callback(void *cookie, sh2_AsyncEvent_t *pEvent) {
  // If we see a reset, set a flag so that sensors will be reconfigured.
  if (pEvent->eventId == SH2_RESET) {
    // Serial.println("Reset!");
    _reset_occurred = true;
  }
}

// Handle sensor events.
static void sensorHandler(void *cookie, sh2_SensorEvent_t *event) {
  int rc;

  // Serial.println("Got an event!");

  rc = sh2_decodeSensorEvent(_sensor_value, event);
  if (rc != SH2_OK) {
    Serial.println("BNO08x - Error decoding sensor event");
    _sensor_value->timestamp = 0;
    return;
  }
}