How does the LIS3MDL I2C library work?

CircuitPython on hardware including Adafruit's boards, and CircuitPython libraries using Blinka on host computers.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
Chadfrom308
 
Posts: 16
Joined: Wed Feb 10, 2021 1:24 pm

How does the LIS3MDL I2C library work?

Post by Chadfrom308 »

I don't understand how the LIS3MDL library works.

You can set the sensor range like:

Code: Select all

sensor.range = adafruit_lis3mdl.Range.RANGE_16_GAUSS
But I don't understand how it actually accomplishes the task.

Line 271 of the LIS3MDL CircuitPython library: https://github.com/adafruit/Adafruit_Ci ... lis3mdl.py

Code: Select all

    @property
    def range(self):
        """The measurement range for the magnetic sensor. Must be a ``Range``"""
        return self._range

    @range.setter
    def range(self, value):
        if not Range.is_valid(value):
            raise AttributeError("``range`` must be a ``Range``")

        self._range = value

        sleep(0.010)
To me, it looks like when you set the range, it only sets internal variables, and it doesn't appear to execute any I2C logic.

I am stumped and I'm not sure how it works. Can anyone help explain?

User avatar
dastels
 
Posts: 15656
Joined: Tue Oct 20, 2015 3:22 pm

Re: How does the LIS3MDL I2C library work?

Post by dastels »

The interaction with the device is done via RWBits: https://github.com/adafruit/Adafruit_Ci ... 2c_bits.py

If you look in the definition of _range, you can see it's an instance of RWBits:

Code: Select all

 _range = RWBits(2, _LIS3MDL_CTRL_REG2, 5)
Now, if we look at the __get__ and __set__ methods for RWBits, we can see it accessing the I2C bus:

Code: Select all

    def __get__(self, obj, objtype=None):
        with obj.i2c_device as i2c:
            i2c.write_then_readinto(self.buffer, self.buffer, out_end=1, in_start=1)
        # read the number of bytes into a single variable
        reg = 0
        order = range(len(self.buffer) - 1, 0, -1)
        if not self.lsb_first:
            order = reversed(order)
        for i in order:
            reg = (reg << 8) | self.buffer[i]
        reg = (reg & self.bit_mask) >> self.lowest_bit
        # If the value is signed and negative, convert it
        if reg & self.sign_bit:
            reg -= 2 * self.sign_bit
        return reg

    def __set__(self, obj, value):
        value <<= self.lowest_bit  # shift the value over to the right spot
        with obj.i2c_device as i2c:
            i2c.write_then_readinto(self.buffer, self.buffer, out_end=1, in_start=1)
            reg = 0
            order = range(len(self.buffer) - 1, 0, -1)
            if not self.lsb_first:
                order = range(1, len(self.buffer))
            for i in order:
                reg = (reg << 8) | self.buffer[i]
            # print("old reg: ", hex(reg))
            reg &= ~self.bit_mask  # mask off the bits we're about to change
            reg |= value  # then or in our new value
            # print("new reg: ", hex(reg))
            for i in reversed(order):
                self.buffer[i] = reg & 0xFF
                reg >>= 8
            i2c.write(self.buffer)
RWBits is what's called a descriptor in Python:
Descriptors let objects customize attribute lookup, storage, and deletion.
See https://docs.python.org/3/howto/descriptor.html for more information.

Dave

User avatar
Chadfrom308
 
Posts: 16
Joined: Wed Feb 10, 2021 1:24 pm

Re: How does the LIS3MDL I2C library work?

Post by Chadfrom308 »

That's pretty clever! I understand now! thanks!

User avatar
dastels
 
Posts: 15656
Joined: Tue Oct 20, 2015 3:22 pm

Re: How does the LIS3MDL I2C library work?

Post by dastels »

Yes, it's a nice bit of meta-programming.

Dave

Locked
Please be positive and constructive with your questions and comments.

Return to “Adafruit CircuitPython”