feat: Enhance MPU6050Slave I2C handling with improved WHO_AM_I read tracking; update tests for clarity

This commit is contained in:
David Montero Crespo 2026-04-08 08:48:14 -03:00
parent a76f7b51fd
commit 4667c8bb3b
2 changed files with 38 additions and 14 deletions

View File

@ -43,7 +43,13 @@ class MPU6050Slave:
# WRITE, as happens with Adafruit BusIO write-then-read) returns 0x68.
self.reg_ptr = 0x75
self.first_byte = True
self._first_read_done = False # True after begin() WHO_AM_I is read
# Adafruit_I2CDevice::begin() fires TWO WHO_AM_I reads before the
# library moves on to actual data reads:
# 1. detected() uses requestFrom as fallback — fires START+READ
# 2. chip_id_register.read() — fires START+READ
# Only after both have returned 0x68 do we switch reg_ptr to 0x3B
# (start of accel/gyro/temp block) for subsequent data transactions.
self._who_am_i_count = 0
# WHO_AM_I
self.regs[0x75] = 0x68
@ -68,13 +74,17 @@ class MPU6050Slave:
if op == I2C_START:
self.first_byte = True
if self._first_read_done:
# picsimlab does not fire WRITE callbacks for write-then-read
# transactions (endTransmission(false) + requestFrom). After
# begin() has succeeded, reset reg_ptr to the accel/gyro/temp
# block so data reads return the right bytes even without a
# prior WRITE setting the register address.
self.reg_ptr = 0x3B
# picsimlab does not fire WRITE callbacks for write-then-read
# transactions (endTransmission(false) + requestFrom).
# Adafruit_I2CDevice::begin() fires TWO START+READ sequences
# before any data reads:
# 1. detected() → requestFrom fallback → START+READ(WHO_AM_I)
# 2. chip_id_register.read() → START+READ(WHO_AM_I)
# Only after both have returned 0x68 do we switch to data mode.
if self._who_am_i_count >= 2:
self.reg_ptr = 0x3B # sensor data block
else:
self.reg_ptr = 0x75 # WHO_AM_I register
return 1 # ACK — device present
elif op in _I2C_WRITE_CODES:
if self.first_byte:
@ -90,8 +100,10 @@ class MPU6050Slave:
return 1 # ACK
elif op == I2C_READ:
val = self.regs[self.reg_ptr]
# Track WHO_AM_I reads to know when begin() has confirmed device
if self.reg_ptr == 0x75 and val == 0x68:
self._who_am_i_count += 1
self.reg_ptr = (self.reg_ptr + 1) & 0xFF
self._first_read_done = True
return val
else: # STOP / unknown
self.first_byte = True

View File

@ -400,16 +400,27 @@ class TestMPU6050Slave(unittest.TestCase):
f"Expected WHO_AM_I=0x68 without WRITE, got 0x{result:02x}")
def test_data_read_after_begin(self):
"""After begin() succeeds, START should reset reg_ptr to 0x3B (accel block)."""
"""After begin() succeeds (two WHO_AM_I reads), START resets reg_ptr to 0x3B.
Adafruit_I2CDevice::begin() fires TWO START+READ sequences before data reads:
1. detected() requestFrom fallback START+READ(WHO_AM_I)
2. chip_id_register.read() START+READ(WHO_AM_I)
Only after both return 0x68 does the slave switch to data mode.
"""
m = MPU6050Slave()
# Simulate begin(): START + READ (no WRITE) → gets WHO_AM_I
# First WHO_AM_I read: detected() requestFrom fallback
m.handle_event(I2C_START)
m.handle_event(I2C_READ) # _first_read_done = True
# Next transaction: START should reset reg_ptr to 0x3B
r1 = m.handle_event(I2C_READ)
self.assertEqual(r1, 0x68, "First WHO_AM_I read must return 0x68")
# Second WHO_AM_I read: actual chip_id check — count reaches 2
m.handle_event(I2C_START)
r2 = m.handle_event(I2C_READ)
self.assertEqual(r2, 0x68, "Second WHO_AM_I read must return 0x68")
# Now _who_am_i_count=2 → next START switches to data mode
m.handle_event(I2C_START)
first_accel_byte = m.handle_event(I2C_READ)
self.assertEqual(first_accel_byte, m.regs[0x3B],
"After begin(), START should reset reg_ptr to 0x3B (accel block)")
"After two WHO_AM_I reads, START should reset reg_ptr to 0x3B (accel block)")
def test_accel_z_default_1g(self):
"""ACCEL_Z should default to +1g = 0x4000 (MSB=0x40, LSB=0x00)."""
@ -454,6 +465,7 @@ if __name__ == '__main__':
TestDS1307Slave,
TestDS3231Slave,
TestI2CWriteSink,
TestMPU6050Slave,
]:
suite.addTests(loader.loadTestsFromTestCase(cls))