Skip to content

Commit 5030ab6

Browse files
张林盛claude
andcommitted
feat(imu): add BMI220 sensor support for TUYA_T5AI_PIXEL board
The PIXEL board ships with a BMI220 (chip_id 0x26) instead of BMI270 (chip_id 0x24). BMI220 requires its own 8192-byte config firmware (sourced from ChromeOS EC v2.47.1) — the BMI270 firmware is incompatible. Changes: - Add BMI220 driver (board_bmi220_api.c/h) using Bosch BMI2 library - Add BMI220-specific config firmware (bmi260_config_file.c) - Patch bmi2.c chip_id check to accept 0x26 - Add IMU abstraction layer in tuya_main.c (BMI220 preferred, BMI270 fallback) - Add ENABLE_IMU_BMI220 Kconfig option Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 0edaf50 commit 5030ab6

8 files changed

Lines changed: 1318 additions & 22 deletions

File tree

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# BMI270 到 BMI220 迁移总结
2+
3+
## 背景
4+
5+
TUYA_T5AI_PIXEL 开发板上实际搭载的 IMU 芯片为 **BMI220**(chip_id = `0x26`),而非 BMI270(chip_id = `0x24`)。原 BMI270 驱动初始化时 chip_id 校验失败,即使绕过校验,BMI270 的配置固件与 BMI220 不兼容,导致传感器数据全部为零。
6+
7+
## 问题根因
8+
9+
BMI2xx 系列传感器在上电后必须上传一份 **8192 字节的配置固件(config file)**,芯片内部微引擎才能正常工作。每个型号的固件不通用:
10+
11+
| 芯片 | Chip ID | 配置固件数组名 | 固件来源 |
12+
|----------|---------|--------------------------|----------|
13+
| BMI270 | 0x24 | `bmi270_config_file` | Bosch BMI270 SDK |
14+
| BMI220 | 0x26 | `bmi260_config_file` | ChromeOS EC `third_party/bmi220` (v2.47.1) |
15+
16+
上传错误的固件后,`INTERNAL_STATUS` 寄存器(0x21)返回 `0x00`(未初始化),加速度计和陀螺仪输出全为零。
17+
18+
## 修改清单
19+
20+
### 1. 新增文件
21+
22+
| 文件 | 说明 |
23+
|------|------|
24+
| `boards/T5AI/TUYA_T5AI_PIXEL/board_bmi220_api.h` | BMI220 驱动头文件,定义 `bmi220_dev_t``bmi220_sensor_data_t` 结构体及 API |
25+
| `boards/T5AI/TUYA_T5AI_PIXEL/board_bmi220_api.c` | BMI220 驱动实现,基于 Bosch BMI2 库,使用 `bmi260_config_file` 固件 |
26+
| `src/peripherals/imu/bmi270/bmi260_config_file.c` | BMI220 专用配置固件(8192 字节),数组名 `bmi260_config_file[]` |
27+
28+
### 2. 修改文件
29+
30+
#### `src/peripherals/imu/bmi270/bmi2.c`(第 1907 行)
31+
32+
```c
33+
// 修改前
34+
if (chip_id == dev->chip_id)
35+
36+
// 修改后
37+
if (chip_id == dev->chip_id || chip_id == 0x26)
38+
```
39+
40+
> 允许 BMI220 的 chip_id `0x26` 通过 Bosch BMI2 库的校验。
41+
42+
#### `boards/T5AI/TUYA_T5AI_PIXEL/board_bmi220_api.c`
43+
44+
```c
45+
// 修改前
46+
extern const uint8_t bmi270_config_file[];
47+
bmi2_dev_220.config_file_ptr = bmi270_config_file;
48+
49+
// 修改后
50+
extern const uint8_t bmi260_config_file[];
51+
bmi2_dev_220.config_file_ptr = bmi260_config_file;
52+
```
53+
54+
> 指向 BMI220 专用的配置固件。
55+
56+
#### `apps/tuya_t5_pixel/tuya_t5_pixel_demo/src/tuya_main.c`
57+
58+
- 新增 `#include "board_bmi220_api.h"`
59+
- 新增 IMU 抽象层:`imu_type_t` 枚举(`IMU_TYPE_NONE` / `IMU_TYPE_BMI220` / `IMU_TYPE_BMI270`
60+
- 新增 `imu_read_data()``imu_is_ready()` 统一接口
61+
- `user_main()` 中优先初始化 BMI220,失败则回退到 BMI270
62+
- `sand_update_physics()` 使用 `imu_read_data()` 替代直接调用 BMI270 API
63+
64+
#### `src/peripherals/imu/Kconfig`
65+
66+
新增 `ENABLE_IMU_BMI220` 配置选项。
67+
68+
#### `boards/T5AI/TUYA_T5AI_PIXEL/Kconfig`
69+
70+
新增 `select ENABLE_IMU_BMI220`,与 `ENABLE_IMU_BMI270` 同时启用。
71+
72+
## 架构设计
73+
74+
```
75+
tuya_main.c
76+
|
77+
|-- imu_read_data() / imu_is_ready() ← 统一抽象层
78+
| |
79+
| |-- g_imu_type == IMU_TYPE_BMI220 → board_bmi220_read_data()
80+
| |-- g_imu_type == IMU_TYPE_BMI270 → board_bmi270_read_data()
81+
|
82+
|-- user_main()
83+
|-- board_bmi220_register() ← 优先尝试 BMI220
84+
|-- board_bmi270_register() ← 回退 BMI270
85+
```
86+
87+
BMI270 原有代码完整保留,两个驱动通过不同接口独立初始化,运行时根据 `g_imu_type` 选择对应驱动读取数据。
88+
89+
## BMI220 驱动配置参数
90+
91+
| 参数 ||
92+
|------|----|
93+
| I2C 端口 | `TUYA_I2C_NUM_0` |
94+
| I2C 地址 | `0x68`(SDO = GND) |
95+
| I2C 速率 | 400 kHz |
96+
| SCL / SDA | GPIO20 / GPIO21 |
97+
| 加速度计 | 16G 量程,200Hz ODR |
98+
| 陀螺仪 | 2000 dps 量程,200Hz ODR |
99+
100+
## 验证结果
101+
102+
```
103+
IMU data: acc(7.32, -5.68, -3.85) gyr(-7.93, 9.58, 61.40)
104+
IMU data: acc(7.19, -5.01, -4.19) gyr(-2.08, 2.38, 0.18)
105+
IMU data: acc(6.57, -3.36, -3.98) gyr(19.04, -80.81, -57.80)
106+
```
107+
108+
加速度计和陀螺仪数据正常输出,沙子物理模式中重力方向响应正确。

apps/tuya_t5_pixel/tuya_t5_pixel_demo/src/tuya_main.c

Lines changed: 122 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "board_com_api.h"
55
#include "board_pixel_api.h"
66
#include "board_buzzer_api.h"
7+
#include "board_bmi220_api.h"
78
#include "board_bmi270_api.h"
89
#include "tdl_button_manage.h"
910
#include <string.h>
@@ -70,9 +71,69 @@ typedef struct {
7071

7172
static sand_particle_t g_sand_particles[MAX_SAND_PARTICLES];
7273
static uint32_t g_last_sand_spawn_time = 0;
74+
/* IMU sensor abstraction: supports BMI220 (default) and BMI270 */
75+
typedef enum {
76+
IMU_TYPE_NONE = 0,
77+
IMU_TYPE_BMI220,
78+
IMU_TYPE_BMI270,
79+
} imu_type_t;
80+
81+
typedef struct {
82+
float acc_x, acc_y, acc_z;
83+
float gyr_x, gyr_y, gyr_z;
84+
} imu_sensor_data_t;
85+
86+
static imu_type_t g_imu_type = IMU_TYPE_NONE;
87+
static bmi220_dev_t *g_bmi220_dev = NULL;
7388
static bmi270_dev_t *g_bmi270_dev = NULL;
7489
static bool g_sand_initialized = false;
7590

91+
/**
92+
* @brief Read IMU sensor data (works with either BMI220 or BMI270)
93+
*/
94+
static OPERATE_RET imu_read_data(imu_sensor_data_t *data)
95+
{
96+
if (!data) {
97+
return OPRT_INVALID_PARM;
98+
}
99+
100+
if (g_imu_type == IMU_TYPE_BMI220 && g_bmi220_dev != NULL && board_bmi220_is_ready(g_bmi220_dev)) {
101+
bmi220_sensor_data_t raw = {0};
102+
OPERATE_RET ret = board_bmi220_read_data(g_bmi220_dev, &raw);
103+
if (ret == OPRT_OK) {
104+
data->acc_x = raw.acc_x;
105+
data->acc_y = raw.acc_y;
106+
data->acc_z = raw.acc_z;
107+
data->gyr_x = raw.gyr_x;
108+
data->gyr_y = raw.gyr_y;
109+
data->gyr_z = raw.gyr_z;
110+
}
111+
return ret;
112+
} else if (g_imu_type == IMU_TYPE_BMI270 && g_bmi270_dev != NULL && board_bmi270_is_ready(g_bmi270_dev)) {
113+
bmi270_sensor_data_t raw = {0};
114+
OPERATE_RET ret = board_bmi270_read_data(g_bmi270_dev, &raw);
115+
if (ret == OPRT_OK) {
116+
data->acc_x = raw.acc_x;
117+
data->acc_y = raw.acc_y;
118+
data->acc_z = raw.acc_z;
119+
data->gyr_x = raw.gyr_x;
120+
data->gyr_y = raw.gyr_y;
121+
data->gyr_z = raw.gyr_z;
122+
}
123+
return ret;
124+
}
125+
126+
return OPRT_COM_ERROR;
127+
}
128+
129+
/**
130+
* @brief Check if any IMU sensor is ready
131+
*/
132+
static bool imu_is_ready(void)
133+
{
134+
return g_imu_type != IMU_TYPE_NONE;
135+
}
136+
76137
/***********************************************************
77138
********************function declaration********************
78139
***********************************************************/
@@ -965,23 +1026,38 @@ static void sand_init_particle(sand_particle_t *particle)
9651026
}
9661027

9671028
/**
968-
* @brief Update sand particle physics based on BMI270 sensor data
1029+
* @brief Update sand particle physics based on IMU sensor data
9691030
*/
9701031
static void sand_update_physics(void)
9711032
{
972-
bmi270_sensor_data_t sensor_data = {0};
1033+
imu_sensor_data_t sensor_data = {0};
9731034
float acc_x = 0.0f, acc_y = 0.0f;
9741035
float gyr_x = 0.0f, gyr_y = 0.0f;
9751036

9761037
// Read sensor data if available
977-
if (g_bmi270_dev != NULL && board_bmi270_is_ready(g_bmi270_dev)) {
978-
if (board_bmi270_read_data(g_bmi270_dev, &sensor_data) == OPRT_OK) {
1038+
static uint32_t imu_dbg_cnt = 0;
1039+
if (imu_is_ready()) {
1040+
OPERATE_RET imu_ret = imu_read_data(&sensor_data);
1041+
if (imu_ret == OPRT_OK) {
9791042
acc_x = sensor_data.acc_x;
9801043
acc_y = sensor_data.acc_y;
9811044
// acc_z = sensor_data.acc_z;
9821045
gyr_x = sensor_data.gyr_x;
9831046
gyr_y = sensor_data.gyr_y;
9841047
// gyr_z = sensor_data.gyr_z;
1048+
if (imu_dbg_cnt++ % 500 == 0) {
1049+
PR_NOTICE("IMU data: acc(%.2f, %.2f, %.2f) gyr(%.2f, %.2f, %.2f)",
1050+
sensor_data.acc_x, sensor_data.acc_y, sensor_data.acc_z,
1051+
sensor_data.gyr_x, sensor_data.gyr_y, sensor_data.gyr_z);
1052+
}
1053+
} else {
1054+
if (imu_dbg_cnt++ % 500 == 0) {
1055+
PR_ERR("IMU read failed: %d", imu_ret);
1056+
}
1057+
}
1058+
} else {
1059+
if (imu_dbg_cnt++ % 500 == 0) {
1060+
PR_WARN("IMU not ready, imu_type=%d", g_imu_type);
9851061
}
9861062
}
9871063

@@ -1441,28 +1517,54 @@ static void user_main(void)
14411517
}
14421518
PR_NOTICE("Hardware initialized");
14431519

1444-
// Initialize BMI270 sensor
1445-
g_bmi270_dev = board_bmi270_get_handle();
1446-
if (g_bmi270_dev != NULL) {
1447-
PR_NOTICE("BMI270 sensor handle obtained");
1448-
// Wait a bit for hardware registration to complete
1449-
tal_system_sleep(200);
1450-
1451-
// Check if sensor is ready
1452-
if (!board_bmi270_is_ready(g_bmi270_dev)) {
1453-
PR_NOTICE("Initializing BMI270 sensor...");
1454-
rt = board_bmi270_init(g_bmi270_dev);
1520+
// Initialize IMU sensor: try BMI220 first (no firmware upload), fallback to BMI270
1521+
PR_NOTICE("Initializing IMU sensor...");
1522+
tal_system_sleep(200);
1523+
1524+
// Try BMI220 first (direct register access, no firmware upload needed)
1525+
g_bmi220_dev = board_bmi220_get_handle();
1526+
if (g_bmi220_dev != NULL) {
1527+
PR_NOTICE("Trying BMI220 sensor...");
1528+
if (!board_bmi220_is_ready(g_bmi220_dev)) {
1529+
rt = board_bmi220_init(g_bmi220_dev);
14551530
if (OPRT_OK == rt) {
1456-
PR_NOTICE("BMI270 sensor initialized successfully");
1531+
g_imu_type = IMU_TYPE_BMI220;
1532+
PR_NOTICE("BMI220 sensor initialized successfully");
14571533
} else {
1458-
PR_WARN("BMI270 sensor initialization failed: %d (will continue without sensor)", rt);
1459-
g_bmi270_dev = NULL;
1534+
PR_WARN("BMI220 init failed: %d, trying BMI270...", rt);
1535+
g_bmi220_dev = NULL;
14601536
}
14611537
} else {
1462-
PR_NOTICE("BMI270 sensor already initialized");
1538+
g_imu_type = IMU_TYPE_BMI220;
1539+
PR_NOTICE("BMI220 sensor already initialized");
1540+
}
1541+
}
1542+
1543+
// Fallback to BMI270 if BMI220 failed
1544+
if (g_imu_type == IMU_TYPE_NONE) {
1545+
g_bmi270_dev = board_bmi270_get_handle();
1546+
if (g_bmi270_dev != NULL) {
1547+
PR_NOTICE("Trying BMI270 sensor...");
1548+
if (!board_bmi270_is_ready(g_bmi270_dev)) {
1549+
rt = board_bmi270_init(g_bmi270_dev);
1550+
if (OPRT_OK == rt) {
1551+
g_imu_type = IMU_TYPE_BMI270;
1552+
PR_NOTICE("BMI270 sensor initialized successfully");
1553+
} else {
1554+
PR_WARN("BMI270 init failed: %d (continuing without IMU)", rt);
1555+
g_bmi270_dev = NULL;
1556+
}
1557+
} else {
1558+
g_imu_type = IMU_TYPE_BMI270;
1559+
PR_NOTICE("BMI270 sensor already initialized");
1560+
}
14631561
}
1562+
}
1563+
1564+
if (g_imu_type == IMU_TYPE_NONE) {
1565+
PR_WARN("No IMU sensor available (continuing without sensor)");
14641566
} else {
1465-
PR_WARN("BMI270 sensor not available (will continue without sensor)");
1567+
PR_NOTICE("IMU active: %s", g_imu_type == IMU_TYPE_BMI220 ? "BMI220" : "BMI270");
14661568
}
14671569

14681570
// Initialize buzzer

boards/T5AI/TUYA_T5AI_PIXEL/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ config BOARD_CONFIG
1818
select ENABLE_SPI
1919
select ENABLE_LEDS_PIXEL
2020
select ENABLE_IMU
21+
select ENABLE_IMU_BMI220
2122
select ENABLE_IMU_BMI270
2223

0 commit comments

Comments
 (0)