[RA4 & RA6] 【RA-Eco-RA6M4开发板评测】基于Zephyr的BNO080 移植

[复制链接]
30|0
Ggvvvvvh 发表于 2026-7-2 23:05 | 显示全部楼层 |阅读模式
[i=s] 本帖最后由 流鱼 于 2026-7-3 13:53 编辑 [/i]

BNO080 移植指南

将 BNO080 9 轴 IMU 的官方ceva sh2 库移植到 Zephyr RTOS 平台。

https://github.com/ceva-dsp/sh2


1. BNO080 简介

BNO080 是 Bosch 的系统级封装 (SiP) 9 轴 IMU,

屏幕截图2026-07-02223107.png

屏幕截图2026-07-02225542.png

集成:

  • 三轴加速度计
  • 三轴陀螺仪
  • 三轴磁力计
  • 32-bit ARM Cortex-M0+ 微控制器(运行 SH-2 固件)

1.1 关键特性

特性 参数
通信接口 I2C / SPI / UART(通过 PS0/PS1 引脚选择)
工作电压 1.7V~ 3.6V
传感器融合 内置 SH-2 算法(四元数、欧拉角、线性加速度等)
输出数据 加速度、角速度、磁场、旋转矢量、线性加速度、重力等
中断输出 INT 引脚(低电平有效)
复位 RST 引脚(低电平有效)

1.2 通信协议选择

通过 PS1/PS0 引脚电平选择(BOOTN=1 正常模式):

PS1 PS0 协议
0 0 I2C
0 1 UART-RVC
1 0 UART
1 1 SPI

本项目使用 SPI 模式(PS1=1, PS0=1,均拉高到 3.3V)。


2. 官方库结构

BNO080 使用 SH2 传感器库:

drv/bno080/
├── inc/
│   ├── sh2.h              # SH2 主 API 头文件
│   ├── sh2_SensorValue.h  # 传感器数据解码
│   ├── sh2_hal.h          # HAL 接口定义(需用户实现)
│   ├── sh2_err.h          # 错误码定义
│   ├── sh2_util.h         # 工具函数
│   ├── shtp.h             # SHTP 协议层
│   ├── euler.h            # 欧拉角转换
│   └── bno080_sensor.h   # 位域传感器定义和统一数据结构
├── src/
│   ├── sh2.c              # SH2 核心实现
│   ├── sh2_SensorValue.c  # 传感器事件解码
│   ├── sh2_util.c         # 工具函数实现
│   ├── shtp.c             # SHTP 协议实现
│   └── euler.c            # 欧拉角转换实现
└── platform/
    ├── sh2_spi_hal.c      # SPI HAL 实现(Zephyr 适配)
    ├── sh2_spi_hal.h
    ├── sh2_i2c_hal.c      # I2C HAL 实现(Zephyr 适配)
    ├── sh2_i2c_hal.h
    ├── sh2_uart_hal.c     # UART HAL 实现(Zephyr 适配)
    ├── sh2_uart_hal.h
    ├── sh2_demo.c         # 应用层封装
    └── sh2_demo.h

2.1 官方 SH2 API 核心函数

函数 说明
sh2_open() 打开传感器会话
sh2_close() 关闭会话
sh2_service() 服务设备(读取数据、分发回调)
sh2_setSensorCallback() 注册传感器数据回调
sh2_setSensorConfig() 配置传感器(启用/禁用、报告间隔)
sh2_getSensorConfig() 获取传感器配置
sh2_getProdIds() 获取产品 ID
sh2_saveDcdNow() 保存动态校准数据

2.2 HAL 接口(需自行实现)

struct sh2_Hal_s {
    int  (*open)(sh2_Hal_t *self);
    void (*close)(sh2_Hal_t *self);
    int  (*read)(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len, uint32_t *t_us);
    int  (*write)(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len);
    uint32_t (*getTimeUs)(sh2_Hal_t *self);
};

关键要求:

  • open: 初始化硬件(GPIO、SPI/I2C),执行复位序列
  • close: 关闭硬件,进入复位状态
  • read: 如果有完整 SHTP 传输,加载到 pBuffer 并返回长度;否则返回 0
  • write: 复制数据到发送缓冲区,返回接受字节数
  • getTimeUs: 返回微秒时间戳

3. 移植架构

3.1 分层设计

┌─────────────────────────────────────────┐
│        应用层 (example_bno080.c)        │
│   调用 sh2_demo_* 接口读取传感器数据     │
├─────────────────────────────────────────┤
│      应用封装层 (sh2_demo.c)            │
│   封装 SH2 初始化、回调、数据缓存        │
├─────────────────────────────────────────┤
│      SH2 核心库 (sh2.c, shtp.c)         │
│   SHTP 协议、传感器配置、数据处理        │
├─────────────────────────────────────────┤
│      HAL 层 (sh2_spi_hal.c)             │
│   将 SH2 HAL 接口映射到 Zephyr SPI/GPIO │
├─────────────────────────────────────────┤
│      Zephyr RTOS (SPI/GPIO 驱动)        │
│   硬件抽象,设备树绑定                   │
├─────────────────────────────────────────┤
│      硬件 (MCU + BNO080 传感器)         │
└─────────────────────────────────────────┘

3.2 关键设计决策

  1. 不修改 SH2 核心库 — 仅实现 HAL 层
  2. SPI 模式 — PS0=1, PS1=1(拉高到 3.3V)
  3. GPIO CS — 不使用硬件 SSL,避免引脚冲突
  4. 中断驱动 — INT 引脚触发数据读取
  5. 同步 SPIread()/write() 直接执行同步 SPI 传输
  6. 应用封装sh2_demo.c 封装 SH2 初始化和数据缓存

4. HAL 层实现

4.1 同步 SPI 传输模型

BNO080 SPI 通信采用同步传输模型,HAL 层的 read()write() 函数直接执行同步 SPI 操作(spi_transceive()),无需状态机或异步缓冲。

核心设计:

  • 使用 Zephyr spi_transceive() 进行全双工同步传输
  • data_ready 标志由 INT 中断设置,read() 检查该标志决定是否执行 SPI 读取
  • write() 先拉低 PS0(WAKEN)唤醒传感器,再执行 SPI 写入
  • CS 引脚由 GPIO 手动控制,不使用硬件 SSL

4.2 GPIO 控制

/* 设备树节点 */
#define BNO080_NODE DT_NODELABEL(bno080)

/* SPI 设备 */
static const struct device *spi_dev = DEVICE_DT_GET(DT_BUS(BNO080_NODE));

/* GPIO 设备 */
static const struct device *cs_gpio_dev =
    DEVICE_DT_GET(DT_GPIO_CTLR(BNO080_NODE, cs_gpios));
static const struct device *int_reset_gpio_dev =
    DEVICE_DT_GET(DT_GPIO_CTLR(BNO080_NODE, int_gpios));

/* 引脚定义 */
#define CS_PIN      DT_GPIO_PIN(BNO080_NODE, cs_gpios)
#define INT_PIN     DT_GPIO_PIN(BNO080_NODE, int_gpios)
#define RESET_PIN   DT_GPIO_PIN(BNO080_NODE, reset_gpios)

4.3 SPI 配置

/* SPI 配置 - 使用设备树和 Kconfig 配置 */
static struct spi_config spi_cfg = {
    .frequency = DT_PROP(BNO080_NODE, spi_max_frequency),
    .operation = (SPI_OP_MODE_MASTER | SPI_WORD_SET(8) | SPI_TRANSFER_MSB |
                  SPI_MODE_CPOL | SPI_MODE_CPHA | SPI_HOLD_ON_CS),
    .slave = 0,
    .cs = { .gpio = { .port = NULL, .pin = 0, .dt_flags = 0 }, .delay = 0 },
};

SPI 模式说明:

  • SPI_MODE_CPOL | SPI_MODE_CPHA = Mode 3(CPOL=1, CPHA=1)
  • BNO080 支持 Mode 0 和 Mode 3
  • 如果通信失败,可尝试移除 SPI_MODE_CPOL | SPI_MODE_CPHA 切换到 Mode 0

4.4 HAL open 函数

static int sh2_spi_hal_open(sh2_Hal_t *self)
{
    /* 1. 检查设备就绪 */
    if (!device_is_ready(spi_dev)) return SH2_ERR_IO;

    /* 2. 配置 GPIO */
    gpio_pin_configure(cs_gpio_dev, CS_PIN, GPIO_OUTPUT_HIGH);     /* CS 初始高 */
    gpio_pin_configure(int_reset_gpio_dev, RESET_PIN, GPIO_OUTPUT_LOW);  /* RST 低 */
    gpio_pin_configure(int_reset_gpio_dev, INT_PIN,
                       GPIO_INPUT | GPIO_INT_EDGE_FALLING);         /* INT 下降沿 */

    /* 3. 保存 HAL 实例 */
    hal_instance = self;

    /* 4. 初始化状态 */
    in_reset = true;
    is_open = true;

    /* 5. 执行虚拟 SPI 操作(建立 SCLK 初始状态) */
    spi_dummy_op();

    /* 6. 延时确保复位生效 */
    delay_us(RESET_DELAY_US);

    /* 7. 设置启动模式为 SHTP-SPI (PS1=1, PS0=1) */
    set_ps0(true);
    set_ps1(true);

    /* 8. 释放复位,非 DFU 模式 */
    set_bootn(true);
    set_reset(true);

    /* 9. 注册 INT 中断回调 */
    gpio_init_callback(&int_cb_data, int_callback, BIT(INT_PIN));
    gpio_add_callback(int_reset_gpio_dev, &int_cb_data);
    gpio_pin_interrupt_configure(int_reset_gpio_dev, INT_PIN, GPIO_INT_EDGE_FALLING);

    /* 10. 等待 INT 被触发 */
    delay_us(START_DELAY_US);

    return SH2_OK;
}

4.5 HAL read/write 函数

static int sh2_spi_hal_read(sh2_Hal_t *self, uint8_t *pBuffer,
                             unsigned len, uint32_t *t)
{
    static uint8_t dummy_tx[SH2_HAL_MAX_TRANSFER_IN] = {0};

    if (len < READ_LEN) return SH2_ERR_BAD_PARAM;

    /* 检查 data_ready 标志(由 INT 中断设置) */
    if (!data_ready) return 0;
    data_ready = false;

    /* 拉低 CS */
    set_cs(false);
    k_usleep(50);  /* CS setup time */

    /* 读取 header (4 字节) */
    if (spi_transfer_sync(dummy_tx, pBuffer, READ_LEN) != 0) {
        set_cs(true);
        return 0;
    }

    /* 解析数据长度 */
    uint16_t rx_len = (pBuffer[0] + (pBuffer[1] << 8)) & ~0x8000;
    if (rx_len > len || rx_len < READ_LEN) {
        set_cs(true);
        return 0;
    }

    /* 读取 payload */
    if (rx_len > READ_LEN) {
        if (spi_transfer_sync(dummy_tx, pBuffer + READ_LEN, rx_len - READ_LEN) != 0) {
            set_cs(true);
            return 0;
        }
    }

    set_cs(true);
    *t = rx_timestamp_us;
    return rx_len;
}

static int sh2_spi_hal_write(sh2_Hal_t *self, uint8_t *pBuffer, unsigned len)
{
    if (len == 0 || pBuffer == NULL) return SH2_ERR_BAD_PARAM;

    /* 激活 WAKEN(拉低 PS0) */
    set_ps0(false);
    set_cs(false);

    if (spi_transfer_sync(pBuffer, NULL, len) != 0) {
        set_cs(true);
        set_ps0(true);
        return SH2_ERR_IO;
    }

    set_cs(true);
    set_ps0(true);
    return len;
}

关键设计:

  • data_ready 标志由 INT 中断回调设置,read() 检查该标志避免无数据时的 SPI 操作
  • read() 分两步:先读 4 字节 header 获取长度,再读 payload
  • write() 通过拉低 PS0(WAKEN)唤醒传感器,写入完成后恢复
  • CS setup time (50us) 满足 BNO080 时序要求

4.6 中断处理

/* INT 引脚中断回调(中断上下文) */
static void int_callback(const struct device *dev,
                         struct gpio_callback *cb, gpio_port_pins_t pins)
{
    /* 记录时间戳 */
    rx_timestamp_us = time_now_us();
    in_reset = false;
    data_ready = true;  /* 设置数据就绪标志 */
}

关键设计:

  • 中断回调仅设置 data_ready 标志和记录时间戳,不执行 SPI 传输
  • read() 函数在下一次被 SH2 核心调用时检查 data_ready 标志并执行同步读取
  • 无工作队列、无异步缓冲,实现简洁

5. 位域传感器管理

5.1 位域掩码结构

BNO080 支持大量传感器类型(0x01~0x2E),使用双 uint32_t 位域高效管理:

typedef struct {
    uint32_t mask_lo;  /* 传感器 ID 0x00-0x1F */
    uint32_t mask_hi;  /* 传感器 ID 0x20-0x3F */
} bno080_mask_t;

位操作宏: | 宏 | 说明 | |---|---| | BNO080_MASK_SET(mask, id) | 设置传感器位 | | BNO080_MASK_CLEAR(mask, id) | 清除传感器位 | | BNO080_MASK_TEST(mask, id) | 测试传感器位 | | BNO080_MASK_OR(dst, src) | 合并掩码 | | BNO080_MASK_IS_ZERO(mask) | 检查是否为空 |

5.2 预定义传感器组

/* 基础 9 轴 */
#define BNO080_GROUP_BASIC_9AXIS   /* ACC + GYRO + MAG + RV + LINACC + GRAVITY */

/* 原始数据 */
#define BNO080_GROUP_ALL_RAW       /* RAW_ACC + RAW_GYRO + RAW_MAG */

/* 未校准 */
#define BNO080_GROUP_ALL_UNCAL     /* GYRO_UNCAL + MAG_UNCAL */

/* 旋转矢量变体 */
#define BNO080_GROUP_ALL_RV        /* RV + GAME_RV + GEOMAG_RV + GYRO_RV */

/* 环境传感器 */
#define BNO080_GROUP_ALL_ENV       /* PRESSURE + LIGHT + HUMIDITY + PROXIMITY + TEMP */

/* 活动检测器 */
#define BNO080_GROUP_ALL_DETECTORS /* STEP + TAP + SHAKE + FLIP + PICKUP + ... */

/* 全部传感器 */
#define BNO080_GROUP_ALL           /* 所有支持的传感器类型 */

5.3 统一数据结构

typedef struct {
    bno080_mask_t valid_mask;  /* 标记哪些字段有效 */

    /* 基础运动传感器 */
    sh2_Accelerometer_t accel;
    sh2_Gyroscope_t gyro;
    sh2_MagneticField_t mag;
    sh2_RotationVectorWAcc_t rv;
    sh2_Accelerometer_t lin_accel;
    sh2_Accelerometer_t gravity;

    /* 原始数据、未校准数据、旋转矢量变体、环境传感器、活动检测器等 */
    /* ... */
} bno080_data_t;

bno080_data_fill() 内联函数根据 sensorId 自动填充对应字段并设置 valid_mask 位。

5.4 sh2_demo API

函数 说明
sh2_demo_init() 初始化 SH2 Demo(根据 Kconfig 选择默认传感器组)
sh2_demo_service() 服务循环,必须定期调用
sh2_demo_get_sensor_data(id, data) 获取单个传感器数据
sh2_demo_read_all(data) 读取所有启用传感器数据到 bno080_data_t
sh2_demo_enable_sensors(mask) 启用传感器(位域方式)
sh2_demo_disable_sensors(mask) 禁用传感器(位域方式)
sh2_demo_enable_all() 启用所有传感器
sh2_demo_get_enabled_mask() 获取当前启用的传感器掩码

6. 设备树绑定

6.1 绑定文件 (bosch,bno080.yaml)

compatible: "bosch,bno080"
include: [spi-device.yaml, sensor-device.yaml]

properties:
  int-gpios:
    type: phandle-array
    required: true
    description: INT 引脚,中断输出(低电平有效)

  reset-gpios:
    type: phandle-array
    required: false
    description: RST 引脚,复位(低电平有效)

  ps0-gpios:
    type: phandle-array
    required: false
    description: PS0 引脚(可选,硬件固定时可省略)

  ps1-gpios:
    type: phandle-array
    required: false
    description: PS1 引脚(可选,硬件固定时可省略)

  cs-gpios:
    type: phandle-array
    required: true
    description: CS 引脚(低电平有效)

  current-speed:
    type: int
    required: false
    default: 115200
    description: UART 波特率(仅 UART 模式)

  reset-delay-us:
    type: int
    required: false
    default: 10000
    description: |
      Reset delay in microseconds.
      Time to wait after asserting reset before releasing.
      Default is 10000 us (10 ms) as per BNO080 datasheet.

  start-delay-us:
    type: int
    required: false
    default: 2000000
    description: |
      Start delay in microseconds.
      Time to wait after power-on before sensor is ready.
      Default is 2000000 us (2 seconds) as per BNO080 datasheet.

6.2 设备树覆盖 (overlay)

屏幕截图2026-07-01231238.png

#include <zephyr/dt-bindings/gpio/gpio.h>
#include <zephyr/dt-bindings/pinctrl/renesas/pinctrl-ra.h>

/ {
    chosen {
        zephyr,spi = &spi1;
    };
};

/* 定义 SPI1 pinctrl( SSL 为CS 片选) */
&pinctrl {
    spi1_no_ssl: spi1_no_ssl {
        group1 {
            psels = <RA_PSEL(RA_PSEL_SPI, 1, 0)>,   /* MISO: P1.0 */
                    <RA_PSEL(RA_PSEL_SPI, 1, 1)>,   /* MOSI: P1.1 */
                    <RA_PSEL(RA_PSEL_SPI, 1, 2)>;   /* RSPCK: P1.2 */
        };
    };
};

&spi1 {
    status = "okay";
    pinctrl-0 = <&spi1_no_ssl>;
    pinctrl-names = "default";

    bno080: bno080@0 {
        compatible = "bosch,bno080";
        reg = <0>;
        spi-max-frequency = <1000000>;  /* 1MHz */
        spi-cpol;                        /* SPI Mode 3 */
        spi-cpha;
        status = "okay";
        cs-gpios = <&ioport1 3 GPIO_ACTIVE_LOW>;    /* P1.3 */
        int-gpios = <&ioport3 1 GPIO_ACTIVE_LOW>;   /* P3.1 */
        reset-gpios = <&ioport3 6 GPIO_ACTIVE_LOW>; /* P3.6 */
    };
};

关键注意事项:

  1. CS 引脚必须使用
  2. PS0/PS1 硬件拉高 — 需要拉高 3.3V
  3. SPI 模式spi-cpol + spi-cpha = Mode 3

7. Kconfig 配置

7.1 项目 Kconfig

menuconfig ENABLE_BNO080
    bool "Enable BNO080 9-axis IMU"
    help
      Enable BNO080 9-axis IMU sensor driver and examples (SPI interface).

if ENABLE_BNO080

choice BNO080_DEFAULT_SENSORS
    prompt "BNO080 default sensor group"
    default BNO080_DEFAULT_ALL

config BNO080_DEFAULT_BASIC_9AXIS
    bool "Basic 9-axis (accel/gyro/mag/rv/linacc/gravity)"

config BNO080_DEFAULT_ALL_RAW
    bool "All raw sensors"

config BNO080_DEFAULT_ALL
    bool "All sensors"

endchoice

menuconfig BNO080_TIMING
    bool "BNO080 timing configuration"
    default y

if BNO080_TIMING

config BNO080_RESET_DELAY_US
    int "Reset delay (microseconds)"
    default 10000
    help
      Delay after asserting reset before releasing.

config BNO080_START_DELAY_US
    int "Start delay (microseconds)"
    default 2000000
    help
      Delay after power-on before sensor is ready.

config BNO080_SPI_FREQ
    int "SPI frequency (Hz)"
    default 1000000
    help
      SPI clock frequency for BNO080 communication.

endif # BNO080_TIMING

endif # ENABLE_BNO080

7.2 板级 prj.conf

# SPI 驱动
CONFIG_SPI=y
CONFIG_SPI_ASYNC=y
CONFIG_SPI_RENESAS_RA=y
CONFIG_SPI_INTERRUPT=y

# GPIO 驱动
CONFIG_GPIO=y
CONFIG_GPIO_RA_IOPORT=y

# 外部中断
CONFIG_RENESAS_RA_EXTERNAL_INTERRUPT=y

# 启用 BNO080
CONFIG_ENABLE_BNO080=y

8. CMake 构建集成

# BNO080 驱动源文件
set(BNO080_DIR ${CMAKE_CURRENT_SOURCE_DIR}/drv/bno080)

# SH2 核心库
set(SH2_SOURCES
    ${BNO080_DIR}/src/sh2.c
    ${BNO080_DIR}/src/sh2_SensorValue.c
    ${BNO080_DIR}/src/sh2_util.c
    ${BNO080_DIR}/src/shtp.c
    ${BNO080_DIR}/src/euler.c
)

# SH2 平台适配层
set(SH2_PLATFORM_SOURCES
    ${BNO080_DIR}/platform/sh2_spi_hal.c
    ${BNO080_DIR}/platform/sh2_i2c_hal.c
    ${BNO080_DIR}/platform/sh2_uart_hal.c
    ${BNO080_DIR}/platform/sh2_demo.c
)

# BNO080 所有源文件汇总
set(BNO080_SOURCES
    ${SH2_SOURCES}
    ${SH2_PLATFORM_SOURCES}
)

set(BNO080_INCLUDE_DIRS
    ${BNO080_DIR}/inc
    ${BNO080_DIR}/platform
)

# 条件编译
if(CONFIG_ENABLE_BNO080)
    target_sources(app PRIVATE example/example_bno080.c)
    target_sources(app PRIVATE ${BNO080_SOURCES})
    target_include_directories(app PRIVATE ${BNO080_INCLUDE_DIRS})
endif()

9. 应用层调用

9.1 初始化流程

/* 1. 初始化 SH2 Demo */
int ret = sh2_demo_init();
if (ret) {
    LOG_ERR("SH2 Demo 初始化失败: %d", ret);
    return ret;
}

/* sh2_demo_init() 内部执行: */
/* - 初始化 SPI HAL */
/* - 调用 sh2_open() */
/* - 设置传感器回调 */
/* - 配置并启用各传感器 */

9.2 传感器配置

/* 在 sh2_demo_init() 中配置传感器 */
sh2_SensorConfig_t config;
memset(&config, 0, sizeof(config));
config.reportInterval_us = 100000;  /* 100ms 报告间隔 */
config.changeSensitivityEnabled = false;
config.wakeupEnabled = false;
config.alwaysOnEnabled = false;

/* 启用加速度计 */
sh2_setSensorConfig(SH2_ACCELEROMETER, &config);

/* 启用陀螺仪 */
sh2_setSensorConfig(SH2_GYROSCOPE_CALIBRATED, &config);

/* 启用磁力计 */
sh2_setSensorConfig(SH2_MAGNETIC_FIELD_CALIBRATED, &config);

/* 启用旋转矢量 */
sh2_setSensorConfig(SH2_ROTATION_VECTOR, &config);

/* 启用线性加速度 */
sh2_setSensorConfig(SH2_LINEAR_ACCELERATION, &config);

/* 启用重力 */
sh2_setSensorConfig(SH2_GRAVITY, &config);

9.3 数据读取

/* 主循环 */
while (1) {
    /* 服务 SH2 设备(必须定期调用) */
    sh2_demo_service();  /* 内部调用 sh2_service() */

    /* 读取加速度计 */
    sh2_SensorValue_t data;
    if (sh2_demo_get_sensor_data(SH2_ACCELEROMETER, &data) == 0) {
        LOG_INF("ACC (m/s^2): X=%.3f Y=%.3f Z=%.3f",
                data.un.accelerometer.x,
                data.un.accelerometer.y,
                data.un.accelerometer.z);
    }

    /* 读取陀螺仪 */
    if (sh2_demo_get_sensor_data(SH2_GYROSCOPE_CALIBRATED, &data) == 0) {
        LOG_INF("GYR (rad/s): X=%.3f Y=%.3f Z=%.3f",
                data.un.gyroscope.x,
                data.un.gyroscope.y,
                data.un.gyroscope.z);
    }

    /* 读取旋转矢量 */
    if (sh2_demo_get_sensor_data(SH2_ROTATION_VECTOR, &data) == 0) {
        LOG_INF("ARVR_GRV: i=%.3f j=%.3f k=%.3f real=%.3f",
                data.un.rotationVector.i,
                data.un.rotationVector.j,
                data.un.rotationVector.k,
                data.un.rotationVector.real);
    }

    k_sleep(K_MSEC(10));
}

9.4 支持的传感器类型

传感器 ID 数据类型 单位
SH2_ACCELEROMETER sh2_Accelerometer_t m/s^2
SH2_LINEAR_ACCELERATION sh2_Accelerometer_t m/s^2
SH2_GRAVITY sh2_Accelerometer_t m/s^2
SH2_GYROSCOPE_CALIBRATED sh2_Gyroscope_t rad/s
SH2_MAGNETIC_FIELD_CALIBRATED sh2_MagneticField_t uTesla
SH2_ROTATION_VECTOR sh2_RotationVectorWAcc_t 四元数
SH2_GAME_ROTATION_VECTOR sh2_RotationVector_t 四元数
SH2_PRESSURE sh2_Pressure_t hPa
SH2_AMBIENT_LIGHT sh2_AmbientLight_t lux
SH2_HUMIDITY sh2_Humidity_t %
SH2_PROXIMITY sh2_Proximity_t cm
SH2_TEMPERATURE sh2_Temperature_t C
SH2_STEP_COUNTER sh2_StepCounter_t steps
SH2_STABILITY_CLASSIFIER sh2_StabilityClassifier_t enum

10. 移植步骤清单

步骤 1:获取官方库

  1. 从 Hillcrest 获取 SH2 传感器库
  2. src/inc/ 复制到 drv/bno080/

步骤 2:实现 HAL 层

  1. 创建 platform/sh2_spi_hal.c
  2. 实现 sh2_Hal_t 接口:
    • open: 初始化 SPI、GPIO,执行复位序列
    • close: 关闭 SPI,进入复位
    • read: 检查 data_ready 标志,执行同步 SPI 读取
    • write: 拉低 PS0 唤醒传感器,执行同步 SPI 写入
    • getTimeUs: 返回微秒时间戳
  3. 实现 INT 中断回调(仅设置 data_ready 标志)

步骤 3:实现应用封装层

  1. 创建 platform/sh2_demo.c
  2. 封装 sh2_demo_init()
    • 初始化 HAL
    • 调用 sh2_open()
    • 设置传感器回调
    • 配置传感器
  3. 封装 sh2_demo_service():调用 sh2_service()
  4. 封装 sh2_demo_get_sensor_data():从缓存读取数据

步骤 4:创建设备树绑定

  1. 创建 dts/bindings/sensor/bosch,bno080.yaml
  2. 定义 compatible = "bosch,bno080"
  3. 添加 int-gpiosreset-gpioscs-gpiosps0-gpiosps1-gpiosreset-delay-usstart-delay-us 属性

步骤 5:编写设备树覆盖

  1. boards/xxx.overlay 中添加 BNO080 节点
  2. 配置 SPI 控制器和引脚
  3. 确保 CS 使用 GPIO(不使用硬件 SSL)

步骤 6:配置 Kconfig 和 CMake

  1. Kconfig 中添加 ENABLE_BNO080
  2. CMakeLists.txt 中条件编译 BNO080 源文件

步骤 7:编写应用层代码

  1. 参考 sh2-demo 示例编写 example_bno080.c
  2. 调用 sh2_demo_init() 初始化
  3. 在主循环中调用 sh2_demo_service()sh2_demo_get_sensor_data()

11. 成果展示

converted2.gif

converted1.gif


12. 常见问题与解决方案

12.1 SPI 通信失败(MISO 返回 0xFF)

可能原因:

  1. CS 引脚冲突 — 硬件 SSL 与 GPIO CS 配置冲突
  2. PS0/PS1 未拉高 — BNO080 未进入 SPI 模式
  3. SPI 模式错误 — CPOL/CPHA 配置不匹配
  4. 引脚接线错误 — MISO/MOSI 接反

解决方案:

  1. 检查 pinctrl 配置,确保使用 spi1_default
  2. 确认 PS0 和 PS1 引脚已拉高到 3.3V
  3. 尝试不同的 SPI 模式(Mode 0 或 Mode 3)
  4. 使用逻辑分析仪检查 CS、CLK、MISO 信号

12.2 初始化超时

可能原因:

  1. 硬件接线错误 — 电源、地、复位引脚未正确连接
  2. PS0/PS1 配置错误 — BNO080 未进入预期模式
  3. 复位序列不正确 — 复位时间不足

解决方案:

  1. 检查硬件接线,特别是 VCC、GND、RST
  2. 确认 PS0/PS1 电平符合预期模式(SPI: PS1=1, PS0=1)
  3. 确保复位延时足够(RESET_DELAY_US = 10ms, START_DELAY_US = 2s)

12.3 INT 中断不触发

可能原因:

  1. GPIO 中断配置错误 — 引脚不支持外部中断
  2. 中断优先级问题 — 优先级设置不当
  3. data_ready 标志未正确设置 — 中断回调未注册

解决方案:

  1. 查阅 MCU 数据手册,确认引脚支持外部中断
  2. 检查 overlay 中 port_irq 配置
  3. 确保 INT 中断回调中正确设置 data_ready = true

12.4 传感器数据不更新

可能原因:

  1. sh2_service() 未定期调用 — 数据未从 HAL 读取
  2. 传感器未启用 — 未调用 sh2_setSensorConfig()
  3. 回调未注册 — 未调用 sh2_setSensorCallback()

解决方案:

  1. 确保主循环中调用 sh2_demo_service()(建议间隔 10ms)
  2. 检查 sh2_demo_init() 中是否启用了目标传感器
  3. 检查回调注册是否成功

12.5 校准数据丢失

可能原因:

  • BNO080 断电后动态校准数据丢失

解决方案:

/* 保存校准数据到 flash */
sh2_saveDcdNow();

/* 或启用自动保存 */
sh2_setDcdAutoSave(true);

12.6 只有加速度计输出数据

现象:

  • 只有加速度计 (ACC) 数据正常输出
  • 陀螺仪、磁力计、旋转矢量等传感器无数据

可能原因:

  1. SH2 库 bugsh2.csetSensorConfigStart() 函数使用了错误的字段名
  2. 传感器配置未正确发送 — 配置命令发送到错误的内存地址

解决方案: 检查并修复 drv/bno080/src/sh2.c 第 977 行:

// 错误代码(原始 SH2 库)
sh2_SensorConfig_t *pConfig = pSh2->opData.getSensorConfig.pConfig;

// 正确代码(修复后)
sh2_SensorConfig_t *pConfig = pSh2->opData.setSensorConfig.pConfig;

根本原因分析:

  • opData 是一个联合体,包含 getSensorConfigsetSensorConfig 两个结构
  • 原始代码在 setSensorConfigStart() 中错误地使用了 getSensorConfig.pConfig
  • 导致配置指针指向错误位置,传感器配置命令未正确发送
  • 加速度计是 BNO080 的默认传感器,不需要配置就能输出数据
  • 其他传感器需要正确的配置命令才能启用

12.7 传感器初始化时间过长

现象:

  • 初始化后立即读取数据,部分传感器无输出
  • 需要等待一段时间后才能看到所有传感器数据

可能原因:

  • BNO080 传感器初始化需要时间
  • 加速度计最先就绪,陀螺仪、磁力计需要更长时间
  • 融合传感器(旋转矢量、线性加速度、重力)依赖基础传感器

解决方案: 使用 sh2_demo_wait_for_sensors_ready() 等待传感器就绪:

/* 初始化 SH2 Demo */
int ret = sh2_demo_init();
if (ret) {
    LOG_ERR("SH2 Demo 初始化失败: %d", ret);
    return ret;
}

/* 等待传感器就绪(默认超时 5 秒) */
ret = sh2_demo_wait_for_sensors_ready(0);
if (ret < 0) {
    LOG_ERR("等待传感器就绪失败: %d", ret);
    return ret;
}

LOG_INF("传感器就绪,开始数据采集");

/* 主循环 */
while (1) {
    sh2_demo_service();
    /* 读取传感器数据 */
}

函数说明:

  • sh2_demo_wait_for_sensors_ready(timeout_ms):等待所有启用的传感器就绪
  • 参数 timeout_ms:超时时间(毫秒),0 表示使用默认超时(5000ms)
  • 返回值:就绪的传感器数量,负值表示错误
  • 函数会输出日志,显示哪些传感器已就绪,哪些未就绪

提供移植驱动库,后续实现完整项目整体提交
upload 附件:bno080.zip

您需要登录后才可以回帖 登录 | 注册

本版积分规则

6

主题

11

帖子

1

粉丝
快速回复 在线客服 返回列表 返回顶部
0