背景
最近在倒腾RP2040,板卡上灯珠玩完了,就开始倒腾I2C接口,而玩I2C接口最好的方法就是跳线到I2C外设上把玩。刚好手头上有之前做项目剩下的BMI160的板子,就用这个板子玩I2C。
BMI160简介
BMI160是一款高性能的惯性测量单元(IMU),它集成了加速度计和陀螺仪,旨在提供稳定且精确的运动数据。以下是BMI160的一些详细特点:
高精度测量
加速度计:BMI160的加速度计具有极高的灵敏度,能够准确测量设备在三维空间中的加速度变化。无论是静止、匀速运动还是加速运动,它都能提供可靠的数据。
陀螺仪:陀螺仪部分采用了先进的角速度检测技术,能够实时监测设备的旋转和倾斜角度,为设备提供准确的姿态信息。
低功耗设计
智能电源管理:BMI160具备智能电源管理功能,可以根据应用需求动态调整工作模式,降低功耗。同时,它还支持休眠和唤醒功能,进一步延长了设备的使用时间。
低噪声设计:传感器内部的低噪声电路设计使得BMI160在保持高精度测量的同时,具有较低的功耗表现。
集成度高:
单一芯片解决方案:BMI160将加速度计和陀螺仪集成在单个芯片上,大大简化了电路设计。这种设计不仅减少了生产成本,还提高了系统的稳定性和可靠性。
易于集成:BMI160提供了标准的数字接口(如I²C和SPI),便于与其他电子元件进行连接和通信。这使得它在各种应用场景中都能轻松集成。
小尺寸封装
紧凑外观:BMI160采用了紧凑的封装设计,使得它在保持高性能的同时,能够轻松适应各种小型设备的需求。
轻量级:由于采用了先进的材料和技术,BMI160具有较轻的重量,不会对设备造成额外的负担。
总的来说,BMI160凭借其高精度测量、低功耗设计、高集成度和小尺寸封装等特点,成为各类需要运动检测和测量设备的理想选择。
接线方法
图一
图一中画红色圈位置飞线到BMI160的I2C接口上。
图二
图二中,画红色圈部分与树莓派 PICO的I2C连接,绿色部分在我的板卡上接到VDDIO上
代码编写
commands.py
# command START_FOC = const(0x03) ACC_MODE_NORMAL = const(0x11) GYR_MODE_NORMAL = const(0x15) FIFO_FLUSH = const(0xB0) INT_RESET = const(0xB1) STEP_CNT_CLR = const(0xB2) SOFT_RESET = const(0xB6)
registers.py
# register PMU_STATUS = const(0x03) GYRO_X_L = const(0x0C) ACCEL_RANGE = const(0X41) GYRO_RANGE = const(0X43) INT_MAP_0 = const(0x55) INT_MAP_1 = const(0x56) INT_MAP_2 = const(0x57) CMD = const(0x7E)
definitions.py
## bit field offsets and lengths ACC_PMU_STATUS_BIT = const(4) ACC_PMU_STATUS_LEN = const(2) GYR_PMU_STATUS_BIT = const(2) GYR_PMU_STATUS_LEN = const(2) GYRO_RANGE_SEL_BIT = const(0) GYRO_RANGE_SEL_LEN = const(3) ACCEL_RANGE_SEL_BIT = const(0) ACCEL_RANGE_SEL_LEN = const(4) ## Gyroscope Sensitivity Range options # see setFullScaleGyroRange() GYRO_RANGE_2000 = const(0) # +/- 2000 degrees/second GYRO_RANGE_1000 = const(1) # +/- 1000 degrees/second GYRO_RANGE_500 = const(2) # +/- 500 degrees/second GYRO_RANGE_250 = const(3) # +/- 250 degrees/second GYRO_RANGE_125 = const(4) # +/- 125 degrees/second ## Accelerometer Sensitivity Range options # see setFullScaleAccelRange() ACCEL_RANGE_2G = const(0X03) # +/- 2g range ACCEL_RANGE_4G = const(0X05) # +/- 4g range ACCEL_RANGE_8G = const(0X08) # +/- 8g range ACCEL_RANGE_16G = const(0X0C) # +/- 16g range
bmi160.py
import registers import commands import definitions from time import sleep_ms, sleep_us from struct import unpack class BMI160: def __init__(self): self._reg_write(registers.CMD, commands.SOFT_RESET) sleep_ms(1) self._reg_read(0x7F) sleep_ms(1) self._reg_write(registers.CMD, commands.ACC_MODE_NORMAL) while (1 != self._reg_read_bits(registers.PMU_STATUS, definitions.ACC_PMU_STATUS_BIT, definitions.ACC_PMU_STATUS_LEN)): pass sleep_ms(1) self._reg_write(registers.CMD, commands.GYR_MODE_NORMAL) sleep_ms(1) while (1 != self._reg_read_bits(registers.PMU_STATUS, definitions.GYR_PMU_STATUS_BIT, definitions.GYR_PMU_STATUS_LEN)): pass sleep_ms(1) self.setFullScaleGyroRange(definitions.GYRO_RANGE_250, 250.0) self.setFullScaleAccelRange(definitions.ACCEL_RANGE_2G, 2.0) self._reg_write(registers.INT_MAP_0, 0xFF) self._reg_write(registers.INT_MAP_1, 0xF0) self._reg_write(registers.INT_MAP_2, 0x00) def _reg_read_bits(self, reg, pos, len): b = self._reg_read(reg) mask = (1 << len) - 1 b >>= pos b &= mask return b def _reg_write_bits(self, reg, data, pos, len): b = self._reg_read(reg) mask = ((1 << len) - 1) << pos data <<= pos data &= mask b &= ~(mask) b |= data self._reg_write(reg, b) def setFullScaleGyroRange(self, range, real): self._reg_write_bits(registers.GYRO_RANGE, range, definitions.GYRO_RANGE_SEL_BIT, definitions.GYRO_RANGE_SEL_LEN) gyro_range = real def setFullScaleAccelRange(self, range, real): self._reg_write_bits(registers.ACCEL_RANGE, range, definitions.ACCEL_RANGE_SEL_BIT, definitions.ACCEL_RANGE_SEL_LEN) accel_range = real def getRawData(self): raw = self._regs_read(registers.GYRO_X_L, 12) vals = unpack('<6h', bytes(raw)) return vals def getRegister(self, reg): return self._reg_read(reg) def setRegister(self, reg, data): self._reg_write(reg, data) class BMI160_I2C(BMI160): def __init__(self, i2c, addr=0x69): self.i2c = i2c self.addr = addr super().__init__() def _reg_write(self, reg, data): self.i2c.writeto(self.addr, bytes([reg, data])) def _reg_read(self, reg): return self._regs_read(reg, 1)[0] def _regs_read(self, reg, n): self.i2c.writeto(self.addr, bytes([reg])) sleep_us(2) return self.i2c.readfrom(self.addr, n)
main.py
from machine import Pin, I2C from bmi160 import BMI160_I2C from time import sleep_ms i2c = I2C(sda=Pin(21), scl=Pin(22)) bmi160 = BMI160_I2C(i2c) while True: print("{0:>8}{1:>8}{2:>8}{3:>8}{4:>8}{5:>8}".format(*bmi160.getRawData())) sleep_ms(1000//25)
运行结果
>>> %Run -c $EDITOR_CONTENT MPY: soft reboot 31264 -23835 -21161 -11473 -16412 -15670 22743 -3364 -3696 10350 -8500 -689 -19636 -4658 -29486 -29934 15991 1511 -19540 -25601 -5009 24609 -5118 -3268 -2341 28048 13233 7505 5471 -12547 -10720 -24762 -23464 12531 18157 1582 13790 20958 -6130 -30365 19096 -2712 8038 -12694 -21511 -6470 9043 -14693 2005 -25850 -23514 22028 12103 19144 -16240 -4796 6153 -17913 -8854 15680 10561 -15754 30936 28389 14195 -1150
总结
用了一周的micropython了,发现这种应用层便准化的写法真的是太方便了,写出来的代码也几乎不需要修改便可迁移到其他支持micropython的平台上。唯一不确定的是,micropython对比纯C写法性能的消耗相差有多大,需要同一平台详细测试才清楚。