Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# BMI270 to BMI220 Migration Summary

## Background

The TUYA_T5AI_PIXEL board ships with a **BMI220** IMU (chip_id = `0x26`), not the BMI270 (chip_id = `0x24`). The original BMI270 driver failed during chip_id validation. Even when that check was bypassed, the BMI270 config firmware is incompatible with BMI220, causing all sensor outputs to read zero.

## Root Cause

BMI2xx sensors require an **8192-byte config firmware upload** after power-on for the internal micro-engine to function. Each variant needs its own firmware — they are not interchangeable:

| Chip | Chip ID | Config Array Name | Firmware Source |
|--------|---------|--------------------------|-----------------|
| BMI270 | 0x24 | `bmi270_config_file` | Bosch BMI270 SDK |
| BMI220 | 0x26 | `bmi260_config_file` | ChromeOS EC `third_party/bmi220` (v2.47.1) |

Uploading the wrong firmware results in `INTERNAL_STATUS` register (0x21) returning `0x00` (not initialized), with accelerometer and gyroscope outputs stuck at zero.

## Changes

### 1. New Files

| File | Description |
|------|-------------|
| `boards/T5AI/TUYA_T5AI_PIXEL/board_bmi220_api.h` | BMI220 driver header — `bmi220_dev_t`, `bmi220_sensor_data_t` structs and API |
| `boards/T5AI/TUYA_T5AI_PIXEL/board_bmi220_api.c` | BMI220 driver implementation using Bosch BMI2 library with `bmi260_config_file` |
| `src/peripherals/imu/bmi220/bmi260_config_file.c` | BMI220-specific config firmware (8192 bytes), array `bmi260_config_file[]` |

### 2. Modified Files

#### `apps/tuya_t5_pixel/tuya_t5_pixel_demo/src/tuya_main.c`

- Added `#include "board_bmi220_api.h"`
- Added IMU abstraction layer: `imu_type_t` enum (`IMU_TYPE_NONE` / `IMU_TYPE_BMI220` / `IMU_TYPE_BMI270`)
- Added unified `imu_read_data()` and `imu_is_ready()` interface
- `user_main()` tries BMI220 first, falls back to BMI270
- `sand_update_physics()` uses `imu_read_data()` instead of direct BMI270 calls

#### `src/peripherals/imu/Kconfig`

Added `ENABLE_IMU_BMI220` config option.

#### `boards/T5AI/TUYA_T5AI_PIXEL/Kconfig`

Added `select ENABLE_IMU_BMI220` alongside `select ENABLE_IMU_BMI270`.

#### `src/peripherals/imu/CMakeLists.txt`

Added BMI220 source directory glob under `CONFIG_ENABLE_IMU_BMI220`.

## Architecture

```
tuya_main.c
|
|-- imu_read_data() / imu_is_ready() <-- unified abstraction
| |
| |-- g_imu_type == IMU_TYPE_BMI220 -> board_bmi220_read_data()
| |-- g_imu_type == IMU_TYPE_BMI270 -> board_bmi270_read_data()
|
|-- user_main()
|-- board_bmi220_register() <-- try BMI220 first
|-- board_bmi270_register() <-- fallback to BMI270
```

The BMI270 driver code is fully preserved. Both drivers initialize independently via separate interfaces. At runtime, `g_imu_type` selects the active driver.

### Why bmi270_* API functions appear in the BMI220 driver

`bmi270_get_sensor_config()`, `bmi270_set_sensor_config()`, and `bmi270_sensor_enable()` operate on generic BMI2 registers (ACC_CONF, GYR_CONF, POWER_CTRL) that are identical across BMI220/BMI260/BMI270. This is safe and intentional — these are not BMI270-specific functions despite their naming.

## BMI220 Driver Configuration

| Parameter | Value |
|-----------|-------|
| I2C Port | `TUYA_I2C_NUM_0` |
| I2C Address | `0x68` (SDO = GND) |
| I2C Speed | 400 kHz |
| SCL / SDA | GPIO20 / GPIO21 |
| Accelerometer | 16G range, 200Hz ODR |
| Gyroscope | 2000 dps range, 200Hz ODR |

## Verification

```
IMU data: acc(7.32, -5.68, -3.85) gyr(-7.93, 9.58, 61.40)
IMU data: acc(7.19, -5.01, -4.19) gyr(-2.08, 2.38, 0.18)
IMU data: acc(6.57, -3.36, -3.98) gyr(19.04, -80.81, -57.80)
```

Accelerometer and gyroscope data output correctly. Sand physics mode responds to gravity direction as expected.
130 changes: 108 additions & 22 deletions apps/tuya_t5_pixel/tuya_t5_pixel_demo/src/tuya_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "board_com_api.h"
#include "board_pixel_api.h"
#include "board_buzzer_api.h"
#include "board_bmi220_api.h"
#include "board_bmi270_api.h"
#include "tdl_button_manage.h"
#include <string.h>
Expand Down Expand Up @@ -70,9 +71,69 @@ typedef struct {

static sand_particle_t g_sand_particles[MAX_SAND_PARTICLES];
static uint32_t g_last_sand_spawn_time = 0;
/* IMU sensor abstraction: supports BMI220 (default) and BMI270 */
typedef enum {
IMU_TYPE_NONE = 0,
IMU_TYPE_BMI220,
IMU_TYPE_BMI270,
} imu_type_t;

typedef struct {
float acc_x, acc_y, acc_z;
float gyr_x, gyr_y, gyr_z;
} imu_sensor_data_t;

static imu_type_t g_imu_type = IMU_TYPE_NONE;
static bmi220_dev_t *g_bmi220_dev = NULL;
static bmi270_dev_t *g_bmi270_dev = NULL;
static bool g_sand_initialized = false;

/**
* @brief Read IMU sensor data (works with either BMI220 or BMI270)
*/
static OPERATE_RET imu_read_data(imu_sensor_data_t *data)
{
if (!data) {
return OPRT_INVALID_PARM;
}

if (g_imu_type == IMU_TYPE_BMI220 && g_bmi220_dev != NULL && board_bmi220_is_ready(g_bmi220_dev)) {
bmi220_sensor_data_t raw = {0};
OPERATE_RET ret = board_bmi220_read_data(g_bmi220_dev, &raw);
if (ret == OPRT_OK) {
data->acc_x = raw.acc_x;
data->acc_y = raw.acc_y;
data->acc_z = raw.acc_z;
data->gyr_x = raw.gyr_x;
data->gyr_y = raw.gyr_y;
data->gyr_z = raw.gyr_z;
}
return ret;
} else if (g_imu_type == IMU_TYPE_BMI270 && g_bmi270_dev != NULL && board_bmi270_is_ready(g_bmi270_dev)) {
bmi270_sensor_data_t raw = {0};
OPERATE_RET ret = board_bmi270_read_data(g_bmi270_dev, &raw);
if (ret == OPRT_OK) {
data->acc_x = raw.acc_x;
data->acc_y = raw.acc_y;
data->acc_z = raw.acc_z;
data->gyr_x = raw.gyr_x;
data->gyr_y = raw.gyr_y;
data->gyr_z = raw.gyr_z;
}
return ret;
}

return OPRT_COM_ERROR;
}

/**
* @brief Check if any IMU sensor is ready
*/
static bool imu_is_ready(void)
{
return g_imu_type != IMU_TYPE_NONE;
}

/***********************************************************
********************function declaration********************
***********************************************************/
Expand Down Expand Up @@ -965,23 +1026,22 @@ static void sand_init_particle(sand_particle_t *particle)
}

/**
* @brief Update sand particle physics based on BMI270 sensor data
* @brief Update sand particle physics based on IMU sensor data
*/
static void sand_update_physics(void)
{
bmi270_sensor_data_t sensor_data = {0};
imu_sensor_data_t sensor_data = {0};
float acc_x = 0.0f, acc_y = 0.0f;
float gyr_x = 0.0f, gyr_y = 0.0f;

// Read sensor data if available
if (g_bmi270_dev != NULL && board_bmi270_is_ready(g_bmi270_dev)) {
if (board_bmi270_read_data(g_bmi270_dev, &sensor_data) == OPRT_OK) {
if (imu_is_ready()) {
OPERATE_RET imu_ret = imu_read_data(&sensor_data);
if (imu_ret == OPRT_OK) {
acc_x = sensor_data.acc_x;
acc_y = sensor_data.acc_y;
// acc_z = sensor_data.acc_z;
gyr_x = sensor_data.gyr_x;
gyr_y = sensor_data.gyr_y;
// gyr_z = sensor_data.gyr_z;
}
}

Expand Down Expand Up @@ -1441,28 +1501,54 @@ static void user_main(void)
}
PR_NOTICE("Hardware initialized");

// Initialize BMI270 sensor
g_bmi270_dev = board_bmi270_get_handle();
if (g_bmi270_dev != NULL) {
PR_NOTICE("BMI270 sensor handle obtained");
// Wait a bit for hardware registration to complete
tal_system_sleep(200);

// Check if sensor is ready
if (!board_bmi270_is_ready(g_bmi270_dev)) {
PR_NOTICE("Initializing BMI270 sensor...");
rt = board_bmi270_init(g_bmi270_dev);
// Initialize IMU sensor: try BMI220 first (no firmware upload), fallback to BMI270
PR_NOTICE("Initializing IMU sensor...");
tal_system_sleep(200);

// Try BMI220 first (direct register access, no firmware upload needed)
g_bmi220_dev = board_bmi220_get_handle();
if (g_bmi220_dev != NULL) {
PR_NOTICE("Trying BMI220 sensor...");
if (!board_bmi220_is_ready(g_bmi220_dev)) {
rt = board_bmi220_init(g_bmi220_dev);
if (OPRT_OK == rt) {
PR_NOTICE("BMI270 sensor initialized successfully");
g_imu_type = IMU_TYPE_BMI220;
PR_NOTICE("BMI220 sensor initialized successfully");
} else {
PR_WARN("BMI270 sensor initialization failed: %d (will continue without sensor)", rt);
g_bmi270_dev = NULL;
PR_WARN("BMI220 init failed: %d, trying BMI270...", rt);
g_bmi220_dev = NULL;
}
} else {
PR_NOTICE("BMI270 sensor already initialized");
g_imu_type = IMU_TYPE_BMI220;
PR_NOTICE("BMI220 sensor already initialized");
}
}

// Fallback to BMI270 if BMI220 failed
if (g_imu_type == IMU_TYPE_NONE) {
g_bmi270_dev = board_bmi270_get_handle();
if (g_bmi270_dev != NULL) {
PR_NOTICE("Trying BMI270 sensor...");
if (!board_bmi270_is_ready(g_bmi270_dev)) {
rt = board_bmi270_init(g_bmi270_dev);
if (OPRT_OK == rt) {
g_imu_type = IMU_TYPE_BMI270;
PR_NOTICE("BMI270 sensor initialized successfully");
} else {
PR_WARN("BMI270 init failed: %d (continuing without IMU)", rt);
g_bmi270_dev = NULL;
}
} else {
g_imu_type = IMU_TYPE_BMI270;
PR_NOTICE("BMI270 sensor already initialized");
}
}
}

if (g_imu_type == IMU_TYPE_NONE) {
PR_WARN("No IMU sensor available (continuing without sensor)");
} else {
PR_WARN("BMI270 sensor not available (will continue without sensor)");
PR_NOTICE("IMU active: %s", g_imu_type == IMU_TYPE_BMI220 ? "BMI220" : "BMI270");
}

// Initialize buzzer
Expand Down
1 change: 1 addition & 0 deletions boards/T5AI/TUYA_T5AI_PIXEL/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ config BOARD_CONFIG
select ENABLE_SPI
select ENABLE_LEDS_PIXEL
select ENABLE_IMU
select ENABLE_IMU_BMI220
select ENABLE_IMU_BMI270

Loading
Loading