M5Unit-IMU 0.0.2 git rev:e5b00aa
Loading...
Searching...
No Matches
unit_bmi270.hpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD
3 *
4 * SPDX-License-Identifier: MIT
5 */
10#ifndef M5_UNIT_IMU_UNIT_UNIT_BMI270_HPP
11#define M5_UNIT_IMU_UNIT_UNIT_BMI270_HPP
12
13#include <M5UnitComponent.hpp>
14#include <m5_utility/container/circular_buffer.hpp>
15#include <memory>
16#include "unit_bmi270_data.hpp"
17#include "unit_bmm150_data.hpp"
19
20namespace m5 {
21namespace unit {
22
32class UnitBMI270 : public Component,
33 public PeriodicMeasurementAdapter<UnitBMI270, bmi270::Data>,
35 M5_UNIT_COMPONENT_HPP_BUILDER(UnitBMI270, 0x69);
36
37public:
47 struct config_t {
49 bool start_periodic{true};
51 bmi270::AccelRange accel_range{bmi270::AccelRange::G8};
53 bmi270::GyroRange gyro_range{bmi270::GyroRange::DPS2000};
56 bmi270::AccelODR accel_odr{bmi270::AccelODR::Rate200Hz};
60 bmi270::GyroODR gyro_odr{bmi270::GyroODR::Rate200Hz};
63 bmi270::FilterPerf accel_filter_perf{bmi270::FilterPerf::Performance};
67 bmi270::AccelBwp accel_bwp{bmi270::AccelBwp::NormAvg4};
69 bmi270::FilterPerf gyro_filter_perf{bmi270::FilterPerf::Performance};
71 bmi270::GyroBwp gyro_bwp{bmi270::GyroBwp::Norm};
72 };
73
76 explicit UnitBMI270(const uint8_t addr = DEFAULT_ADDRESS);
78 virtual ~UnitBMI270()
79 {
80 }
81
83 virtual bool begin() override;
86 virtual void update(const bool force = false) override;
87
93 {
94 return _cfg;
95 }
98 void config(const config_t& cfg)
99 {
100 _cfg = cfg;
101 }
103
110 bool writeAccelRange(const bmi270::AccelRange range);
115 bool writeGyroRange(const bmi270::GyroRange range);
123 bool readGyroRange(bmi270::GyroRange& range);
125
134 bool writeAccelODR(const bmi270::AccelODR odr);
142 bool writeGyroODR(const bmi270::GyroODR odr);
150 bool readGyroODR(bmi270::GyroODR& odr);
152
161 bool softReset();
163
166
181 bool startPeriodicMeasurement(const bmi270::AccelRange accel_range, const bmi270::GyroRange gyro_range,
182 const bmi270::AccelODR accel_odr, const bmi270::GyroODR gyro_odr,
183 const bmi270::FilterPerf accel_filter_perf, const bmi270::AccelBwp accel_bwp,
184 const bmi270::FilterPerf gyro_filter_perf, const bmi270::GyroBwp gyro_bwp);
192
195
204 bool sleep() override;
212 bool wakeup() override;
214
220 virtual m5::imu::Capability imuSpec() const override
221 {
223 return Capability::Accel | Capability::Gyro | Capability::Temp;
224 }
231 virtual void imuConvertParam(m5::imu::imu_convert_param_t& out) const override;
233
234protected:
235 virtual bool start_periodic_measurement(const bmi270::AccelRange accel_range, const bmi270::GyroRange gyro_range,
236 const bmi270::AccelODR accel_odr, const bmi270::GyroODR gyro_odr,
237 const bmi270::FilterPerf accel_filter_perf,
238 const bmi270::AccelBwp accel_bwp, const bmi270::FilterPerf gyro_filter_perf,
239 const bmi270::GyroBwp gyro_bwp);
240 bool start_periodic_measurement();
241 bool stop_periodic_measurement();
242 bool read_measurement(bmi270::Data& d);
243 // Read one sample's sensor registers into d. Split out of read_measurement() so a subclass
244 // can widen the burst without repeating the temperature handling that follows it (see
245 // UnitBMI270AuxBMM150).
246 virtual bool read_sensor_burst(bmi270::Data& d);
247 bool write_register8_verified(const uint8_t reg, const uint8_t val);
248 // readRegister8() without its "Failed to write" log, for the reads whose failure is expected:
249 // the dummy transaction that brings the I2C interface up after power-on, the two polls that wait
250 // out the ~2ms reboot after a soft reset, and the verified-write readback that is retried
251 bool read_register8_quiet(const uint8_t reg, uint8_t& out);
252 // Last resort for a CHIP_ID that came back wrong: soft reset the device and read it again.
253 // Call only once the address has acknowledged, and only with the value it answered: the reset
254 // is skipped unless that value is one the real CHIP_ID could have decayed into on a contended
255 // bus (see the .cpp), so a different chip living at this address is never written to. Reaches
256 // only the form where the chip still answers: a bus being held low takes no writes at all
257 bool recover_wedged_interface(const uint8_t id);
258 // Uploads the 8192 byte configuration blob (unit_bmi270_config.inl) to REG_INIT_DATA,
259 // split into chunks that fit within the I2C write buffer (see .cpp for chunk size
260 // rationale). Returns false (and logs) on the first failed chunk, unlike M5Unified's
261 // BMI270_Class::_upload_file() which always returns true regardless of write result.
262 bool upload_config();
263
264 M5_UNIT_COMPONENT_PERIODIC_MEASUREMENT_ADAPTER_HPP_BUILDER(UnitBMI270, bmi270::Data);
265
266private:
267 // Settings for begin(); never written to after that. What is currently on the device lives in
268 // the live-state members below, so config() keeps meaning "what was configured"
269 config_t _cfg{};
270 // Mirrors of the registers this driver can change at runtime. imuConvertParam() needs the range
271 // actually in effect (a stale factor would silently misreport every sample), and the no-argument
272 // startPeriodicMeasurement() resumes from these rather than reverting to _cfg
273 bmi270::AccelRange _accel_range{};
274 bmi270::GyroRange _gyro_range{};
275 bmi270::AccelODR _accel_odr{};
276 bmi270::GyroODR _gyro_odr{};
277 // Bandwidth and filter performance have no runtime setter of their own, but they share
278 // REG_ACC_CONF/REG_GYR_CONF with the rate: writeAccelODR() has to recompose the whole register,
279 // and taking these from _cfg there would silently apply a config() change the caller only meant
280 // to take effect at the next start
281 bmi270::FilterPerf _accel_filter_perf{};
282 bmi270::AccelBwp _accel_bwp{};
283 bmi270::FilterPerf _gyro_filter_perf{};
284 bmi270::GyroBwp _gyro_bwp{};
285 int16_t _last_temp{}; // Latest valid REG_TEMPERATURE_0 reading (register update is not per-sample)
286 uint32_t _pushed{}, _consumed{}; // readImuRaw pop counters (do not disturb latest()/oldest())
287 std::unique_ptr<m5::container::CircularBuffer<bmi270::Data>> _data{};
288};
289
290namespace bmi270 {
293constexpr uint32_t DEFAULT_STORED_SIZE{8};
294
295namespace command {
297// Register addresses (I2C addr 0x68 (SDO=GND) / 0x69 (SDO=VDDIO, M5 board default))
298constexpr uint8_t REG_DATA_0{0x04}; // AUX (mag) burst start: 20 bytes = mag(8)+accel(6)+gyro(6), see REG_DATA_8
299constexpr uint8_t REG_STATUS{0x03}; // bit2 = aux_man_op busy (named REG_STATUS, not STATUS: ESP-IDF's
300 // ets_sys.h has a global `typedef enum STATUS STATUS`)
301constexpr uint8_t REG_DATA_8{0x0C}; // 12 bytes: accel X/Y/Z, gyro X/Y/Z (little-endian)
302constexpr uint8_t REG_INT_STATUS_1{0x1D}; // bit7 acc_drdy, bit6 gyr_drdy, bit5 aux_drdy
303constexpr uint8_t REG_TEMPERATURE_0{0x22}; // 2 bytes, little-endian, 0x8000 = invalid
304constexpr uint8_t REG_INTERNAL_STATUS{0x21};
305constexpr uint8_t REG_ACC_CONF{0x40};
306constexpr uint8_t REG_ACC_RANGE{0x41};
307constexpr uint8_t REG_GYR_CONF{0x42};
308constexpr uint8_t REG_GYR_RANGE{0x43};
309constexpr uint8_t REG_AUX_DEV_ID{0x4B};
310// Poll rate and trigger-readout offset for the AUX target (see UnitBMI270AuxBMM150)
311constexpr uint8_t REG_AUX_CONF{0x44};
312constexpr uint8_t REG_AUX_IF_CONF{
313 0x4C}; // bit7 manual_en, bit6 fcu_write_en, bits[3:2] man_rd_burst, bits[1:0] aux_rd_burst
314constexpr uint8_t REG_AUX_RD_ADDR{0x4D}; // write triggers an AUX read transaction
315constexpr uint8_t REG_AUX_WR_ADDR{0x4E}; // write triggers an AUX write transaction (of REG_AUX_WR_DATA)
316constexpr uint8_t REG_AUX_WR_DATA{0x4F};
317constexpr uint8_t REG_INIT_CTRL{0x59};
318constexpr uint8_t REG_INIT_ADDR_0{0x5B}; // followed by INIT_ADDR_1 (0x5C)
319constexpr uint8_t REG_INIT_DATA{0x5E};
320constexpr uint8_t REG_IF_CONF{0x6B}; // bit5 aux_en
321constexpr uint8_t REG_PWR_CONF{0x7C}; // bit0 adv_power_save
322constexpr uint8_t REG_PWR_CTRL{0x7D}; // bit0 aux_en, bit1 gyr_en, bit2 acc_en, bit3 temp_en
323constexpr uint8_t REG_CMD{0x7E};
324constexpr uint8_t REG_CHIP_ID{0x00};
325
326// BMM150 registers, reachable only indirectly through the AUX bridge above (distinct address
327// space from this chip's own registers). Only UnitBMI270AuxBMM150 talks to them; they live here
328// because they are addressed through this chip's AUX registers
329constexpr uint8_t REG_BMM150_CHIP_ID{0x40};
330constexpr uint8_t REG_BMM150_DATA_X_LSB{0x42};
331constexpr uint8_t REG_BMM150_POWER_CONTROL{0x4B};
332constexpr uint8_t REG_BMM150_OP_MODE{0x4C};
333constexpr uint8_t REG_BMM150_REP_XY{0x51};
334constexpr uint8_t REG_BMM150_REP_Z{0x52};
336} // namespace command
337
339constexpr uint8_t CHIP_ID{0x24};
340
354template <class ReadReg>
355bool detectAddress(ReadReg read, uint8_t& addr)
356{
357 for (const uint8_t candidate : {static_cast<uint8_t>(0x68), static_cast<uint8_t>(0x69)}) {
358 uint8_t id{};
359 if (read(candidate, command::REG_CHIP_ID, id) && id == CHIP_ID) {
360 addr = candidate;
361 return true;
362 }
363 }
364 return false;
365}
366} // namespace bmi270
367
368} // namespace unit
369} // namespace m5
370#endif
Thin contract between measurement units and consumers (e.g. AHRS)
Definition imu_data_source.hpp:27
6-axis (accel + gyro) IMU unit. CoreS3/AtomS3R built-in
Definition unit_bmi270.hpp:34
virtual m5::imu::Capability readImuRaw(m5::imu::imu_raw_data_t &out) override
Read the latest raw IMU data.
Definition unit_bmi270.cpp:556
config_t config() const
Gets the configuration.
Definition unit_bmi270.hpp:92
bool readGyroRange(bmi270::GyroRange &range)
Read the gyroscope range from the device (REG_GYR_RANGE bits[2:0])
Definition unit_bmi270.cpp:354
virtual void imuConvertParam(m5::imu::imu_convert_param_t &out) const override
Gets the conversion parameters for raw IMU data.
Definition unit_bmi270.cpp:589
bool readAccelODR(bmi270::AccelODR &odr)
Read the accelerometer output data rate from the device (REG_ACC_CONF bits[3:0])
Definition unit_bmi270.cpp:397
bool readAccelRange(bmi270::AccelRange &range)
Read the accelerometer range from the device (REG_ACC_RANGE bits[1:0])
Definition unit_bmi270.cpp:344
bool writeGyroRange(const bmi270::GyroRange range)
Write the gyroscope range.
Definition unit_bmi270.cpp:331
virtual ~UnitBMI270()
Destructor.
Definition unit_bmi270.hpp:78
virtual bool begin() override
Begin the unit.
Definition unit_bmi270.cpp:81
bool writeAccelRange(const bmi270::AccelRange range)
Write the accelerometer range.
Definition unit_bmi270.cpp:318
bool sleep() override
Suspend the chip (REG_PWR_CTRL off, REG_PWR_CONF adv_power_save)
Definition unit_bmi270.cpp:288
virtual void update(const bool force=false) override
Update the unit.
Definition unit_bmi270.cpp:199
virtual m5::imu::Capability imuSpec() const override
Capability of this unit.
Definition unit_bmi270.hpp:220
void config(const config_t &cfg)
Set the configuration.
Definition unit_bmi270.hpp:98
UnitBMI270(const uint8_t addr=DEFAULT_ADDRESS)
Construct with the I2C address the chip will answer on.
Definition unit_bmi270.cpp:72
bool writeGyroODR(const bmi270::GyroODR odr)
Write the gyroscope output data rate.
Definition unit_bmi270.cpp:383
bool stopPeriodicMeasurement()
Stop periodic measurement.
Definition unit_bmi270.cpp:283
bool readGyroODR(bmi270::GyroODR &odr)
Read the gyroscope output data rate from the device (REG_GYR_CONF bits[3:0])
Definition unit_bmi270.cpp:412
bool softReset()
Soft reset (REG_CMD CMD_SOFTRESET), then re-run the mandatory post-reset initialization (config blob ...
Definition unit_bmi270.cpp:128
bool writeAccelODR(const bmi270::AccelODR odr)
Write the accelerometer output data rate.
Definition unit_bmi270.cpp:368
bool wakeup() override
Resume the chip from sleep()
Definition unit_bmi270.cpp:303
bool startPeriodicMeasurement()
Start periodic measurement in the current settings.
Definition unit_bmi270.cpp:278
Public interface for IMU data providers.
Capability
Capability of the IMU data source (bitwise combinable)
Definition imu_types.hpp:22
Top level namespace of M5Stack.
Definition ahrs_controller.cpp:26
Unit-related namespace.
Raw to physical unit conversion coefficients.
Definition imu_types.hpp:69
Unified raw data (chip-native axes, no remap applied)
Definition imu_types.hpp:57
Settings for begin()
Definition unit_bmi270.hpp:47
bmi270::AccelBwp accel_bwp
Definition unit_bmi270.hpp:67
bmi270::FilterPerf gyro_filter_perf
Gyroscope filter performance mode.
Definition unit_bmi270.hpp:69
bmi270::FilterPerf accel_filter_perf
Definition unit_bmi270.hpp:63
bmi270::AccelODR accel_odr
Definition unit_bmi270.hpp:56
bmi270::AccelRange accel_range
Accelerometer range.
Definition unit_bmi270.hpp:51
bmi270::GyroODR gyro_odr
Definition unit_bmi270.hpp:60
bmi270::GyroRange gyro_range
Gyroscope range.
Definition unit_bmi270.hpp:53
bool start_periodic
Start periodic measurement on begin()?
Definition unit_bmi270.hpp:49
bmi270::GyroBwp gyro_bwp
Gyroscope bandwidth parameter (the chip's power-on default)
Definition unit_bmi270.hpp:71
One measurement. Accel/gyro are always 12 bytes, little-endian (register 0x0C when the AUX/BMM150 bri...
Definition unit_bmi270_data.hpp:179
constexpr uint8_t CHIP_ID
BMI270 chip identifier (value read from command::REG_CHIP_ID)
Definition unit_bmi270.hpp:339
bool detectAddress(ReadReg read, uint8_t &addr)
Detect which I2C address a built-in BMI270 uses on a bus (0x68 or 0x69, board-dependent)
Definition unit_bmi270.hpp:355
BMI270 setting enums and measurement data (framework independent)
GyroBwp
Gyroscope bandwidth parameter (REG_GYR_CONF register 0x42, bits[5:4])
Definition unit_bmi270_data.hpp:110
AccelBwp
Accelerometer bandwidth parameter (REG_ACC_CONF register 0x40, bits[6:4])
Definition unit_bmi270_data.hpp:93
FilterPerf
Filter performance mode (REG_ACC_CONF bit7 / REG_GYR_CONF bit7)
Definition unit_bmi270_data.hpp:81
GyroODR
Gyroscope output data rate (REG_GYR_CONF register 0x42, bits[3:0])
Definition unit_bmi270_data.hpp:65
GyroRange
Gyroscope full scale range (REG_GYR_RANGE register 0x43)
Definition unit_bmi270_data.hpp:38
AccelRange
Accelerometer full scale range (REG_ACC_RANGE register 0x41)
Definition unit_bmi270_data.hpp:25
AccelODR
Accelerometer output data rate (REG_ACC_CONF register 0x40, bits[3:0])
Definition unit_bmi270_data.hpp:50
BMM150 setting enums and measurement data (framework independent)